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