/* * Copyright 2009-2014, Haiku, Inc. * Distributed under the terms of the MIT License. * * Authors: * Michael Lotz */ #include "NetReceiver.h" #include "NetSender.h" #include "RemoteMessage.h" #include "RemoteView.h" #include "StreamingRingBuffer.h" #include #include #include #include #include #include #include #include #include #include #include static const uint8 kCursorData[] = { 16 /* size, 16x16 */, 1 /* depth, 1 bit per pixel */, 0, 0, /* hot spot at 0, 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; #define TRACE(x...) /*printf("RemoteView: " x)*/ #define TRACE_ALWAYS(x...) printf("RemoteView: " x) #define TRACE_ERROR(x...) printf("RemoteView: " x) typedef struct engine_state { uint32 token; BView * view; ::pattern pattern; BRegion clipping_region; float pen_size; bool sync_drawing; } engine_state; RemoteView::RemoteView(BRect frame, const char *remoteHost, uint16 remotePort) : BView(frame, "RemoteView", B_FOLLOW_NONE, B_WILL_DRAW), fInitStatus(B_NO_INIT), fIsConnected(false), fReceiveBuffer(NULL), fSendBuffer(NULL), fEndpoint(NULL), fReceiver(NULL), fSender(NULL), fStopThread(false), fOffscreenBitmap(NULL), fOffscreen(NULL), fViewCursor(kCursorData), fCursorBitmap(NULL), fCursorVisible(false) { fReceiveBuffer = new(std::nothrow) StreamingRingBuffer(16 * 1024); if (fReceiveBuffer == NULL) { fInitStatus = B_NO_MEMORY; TRACE_ERROR("no memory available\n"); return; } fInitStatus = fReceiveBuffer->InitCheck(); if (fInitStatus != B_OK) return; fSendBuffer = new(std::nothrow) StreamingRingBuffer(16 * 1024); if (fSendBuffer == NULL) { fInitStatus = B_NO_MEMORY; TRACE_ERROR("no memory available\n"); return; } fInitStatus = fSendBuffer->InitCheck(); if (fInitStatus != B_OK) return; fEndpoint = new(std::nothrow) BNetEndpoint(); if (fEndpoint == NULL) { fInitStatus = B_NO_MEMORY; TRACE_ERROR("no memory available\n"); return; } fInitStatus = fEndpoint->Connect(remoteHost, remotePort); if (fInitStatus != B_OK) { TRACE_ERROR("failed to connect to %s:%" B_PRIu16 "\n", remoteHost, remotePort); return; } fSender = new(std::nothrow) NetSender(fEndpoint, fSendBuffer); if (fSender == NULL) { fInitStatus = B_NO_MEMORY; TRACE_ERROR("no memory available\n"); return; } fReceiver = new(std::nothrow) NetReceiver(fEndpoint, fReceiveBuffer); if (fReceiver == NULL) { fInitStatus = B_NO_MEMORY; TRACE_ERROR("no memory available\n"); return; } BRect bounds = frame.OffsetToCopy(0, 0); fOffscreenBitmap = new(std::nothrow) BBitmap(bounds, B_BITMAP_ACCEPTS_VIEWS, B_RGB32); if (fOffscreenBitmap == NULL) { fInitStatus = B_NO_MEMORY; TRACE_ERROR("no memory available\n"); return; } fOffscreen = new(std::nothrow) BView(bounds, "offscreen remote view", B_FOLLOW_NONE, B_WILL_DRAW); if (fOffscreen == NULL) { fInitStatus = B_NO_MEMORY; TRACE_ERROR("no memory available\n"); return; } fOffscreenBitmap->AddChild(fOffscreen); fOffscreen->SetDrawingMode(B_OP_COPY); fDrawThread = spawn_thread(&_DrawEntry, "draw thread", B_NORMAL_PRIORITY, this); if (fDrawThread < 0) { fInitStatus = fDrawThread; TRACE_ERROR("failed to start _DrawThread()\n"); TRACE_ERROR("status = %" B_PRIx32 "\n", fInitStatus); return; } resume_thread(fDrawThread); } RemoteView::~RemoteView() { fStopThread = true; delete fReceiver; delete fReceiveBuffer; delete fSendBuffer; delete fSender; delete fEndpoint; delete fOffscreenBitmap; delete fCursorBitmap; int32 result; wait_for_thread(fDrawThread, &result); } status_t RemoteView::InitCheck() { return fInitStatus; } void RemoteView::AttachedToWindow() { SetViewColor(B_TRANSPARENT_COLOR); SetViewCursor(&fViewCursor); } void RemoteView::Draw(BRect updateRect) { SetDrawingMode(B_OP_COPY); fOffscreenBitmap->Lock(); fOffscreen->Sync(); DrawBitmap(fOffscreenBitmap, updateRect, updateRect); if (fCursorVisible && fCursorBitmap != NULL && fCursorFrame.Intersects(updateRect)) { DrawBitmap(fOffscreenBitmap, fCursorFrame, fCursorFrame); SetDrawingMode(B_OP_ALPHA); DrawBitmap(fCursorBitmap, fCursorFrame.LeftTop()); } fOffscreenBitmap->Unlock(); } void RemoteView::MouseMoved(BPoint where, uint32 code, const BMessage *dragMessage) { if (!fIsConnected) return; _SendMouseMessage(RP_MOUSE_MOVED, where); } void RemoteView::MouseDown(BPoint where) { if (!fIsConnected) return; _SendMouseMessage(RP_MOUSE_DOWN, where); } void RemoteView::MouseUp(BPoint where) { if (!fIsConnected) return; _SendMouseMessage(RP_MOUSE_UP, where); } void RemoteView::KeyDown(const char *bytes, int32 numBytes) { if (!fIsConnected) return; _SendKeyMessage(RP_KEY_DOWN, bytes, numBytes); } void RemoteView::KeyUp(const char *bytes, int32 numBytes) { if (!fIsConnected) return; _SendKeyMessage(RP_KEY_UP, bytes, numBytes); } void RemoteView::MessageReceived(BMessage *message) { if (!fIsConnected) { BView::MessageReceived(message); return; } switch (message->what) { case B_UNMAPPED_KEY_DOWN: case B_UNMAPPED_KEY_UP: // these are easily repeated and then cause a flood of messages // so we might not want them. break; case B_MODIFIERS_CHANGED: { uint32 modifiers = 0; message->FindInt32("modifiers", (int32 *)&modifiers); RemoteMessage message(NULL, fSendBuffer); message.Start(RP_MODIFIERS_CHANGED); message.Add(modifiers); break; } case B_MOUSE_WHEEL_CHANGED: { float xDelta, yDelta; if (message->FindFloat("be:wheel_delta_x", &xDelta) != B_OK) xDelta = 0; if (message->FindFloat("be:wheel_delta_y", &yDelta) != B_OK) yDelta = 0; RemoteMessage message(NULL, fSendBuffer); message.Start(RP_MOUSE_WHEEL_CHANGED); message.Add(xDelta); message.Add(yDelta); break; } } BView::MessageReceived(message); } void RemoteView::_SendMouseMessage(uint16 code, BPoint where) { RemoteMessage message(NULL, fSendBuffer); message.Start(code); message.Add(where); if (code == RP_MOUSE_MOVED) return; BMessage *event = Window()->CurrentMessage(); int32 buttons = 0; event->FindInt32("buttons", &buttons); message.Add(buttons); if (code == RP_MOUSE_DOWN) return; int32 clicks; event->FindInt32("clicks", &clicks); message.Add(clicks); } void RemoteView::_SendKeyMessage(uint16 code, const char *bytes, int32 numBytes) { RemoteMessage message(NULL, fSendBuffer); message.Start(code); message.Add(numBytes); message.AddList(bytes, numBytes); BMessage *event = Window()->CurrentMessage(); int32 rawChar, key; event->FindInt32("raw_char", &rawChar); event->FindInt32("key", &key); message.Add(rawChar); message.Add(key); } int RemoteView::_StateCompareByKey(const uint32 *key, const engine_state *state) { if (state->token == *key) return 0; if (state->token < *key) return -1; return 1; } engine_state * RemoteView::_CreateState(uint32 token) { int32 index = fStates.BinarySearchIndexByKey(token, &_StateCompareByKey); if (index >= 0) { TRACE_ERROR("state for token %" B_PRIu32 " already in list\n", token); return NULL; } engine_state *state = new(std::nothrow) engine_state; if (state == NULL) { TRACE_ERROR("failed to allocate engine state\n"); return NULL; } fOffscreenBitmap->Lock(); BView *offscreen = new(std::nothrow) BView(fOffscreenBitmap->Bounds(), "offscreen remote view", B_FOLLOW_NONE, B_WILL_DRAW); if (offscreen == NULL) { TRACE_ERROR("failed to allocate offscreen view\n"); fOffscreenBitmap->Unlock(); delete state; return NULL; } fOffscreenBitmap->AddChild(offscreen); fOffscreenBitmap->Unlock(); state->token = token; state->view = offscreen; state->pattern = B_SOLID_HIGH; state->clipping_region.MakeEmpty(); state->pen_size = 0; state->sync_drawing = true; fStates.AddItem(state, -index - 1); return state; } void RemoteView::_DeleteState(uint32 token) { int32 index = fStates.BinarySearchIndexByKey(token, &_StateCompareByKey); if (index < 0) return; engine_state *state = fStates.RemoveItemAt(index); fOffscreenBitmap->RemoveChild(state->view); delete state->view; delete state; } engine_state * RemoteView::_FindState(uint32 token) { return fStates.BinarySearchByKey(token, &_StateCompareByKey); } int32 RemoteView::_DrawEntry(void *data) { ((RemoteView *)data)->_DrawThread(); return 0; } void RemoteView::_DrawThread() { RemoteMessage reply(NULL, fSendBuffer); RemoteMessage message(fReceiveBuffer, NULL); // cursor BPoint cursorHotSpot(0, 0); reply.Start(RP_INIT_CONNECTION); reply.Flush(); while (!fStopThread) { uint16 code; status_t status = message.NextMessage(code); if (status != B_OK) { if (status == B_TIMED_OUT || status == -1) { TRACE_ERROR("could not connect to device\n"); } else { TRACE_ERROR("failed to read message from receiver\n"); break; } } TRACE("code %u with %ld bytes data\n", code, message.DataLeft()); BAutolock locker(this->Looper()); if (!locker.IsLocked()) break; // handle stuff that doesn't go to a specific engine switch (code) { case RP_INIT_CONNECTION: { BRect bounds = fOffscreenBitmap->Bounds(); reply.Start(RP_UPDATE_DISPLAY_MODE); reply.Add(bounds.IntegerWidth() + 1); reply.Add(bounds.IntegerHeight() + 1); if (reply.Flush() == B_OK) fIsConnected = true; continue; } case RP_CLOSE_CONNECTION: { be_app->PostMessage(B_QUIT_REQUESTED); continue; } case RP_CREATE_STATE: case RP_DELETE_STATE: { uint32 token; message.Read(token); if (code == RP_CREATE_STATE) _CreateState(token); else _DeleteState(token); continue; } case RP_SET_CURSOR: { BBitmap *bitmap; BPoint oldHotSpot = cursorHotSpot; message.Read(cursorHotSpot); if (message.ReadBitmap(&bitmap) != B_OK) continue; delete fCursorBitmap; fCursorBitmap = bitmap; Invalidate(fCursorFrame); BRect bounds = fCursorBitmap->Bounds(); fCursorFrame.right = fCursorFrame.left + bounds.IntegerWidth() + 1; fCursorFrame.bottom = fCursorFrame.bottom + bounds.IntegerHeight() + 1; fCursorFrame.OffsetBy(oldHotSpot - cursorHotSpot); Invalidate(fCursorFrame); continue; } case RP_SET_CURSOR_VISIBLE: { bool wasVisible = fCursorVisible; message.Read(fCursorVisible); if (wasVisible != fCursorVisible) Invalidate(fCursorFrame); continue; } case RP_MOVE_CURSOR_TO: { BPoint position; message.Read(position); if (fCursorVisible) Invalidate(fCursorFrame); fCursorFrame.OffsetTo(position - cursorHotSpot); Invalidate(fCursorFrame); continue; } case RP_INVALIDATE_RECT: { BRect rect; if (message.Read(rect) != B_OK) continue; Invalidate(rect); continue; } case RP_INVALIDATE_REGION: { BRegion region; if (message.ReadRegion(region) != B_OK) continue; Invalidate(®ion); continue; } case RP_FILL_REGION_COLOR_NO_CLIPPING: { BRegion region; rgb_color color; message.ReadRegion(region); if (message.Read(color) != B_OK) continue; fOffscreen->LockLooper(); fOffscreen->SetHighColor(color); fOffscreen->FillRegion(®ion); fOffscreen->UnlockLooper(); Invalidate(®ion); continue; } case RP_COPY_RECT_NO_CLIPPING: { int32 xOffset, yOffset; BRect rect; message.Read(xOffset); message.Read(yOffset); if (message.Read(rect) != B_OK) continue; BRect dest = rect.OffsetByCopy(xOffset, yOffset); fOffscreen->LockLooper(); fOffscreen->CopyBits(rect, dest); fOffscreen->UnlockLooper(); continue; } } uint32 token; message.Read(token); engine_state *state = _FindState(token); if (state == NULL) { TRACE_ERROR("didn't find state for token %" B_PRIu32 "\n", token); state = _CreateState(token); if (state == NULL) { TRACE_ERROR("failed to create state for unknown token\n"); continue; } } BView *offscreen = state->view; ::pattern &pattern = state->pattern; BRegion &clippingRegion = state->clipping_region; float &penSize = state->pen_size; bool &syncDrawing = state->sync_drawing; BRegion invalidRegion; BAutolock offscreenLocker(offscreen->Looper()); if (!offscreenLocker.IsLocked()) break; switch (code) { case RP_ENABLE_SYNC_DRAWING: syncDrawing = true; continue; case RP_DISABLE_SYNC_DRAWING: syncDrawing = false; continue; case RP_SET_OFFSETS: { int32 xOffset, yOffset; message.Read(xOffset); if (message.Read(yOffset) != B_OK) continue; offscreen->MovePenTo(xOffset, yOffset); break; } case RP_SET_HIGH_COLOR: case RP_SET_LOW_COLOR: { rgb_color color; if (message.Read(color) != B_OK) continue; if (code == RP_SET_HIGH_COLOR) offscreen->SetHighColor(color); else offscreen->SetLowColor(color); break; } case RP_SET_PEN_SIZE: { float newPenSize; if (message.Read(newPenSize) != B_OK) continue; offscreen->SetPenSize(newPenSize); penSize = newPenSize / 2; break; } case RP_SET_STROKE_MODE: { cap_mode capMode; join_mode joinMode; float miterLimit; message.Read(capMode); message.Read(joinMode); if (message.Read(miterLimit) != B_OK) continue; offscreen->SetLineMode(capMode, joinMode, miterLimit); break; } case RP_SET_BLENDING_MODE: { source_alpha sourceAlpha; alpha_function alphaFunction; message.Read(sourceAlpha); if (message.Read(alphaFunction) != B_OK) continue; offscreen->SetBlendingMode(sourceAlpha, alphaFunction); break; } case RP_SET_TRANSFORM: { BAffineTransform transform; if (message.ReadTransform(transform) != B_OK) continue; offscreen->SetTransform(transform); break; } case RP_SET_PATTERN: { if (message.Read(pattern) != B_OK) continue; break; } case RP_SET_DRAWING_MODE: { drawing_mode drawingMode; if (message.Read(drawingMode) != B_OK) continue; offscreen->SetDrawingMode(drawingMode); break; } case RP_SET_FONT: { BFont font; if (message.ReadFontState(font) != B_OK) continue; offscreen->SetFont(&font); break; } case RP_CONSTRAIN_CLIPPING_REGION: { if (message.ReadRegion(clippingRegion) != B_OK) continue; offscreen->ConstrainClippingRegion(&clippingRegion); break; } case RP_INVERT_RECT: { BRect rect; if (message.Read(rect) != B_OK) continue; offscreen->InvertRect(rect); invalidRegion.Include(rect); break; } case RP_DRAW_BITMAP: { BBitmap *bitmap; BRect bitmapRect, viewRect; uint32 options; message.Read(bitmapRect); message.Read(viewRect); message.Read(options); if (message.ReadBitmap(&bitmap) != B_OK || bitmap == NULL) continue; offscreen->DrawBitmap(bitmap, bitmapRect, viewRect, options); invalidRegion.Include(viewRect); delete bitmap; break; } case RP_DRAW_BITMAP_RECTS: { color_space colorSpace; int32 rectCount; uint32 flags, options; message.Read(options); message.Read(colorSpace); message.Read(flags); message.Read(rectCount); for (int32 i = 0; i < rectCount; i++) { BBitmap *bitmap; BRect viewRect; message.Read(viewRect); if (message.ReadBitmap(&bitmap, true, colorSpace, flags) != B_OK || bitmap == NULL) { continue; } offscreen->DrawBitmap(bitmap, bitmap->Bounds(), viewRect, options); invalidRegion.Include(viewRect); delete bitmap; } break; } case RP_STROKE_ARC: case RP_FILL_ARC: case RP_FILL_ARC_GRADIENT: { BRect rect; float angle, span; message.Read(rect); message.Read(angle); if (message.Read(span) != B_OK) continue; if (code == RP_STROKE_ARC) { offscreen->StrokeArc(rect, angle, span, pattern); rect.InsetBy(-penSize, -penSize); } else if (code == RP_FILL_ARC) offscreen->FillArc(rect, angle, span, pattern); else { BGradient *gradient; if (message.ReadGradient(&gradient) != B_OK) continue; offscreen->FillArc(rect, angle, span, *gradient); delete gradient; } invalidRegion.Include(rect); break; } case RP_STROKE_BEZIER: case RP_FILL_BEZIER: case RP_FILL_BEZIER_GRADIENT: { BPoint points[4]; if (message.ReadList(points, 4) != B_OK) continue; BRect bounds = _BuildInvalidateRect(points, 4); if (code == RP_STROKE_BEZIER) { offscreen->StrokeBezier(points, pattern); bounds.InsetBy(-penSize, -penSize); } else if (code == RP_FILL_BEZIER) offscreen->FillBezier(points, pattern); else { BGradient *gradient; if (message.ReadGradient(&gradient) != B_OK) continue; offscreen->FillBezier(points, *gradient); delete gradient; } invalidRegion.Include(bounds); break; } case RP_STROKE_ELLIPSE: case RP_FILL_ELLIPSE: case RP_FILL_ELLIPSE_GRADIENT: { BRect rect; if (message.Read(rect) != B_OK) continue; if (code == RP_STROKE_ELLIPSE) { offscreen->StrokeEllipse(rect, pattern); rect.InsetBy(-penSize, -penSize); } else if (code == RP_FILL_ELLIPSE) offscreen->FillEllipse(rect, pattern); else { BGradient *gradient; if (message.ReadGradient(&gradient) != B_OK) continue; offscreen->FillEllipse(rect, *gradient); delete gradient; } invalidRegion.Include(rect); break; } case RP_STROKE_POLYGON: case RP_FILL_POLYGON: case RP_FILL_POLYGON_GRADIENT: { BRect bounds; bool closed; int32 numPoints; message.Read(bounds); message.Read(closed); if (message.Read(numPoints) != B_OK) continue; BPoint points[numPoints]; for (int32 i = 0; i < numPoints; i++) message.Read(points[i]); if (code == RP_STROKE_POLYGON) { offscreen->StrokePolygon(points, numPoints, bounds, closed, pattern); bounds.InsetBy(-penSize, -penSize); } else if (code == RP_FILL_POLYGON) offscreen->FillPolygon(points, numPoints, bounds, pattern); else { BGradient *gradient; if (message.ReadGradient(&gradient) != B_OK) continue; offscreen->FillPolygon(points, numPoints, bounds, *gradient); delete gradient; } invalidRegion.Include(bounds); break; } case RP_STROKE_RECT: case RP_FILL_RECT: case RP_FILL_RECT_GRADIENT: { BRect rect; if (message.Read(rect) != B_OK) continue; if (code == RP_STROKE_RECT) { offscreen->StrokeRect(rect, pattern); rect.InsetBy(-penSize, -penSize); } else if (code == RP_FILL_RECT) offscreen->FillRect(rect, pattern); else { BGradient *gradient; if (message.ReadGradient(&gradient) != B_OK) continue; offscreen->FillRect(rect, *gradient); delete gradient; } invalidRegion.Include(rect); break; } case RP_STROKE_ROUND_RECT: case RP_FILL_ROUND_RECT: case RP_FILL_ROUND_RECT_GRADIENT: { BRect rect; float xRadius, yRadius; message.Read(rect); message.Read(xRadius); if (message.Read(yRadius) != B_OK) continue; if (code == RP_STROKE_ROUND_RECT) { offscreen->StrokeRoundRect(rect, xRadius, yRadius, pattern); rect.InsetBy(-penSize, -penSize); } else if (code == RP_FILL_ROUND_RECT) offscreen->FillRoundRect(rect, xRadius, yRadius, pattern); else { BGradient *gradient; if (message.ReadGradient(&gradient) != B_OK) continue; offscreen->FillRoundRect(rect, xRadius, yRadius, *gradient); delete gradient; } invalidRegion.Include(rect); break; } case RP_STROKE_SHAPE: case RP_FILL_SHAPE: case RP_FILL_SHAPE_GRADIENT: { BRect bounds; int32 opCount, pointCount; message.Read(bounds); if (message.Read(opCount) != B_OK) continue; BMessage archive; for (int32 i = 0; i < opCount; i++) { int32 op; message.Read(op); archive.AddInt32("ops", op); } if (message.Read(pointCount) != B_OK) continue; for (int32 i = 0; i < pointCount; i++) { BPoint point; message.Read(point); archive.AddPoint("pts", point); } BPoint offset; message.Read(offset); float scale; if (message.Read(scale) != B_OK) continue; offscreen->PushState(); offscreen->MovePenTo(offset); offscreen->SetScale(scale); BShape shape(&archive); if (code == RP_STROKE_SHAPE) { offscreen->StrokeShape(&shape, pattern); bounds.InsetBy(-penSize, -penSize); } else if (code == RP_FILL_SHAPE) offscreen->FillShape(&shape, pattern); else { BGradient *gradient; if (message.ReadGradient(&gradient) != B_OK) { offscreen->PopState(); continue; } offscreen->FillShape(&shape, *gradient); delete gradient; } offscreen->PopState(); invalidRegion.Include(bounds); break; } case RP_STROKE_TRIANGLE: case RP_FILL_TRIANGLE: case RP_FILL_TRIANGLE_GRADIENT: { BRect bounds; BPoint points[3]; message.ReadList(points, 3); if (message.Read(bounds) != B_OK) continue; if (code == RP_STROKE_TRIANGLE) { offscreen->StrokeTriangle(points[0], points[1], points[2], bounds, pattern); bounds.InsetBy(-penSize, -penSize); } else if (code == RP_FILL_TRIANGLE) { offscreen->FillTriangle(points[0], points[1], points[2], bounds, pattern); } else { BGradient *gradient; if (message.ReadGradient(&gradient) != B_OK) continue; offscreen->FillTriangle(points[0], points[1], points[2], bounds, *gradient); delete gradient; } invalidRegion.Include(bounds); break; } case RP_STROKE_LINE: { BPoint points[2]; if (message.ReadList(points, 2) != B_OK) continue; offscreen->StrokeLine(points[0], points[1], pattern); BRect bounds = _BuildInvalidateRect(points, 2); invalidRegion.Include(bounds.InsetBySelf(-penSize, -penSize)); break; } case RP_STROKE_LINE_ARRAY: { int32 numLines; if (message.Read(numLines) != B_OK) continue; BRect bounds; offscreen->BeginLineArray(numLines); for (int32 i = 0; i < numLines; i++) { rgb_color color; BPoint start, end; message.ReadArrayLine(start, end, color); offscreen->AddLine(start, end, color); bounds.left = min_c(bounds.left, min_c(start.x, end.x)); bounds.top = min_c(bounds.top, min_c(start.y, end.y)); bounds.right = max_c(bounds.right, max_c(start.x, end.x)); bounds.bottom = max_c(bounds.bottom, max_c(start.y, end.y)); } offscreen->EndLineArray(); invalidRegion.Include(bounds); break; } case RP_FILL_REGION: case RP_FILL_REGION_GRADIENT: { BRegion region; if (message.ReadRegion(region) != B_OK) continue; if (code == RP_FILL_REGION) offscreen->FillRegion(®ion, pattern); else { BGradient *gradient; if (message.ReadGradient(&gradient) != B_OK) continue; offscreen->FillRegion(®ion, *gradient); delete gradient; } invalidRegion.Include(®ion); break; } case RP_STROKE_POINT_COLOR: { BPoint point; rgb_color color; message.Read(point); if (message.Read(color) != B_OK) continue; rgb_color oldColor = offscreen->HighColor(); offscreen->SetHighColor(color); offscreen->StrokeLine(point, point); offscreen->SetHighColor(oldColor); invalidRegion.Include( BRect(point, point).InsetBySelf(-penSize, -penSize)); break; } case RP_STROKE_LINE_1PX_COLOR: { BPoint points[2]; rgb_color color; message.ReadList(points, 2); if (message.Read(color) != B_OK) continue; float oldSize = offscreen->PenSize(); rgb_color oldColor = offscreen->HighColor(); drawing_mode oldMode = offscreen->DrawingMode(); offscreen->SetPenSize(1); offscreen->SetHighColor(color); offscreen->SetDrawingMode(B_OP_OVER); offscreen->StrokeLine(points[0], points[1]); offscreen->SetDrawingMode(oldMode); offscreen->SetHighColor(oldColor); offscreen->SetPenSize(oldSize); invalidRegion.Include(_BuildInvalidateRect(points, 2)); break; } case RP_STROKE_RECT_1PX_COLOR: case RP_FILL_RECT_COLOR: { BRect rect; rgb_color color; message.Read(rect); if (message.Read(color) != B_OK) continue; rgb_color oldColor = offscreen->HighColor(); offscreen->SetHighColor(color); if (code == RP_STROKE_RECT_1PX_COLOR) { float oldSize = PenSize(); offscreen->SetPenSize(1); offscreen->StrokeRect(rect); offscreen->SetPenSize(oldSize); } else offscreen->FillRect(rect); offscreen->SetHighColor(oldColor); invalidRegion.Include(rect); break; } case RP_DRAW_STRING: { BPoint point; size_t length; char *string; bool hasDelta; message.Read(point); message.ReadString(&string, length); if (message.Read(hasDelta) != B_OK) { free(string); continue; } if (hasDelta) { escapement_delta delta[length]; message.ReadList(delta, length); offscreen->DrawString(string, point, delta); } else offscreen->DrawString(string, point); free(string); reply.Start(RP_DRAW_STRING_RESULT); reply.Add(token); reply.Add(offscreen->PenLocation()); reply.Flush(); font_height height; offscreen->GetFontHeight(&height); BRect bounds(point, offscreen->PenLocation()); bounds.top -= height.ascent; bounds.bottom += height.descent; invalidRegion.Include(bounds); break; } case RP_DRAW_STRING_WITH_OFFSETS: { size_t length; char *string; message.ReadString(&string, length); int32 count = UTF8CountChars(string, length); BPoint offsets[count]; if (message.ReadList(offsets, count) != B_OK) { free(string); continue; } offscreen->DrawString(string, offsets, count); reply.Start(RP_DRAW_STRING_RESULT); reply.Add(token); reply.Add(offscreen->PenLocation()); reply.Flush(); BFont font; offscreen->GetFont(&font); BRect boxes[count]; font.GetBoundingBoxesAsGlyphs(string, count, B_SCREEN_METRIC, boxes); free(string); font_height height; offscreen->GetFontHeight(&height); for (int32 i = 0; i < count; i++) { // TODO: validate boxes[i].OffsetBy(offsets[i] + BPoint(0, -height.ascent)); invalidRegion.Include(boxes[i]); } break; } case RP_READ_BITMAP: { BRect bounds; bool drawCursor; message.Read(bounds); if (message.Read(drawCursor) != B_OK) continue; // TODO: support the drawCursor flag BBitmap bitmap(bounds, B_BITMAP_NO_SERVER_LINK, B_RGB32); bitmap.ImportBits(fOffscreenBitmap, bounds.LeftTop(), BPoint(0, 0), bounds.Size()); reply.Start(RP_READ_BITMAP_RESULT); reply.Add(token); reply.AddBitmap(&bitmap); reply.Flush(); break; } default: TRACE_ERROR("unknown protocol code: %u\n", code); break; } if (syncDrawing) { offscreen->Sync(); Invalidate(&invalidRegion); } } } BRect RemoteView::_BuildInvalidateRect(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; }