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
29static 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
47typedef 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
57RemoteView::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
159RemoteView::~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
179status_t
180RemoteView::InitCheck()
181{
182	return fInitStatus;
183}
184
185
186void
187RemoteView::AttachedToWindow()
188{
189	SetViewColor(B_TRANSPARENT_COLOR);
190	SetViewCursor(&fViewCursor);
191}
192
193
194void
195RemoteView::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
214void
215RemoteView::MouseMoved(BPoint where, uint32 code, const BMessage *dragMessage)
216{
217	if (!fIsConnected)
218		return;
219
220	_SendMouseMessage(RP_MOUSE_MOVED, where);
221}
222
223
224void
225RemoteView::MouseDown(BPoint where)
226{
227	if (!fIsConnected)
228		return;
229
230	_SendMouseMessage(RP_MOUSE_DOWN, where);
231}
232
233
234void
235RemoteView::MouseUp(BPoint where)
236{
237	if (!fIsConnected)
238		return;
239
240	_SendMouseMessage(RP_MOUSE_UP, where);
241}
242
243
244void
245RemoteView::KeyDown(const char *bytes, int32 numBytes)
246{
247	if (!fIsConnected)
248		return;
249
250	_SendKeyMessage(RP_KEY_DOWN, bytes, numBytes);
251}
252
253
254void
255RemoteView::KeyUp(const char *bytes, int32 numBytes)
256{
257	if (!fIsConnected)
258		return;
259
260	_SendKeyMessage(RP_KEY_UP, bytes, numBytes);
261}
262
263
264void
265RemoteView::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
309void
310RemoteView::_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
334void
335RemoteView::_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
353int
354RemoteView::_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
366engine_state *
367RemoteView::_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
406void
407RemoteView::_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
421engine_state *
422RemoteView::_FindState(uint32 token)
423{
424	return fStates.BinarySearchByKey(token, &_StateCompareByKey);
425}
426
427
428int32
429RemoteView::_DrawEntry(void *data)
430{
431	((RemoteView *)data)->_DrawThread();
432	return 0;
433}
434
435
436void
437RemoteView::_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
1318BRect
1319RemoteView::_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