From 8d33dc2971c49b5fe2eb9401e77406199d524d8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Revol?= Date: Sun, 1 Apr 2012 11:04:41 +0200 Subject: [PATCH] An html5 based drawing engine This implements an html5 rendering engine. Inspired by the Broadway GDK backend for Gnome. Work in progress. For now it just connects and dumps debug output. --- src/servers/app/Jamfile | 3 +- src/servers/app/ScreenManager.cpp | 11 +- src/servers/app/drawing/Jamfile | 1 + .../app/drawing/html5/CanvasEventStream.cpp | 218 ++++ .../app/drawing/html5/CanvasEventStream.h | 45 + .../app/drawing/html5/CanvasMessage.cpp | 500 ++++++++ src/servers/app/drawing/html5/CanvasMessage.h | 371 ++++++ .../app/drawing/html5/HTML5DrawingEngine.cpp | 1117 +++++++++++++++++ .../app/drawing/html5/HTML5DrawingEngine.h | 180 +++ .../app/drawing/html5/HTML5HWInterface.cpp | 612 +++++++++ .../app/drawing/html5/HTML5HWInterface.h | 131 ++ src/servers/app/drawing/html5/Jamfile | 32 + src/servers/app/drawing/html5/NetSender.cpp | 75 ++ src/servers/app/drawing/html5/NetSender.h | 34 + .../app/drawing/html5/StreamingRingBuffer.cpp | 176 +++ .../app/drawing/html5/StreamingRingBuffer.h | 47 + src/servers/app/drawing/html5/WebHandler.cpp | 85 ++ src/servers/app/drawing/html5/WebHandler.h | 58 + src/servers/app/drawing/html5/WebServer.cpp | 155 +++ src/servers/app/drawing/html5/WebServer.h | 47 + src/servers/app/drawing/html5/WebWorker.cpp | 152 +++ src/servers/app/drawing/html5/WebWorker.h | 39 + src/servers/app/drawing/html5/base64.cpp | 115 ++ src/servers/app/drawing/html5/base64.h | 15 + src/servers/app/drawing/html5/desktop.html | 29 + src/servers/app/drawing/html5/desktop.html.h | 30 + src/servers/app/drawing/html5/haiku.js | 100 ++ src/servers/app/drawing/html5/haiku.js.h | 101 ++ 28 files changed, 4477 insertions(+), 2 deletions(-) create mode 100644 src/servers/app/drawing/html5/CanvasEventStream.cpp create mode 100644 src/servers/app/drawing/html5/CanvasEventStream.h create mode 100644 src/servers/app/drawing/html5/CanvasMessage.cpp create mode 100644 src/servers/app/drawing/html5/CanvasMessage.h create mode 100644 src/servers/app/drawing/html5/HTML5DrawingEngine.cpp create mode 100644 src/servers/app/drawing/html5/HTML5DrawingEngine.h create mode 100644 src/servers/app/drawing/html5/HTML5HWInterface.cpp create mode 100644 src/servers/app/drawing/html5/HTML5HWInterface.h create mode 100644 src/servers/app/drawing/html5/Jamfile create mode 100644 src/servers/app/drawing/html5/NetSender.cpp create mode 100644 src/servers/app/drawing/html5/NetSender.h create mode 100644 src/servers/app/drawing/html5/StreamingRingBuffer.cpp create mode 100644 src/servers/app/drawing/html5/StreamingRingBuffer.h create mode 100644 src/servers/app/drawing/html5/WebHandler.cpp create mode 100644 src/servers/app/drawing/html5/WebHandler.h create mode 100644 src/servers/app/drawing/html5/WebServer.cpp create mode 100644 src/servers/app/drawing/html5/WebServer.h create mode 100644 src/servers/app/drawing/html5/WebWorker.cpp create mode 100644 src/servers/app/drawing/html5/WebWorker.h create mode 100644 src/servers/app/drawing/html5/base64.cpp create mode 100644 src/servers/app/drawing/html5/base64.h create mode 100644 src/servers/app/drawing/html5/desktop.html create mode 100644 src/servers/app/drawing/html5/desktop.html.h create mode 100644 src/servers/app/drawing/html5/haiku.js create mode 100644 src/servers/app/drawing/html5/haiku.js.h diff --git a/src/servers/app/Jamfile b/src/servers/app/Jamfile index f31ebbbad1..0b917f3c7e 100644 --- a/src/servers/app/Jamfile +++ b/src/servers/app/Jamfile @@ -84,7 +84,8 @@ Server app_server : # libraries : libtranslation.so libbe.so libbnetapi.so - libasdrawing.a libasremote.a libpainter.a libagg.a $(HAIKU_FREETYPE_LIB) + libasdrawing.a libasremote.a libashtml5.a + libpainter.a libagg.a $(HAIKU_FREETYPE_LIB) libstackandtile.a liblinprog.a libtextencoding.so libshared.a $(TARGET_LIBSTDC++) diff --git a/src/servers/app/ScreenManager.cpp b/src/servers/app/ScreenManager.cpp index 308939a098..064bb79f2d 100644 --- a/src/servers/app/ScreenManager.cpp +++ b/src/servers/app/ScreenManager.cpp @@ -15,6 +15,7 @@ #include "ServerConfig.h" #include "remote/RemoteHWInterface.h" +#include "html5/HTML5HWInterface.h" #include #include @@ -109,7 +110,15 @@ ScreenManager::AcquireScreens(ScreenOwner* owner, int32* wishList, // there's a specific target screen we want to initialize // TODO: right now we only support remote screens, but we could // also target specific accelerants to support other graphics cards - RemoteHWInterface* interface = new(nothrow) RemoteHWInterface(target); + HWInterface* interface; + /* + if (strncmp(target, "vnc:", 4) == 0) + interface = new(nothrow) VNCHWInterface(target); + else*/ + if (strncmp(target, "html5:", 6) == 0) + interface = new(nothrow) HTML5HWInterface(target); + else + interface = new(nothrow) RemoteHWInterface(target); if (interface != NULL) { screen_item* item = _AddHWInterface(interface); if (item != NULL && list.AddItem(item->screen)) { diff --git a/src/servers/app/drawing/Jamfile b/src/servers/app/drawing/Jamfile index ee5c46564e..f84cc4f1f8 100644 --- a/src/servers/app/drawing/Jamfile +++ b/src/servers/app/drawing/Jamfile @@ -30,3 +30,4 @@ StaticLibrary libasdrawing.a : SubInclude HAIKU_TOP src servers app drawing Painter ; SubInclude HAIKU_TOP src servers app drawing remote ; +SubInclude HAIKU_TOP src servers app drawing html5 ; diff --git a/src/servers/app/drawing/html5/CanvasEventStream.cpp b/src/servers/app/drawing/html5/CanvasEventStream.cpp new file mode 100644 index 0000000000..ec9cc99d2a --- /dev/null +++ b/src/servers/app/drawing/html5/CanvasEventStream.cpp @@ -0,0 +1,218 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include "CanvasEventStream.h" + +#include "CanvasMessage.h" +#include "StreamingRingBuffer.h" + +#include + +#include + + +CanvasEventStream::CanvasEventStream() + : + fEventList(10, true), + fEventListLocker("canvas event list"), + fEventNotification(-1), + fWaitingOnEvent(false), + fLatestMouseMovedEvent(NULL), + fMousePosition(0, 0), + fMouseButtons(0), + fModifiers(0) +{ + fEventNotification = create_sem(0, "canvas event notification"); +} + + +CanvasEventStream::~CanvasEventStream() +{ + delete_sem(fEventNotification); +} + + +void +CanvasEventStream::UpdateScreenBounds(BRect bounds) +{ +} + + +bool +CanvasEventStream::GetNextEvent(BMessage** _event) +{ + BAutolock lock(fEventListLocker); + while (fEventList.CountItems() == 0) { + fWaitingOnEvent = true; + lock.Unlock(); + + status_t result; + do { + result = acquire_sem(fEventNotification); + } while (result == B_INTERRUPTED); + + lock.Lock(); + if (!lock.IsLocked()) + return false; + } + + *_event = fEventList.RemoveItemAt(0); + return true; +} + + +status_t +CanvasEventStream::InsertEvent(BMessage* event) +{ + BAutolock lock(fEventListLocker); + if (!lock.IsLocked()) + return B_ERROR; + + if (!fEventList.AddItem(event)) + return B_ERROR; + + if (event->what == B_MOUSE_MOVED) + fLatestMouseMovedEvent = event; + + return B_OK; +} + + +BMessage* +CanvasEventStream::PeekLatestMouseMoved() +{ + return fLatestMouseMovedEvent; +} + + +bool +CanvasEventStream::EventReceived(CanvasMessage& message) +{ + uint16 code = message.Code(); + uint32 what = 0; + switch (code) { + case RP_MOUSE_MOVED: + what = B_MOUSE_MOVED; + break; + case RP_MOUSE_DOWN: + what = B_MOUSE_DOWN; + break; + case RP_MOUSE_UP: + what = B_MOUSE_UP; + break; + case RP_MOUSE_WHEEL_CHANGED: + what = B_MOUSE_WHEEL_CHANGED; + break; + case RP_KEY_DOWN: + what = B_KEY_DOWN; + break; + case RP_KEY_UP: + what = B_KEY_UP; + break; + case RP_MODIFIERS_CHANGED: + what = B_MODIFIERS_CHANGED; + break; + } + + if (what == 0) + return false; + + BMessage* event = new BMessage(what); + if (event == NULL) + return false; + + event->AddInt64("when", system_time()); + + switch (code) { + case RP_MOUSE_MOVED: + case RP_MOUSE_DOWN: + case RP_MOUSE_UP: + { + message.Read(fMousePosition); + if (code != RP_MOUSE_MOVED) + message.Read(fMouseButtons); + + event->AddPoint("where", fMousePosition); + event->AddInt32("buttons", fMouseButtons); + event->AddInt32("modifiers", fModifiers); + + if (code == RP_MOUSE_DOWN) { + int32 clicks; + if (message.Read(clicks) == B_OK) + event->AddInt32("clicks", clicks); + } + + if (code == RP_MOUSE_MOVED) + fLatestMouseMovedEvent = event; + break; + } + + case RP_MOUSE_WHEEL_CHANGED: + { + float xDelta, yDelta; + message.Read(xDelta); + message.Read(yDelta); + event->AddFloat("be:wheel_delta_x", xDelta); + event->AddFloat("be:wheel_delta_y", yDelta); + break; + } + + case RP_KEY_DOWN: + case RP_KEY_UP: + { + int32 numBytes; + if (message.Read(numBytes) != B_OK) + break; + + char* bytes = (char*)malloc(numBytes + 1); + if (bytes == NULL) + break; + + if (message.ReadList(bytes, numBytes) != B_OK) { + free(bytes); + break; + } + + for (int32 i = 0; i < numBytes; i++) + event->AddInt8("byte", (int8)bytes[i]); + + bytes[numBytes] = 0; + event->AddData("bytes", B_STRING_TYPE, bytes, numBytes + 1, false); + event->AddInt32("modifiers", fModifiers); + + int32 rawChar; + if (message.Read(rawChar) == B_OK) + event->AddInt32("raw_char", rawChar); + + int32 key; + if (message.Read(key) == B_OK) + event->AddInt32("key", key); + + free(bytes); + break; + } + + case RP_MODIFIERS_CHANGED: + { + event->AddInt32("be:old_modifiers", fModifiers); + message.Read(fModifiers); + event->AddInt32("modifiers", fModifiers); + break; + } + } + + BAutolock lock(fEventListLocker); + fEventList.AddItem(event); + if (fWaitingOnEvent) { + fWaitingOnEvent = false; + lock.Unlock(); + release_sem(fEventNotification); + } + + return true; +} diff --git a/src/servers/app/drawing/html5/CanvasEventStream.h b/src/servers/app/drawing/html5/CanvasEventStream.h new file mode 100644 index 0000000000..68d0050cc6 --- /dev/null +++ b/src/servers/app/drawing/html5/CanvasEventStream.h @@ -0,0 +1,45 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ +#ifndef CANVAS_EVENT_STREAM_H +#define CANVAS_EVENT_STREAM_H + +#include "EventStream.h" + +#include +#include + +class CanvasMessage; + +class CanvasEventStream : public EventStream { +public: + CanvasEventStream(); +virtual ~CanvasEventStream(); + +virtual bool IsValid() { return true; } +virtual void SendQuit() {} + +virtual void UpdateScreenBounds(BRect bounds); +virtual bool GetNextEvent(BMessage** _event); +virtual status_t InsertEvent(BMessage* event); +virtual BMessage* PeekLatestMouseMoved(); + + bool EventReceived(CanvasMessage& message); + +private: + BObjectList fEventList; + BLocker fEventListLocker; + sem_id fEventNotification; + bool fWaitingOnEvent; + BMessage* fLatestMouseMovedEvent; + + BPoint fMousePosition; + uint32 fMouseButtons; + uint32 fModifiers; +}; + +#endif // CANVAS_EVENT_STREAM_H diff --git a/src/servers/app/drawing/html5/CanvasMessage.cpp b/src/servers/app/drawing/html5/CanvasMessage.cpp new file mode 100644 index 0000000000..367d0d446b --- /dev/null +++ b/src/servers/app/drawing/html5/CanvasMessage.cpp @@ -0,0 +1,500 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + * François Revol + */ + +#include "CanvasMessage.h" + +#include "DrawState.h" +#include "ServerBitmap.h" +#include "ServerCursor.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + + +status_t +CanvasMessage::NextMessage(uint16& code) +{ + if (fDataLeft > 0) { + // discard remainder of message + int32 readSize = fSource->Read(NULL, fDataLeft); + if (readSize < 0) + return readSize; + } + + static const uint32 kHeaderSize = sizeof(uint16) + sizeof(uint32); + + fDataLeft = kHeaderSize; + Read(code); + uint32 dataLeft; + status_t result = Read(dataLeft); + if (result != B_OK) + return result; + + if (dataLeft < kHeaderSize) + return B_ERROR; + + fDataLeft = dataLeft - kHeaderSize; + fCode = code; + return B_OK; +} + + +void +CanvasMessage::Cancel() +{ + fAvailable += fWriteIndex; + fWriteIndex = 0; +} + + +void +CanvasMessage::AddBitmap(const ServerBitmap& bitmap, bool minimal) +{ + //TODO:send PNG / as data: or as http: url + Add(bitmap.Width()); + Add(bitmap.Height()); + Add(bitmap.BytesPerRow()); + + if (!minimal) { + Add(bitmap.ColorSpace()); + Add(bitmap.Flags()); + } + + uint32 bitsLength = bitmap.BitsLength(); + Add(bitsLength); + + if (!_MakeSpace(bitsLength)) + return; + + memcpy(fBuffer + fWriteIndex, bitmap.Bits(), bitsLength); + fWriteIndex += bitsLength; + fAvailable -= bitsLength; +} + + +void +CanvasMessage::AddFont(const ServerFont& font) +{ + //TODO:Use TTF/WOFF URL + Add(font.Direction()); + Add((uint8)font.Encoding()); + Add(font.Flags()); + Add((uint8)font.Spacing()); + Add(font.Shear()); + Add(font.Rotation()); + Add(font.FalseBoldWidth()); + Add(font.Size()); + Add(font.Face()); + Add(font.GetFamilyAndStyle()); +} + + +void +CanvasMessage::AddDrawState(const DrawState& drawState) +{ + Add(drawState.PenSize()); + Add(drawState.SubPixelPrecise()); + Add(drawState.GetDrawingMode()); + Add(drawState.AlphaSrcMode()); + Add(drawState.AlphaFncMode()); + AddPattern(drawState.GetPattern()); + Add(drawState.LineCapMode()); + Add(drawState.LineJoinMode()); + Add(drawState.MiterLimit()); + Add(drawState.HighColor()); + Add(drawState.LowColor()); +} + + +void +CanvasMessage::AddArrayLine(const ViewLineArrayInfo& line) +{ + Add(line.startPoint); + Add(line.endPoint); + Add(line.color); +} + + +void +CanvasMessage::AddCursor(const ServerCursor& cursor) +{ + //TODO:send as .cur data: + Add(cursor.GetHotSpot()); + AddBitmap(cursor); +} + + +void +CanvasMessage::AddPattern(const Pattern& pattern) +{ + Add(pattern.GetPattern()); +} + + +void +CanvasMessage::AddGradient(const BGradient& gradient) +{ + Add(gradient.GetType()); + + switch (gradient.GetType()) { + case BGradient::TYPE_NONE: + break; + + case BGradient::TYPE_LINEAR: + { + const BGradientLinear* linear + = dynamic_cast(&gradient); + if (linear == NULL) + return; + + Add(linear->Start()); + Add(linear->End()); + break; + } + + case BGradient::TYPE_RADIAL: + { + const BGradientRadial* radial + = dynamic_cast(&gradient); + if (radial == NULL) + return; + + Add(radial->Center()); + Add(radial->Radius()); + break; + } + + case BGradient::TYPE_RADIAL_FOCUS: + { + const BGradientRadialFocus* radialFocus + = dynamic_cast(&gradient); + if (radialFocus == NULL) + return; + + Add(radialFocus->Center()); + Add(radialFocus->Focal()); + Add(radialFocus->Radius()); + break; + } + + case BGradient::TYPE_DIAMOND: + { + const BGradientDiamond* diamond + = dynamic_cast(&gradient); + if (diamond == NULL) + return; + + Add(diamond->Center()); + break; + } + + case BGradient::TYPE_CONIC: + { + const BGradientConic* conic + = dynamic_cast(&gradient); + if (conic == NULL) + return; + + Add(conic->Center()); + Add(conic->Angle()); + break; + } + } + + int32 stopCount = gradient.CountColorStops(); + Add(stopCount); + + for (int32 i = 0; i < stopCount; i++) { + BGradient::ColorStop* stop = gradient.ColorStopAt(i); + if (stop == NULL) + return; + + Add(stop->color); + Add(stop->offset); + } +} + + +status_t +CanvasMessage::ReadString(char** _string, size_t& _length) +{ + uint32 length; + status_t result = Read(length); + if (result != B_OK) + return result; + + if (length > fDataLeft) + return B_ERROR; + + char *string = (char *)malloc(length + 1); + if (string == NULL) + return B_NO_MEMORY; + + int32 readSize = fSource->Read(string, length); + if (readSize < 0) { + free(string); + return readSize; + } + + if ((uint32)readSize != length) { + free(string); + return B_ERROR; + } + + fDataLeft -= readSize; + + string[length] = 0; + *_string = string; + _length = length; + return B_OK; +} + + +status_t +CanvasMessage::ReadBitmap(BBitmap** _bitmap, bool minimal, + color_space colorSpace, uint32 flags) +{ + uint32 bitsLength; + int32 width, height, bytesPerRow; + + Read(width); + Read(height); + Read(bytesPerRow); + + if (!minimal) { + Read(colorSpace); + Read(flags); + } + + Read(bitsLength); + + if (bitsLength > fDataLeft) + return B_ERROR; + +#ifndef CLIENT_COMPILE + flags = B_BITMAP_NO_SERVER_LINK; +#endif + + BBitmap *bitmap = new(std::nothrow) BBitmap( + BRect(0, 0, width - 1, height - 1), flags, colorSpace, bytesPerRow); + if (bitmap == NULL) + return B_NO_MEMORY; + + status_t result = bitmap->InitCheck(); + if (result != B_OK) { + delete bitmap; + return result; + } + + if (bitmap->BitsLength() < (int32)bitsLength) { + delete bitmap; + return B_ERROR; + } + + int32 readSize = fSource->Read(bitmap->Bits(), bitsLength); + if ((uint32)readSize != bitsLength) { + delete bitmap; + return readSize < 0 ? readSize : B_ERROR; + } + + fDataLeft -= readSize; + *_bitmap = bitmap; + return B_OK; +} + + +status_t +CanvasMessage::ReadFontState(BFont& font) +{ + uint8 encoding, spacing; + uint16 face; + uint32 flags, familyAndStyle; + font_direction direction; + float falseBoldWidth, rotation, shear, size; + + Read(direction); + Read(encoding); + Read(flags); + Read(spacing); + Read(shear); + Read(rotation); + Read(falseBoldWidth); + Read(size); + Read(face); + status_t result = Read(familyAndStyle); + if (result != B_OK) + return result; + + font.SetFamilyAndStyle(familyAndStyle); + font.SetEncoding(encoding); + font.SetFlags(flags); + font.SetSpacing(spacing); + font.SetShear(shear); + font.SetRotation(rotation); + font.SetFalseBoldWidth(falseBoldWidth); + font.SetSize(size); + font.SetFace(face); + return B_OK; +} + + +status_t +CanvasMessage::ReadViewState(BView& view, ::pattern& pattern) +{ + bool subPixelPrecise; + float penSize, miterLimit; + drawing_mode drawingMode; + source_alpha sourceAlpha; + alpha_function alphaFunction; + cap_mode capMode; + join_mode joinMode; + rgb_color highColor, lowColor; + + Read(penSize); + Read(subPixelPrecise); + Read(drawingMode); + Read(sourceAlpha); + Read(alphaFunction); + Read(pattern); + Read(capMode); + Read(joinMode); + Read(miterLimit); + Read(highColor); + status_t result = Read(lowColor); + if (result != B_OK) + return result; + + uint32 flags = view.Flags() & ~B_SUBPIXEL_PRECISE; + view.SetFlags(flags | (subPixelPrecise ? B_SUBPIXEL_PRECISE : 0)); + view.SetPenSize(penSize); + view.SetDrawingMode(drawingMode); + view.SetBlendingMode(sourceAlpha, alphaFunction); + view.SetLineMode(capMode, joinMode, miterLimit); + view.SetHighColor(highColor); + view.SetLowColor(lowColor); + return B_OK; +} + + +status_t +CanvasMessage::ReadGradient(BGradient** _gradient) +{ + BGradient::Type type; + Read(type); + + BGradient *gradient = NULL; + switch (type) { + case BGradient::TYPE_NONE: + break; + + case BGradient::TYPE_LINEAR: + { + BPoint start, end; + + Read(start); + Read(end); + + gradient = new(std::nothrow) BGradientLinear(start, end); + break; + } + + case BGradient::TYPE_RADIAL: + { + BPoint center; + float radius; + + Read(center); + Read(radius); + + gradient = new(std::nothrow) BGradientRadial(center, radius); + break; + } + + case BGradient::TYPE_RADIAL_FOCUS: + { + BPoint center, focal; + float radius; + + Read(center); + Read(focal); + Read(radius); + + gradient = new(std::nothrow) BGradientRadialFocus(center, radius, + focal); + break; + } + + case BGradient::TYPE_DIAMOND: + { + BPoint center; + + Read(center); + + gradient = new(std::nothrow) BGradientDiamond(center); + break; + } + + case BGradient::TYPE_CONIC: + { + BPoint center; + float angle; + + Read(center); + Read(angle); + + gradient = new(std::nothrow) BGradientConic(center, angle); + break; + } + } + + if (gradient == NULL) + return B_NO_MEMORY; + + int32 stopCount; + status_t result = Read(stopCount); + if (result != B_OK) { + delete gradient; + return result; + } + + for (int32 i = 0; i < stopCount; i++) { + rgb_color color; + float offset; + + Read(color); + result = Read(offset); + if (result != B_OK) + return result; + + gradient->AddColor(color, offset); + } + + *_gradient = gradient; + return B_OK; +} + + +status_t +CanvasMessage::ReadArrayLine(BPoint& startPoint, BPoint& endPoint, + rgb_color& color) +{ + Read(startPoint); + Read(endPoint); + return Read(color); +} diff --git a/src/servers/app/drawing/html5/CanvasMessage.h b/src/servers/app/drawing/html5/CanvasMessage.h new file mode 100644 index 0000000000..ca3648c559 --- /dev/null +++ b/src/servers/app/drawing/html5/CanvasMessage.h @@ -0,0 +1,371 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + * François Revol + */ +#ifndef CANVAS_MESSAGE_H +#define CANCAS_MESSAGE_H + +#include "PatternHandler.h" +#include + +#include "StreamingRingBuffer.h" +#include "base64.h" + +#include +#include + +#include +#include +#include + +class BBitmap; +class BFont; +class BGradient; +class BView; +class DrawState; +class Pattern; +class RemotePainter; +class ServerBitmap; +class ServerCursor; +class ServerFont; +class ViewLineArrayInfo; + +enum { + RP_INIT_CONNECTION = 1, + RP_UPDATE_DISPLAY_MODE, + RP_CLOSE_CONNECTION, + + RP_CREATE_STATE = 20, + RP_DELETE_STATE, + RP_ENABLE_SYNC_DRAWING, + RP_DISABLE_SYNC_DRAWING, + RP_INVALIDATE_RECT, + RP_INVALIDATE_REGION, + + RP_SET_OFFSETS = 40, + RP_SET_HIGH_COLOR, + RP_SET_LOW_COLOR, + RP_SET_PEN_SIZE, + RP_SET_STROKE_MODE, + RP_SET_BLENDING_MODE, + RP_SET_PATTERN, + RP_SET_DRAWING_MODE, + RP_SET_FONT, + + RP_CONSTRAIN_CLIPPING_REGION = 60, + RP_COPY_RECT_NO_CLIPPING, + RP_INVERT_RECT, + RP_DRAW_BITMAP, + RP_DRAW_BITMAP_RECTS, + + RP_STROKE_ARC = 80, + RP_STROKE_BEZIER, + RP_STROKE_ELLIPSE, + RP_STROKE_POLYGON, + RP_STROKE_RECT, + RP_STROKE_ROUND_RECT, + RP_STROKE_SHAPE, + RP_STROKE_TRIANGLE, + RP_STROKE_LINE, + RP_STROKE_LINE_ARRAY, + + RP_FILL_ARC = 100, + RP_FILL_BEZIER, + RP_FILL_ELLIPSE, + RP_FILL_POLYGON, + RP_FILL_RECT, + RP_FILL_ROUND_RECT, + RP_FILL_SHAPE, + RP_FILL_TRIANGLE, + RP_FILL_REGION, + + RP_FILL_ARC_GRADIENT = 120, + RP_FILL_BEZIER_GRADIENT, + RP_FILL_ELLIPSE_GRADIENT, + RP_FILL_POLYGON_GRADIENT, + RP_FILL_RECT_GRADIENT, + RP_FILL_ROUND_RECT_GRADIENT, + RP_FILL_SHAPE_GRADIENT, + RP_FILL_TRIANGLE_GRADIENT, + RP_FILL_REGION_GRADIENT, + + RP_STROKE_POINT_COLOR = 140, + RP_STROKE_LINE_1PX_COLOR, + RP_STROKE_RECT_1PX_COLOR, + + RP_FILL_RECT_COLOR = 160, + RP_FILL_REGION_COLOR_NO_CLIPPING, + + RP_DRAW_STRING = 180, + RP_DRAW_STRING_WITH_OFFSETS, + RP_DRAW_STRING_RESULT, + RP_STRING_WIDTH, + RP_STRING_WIDTH_RESULT, + RP_READ_BITMAP, + RP_READ_BITMAP_RESULT, + + RP_SET_CURSOR = 200, + RP_SET_CURSOR_VISIBLE, + RP_MOVE_CURSOR_TO, + + RP_MOUSE_MOVED = 220, + RP_MOUSE_DOWN, + RP_MOUSE_UP, + RP_MOUSE_WHEEL_CHANGED, + + RP_KEY_DOWN = 240, + RP_KEY_UP, + RP_UNMAPPED_KEY_DOWN, + RP_UNMAPPED_KEY_UP, + RP_MODIFIERS_CHANGED +}; + + +class CanvasMessage { +public: + CanvasMessage(StreamingRingBuffer* source, + StreamingRingBuffer *target); + ~CanvasMessage(); + + void Start(uint16 code); + status_t Flush(); + void Cancel(); + + status_t NextMessage(uint16& code); + uint16 Code() { return fCode; } + uint32 DataLeft() { return fDataLeft; } + + template + void Add(const T& value); + + void AddString(const char* string, size_t length); + void AddRegion(const BRegion& region); + void AddGradient(const BGradient& gradient); + + void AddBitmap(const ServerBitmap& bitmap, + bool minimal = false); + void AddFont(const ServerFont& font); + void AddPattern(const Pattern& pattern); + void AddDrawState(const DrawState& drawState); + void AddArrayLine(const ViewLineArrayInfo& line); + void AddCursor(const ServerCursor& cursor); + + template + void AddList(const T* array, int32 count); + + template + status_t Read(T& value); + + status_t ReadRegion(BRegion& region); + status_t ReadFontState(BFont& font); + // sets font state + status_t ReadViewState(BView& view, ::pattern& pattern); + // sets viewstate and returns pattern + + status_t ReadString(char** _string, size_t& length); + status_t ReadBitmap(BBitmap** _bitmap, + bool minimal = false, + color_space colorSpace = B_RGB32, + uint32 flags = 0); + status_t ReadGradient(BGradient** _gradient); + status_t ReadArrayLine(BPoint& startPoint, + BPoint& endPoint, rgb_color& color); + + template + status_t ReadList(T* array, int32 count); + +private: + bool _MakeSpace(size_t size); + + StreamingRingBuffer* fSource; + StreamingRingBuffer* fTarget; + + uint8* fBuffer; + size_t fAvailable; + size_t fWriteIndex; + uint32 fDataLeft; + uint16 fCode; +}; + + +inline +CanvasMessage::CanvasMessage(StreamingRingBuffer* source, + StreamingRingBuffer* target) + : + fSource(source), + fTarget(target), + fBuffer(NULL), + fAvailable(0), + fWriteIndex(0), + fDataLeft(0) +{ +} + + +inline +CanvasMessage::~CanvasMessage() +{ + if (fWriteIndex > 0) + Flush(); + free(fBuffer); +} + + +inline void +CanvasMessage::Start(uint16 code) +{ + if (fWriteIndex > 0) + Flush(); + + Add(code); + +// uint32 sizeDummy; +// Add(sizeDummy); +} + + +inline status_t +CanvasMessage::Flush() +{ + if (fWriteIndex == 0) + return B_NO_INIT; + + // end by the multipart boundary + static const char boundary[] = "--x\r\n\r\n"; + AddString(boundary, sizeof(boundary) - 1); + + uint32 length = fWriteIndex; + fAvailable += fWriteIndex; + fWriteIndex = 0; + +// memcpy(fBuffer + sizeof(uint16) * 2, &length, sizeof(uint32)); + return fTarget->Write(fBuffer, length); +} + + +template +inline void +CanvasMessage::Add(const T& value) +{ + ssize_t done; + + if (!_MakeSpace(sizeof(T) * 2)) // 4/3 actually + return; + + //memcpy(fBuffer + fWriteIndex, &value, sizeof(T)); + done = encode_base64((char *)fBuffer + fWriteIndex, (const char *)(&value), + sizeof(T)); + fWriteIndex += done; + fAvailable -= done; +} + + +inline void +CanvasMessage::AddString(const char* string, size_t length) +{ + Add(length); + if (length > fAvailable && !_MakeSpace(length)) + return; + + memcpy(fBuffer + fWriteIndex, string, length); + fWriteIndex += length; + fAvailable -= length; +} + + +inline void +CanvasMessage::AddRegion(const BRegion& region) +{ + int32 rectCount = region.CountRects(); + Add(rectCount); + + for (int32 i = 0; i < rectCount; i++) + Add(region.RectAt(i)); +} + + +template +inline void +CanvasMessage::AddList(const T* array, int32 count) +{ + for (int32 i = 0; i < count; i++) + Add(array[i]); +} + + +template +inline status_t +CanvasMessage::Read(T& value) +{ + //TODO + if (fDataLeft < sizeof(T)) + return B_ERROR; + + int32 readSize = fSource->Read(&value, sizeof(T)); + if (readSize < 0) + return readSize; + + if (readSize != sizeof(T)) + return B_ERROR; + + fDataLeft -= sizeof(T); + return B_OK; +} + + +inline status_t +CanvasMessage::ReadRegion(BRegion& region) +{ + region.MakeEmpty(); + + int32 rectCount; + Read(rectCount); + + for (int32 i = 0; i < rectCount; i++) { + BRect rect; + status_t result = Read(rect); + if (result != B_OK) + return result; + + region.Include(rect); + } + + return B_OK; +} + + +template +inline status_t +CanvasMessage::ReadList(T* array, int32 count) +{ + for (int32 i = 0; i < count; i++) { + status_t result = Read(array[i]); + if (result != B_OK) + return result; + } + + return B_OK; +} + + +inline bool +CanvasMessage::_MakeSpace(size_t size) +{ + if (fAvailable >= size) + return true; + + size_t extraSize = size + 20; + uint8 *newBuffer = (uint8*)realloc(fBuffer, fWriteIndex + extraSize); + if (newBuffer == NULL) + return false; + + fAvailable = extraSize; + fBuffer = newBuffer; + return true; +} + +#endif // CANVAS_MESSAGE_H diff --git a/src/servers/app/drawing/html5/HTML5DrawingEngine.cpp b/src/servers/app/drawing/html5/HTML5DrawingEngine.cpp new file mode 100644 index 0000000000..f1434a3fe9 --- /dev/null +++ b/src/servers/app/drawing/html5/HTML5DrawingEngine.cpp @@ -0,0 +1,1117 @@ +/* + * Copyright 2009-2010, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + * François Revol + */ + +#include "HTML5DrawingEngine.h" +#include "CanvasMessage.h" + +#include "BitmapDrawingEngine.h" +#include "DrawState.h" + +#include +#include + +#include + + +HTML5DrawingEngine::HTML5DrawingEngine(HTML5HWInterface* interface) + : + DrawingEngine(interface), + fHWInterface(interface), + fToken((uint32)this), // TODO: need to redo that for 64 bit + fExtendWidth(0), + fCallbackAdded(false), + fResultNotify(-1), + fStringWidthResult(0.0f), + fReadBitmapResult(NULL), + fBitmapDrawingEngine(NULL) +{ + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_CREATE_STATE); + message.Add(fToken); +} + + +HTML5DrawingEngine::~HTML5DrawingEngine() +{ + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_DELETE_STATE); + message.Add(fToken); + message.Flush(); + + delete fBitmapDrawingEngine; + + if (fCallbackAdded) + fHWInterface->RemoveCallback(fToken); + if (fResultNotify >= 0) + delete_sem(fResultNotify); +} + + +// #pragma mark - + + +void +HTML5DrawingEngine::FrameBufferChanged() +{ + // Not allowed +} + + +// #pragma mark - + + +void +HTML5DrawingEngine::SetCopyToFrontEnabled(bool enabled) +{ + DrawingEngine::SetCopyToFrontEnabled(enabled); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(enabled ? RP_ENABLE_SYNC_DRAWING : RP_DISABLE_SYNC_DRAWING); + message.Add(fToken); +} + + +// #pragma mark - + + +//! the HTML5DrawingEngine needs to be locked! +void +HTML5DrawingEngine::ConstrainClippingRegion(const BRegion* region) +{ + if (fClippingRegion == *region) + return; + + fClippingRegion = *region; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_CONSTRAIN_CLIPPING_REGION); + message.Add(fToken); + message.AddRegion(*region); +} + + +void +HTML5DrawingEngine::SetDrawState(const DrawState* state, int32 xOffset, + int32 yOffset) +{ + SetPenSize(state->PenSize()); + SetDrawingMode(state->GetDrawingMode()); + SetBlendingMode(state->AlphaSrcMode(), state->AlphaFncMode()); + SetPattern(state->GetPattern().GetPattern()); + SetStrokeMode(state->LineCapMode(), state->LineJoinMode(), + state->MiterLimit()); + SetHighColor(state->HighColor()); + SetLowColor(state->LowColor()); + SetFont(state->Font()); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_OFFSETS); + message.Add(fToken); + message.Add(xOffset); + message.Add(yOffset); +} + + +void +HTML5DrawingEngine::SetHighColor(const rgb_color& color) +{ + if (fState.HighColor() == color) + return; + + fState.SetHighColor(color); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_HIGH_COLOR); + message.Add(fToken); + message.Add(color); +} + + +void +HTML5DrawingEngine::SetLowColor(const rgb_color& color) +{ + if (fState.LowColor() == color) + return; + + fState.SetLowColor(color); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_LOW_COLOR); + message.Add(fToken); + message.Add(color); +} + + +void +HTML5DrawingEngine::SetPenSize(float size) +{ + if (fState.PenSize() == size) + return; + + fState.SetPenSize(size); + fExtendWidth = -(size / 2); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_PEN_SIZE); + message.Add(fToken); + message.Add(size); +} + + +void +HTML5DrawingEngine::SetStrokeMode(cap_mode lineCap, join_mode joinMode, + float miterLimit) +{ + if (fState.LineCapMode() == lineCap && fState.LineJoinMode() == joinMode + && fState.MiterLimit() == miterLimit) + return; + + fState.SetLineCapMode(lineCap); + fState.SetLineJoinMode(joinMode); + fState.SetMiterLimit(miterLimit); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_STROKE_MODE); + message.Add(fToken); + message.Add(lineCap); + message.Add(joinMode); + message.Add(miterLimit); +} + + +void +HTML5DrawingEngine::SetBlendingMode(source_alpha sourceAlpha, + alpha_function alphaFunc) +{ + if (fState.AlphaSrcMode() == sourceAlpha + && fState.AlphaFncMode() == alphaFunc) + return; + + fState.SetBlendingMode(sourceAlpha, alphaFunc); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_BLENDING_MODE); + message.Add(fToken); + message.Add(sourceAlpha); + message.Add(alphaFunc); +} + + +void +HTML5DrawingEngine::SetPattern(const struct pattern& pattern) +{ + if (fState.GetPattern() == pattern) + return; + + fState.SetPattern(pattern); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_PATTERN); + message.Add(fToken); + message.Add(pattern); +} + + +void +HTML5DrawingEngine::SetDrawingMode(drawing_mode mode) +{ + if (fState.GetDrawingMode() == mode) + return; + + fState.SetDrawingMode(mode); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_DRAWING_MODE); + message.Add(fToken); + message.Add(mode); +} + + +void +HTML5DrawingEngine::SetDrawingMode(drawing_mode mode, drawing_mode& oldMode) +{ + oldMode = fState.GetDrawingMode(); + SetDrawingMode(mode); +} + + +void +HTML5DrawingEngine::SetFont(const ServerFont& font) +{ + if (fState.Font() == font) + return; + + fState.SetFont(font); + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_FONT); + message.Add(fToken); + message.AddFont(font); +} + + +void +HTML5DrawingEngine::SetFont(const DrawState* state) +{ + SetFont(state->Font()); +} + + +// #pragma mark - + + +BRect +HTML5DrawingEngine::CopyRect(BRect rect, int32 xOffset, int32 yOffset) const +{ + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_COPY_RECT_NO_CLIPPING); + message.Add(xOffset); + message.Add(yOffset); + message.Add(rect); + return rect.OffsetBySelf(xOffset, yOffset); +} + + +void +HTML5DrawingEngine::InvertRect(BRect rect) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_INVERT_RECT); + message.Add(fToken); + message.Add(rect); +} + + +void +HTML5DrawingEngine::DrawBitmap(ServerBitmap* bitmap, const BRect& _bitmapRect, + const BRect& _viewRect, uint32 options) +{ + BRect bitmapRect = _bitmapRect; + BRect viewRect = _viewRect; + double xScale = (bitmapRect.Width() + 1) / (viewRect.Width() + 1); + double yScale = (bitmapRect.Height() + 1) / (viewRect.Height() + 1); + + // constrain rect to passed bitmap bounds + // and transfer the changes to the viewRect with the right scale + BRect actualBitmapRect = bitmap->Bounds(); + if (bitmapRect.left < actualBitmapRect.left) { + float diff = actualBitmapRect.left - bitmapRect.left; + viewRect.left += diff / xScale; + bitmapRect.left = actualBitmapRect.left; + } + if (bitmapRect.top < actualBitmapRect.top) { + float diff = actualBitmapRect.top - bitmapRect.top; + viewRect.top += diff / yScale; + bitmapRect.top = actualBitmapRect.top; + } + if (bitmapRect.right > actualBitmapRect.right) { + float diff = bitmapRect.right - actualBitmapRect.right; + viewRect.right -= diff / xScale; + bitmapRect.right = actualBitmapRect.right; + } + if (bitmapRect.bottom > actualBitmapRect.bottom) { + float diff = bitmapRect.bottom - actualBitmapRect.bottom; + viewRect.bottom -= diff / yScale; + bitmapRect.bottom = actualBitmapRect.bottom; + } + + BRegion clippedRegion(viewRect); + clippedRegion.IntersectWith(&fClippingRegion); + + int32 rectCount = clippedRegion.CountRects(); + if (rectCount == 0) + return; + + if (rectCount > 1 || (rectCount == 1 && clippedRegion.RectAt(0) != viewRect) + || viewRect.Width() < bitmapRect.Width() + || viewRect.Height() < bitmapRect.Height()) { + UtilityBitmap** bitmaps; + if (_ExtractBitmapRegions(*bitmap, options, bitmapRect, viewRect, + xScale, yScale, clippedRegion, bitmaps) != B_OK) { + return; + } + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_DRAW_BITMAP_RECTS); + message.Add(fToken); + message.Add(options); + message.Add(bitmap->ColorSpace()); + message.Add(bitmap->Flags()); + message.Add(rectCount); + + for (int32 i = 0; i < rectCount; i++) { + message.Add(clippedRegion.RectAt(i)); + message.AddBitmap(*bitmaps[i], true); + delete bitmaps[i]; + } + + free(bitmaps); + return; + } + + // TODO: we may want to cache/checksum bitmaps + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_DRAW_BITMAP); + message.Add(fToken); + message.Add(bitmapRect); + message.Add(viewRect); + message.Add(options); + message.AddBitmap(*bitmap); +} + + +void +HTML5DrawingEngine::DrawArc(BRect rect, const float& angle, const float& span, + bool filled) +{ + BRect bounds = rect; + if (!filled) + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(filled ? RP_FILL_ARC : RP_STROKE_ARC); + message.Add(fToken); + message.Add(rect); + message.Add(angle); + message.Add(span); +} + +void +HTML5DrawingEngine::FillArc(BRect rect, const float& angle, const float& span, + const BGradient& gradient) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_ARC_GRADIENT); + message.Add(fToken); + message.Add(rect); + message.Add(angle); + message.Add(span); + message.AddGradient(gradient); +} + + +void +HTML5DrawingEngine::DrawBezier(BPoint* points, bool filled) +{ + BRect bounds = _BuildBounds(points, 4); + if (!filled) + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(filled ? RP_FILL_BEZIER : RP_STROKE_BEZIER); + message.Add(fToken); + message.AddList(points, 4); +} + + +void +HTML5DrawingEngine::FillBezier(BPoint* points, const BGradient& gradient) +{ + BRect bounds = _BuildBounds(points, 4); + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_BEZIER_GRADIENT); + message.Add(fToken); + message.AddList(points, 4); + message.AddGradient(gradient); +} + + +void +HTML5DrawingEngine::DrawEllipse(BRect rect, bool filled) +{ + BRect bounds = rect; + if (!filled) + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(filled ? RP_FILL_ELLIPSE : RP_STROKE_ELLIPSE); + message.Add(fToken); + message.Add(rect); +} + + +void +HTML5DrawingEngine::FillEllipse(BRect rect, const BGradient& gradient) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_ELLIPSE_GRADIENT); + message.Add(fToken); + message.Add(rect); + message.AddGradient(gradient); +} + + +void +HTML5DrawingEngine::DrawPolygon(BPoint* pointList, int32 numPoints, + BRect bounds, bool filled, bool closed) +{ + BRect clipBounds = bounds; + if (!filled) + clipBounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(clipBounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(filled ? RP_FILL_POLYGON : RP_STROKE_POLYGON); + message.Add(fToken); + message.Add(bounds); + message.Add(closed); + message.Add(numPoints); + for (int32 i = 0; i < numPoints; i++) + message.Add(pointList[i]); +} + + +void +HTML5DrawingEngine::FillPolygon(BPoint* pointList, int32 numPoints, + BRect bounds, const BGradient& gradient, bool closed) +{ + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_POLYGON_GRADIENT); + message.Add(fToken); + message.Add(bounds); + message.Add(closed); + message.Add(numPoints); + for (int32 i = 0; i < numPoints; i++) + message.Add(pointList[i]); + message.AddGradient(gradient); +} + + +// #pragma mark - rgb_color versions + + +void +HTML5DrawingEngine::StrokePoint(const BPoint& point, const rgb_color& color) +{ + BRect bounds(point, point); + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_STROKE_POINT_COLOR); + message.Add(fToken); + message.Add(point); + message.Add(color); +} + + +void +HTML5DrawingEngine::StrokeLine(const BPoint& start, const BPoint& end, + const rgb_color& color) +{ + BPoint points[2] = { start, end }; + BRect bounds = _BuildBounds(points, 2); + + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_STROKE_LINE_1PX_COLOR); + message.Add(fToken); + message.AddList(points, 2); + message.Add(color); +} + + +void +HTML5DrawingEngine::StrokeRect(BRect rect, const rgb_color &color) +{ + BRect bounds = rect; + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_STROKE_RECT_1PX_COLOR); + message.Add(fToken); + message.Add(rect); + message.Add(color); +} + + +void +HTML5DrawingEngine::FillRect(BRect rect, const rgb_color& color) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_RECT_COLOR); + message.Add(fToken); + message.Add(rect); + message.Add(color); +} + + +void +HTML5DrawingEngine::FillRegion(BRegion& region, const rgb_color& color) +{ + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_REGION_COLOR_NO_CLIPPING); + message.AddRegion(region); + message.Add(color); +} + + +// #pragma mark - DrawState versions + + +void +HTML5DrawingEngine::StrokeRect(BRect rect) +{ + BRect bounds = rect; + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_STROKE_RECT); + message.Add(fToken); + message.Add(rect); +} + + +void +HTML5DrawingEngine::FillRect(BRect rect) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_RECT); + message.Add(fToken); + message.Add(rect); +} + + +void +HTML5DrawingEngine::FillRect(BRect rect, const BGradient& gradient) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_RECT_GRADIENT); + message.Add(fToken); + message.Add(rect); + message.AddGradient(gradient); +} + + +void +HTML5DrawingEngine::FillRegion(BRegion& region) +{ + BRegion clippedRegion = region; + clippedRegion.IntersectWith(&fClippingRegion); + if (clippedRegion.CountRects() == 0) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_REGION); + message.Add(fToken); + message.AddRegion(clippedRegion.CountRects() < region.CountRects() + ? clippedRegion : region); +} + + +void +HTML5DrawingEngine::FillRegion(BRegion& region, const BGradient& gradient) +{ + BRegion clippedRegion = region; + clippedRegion.IntersectWith(&fClippingRegion); + if (clippedRegion.CountRects() == 0) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_REGION_GRADIENT); + message.Add(fToken); + message.AddRegion(clippedRegion.CountRects() < region.CountRects() + ? clippedRegion : region); + message.AddGradient(gradient); +} + + +void +HTML5DrawingEngine::DrawRoundRect(BRect rect, float xRadius, float yRadius, + bool filled) +{ + BRect bounds = rect; + if (!filled) + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(filled ? RP_FILL_ROUND_RECT : RP_STROKE_ROUND_RECT); + message.Add(fToken); + message.Add(rect); + message.Add(xRadius); + message.Add(yRadius); +} + + +void +HTML5DrawingEngine::FillRoundRect(BRect rect, float xRadius, float yRadius, + const BGradient& gradient) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_ROUND_RECT_GRADIENT); + message.Add(fToken); + message.Add(rect); + message.Add(xRadius); + message.Add(yRadius); + message.AddGradient(gradient); +} + + +void +HTML5DrawingEngine::DrawShape(const BRect& bounds, int32 opCount, + const uint32* opList, int32 pointCount, const BPoint* pointList, + bool filled, const BPoint& viewToScreenOffset, float viewScale) +{ + BRect clipBounds = bounds; + if (!filled) + clipBounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(clipBounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(filled ? RP_FILL_SHAPE : RP_STROKE_SHAPE); + message.Add(fToken); + message.Add(bounds); + message.Add(opCount); + message.AddList(opList, opCount); + message.Add(pointCount); + message.AddList(pointList, pointCount); + message.Add(viewToScreenOffset); + message.Add(viewScale); +} + + +void +HTML5DrawingEngine::FillShape(const BRect& bounds, int32 opCount, + const uint32* opList, int32 pointCount, const BPoint* pointList, + const BGradient& gradient, const BPoint& viewToScreenOffset, + float viewScale) +{ + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_SHAPE_GRADIENT); + message.Add(fToken); + message.Add(bounds); + message.Add(opCount); + message.AddList(opList, opCount); + message.Add(pointCount); + message.AddList(pointList, pointCount); + message.AddGradient(gradient); + message.Add(viewToScreenOffset); + message.Add(viewScale); +} + + +void +HTML5DrawingEngine::DrawTriangle(BPoint* points, const BRect& bounds, + bool filled) +{ + BRect clipBounds = bounds; + if (!filled) + clipBounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(clipBounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(filled ? RP_FILL_TRIANGLE : RP_STROKE_TRIANGLE); + message.Add(fToken); + message.AddList(points, 3); + message.Add(bounds); +} + + +void +HTML5DrawingEngine::FillTriangle(BPoint* points, const BRect& bounds, + const BGradient& gradient) +{ + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_TRIANGLE_GRADIENT); + message.Add(fToken); + message.Add(points[0]); + message.Add(points[1]); + message.Add(points[2]); + message.Add(bounds); + message.AddGradient(gradient); +} + + +void +HTML5DrawingEngine::StrokeLine(const BPoint &start, const BPoint &end) +{ + BPoint points[2] = { start, end }; + BRect bounds = _BuildBounds(points, 2); + + if (!fClippingRegion.Intersects(bounds)) + return; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_STROKE_LINE); + message.Add(fToken); + message.AddList(points, 2); +} + + +void +HTML5DrawingEngine::StrokeLineArray(int32 numLines, + const ViewLineArrayInfo *lineData) +{ + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_STROKE_LINE_ARRAY); + message.Add(fToken); + message.Add(numLines); + for (int32 i = 0; i < numLines; i++) + message.AddArrayLine(lineData[i]); +} + + +// #pragma mark - string functions + + +BPoint +HTML5DrawingEngine::DrawString(const char* string, int32 length, + const BPoint& point, escapement_delta* delta) +{ + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + + message.Start(RP_DRAW_STRING); + message.Add(fToken); + message.Add(point); + message.AddString(string, length); + message.Add(delta != NULL); + if (delta != NULL) + message.AddList(delta, length); + + status_t result = _AddCallback(); + if (message.Flush() != B_OK) + return point; + + if (result != B_OK) + return point; + + do { + result = acquire_sem_etc(fResultNotify, 1, B_RELATIVE_TIMEOUT, + 1 * 1000 * 1000); + } while (result == B_INTERRUPTED); + + if (result != B_OK) + return point; + + return fDrawStringResult; +} + + +BPoint +HTML5DrawingEngine::DrawString(const char* string, int32 length, + const BPoint* offsets) +{ + // Guaranteed to have at least one point. + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + + message.Start(RP_DRAW_STRING_WITH_OFFSETS); + message.Add(fToken); + message.AddString(string, length); + message.AddList(offsets, UTF8CountChars(string, length)); + + status_t result = _AddCallback(); + if (message.Flush() != B_OK) + return offsets[0]; + + if (result != B_OK) + return offsets[0]; + + do { + result = acquire_sem_etc(fResultNotify, 1, B_RELATIVE_TIMEOUT, + 1 * 1000 * 1000); + } while (result == B_INTERRUPTED); + + if (result != B_OK) + return offsets[0]; + + return fDrawStringResult; +} + + +float +HTML5DrawingEngine::StringWidth(const char* string, int32 length, + escapement_delta* delta) +{ + // TODO: decide if really needed and use callback if so + return fState.Font().StringWidth(string, length, delta); +} + + +// #pragma mark - + + +status_t +HTML5DrawingEngine::ReadBitmap(ServerBitmap* bitmap, bool drawCursor, + BRect bounds) +{ + if (_AddCallback() != B_OK) + return B_UNSUPPORTED; + + CanvasMessage message(NULL, fHWInterface->SendBuffer()); + + message.Start(RP_READ_BITMAP); + message.Add(fToken); + message.Add(bounds); + message.Add(drawCursor); + if (message.Flush() != B_OK) + return B_UNSUPPORTED; + + status_t result; + do { + result = acquire_sem_etc(fResultNotify, 1, B_RELATIVE_TIMEOUT, + 100 * 1000 * 1000); + } while (result == B_INTERRUPTED); + + if (result != B_OK) + return result; + + BBitmap* read = fReadBitmapResult; + if (read == NULL) + return B_UNSUPPORTED; + + result = bitmap->ImportBits(read->Bits(), read->BitsLength(), + read->BytesPerRow(), read->ColorSpace()); + delete read; + return result; +} + + +// #pragma mark - + + +status_t +HTML5DrawingEngine::_AddCallback() +{ + if (fCallbackAdded) + return B_OK; + + if (fResultNotify < 0) + fResultNotify = create_sem(0, "drawing engine result"); + if (fResultNotify < 0) + return fResultNotify; + + status_t result = fHWInterface->AddCallback(fToken, &_DrawingEngineResult, + this); + + fCallbackAdded = result == B_OK; + return result; +} + + +bool +HTML5DrawingEngine::_DrawingEngineResult(void* cookie, CanvasMessage& message) +{ + HTML5DrawingEngine* engine = (HTML5DrawingEngine*)cookie; + + switch (message.Code()) { + case RP_DRAW_STRING_RESULT: + if (message.Read(engine->fDrawStringResult) != B_OK) + return false; + break; + + case RP_STRING_WIDTH_RESULT: + if (message.Read(engine->fStringWidthResult) != B_OK) + return false; + break; + + case RP_READ_BITMAP_RESULT: + if (message.ReadBitmap(&engine->fReadBitmapResult) != B_OK) + return false; + break; + + default: + return false; + } + + release_sem(engine->fResultNotify); + return true; +} + + +BRect +HTML5DrawingEngine::_BuildBounds(BPoint* points, int32 pointCount) +{ + BRect bounds(1000000, 1000000, 0, 0); + for (int32 i = 0; i < pointCount; i++) { + bounds.left = min_c(bounds.left, points[i].x); + bounds.top = min_c(bounds.top, points[i].y); + bounds.right = max_c(bounds.right, points[i].x); + bounds.bottom = max_c(bounds.bottom, points[i].y); + } + + return bounds; +} + + +status_t +HTML5DrawingEngine::_ExtractBitmapRegions(ServerBitmap& bitmap, uint32 options, + const BRect& bitmapRect, const BRect& viewRect, double xScale, + double yScale, BRegion& region, UtilityBitmap**& bitmaps) +{ + int32 rectCount = region.CountRects(); + bitmaps = (UtilityBitmap**)malloc(rectCount * sizeof(UtilityBitmap*)); + if (bitmaps == NULL) + return B_NO_MEMORY; + + for (int32 i = 0; i < rectCount; i++) { + BRect sourceRect = region.RectAt(i).OffsetByCopy(-viewRect.LeftTop()); + int32 targetWidth = (int32)(sourceRect.Width() + 1.5); + int32 targetHeight = (int32)(sourceRect.Height() + 1.5); + + if (xScale != 1.0) { + sourceRect.left = (int32)(sourceRect.left * xScale + 0.5); + sourceRect.right = (int32)(sourceRect.right * xScale + 0.5); + if (xScale < 1.0) + targetWidth = (int32)(sourceRect.Width() + 1.5); + } + + if (yScale != 1.0) { + sourceRect.top = (int32)(sourceRect.top * yScale + 0.5); + sourceRect.bottom = (int32)(sourceRect.bottom * yScale + 0.5); + if (yScale < 1.0) + targetHeight = (int32)(sourceRect.Height() + 1.5); + } + + sourceRect.OffsetBy(bitmapRect.LeftTop()); + // sourceRect is now the part of the bitmap we want copied + + status_t result = B_OK; + if ((xScale > 1.0 || yScale > 1.0) + && (targetWidth * targetHeight < (int32)(sourceRect.Width() + 1.5) + * (int32)(sourceRect.Height() + 1.5))) { + // the target bitmap is smaller than the source, scale it locally + // and send over the smaller version to avoid sending any extra data + if (fBitmapDrawingEngine == NULL) { + fBitmapDrawingEngine + = new(std::nothrow) BitmapDrawingEngine(B_RGBA32); + if (fBitmapDrawingEngine == NULL) + result = B_NO_MEMORY; + } + + if (result == B_OK) { + result = fBitmapDrawingEngine->SetSize(targetWidth, + targetHeight); + } + + if (result == B_OK) { + fBitmapDrawingEngine->SetDrawingMode(B_OP_COPY); + + switch (bitmap.ColorSpace()) { + case B_RGBA32: + case B_RGBA32_BIG: + case B_RGBA15: + case B_RGBA15_BIG: + break; + + default: + { + // we need to clear the background if there may be + // transparency through transparent magic (we use + // B_OP_COPY when we draw alpha enabled bitmaps, so we + // don't need to clear there) + // TODO: this is not actually correct, as we're going to + // loose the transparency with the conversion to the + // original non-alpha colorspace happening in + // ExportToBitmap + rgb_color background = { 0, 0, 0, 0 }; + fBitmapDrawingEngine->FillRect( + BRect(0, 0, targetWidth - 1, targetHeight -1), + background); + fBitmapDrawingEngine->SetDrawingMode(B_OP_OVER); + break; + } + } + + fBitmapDrawingEngine->DrawBitmap(&bitmap, sourceRect, + BRect(0, 0, targetWidth - 1, targetHeight - 1), options); + bitmaps[i] = fBitmapDrawingEngine->ExportToBitmap(targetWidth, + targetHeight, bitmap.ColorSpace()); + if (bitmaps[i] == NULL) + result = B_NO_MEMORY; + } + } else { + // source is smaller or equal target, extract the relevant rects + // directly without any scaling and conversion + targetWidth = (int32)(sourceRect.Width() + 1.5); + targetHeight = (int32)(sourceRect.Height() + 1.5); + + bitmaps[i] = new(std::nothrow) UtilityBitmap( + BRect(0, 0, targetWidth - 1, targetHeight - 1), + bitmap.ColorSpace(), 0); + if (bitmaps[i] == NULL) + result = B_NO_MEMORY; + + result = bitmaps[i]->ImportBits(bitmap.Bits(), bitmap.BitsLength(), + bitmap.BytesPerRow(), bitmap.ColorSpace(), sourceRect.LeftTop(), + BPoint(0, 0), targetWidth, targetHeight); + if (result != B_OK) + delete bitmaps[i]; + } + + if (result != B_OK) { + for (int32 j = 0; j < i; j++) + delete bitmaps[j]; + free(bitmaps); + return result; + } + } + + return B_OK; +} diff --git a/src/servers/app/drawing/html5/HTML5DrawingEngine.h b/src/servers/app/drawing/html5/HTML5DrawingEngine.h new file mode 100644 index 0000000000..5a2c547f75 --- /dev/null +++ b/src/servers/app/drawing/html5/HTML5DrawingEngine.h @@ -0,0 +1,180 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + * François Revol + */ +#ifndef HTML5_DRAWING_ENGINE_H +#define HTML5_DRAWING_ENGINE_H + +#include "DrawingEngine.h" +#include "DrawState.h" +#include "HTML5HWInterface.h" +#include "ServerFont.h" + +class BPoint; +class BRect; +class BRegion; + +class BitmapDrawingEngine; +class ServerBitmap; + +class HTML5DrawingEngine : public DrawingEngine { +public: + HTML5DrawingEngine( + HTML5HWInterface* interface); + virtual ~HTML5DrawingEngine(); + + // HWInterfaceListener interface + virtual void FrameBufferChanged(); + + virtual void SetCopyToFrontEnabled(bool enabled); + + // for screen shots + virtual status_t ReadBitmap(ServerBitmap* bitmap, + bool drawCursor, BRect bounds); + + // clipping for all drawing functions, passing a NULL region + // will remove any clipping (drawing allowed everywhere) + virtual void ConstrainClippingRegion(const BRegion* region); + + virtual void SetDrawState(const DrawState* state, + int32 xOffset = 0, int32 yOffset = 0); + + virtual void SetHighColor(const rgb_color& color); + virtual void SetLowColor(const rgb_color& color); + virtual void SetPenSize(float size); + virtual void SetStrokeMode(cap_mode lineCap, + join_mode joinMode, float miterLimit); + virtual void SetPattern(const struct pattern& pattern); + virtual void SetDrawingMode(drawing_mode mode); + virtual void SetDrawingMode(drawing_mode mode, + drawing_mode& oldMode); + virtual void SetBlendingMode(source_alpha srcAlpha, + alpha_function alphaFunc); + virtual void SetFont(const ServerFont& font); + virtual void SetFont(const DrawState* state); + + // drawing functions + virtual void InvertRect(BRect rect); + + virtual void DrawBitmap(ServerBitmap* bitmap, + const BRect& bitmapRect, + const BRect& viewRect, uint32 options = 0); + + // drawing primitives + virtual void DrawArc(BRect rect, const float& angle, + const float& span, bool filled); + virtual void FillArc(BRect rect, const float& angle, + const float& span, + const BGradient& gradient); + + virtual void DrawBezier(BPoint* points, bool filled); + virtual void FillBezier(BPoint* points, + const BGradient& gradient); + + virtual void DrawEllipse(BRect rect, bool filled); + virtual void FillEllipse(BRect rect, + const BGradient& gradient); + + virtual void DrawPolygon(BPoint* pointList, int32 numPoints, + BRect bounds, bool filled, bool closed); + virtual void FillPolygon(BPoint* pointList, int32 numPoints, + BRect bounds, const BGradient& gradient, + bool closed); + + // these rgb_color versions are used internally by the server + virtual void StrokePoint(const BPoint& point, + const rgb_color& color); + virtual void StrokeRect(BRect rect, const rgb_color &color); + virtual void FillRect(BRect rect, const rgb_color &color); + virtual void FillRegion(BRegion& region, + const rgb_color& color); + + virtual void StrokeRect(BRect rect); + virtual void FillRect(BRect rect); + virtual void FillRect(BRect rect, const BGradient& gradient); + + virtual void FillRegion(BRegion& region); + virtual void FillRegion(BRegion& region, + const BGradient& gradient); + + virtual void DrawRoundRect(BRect rect, float xRadius, + float yRadius, bool filled); + virtual void FillRoundRect(BRect rect, float xRadius, + float yRadius, const BGradient& gradient); + + virtual void DrawShape(const BRect& bounds, + int32 opCount, const uint32* opList, + int32 pointCount, const BPoint* pointList, + bool filled, + const BPoint& viewToScreenOffset, + float viewScale); + virtual void FillShape(const BRect& bounds, + int32 opCount, const uint32* opList, + int32 pointCount, const BPoint* pointList, + const BGradient& gradient, + const BPoint& viewToScreenOffset, + float viewScale); + + virtual void DrawTriangle(BPoint* points, + const BRect& bounds, bool filled); + virtual void FillTriangle(BPoint* points, + const BRect& bounds, + const BGradient& gradient); + + // these versions are used by the Decorator + virtual void StrokeLine(const BPoint& start, + const BPoint& end, const rgb_color& color); + virtual void StrokeLine(const BPoint& start, + const BPoint& end); + virtual void StrokeLineArray(int32 numlines, + const ViewLineArrayInfo* data); + + // returns the pen position behind the (virtually) drawn string + virtual BPoint DrawString(const char* string, int32 length, + const BPoint& point, + escapement_delta* delta = NULL); +virtual BPoint DrawString(const char* string, int32 length, + const BPoint* offsets); + + virtual float StringWidth(const char* string, int32 length, + escapement_delta* delta = NULL); + + // software rendering backend invoked by CopyRegion() for the sorted + // individual rects + virtual BRect CopyRect(BRect rect, int32 xOffset, + int32 yOffset) const; + +private: + status_t _AddCallback(); + + static bool _DrawingEngineResult(void* cookie, + CanvasMessage& message); + + BRect _BuildBounds(BPoint* points, int32 pointCount); + status_t _ExtractBitmapRegions(ServerBitmap& bitmap, + uint32 options, const BRect& bitmapRect, + const BRect& viewRect, double xScale, + double yScale, BRegion& region, + UtilityBitmap**& bitmaps); + + HTML5HWInterface* fHWInterface; + uint32 fToken; + + DrawState fState; + BRegion fClippingRegion; + float fExtendWidth; + + bool fCallbackAdded; + sem_id fResultNotify; + BPoint fDrawStringResult; + float fStringWidthResult; + BBitmap* fReadBitmapResult; + + BitmapDrawingEngine* fBitmapDrawingEngine; +}; + +#endif // HTML5_DRAWING_ENGINE_H diff --git a/src/servers/app/drawing/html5/HTML5HWInterface.cpp b/src/servers/app/drawing/html5/HTML5HWInterface.cpp new file mode 100644 index 0000000000..1d7f2137b2 --- /dev/null +++ b/src/servers/app/drawing/html5/HTML5HWInterface.cpp @@ -0,0 +1,612 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + * François Revol + */ + +#include "HTML5HWInterface.h" +#include "HTML5DrawingEngine.h" +#include "CanvasEventStream.h" +#include "CanvasMessage.h" + +#include "WebHandler.h" +#include "WebServer.h" +#include "WebWorker.h" +//#include "NetReceiver.h" +//#include "NetSender.h" +#include "StreamingRingBuffer.h" + +#include "desktop.html.h" +#include "haiku.js.h" + +#include +#include + +#include +#include + + +#define TRACE(x...) debug_printf("HTML5HWInterface: "x) +#define TRACE_ALWAYS(x...) debug_printf("HTML5HWInterface: "x) +#define TRACE_ERROR(x...) debug_printf("HTML5HWInterface: "x) + + +struct callback_info { + uint32 token; + HTML5HWInterface::CallbackFunction callback; + void* cookie; +}; + + +HTML5HWInterface::HTML5HWInterface(const char* target) + : + HWInterface(), + fTarget(target), + fRemoteHost(NULL), + fRemotePort(10900), + fIsConnected(false), + fProtocolVersion(100), + fConnectionSpeed(0), + fListenPort(10901), + fReceiveEndpoint(NULL), + fSendBuffer(NULL), + fReceiveBuffer(NULL), + fServer(NULL), +// fSender(NULL), +// fReceiver(NULL), + fEventThread(-1), + fEventStream(NULL), + fCallbackLocker("callback locker") +{ + fDisplayMode.virtual_width = 640; + fDisplayMode.virtual_height = 480; + fDisplayMode.space = B_RGB32; + + //TODO: Cleanup; parse host ?? + fRemoteHost = strdup(fTarget); + if (strncmp(fRemoteHost, "html5:", 6) != 0) { + fInitStatus = B_BAD_VALUE; + return; + } + char *portStart = fRemoteHost + 5;//strchr(fRemoteHost + 6, ':'); + if (portStart != NULL) { + portStart[0] = 0; + portStart++; + if (sscanf(portStart, "%lu", &fRemotePort) != 1) { + fInitStatus = B_BAD_VALUE; + return; + } + + fListenPort = fRemotePort; + } + + fReceiveEndpoint = new(std::nothrow) BNetEndpoint(); + if (fReceiveEndpoint == NULL) { + fInitStatus = B_NO_MEMORY; + return; + } + + fInitStatus = fReceiveEndpoint->Bind(fListenPort); + if (fInitStatus != B_OK) + return; + + fSendBuffer = new(std::nothrow) StreamingRingBuffer(16 * 1024); + if (fSendBuffer == NULL) { + fInitStatus = B_NO_MEMORY; + return; + } + + fInitStatus = fSendBuffer->InitCheck(); + if (fInitStatus != B_OK) + return; + + fReceiveBuffer = new(std::nothrow) StreamingRingBuffer(16 * 1024); + if (fReceiveBuffer == NULL) { + fInitStatus = B_NO_MEMORY; + return; + } + + fInitStatus = fReceiveBuffer->InitCheck(); + if (fInitStatus != B_OK) + return; + + fServer = new(std::nothrow) WebServer(fReceiveEndpoint); + if (fServer == NULL) { + fInitStatus = B_NO_MEMORY; + return; + } + + WebHandler *handler; + handler = new(std::nothrow) WebHandler("output", fSendBuffer); + if (handler == NULL) { + fInitStatus = B_NO_MEMORY; + return; + } + handler->SetMultipart(); + handler->SetType("multipart/x-mixed-replace;boundary=x"); + fServer->AddHandler(handler); + + handler = new(std::nothrow) WebHandler("input", fReceiveBuffer); + if (handler == NULL) { + fInitStatus = B_NO_MEMORY; + return; + } + fServer->AddHandler(handler); + + //static const char desktop_html[] = "Haiku" + // ""; + handler = new(std::nothrow) WebHandler("desktop.html", + new(std::nothrow) BMemoryIO(desktop_html, sizeof(desktop_html) - 1)); + if (handler == NULL) { + fInitStatus = B_NO_MEMORY; + return; + } + fServer->AddHandler(handler); + + //static const char haiku_js[] = "window.alert('plop');\n"; + handler = new(std::nothrow) WebHandler("haiku.js", + new(std::nothrow) BMemoryIO(haiku_js, sizeof(haiku_js) - 1)); + if (handler == NULL) { + fInitStatus = B_NO_MEMORY; + return; + } + fServer->AddHandler(handler); + + + fEventStream = new(std::nothrow) CanvasEventStream(); + if (fEventStream == NULL) { + fInitStatus = B_NO_MEMORY; + return; + } + +// fInitStatus = _Connect(); +// if (fInitStatus != B_OK) +// return; + fDisplayMode.virtual_width = 800; + fDisplayMode.virtual_height = 600; + + + fEventThread = spawn_thread(_EventThreadEntry, "HTML5 event thread", + B_NORMAL_PRIORITY, this); + if (fEventThread < 0) { + fInitStatus = fEventThread; + return; + } + + resume_thread(fEventThread); +} + + +HTML5HWInterface::~HTML5HWInterface() +{ +// delete fReceiver; + delete fReceiveBuffer; + + delete fSendBuffer; +// delete fSender; + + delete fReceiveEndpoint; +// delete fSendEndpoint; + delete fServer; + + delete fEventStream; + + free(fRemoteHost); +} + + +status_t +HTML5HWInterface::Initialize() +{ + return fInitStatus; +} + + +status_t +HTML5HWInterface::Shutdown() +{ + _Disconnect(); + return B_OK; +} + + +DrawingEngine* +HTML5HWInterface::CreateDrawingEngine() +{ + return new(std::nothrow) HTML5DrawingEngine(this); +} + + +EventStream* +HTML5HWInterface::CreateEventStream() +{ + return fEventStream; +} + + +status_t +HTML5HWInterface::AddCallback(uint32 token, CallbackFunction callback, + void* cookie) +{ + BAutolock lock(fCallbackLocker); + int32 index = fCallbacks.BinarySearchIndexByKey(token, &_CallbackCompare); + if (index >= 0) + return B_NAME_IN_USE; + + callback_info* info = new(std::nothrow) callback_info; + if (info == NULL) + return B_NO_MEMORY; + + info->token = token; + info->callback = callback; + info->cookie = cookie; + + fCallbacks.AddItem(info, -index - 1); + return B_OK; +} + + +bool +HTML5HWInterface::RemoveCallback(uint32 token) +{ + BAutolock lock(fCallbackLocker); + int32 index = fCallbacks.BinarySearchIndexByKey(token, &_CallbackCompare); + if (index < 0) + return false; + + delete fCallbacks.RemoveItemAt(index); + return true; +} + + +callback_info* +HTML5HWInterface::_FindCallback(uint32 token) +{ + BAutolock lock(fCallbackLocker); + return fCallbacks.BinarySearchByKey(token, &_CallbackCompare); +} + + +int +HTML5HWInterface::_CallbackCompare(const uint32* key, + const callback_info* info) +{ + if (info->token == *key) + return 0; + + if (info->token < *key) + return -1; + + return 1; +} + + +int32 +HTML5HWInterface::_EventThreadEntry(void* data) +{ + return ((HTML5HWInterface*)data)->_EventThread(); +} + + +status_t +HTML5HWInterface::_EventThread() +{ + CanvasMessage message(fReceiveBuffer, fSendBuffer); + while (true) { + uint16 code; + status_t result = message.NextMessage(code); + if (result != B_OK) { + TRACE_ERROR("failed to read message from receiver: %s\n", + strerror(result)); + return result; + } + + TRACE("got message code %u with %lu bytes\n", code, message.DataLeft()); + + if (code >= RP_MOUSE_MOVED && code <= RP_MODIFIERS_CHANGED) { + // an input event, dispatch to the event stream + if (fEventStream->EventReceived(message)) + continue; + } + + switch (code) { + case RP_UPDATE_DISPLAY_MODE: + { + // TODO: implement, we only handle it in the context of the + // initial mode setup on connect + break; + } + + default: + { + uint32 token; + if (message.Read(token) == B_OK) { + callback_info* info = _FindCallback(token); + if (info != NULL && info->callback(info->cookie, message)) + break; + } + + TRACE_ERROR("unhandled HTML5 event code %u\n", code); + break; + } + } + } +} + + +status_t +HTML5HWInterface::_Connect() +{ +#if 0 + TRACE("connecting to host \"%s\" port %lu\n", fRemoteHost, fRemotePort); + status_t result = fSendEndpoint->Connect(fRemoteHost, (uint16)fRemotePort); + if (result != B_OK) { + TRACE_ERROR("failed to connect to host \"%s\" port %lu\n", fRemoteHost, + fRemotePort); + return result; + } + + CanvasMessage message(fReceiveBuffer, fSendBuffer); + message.Start(RP_INIT_CONNECTION); + message.Add(fListenPort); + result = message.Flush(); + if (result != B_OK) { + TRACE_ERROR("failed to send init connection message\n"); + return result; + } + + uint16 code; + result = message.NextMessage(code); + if (result != B_OK) { + TRACE_ERROR("failed to read message from receiver: %s\n", + strerror(result)); + return result; + } + + TRACE("code %u with %lu bytes of data\n", code, message.DataLeft()); + if (code != RP_UPDATE_DISPLAY_MODE) { + TRACE_ERROR("invalid connection init code %u\n", code); + return B_ERROR; + } + + int32 width, height; + message.Read(width); + result = message.Read(height); + if (result != B_OK) { + TRACE_ERROR("failed to get initial display mode\n"); + return result; + } + + fDisplayMode.virtual_width = width; + fDisplayMode.virtual_height = height; +#endif + return B_OK; +} + + +void +HTML5HWInterface::_Disconnect() +{ + if (fIsConnected) { + CanvasMessage message(NULL, fSendBuffer); + message.Start(RP_CLOSE_CONNECTION); + message.Flush(); + fIsConnected = false; + } + + if (fReceiveEndpoint != NULL) + fReceiveEndpoint->Close(); +} + + +status_t +HTML5HWInterface::SetMode(const display_mode& mode) +{ + // The display mode depends on the screen resolution of the client, we + // don't allow to change it. + return B_UNSUPPORTED; +} + + +void +HTML5HWInterface::GetMode(display_mode* mode) +{ + if (mode == NULL || !ReadLock()) + return; + + *mode = fDisplayMode; + ReadUnlock(); +} + + +status_t +HTML5HWInterface::GetDeviceInfo(accelerant_device_info* info) +{ + if (!ReadLock()) + return B_ERROR; + + info->version = fProtocolVersion; + info->dac_speed = fConnectionSpeed; + info->memory = 33554432; // 32MB + snprintf(info->name, sizeof(info->name), "Haiku, Inc. HTML5HWInterface"); + snprintf(info->chipset, sizeof(info->chipset), "Haiku, Inc. Chipset"); + snprintf(info->serial_no, sizeof(info->serial_no), fTarget); + + ReadUnlock(); + return B_OK; +} + + +status_t +HTML5HWInterface::GetFrameBufferConfig(frame_buffer_config& config) +{ + // We don't actually have a frame buffer. + return B_UNSUPPORTED; +} + + +status_t +HTML5HWInterface::GetModeList(display_mode** _modes, uint32* _count) +{ + AutoReadLocker _(this); + + display_mode* modes = new(std::nothrow) display_mode[1]; + if (modes == NULL) + return B_NO_MEMORY; + + modes[0] = fDisplayMode; + *_modes = modes; + *_count = 1; + return B_OK; +} + + +status_t +HTML5HWInterface::GetPixelClockLimits(display_mode* mode, uint32* low, + uint32* high) +{ + return B_UNSUPPORTED; +} + + +status_t +HTML5HWInterface::GetTimingConstraints(display_timing_constraints* constraints) +{ + return B_UNSUPPORTED; +} + + +status_t +HTML5HWInterface::ProposeMode(display_mode* candidate, const display_mode* low, + const display_mode* high) +{ + return B_UNSUPPORTED; +} + + +status_t +HTML5HWInterface::SetDPMSMode(uint32 state) +{ + return B_UNSUPPORTED; +} + + +uint32 +HTML5HWInterface::DPMSMode() +{ + return B_UNSUPPORTED; +} + + +uint32 +HTML5HWInterface::DPMSCapabilities() +{ + return 0; +} + + +sem_id +HTML5HWInterface::RetraceSemaphore() +{ + return -1; +} + + +status_t +HTML5HWInterface::WaitForRetrace(bigtime_t timeout) +{ + return B_UNSUPPORTED; +} + + +void +HTML5HWInterface::SetCursor(ServerCursor* cursor) +{ + HWInterface::SetCursor(cursor); + CanvasMessage message(NULL, fSendBuffer); + message.Start(RP_SET_CURSOR); + message.AddCursor(Cursor().Get()); +} + + +void +HTML5HWInterface::SetCursorVisible(bool visible) +{ + HWInterface::SetCursorVisible(visible); + CanvasMessage message(NULL, fSendBuffer); + message.Start(RP_SET_CURSOR_VISIBLE); + message.Add(visible); +} + + +void +HTML5HWInterface::MoveCursorTo(float x, float y) +{ + HWInterface::MoveCursorTo(x, y); + CanvasMessage message(NULL, fSendBuffer); + message.Start(RP_MOVE_CURSOR_TO); + message.Add(x); + message.Add(y); +} + + +void +HTML5HWInterface::SetDragBitmap(const ServerBitmap* bitmap, + const BPoint& offsetFromCursor) +{ + HWInterface::SetDragBitmap(bitmap, offsetFromCursor); + CanvasMessage message(NULL, fSendBuffer); + message.Start(RP_SET_CURSOR); + message.AddCursor(CursorAndDragBitmap().Get()); +} + + +RenderingBuffer* +HTML5HWInterface::FrontBuffer() const +{ + return NULL; +} + + +RenderingBuffer* +HTML5HWInterface::BackBuffer() const +{ + return NULL; +} + + +bool +HTML5HWInterface::IsDoubleBuffered() const +{ + return false; +} + + +status_t +HTML5HWInterface::InvalidateRegion(BRegion& region) +{ + CanvasMessage message(NULL, fSendBuffer); + message.Start(RP_INVALIDATE_REGION); + message.AddRegion(region); + return B_OK; +} + + +status_t +HTML5HWInterface::Invalidate(const BRect& frame) +{ + CanvasMessage message(NULL, fSendBuffer); + message.Start(RP_INVALIDATE_RECT); + message.Add(frame); + return B_OK; +} + + +status_t +HTML5HWInterface::CopyBackToFront(const BRect& frame) +{ + return B_OK; +} diff --git a/src/servers/app/drawing/html5/HTML5HWInterface.h b/src/servers/app/drawing/html5/HTML5HWInterface.h new file mode 100644 index 0000000000..2d6959ba5a --- /dev/null +++ b/src/servers/app/drawing/html5/HTML5HWInterface.h @@ -0,0 +1,131 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + * François Revol + */ +#ifndef HTML5_HW_INTERFACE_H +#define HTML5_HW_INTERFACE_H + +#include "HWInterface.h" + +#include +#include + +class BNetEndpoint; +class StreamingRingBuffer; +class NetSender; +class NetReceiver; +class CanvasEventStream; +class CanvasMessage; +class WebServer; + +struct callback_info; + + +//XXX: CanvasHWInterface ?? +class HTML5HWInterface : public HWInterface { +public: + HTML5HWInterface(const char* target); +virtual ~HTML5HWInterface(); + +virtual status_t Initialize(); +virtual status_t Shutdown(); + +virtual DrawingEngine* CreateDrawingEngine(); +virtual EventStream* CreateEventStream(); + +virtual status_t SetMode(const display_mode& mode); +virtual void GetMode(display_mode* mode); + +virtual status_t GetDeviceInfo(accelerant_device_info* info); +virtual status_t GetFrameBufferConfig( + frame_buffer_config& config); + +virtual status_t GetModeList(display_mode** _modeList, + uint32* _count); +virtual status_t GetPixelClockLimits(display_mode* mode, + uint32* _low, uint32* _high); +virtual status_t GetTimingConstraints( + display_timing_constraints* constraints); +virtual status_t ProposeMode(display_mode* candidate, + const display_mode* low, + const display_mode* high); + +virtual sem_id RetraceSemaphore(); +virtual status_t WaitForRetrace( + bigtime_t timeout = B_INFINITE_TIMEOUT); + +virtual status_t SetDPMSMode(uint32 state); +virtual uint32 DPMSMode(); +virtual uint32 DPMSCapabilities(); + + // cursor handling +virtual void SetCursor(ServerCursor* cursor); +virtual void SetCursorVisible(bool visible); +virtual void MoveCursorTo(float x, float y); +virtual void SetDragBitmap(const ServerBitmap* bitmap, + const BPoint& offsetFormCursor); + + // frame buffer access +virtual RenderingBuffer* FrontBuffer() const; +virtual RenderingBuffer* BackBuffer() const; +virtual bool IsDoubleBuffered() const; + +virtual status_t InvalidateRegion(BRegion& region); +virtual status_t Invalidate(const BRect& frame); +virtual status_t CopyBackToFront(const BRect& frame); + + // drawing engine interface + StreamingRingBuffer* ReceiveBuffer() { return fReceiveBuffer; } + StreamingRingBuffer* SendBuffer() { return fSendBuffer; } + +typedef bool (*CallbackFunction)(void* cookie, CanvasMessage& message); + + status_t AddCallback(uint32 token, + CallbackFunction callback, + void* cookie); + bool RemoveCallback(uint32 token); + +private: + callback_info* _FindCallback(uint32 token); +static int _CallbackCompare(const uint32* key, + const callback_info* info); + +static int32 _EventThreadEntry(void* data); + status_t _EventThread(); + + status_t _Connect(); + void _Disconnect(); + + const char* fTarget; + char* fRemoteHost; + uint32 fRemotePort; + + status_t fInitStatus; + bool fIsConnected; + uint32 fProtocolVersion; + uint32 fConnectionSpeed; + display_mode fDisplayMode; + uint16 fListenPort; + +// BNetEndpoint* fSendEndpoint; + BNetEndpoint* fReceiveEndpoint; + + StreamingRingBuffer* fSendBuffer; + StreamingRingBuffer* fReceiveBuffer; + + WebServer* fServer; +// NetSender* fSender; +// NetReceiver* fReceiver; + + thread_id fEventThread; + CanvasEventStream* fEventStream; + + BLocker fCallbackLocker; + BObjectList fCallbacks; +}; + +#endif // HTML5_HW_INTERFACE_H diff --git a/src/servers/app/drawing/html5/Jamfile b/src/servers/app/drawing/html5/Jamfile new file mode 100644 index 0000000000..611656e782 --- /dev/null +++ b/src/servers/app/drawing/html5/Jamfile @@ -0,0 +1,32 @@ +SubDir HAIKU_TOP src servers app drawing html5 ; + +UseLibraryHeaders agg ; +UsePrivateHeaders app graphics interface kernel shared ; +UsePrivateHeaders [ FDirName graphics common ] ; +UsePrivateSystemHeaders ; + +UseHeaders [ FDirName $(HAIKU_TOP) src servers app ] ; +UseHeaders [ FDirName $(HAIKU_TOP) src servers app font ] ; +UseHeaders [ FDirName $(HAIKU_TOP) src servers app drawing ] ; +UseHeaders [ FDirName $(HAIKU_TOP) src servers app drawing Painter ] ; +UseHeaders [ FDirName $(HAIKU_TOP) src servers app drawing Painter drawing_modes ] ; +UseHeaders [ FDirName $(HAIKU_TOP) src servers app drawing Painter font_support ] ; +UseHeaders $(HAIKU_FREETYPE_HEADERS) : true ; + +StaticLibrary libashtml5.a : + base64.cpp + + #NetReceiver.cpp + #NetSender.cpp + + HTML5DrawingEngine.cpp + CanvasEventStream.cpp + HTML5HWInterface.cpp + CanvasMessage.cpp + + StreamingRingBuffer.cpp + + WebHandler.cpp + WebServer.cpp + WebWorker.cpp +; diff --git a/src/servers/app/drawing/html5/NetSender.cpp b/src/servers/app/drawing/html5/NetSender.cpp new file mode 100644 index 0000000000..0d7cca0d53 --- /dev/null +++ b/src/servers/app/drawing/html5/NetSender.cpp @@ -0,0 +1,75 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include "NetSender.h" + +#include "StreamingRingBuffer.h" + +#include + +#include +#include +#include + +#define TRACE(x...) /*debug_printf("NetSender: "x)*/ +#define TRACE_ERROR(x...) debug_printf("NetSender: "x) + + +NetSender::NetSender(BNetEndpoint *endpoint, StreamingRingBuffer *source) + : + fEndpoint(endpoint), + fSource(source), + fSenderThread(-1), + fStopThread(false) +{ + fSenderThread = spawn_thread(_NetworkSenderEntry, "network sender", + B_NORMAL_PRIORITY, this); + resume_thread(fSenderThread); +} + + +NetSender::~NetSender() +{ + fStopThread = true; + int32 result; + wait_for_thread(fSenderThread, &result); +} + + +int32 +NetSender::_NetworkSenderEntry(void *data) +{ + return ((NetSender *)data)->_NetworkSender(); +} + + +status_t +NetSender::_NetworkSender() +{ + while (!fStopThread) { + uint8 buffer[4096]; + int32 readSize = fSource->Read(buffer, sizeof(buffer), true); + if (readSize < 0) { + TRACE_ERROR("read failed, stopping sender thread: %s\n", + strerror(readSize)); + return readSize; + } + + while (readSize > 0) { + int32 sendSize = fEndpoint->Send(buffer, readSize); + if (sendSize < 0) { + TRACE_ERROR("sending data failed: %s\n", strerror(sendSize)); + return sendSize; + } + + readSize -= sendSize; + } + } + + return B_OK; +} diff --git a/src/servers/app/drawing/html5/NetSender.h b/src/servers/app/drawing/html5/NetSender.h new file mode 100644 index 0000000000..dbc415ee3e --- /dev/null +++ b/src/servers/app/drawing/html5/NetSender.h @@ -0,0 +1,34 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ +#ifndef NET_SENDER_H +#define NET_SENDER_H + +#include +#include + +class BNetEndpoint; +class StreamingRingBuffer; + +class NetSender { +public: + NetSender(BNetEndpoint *endpoint, + StreamingRingBuffer *source); + ~NetSender(); + +private: +static int32 _NetworkSenderEntry(void *data); + status_t _NetworkSender(); + + BNetEndpoint * fEndpoint; + StreamingRingBuffer * fSource; + + thread_id fSenderThread; + bool fStopThread; +}; + +#endif // NET_SENDER_H diff --git a/src/servers/app/drawing/html5/StreamingRingBuffer.cpp b/src/servers/app/drawing/html5/StreamingRingBuffer.cpp new file mode 100644 index 0000000000..601bc2293e --- /dev/null +++ b/src/servers/app/drawing/html5/StreamingRingBuffer.cpp @@ -0,0 +1,176 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include "StreamingRingBuffer.h" + +#include + +#include +#include +#include + +#define TRACE(x...) /*debug_printf("StreamingRingBuffer: "x)*/ +#define TRACE_ERROR(x...) debug_printf("StreamingRingBuffer: "x) + + +StreamingRingBuffer::StreamingRingBuffer(size_t bufferSize) + : + fReaderWaiting(false), + fWriterWaiting(false), + fReaderNotifier(-1), + fWriterNotifier(-1), + fReaderLocker("StreamingRingBuffer reader"), + fWriterLocker("StreamingRingBuffer writer"), + fDataLocker("StreamingRingBuffer data"), + fBuffer(NULL), + fBufferSize(bufferSize), + fReadable(0), + fReadPosition(0), + fWritePosition(0) +{ + fReaderNotifier = create_sem(0, "StreamingRingBuffer read notify"); + fWriterNotifier = create_sem(0, "StreamingRingBuffer write notify"); + + fBuffer = (uint8 *)malloc(fBufferSize); + if (fBuffer == NULL) + fBufferSize = 0; +} + + +StreamingRingBuffer::~StreamingRingBuffer() +{ + delete_sem(fReaderNotifier); + delete_sem(fWriterNotifier); + free(fBuffer); +} + + +status_t +StreamingRingBuffer::InitCheck() +{ + if (fReaderNotifier < 0) + return fReaderNotifier; + if (fWriterNotifier < 0) + return fWriterNotifier; + if (fBuffer == NULL) + return B_NO_MEMORY; + + return B_OK; +} + + +int32 +StreamingRingBuffer::Read(void *buffer, size_t length, bool onlyBlockOnNoData) +{ + BAutolock readerLock(fReaderLocker); + if (!readerLock.IsLocked()) + return B_ERROR; + + BAutolock dataLock(fDataLocker); + if (!dataLock.IsLocked()) + return B_ERROR; + + int32 readSize = 0; + while (length > 0) { + size_t copyLength = min_c(length, fBufferSize - fReadPosition); + copyLength = min_c(copyLength, fReadable); + + if (copyLength == 0) { + if (onlyBlockOnNoData && readSize > 0) + return readSize; + + fReaderWaiting = true; + dataLock.Unlock(); + + status_t result; + do { + TRACE("waiting in reader\n"); + result = acquire_sem(fReaderNotifier); + TRACE("done waiting in reader with status: 0x%08lx\n", result); + } while (result == B_INTERRUPTED); + + if (result != B_OK) + return result; + + if (!dataLock.Lock()) + return B_ERROR; + + continue; + } + + // support discarding input + if (buffer != NULL) { + memcpy(buffer, fBuffer + fReadPosition, copyLength); + buffer = (uint8 *)buffer + copyLength; + } + + fReadPosition = (fReadPosition + copyLength) % fBufferSize; + fReadable -= copyLength; + readSize += copyLength; + length -= copyLength; + + if (fWriterWaiting) { + release_sem_etc(fWriterNotifier, 1, B_DO_NOT_RESCHEDULE); + fWriterWaiting = false; + } + } + + return readSize; +} + + +status_t +StreamingRingBuffer::Write(const void *buffer, size_t length) +{ + BAutolock writerLock(fWriterLocker); + if (!writerLock.IsLocked()) + return B_ERROR; + + BAutolock dataLock(fDataLocker); + if (!dataLock.IsLocked()) + return B_ERROR; + + while (length > 0) { + size_t copyLength = min_c(length, fBufferSize - fWritePosition); + copyLength = min_c(copyLength, fBufferSize - fReadable); + + if (copyLength == 0) { + fWriterWaiting = true; + dataLock.Unlock(); + + status_t result; + do { + TRACE("waiting in writer\n"); + result = acquire_sem(fWriterNotifier); + TRACE("done waiting in writer with status: 0x%08lx\n", result); + } while (result == B_INTERRUPTED); + + if (result != B_OK) + return result; + + if (!dataLock.Lock()) + return B_ERROR; + + continue; + } + + memcpy(fBuffer + fWritePosition, buffer, copyLength); + fWritePosition = (fWritePosition + copyLength) % fBufferSize; + fReadable += copyLength; + + buffer = (uint8 *)buffer + copyLength; + length -= copyLength; + + if (fReaderWaiting) { + release_sem_etc(fReaderNotifier, 1, B_DO_NOT_RESCHEDULE); + fReaderWaiting = false; + } + } + + return B_OK; +} diff --git a/src/servers/app/drawing/html5/StreamingRingBuffer.h b/src/servers/app/drawing/html5/StreamingRingBuffer.h new file mode 100644 index 0000000000..a38744b085 --- /dev/null +++ b/src/servers/app/drawing/html5/StreamingRingBuffer.h @@ -0,0 +1,47 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ +#ifndef STREAMING_RING_BUFFER_H +#define STREAMING_RING_BUFFER_H + +#include +#include +#include + +class StreamingRingBuffer { +public: + StreamingRingBuffer(size_t bufferSize); + ~StreamingRingBuffer(); + + status_t InitCheck(); + + // blocking read and write + int32 Read(void *buffer, size_t length, + bool onlyBlockOnNoData = false); + status_t Write(const void *buffer, size_t length); + +private: + bool _Lock(); + void _Unlock(); + + bool fReaderWaiting; + bool fWriterWaiting; + sem_id fReaderNotifier; + sem_id fWriterNotifier; + + BLocker fReaderLocker; + BLocker fWriterLocker; + BLocker fDataLocker; + + uint8 * fBuffer; + size_t fBufferSize; + size_t fReadable; + int32 fReadPosition; + int32 fWritePosition; +}; + +#endif // STREAMING_RING_BUFFER_H diff --git a/src/servers/app/drawing/html5/WebHandler.cpp b/src/servers/app/drawing/html5/WebHandler.cpp new file mode 100644 index 0000000000..1d859b87c8 --- /dev/null +++ b/src/servers/app/drawing/html5/WebHandler.cpp @@ -0,0 +1,85 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + * François Revol + */ + +#include "WebHandler.h" + +#include "StreamingRingBuffer.h" + +#include + +#include +#include +#include + +#define TRACE(x...) debug_printf("WebHandler: "x) +#define TRACE_ERROR(x...) debug_printf("WebHandler: "x) + + +WebHandler::WebHandler(const char *path, BDataIO *data) + : + fPath(path), + fMultipart(false), + fType(""), + fData(data), + fTarget(NULL) +{ +} + + +WebHandler::WebHandler(const char *path, StreamingRingBuffer *target) + : + fPath(path), + fMultipart(false), + fType(""), + fData(NULL), + fTarget(target) +{ +} + + +WebHandler::~WebHandler() +{ + delete fData; +} + + +status_t +WebHandler::HandleRequest(WebWorker *worker, BString &path) +{ +// off_t offset = 0LL; + + return B_OK; +} + + +int +WebHandler::_CallbackCompare(const BString* key, + const WebHandler* info) +{ + int diff = strcmp(*key, info->fPath.String()); + TRACE("'%s' <> '%s' %d\n", key->String(), info->fPath.String(), diff); + if (diff == 0) + return 0; + if (diff < 0) + return -1; + return 1; +// return strcmp(*key, info->fPath.String()); +} + + +int +WebHandler::_CallbackCompare(const WebHandler* a, const WebHandler* b) +{ + int diff = strcmp(a->fPath.String(), b->fPath.String()); + return diff; +// return strcmp(*key, info->fPath.String()); +} + + + diff --git a/src/servers/app/drawing/html5/WebHandler.h b/src/servers/app/drawing/html5/WebHandler.h new file mode 100644 index 0000000000..24ef6fba80 --- /dev/null +++ b/src/servers/app/drawing/html5/WebHandler.h @@ -0,0 +1,58 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + * François Revol + */ +#ifndef WEB_HANDLER_H +#define WEB_HANDLER_H + +#include +#include +#include + +class BNetEndpoint; +class StreamingRingBuffer; +class BDataIO; +class WebWorker; + +class WebHandler { +public: + WebHandler(const char *path, + BDataIO *data=NULL); + WebHandler(const char *path, + StreamingRingBuffer *target); + virtual ~WebHandler(); + + void SetMultipart(bool multipart=true) { + fMultipart = true; } + bool IsMultipart() { return fMultipart; } + void SetType(const char *type) { fType = type; } + + + BString & Name() { return fPath; } +// BNetEndpoint * Endpoint() { return fEndpoint; } +virtual status_t HandleRequest(WebWorker *worker, + BString &path); + +static int _CallbackCompare(const BString* key, + const WebHandler* info); +static int _CallbackCompare(const WebHandler* a, + const WebHandler* b); + +private: + friend class WebWorker; + BString fPath; + + bool fMultipart; + BString fType; // MIME + BDataIO * fData; + StreamingRingBuffer * fTarget; + + thread_id fReceiverThread; + bool fStopThread; +}; + +#endif // WEB_HANDLER_H diff --git a/src/servers/app/drawing/html5/WebServer.cpp b/src/servers/app/drawing/html5/WebServer.cpp new file mode 100644 index 0000000000..8c60c191e1 --- /dev/null +++ b/src/servers/app/drawing/html5/WebServer.cpp @@ -0,0 +1,155 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + * François Revol + */ + +#include "WebHandler.h" +#include "WebServer.h" +#include "WebWorker.h" + +#include "StreamingRingBuffer.h" + +#include +#include + +#include +#include +#include + +#define TRACE(x...) debug_printf("WebServer: "x) +#define TRACE_ERROR(x...) debug_printf("WebServer: "x) + + +WebServer::WebServer(BNetEndpoint *listener) + : + fListener(listener), + fReceiverThread(-1), + fStopThread(false), + fLocker("WebServer locker") +{ + fReceiverThread = spawn_thread(_NetworkReceiverEntry, "html5 server", + B_NORMAL_PRIORITY, this); + resume_thread(fReceiverThread); +} + + +WebServer::~WebServer() +{ + fStopThread = true; + + if (fListener != NULL) + fListener->Close(); + + //int32 result; + //wait_for_thread(fReceiverThread, &result); + // TODO: find out why closing the endpoint doesn't notify the waiter + + kill_thread(fReceiverThread); +} + + +void +WebServer::AddHandler(WebHandler *handler) +{ + BAutolock lock(fLocker); + fHandlers.AddItem(handler); +} + +int32 +WebServer::_NetworkReceiverEntry(void *data) +{ + return ((WebServer *)data)->_NetworkReceiver(); +} + + +status_t +WebServer::_NetworkReceiver() +{ + status_t result = fListener->Listen(); + if (result != B_OK) { + TRACE_ERROR("failed to listen on port: %s\n", strerror(result)); + return result; + } + + fHandlers.SortItems(&WebHandler::_CallbackCompare); + + while (!fStopThread) { + BNetEndpoint *endpoint = fListener->Accept(1000); + if (endpoint == NULL) + continue; + + TRACE("new endpoint connection: %p\n", endpoint); + while (!fStopThread) { + int32 errorCount = 0; + uint8 buffer[4096]; + int32 readSize = endpoint->Receive(buffer, sizeof(buffer) - 1); + if (readSize < 0) { + TRACE_ERROR("read failed, closing connection: %s\n", + strerror(readSize)); + delete endpoint; + break; + } + + if (readSize == 0) { + TRACE("read 0 bytes, retrying\n"); + snooze(100 * 1000); + errorCount++; + if (errorCount == 5) { + TRACE_ERROR("failed to read, assuming disconnect\n"); + delete endpoint; + break; + } + continue; + } + buffer[readSize] = '\0'; + + const char err404[] = "HTTP/1.1 404 Not found\r\n\r\n"; + + // XXX: HACK HACK HACK + const char *p = (const char *)buffer; + if (strncmp(p, "GET ", 4) == 0) { + p += 4; + const char *s = strchr(p, ' '); + if (s && strncmp(s, " HTTP/", 6) == 0) { + if (*p == '/') + p++; + BString path(p, s - p); + if (p == s) + path = "desktop.html"; + TRACE("searching handler for '%s'\n", path.String()); + WebHandler *handler = fHandlers.BinarySearchByKey( + path, &WebHandler::_CallbackCompare); + if (handler) { + TRACE("found handler '%s'\n", handler->Name().String()); + WebWorker *worker = + new (std::nothrow) WebWorker(endpoint, handler); + if (worker) { + fWorkers.AddItem(worker); + break; + } + } + } + } + + // some error + endpoint->Send(err404, sizeof(err404) - 1); + delete endpoint; + break; + +#if 0 + status_t result = fTarget->Write(buffer, readSize); + if (result != B_OK) { + TRACE_ERROR("writing to ring buffer failed: %s\n", + strerror(result)); + return result; + } +#endif + } + } + + return B_OK; +} diff --git a/src/servers/app/drawing/html5/WebServer.h b/src/servers/app/drawing/html5/WebServer.h new file mode 100644 index 0000000000..ea1a0bc3b5 --- /dev/null +++ b/src/servers/app/drawing/html5/WebServer.h @@ -0,0 +1,47 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + * François Revol + */ +#ifndef WEB_SERVER_H +#define WEB_SERVER_H + +//#include "WebWorker.h" + +#include +#include +#include +#include + +class BNetEndpoint; +class WebHandler; +class WebWorker; + +class WebServer { +public: + WebServer(BNetEndpoint *listener); + ~WebServer(); + +// BNetEndpoint * Endpoint() { return fEndpoint; } + + void AddHandler(WebHandler *handler); + +private: +static int32 _NetworkReceiverEntry(void *data); + status_t _NetworkReceiver(); + + BNetEndpoint * fListener; + + thread_id fReceiverThread; + bool fStopThread; + + BLocker fLocker; + BObjectList fHandlers; + BObjectList fWorkers; +// BNetEndpoint * fEndpoint; +}; + +#endif // WEB_SERVER_H diff --git a/src/servers/app/drawing/html5/WebWorker.cpp b/src/servers/app/drawing/html5/WebWorker.cpp new file mode 100644 index 0000000000..b8c5ef671b --- /dev/null +++ b/src/servers/app/drawing/html5/WebWorker.cpp @@ -0,0 +1,152 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + * François Revol + */ + +#include "WebHandler.h" +#include "WebWorker.h" + +#include "StreamingRingBuffer.h" + +#include + +#include +#include +#include + +#define TRACE(x...) debug_printf("WebWorker: "x) +#define TRACE_ERROR(x...) debug_printf("WebWorker: "x) + + +WebWorker::WebWorker(BNetEndpoint *endpoint, WebHandler *handler) + : + fHandler(handler), + fWorkThread(-1), + fStopThread(false), + fEndpoint(endpoint) +{ + fWorkThread = spawn_thread(_WorkEntry, "html5 http worker", + B_NORMAL_PRIORITY, this); + resume_thread(fWorkThread); +} + + +WebWorker::~WebWorker() +{ + fStopThread = true; + + if (fEndpoint != NULL) + fEndpoint->Close(); + + //int32 result; + //wait_for_thread(fWorkThread, &result); + // TODO: find out why closing the endpoint doesn't notify the waiter + + kill_thread(fWorkThread); +} + + +int32 +WebWorker::_WorkEntry(void *data) +{ + return ((WebWorker *)data)->_Work(); +} + + +status_t +WebWorker::_Work() +{ + off_t pos = 0LL, contentSize = -1LL; + int32 errorCount = 0; + status_t result; + TRACE("new endpoint connection: %p\n", fEndpoint); + + BDataIO *io = fHandler->fData; + BPositionIO *pio = dynamic_cast(io); + StreamingRingBuffer *rb = fHandler->fTarget; + + if (pio) + pio->GetSize(&contentSize); + + BString headers("HTTP/1.1 200 OK\r\n"); + headers << "Server: app_server(Haiku)\r\n"; + headers << "Accept-Ranges: bytes\r\n"; + headers << "Connection: "; + if (fHandler->IsMultipart()) + headers << "close"; + else + headers << "keep-alive"; + headers << "\r\n"; + if (fHandler->fType.Length()) + headers << "Content-Type: " << fHandler->fType << "\r\n"; + if (contentSize > 0) + headers << "Content-Length: " << contentSize << "\r\n"; + headers << "\r\n"; + + result = fEndpoint->Send(headers.String(), headers.Length()); + if (result < headers.Length()) { + TRACE_ERROR("sending headers failed: %s\n", + strerror(result)); + return result; + } + + // send initial multipart boundary + // XXX: this is messy! + if (fHandler->IsMultipart()) { + static const char boundary[] = "--x\r\n\r\n"; + result = fEndpoint->Send(boundary, sizeof(boundary) - 1); + if (result < (signed)sizeof(boundary) - 1) { + TRACE_ERROR("writing to peer failed: %s\n", + strerror(result)); + return result; + } + } + + while (!fStopThread) { + uint8 buffer[4096]; + int32 readSize = 0; + if (pio) + readSize = pio->ReadAt(pos, buffer, sizeof(buffer)); + else if (io) + readSize = io->Read(buffer, sizeof(buffer)); + else if (rb) + readSize = rb->Read(buffer, sizeof(buffer)); + TRACE("readSize %ld\n", readSize); + + if (readSize < 0) { + TRACE_ERROR("read failed, closing connection: %s\n", + strerror(readSize)); + return readSize; + } + + if (readSize == 0) { + //XXX:should just break; +// TRACE("read 0 bytes, retrying\n"); + snooze(100 * 1000); + errorCount++; + if (errorCount == 5) { + TRACE_ERROR("failed to read, assuming disconnect\n"); + fEndpoint->Close(); + break; + } + + continue; + } + pos += readSize; + + errorCount = 0; + result = fEndpoint->Send(buffer, readSize); +TRACE("writeSize %ld\n", result); + if (result < readSize) { + TRACE_ERROR("writing to peer failed: %s\n", + strerror(result)); + return result; + } + } + + return B_OK; +} diff --git a/src/servers/app/drawing/html5/WebWorker.h b/src/servers/app/drawing/html5/WebWorker.h new file mode 100644 index 0000000000..a5166c0bd3 --- /dev/null +++ b/src/servers/app/drawing/html5/WebWorker.h @@ -0,0 +1,39 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + * François Revol + */ +#ifndef WEB_WORKER_H +#define WEB_WORKER_H + +#include +#include + +class BNetEndpoint; +class StreamingRingBuffer; +class WebHandler; + +class WebWorker { +public: + WebWorker(BNetEndpoint *endpoint, + WebHandler *handler); + ~WebWorker(); + + BNetEndpoint * Endpoint() { return fEndpoint; } + +private: +static int32 _WorkEntry(void *data); + status_t _Work(); + + WebHandler * fHandler; + + thread_id fWorkThread; + bool fStopThread; + + BNetEndpoint * fEndpoint; +}; + +#endif // WEB_WORKER_H diff --git a/src/servers/app/drawing/html5/base64.cpp b/src/servers/app/drawing/html5/base64.cpp new file mode 100644 index 0000000000..2afffe7bab --- /dev/null +++ b/src/servers/app/drawing/html5/base64.cpp @@ -0,0 +1,115 @@ +/* + * Copyright 2011, Haiku, Inc. All rights reserved. + * Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved. + */ + + +#include +#include + +#include +#include + +static const char kBase64Alphabet[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '+', + '/' + }; + +static const char kHexAlphabet[16] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8','9','A','B','C','D','E','F'}; + + +ssize_t +encode_base64(char *out, const char *in, off_t length) +{ + uint32 concat; + int i = 0; + int k = 0; + + while (i < length) { + concat = ((in[i] & 0xff) << 16); + + if ((i+1) < length) + concat |= ((in[i+1] & 0xff) << 8); + if ((i+2) < length) + concat |= (in[i+2] & 0xff); + + i += 3; + + out[k++] = kBase64Alphabet[(concat >> 18) & 63]; + out[k++] = kBase64Alphabet[(concat >> 12) & 63]; + if ((i+1) < length) + out[k++] = kBase64Alphabet[(concat >> 6) & 63]; + if ((i+2) < length) + out[k++] = kBase64Alphabet[concat & 63]; + } + + return k; +} + + +ssize_t +decode_base64(char *out, const char *in, off_t length) +{ + uint32 concat, value; + int lastOutLine = 0; + int i, j; + int outIndex = 0; + + for (i = 0; i < length; i += 4) { + concat = 0; + + for (j = 0; j < 4 && (i + j) < length; j++) { + value = in[i + j]; + + if (value == '\n' || value == '\r') { + // jump over line breaks + lastOutLine = outIndex; + i++; + j--; + continue; + } + + if ((value >= 'A') && (value <= 'Z')) + value -= 'A'; + else if ((value >= 'a') && (value <= 'z')) + value = value - 'a' + 26; + else if ((value >= '0') && (value <= '9')) + value = value - '0' + 52; + else if (value == '+') + value = 62; + else if (value == '/') + value = 63; + else if (value == '=') + break; + else { + // there is an invalid character in this line - we will + // ignore the whole line and go to the next + outIndex = lastOutLine; + while (i < length && in[i] != '\n' && in[i] != '\r') + i++; + concat = 0; + } + + value = value << ((3-j)*6); + + concat |= value; + } + + if (j > 1) + out[outIndex++] = (concat & 0x00ff0000) >> 16; + if (j > 2) + out[outIndex++] = (concat & 0x0000ff00) >> 8; + if (j > 3) + out[outIndex++] = (concat & 0x000000ff); + } + + return outIndex; +} + + diff --git a/src/servers/app/drawing/html5/base64.h b/src/servers/app/drawing/html5/base64.h new file mode 100644 index 0000000000..fa2d3f0625 --- /dev/null +++ b/src/servers/app/drawing/html5/base64.h @@ -0,0 +1,15 @@ +/* + * Copyright 2011, Haiku, Inc. All rights reserved. + * Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved. + */ +#ifndef BASE64_H +#define BASE64_H + +#include + + +extern ssize_t encode_base64(char *out, const char *in, off_t length); +extern ssize_t decode_base64(char *out, const char *in, off_t length); + + +#endif // BASE64_H diff --git a/src/servers/app/drawing/html5/desktop.html b/src/servers/app/drawing/html5/desktop.html new file mode 100644 index 0000000000..6f9262d208 --- /dev/null +++ b/src/servers/app/drawing/html5/desktop.html @@ -0,0 +1,29 @@ + + + + +Haiku + + + + + + +Your browser does not support the <canvas> element, which is required. + +
+ + diff --git a/src/servers/app/drawing/html5/desktop.html.h b/src/servers/app/drawing/html5/desktop.html.h new file mode 100644 index 0000000000..8b0e9512a8 --- /dev/null +++ b/src/servers/app/drawing/html5/desktop.html.h @@ -0,0 +1,30 @@ +static const char desktop_html[] = + "\n" + "\n" + "\n" + "\n" + "Haiku\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "Your browser does not support the <canvas> element, which is required.\n" + "\n" + "
\n" + "\n" + "\n"; diff --git a/src/servers/app/drawing/html5/haiku.js b/src/servers/app/drawing/html5/haiku.js new file mode 100644 index 0000000000..8c7b808efd --- /dev/null +++ b/src/servers/app/drawing/html5/haiku.js @@ -0,0 +1,100 @@ +/* + * Copyright 2012, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * François Revol + * + * Loosely inspired by + * http://git.gnome.org/browse/gtk+/tree/gdk/broadway/broadway.js?h=broadway + */ + +var logDiv; +var desktop; // the canvas + +function dbg(str) +{ + var div = document.createElement("div"); + div.className = "haiku_online_debug"; + div.appendChild(document.createTextNode(str)); + logDiv.appendChild(div); +} + +function err(str) +{ + var div = document.createElement("div"); + div.className = "haiku_online_error"; + div.appendChild(document.createTextNode(str)); + logDiv.appendChild(div); +} + +function createXHR() +{ + try { + return new XMLHttpRequest(); + } catch(e) { + err("Failed to create XHR: " + e); + } + return null; +} + +function onMessage(message) +{ + dbg("onMessage: "); +window.popup("plop"); + dbg(message.target.responseText); +} + +function initDesktop() +{ + dbg("initDesktop()"); + desktop = document.getElementById("desktop"); + var xhr = createXHR(); + if (xhr) { + if (typeof xhr.multipart != "undefined") + xhr.multipart = true; + else { + err("XHR has no multipart field!"); + return; + } + if (typeof xhr.async != "undefined") + xhr.async = true; + else { + dbg("XHR has no async field!"); + } + dbg("multipart: " + xhr.multipart); + xhr.open("GET", "http://127.0.0.1:8080/output", true); + //xhr.onload = onMessage; + xhr.onreadystatechange = function() { + dbg("readystate changed: " + xhr.readyState); + if (xhr.readyState != 4) + return; + dbg("status: " + xhr.status); + dbg("resonseType: " + xhr.responseType); + dbg("response: " + xhr.responseText); + dbg("headers: " + xhr.getAllResponseHeaders()); + + } + //xhr.responseType = "arraybuffer"; + //dbg("xhr.onload:" + xhr.onload); + //dbg("xhr.send:" + xhr.send); + xhr.overrideMimeType("multipart/x-mixed-replace;boundary=x"); + xhr.send(null); + //dbg("headers: " + xhr.getAllResponseHeaders()); + } else + err("No XHR"); + dbg("done"); +} + + +function onPageLoad() { + logDiv = document.getElementById("log"); + dbg("onPageLoad()"); + initDesktop(); +} + +function onPageUnload() { + dbg("onPageUnload()"); +} + + diff --git a/src/servers/app/drawing/html5/haiku.js.h b/src/servers/app/drawing/html5/haiku.js.h new file mode 100644 index 0000000000..18e8abf065 --- /dev/null +++ b/src/servers/app/drawing/html5/haiku.js.h @@ -0,0 +1,101 @@ +static const char haiku_js[] = + "/*\n" + " * Copyright 2012, Haiku, Inc.\n" + " * Distributed under the terms of the MIT License.\n" + " *\n" + " * Authors:\n" + " * François Revol \n" + " *\n" + " * Loosely inspired by\n" + " * http://git.gnome.org/browse/gtk+/tree/gdk/broadway/broadway.js?h=broadway\n" + " */\n" + "\n" + "var logDiv;\n" + "var desktop; // the canvas\n" + "\n" + "function dbg(str)\n" + "{\n" + " var div = document.createElement(\"div\");\n" + " div.className = \"haiku_online_debug\";\n" + " div.appendChild(document.createTextNode(str));\n" + " logDiv.appendChild(div);\n" + "}\n" + "\n" + "function err(str)\n" + "{\n" + " var div = document.createElement(\"div\");\n" + " div.className = \"haiku_online_error\";\n" + " div.appendChild(document.createTextNode(str));\n" + " logDiv.appendChild(div);\n" + "}\n" + "\n" + "function createXHR()\n" + "{\n" + " try {\n" + " return new XMLHttpRequest();\n" + " } catch(e) {\n" + " err(\"Failed to create XHR: \" + e);\n" + " }\n" + " return null;\n" + "}\n" + "\n" + "function onMessage(message)\n" + "{\n" + " dbg(\"onMessage: \");\n" + "window.popup(\"plop\");\n" + " dbg(message.target.responseText);\n" + "}\n" + "\n" + "function initDesktop()\n" + "{\n" + " dbg(\"initDesktop()\");\n" + " desktop = document.getElementById(\"desktop\");\n" + " var xhr = createXHR();\n" + " if (xhr) {\n" + " if (typeof xhr.multipart != \"undefined\")\n" + " xhr.multipart = true;\n" + " else {\n" + " err(\"XHR has no multipart field!\");\n" + " return;\n" + " }\n" + " if (typeof xhr.async != \"undefined\")\n" + " xhr.async = true;\n" + " else {\n" + " dbg(\"XHR has no async field!\");\n" + " }\n" + " dbg(\"multipart: \" + xhr.multipart);\n" + " xhr.open(\"GET\", \"http://127.0.0.1:8080/output\", true);\n" + " //xhr.onload = onMessage;\n" + " xhr.onreadystatechange = function() {\n" + " dbg(\"readystate changed: \" + xhr.readyState);\n" + " if (xhr.readyState != 4)\n" + " return;\n" + " dbg(\"status: \" + xhr.status);\n" + " dbg(\"resonseType: \" + xhr.responseType);\n" + " dbg(\"response: \" + xhr.responseText);\n" + " dbg(\"headers: \" + xhr.getAllResponseHeaders());\n" + "\n" + " }\n" + " //xhr.responseType = \"arraybuffer\";\n" + " //dbg(\"xhr.onload:\" + xhr.onload);\n" + " //dbg(\"xhr.send:\" + xhr.send);\n" + " xhr.overrideMimeType(\"multipart/x-mixed-replace;boundary=x\");\n" + " xhr.send(null);\n" + " //dbg(\"headers: \" + xhr.getAllResponseHeaders());\n" + " } else\n" + " err(\"No XHR\");\n" + " dbg(\"done\");\n" + "}\n" + "\n" + "\n" + "function onPageLoad() {\n" + " logDiv = document.getElementById(\"log\");\n" + " dbg(\"onPageLoad()\");\n" + " initDesktop();\n" + "}\n" + "\n" + "function onPageUnload() {\n" + " dbg(\"onPageUnload()\");\n" + "}\n" + "\n" + "\n";