xref: /haiku/src/apps/remotedesktop/RemoteView.cpp (revision 2cad94c1c30b6223ad8c08710b26e071d32e9979)
1 /*
2  * Copyright 2009-2014, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Michael Lotz <mmlr@mlotz.ch>
7  */
8 
9 #include "NetReceiver.h"
10 #include "NetSender.h"
11 #include "RemoteMessage.h"
12 #include "RemoteView.h"
13 #include "StreamingRingBuffer.h"
14 
15 #include <Application.h>
16 #include <Autolock.h>
17 #include <Bitmap.h>
18 #include <Message.h>
19 #include <NetEndpoint.h>
20 #include <Region.h>
21 #include <Shape.h>
22 #include <Window.h>
23 #include <utf8_functions.h>
24 
25 #include <new>
26 #include <stdio.h>
27 
28 
29 static const uint8 kCursorData[] = { 16 /* size, 16x16 */,
30 	1 /* depth, 1 bit per pixel */, 0, 0, /* hot spot at 0, 0 */
31 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
32 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
33 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
34 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
36 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
37 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
38 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
39 };
40 
41 
42 #define TRACE(x...)				/*printf("RemoteView: " x)*/
43 #define TRACE_ALWAYS(x...)		printf("RemoteView: " x)
44 #define TRACE_ERROR(x...)		printf("RemoteView: " x)
45 
46 
47 typedef struct engine_state {
48 	uint32		token;
49 	BView *		view;
50 	::pattern	pattern;
51 	BRegion		clipping_region;
52 	float		pen_size;
53 	bool		sync_drawing;
54 } engine_state;
55 
56 
57 RemoteView::RemoteView(BRect frame, uint16 listenPort)
58 	:
59 	BView(frame, "RemoteView", B_FOLLOW_NONE, B_WILL_DRAW),
60 	fInitStatus(B_NO_INIT),
61 	fIsConnected(false),
62 	fReceiveBuffer(NULL),
63 	fSendBuffer(NULL),
64 	fReceiveEndpoint(NULL),
65 	fSendEndpoint(NULL),
66 	fReceiver(NULL),
67 	fSender(NULL),
68 	fStopThread(false),
69 	fOffscreenBitmap(NULL),
70 	fOffscreen(NULL),
71 	fViewCursor(kCursorData),
72 	fCursorBitmap(NULL),
73 	fCursorVisible(false)
74 {
75 	fReceiveBuffer = new(std::nothrow) StreamingRingBuffer(16 * 1024);
76 	if (fReceiveBuffer == NULL) {
77 		fInitStatus = B_NO_MEMORY;
78 		TRACE_ERROR("no memory available\n");
79 		return;
80 	}
81 
82 	fInitStatus = fReceiveBuffer->InitCheck();
83 	if (fInitStatus != B_OK)
84 		return;
85 
86 	fSendBuffer = new(std::nothrow) StreamingRingBuffer(16 * 1024);
87 	if (fSendBuffer == NULL) {
88 		fInitStatus = B_NO_MEMORY;
89 		TRACE_ERROR("no memory available\n");
90 		return;
91 	}
92 
93 	fInitStatus = fSendBuffer->InitCheck();
94 	if (fInitStatus != B_OK)
95 		return;
96 
97 	fReceiveEndpoint = new(std::nothrow) BNetEndpoint();
98 	if (fReceiveEndpoint == NULL) {
99 		fInitStatus = B_NO_MEMORY;
100 		TRACE_ERROR("no memory available\n");
101 		return;
102 	}
103 
104 	fInitStatus = fReceiveEndpoint->Bind(listenPort);
105 	if (fInitStatus != B_OK)
106 		return;
107 
108 	fReceiver = new(std::nothrow) NetReceiver(fReceiveEndpoint, fReceiveBuffer);
109 	if (fReceiver == NULL) {
110 		fInitStatus = B_NO_MEMORY;
111 		TRACE_ERROR("no memory available\n");
112 		return;
113 	}
114 
115 	fSendEndpoint = new(std::nothrow) BNetEndpoint();
116 	if (fSendEndpoint == NULL) {
117 		fInitStatus = B_NO_MEMORY;
118 		TRACE_ERROR("no memory available\n");
119 		return;
120 	}
121 
122 	fSender = new(std::nothrow) NetSender(fSendEndpoint, fSendBuffer);
123 	if (fSender == NULL) {
124 		fInitStatus = B_NO_MEMORY;
125 		TRACE_ERROR("no memory available\n");
126 		return;
127 	}
128 
129 	BRect bounds = frame.OffsetToCopy(0, 0);
130 	fOffscreenBitmap = new(std::nothrow) BBitmap(bounds, B_BITMAP_ACCEPTS_VIEWS,
131 		B_RGB32);
132 	if (fOffscreenBitmap == NULL) {
133 		fInitStatus = B_NO_MEMORY;
134 		TRACE_ERROR("no memory available\n");
135 		return;
136 	}
137 
138 	fOffscreen = new(std::nothrow) BView(bounds, "offscreen remote view",
139 		B_FOLLOW_NONE, B_WILL_DRAW);
140 	if (fOffscreen == NULL) {
141 		fInitStatus = B_NO_MEMORY;
142 		TRACE_ERROR("no memory available\n");
143 		return;
144 	}
145 
146 	fOffscreenBitmap->AddChild(fOffscreen);
147 	fOffscreen->SetDrawingMode(B_OP_COPY);
148 
149 	fDrawThread = spawn_thread(&_DrawEntry, "draw thread", B_NORMAL_PRIORITY,
150 		this);
151 	if (fDrawThread < 0) {
152 		fInitStatus = fDrawThread;
153 
154 		TRACE_ERROR("failed to start _DrawThread()\n");
155 		TRACE_ERROR("status = %" B_PRIx32 "\n", fInitStatus);
156 
157 		return;
158 	}
159 
160 	resume_thread(fDrawThread);
161 }
162 
163 
164 RemoteView::~RemoteView()
165 {
166 	fStopThread = true;
167 
168 	delete fReceiver;
169 	delete fReceiveBuffer;
170 
171 	delete fSendBuffer;
172 	delete fSender;
173 
174 	delete fReceiveEndpoint;
175 	delete fSendEndpoint;
176 
177 	delete fOffscreenBitmap;
178 	delete fCursorBitmap;
179 
180 	int32 result;
181 	wait_for_thread(fDrawThread, &result);
182 }
183 
184 
185 status_t
186 RemoteView::InitCheck()
187 {
188 	return fInitStatus;
189 }
190 
191 
192 void
193 RemoteView::AttachedToWindow()
194 {
195 	SetViewColor(B_TRANSPARENT_COLOR);
196 	SetViewCursor(&fViewCursor);
197 }
198 
199 
200 void
201 RemoteView::Draw(BRect updateRect)
202 {
203 	SetDrawingMode(B_OP_COPY);
204 	fOffscreenBitmap->Lock();
205 	fOffscreen->Sync();
206 
207 	DrawBitmap(fOffscreenBitmap, updateRect, updateRect);
208 
209 	if (fCursorVisible && fCursorBitmap != NULL
210 		&& fCursorFrame.Intersects(updateRect)) {
211 		DrawBitmap(fOffscreenBitmap, fCursorFrame, fCursorFrame);
212 		SetDrawingMode(B_OP_ALPHA);
213 		DrawBitmap(fCursorBitmap, fCursorFrame.LeftTop());
214 	}
215 
216 	fOffscreenBitmap->Unlock();
217 }
218 
219 
220 void
221 RemoteView::MouseMoved(BPoint where, uint32 code, const BMessage *dragMessage)
222 {
223 	if (!fIsConnected)
224 		return;
225 
226 	_SendMouseMessage(RP_MOUSE_MOVED, where);
227 }
228 
229 
230 void
231 RemoteView::MouseDown(BPoint where)
232 {
233 	if (!fIsConnected)
234 		return;
235 
236 	_SendMouseMessage(RP_MOUSE_DOWN, where);
237 }
238 
239 
240 void
241 RemoteView::MouseUp(BPoint where)
242 {
243 	if (!fIsConnected)
244 		return;
245 
246 	_SendMouseMessage(RP_MOUSE_UP, where);
247 }
248 
249 
250 void
251 RemoteView::KeyDown(const char *bytes, int32 numBytes)
252 {
253 	if (!fIsConnected)
254 		return;
255 
256 	_SendKeyMessage(RP_KEY_DOWN, bytes, numBytes);
257 }
258 
259 
260 void
261 RemoteView::KeyUp(const char *bytes, int32 numBytes)
262 {
263 	if (!fIsConnected)
264 		return;
265 
266 	_SendKeyMessage(RP_KEY_UP, bytes, numBytes);
267 }
268 
269 
270 void
271 RemoteView::MessageReceived(BMessage *message)
272 {
273 	if (!fIsConnected) {
274 		BView::MessageReceived(message);
275 		return;
276 	}
277 
278 	switch (message->what) {
279 		case B_UNMAPPED_KEY_DOWN:
280 		case B_UNMAPPED_KEY_UP:
281 			// these are easily repeated and then cause a flood of messages
282 			// so we might not want them.
283 			break;
284 
285 		case B_MODIFIERS_CHANGED:
286 		{
287 			uint32 modifiers = 0;
288 			message->FindInt32("modifiers", (int32 *)&modifiers);
289 			RemoteMessage message(NULL, fSendBuffer);
290 			message.Start(RP_MODIFIERS_CHANGED);
291 			message.Add(modifiers);
292 			break;
293 		}
294 
295 		case B_MOUSE_WHEEL_CHANGED:
296 		{
297 			float xDelta, yDelta;
298 			if (message->FindFloat("be:wheel_delta_x", &xDelta) != B_OK)
299 				xDelta = 0;
300 			if (message->FindFloat("be:wheel_delta_y", &yDelta) != B_OK)
301 				yDelta = 0;
302 
303 			RemoteMessage message(NULL, fSendBuffer);
304 			message.Start(RP_MOUSE_WHEEL_CHANGED);
305 			message.Add(xDelta);
306 			message.Add(yDelta);
307 			break;
308 		}
309 	}
310 
311 	BView::MessageReceived(message);
312 }
313 
314 
315 void
316 RemoteView::_SendMouseMessage(uint16 code, BPoint where)
317 {
318 	RemoteMessage message(NULL, fSendBuffer);
319 	message.Start(code);
320 	message.Add(where);
321 
322 	if (code == RP_MOUSE_MOVED)
323 		return;
324 
325 	BMessage *event = Window()->CurrentMessage();
326 
327 	int32 buttons = 0;
328 	event->FindInt32("buttons", &buttons);
329 	message.Add(buttons);
330 
331 	if (code == RP_MOUSE_DOWN)
332 		return;
333 
334 	int32 clicks;
335 	event->FindInt32("clicks", &clicks);
336 	message.Add(clicks);
337 }
338 
339 
340 void
341 RemoteView::_SendKeyMessage(uint16 code, const char *bytes, int32 numBytes)
342 {
343 	RemoteMessage message(NULL, fSendBuffer);
344 	message.Start(code);
345 	message.Add(numBytes);
346 	message.AddList(bytes, numBytes);
347 
348 	BMessage *event = Window()->CurrentMessage();
349 
350 	int32 rawChar, key;
351 	event->FindInt32("raw_char", &rawChar);
352 	event->FindInt32("key", &key);
353 
354 	message.Add(rawChar);
355 	message.Add(key);
356 }
357 
358 
359 int
360 RemoteView::_StateCompareByKey(const uint32 *key, const engine_state *state)
361 {
362 	if (state->token == *key)
363 		return 0;
364 
365 	if (state->token < *key)
366 		return -1;
367 
368 	return 1;
369 }
370 
371 
372 void
373 RemoteView::_CreateState(uint32 token)
374 {
375 	int32 index = fStates.BinarySearchIndexByKey(token, &_StateCompareByKey);
376 	if (index >= 0) {
377 		TRACE_ERROR("state for token %" B_PRIu32 " already in list\n", token);
378 		return;
379 	}
380 
381 	engine_state *state = new(std::nothrow) engine_state;
382 	if (state == NULL) {
383 		TRACE_ERROR("failed to allocate engine state\n");
384 		return;
385 	}
386 
387 	fOffscreenBitmap->Lock();
388 	BView *offscreen = new(std::nothrow) BView(fOffscreenBitmap->Bounds(),
389 		"offscreen remote view", B_FOLLOW_NONE, B_WILL_DRAW);
390 	if (offscreen == NULL) {
391 		TRACE_ERROR("failed to allocate offscreen view\n");
392 		fOffscreenBitmap->Unlock();
393 		delete state;
394 		return;
395 	}
396 
397 	fOffscreenBitmap->AddChild(offscreen);
398 	fOffscreenBitmap->Unlock();
399 
400 	state->token = token;
401 	state->view = offscreen;
402 	state->pattern = B_SOLID_HIGH;
403 	state->clipping_region.MakeEmpty();
404 	state->pen_size = 0;
405 	state->sync_drawing = true;
406 
407 	fStates.AddItem(state, -index - 1);
408 }
409 
410 
411 void
412 RemoteView::_DeleteState(uint32 token)
413 {
414 	int32 index = fStates.BinarySearchIndexByKey(token, &_StateCompareByKey);
415 	if (index < 0)
416 		return;
417 
418 	engine_state *state = fStates.RemoveItemAt(index);
419 
420 	fOffscreenBitmap->RemoveChild(state->view);
421 	delete state->view;
422 	delete state;
423 }
424 
425 
426 engine_state *
427 RemoteView::_FindState(uint32 token)
428 {
429 	return fStates.BinarySearchByKey(token, &_StateCompareByKey);
430 }
431 
432 
433 int32
434 RemoteView::_DrawEntry(void *data)
435 {
436 	((RemoteView *)data)->_DrawThread();
437 	return 0;
438 }
439 
440 
441 void
442 RemoteView::_DrawThread()
443 {
444 	RemoteMessage reply(NULL, fSendBuffer);
445 	RemoteMessage message(fReceiveBuffer, NULL);
446 
447 	// cursor
448 	BPoint cursorHotSpot(0, 0);
449 
450 	while (!fStopThread) {
451 		uint16 code;
452 		status_t status = message.NextMessage(code);
453 
454 		if (status != B_OK) {
455 			if (status == B_TIMED_OUT || status == -1) {
456 				TRACE_ERROR("could not connect to device\n");
457 			} else {
458 				TRACE_ERROR("failed to read message from receiver\n");
459 				break;
460 			}
461 		}
462 
463 		TRACE("code %u with %ld bytes data\n", code, message.DataLeft());
464 
465 		BAutolock locker(this->Looper());
466 		if (!locker.IsLocked())
467 			break;
468 
469 		// handle stuff that doesn't go to a specific engine
470 		switch (code) {
471 			case RP_INIT_CONNECTION:
472 			{
473 				uint16 port;
474 				status_t result = message.Read(port);
475 				if (result != B_OK) {
476 					TRACE_ERROR("failed to read remote port\n");
477 					continue;
478 				}
479 
480 				BNetEndpoint *endpoint = fReceiver->Endpoint();
481 				if (endpoint == NULL) {
482 					TRACE_ERROR("receiver not connected anymore\n");
483 					continue;
484 				}
485 
486 				in_addr remoteHost;
487 				char hostName[MAXHOSTNAMELEN + 1];
488 				BNetAddress address(endpoint->RemoteAddr());
489 				address.GetAddr(remoteHost);
490 				address.GetAddr(hostName, NULL);
491 				address.SetTo(remoteHost, port);
492 
493 				TRACE("connecting to host \"%s\" port %u\n", hostName, port);
494 				result = fSendEndpoint->Connect(address);
495 				if (result != B_OK) {
496 					TRACE_ERROR("failed to connect to host \"%s\" port %u\n",
497 						hostName, port);
498 					continue;
499 				}
500 
501 				BRect bounds = fOffscreenBitmap->Bounds();
502 				reply.Start(RP_UPDATE_DISPLAY_MODE);
503 				reply.Add(bounds.IntegerWidth() + 1);
504 				reply.Add(bounds.IntegerHeight() + 1);
505 				if (reply.Flush() == B_OK)
506 					fIsConnected = true;
507 
508 				continue;
509 			}
510 
511 			case RP_CLOSE_CONNECTION:
512 			{
513 				be_app->PostMessage(B_QUIT_REQUESTED);
514 				continue;
515 			}
516 
517 			case RP_CREATE_STATE:
518 			case RP_DELETE_STATE:
519 			{
520 				uint32 token;
521 				message.Read(token);
522 
523 				if (code == RP_CREATE_STATE)
524 					_CreateState(token);
525 				else
526 					_DeleteState(token);
527 
528 				continue;
529 			}
530 
531 			case RP_SET_CURSOR:
532 			{
533 				BBitmap *bitmap;
534 				BPoint oldHotSpot = cursorHotSpot;
535 				message.Read(cursorHotSpot);
536 				if (message.ReadBitmap(&bitmap) != B_OK)
537 					continue;
538 
539 				delete fCursorBitmap;
540 				fCursorBitmap = bitmap;
541 
542 				Invalidate(fCursorFrame);
543 
544 				BRect bounds = fCursorBitmap->Bounds();
545 				fCursorFrame.right = fCursorFrame.left
546 					+ bounds.IntegerWidth() + 1;
547 				fCursorFrame.bottom = fCursorFrame.bottom
548 					+ bounds.IntegerHeight() + 1;
549 
550 				fCursorFrame.OffsetBy(oldHotSpot - cursorHotSpot);
551 
552 				Invalidate(fCursorFrame);
553 				continue;
554 			}
555 
556 			case RP_SET_CURSOR_VISIBLE:
557 			{
558 				bool wasVisible = fCursorVisible;
559 				message.Read(fCursorVisible);
560 				if (wasVisible != fCursorVisible)
561 					Invalidate(fCursorFrame);
562 				continue;
563 			}
564 
565 			case RP_MOVE_CURSOR_TO:
566 			{
567 				BPoint position;
568 				message.Read(position);
569 
570 				if (fCursorVisible)
571 					Invalidate(fCursorFrame);
572 
573 				fCursorFrame.OffsetTo(position - cursorHotSpot);
574 
575 				Invalidate(fCursorFrame);
576 				continue;
577 			}
578 
579 			case RP_INVALIDATE_RECT:
580 			{
581 				BRect rect;
582 				if (message.Read(rect) != B_OK)
583 					continue;
584 
585 				Invalidate(rect);
586 				continue;
587 			}
588 
589 			case RP_INVALIDATE_REGION:
590 			{
591 				BRegion region;
592 				if (message.ReadRegion(region) != B_OK)
593 					continue;
594 
595 				Invalidate(&region);
596 				continue;
597 			}
598 
599 			case RP_FILL_REGION_COLOR_NO_CLIPPING:
600 			{
601 				BRegion region;
602 				rgb_color color;
603 
604 				message.ReadRegion(region);
605 				if (message.Read(color) != B_OK)
606 					continue;
607 
608 				fOffscreen->LockLooper();
609 				fOffscreen->SetHighColor(color);
610 				fOffscreen->FillRegion(&region);
611 				fOffscreen->UnlockLooper();
612 				Invalidate(&region);
613 				continue;
614 			}
615 
616 			case RP_COPY_RECT_NO_CLIPPING:
617 			{
618 				int32 xOffset, yOffset;
619 				BRect rect;
620 
621 				message.Read(xOffset);
622 				message.Read(yOffset);
623 				if (message.Read(rect) != B_OK)
624 					continue;
625 
626 				BRect dest = rect.OffsetByCopy(xOffset, yOffset);
627 				fOffscreen->LockLooper();
628 				fOffscreen->CopyBits(rect, dest);
629 				fOffscreen->UnlockLooper();
630 				continue;
631 			}
632 		}
633 
634 		uint32 token;
635 		message.Read(token);
636 
637 		engine_state *state = _FindState(token);
638 		if (state == NULL) {
639 			TRACE_ERROR("didn't find state for token %" B_PRIu32 "\n", token);
640 			continue;
641 		}
642 
643 		BView *offscreen = state->view;
644 		::pattern &pattern = state->pattern;
645 		BRegion &clippingRegion = state->clipping_region;
646 		float &penSize = state->pen_size;
647 		bool &syncDrawing = state->sync_drawing;
648 		BRegion invalidRegion;
649 
650 		BAutolock offscreenLocker(offscreen->Looper());
651 		if (!offscreenLocker.IsLocked())
652 			break;
653 
654 		switch (code) {
655 			case RP_ENABLE_SYNC_DRAWING:
656 				syncDrawing = true;
657 				continue;
658 
659 			case RP_DISABLE_SYNC_DRAWING:
660 				syncDrawing = false;
661 				continue;
662 
663 			case RP_SET_OFFSETS:
664 			{
665 				int32 xOffset, yOffset;
666 				message.Read(xOffset);
667 				if (message.Read(yOffset) != B_OK)
668 					continue;
669 
670 				offscreen->MovePenTo(xOffset, yOffset);
671 				break;
672 			}
673 
674 			case RP_SET_HIGH_COLOR:
675 			case RP_SET_LOW_COLOR:
676 			{
677 				rgb_color color;
678 				if (message.Read(color) != B_OK)
679 					continue;
680 
681 				if (code == RP_SET_HIGH_COLOR)
682 					offscreen->SetHighColor(color);
683 				else
684 					offscreen->SetLowColor(color);
685 
686 				break;
687 			}
688 
689 			case RP_SET_PEN_SIZE:
690 			{
691 				float newPenSize;
692 				if (message.Read(newPenSize) != B_OK)
693 					continue;
694 
695 				offscreen->SetPenSize(newPenSize);
696 				penSize = newPenSize / 2;
697 				break;
698 			}
699 
700 			case RP_SET_STROKE_MODE:
701 			{
702 				cap_mode capMode;
703 				join_mode joinMode;
704 				float miterLimit;
705 
706 				message.Read(capMode);
707 				message.Read(joinMode);
708 				if (message.Read(miterLimit) != B_OK)
709 					continue;
710 
711 				offscreen->SetLineMode(capMode, joinMode, miterLimit);
712 				break;
713 			}
714 
715 			case RP_SET_BLENDING_MODE:
716 			{
717 				source_alpha sourceAlpha;
718 				alpha_function alphaFunction;
719 
720 				message.Read(sourceAlpha);
721 				if (message.Read(alphaFunction) != B_OK)
722 					continue;
723 
724 				offscreen->SetBlendingMode(sourceAlpha, alphaFunction);
725 				break;
726 			}
727 
728 			case RP_SET_PATTERN:
729 			{
730 				if (message.Read(pattern) != B_OK)
731 					continue;
732 				break;
733 			}
734 
735 			case RP_SET_DRAWING_MODE:
736 			{
737 				drawing_mode drawingMode;
738 				if (message.Read(drawingMode) != B_OK)
739 					continue;
740 
741 				offscreen->SetDrawingMode(drawingMode);
742 				break;
743 			}
744 
745 			case RP_SET_FONT:
746 			{
747 				BFont font;
748 				if (message.ReadFontState(font) != B_OK)
749 					continue;
750 
751 				offscreen->SetFont(&font);
752 				break;
753 			}
754 
755 			case RP_CONSTRAIN_CLIPPING_REGION:
756 			{
757 				if (message.ReadRegion(clippingRegion) != B_OK)
758 					continue;
759 
760 				offscreen->ConstrainClippingRegion(&clippingRegion);
761 				break;
762 			}
763 
764 			case RP_INVERT_RECT:
765 			{
766 				BRect rect;
767 				if (message.Read(rect) != B_OK)
768 					continue;
769 
770 				offscreen->InvertRect(rect);
771 				invalidRegion.Include(rect);
772 				break;
773 			}
774 
775 			case RP_DRAW_BITMAP:
776 			{
777 				BBitmap *bitmap;
778 				BRect bitmapRect, viewRect;
779 				uint32 options;
780 
781 				message.Read(bitmapRect);
782 				message.Read(viewRect);
783 				message.Read(options);
784 				if (message.ReadBitmap(&bitmap) != B_OK || bitmap == NULL)
785 					continue;
786 
787 				offscreen->DrawBitmap(bitmap, bitmapRect, viewRect, options);
788 				invalidRegion.Include(viewRect);
789 				delete bitmap;
790 				break;
791 			}
792 
793 			case RP_DRAW_BITMAP_RECTS:
794 			{
795 				color_space colorSpace;
796 				int32 rectCount;
797 				uint32 flags, options;
798 
799 				message.Read(options);
800 				message.Read(colorSpace);
801 				message.Read(flags);
802 				message.Read(rectCount);
803 				for (int32 i = 0; i < rectCount; i++) {
804 					BBitmap *bitmap;
805 					BRect viewRect;
806 
807 					message.Read(viewRect);
808 					if (message.ReadBitmap(&bitmap, true, colorSpace,
809 							flags) != B_OK || bitmap == NULL) {
810 						continue;
811 					}
812 
813 					offscreen->DrawBitmap(bitmap, bitmap->Bounds(), viewRect,
814 						options);
815 					invalidRegion.Include(viewRect);
816 					delete bitmap;
817 				}
818 
819 				break;
820 			}
821 
822 			case RP_STROKE_ARC:
823 			case RP_FILL_ARC:
824 			case RP_FILL_ARC_GRADIENT:
825 			{
826 				BRect rect;
827 				float angle, span;
828 
829 				message.Read(rect);
830 				message.Read(angle);
831 				if (message.Read(span) != B_OK)
832 					continue;
833 
834 				if (code == RP_STROKE_ARC) {
835 					offscreen->StrokeArc(rect, angle, span, pattern);
836 					rect.InsetBy(-penSize, -penSize);
837 				} else if (code == RP_FILL_ARC)
838 					offscreen->FillArc(rect, angle, span, pattern);
839 				else {
840 					BGradient *gradient;
841 					if (message.ReadGradient(&gradient) != B_OK)
842 						continue;
843 
844 					offscreen->FillArc(rect, angle, span, *gradient);
845 					delete gradient;
846 				}
847 
848 				invalidRegion.Include(rect);
849 				break;
850 			}
851 
852 			case RP_STROKE_BEZIER:
853 			case RP_FILL_BEZIER:
854 			case RP_FILL_BEZIER_GRADIENT:
855 			{
856 				BPoint points[4];
857 				if (message.ReadList(points, 4) != B_OK)
858 					continue;
859 
860 				BRect bounds = _BuildInvalidateRect(points, 4);
861 				if (code == RP_STROKE_BEZIER) {
862 					offscreen->StrokeBezier(points, pattern);
863 					bounds.InsetBy(-penSize, -penSize);
864 				} else if (code == RP_FILL_BEZIER)
865 					offscreen->FillBezier(points, pattern);
866 				else {
867 					BGradient *gradient;
868 					if (message.ReadGradient(&gradient) != B_OK)
869 						continue;
870 
871 					offscreen->FillBezier(points, *gradient);
872 					delete gradient;
873 				}
874 
875 				invalidRegion.Include(bounds);
876 				break;
877 			}
878 
879 			case RP_STROKE_ELLIPSE:
880 			case RP_FILL_ELLIPSE:
881 			case RP_FILL_ELLIPSE_GRADIENT:
882 			{
883 				BRect rect;
884 				if (message.Read(rect) != B_OK)
885 					continue;
886 
887 				if (code == RP_STROKE_ELLIPSE) {
888 					offscreen->StrokeEllipse(rect, pattern);
889 					rect.InsetBy(-penSize, -penSize);
890 				} else if (code == RP_FILL_ELLIPSE)
891 					offscreen->FillEllipse(rect, pattern);
892 				else {
893 					BGradient *gradient;
894 					if (message.ReadGradient(&gradient) != B_OK)
895 						continue;
896 
897 					offscreen->FillEllipse(rect, *gradient);
898 					delete gradient;
899 				}
900 
901 				invalidRegion.Include(rect);
902 				break;
903 			}
904 
905 			case RP_STROKE_POLYGON:
906 			case RP_FILL_POLYGON:
907 			case RP_FILL_POLYGON_GRADIENT:
908 			{
909 				BRect bounds;
910 				bool closed;
911 				int32 numPoints;
912 
913 				message.Read(bounds);
914 				message.Read(closed);
915 				if (message.Read(numPoints) != B_OK)
916 					continue;
917 
918 				BPoint points[numPoints];
919 				for (int32 i = 0; i < numPoints; i++)
920 					message.Read(points[i]);
921 
922 				if (code == RP_STROKE_POLYGON) {
923 					offscreen->StrokePolygon(points, numPoints, bounds, closed,
924 						pattern);
925 					bounds.InsetBy(-penSize, -penSize);
926 				} else if (code == RP_FILL_POLYGON)
927 					offscreen->FillPolygon(points, numPoints, bounds, pattern);
928 				else {
929 					BGradient *gradient;
930 					if (message.ReadGradient(&gradient) != B_OK)
931 						continue;
932 
933 					offscreen->FillPolygon(points, numPoints, bounds,
934 						*gradient);
935 					delete gradient;
936 				}
937 
938 				invalidRegion.Include(bounds);
939 				break;
940 			}
941 
942 			case RP_STROKE_RECT:
943 			case RP_FILL_RECT:
944 			case RP_FILL_RECT_GRADIENT:
945 			{
946 				BRect rect;
947 				if (message.Read(rect) != B_OK)
948 					continue;
949 
950 				if (code == RP_STROKE_RECT) {
951 					offscreen->StrokeRect(rect, pattern);
952 					rect.InsetBy(-penSize, -penSize);
953 				} else if (code == RP_FILL_RECT)
954 					offscreen->FillRect(rect, pattern);
955 				else {
956 					BGradient *gradient;
957 					if (message.ReadGradient(&gradient) != B_OK)
958 						continue;
959 
960 					offscreen->FillRect(rect, *gradient);
961 					delete gradient;
962 				}
963 
964 				invalidRegion.Include(rect);
965 				break;
966 			}
967 
968 			case RP_STROKE_ROUND_RECT:
969 			case RP_FILL_ROUND_RECT:
970 			case RP_FILL_ROUND_RECT_GRADIENT:
971 			{
972 				BRect rect;
973 				float xRadius, yRadius;
974 
975 				message.Read(rect);
976 				message.Read(xRadius);
977 				if (message.Read(yRadius) != B_OK)
978 					continue;
979 
980 				if (code == RP_STROKE_ROUND_RECT) {
981 					offscreen->StrokeRoundRect(rect, xRadius, yRadius,
982 						pattern);
983 					rect.InsetBy(-penSize, -penSize);
984 				} else if (code == RP_FILL_ROUND_RECT)
985 					offscreen->FillRoundRect(rect, xRadius, yRadius, pattern);
986 				else {
987 					BGradient *gradient;
988 					if (message.ReadGradient(&gradient) != B_OK)
989 						continue;
990 
991 					offscreen->FillRoundRect(rect, xRadius, yRadius,
992 						*gradient);
993 					delete gradient;
994 				}
995 
996 				invalidRegion.Include(rect);
997 				break;
998 			}
999 
1000 			case RP_STROKE_SHAPE:
1001 			case RP_FILL_SHAPE:
1002 			case RP_FILL_SHAPE_GRADIENT:
1003 			{
1004 				BRect bounds;
1005 				int32 opCount, pointCount;
1006 
1007 				message.Read(bounds);
1008 				if (message.Read(opCount) != B_OK)
1009 					continue;
1010 
1011 				BMessage archive;
1012 				for (int32 i = 0; i < opCount; i++) {
1013 					int32 op;
1014 					message.Read(op);
1015 					archive.AddInt32("ops", op);
1016 				}
1017 
1018 				if (message.Read(pointCount) != B_OK)
1019 					continue;
1020 
1021 				for (int32 i = 0; i < pointCount; i++) {
1022 					BPoint point;
1023 					message.Read(point);
1024 					archive.AddPoint("pts", point);
1025 				}
1026 
1027 				BPoint offset;
1028 				message.Read(offset);
1029 
1030 				float scale;
1031 				if (message.Read(scale) != B_OK)
1032 					continue;
1033 
1034 				offscreen->PushState();
1035 				offscreen->MovePenTo(offset);
1036 				offscreen->SetScale(scale);
1037 
1038 				BShape shape(&archive);
1039 				if (code == RP_STROKE_SHAPE) {
1040 					offscreen->StrokeShape(&shape, pattern);
1041 					bounds.InsetBy(-penSize, -penSize);
1042 				} else if (code == RP_FILL_SHAPE)
1043 					offscreen->FillShape(&shape, pattern);
1044 				else {
1045 					BGradient *gradient;
1046 					if (message.ReadGradient(&gradient) != B_OK) {
1047 						offscreen->PopState();
1048 						continue;
1049 					}
1050 
1051 					offscreen->FillShape(&shape, *gradient);
1052 					delete gradient;
1053 				}
1054 
1055 				offscreen->PopState();
1056 				invalidRegion.Include(bounds);
1057 				break;
1058 			}
1059 
1060 			case RP_STROKE_TRIANGLE:
1061 			case RP_FILL_TRIANGLE:
1062 			case RP_FILL_TRIANGLE_GRADIENT:
1063 			{
1064 				BRect bounds;
1065 				BPoint points[3];
1066 
1067 				message.ReadList(points, 3);
1068 				if (message.Read(bounds) != B_OK)
1069 					continue;
1070 
1071 				if (code == RP_STROKE_TRIANGLE) {
1072 					offscreen->StrokeTriangle(points[0], points[1], points[2],
1073 						bounds, pattern);
1074 					bounds.InsetBy(-penSize, -penSize);
1075 				} else if (code == RP_FILL_TRIANGLE) {
1076 					offscreen->FillTriangle(points[0], points[1], points[2],
1077 						bounds, pattern);
1078 				} else {
1079 					BGradient *gradient;
1080 					if (message.ReadGradient(&gradient) != B_OK)
1081 						continue;
1082 
1083 					offscreen->FillTriangle(points[0], points[1], points[2],
1084 						bounds, *gradient);
1085 					delete gradient;
1086 				}
1087 
1088 				invalidRegion.Include(bounds);
1089 				break;
1090 			}
1091 
1092 			case RP_STROKE_LINE:
1093 			{
1094 				BPoint points[2];
1095 				if (message.ReadList(points, 2) != B_OK)
1096 					continue;
1097 
1098 				offscreen->StrokeLine(points[0], points[1], pattern);
1099 
1100 				BRect bounds = _BuildInvalidateRect(points, 2);
1101 				invalidRegion.Include(bounds.InsetBySelf(-penSize, -penSize));
1102 				break;
1103 			}
1104 
1105 			case RP_STROKE_LINE_ARRAY:
1106 			{
1107 				int32 numLines;
1108 				if (message.Read(numLines) != B_OK)
1109 					continue;
1110 
1111 				BRect bounds;
1112 				offscreen->BeginLineArray(numLines);
1113 				for (int32 i = 0; i < numLines; i++) {
1114 					rgb_color color;
1115 					BPoint start, end;
1116 					message.ReadArrayLine(start, end, color);
1117 					offscreen->AddLine(start, end, color);
1118 
1119 					bounds.left = min_c(bounds.left, min_c(start.x, end.x));
1120 					bounds.top = min_c(bounds.top, min_c(start.y, end.y));
1121 					bounds.right = max_c(bounds.right, max_c(start.x, end.x));
1122 					bounds.bottom = max_c(bounds.bottom, max_c(start.y, end.y));
1123 				}
1124 
1125 				offscreen->EndLineArray();
1126 				invalidRegion.Include(bounds);
1127 				break;
1128 			}
1129 
1130 			case RP_FILL_REGION:
1131 			case RP_FILL_REGION_GRADIENT:
1132 			{
1133 				BRegion region;
1134 				if (message.ReadRegion(region) != B_OK)
1135 					continue;
1136 
1137 				if (code == RP_FILL_REGION)
1138 					offscreen->FillRegion(&region, pattern);
1139 				else {
1140 					BGradient *gradient;
1141 					if (message.ReadGradient(&gradient) != B_OK)
1142 						continue;
1143 
1144 					offscreen->FillRegion(&region, *gradient);
1145 					delete gradient;
1146 				}
1147 
1148 				invalidRegion.Include(&region);
1149 				break;
1150 			}
1151 
1152 			case RP_STROKE_POINT_COLOR:
1153 			{
1154 				BPoint point;
1155 				rgb_color color;
1156 
1157 				message.Read(point);
1158 				if (message.Read(color) != B_OK)
1159 					continue;
1160 
1161 				rgb_color oldColor = offscreen->HighColor();
1162 				offscreen->SetHighColor(color);
1163 				offscreen->StrokeLine(point, point);
1164 				offscreen->SetHighColor(oldColor);
1165 
1166 				invalidRegion.Include(
1167 					BRect(point, point).InsetBySelf(-penSize, -penSize));
1168 				break;
1169 			}
1170 
1171 			case RP_STROKE_LINE_1PX_COLOR:
1172 			{
1173 				BPoint points[2];
1174 				rgb_color color;
1175 
1176 				message.ReadList(points, 2);
1177 				if (message.Read(color) != B_OK)
1178 					continue;
1179 
1180 				float oldSize = offscreen->PenSize();
1181 				rgb_color oldColor = offscreen->HighColor();
1182 				drawing_mode oldMode = offscreen->DrawingMode();
1183 				offscreen->SetPenSize(1);
1184 				offscreen->SetHighColor(color);
1185 				offscreen->SetDrawingMode(B_OP_OVER);
1186 
1187 				offscreen->StrokeLine(points[0], points[1]);
1188 
1189 				offscreen->SetDrawingMode(oldMode);
1190 				offscreen->SetHighColor(oldColor);
1191 				offscreen->SetPenSize(oldSize);
1192 
1193 				invalidRegion.Include(_BuildInvalidateRect(points, 2));
1194 				break;
1195 			}
1196 
1197 			case RP_STROKE_RECT_1PX_COLOR:
1198 			case RP_FILL_RECT_COLOR:
1199 			{
1200 				BRect rect;
1201 				rgb_color color;
1202 
1203 				message.Read(rect);
1204 				if (message.Read(color) != B_OK)
1205 					continue;
1206 
1207 				rgb_color oldColor = offscreen->HighColor();
1208 				offscreen->SetHighColor(color);
1209 
1210 				if (code == RP_STROKE_RECT_1PX_COLOR) {
1211 					float oldSize = PenSize();
1212 					offscreen->SetPenSize(1);
1213 					offscreen->StrokeRect(rect);
1214 					offscreen->SetPenSize(oldSize);
1215 				} else
1216 					offscreen->FillRect(rect);
1217 
1218 				offscreen->SetHighColor(oldColor);
1219 				invalidRegion.Include(rect);
1220 				break;
1221 			}
1222 
1223 			case RP_DRAW_STRING:
1224 			{
1225 				BPoint point;
1226 				size_t length;
1227 				char *string;
1228 				bool hasDelta;
1229 
1230 				message.Read(point);
1231 				message.ReadString(&string, length);
1232 				if (message.Read(hasDelta) != B_OK) {
1233 					free(string);
1234 					continue;
1235 				}
1236 
1237 				if (hasDelta) {
1238 					escapement_delta delta[length];
1239 					message.ReadList(delta, length);
1240 					offscreen->DrawString(string, point, delta);
1241 				} else
1242 					offscreen->DrawString(string, point);
1243 
1244 				free(string);
1245 				reply.Start(RP_DRAW_STRING_RESULT);
1246 				reply.Add(token);
1247 				reply.Add(offscreen->PenLocation());
1248 				reply.Flush();
1249 
1250 				font_height height;
1251 				offscreen->GetFontHeight(&height);
1252 
1253 				BRect bounds(point, offscreen->PenLocation());
1254 				bounds.top -= height.ascent;
1255 				bounds.bottom += height.descent;
1256 				invalidRegion.Include(bounds);
1257 				break;
1258 			}
1259 
1260 			case RP_DRAW_STRING_WITH_OFFSETS:
1261 			{
1262 				size_t length;
1263 				char *string;
1264 				message.ReadString(&string, length);
1265 				int32 count = UTF8CountChars(string, length);
1266 
1267 				BPoint offsets[count];
1268 				if (message.ReadList(offsets, count) != B_OK) {
1269 					free(string);
1270 					continue;
1271 				}
1272 
1273 				offscreen->DrawString(string, offsets, count);
1274 
1275 				free(string);
1276 				reply.Start(RP_DRAW_STRING_RESULT);
1277 				reply.Add(token);
1278 				reply.Add(offscreen->PenLocation());
1279 				reply.Flush();
1280 
1281 				BFont font;
1282 				offscreen->GetFont(&font);
1283 
1284 				BRect boxes[count];
1285 				font.GetBoundingBoxesAsGlyphs(string, count, B_SCREEN_METRIC,
1286 					boxes);
1287 
1288 				font_height height;
1289 				offscreen->GetFontHeight(&height);
1290 
1291 				for (int32 i = 0; i < count; i++) {
1292 					// TODO: validate
1293 					boxes[i].OffsetBy(offsets[i] + BPoint(0, -height.ascent));
1294 					invalidRegion.Include(boxes[i]);
1295 				}
1296 
1297 				break;
1298 			}
1299 
1300 			case RP_READ_BITMAP:
1301 			{
1302 				BRect bounds;
1303 				bool drawCursor;
1304 
1305 				message.Read(bounds);
1306 				if (message.Read(drawCursor) != B_OK)
1307 					continue;
1308 
1309 				// TODO: support the drawCursor flag
1310 				BBitmap bitmap(bounds, B_BITMAP_NO_SERVER_LINK, B_RGB32);
1311 				bitmap.ImportBits(fOffscreenBitmap, bounds.LeftTop(),
1312 					BPoint(0, 0), bounds.IntegerWidth() + 1,
1313 					bounds.IntegerHeight() + 1);
1314 
1315 				reply.Start(RP_READ_BITMAP_RESULT);
1316 				reply.Add(token);
1317 				reply.AddBitmap(&bitmap);
1318 				reply.Flush();
1319 				break;
1320 			}
1321 
1322 			default:
1323 				TRACE_ERROR("unknown protocol code: %u\n", code);
1324 				break;
1325 		}
1326 
1327 		if (syncDrawing) {
1328 			offscreen->Sync();
1329 			Invalidate(&invalidRegion);
1330 		}
1331 	}
1332 }
1333 
1334 
1335 BRect
1336 RemoteView::_BuildInvalidateRect(BPoint *points, int32 pointCount)
1337 {
1338 	BRect bounds(1000000, 1000000, 0, 0);
1339 	for (int32 i = 0; i < pointCount; i++) {
1340 		bounds.left = min_c(bounds.left, points[i].x);
1341 		bounds.top = min_c(bounds.top, points[i].y);
1342 		bounds.right = max_c(bounds.right, points[i].x);
1343 		bounds.bottom = max_c(bounds.bottom, points[i].y);
1344 	}
1345 
1346 	return bounds;
1347 }
1348