An html5 <canvas> 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.
This commit is contained in:
François Revol 2012-04-01 11:04:41 +02:00
parent d1abffcaa2
commit 8d33dc2971
28 changed files with 4477 additions and 2 deletions

View File

@ -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++)

View File

@ -15,6 +15,7 @@
#include "ServerConfig.h"
#include "remote/RemoteHWInterface.h"
#include "html5/HTML5HWInterface.h"
#include <Autolock.h>
#include <Entry.h>
@ -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)) {

View File

@ -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 ;

View File

@ -0,0 +1,218 @@
/*
* Copyright 2009, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
*/
#include "CanvasEventStream.h"
#include "CanvasMessage.h"
#include "StreamingRingBuffer.h"
#include <Autolock.h>
#include <new>
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;
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2009, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
*/
#ifndef CANVAS_EVENT_STREAM_H
#define CANVAS_EVENT_STREAM_H
#include "EventStream.h"
#include <Locker.h>
#include <ObjectList.h>
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<BMessage> fEventList;
BLocker fEventListLocker;
sem_id fEventNotification;
bool fWaitingOnEvent;
BMessage* fLatestMouseMovedEvent;
BPoint fMousePosition;
uint32 fMouseButtons;
uint32 fModifiers;
};
#endif // CANVAS_EVENT_STREAM_H

View File

@ -0,0 +1,500 @@
/*
* Copyright 2009, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
* François Revol <revol@free.fr>
*/
#include "CanvasMessage.h"
#include "DrawState.h"
#include "ServerBitmap.h"
#include "ServerCursor.h"
#include <Bitmap.h>
#include <Font.h>
#include <View.h>
#include <Gradient.h>
#include <GradientLinear.h>
#include <GradientRadial.h>
#include <GradientRadialFocus.h>
#include <GradientDiamond.h>
#include <GradientConic.h>
#include <new>
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<const BGradientLinear *>(&gradient);
if (linear == NULL)
return;
Add(linear->Start());
Add(linear->End());
break;
}
case BGradient::TYPE_RADIAL:
{
const BGradientRadial* radial
= dynamic_cast<const BGradientRadial *>(&gradient);
if (radial == NULL)
return;
Add(radial->Center());
Add(radial->Radius());
break;
}
case BGradient::TYPE_RADIAL_FOCUS:
{
const BGradientRadialFocus* radialFocus
= dynamic_cast<const BGradientRadialFocus *>(&gradient);
if (radialFocus == NULL)
return;
Add(radialFocus->Center());
Add(radialFocus->Focal());
Add(radialFocus->Radius());
break;
}
case BGradient::TYPE_DIAMOND:
{
const BGradientDiamond* diamond
= dynamic_cast<const BGradientDiamond *>(&gradient);
if (diamond == NULL)
return;
Add(diamond->Center());
break;
}
case BGradient::TYPE_CONIC:
{
const BGradientConic* conic
= dynamic_cast<const BGradientConic *>(&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);
}

View File

@ -0,0 +1,371 @@
/*
* Copyright 2009, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
* François Revol <revol@free.fr>
*/
#ifndef CANVAS_MESSAGE_H
#define CANCAS_MESSAGE_H
#include "PatternHandler.h"
#include <ViewPrivate.h>
#include "StreamingRingBuffer.h"
#include "base64.h"
#include <GraphicsDefs.h>
#include <Region.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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<typename T>
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<typename T>
void AddList(const T* array, int32 count);
template<typename T>
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<typename T>
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<typename T>
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<typename T>
inline void
CanvasMessage::AddList(const T* array, int32 count)
{
for (int32 i = 0; i < count; i++)
Add(array[i]);
}
template<typename T>
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<typename T>
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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,180 @@
/*
* Copyright 2009, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
* François Revol <revol@free.fr>
*/
#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

View File

@ -0,0 +1,612 @@
/*
* Copyright 2009, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
* François Revol <revol@free.fr>
*/
#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 <Autolock.h>
#include <NetEndpoint.h>
#include <new>
#include <string.h>
#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[] = "<html><head><title>Haiku</title></head>"
// "<body></body></html>";
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;
}

View File

@ -0,0 +1,131 @@
/*
* Copyright 2009, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
* François Revol <revol@free.fr>
*/
#ifndef HTML5_HW_INTERFACE_H
#define HTML5_HW_INTERFACE_H
#include "HWInterface.h"
#include <Locker.h>
#include <ObjectList.h>
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<callback_info> fCallbacks;
};
#endif // HTML5_HW_INTERFACE_H

View File

@ -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
;

View File

@ -0,0 +1,75 @@
/*
* Copyright 2009, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
*/
#include "NetSender.h"
#include "StreamingRingBuffer.h"
#include <NetEndpoint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2009, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
*/
#ifndef NET_SENDER_H
#define NET_SENDER_H
#include <OS.h>
#include <SupportDefs.h>
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

View File

@ -0,0 +1,176 @@
/*
* Copyright 2009, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
*/
#include "StreamingRingBuffer.h"
#include <Autolock.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2009, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
*/
#ifndef STREAMING_RING_BUFFER_H
#define STREAMING_RING_BUFFER_H
#include <OS.h>
#include <SupportDefs.h>
#include <Locker.h>
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

