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