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