From 68667bf48a9e29a2d142cb3308b606d80bee3c2d Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Sun, 4 Oct 2009 14:15:17 +0000 Subject: [PATCH] * Adding a remote desktop interface that operates on app_server drawing primitives by providing a RemoteDrawingEngine and a RemoteHWInterface. Not really optimized yet, still a bit WIP. * Adding corresponding infrastructure like a blocking ring buffer and network sender/receiver that are attached to the buffers to feed/drain them as well as a RemoteMessage helper that provides a message based interface. * Adding target screen concept to request an app to be run on a specific screen. It's controlled by the TARGET_SCREEN environment variable which is added on the app side and sent to the app_server. * Right now only remote target screens are supported, in which case a new RemoteHWInterface is created that tries to connect to the given host:port. * Fix shape bounds when drawing, they need to be translated by the pen position and converted to screen like the points as well. Wasn't visible though as the bounds weren't used in the normal DrawingEngine. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@33417 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/kits/app/Application.cpp | 1 + src/servers/app/AppServer.cpp | 26 +- src/servers/app/AppServer.h | 4 +- src/servers/app/Desktop.cpp | 14 +- src/servers/app/Desktop.h | 4 +- src/servers/app/Jamfile | 4 +- src/servers/app/Screen.cpp | 2 +- src/servers/app/ScreenManager.cpp | 25 +- src/servers/app/ScreenManager.h | 9 +- src/servers/app/ServerFont.cpp | 17 + src/servers/app/ServerFont.h | 1 + src/servers/app/ServerWindow.cpp | 10 +- src/servers/app/VirtualScreen.cpp | 6 +- src/servers/app/drawing/DrawingEngine.cpp | 8 +- src/servers/app/drawing/DrawingEngine.h | 171 ++-- src/servers/app/drawing/HWInterface.cpp | 43 + src/servers/app/drawing/HWInterface.h | 15 +- src/servers/app/drawing/Jamfile | 1 + src/servers/app/drawing/remote/Jamfile | 25 + .../app/drawing/remote/NetReceiver.cpp | 110 ++ src/servers/app/drawing/remote/NetReceiver.h | 38 + src/servers/app/drawing/remote/NetSender.cpp | 75 ++ src/servers/app/drawing/remote/NetSender.h | 34 + .../drawing/remote/RemoteDrawingEngine.cpp | 939 ++++++++++++++++++ .../app/drawing/remote/RemoteDrawingEngine.h | 165 +++ .../app/drawing/remote/RemoteEventStream.cpp | 215 ++++ .../app/drawing/remote/RemoteEventStream.h | 45 + .../app/drawing/remote/RemoteHWInterface.cpp | 572 +++++++++++ .../app/drawing/remote/RemoteHWInterface.h | 127 +++ .../app/drawing/remote/RemoteMessage.cpp | 501 ++++++++++ .../app/drawing/remote/RemoteMessage.h | 359 +++++++ .../drawing/remote/StreamingRingBuffer.cpp | 176 ++++ .../app/drawing/remote/StreamingRingBuffer.h | 47 + 33 files changed, 3669 insertions(+), 120 deletions(-) create mode 100644 src/servers/app/drawing/remote/Jamfile create mode 100644 src/servers/app/drawing/remote/NetReceiver.cpp create mode 100644 src/servers/app/drawing/remote/NetReceiver.h create mode 100644 src/servers/app/drawing/remote/NetSender.cpp create mode 100644 src/servers/app/drawing/remote/NetSender.h create mode 100644 src/servers/app/drawing/remote/RemoteDrawingEngine.cpp create mode 100644 src/servers/app/drawing/remote/RemoteDrawingEngine.h create mode 100644 src/servers/app/drawing/remote/RemoteEventStream.cpp create mode 100644 src/servers/app/drawing/remote/RemoteEventStream.h create mode 100644 src/servers/app/drawing/remote/RemoteHWInterface.cpp create mode 100644 src/servers/app/drawing/remote/RemoteHWInterface.h create mode 100644 src/servers/app/drawing/remote/RemoteMessage.cpp create mode 100644 src/servers/app/drawing/remote/RemoteMessage.h create mode 100644 src/servers/app/drawing/remote/StreamingRingBuffer.cpp create mode 100644 src/servers/app/drawing/remote/StreamingRingBuffer.h diff --git a/src/kits/app/Application.cpp b/src/kits/app/Application.cpp index 3ef471b054..341645cfc6 100644 --- a/src/kits/app/Application.cpp +++ b/src/kits/app/Application.cpp @@ -1272,6 +1272,7 @@ BApplication::_ConnectToServer() fServerLink->StartMessage(AS_GET_DESKTOP); fServerLink->Attach(clientPort); fServerLink->Attach(getuid()); + fServerLink->AttachString(getenv("TARGET_SCREEN")); int32 code; if (fServerLink->FlushWithReply(code) != B_OK || code != B_OK) { diff --git a/src/servers/app/AppServer.cpp b/src/servers/app/AppServer.cpp index bce30f4be9..612d6450a5 100644 --- a/src/servers/app/AppServer.cpp +++ b/src/servers/app/AppServer.cpp @@ -176,12 +176,12 @@ AppServer::RunLooper() /*! \brief Creates a desktop object for an authorized user */ Desktop* -AppServer::_CreateDesktop(uid_t userID) +AppServer::_CreateDesktop(uid_t userID, const char* targetScreen) { BAutolock locker(fDesktopLock); Desktop* desktop = NULL; try { - desktop = new Desktop(userID); + desktop = new Desktop(userID, targetScreen); status_t status = desktop->Init(); if (status == B_OK) { @@ -208,16 +208,20 @@ AppServer::_CreateDesktop(uid_t userID) /*! \brief Finds the desktop object that belongs to a certain user */ -Desktop * -AppServer::_FindDesktop(uid_t userID) +Desktop* +AppServer::_FindDesktop(uid_t userID, const char* targetScreen) { BAutolock locker(fDesktopLock); for (int32 i = 0; i < fDesktops.CountItems(); i++) { Desktop* desktop = fDesktops.ItemAt(i); - if (desktop->UserID() == userID) + if (desktop->UserID() == userID + && ((desktop->TargetScreen() == NULL && targetScreen == NULL) + || (desktop->TargetScreen() != NULL && targetScreen != NULL + && strcmp(desktop->TargetScreen(), targetScreen) == 0))) { return desktop; + } } return NULL; @@ -242,15 +246,23 @@ AppServer::_DispatchMessage(int32 code, BPrivate::LinkReceiver& msg) int32 userID; msg.Read(&userID); - Desktop* desktop = _FindDesktop(userID); + char* targetScreen = NULL; + msg.ReadString(&targetScreen); + if (targetScreen != NULL && strlen(targetScreen) == 0) { + free(targetScreen); + targetScreen = NULL; + } + + Desktop* desktop = _FindDesktop(userID, targetScreen); if (desktop == NULL) { // we need to create a new desktop object for this user // TODO: test if the user exists on the system // TODO: maybe have a separate AS_START_DESKTOP_SESSION for // authorizing the user - desktop = _CreateDesktop(userID); + desktop = _CreateDesktop(userID, targetScreen); } + free(targetScreen); BPrivate::LinkSender reply(replyPort); if (desktop != NULL) { reply.StartMessage(B_OK); diff --git a/src/servers/app/AppServer.h b/src/servers/app/AppServer.h index 8f33e2b1d7..cffa5b3fd4 100644 --- a/src/servers/app/AppServer.h +++ b/src/servers/app/AppServer.h @@ -42,8 +42,8 @@ class AppServer : public MessageLooper { private: virtual void _DispatchMessage(int32 code, BPrivate::LinkReceiver& link); - Desktop* _CreateDesktop(uid_t userID); - Desktop* _FindDesktop(uid_t userID); + Desktop* _CreateDesktop(uid_t userID, const char* targetScreen); + Desktop* _FindDesktop(uid_t userID, const char* targetScreen); void _LaunchInputServer(); diff --git a/src/servers/app/Desktop.cpp b/src/servers/app/Desktop.cpp index f533c93781..c1e8a158b8 100644 --- a/src/servers/app/Desktop.cpp +++ b/src/servers/app/Desktop.cpp @@ -289,11 +289,12 @@ workspace_in_workspaces(int32 index, uint32 workspaces) // #pragma mark - -Desktop::Desktop(uid_t userID) +Desktop::Desktop(uid_t userID, const char* targetScreen) : MessageLooper("desktop"), fUserID(userID), + fTargetScreen(strdup(targetScreen)), fSettings(NULL), fSharedReadOnlyArea(-1), fApplicationsLock("application list"), @@ -355,7 +356,7 @@ Desktop::Init() const size_t areaSize = B_PAGE_SIZE; char name[B_OS_NAME_LENGTH]; - snprintf(name, sizeof(name), "d:%d:shared read only", /*id*/0); + snprintf(name, sizeof(name), "d:%d:shared read only", fUserID); fSharedReadOnlyArea = create_area(name, (void **)&fServerReadOnlyMemory, B_ANY_ADDRESS, areaSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); if (fSharedReadOnlyArea < B_OK) @@ -385,8 +386,12 @@ Desktop::Init() gInputManager->AddStream(new InputServerStream); #endif + EventStream* stream = fVirtualScreen.HWInterface()->CreateEventStream(); + if (stream == NULL) + stream = gInputManager->GetStream(); + fEventDispatcher.SetDesktop(this); - fEventDispatcher.SetTo(gInputManager->GetStream()); + fEventDispatcher.SetTo(stream); if (fEventDispatcher.InitCheck() != B_OK) _LaunchInputServer(); @@ -2079,7 +2084,8 @@ Desktop::_LaunchInputServer() void Desktop::_GetLooperName(char* name, size_t length) { - snprintf(name, length, "d:%d:%s", /*id*/0, /*name*/"baron"); + snprintf(name, length, "d:%d:%s", fUserID, + fTargetScreen == NULL ? "baron" : fTargetScreen); } diff --git a/src/servers/app/Desktop.h b/src/servers/app/Desktop.h index eea2414696..d98fbd7bac 100644 --- a/src/servers/app/Desktop.h +++ b/src/servers/app/Desktop.h @@ -59,12 +59,13 @@ namespace BPrivate { class Desktop : public MessageLooper, public ScreenOwner { public: - Desktop(uid_t userID); + Desktop(uid_t userID, const char* targetScreen); virtual ~Desktop(); status_t Init(); uid_t UserID() const { return fUserID; } + const char* TargetScreen() { return fTargetScreen; } virtual port_id MessagePort() const { return fMessagePort; } area_id SharedReadOnlyArea() const { return fSharedReadOnlyArea; } @@ -296,6 +297,7 @@ private: friend class LockedDesktopSettings; uid_t fUserID; + const char* fTargetScreen; ::VirtualScreen fVirtualScreen; DesktopSettingsPrivate* fSettings; port_id fMessagePort; diff --git a/src/servers/app/Jamfile b/src/servers/app/Jamfile index 8393b0f402..00e0d60224 100644 --- a/src/servers/app/Jamfile +++ b/src/servers/app/Jamfile @@ -63,8 +63,8 @@ Server app_server : # libraries : - libtranslation.so libbe.so - libasdrawing.a libpainter.a libagg.a libfreetype.so + libtranslation.so libbe.so libbnetapi.so + libasdrawing.a libasremote.a libpainter.a libagg.a libfreetype.so libtextencoding.so libshared.a $(TARGET_LIBSTDC++) : app_server.rdef diff --git a/src/servers/app/Screen.cpp b/src/servers/app/Screen.cpp index f1ab5b54de..6b3b081129 100644 --- a/src/servers/app/Screen.cpp +++ b/src/servers/app/Screen.cpp @@ -42,7 +42,7 @@ get_mode_frequency(const display_mode& mode) Screen::Screen(::HWInterface *interface, int32 id) : fID(id), - fDriver(interface ? new DrawingEngine(interface) : NULL), + fDriver(interface ? interface->CreateDrawingEngine() : NULL), fHWInterface(interface) { } diff --git a/src/servers/app/ScreenManager.cpp b/src/servers/app/ScreenManager.cpp index c117358562..844a4e5fd0 100644 --- a/src/servers/app/ScreenManager.cpp +++ b/src/servers/app/ScreenManager.cpp @@ -14,6 +14,8 @@ #include "Screen.h" #include "ServerConfig.h" +#include "remote/RemoteHWInterface.h" + #include #include #include @@ -86,7 +88,7 @@ ScreenManager::CountScreens() const status_t ScreenManager::AcquireScreens(ScreenOwner* owner, int32* wishList, - int32 wishCount, bool force, ScreenList& list) + int32 wishCount, const char* target, bool force, ScreenList& list) { BAutolock locker(this); int32 added = 0; @@ -102,6 +104,20 @@ ScreenManager::AcquireScreens(ScreenOwner* owner, int32* wishList, } } + if (added == 0 && target != NULL) { + // 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); + if (interface != NULL) { + screen_item* item = _AddHWInterface(interface); + if (item != NULL && list.AddItem(item->screen)) { + item->owner = owner; + added++; + } + } + } + return added > 0 ? B_OK : B_ENTRY_NOT_FOUND; } @@ -153,13 +169,13 @@ ScreenManager::_ScanDrivers() } -void +ScreenManager::screen_item* ScreenManager::_AddHWInterface(HWInterface* interface) { Screen* screen = new(nothrow) Screen(interface, fScreenList.CountItems()); if (screen == NULL) { delete interface; - return; + return NULL; } // The interface is now owned by the screen @@ -170,13 +186,14 @@ ScreenManager::_AddHWInterface(HWInterface* interface) item->screen = screen; item->owner = NULL; if (fScreenList.AddItem(item)) - return; + return item; delete item; } } delete screen; + return NULL; } diff --git a/src/servers/app/ScreenManager.h b/src/servers/app/ScreenManager.h index 7951f03216..2c81dd1e1f 100644 --- a/src/servers/app/ScreenManager.h +++ b/src/servers/app/ScreenManager.h @@ -41,20 +41,21 @@ class ScreenManager : public BLooper { int32 CountScreens() const; status_t AcquireScreens(ScreenOwner* owner, int32* wishList, - int32 wishCount, bool force, ScreenList& list); + int32 wishCount, const char* target, bool force, + ScreenList& list); void ReleaseScreens(ScreenList& list); virtual void MessageReceived(BMessage* message); private: - void _ScanDrivers(); - void _AddHWInterface(HWInterface* interface); - struct screen_item { Screen* screen; ScreenOwner* owner; }; + void _ScanDrivers(); + screen_item* _AddHWInterface(HWInterface* interface); + BObjectList fScreenList; }; diff --git a/src/servers/app/ServerFont.cpp b/src/servers/app/ServerFont.cpp index 0a1500f562..5eecba71ee 100644 --- a/src/servers/app/ServerFont.cpp +++ b/src/servers/app/ServerFont.cpp @@ -191,6 +191,23 @@ ServerFont::operator=(const ServerFont& font) } +bool +ServerFont::operator==(const ServerFont& other) const +{ + if (fStyle == NULL && other.fStyle == NULL) + return true; + + if (GetFamilyAndStyle() != other.GetFamilyAndStyle()) + return false; + + return fSize == other.fSize && fRotation == other.fRotation + && fShear == other.fShear && fFalseBoldWidth == other.fFalseBoldWidth + && fFlags == other.fFlags && fSpacing == other.fSpacing + && fEncoding == other.fEncoding && fBounds == other.fBounds + && fDirection == other.fDirection && fFace == other.fFace; +} + + /*! \brief Returns the number of strikes in the font \return The number of strikes in the font diff --git a/src/servers/app/ServerFont.h b/src/servers/app/ServerFont.h index 68691f7297..f162304c6e 100644 --- a/src/servers/app/ServerFont.h +++ b/src/servers/app/ServerFont.h @@ -37,6 +37,7 @@ class ServerFont { virtual ~ServerFont(); ServerFont &operator=(const ServerFont& font); + bool operator==(const ServerFont& other) const; font_direction Direction() const { return fDirection; } diff --git a/src/servers/app/ServerWindow.cpp b/src/servers/app/ServerWindow.cpp index dc8a2f5500..d10790b1ee 100644 --- a/src/servers/app/ServerWindow.cpp +++ b/src/servers/app/ServerWindow.cpp @@ -2541,6 +2541,9 @@ ServerWindow::_DispatchViewDrawingMessage(int32 code, fCurrentView->ConvertToScreenForDrawing(&ptList[i]); } + shapeFrame.OffsetBy(penLocation); + fCurrentView->ConvertToScreenForDrawing(&shapeFrame); + drawingEngine->DrawShape(shapeFrame, opCount, opList, ptCount, ptList, code == AS_FILL_SHAPE); } @@ -3430,7 +3433,7 @@ ServerWindow::MakeWindow(BRect frame, const char* name, // The non-offscreen ServerWindow uses the DrawingEngine instance from // the desktop. return new(std::nothrow) ::Window(frame, name, look, feel, flags, - workspace, this, new (nothrow) DrawingEngine(fDesktop->HWInterface())); + workspace, this, fDesktop->HWInterface()->CreateDrawingEngine()); } @@ -3591,6 +3594,11 @@ ServerWindow::_EnableDirectWindowMode() return B_ERROR; } + if (fDesktop->HWInterface()->FrontBuffer() == NULL) { + // direct window mode not supported + return B_UNSUPPORTED; + } + fDirectWindowInfo = new(std::nothrow) DirectWindowInfo; if (fDirectWindowInfo == NULL) return B_NO_MEMORY; diff --git a/src/servers/app/VirtualScreen.cpp b/src/servers/app/VirtualScreen.cpp index 91a9c103cf..eb12a1abf0 100644 --- a/src/servers/app/VirtualScreen.cpp +++ b/src/servers/app/VirtualScreen.cpp @@ -80,8 +80,8 @@ VirtualScreen::SetConfiguration(Desktop& desktop, _Reset(); ScreenList list; - status_t status = gScreenManager->AcquireScreens(&desktop, NULL, 0, false, - list); + status_t status = gScreenManager->AcquireScreens(&desktop, NULL, 0, + desktop.TargetScreen(), false, list); if (status != B_OK) { // TODO: we would try again here with force == true return status; @@ -200,7 +200,7 @@ VirtualScreen::ScreenByID(int32 id) const for (int32 i = fScreenList.CountItems(); i-- > 0;) { screen_item* item = fScreenList.ItemAt(i); - if (item->screen->ID() == id) + if (item->screen->ID() == id || id == B_MAIN_SCREEN_ID.id) return item->screen; } diff --git a/src/servers/app/drawing/DrawingEngine.cpp b/src/servers/app/drawing/DrawingEngine.cpp index 624b1ceae3..92fc01ae0c 100644 --- a/src/servers/app/drawing/DrawingEngine.cpp +++ b/src/servers/app/drawing/DrawingEngine.cpp @@ -194,9 +194,7 @@ DrawingEngine::SetCopyToFrontEnabled(bool enable) void DrawingEngine::CopyToFront(/*const*/ BRegion& region) { - int32 count = region.CountRects(); - for (int32 i = 0; i < count; i++) - fGraphicsCard->Invalidate(region.RectAt(i)); + fGraphicsCard->InvalidateRegion(region); } @@ -521,7 +519,7 @@ DrawingEngine::CopyRegion(/*const*/ BRegion* region, sortedRectList[nextSortedIndex].bottom = (int32)n->rect.bottom; nextSortedIndex++; } else { - BRect touched = _CopyRect(n->rect, xOffset, yOffset); + BRect touched = CopyRect(n->rect, xOffset, yOffset); fGraphicsCard->Invalidate(touched); } @@ -1399,7 +1397,7 @@ DrawingEngine::ReadBitmap(ServerBitmap *bitmap, bool drawCursor, BRect bounds) // #pragma mark - BRect -DrawingEngine::_CopyRect(BRect src, int32 xOffset, int32 yOffset) const +DrawingEngine::CopyRect(BRect src, int32 xOffset, int32 yOffset) const { // TODO: assumes drawing buffer is 32 bits (which it currently always is) BRect dst; diff --git a/src/servers/app/drawing/DrawingEngine.h b/src/servers/app/drawing/DrawingEngine.h index e97c4c8794..8ab968693e 100644 --- a/src/servers/app/drawing/DrawingEngine.h +++ b/src/servers/app/drawing/DrawingEngine.h @@ -34,160 +34,163 @@ class ServerFont; class DrawingEngine : public HWInterfaceListener { public: DrawingEngine(HWInterface* interface = NULL); - virtual ~DrawingEngine(); +virtual ~DrawingEngine(); // HWInterfaceListener interface - virtual void FrameBufferChanged(); +virtual void FrameBufferChanged(); - // for "changing" hardware - void SetHWInterface(HWInterface* interface); + // for "changing" hardware + void SetHWInterface(HWInterface* interface); - void SetCopyToFrontEnabled(bool enable); - bool CopyToFrontEnabled() const +virtual void SetCopyToFrontEnabled(bool enable); + bool CopyToFrontEnabled() const { return fCopyToFront; } - void CopyToFront(/*const*/ BRegion& region); +virtual void CopyToFront(/*const*/ BRegion& region); - // locking - bool LockParallelAccess(); - bool IsParallelAccessLocked(); - void UnlockParallelAccess(); + // locking + bool LockParallelAccess(); + bool IsParallelAccessLocked(); + void UnlockParallelAccess(); - bool LockExclusiveAccess(); - bool IsExclusiveAccessLocked(); - void UnlockExclusiveAccess(); + bool LockExclusiveAccess(); + bool IsExclusiveAccessLocked(); + void UnlockExclusiveAccess(); - // for screen shots - ServerBitmap* DumpToBitmap(); - status_t ReadBitmap(ServerBitmap *bitmap, bool drawCursor, + // for screen shots + ServerBitmap* DumpToBitmap(); +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) - void ConstrainClippingRegion(const BRegion* region); + // clipping for all drawing functions, passing a NULL region + // will remove any clipping (drawing allowed everywhere) +virtual void ConstrainClippingRegion(const BRegion* region); - void SetDrawState(const DrawState* state, +virtual void SetDrawState(const DrawState* state, int32 xOffset = 0, int32 yOffset = 0); - void SetHighColor(const rgb_color& color); - void SetLowColor(const rgb_color& color); - void SetPenSize(float size); - void SetStrokeMode(cap_mode lineCap, join_mode joinMode, +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); - void SetPattern(const struct pattern& pattern); - void SetDrawingMode(drawing_mode mode); - void SetDrawingMode(drawing_mode mode, +virtual void SetPattern(const struct pattern& pattern); +virtual void SetDrawingMode(drawing_mode mode); +virtual void SetDrawingMode(drawing_mode mode, drawing_mode& oldMode); - void SetBlendingMode(source_alpha srcAlpha, +virtual void SetBlendingMode(source_alpha srcAlpha, alpha_function alphaFunc); - void SetFont(const ServerFont& font); - void SetFont(const DrawState* state); +virtual void SetFont(const ServerFont& font); +virtual void SetFont(const DrawState* state); - void SuspendAutoSync(); - void Sync(); + void SuspendAutoSync(); + void Sync(); - // drawing functions - void CopyRegion(/*const*/ BRegion* region, + // drawing functions +virtual void CopyRegion(/*const*/ BRegion* region, int32 xOffset, int32 yOffset); - void InvertRect(BRect r); +virtual void InvertRect(BRect r); - void DrawBitmap(ServerBitmap* bitmap, +virtual void DrawBitmap(ServerBitmap* bitmap, const BRect& bitmapRect, const BRect& viewRect, uint32 options = 0); - // drawing primitives + // drawing primitives - void DrawArc(BRect r, const float& angle, +virtual void DrawArc(BRect r, const float& angle, const float& span, bool filled); - void FillArc(BRect r, const float& angle, +virtual void FillArc(BRect r, const float& angle, const float& span, const BGradient& gradient); - void DrawBezier(BPoint* pts, bool filled); - void FillBezier(BPoint* pts, const BGradient& gradient); +virtual void DrawBezier(BPoint* pts, bool filled); +virtual void FillBezier(BPoint* pts, const BGradient& gradient); - void DrawEllipse(BRect r, bool filled); - void FillEllipse(BRect r, const BGradient& gradient); +virtual void DrawEllipse(BRect r, bool filled); +virtual void FillEllipse(BRect r, const BGradient& gradient); - void DrawPolygon(BPoint* ptlist, int32 numpts, +virtual void DrawPolygon(BPoint* ptlist, int32 numpts, BRect bounds, bool filled, bool closed); - void FillPolygon(BPoint* ptlist, int32 numpts, +virtual void FillPolygon(BPoint* ptlist, int32 numpts, BRect bounds, const BGradient& gradient, bool closed); - // these rgb_color versions are used internally by the server - void StrokePoint(const BPoint& pt, + // these rgb_color versions are used internally by the server +virtual void StrokePoint(const BPoint& point, const rgb_color& color); - void StrokeRect(BRect r, const rgb_color &color); - void FillRect(BRect r, const rgb_color &color); - void FillRegion(BRegion& r, 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); - void StrokeRect(BRect r); - void FillRect(BRect r); - void FillRect(BRect r, const BGradient& gradient); +virtual void StrokeRect(BRect rect); +virtual void FillRect(BRect rect); +virtual void FillRect(BRect rect, const BGradient& gradient); - void FillRegion(BRegion& r); - void FillRegion(BRegion& r, const BGradient& gradient); +virtual void FillRegion(BRegion& region); +virtual void FillRegion(BRegion& region, + const BGradient& gradient); - void DrawRoundRect(BRect r, float xrad, +virtual void DrawRoundRect(BRect rect, float xrad, float yrad, bool filled); - void FillRoundRect(BRect r, float xrad, +virtual void FillRoundRect(BRect rect, float xrad, float yrad, const BGradient& gradient); - void DrawShape(const BRect& bounds, +virtual void DrawShape(const BRect& bounds, int32 opcount, const uint32* oplist, int32 ptcount, const BPoint* ptlist, bool filled); - void FillShape(const BRect& bounds, +virtual void FillShape(const BRect& bounds, int32 opcount, const uint32* oplist, int32 ptcount, const BPoint* ptlist, const BGradient& gradient); - void DrawTriangle(BPoint* pts, const BRect& bounds, +virtual void DrawTriangle(BPoint* points, const BRect& bounds, bool filled); - void FillTriangle(BPoint* pts, - const BRect& bounds, const BGradient& gradient); +virtual void FillTriangle(BPoint* points, const BRect& bounds, + const BGradient& gradient); - // this version used by Decorator - void StrokeLine(const BPoint& start, + // these versions are used by the Decorator +virtual void StrokeLine(const BPoint& start, const BPoint& end, const rgb_color& color); - void StrokeLine(const BPoint& start, +virtual void StrokeLine(const BPoint& start, const BPoint& end); - void StrokeLineArray(int32 numlines, +virtual void StrokeLineArray(int32 numlines, const ViewLineArrayInfo* data); - // -------- text related calls + // -------- text related calls - // returns the pen position behind the (virtually) drawn - // string - BPoint DrawString(const char* string, int32 length, + // returns the pen position behind the (virtually) drawn + // string +virtual BPoint DrawString(const char* string, int32 length, const BPoint& pt, escapement_delta* delta = NULL); - float StringWidth(const char* string, int32 length, + float StringWidth(const char* string, int32 length, escapement_delta* delta = NULL); - // convenience function which is independent of graphics - // state (to be used by Decorator or ServerApp etc) - float StringWidth(const char* string, + // convenience function which is independent of graphics + // state (to be used by Decorator or ServerApp etc) + float StringWidth(const char* string, int32 length, const ServerFont& font, escapement_delta* delta = NULL); - private: - BRect _CopyRect(BRect r, int32 xOffset, + // software rendering backend invoked by CopyRegion() for the sorted + // individual rects +virtual BRect CopyRect(BRect rect, int32 xOffset, int32 yOffset) const; - void _CopyRect(uint8* bits, uint32 width, +private: + void _CopyRect(uint8* bits, uint32 width, uint32 height, uint32 bytesPerRow, int32 xOffset, int32 yOffset) const; - inline void _CopyToFront(const BRect& frame); +inline void _CopyToFront(const BRect& frame); - Painter* fPainter; - HWInterface* fGraphicsCard; - uint32 fAvailableHWAccleration; - int32 fSuspendSyncLevel; - bool fCopyToFront; + Painter* fPainter; + HWInterface* fGraphicsCard; + uint32 fAvailableHWAccleration; + int32 fSuspendSyncLevel; + bool fCopyToFront; }; #endif // DRAWING_ENGINE_H_ diff --git a/src/servers/app/drawing/HWInterface.cpp b/src/servers/app/drawing/HWInterface.cpp index 4e40cbe815..fe23d4f466 100644 --- a/src/servers/app/drawing/HWInterface.cpp +++ b/src/servers/app/drawing/HWInterface.cpp @@ -18,6 +18,7 @@ #include "drawing_support.h" +#include "DrawingEngine.h" #include "RenderingBuffer.h" #include "SystemPalette.h" #include "UpdateQueue.h" @@ -79,6 +80,20 @@ HWInterface::Initialize() } +DrawingEngine* +HWInterface::CreateDrawingEngine() +{ + return new(std::nothrow) DrawingEngine(this); +} + + +EventStream* +HWInterface::CreateEventStream() +{ + return NULL; +} + + status_t HWInterface::GetAccelerantPath(BString &path) { @@ -165,6 +180,18 @@ HWInterface::Cursor() const } +ServerCursorReference +HWInterface::CursorAndDragBitmap() const +{ + if (!fFloatingOverlaysLock.Lock()) + return ServerCursorReference(NULL); + + ServerCursorReference reference(fCursorAndDragBitmap); + fFloatingOverlaysLock.Unlock(); + return reference; +} + + void HWInterface::SetCursorVisible(bool visible) { @@ -317,6 +344,22 @@ HWInterface::IsDoubleBuffered() const } +/*! The object needs to be already locked! +*/ +status_t +HWInterface::InvalidateRegion(BRegion& region) +{ + int32 count = region.CountRects(); + for (int32 i = 0; i < count; i++) { + status_t result = Invalidate(region.RectAt(i)); + if (result != B_OK) + return result; + } + + return B_OK; +} + + /*! The object needs to be already locked! */ status_t diff --git a/src/servers/app/drawing/HWInterface.h b/src/servers/app/drawing/HWInterface.h index 9e13b921f5..cef1207483 100644 --- a/src/servers/app/drawing/HWInterface.h +++ b/src/servers/app/drawing/HWInterface.h @@ -23,11 +23,13 @@ #include "ServerCursor.h" +class BString; +class DrawingEngine; +class EventStream; class Overlay; class RenderingBuffer; class ServerBitmap; class UpdateQueue; -class BString; enum { @@ -69,6 +71,13 @@ public: virtual status_t Initialize(); virtual status_t Shutdown() = 0; + // allocating a DrawingEngine attached to this HWInterface + virtual DrawingEngine* CreateDrawingEngine(); + + // creating an event stream specific for this HWInterface + // returns NULL when there is no specific event stream necessary + virtual EventStream* CreateEventStream(); + // screen mode stuff virtual status_t SetMode(const display_mode& mode) = 0; virtual void GetMode(display_mode* mode) = 0; @@ -115,6 +124,7 @@ public: // cursor handling (these do their own Read/Write locking) ServerCursorReference Cursor() const; + ServerCursorReference CursorAndDragBitmap() const; virtual void SetCursor(ServerCursor* cursor); virtual void SetCursorVisible(bool visible); bool IsCursorVisible(); @@ -122,7 +132,7 @@ public: virtual void MoveCursorTo(float x, float y); BPoint CursorPosition(); - void SetDragBitmap(const ServerBitmap* bitmap, + virtual void SetDragBitmap(const ServerBitmap* bitmap, const BPoint& offsetFromCursor); // overlay support @@ -148,6 +158,7 @@ public: virtual bool IsDoubleBuffered() const; // Invalidate is used for scheduling an area for updating + virtual status_t InvalidateRegion(BRegion& region); virtual status_t Invalidate(const BRect& frame); // while as CopyBackToFront() actually performs the operation // either directly or asynchronously by the UpdateQueue thread diff --git a/src/servers/app/drawing/Jamfile b/src/servers/app/drawing/Jamfile index c34276d202..52fe15dc34 100644 --- a/src/servers/app/drawing/Jamfile +++ b/src/servers/app/drawing/Jamfile @@ -28,3 +28,4 @@ StaticLibrary libasdrawing.a : ; SubInclude HAIKU_TOP src servers app drawing Painter ; +SubInclude HAIKU_TOP src servers app drawing remote ; diff --git a/src/servers/app/drawing/remote/Jamfile b/src/servers/app/drawing/remote/Jamfile new file mode 100644 index 0000000000..d55a61f523 --- /dev/null +++ b/src/servers/app/drawing/remote/Jamfile @@ -0,0 +1,25 @@ +SubDir HAIKU_TOP src servers app drawing remote ; + +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 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 ] ; +UseFreeTypeHeaders ; + +StaticLibrary libasremote.a : + NetReceiver.cpp + NetSender.cpp + + RemoteDrawingEngine.cpp + RemoteEventStream.cpp + RemoteHWInterface.cpp + RemoteMessage.cpp + + StreamingRingBuffer.cpp +; diff --git a/src/servers/app/drawing/remote/NetReceiver.cpp b/src/servers/app/drawing/remote/NetReceiver.cpp new file mode 100644 index 0000000000..e342066bc9 --- /dev/null +++ b/src/servers/app/drawing/remote/NetReceiver.cpp @@ -0,0 +1,110 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include "NetReceiver.h" + +#include "StreamingRingBuffer.h" + +#include + +#include +#include +#include + +#define TRACE(x...) /*debug_printf("NetReceiver: "x)*/ +#define TRACE_ERROR(x...) debug_printf("NetReceiver: "x) + + +NetReceiver::NetReceiver(BNetEndpoint *listener, StreamingRingBuffer *target) + : + fListener(listener), + fTarget(target), + fReceiverThread(-1), + fStopThread(false), + fEndpoint(NULL) +{ + fReceiverThread = spawn_thread(_NetworkReceiverEntry, "network receiver", + B_NORMAL_PRIORITY, this); + resume_thread(fReceiverThread); +} + + +NetReceiver::~NetReceiver() +{ + fStopThread = true; + + if (fEndpoint != NULL) + fEndpoint->Close(); + + //int32 result; + //wait_for_thread(fReceiverThread, &result); + // TODO: find out why closing the endpoint doesn't notify the waiter + + kill_thread(fReceiverThread); +} + + +int32 +NetReceiver::_NetworkReceiverEntry(void *data) +{ + return ((NetReceiver *)data)->_NetworkReceiver(); +} + + +status_t +NetReceiver::_NetworkReceiver() +{ + status_t result = fListener->Listen(); + if (result != B_OK) { + TRACE_ERROR("failed to listen on port: %s\n", strerror(result)); + return result; + } + + while (!fStopThread) { + fEndpoint = fListener->Accept(1000); + if (fEndpoint == NULL) + continue; + + int32 errorCount = 0; + TRACE("new endpoint connection: %p\n", fEndpoint); + while (!fStopThread) { + uint8 buffer[4096]; + int32 readSize = fEndpoint->Receive(buffer, sizeof(buffer)); + if (readSize < 0) { + TRACE_ERROR("read failed, closing connection: %s\n", + strerror(readSize)); + BNetEndpoint *endpoint = fEndpoint; + fEndpoint = NULL; + delete endpoint; + return readSize; + } + + if (readSize == 0) { + TRACE("read 0 bytes, retrying\n"); + snooze(100 * 1000); + errorCount++; + if (errorCount == 5) { + TRACE_ERROR("failed to read, assuming disconnect\n"); + break; + } + + continue; + } + + errorCount = 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; + } + } + } + + return B_OK; +} diff --git a/src/servers/app/drawing/remote/NetReceiver.h b/src/servers/app/drawing/remote/NetReceiver.h new file mode 100644 index 0000000000..21799a0791 --- /dev/null +++ b/src/servers/app/drawing/remote/NetReceiver.h @@ -0,0 +1,38 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ +#ifndef NET_RECEIVER_H +#define NET_RECEIVER_H + +#include +#include + +class BNetEndpoint; +class StreamingRingBuffer; + +class NetReceiver { +public: + NetReceiver(BNetEndpoint *listener, + StreamingRingBuffer *target); + ~NetReceiver(); + + BNetEndpoint * Endpoint() { return fEndpoint; } + +private: +static int32 _NetworkReceiverEntry(void *data); + status_t _NetworkReceiver(); + + BNetEndpoint * fListener; + StreamingRingBuffer * fTarget; + + thread_id fReceiverThread; + bool fStopThread; + + BNetEndpoint * fEndpoint; +}; + +#endif // NET_RECEIVER_H diff --git a/src/servers/app/drawing/remote/NetSender.cpp b/src/servers/app/drawing/remote/NetSender.cpp new file mode 100644 index 0000000000..0d7cca0d53 --- /dev/null +++ b/src/servers/app/drawing/remote/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/remote/NetSender.h b/src/servers/app/drawing/remote/NetSender.h new file mode 100644 index 0000000000..dbc415ee3e --- /dev/null +++ b/src/servers/app/drawing/remote/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/remote/RemoteDrawingEngine.cpp b/src/servers/app/drawing/remote/RemoteDrawingEngine.cpp new file mode 100644 index 0000000000..75028029b0 --- /dev/null +++ b/src/servers/app/drawing/remote/RemoteDrawingEngine.cpp @@ -0,0 +1,939 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include "RemoteDrawingEngine.h" +#include "RemoteMessage.h" + +#include "DrawState.h" + +#include + +#include + + +RemoteDrawingEngine::RemoteDrawingEngine(RemoteHWInterface* interface) + : + DrawingEngine(interface), + fHWInterface(interface), + fToken((uint32)this), // TODO: need to redo that for 64 bit + fExtendWidth(0), + fCallbackAdded(false), + fResultNotify(-1) +{ + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_CREATE_STATE); + message.Add(fToken); +} + + +RemoteDrawingEngine::~RemoteDrawingEngine() +{ + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_DELETE_STATE); + message.Add(fToken); + message.Flush(); + + if (fCallbackAdded) + fHWInterface->RemoveCallback(fToken); + if (fResultNotify >= 0) + delete_sem(fResultNotify); +} + + +// #pragma mark - + + +void +RemoteDrawingEngine::FrameBufferChanged() +{ + // Not allowed +} + + +// #pragma mark - + + +void +RemoteDrawingEngine::SetCopyToFrontEnabled(bool enabled) +{ + DrawingEngine::SetCopyToFrontEnabled(enabled); + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(enabled ? RP_ENABLE_SYNC_DRAWING : RP_DISABLE_SYNC_DRAWING); + message.Add(fToken); +} + + +// #pragma mark - + + +//! the RemoteDrawingEngine needs to be locked! +void +RemoteDrawingEngine::ConstrainClippingRegion(const BRegion* region) +{ + if (fClippingRegion == *region) + return; + + fClippingRegion = *region; + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_CONSTRAIN_CLIPPING_REGION); + message.Add(fToken); + message.AddRegion(*region); +} + + +void +RemoteDrawingEngine::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()); + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_OFFSETS); + message.Add(fToken); + message.Add(xOffset); + message.Add(yOffset); +} + + +void +RemoteDrawingEngine::SetHighColor(const rgb_color& color) +{ + if (fState.HighColor() == color) + return; + + fState.SetHighColor(color); + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_HIGH_COLOR); + message.Add(fToken); + message.Add(color); +} + + +void +RemoteDrawingEngine::SetLowColor(const rgb_color& color) +{ + if (fState.LowColor() == color) + return; + + fState.SetLowColor(color); + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_LOW_COLOR); + message.Add(fToken); + message.Add(color); +} + + +void +RemoteDrawingEngine::SetPenSize(float size) +{ + if (fState.PenSize() == size) + return; + + fState.SetPenSize(size); + fExtendWidth = -(size / 2); + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_PEN_SIZE); + message.Add(fToken); + message.Add(size); +} + + +void +RemoteDrawingEngine::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); + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_STROKE_MODE); + message.Add(fToken); + message.Add(lineCap); + message.Add(joinMode); + message.Add(miterLimit); +} + + +void +RemoteDrawingEngine::SetBlendingMode(source_alpha sourceAlpha, + alpha_function alphaFunc) +{ + if (fState.AlphaSrcMode() == sourceAlpha + && fState.AlphaFncMode() == alphaFunc) + return; + + fState.SetBlendingMode(sourceAlpha, alphaFunc); + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_BLENDING_MODE); + message.Add(fToken); + message.Add(sourceAlpha); + message.Add(alphaFunc); +} + + +void +RemoteDrawingEngine::SetPattern(const struct pattern& pattern) +{ + if (fState.GetPattern() == pattern) + return; + + fState.SetPattern(pattern); + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_PATTERN); + message.Add(fToken); + message.Add(pattern); +} + + +void +RemoteDrawingEngine::SetDrawingMode(drawing_mode mode) +{ + if (fState.GetDrawingMode() == mode) + return; + + fState.SetDrawingMode(mode); + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_DRAWING_MODE); + message.Add(fToken); + message.Add(mode); +} + + +void +RemoteDrawingEngine::SetDrawingMode(drawing_mode mode, drawing_mode& oldMode) +{ + oldMode = fState.GetDrawingMode(); + SetDrawingMode(mode); +} + + +void +RemoteDrawingEngine::SetFont(const ServerFont& font) +{ + if (fState.Font() == font) + return; + + fState.SetFont(font); + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_SET_FONT); + message.Add(fToken); + message.AddFont(font); +} + + +void +RemoteDrawingEngine::SetFont(const DrawState* state) +{ + SetFont(state->Font()); +} + + +// #pragma mark - + + +BRect +RemoteDrawingEngine::CopyRect(BRect rect, int32 xOffset, int32 yOffset) const +{ + RemoteMessage 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 +RemoteDrawingEngine::InvertRect(BRect rect) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_INVERT_RECT); + message.Add(fToken); + message.Add(rect); +} + + +void +RemoteDrawingEngine::DrawBitmap(ServerBitmap* bitmap, const BRect& _bitmapRect, + const BRect& _viewRect, uint32 options) +{ + if (!fClippingRegion.Intersects(_viewRect)) + return; + + BRect viewRect = _viewRect; + BRect bitmapRect = _bitmapRect; + if (bitmapRect.IntegerWidth() == viewRect.IntegerWidth() + && bitmapRect.IntegerHeight() == viewRect.IntegerHeight()) { + // unscaled bitmap we can chop off stuff we don't need + BRegion target(viewRect); + target.IntersectWith(&fClippingRegion); + BRect frame = target.Frame(); + + if (frame != viewRect) { + BPoint offset = frame.LeftTop() - viewRect.LeftTop(); + viewRect = frame; + bitmapRect = viewRect.OffsetToCopy(bitmapRect.LeftTop() + offset); + } + } + + UtilityBitmap* other = NULL; + BRect bounds = bitmap->Bounds(); + BRect newBounds; + newBounds.right + = min_c(bounds.IntegerWidth(), bitmapRect.IntegerWidth()); + newBounds.bottom + = min_c(bounds.IntegerHeight(), bitmapRect.IntegerHeight()); + + if (newBounds.IntegerWidth() < bounds.IntegerWidth() + || newBounds.IntegerHeight() < bounds.IntegerHeight()) { + + other = new(std::nothrow) UtilityBitmap(newBounds, bitmap->ColorSpace(), + bitmap->Flags()); + + if (other != NULL && other->ImportBits(bitmap->Bits(), + bitmap->BitsLength(), bitmap->BytesPerRow(), + bitmap->ColorSpace(), bitmapRect.LeftTop(), BPoint(0, 0), + newBounds.IntegerWidth() + 1, + newBounds.IntegerHeight() + 1) == B_OK) { + bitmapRect.OffsetTo(0, 0); + bitmap = other; + } + } + + // TODO: we may want to cache/checksum bitmaps + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_DRAW_BITMAP); + message.Add(fToken); + message.Add(bitmapRect); + message.Add(viewRect); + message.Add(options); + message.AddBitmap(*bitmap); + + if (other != NULL) + delete other; +} + + +void +RemoteDrawingEngine::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; + + RemoteMessage 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 +RemoteDrawingEngine::FillArc(BRect rect, const float& angle, const float& span, + const BGradient& gradient) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + RemoteMessage 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 +RemoteDrawingEngine::DrawBezier(BPoint* points, bool filled) +{ + BRect bounds = _BuildBounds(points, 4); + if (!filled) + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(filled ? RP_FILL_BEZIER : RP_STROKE_BEZIER); + message.Add(fToken); + message.AddList(points, 4); +} + + +void +RemoteDrawingEngine::FillBezier(BPoint* points, const BGradient& gradient) +{ + BRect bounds = _BuildBounds(points, 4); + if (!fClippingRegion.Intersects(bounds)) + return; + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_BEZIER_GRADIENT); + message.Add(fToken); + message.AddList(points, 4); + message.AddGradient(gradient); +} + + +void +RemoteDrawingEngine::DrawEllipse(BRect rect, bool filled) +{ + BRect bounds = rect; + if (!filled) + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(filled ? RP_FILL_ELLIPSE : RP_STROKE_ELLIPSE); + message.Add(fToken); + message.Add(rect); +} + + +void +RemoteDrawingEngine::FillEllipse(BRect rect, const BGradient& gradient) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_ELLIPSE_GRADIENT); + message.Add(fToken); + message.Add(rect); + message.AddGradient(gradient); +} + + +void +RemoteDrawingEngine::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; + + RemoteMessage 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 +RemoteDrawingEngine::FillPolygon(BPoint* pointList, int32 numPoints, + BRect bounds, const BGradient& gradient, bool closed) +{ + if (!fClippingRegion.Intersects(bounds)) + return; + + RemoteMessage 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 +RemoteDrawingEngine::StrokePoint(const BPoint& point, const rgb_color& color) +{ + BRect bounds(point, point); + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_STROKE_POINT_COLOR); + message.Add(fToken); + message.Add(point); + message.Add(color); +} + + +void +RemoteDrawingEngine::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; + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_STROKE_LINE_1PX_COLOR); + message.Add(fToken); + message.AddList(points, 2); + message.Add(color); +} + + +void +RemoteDrawingEngine::StrokeRect(BRect rect, const rgb_color &color) +{ + BRect bounds = rect; + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_STROKE_RECT_1PX_COLOR); + message.Add(fToken); + message.Add(rect); + message.Add(color); +} + + +void +RemoteDrawingEngine::FillRect(BRect rect, const rgb_color& color) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_RECT_COLOR); + message.Add(fToken); + message.Add(rect); + message.Add(color); +} + + +void +RemoteDrawingEngine::FillRegion(BRegion& region, const rgb_color& color) +{ + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_REGION_COLOR_NO_CLIPPING); + message.AddRegion(region); + message.Add(color); +} + + +// #pragma mark - DrawState versions + + +void +RemoteDrawingEngine::StrokeRect(BRect rect) +{ + BRect bounds = rect; + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_STROKE_RECT); + message.Add(fToken); + message.Add(rect); +} + + +void +RemoteDrawingEngine::FillRect(BRect rect) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_RECT); + message.Add(fToken); + message.Add(rect); +} + + +void +RemoteDrawingEngine::FillRect(BRect rect, const BGradient& gradient) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_RECT_GRADIENT); + message.Add(fToken); + message.Add(rect); + message.AddGradient(gradient); +} + + +void +RemoteDrawingEngine::FillRegion(BRegion& region) +{ + BRegion clippedRegion = region; + clippedRegion.IntersectWith(&fClippingRegion); + if (clippedRegion.CountRects() == 0) + return; + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_FILL_REGION); + message.Add(fToken); + message.AddRegion(clippedRegion.CountRects() < region.CountRects() + ? clippedRegion : region); +} + + +void +RemoteDrawingEngine::FillRegion(BRegion& region, const BGradient& gradient) +{ + BRegion clippedRegion = region; + clippedRegion.IntersectWith(&fClippingRegion); + if (clippedRegion.CountRects() == 0) + return; + + RemoteMessage 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 +RemoteDrawingEngine::DrawRoundRect(BRect rect, float xRadius, float yRadius, + bool filled) +{ + BRect bounds = rect; + if (!filled) + bounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(bounds)) + return; + + RemoteMessage 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 +RemoteDrawingEngine::FillRoundRect(BRect rect, float xRadius, float yRadius, + const BGradient& gradient) +{ + if (!fClippingRegion.Intersects(rect)) + return; + + RemoteMessage 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 +RemoteDrawingEngine::DrawShape(const BRect& bounds, int32 opCount, + const uint32* opList, int32 pointCount, const BPoint* pointList, + bool filled) +{ + BRect clipBounds = bounds; + if (!filled) + clipBounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(clipBounds)) + return; + + RemoteMessage 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); +} + + +void +RemoteDrawingEngine::FillShape(const BRect& bounds, int32 opCount, + const uint32* opList, int32 pointCount, const BPoint* pointList, + const BGradient& gradient) +{ + if (!fClippingRegion.Intersects(bounds)) + return; + + RemoteMessage 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); +} + + +void +RemoteDrawingEngine::DrawTriangle(BPoint* points, const BRect& bounds, + bool filled) +{ + BRect clipBounds = bounds; + if (!filled) + clipBounds.InsetBy(fExtendWidth, fExtendWidth); + + if (!fClippingRegion.Intersects(clipBounds)) + return; + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(filled ? RP_FILL_TRIANGLE : RP_STROKE_TRIANGLE); + message.Add(fToken); + message.AddList(points, 3); + message.Add(bounds); +} + + +void +RemoteDrawingEngine::FillTriangle(BPoint* points, const BRect& bounds, + const BGradient& gradient) +{ + if (!fClippingRegion.Intersects(bounds)) + return; + + RemoteMessage 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 +RemoteDrawingEngine::StrokeLine(const BPoint &start, const BPoint &end) +{ + BPoint points[2] = { start, end }; + BRect bounds = _BuildBounds(points, 2); + + if (!fClippingRegion.Intersects(bounds)) + return; + + RemoteMessage message(NULL, fHWInterface->SendBuffer()); + message.Start(RP_STROKE_LINE); + message.Add(fToken); + message.AddList(points, 2); +} + + +void +RemoteDrawingEngine::StrokeLineArray(int32 numLines, + const ViewLineArrayInfo *lineData) +{ + RemoteMessage 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 +RemoteDrawingEngine::DrawString(const char* string, int32 length, + const BPoint& point, escapement_delta* delta) +{ + RemoteMessage 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; +} + + +float +RemoteDrawingEngine::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 +RemoteDrawingEngine::ReadBitmap(ServerBitmap* bitmap, bool drawCursor, + BRect bounds) +{ + if (_AddCallback() != B_OK) + return B_UNSUPPORTED; + + RemoteMessage 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 +RemoteDrawingEngine::_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 +RemoteDrawingEngine::_DrawingEngineResult(void* cookie, RemoteMessage& message) +{ + RemoteDrawingEngine* engine = (RemoteDrawingEngine*)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 +RemoteDrawingEngine::_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; +} diff --git a/src/servers/app/drawing/remote/RemoteDrawingEngine.h b/src/servers/app/drawing/remote/RemoteDrawingEngine.h new file mode 100644 index 0000000000..1d25bf9d5e --- /dev/null +++ b/src/servers/app/drawing/remote/RemoteDrawingEngine.h @@ -0,0 +1,165 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ +#ifndef REMOTE_DRAWING_ENGINE_H +#define REMOTE_DRAWING_ENGINE_H + +#include "DrawingEngine.h" +#include "DrawState.h" +#include "RemoteHWInterface.h" +#include "ServerFont.h" + +class BPoint; +class BRect; +class BRegion; + +class ServerBitmap; + +class RemoteDrawingEngine : public DrawingEngine { +public: + RemoteDrawingEngine( + RemoteHWInterface* interface); +virtual ~RemoteDrawingEngine(); + + // 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); +virtual void FillShape(const BRect& bounds, + int32 opCount, const uint32* opList, + int32 pointCount, const BPoint* pointList, + const BGradient& gradient); + +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 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, + RemoteMessage& message); + + BRect _BuildBounds(BPoint* points, int32 pointCount); + + RemoteHWInterface* fHWInterface; + uint32 fToken; + + DrawState fState; + BRegion fClippingRegion; + float fExtendWidth; + + bool fCallbackAdded; + sem_id fResultNotify; + BPoint fDrawStringResult; + float fStringWidthResult; + BBitmap* fReadBitmapResult; +}; + +#endif // REMOTE_DRAWING_ENGINE_H diff --git a/src/servers/app/drawing/remote/RemoteEventStream.cpp b/src/servers/app/drawing/remote/RemoteEventStream.cpp new file mode 100644 index 0000000000..eb681044af --- /dev/null +++ b/src/servers/app/drawing/remote/RemoteEventStream.cpp @@ -0,0 +1,215 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include "RemoteEventStream.h" + +#include "RemoteMessage.h" +#include "StreamingRingBuffer.h" + +#include + +#include + + +RemoteEventStream::RemoteEventStream() + : + fEventList(10, true), + fEventListLocker("remote event list"), + fEventNotification(-1), + fWaitingOnEvent(false), + fLatestMouseMovedEvent(NULL), + fMousePosition(0, 0), + fMouseButtons(0), + fModifiers(0) +{ + fEventNotification = create_sem(0, "remote event notification"); +} + + +RemoteEventStream::~RemoteEventStream() +{ + delete_sem(fEventNotification); +} + + +void +RemoteEventStream::UpdateScreenBounds(BRect bounds) +{ +} + + +bool +RemoteEventStream::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 +RemoteEventStream::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* +RemoteEventStream::PeekLatestMouseMoved() +{ + return fLatestMouseMovedEvent; +} + + +bool +RemoteEventStream::EventReceived(RemoteMessage& 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) + 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); + + 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/remote/RemoteEventStream.h b/src/servers/app/drawing/remote/RemoteEventStream.h new file mode 100644 index 0000000000..95366b89d7 --- /dev/null +++ b/src/servers/app/drawing/remote/RemoteEventStream.h @@ -0,0 +1,45 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ +#ifndef REMOTE_EVENT_STREAM_H +#define REMOTE_EVENT_STREAM_H + +#include "EventStream.h" + +#include +#include + +class RemoteMessage; + +class RemoteEventStream : public EventStream { +public: + RemoteEventStream(); +virtual ~RemoteEventStream(); + +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(RemoteMessage& message); + +private: + BObjectList fEventList; + BLocker fEventListLocker; + sem_id fEventNotification; + bool fWaitingOnEvent; + BMessage* fLatestMouseMovedEvent; + + BPoint fMousePosition; + uint32 fMouseButtons; + uint32 fModifiers; +}; + +#endif // REMOTE_EVENT_STREAM_H diff --git a/src/servers/app/drawing/remote/RemoteHWInterface.cpp b/src/servers/app/drawing/remote/RemoteHWInterface.cpp new file mode 100644 index 0000000000..18fe8327f0 --- /dev/null +++ b/src/servers/app/drawing/remote/RemoteHWInterface.cpp @@ -0,0 +1,572 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include "RemoteHWInterface.h" +#include "RemoteDrawingEngine.h" +#include "RemoteEventStream.h" +#include "RemoteMessage.h" + +#include "NetReceiver.h" +#include "NetSender.h" +#include "StreamingRingBuffer.h" + +#include +#include + +#include +#include + + +#define TRACE(x...) /*debug_printf("RemoteHWInterface: "x)*/ +#define TRACE_ALWAYS(x...) debug_printf("RemoteHWInterface: "x) +#define TRACE_ERROR(x...) debug_printf("RemoteHWInterface: "x) + + +struct callback_info { + uint32 token; + CallbackFunction callback; + void* cookie; +}; + + +RemoteHWInterface::RemoteHWInterface(const char* target) + : + HWInterface(), + fTarget(target), + fRemoteHost(NULL), + fRemotePort(10900), + fIsConnected(false), + fProtocolVersion(100), + fConnectionSpeed(0), + fListenPort(10901), + fSendEndpoint(NULL), + fReceiveEndpoint(NULL), + fSendBuffer(NULL), + fReceiveBuffer(NULL), + fSender(NULL), + fReceiver(NULL), + fEventThread(-1), + fEventStream(NULL), + fCallbackLocker("callback locker") +{ + fDisplayMode.virtual_width = 640; + fDisplayMode.virtual_height = 480; + fDisplayMode.space = B_RGB32; + + fRemoteHost = strdup(fTarget); + char *portStart = strchr(fRemoteHost, ':'); + if (portStart != NULL) { + portStart[0] = 0; + portStart++; + if (sscanf(portStart, "%lu", &fRemotePort) != 1) { + fInitStatus = B_BAD_VALUE; + return; + } + + fListenPort = fRemotePort + 1; + } + + fSendEndpoint = new(std::nothrow) BNetEndpoint(); + if (fSendEndpoint == NULL) { + fInitStatus = B_NO_MEMORY; + return; + } + + 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; + + fSender = new(std::nothrow) NetSender(fSendEndpoint, fSendBuffer); + if (fSender == NULL) { + fInitStatus = B_NO_MEMORY; + return; + } + + fReceiver = new(std::nothrow) NetReceiver(fReceiveEndpoint, fReceiveBuffer); + if (fReceiver == NULL) { + fInitStatus = B_NO_MEMORY; + return; + } + + fEventStream = new(std::nothrow) RemoteEventStream(); + if (fEventStream == NULL) { + fInitStatus = B_NO_MEMORY; + return; + } + + fSendEndpoint->SetTimeout(3 * 1000 * 1000); + fInitStatus = _Connect(); + if (fInitStatus != B_OK) + return; + + fEventThread = spawn_thread(_EventThreadEntry, "remote event thread", + B_NORMAL_PRIORITY, this); + if (fEventThread < 0) { + fInitStatus = fEventThread; + return; + } + + resume_thread(fEventThread); +} + + +RemoteHWInterface::~RemoteHWInterface() +{ + delete fReceiver; + delete fReceiveBuffer; + + delete fSendBuffer; + delete fSender; + + delete fReceiveEndpoint; + delete fSendEndpoint; + + delete fEventStream; + + free(fRemoteHost); +} + + +status_t +RemoteHWInterface::Initialize() +{ + return fInitStatus; +} + + +status_t +RemoteHWInterface::Shutdown() +{ + _Disconnect(); + return B_OK; +} + + +DrawingEngine* +RemoteHWInterface::CreateDrawingEngine() +{ + return new(std::nothrow) RemoteDrawingEngine(this); +} + + +EventStream* +RemoteHWInterface::CreateEventStream() +{ + return fEventStream; +} + + +status_t +RemoteHWInterface::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 +RemoteHWInterface::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* +RemoteHWInterface::_FindCallback(uint32 token) +{ + BAutolock lock(fCallbackLocker); + return fCallbacks.BinarySearchByKey(token, &_CallbackCompare); +} + + +int +RemoteHWInterface::_CallbackCompare(const uint32* key, + const callback_info* info) +{ + if (info->token == *key) + return 0; + + if (info->token < *key) + return -1; + + return 1; +} + + +int32 +RemoteHWInterface::_EventThreadEntry(void* data) +{ + return ((RemoteHWInterface*)data)->_EventThread(); +} + + +status_t +RemoteHWInterface::_EventThread() +{ + RemoteMessage 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 remote event code %u\n", code); + break; + } + } + } +} + + +status_t +RemoteHWInterface::_Connect() +{ + 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; + } + + RemoteMessage 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; + return B_OK; +} + + +void +RemoteHWInterface::_Disconnect() +{ + if (fIsConnected) { + RemoteMessage message(NULL, fSendBuffer); + message.Start(RP_CLOSE_CONNECTION); + message.Flush(); + fIsConnected = false; + } + + if (fSendEndpoint != NULL) + fSendEndpoint->Close(); + if (fReceiveEndpoint != NULL) + fReceiveEndpoint->Close(); +} + + +status_t +RemoteHWInterface::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 +RemoteHWInterface::GetMode(display_mode* mode) +{ + if (mode == NULL || !ReadLock()) + return; + + *mode = fDisplayMode; + ReadUnlock(); +} + + +status_t +RemoteHWInterface::GetDeviceInfo(accelerant_device_info* info) +{ + if (!ReadLock()) + return B_ERROR; + + info->version = fProtocolVersion; + info->dac_speed = fConnectionSpeed; + info->memory = 33554432; // 32MB + sprintf(info->name, "Haiku, Inc. RemoteHWInterface"); + sprintf(info->chipset, "Haiku, Inc. Chipset"); + sprintf(info->serial_no, fTarget); + + ReadUnlock(); + return B_OK; +} + + +status_t +RemoteHWInterface::GetFrameBufferConfig(frame_buffer_config& config) +{ + // We don't actually have a frame buffer. + return B_UNSUPPORTED; +} + + +status_t +RemoteHWInterface::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 +RemoteHWInterface::GetPixelClockLimits(display_mode* mode, uint32* low, + uint32* high) +{ + return B_UNSUPPORTED; +} + + +status_t +RemoteHWInterface::GetTimingConstraints(display_timing_constraints* constraints) +{ + return B_UNSUPPORTED; +} + + +status_t +RemoteHWInterface::ProposeMode(display_mode* candidate, const display_mode* low, + const display_mode* high) +{ + return B_UNSUPPORTED; +} + + +status_t +RemoteHWInterface::SetDPMSMode(uint32 state) +{ + return B_UNSUPPORTED; +} + + +uint32 +RemoteHWInterface::DPMSMode() +{ + return B_UNSUPPORTED; +} + + +uint32 +RemoteHWInterface::DPMSCapabilities() +{ + return 0; +} + + +sem_id +RemoteHWInterface::RetraceSemaphore() +{ + return -1; +} + + +status_t +RemoteHWInterface::WaitForRetrace(bigtime_t timeout) +{ + return B_UNSUPPORTED; +} + + +void +RemoteHWInterface::SetCursor(ServerCursor* cursor) +{ + HWInterface::SetCursor(cursor); + RemoteMessage message(NULL, fSendBuffer); + message.Start(RP_SET_CURSOR); + message.AddCursor(Cursor().Cursor()); +} + + +void +RemoteHWInterface::SetCursorVisible(bool visible) +{ + HWInterface::SetCursorVisible(visible); + RemoteMessage message(NULL, fSendBuffer); + message.Start(RP_SET_CURSOR_VISIBLE); + message.Add(visible); +} + + +void +RemoteHWInterface::MoveCursorTo(float x, float y) +{ + HWInterface::MoveCursorTo(x, y); + RemoteMessage message(NULL, fSendBuffer); + message.Start(RP_MOVE_CURSOR_TO); + message.Add(x); + message.Add(y); +} + + +void +RemoteHWInterface::SetDragBitmap(const ServerBitmap* bitmap, + const BPoint& offsetFromCursor) +{ + HWInterface::SetDragBitmap(bitmap, offsetFromCursor); + RemoteMessage message(NULL, fSendBuffer); + message.Start(RP_SET_CURSOR); + message.AddCursor(CursorAndDragBitmap().Cursor()); +} + + +RenderingBuffer* +RemoteHWInterface::FrontBuffer() const +{ + return NULL; +} + + +RenderingBuffer* +RemoteHWInterface::BackBuffer() const +{ + return NULL; +} + + +bool +RemoteHWInterface::IsDoubleBuffered() const +{ + return false; +} + + +status_t +RemoteHWInterface::InvalidateRegion(BRegion& region) +{ + RemoteMessage message(NULL, fSendBuffer); + message.Start(RP_INVALIDATE_REGION); + message.AddRegion(region); + return B_OK; +} + + +status_t +RemoteHWInterface::Invalidate(const BRect& frame) +{ + RemoteMessage message(NULL, fSendBuffer); + message.Start(RP_INVALIDATE_RECT); + message.Add(frame); + return B_OK; +} + + +status_t +RemoteHWInterface::CopyBackToFront(const BRect& frame) +{ + return B_OK; +} diff --git a/src/servers/app/drawing/remote/RemoteHWInterface.h b/src/servers/app/drawing/remote/RemoteHWInterface.h new file mode 100644 index 0000000000..c196ecbc1f --- /dev/null +++ b/src/servers/app/drawing/remote/RemoteHWInterface.h @@ -0,0 +1,127 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ +#ifndef REMOTE_HW_INTERFACE_H +#define REMOTE_HW_INTERFACE_H + +#include "HWInterface.h" + +#include +#include + +class BNetEndpoint; +class StreamingRingBuffer; +class NetSender; +class NetReceiver; +class RemoteEventStream; +class RemoteMessage; + +struct callback_info; + +typedef bool (*CallbackFunction)(void* cookie, RemoteMessage& message); + + +class RemoteHWInterface : public HWInterface { +public: + RemoteHWInterface(const char* target); +virtual ~RemoteHWInterface(); + +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; } + + 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; + + NetSender* fSender; + NetReceiver* fReceiver; + + thread_id fEventThread; + RemoteEventStream* fEventStream; + + BLocker fCallbackLocker; + BObjectList fCallbacks; +}; + +#endif // REMOTE_HW_INTERFACE_H diff --git a/src/servers/app/drawing/remote/RemoteMessage.cpp b/src/servers/app/drawing/remote/RemoteMessage.cpp new file mode 100644 index 0000000000..1c8db0cedc --- /dev/null +++ b/src/servers/app/drawing/remote/RemoteMessage.cpp @@ -0,0 +1,501 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include "RemoteMessage.h" + +#ifndef CLIENT_COMPILE +#include "DrawState.h" +#include "ServerBitmap.h" +#include "ServerCursor.h" +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + + +status_t +RemoteMessage::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; +} + + +#ifndef CLIENT_COMPILE +void +RemoteMessage::AddBitmap(const ServerBitmap& bitmap) +{ + Add(bitmap.Width()); + Add(bitmap.Height()); + Add(bitmap.BytesPerRow()); + 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 +RemoteMessage::AddFont(const ServerFont& font) +{ + 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 +RemoteMessage::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 +RemoteMessage::AddArrayLine(const ViewLineArrayInfo& line) +{ + Add(line.startPoint); + Add(line.endPoint); + Add(line.color); +} + + +void +RemoteMessage::AddCursor(const ServerCursor& cursor) +{ + Add(cursor.GetHotSpot()); + AddBitmap(cursor); +} + + +void +RemoteMessage::AddPattern(const Pattern& pattern) +{ + Add(pattern.GetPattern()); +} + +#else // !CLIENT_COMPILE + +void +RemoteMessage::AddBitmap(const BBitmap& bitmap) +{ + BRect bounds = bitmap.Bounds(); + Add(bounds.IntegerWidth() + 1); + Add(bounds.IntegerHeight() + 1); + Add(bitmap.BytesPerRow()); + 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; +} +#endif // !CLIENT_COMPILE + + +void +RemoteMessage::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 +RemoteMessage::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) + return readSize; + if ((uint32)readSize != length) + return B_ERROR; + + fDataLeft -= readSize; + + string[length] = 0; + *_string = string; + _length = length; + return B_OK; +} + + +status_t +RemoteMessage::ReadBitmap(BBitmap** _bitmap) +{ + color_space colorSpace; + uint32 bitsLength, flags; + int32 width, height, bytesPerRow; + + Read(width); + Read(height); + Read(bytesPerRow); + 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 +RemoteMessage::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 +RemoteMessage::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 +RemoteMessage::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) + 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 +RemoteMessage::ReadArrayLine(BPoint& startPoint, BPoint& endPoint, + rgb_color& color) +{ + Read(startPoint); + Read(endPoint); + return Read(color); +} diff --git a/src/servers/app/drawing/remote/RemoteMessage.h b/src/servers/app/drawing/remote/RemoteMessage.h new file mode 100644 index 0000000000..8cd97fbe35 --- /dev/null +++ b/src/servers/app/drawing/remote/RemoteMessage.h @@ -0,0 +1,359 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ +#ifndef REMOTE_MESSAGE_H +#define REMOTE_MESSAGE_H + +#ifndef CLIENT_COMPILE +# include "PatternHandler.h" +# include +#endif + +#include "StreamingRingBuffer.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_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_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 RemoteMessage { +public: + RemoteMessage(StreamingRingBuffer* source, + StreamingRingBuffer *target); + ~RemoteMessage(); + + void Start(uint16 code); + status_t Flush(); + + 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); + +#ifndef CLIENT_COMPILE + void AddBitmap(const ServerBitmap& bitmap); + 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); +#else + void AddBitmap(const BBitmap& bitmap); +#endif + + 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); + 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 +RemoteMessage::RemoteMessage(StreamingRingBuffer* source, + StreamingRingBuffer* target) + : + fSource(source), + fTarget(target), + fBuffer(NULL), + fAvailable(0), + fWriteIndex(0), + fDataLeft(0) +{ +} + + +inline +RemoteMessage::~RemoteMessage() +{ + if (fWriteIndex > 0) + Flush(); + free(fBuffer); +} + + +inline void +RemoteMessage::Start(uint16 code) +{ + if (fWriteIndex > 0) + Flush(); + + Add(code); + + uint32 sizeDummy; + Add(sizeDummy); +} + + +inline status_t +RemoteMessage::Flush() +{ + if (fWriteIndex == 0) + return B_NO_INIT; + + uint32 length = fWriteIndex; + fAvailable += fWriteIndex; + fWriteIndex = 0; + + memcpy(fBuffer + sizeof(uint16), &length, sizeof(uint32)); + return fTarget->Write(fBuffer, length); +} + + +template +inline void +RemoteMessage::Add(const T& value) +{ + if (!_MakeSpace(sizeof(T))) + return; + + memcpy(fBuffer + fWriteIndex, &value, sizeof(T)); + fWriteIndex += sizeof(T); + fAvailable -= sizeof(T); +} + + +inline void +RemoteMessage::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 +RemoteMessage::AddRegion(const BRegion& region) +{ + int32 rectCount = region.CountRects(); + Add(rectCount); + + for (int32 i = 0; i < rectCount; i++) + Add(region.RectAt(i)); +} + + +template +inline void +RemoteMessage::AddList(const T* array, int32 count) +{ + for (int32 i = 0; i < count; i++) + Add(array[i]); +} + + +template +inline status_t +RemoteMessage::Read(T& value) +{ + 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 +RemoteMessage::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 +RemoteMessage::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 +RemoteMessage::_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 // REMOTE_MESSAGE_H diff --git a/src/servers/app/drawing/remote/StreamingRingBuffer.cpp b/src/servers/app/drawing/remote/StreamingRingBuffer.cpp new file mode 100644 index 0000000000..601bc2293e --- /dev/null +++ b/src/servers/app/drawing/remote/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/remote/StreamingRingBuffer.h b/src/servers/app/drawing/remote/StreamingRingBuffer.h new file mode 100644 index 0000000000..a38744b085 --- /dev/null +++ b/src/servers/app/drawing/remote/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