diff --git a/src/apps/Jamfile b/src/apps/Jamfile index fbd2228ea4..14af4180cf 100644 --- a/src/apps/Jamfile +++ b/src/apps/Jamfile @@ -42,6 +42,7 @@ HaikuSubInclude poorman ; HaikuSubInclude powerstatus ; HaikuSubInclude processcontroller ; HaikuSubInclude pulse ; +HaikuSubInclude remotedesktop ; HaikuSubInclude resedit ; HaikuSubInclude screenshot ; HaikuSubInclude showimage ; diff --git a/src/apps/remotedesktop/Jamfile b/src/apps/remotedesktop/Jamfile new file mode 100644 index 0000000000..d82472d30a --- /dev/null +++ b/src/apps/remotedesktop/Jamfile @@ -0,0 +1,25 @@ +SubDir HAIKU_TOP src apps remotedesktop ; + +local defines = [ FDefines CLIENT_COMPILE ] ; +local serverDir = [ FDirName $(HAIKU_TOP) src servers app drawing remote ] ; + +SubDirC++Flags $(defines) ; + +UsePrivateHeaders shared ; +UseHeaders $(serverDir) ; + +Application RemoteDesktop : + RemoteDesktop.cpp + RemoteMessage.cpp + RemoteView.cpp + + NetReceiver.cpp + NetSender.cpp + StreamingRingBuffer.cpp + + : be bnetapi $(TARGET_LIBSUPC++) + : RemoteDesktop.rdef +; + +SEARCH on [ FGristFiles NetReceiver.cpp NetSender.cpp RemoteMessage.cpp + StreamingRingBuffer.cpp ] = $(serverDir) ; diff --git a/src/apps/remotedesktop/RemoteDesktop.cpp b/src/apps/remotedesktop/RemoteDesktop.cpp new file mode 100644 index 0000000000..c30612d0c6 --- /dev/null +++ b/src/apps/remotedesktop/RemoteDesktop.cpp @@ -0,0 +1,176 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include +#include +#include + +#include "RemoteView.h" + +#include +#include +#include +#include +#include +#include + + +void +print_usage(const char *app) +{ + printf("usage:\t%s --listenOnly [-p ]\n", app); + printf("\t%s [-p ] [-s ] [-c ]\n", + app); +} + + +int +main(int argc, char *argv[]) +{ + if (argc < 2) { + print_usage(argv[0]); + return 1; + } + + bool listenOnly = false; + uint32 listenPort = 10900; + uint32 sshPort = 22; + const char *command = "/system/apps/Terminal"; + + for (int32 i = 2; i < argc; i++) { + if (strcmp(argv[i], "-p") == 0) { + if (argc < i + 1 || sscanf(argv[i + 1], "%lu", &listenPort) != 1) { + print_usage(argv[0]); + return 2; + } + + i++; + continue; + } + + if (strcmp(argv[i], "-s") == 0) { + if (argc < i + 1 || sscanf(argv[i + 1], "%lu", &sshPort) != 1) { + print_usage(argv[0]); + return 2; + } + + i++; + continue; + } + + if (strcmp(argv[i], "-c") == 0) { + if (argc < i + 1) { + print_usage(argv[0]); + return 2; + } + + i++; + command = argv[i]; + continue; + } + + if (strcmp(argv[i], "--listenOnly") == 0) { + listenOnly = true; + continue; + } + + print_usage(argv[0]); + return 2; + } + + pid_t sshPID = -1; + if (!listenOnly) { + char shellCommand[4096]; + snprintf(shellCommand, sizeof(shellCommand), + "echo connected; export TARGET_SCREEN=localhost:%lu; %s\n", + listenPort, command); + + int pipes[4]; + if (pipe(&pipes[0]) != 0 || pipe(&pipes[2]) != 0) { + printf("failed to create redirection pipes\n"); + return 3; + } + + sshPID = fork(); + if (sshPID < 0) { + printf("failed to fork ssh process\n"); + return 3; + } + + if (sshPID == 0) { + // child code, redirect std* and execute ssh + close(STDOUT_FILENO); + close(STDIN_FILENO); + dup2(pipes[1], STDOUT_FILENO); + dup2(pipes[1], STDERR_FILENO); + dup2(pipes[2], STDIN_FILENO); + for (int32 i = 0; i < 4; i++) + close(pipes[i]); + + char localRedirect[50]; + sprintf(localRedirect, "localhost:%lu:localhost:%lu", + listenPort + 1, listenPort + 1); + + char remoteRedirect[50]; + sprintf(remoteRedirect, "localhost:%lu:localhost:%lu", + listenPort, listenPort); + + char portNumber[10]; + sprintf(portNumber, "%lu", sshPort); + + int result = execl("ssh", "-C", "-L", localRedirect, + "-R", remoteRedirect, "-p", portNumber, argv[1], + shellCommand, NULL); + + // we don't get here unless there was an error in executing + printf("failed to execute ssh process in child\n"); + return result; + } else { + close(pipes[1]); + close(pipes[2]); + + char buffer[10]; + read(pipes[0], buffer, sizeof(buffer)); + // block until connected/error message from ssh + } + } + + BApplication app("application/x-vnd.Haiku-RemoteDesktop"); + BScreen screen; + BWindow *window = new(std::nothrow) BWindow(screen.Frame(), "RemoteDesktop", + B_TITLED_WINDOW, B_QUIT_ON_WINDOW_CLOSE); + + if (window == NULL) { + printf("no memory to allocate window\n"); + return 4; + } + + RemoteView *view = new(std::nothrow) RemoteView(window->Bounds(), + listenPort); + if (view == NULL) { + printf("no memory to allocate remote view\n"); + return 4; + } + + status_t init = view->InitCheck(); + if (init != B_OK) { + printf("initialization of remote view failed: %s\n", strerror(init)); + delete view; + return 5; + } + + window->AddChild(view); + view->MakeFocus(); + window->Show(); + app.Run(); + + if (sshPID >= 0) + kill(sshPID, SIGINT); + + return 0; +} diff --git a/src/apps/remotedesktop/RemoteDesktop.rdef b/src/apps/remotedesktop/RemoteDesktop.rdef new file mode 100644 index 0000000000..3079511332 --- /dev/null +++ b/src/apps/remotedesktop/RemoteDesktop.rdef @@ -0,0 +1,15 @@ +resource app_signature "application/x-vnd.Haiku-RemoteDesktop"; + +resource app_version { + major = 0, + middle = 1, + minor = 0, + + variety = B_APPV_ALPHA, + internal = 0, + + short_info = "RemoteDesktop", + long_info = "RemoteDesktop ©2009 Haiku, Inc." +}; + +resource app_flags B_MULTIPLE_LAUNCH; diff --git a/src/apps/remotedesktop/RemoteView.cpp b/src/apps/remotedesktop/RemoteView.cpp new file mode 100644 index 0000000000..da509875fd --- /dev/null +++ b/src/apps/remotedesktop/RemoteView.cpp @@ -0,0 +1,1249 @@ +/* + * Copyright 2009, 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 + + +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, uint16 listenPort) + : + BView(frame, "RemoteView", B_FOLLOW_NONE, B_WILL_DRAW), + fInitStatus(B_NO_INIT), + fIsConnected(false), + fReceiveBuffer(NULL), + fSendBuffer(NULL), + fReceiveEndpoint(NULL), + fSendEndpoint(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; + return; + } + + fInitStatus = fReceiveBuffer->InitCheck(); + 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; + + fReceiveEndpoint = new(std::nothrow) BNetEndpoint(); + if (fReceiveEndpoint == NULL) { + fInitStatus = B_NO_MEMORY; + return; + } + + fInitStatus = fReceiveEndpoint->Bind(listenPort); + if (fInitStatus != B_OK) + return; + + fReceiver = new(std::nothrow) NetReceiver(fReceiveEndpoint, fReceiveBuffer); + if (fReceiver == NULL) { + fInitStatus = B_NO_MEMORY; + return; + } + + fSendEndpoint = new(std::nothrow) BNetEndpoint(); + if (fSendEndpoint == NULL) { + fInitStatus = B_NO_MEMORY; + return; + } + + fSender = new(std::nothrow) NetSender(fSendEndpoint, fSendBuffer); + if (fSender == NULL) { + fInitStatus = B_NO_MEMORY; + 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; + return; + } + + fOffscreen = new(std::nothrow) BView(bounds, "offscreen remote view", + B_FOLLOW_NONE, B_WILL_DRAW); + if (fOffscreen == NULL) { + fInitStatus = B_NO_MEMORY; + return; + } + + fOffscreenBitmap->AddChild(fOffscreen); + fOffscreen->SetDrawingMode(B_OP_COPY); + + fDrawThread = spawn_thread(&_DrawEntry, "draw thread", B_NORMAL_PRIORITY, + this); + if (fDrawThread < 0) { + fInitStatus = fDrawThread; + return; + } + + resume_thread(fDrawThread); +} + + +RemoteView::~RemoteView() +{ + fStopThread = true; + + delete fReceiver; + delete fReceiveBuffer; + + delete fSendBuffer; + delete fSender; + + delete fReceiveEndpoint; + delete fSendEndpoint; + + 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; +} + + +void +RemoteView::_CreateState(uint32 token) +{ + int32 index = fStates.BinarySearchIndexByKey(token, &_StateCompareByKey); + if (index >= 0) { + TRACE_ERROR("state for token %lu already in list\n", token); + return; + } + + engine_state *state = new(std::nothrow) engine_state; + if (state == NULL) { + TRACE_ERROR("failed to allocate engine state\n"); + return; + } + + 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; + } + + 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); +} + + +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); + + while (!fStopThread) { + uint16 code; + status_t status = message.NextMessage(code); + if (status != B_OK) { + 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 specicifc engine + switch (code) { + case RP_INIT_CONNECTION: + { + uint16 port; + status_t result = message.Read(port); + if (result != B_OK) { + TRACE_ERROR("failed to read remote port\n"); + continue; + } + + BNetEndpoint *endpoint = fReceiver->Endpoint(); + if (endpoint == NULL) { + TRACE_ERROR("receiver not connected anymore\n"); + continue; + } + + in_addr remoteHost; + char hostName[MAXHOSTNAMELEN + 1]; + BNetAddress address(endpoint->RemoteAddr()); + address.GetAddr(remoteHost); + address.GetAddr(hostName, NULL); + address.SetTo(remoteHost, port); + + TRACE("connecting to host \"%s\" port %u\n", hostName, port); + result = fSendEndpoint->Connect(address); + if (result != B_OK) { + TRACE_ERROR("failed to connect to host \"%s\" port %u\n", + hostName, port); + continue; + } + + 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 %lu\n", token); + 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_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_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); + } + + // the shape is in absolute coordinates + offscreen->MovePenTo(0, 0); + + 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) + continue; + + offscreen->FillShape(&shape, *gradient); + delete gradient; + } + + 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_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.IntegerWidth() + 1, + bounds.IntegerHeight() + 1); + + 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; +} diff --git a/src/apps/remotedesktop/RemoteView.h b/src/apps/remotedesktop/RemoteView.h new file mode 100644 index 0000000000..8ce29b149d --- /dev/null +++ b/src/apps/remotedesktop/RemoteView.h @@ -0,0 +1,86 @@ +/* + * Copyright 2009, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ +#ifndef REMOTE_VIEW_H +#define REMOTE_VIEW_H + +#include +#include +#include +#include + +class BBitmap; +class NetReceiver; +class NetSender; +class StreamingRingBuffer; + +struct engine_state; + +class RemoteView : public BView { +public: + RemoteView(BRect frame, uint16 listenPort); +virtual ~RemoteView(); + + status_t InitCheck(); + +virtual void AttachedToWindow(); + +virtual void Draw(BRect updateRect); + +virtual void MouseMoved(BPoint where, uint32 code, + const BMessage *dragMessage); +virtual void MouseDown(BPoint where); +virtual void MouseUp(BPoint where); + +virtual void KeyDown(const char *bytes, int32 numBytes); +virtual void KeyUp(const char *bytes, int32 numBytes); + +virtual void MessageReceived(BMessage *message); + +private: + void _SendMouseMessage(uint16 code, + BPoint where); + void _SendKeyMessage(uint16 code, + const char *bytes, int32 numBytes); + +static int _StateCompareByKey(const uint32 *key, + const engine_state *state); + void _CreateState(uint32 token); + void _DeleteState(uint32 token); + engine_state * _FindState(uint32 token); + +static int32 _DrawEntry(void *data); + void _DrawThread(); + + BRect _BuildInvalidateRect(BPoint *points, + int32 pointCount); + + status_t fInitStatus; + bool fIsConnected; + + StreamingRingBuffer * fReceiveBuffer; + StreamingRingBuffer * fSendBuffer; + BNetEndpoint * fReceiveEndpoint; + BNetEndpoint * fSendEndpoint; + NetReceiver * fReceiver; + NetSender * fSender; + + bool fStopThread; + thread_id fDrawThread; + + BBitmap * fOffscreenBitmap; + BView * fOffscreen; + + BCursor fViewCursor; + BBitmap * fCursorBitmap; + BRect fCursorFrame; + bool fCursorVisible; + + BObjectList fStates; +}; + +#endif // REMOTE_VIEW_H