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