View File

@ -0,0 +1,85 @@
/*
* Copyright 2009, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
* François Revol <revol@free.fr>
*/
#include "WebHandler.h"
#include "StreamingRingBuffer.h"
#include <NetEndpoint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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());
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2009, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
* François Revol <revol@free.fr>
*/
#ifndef WEB_HANDLER_H
#define WEB_HANDLER_H
#include <OS.h>
#include <String.h>
#include <SupportDefs.h>
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

View File

@ -0,0 +1,155 @@
/*
* Copyright 2009, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
* François Revol <revol@free.fr>
*/
#include "WebHandler.h"
#include "WebServer.h"
#include "WebWorker.h"
#include "StreamingRingBuffer.h"
#include <NetEndpoint.h>
#include <Autolock.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2009, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
* François Revol <revol@free.fr>
*/
#ifndef WEB_SERVER_H
#define WEB_SERVER_H
//#include "WebWorker.h"
#include <Locker.h>
#include <ObjectList.h>
#include <OS.h>
#include <SupportDefs.h>
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<WebHandler> fHandlers;
BObjectList<WebWorker> fWorkers;
// BNetEndpoint * fEndpoint;
};
#endif // WEB_SERVER_H

View File

@ -0,0 +1,152 @@
/*
* Copyright 2009, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
* François Revol <revol@free.fr>
*/
#include "WebHandler.h"
#include "WebWorker.h"
#include "StreamingRingBuffer.h"
#include <NetEndpoint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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<BPositionIO *>(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;
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2009, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Lotz <mmlr@mlotz.ch>
* François Revol <revol@free.fr>
*/
#ifndef WEB_WORKER_H
#define WEB_WORKER_H
#include <OS.h>
#include <SupportDefs.h>
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

View File

@ -0,0 +1,115 @@
/*
* Copyright 2011, Haiku, Inc. All rights reserved.
* Copyright 2001-2003 Dr. Zoidberg Enterprises. All rights reserved.
*/
#include <ctype.h>
#include <string.h>
#include <mail_encoding.h>
#include <SupportDefs.h>
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;
}

View File

@ -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 <ctype.h>
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

View File

@ -0,0 +1,29 @@
<html>
<!-- Haiku HTML5 desktop -->
<!-- Loosely inspired by the Broadway GDK backend, just done differently -->
<head>
<title>Haiku</title>
<meta name="robots" content="noindex, nofollow, noarchive" />
<style type="text/css">
<!--
/* basic style */
body { background-color: #336698; }
a:link { color:orange; }
a:visited { color:darkorange; }
a:hover { color:pink; }
.haiku_online_form { color: white; }
.haiku_online_disabled { color: grey; }
.haiku_online_out { color: white; }
.haiku_online_debug { color: orange; }
.haiku_online_error { color: red; font-weight: bold; }
-->
</style>
<script type="text/javascript" src="/haiku.js"></script>
<link rel="shortcut icon" href="http://haiku-os.org/sites/haiku-os.org/themes/shijin/favicon.ico" type="image/x-icon" />
</head>
<body onload="onPageLoad()" onunload="onPageUnload()">
<canvas id="desktop">Your browser does not support the &lt;canvas&gt; element, which is required.</canvas>
<noscript>Your browser does not support javascript or javascript is disabled.</noscript>
<div id="log"></div>
</body>
</html>

View File

@ -0,0 +1,30 @@
static const char desktop_html[] =
"<html>\n"
"<!-- Haiku HTML5 desktop -->\n"
"<!-- Loosely inspired by the Broadway GDK backend, just done differently -->\n"
"<head>\n"
"<title>Haiku</title>\n"
"<meta name=\"robots\" content=\"noindex, nofollow, noarchive\" />\n"
"<style type=\"text/css\">\n"
"<!--\n"
"/* basic style */\n"
"body { background-color: #336698; }\n"
"a:link { color:orange; }\n"
"a:visited { color:darkorange; }\n"
"a:hover { color:pink; }\n"
".haiku_online_form { color: white; }\n"
".haiku_online_disabled { color: grey; }\n"
".haiku_online_out { color: white; }\n"
".haiku_online_debug { color: orange; }\n"
".haiku_online_error { color: red; font-weight: bold; }\n"
"-->\n"
"</style>\n"
"<script type=\"text/javascript\" src=\"/haiku.js\"></script>\n"
"<link rel=\"shortcut icon\" href=\"http://haiku-os.org/sites/haiku-os.org/themes/shijin/favicon.ico\" type=\"image/x-icon\" />\n"
"</head>\n"
"<body onload=\"onPageLoad()\" onunload=\"onPageUnload()\">\n"
"<canvas id=\"desktop\">Your browser does not support the &lt;canvas&gt; element, which is required.</canvas>\n"
"<noscript>Your browser does not support javascript or javascript is disabled.</noscript>\n"
"<div id=\"log\"></div>\n"
"</body>\n"
"</html>\n";

View File

@ -0,0 +1,100 @@
/*
* Copyright 2012, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* François Revol <revol@free.fr>
*
* 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()");
}

View File

@ -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 <revol@free.fr>\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";