1/*
2 * Copyright 2003-2009, Haiku Inc.
3 * Authors:
4 *		Stefano Ceccherini <stefano.ceccherini@gmail.com>
5 *		Carwyn Jones <turok2@currantbun.com>
6 *
7 * Distributed under the terms of the MIT License.
8 */
9
10
11#include <DirectWindow.h>
12
13#include <stdio.h>
14#include <string.h>
15
16#include <Screen.h>
17
18#include <clipping.h>
19#include <AppServerLink.h>
20#include <DirectWindowPrivate.h>
21#include <ServerProtocol.h>
22
23
24//#define DEBUG		1
25#define OUTPUT		printf
26//#define OUTPUT	debug_printf
27
28
29// We don't need this kind of locking, since the directDaemonFunc
30// doesn't access critical shared data.
31#define DW_NEEDS_LOCKING 0
32
33enum dw_status_bits {
34	DW_STATUS_AREA_CLONED	 = 0x1,
35	DW_STATUS_THREAD_STARTED = 0x2,
36	DW_STATUS_SEM_CREATED	 = 0x4
37};
38
39
40#if DEBUG
41
42
43static void
44print_direct_buffer_state(const direct_buffer_state &state)
45{
46	char string[128];
47	int modeState = state & B_DIRECT_MODE_MASK;
48	if (modeState == B_DIRECT_START)
49		strcpy(string, "B_DIRECT_START");
50	else if (modeState == B_DIRECT_MODIFY)
51		strcpy(string, "B_DIRECT_MODIFY");
52	else if (modeState == B_DIRECT_STOP)
53		strcpy(string, "B_DIRECT_STOP");
54
55	if (state & B_CLIPPING_MODIFIED)
56		strcat(string, " | B_CLIPPING_MODIFIED");
57	if (state & B_BUFFER_RESIZED)
58		strcat(string, " | B_BUFFER_RESIZED");
59	if (state & B_BUFFER_MOVED)
60		strcat(string, " | B_BUFFER_MOVED");
61	if (state & B_BUFFER_RESET)
62		strcat(string, " | B_BUFFER_RESET");
63
64	OUTPUT("direct_buffer_state: %s\n", string);
65}
66
67
68static void
69print_direct_driver_state(const direct_driver_state &state)
70{
71	if (state == 0)
72		return;
73
74	char string[64];
75	if (state == B_DRIVER_CHANGED)
76		strcpy(string, "B_DRIVER_CHANGED");
77	else if (state == B_MODE_CHANGED)
78		strcpy(string, "B_MODE_CHANGED");
79
80	OUTPUT("direct_driver_state: %s\n", string);
81}
82
83
84#if DEBUG > 1
85
86
87static void
88print_direct_buffer_layout(const buffer_layout &layout)
89{
90	char string[64];
91	if (layout == B_BUFFER_NONINTERLEAVED)
92		strcpy(string, "B_BUFFER_NONINTERLEAVED");
93	else
94		strcpy(string, "unknown");
95
96	OUTPUT("layout: %s\n", string);
97}
98
99
100static void
101print_direct_buffer_orientation(const buffer_orientation &orientation)
102{
103	char string[64];
104	switch (orientation) {
105		case B_BUFFER_TOP_TO_BOTTOM:
106			strcpy(string, "B_BUFFER_TOP_TO_BOTTOM");
107			break;
108		case B_BUFFER_BOTTOM_TO_TOP:
109			strcpy(string, "B_BUFFER_BOTTOM_TO_TOP");
110			break;
111		default:
112			strcpy(string, "unknown");
113			break;
114	}
115
116	OUTPUT("orientation: %s\n", string);
117}
118
119
120#endif	// DEBUG > 2
121
122
123static void
124print_direct_buffer_info(const direct_buffer_info &info)
125{
126	print_direct_buffer_state(info.buffer_state);
127	print_direct_driver_state(info.driver_state);
128
129#	if DEBUG > 1
130	OUTPUT("bits: %p\n", info.bits);
131	OUTPUT("pci_bits: %p\n", info.pci_bits);
132	OUTPUT("bytes_per_row: %ld\n", info.bytes_per_row);
133	OUTPUT("bits_per_pixel: %lu\n", info.bits_per_pixel);
134	OUTPUT("pixel_format: %d\n", info.pixel_format);
135	print_direct_buffer_layout(info.layout);
136	print_direct_buffer_orientation(info.orientation);
137
138#		if DEBUG > 2
139	// TODO: this won't work correctly with debug_printf()
140	printf("CLIPPING INFO:\n");
141	printf("clipping_rects count: %ld\n", info.clip_list_count);
142
143	printf("- window_bounds:\n");
144	BRegion region;
145	region.Set(info.window_bounds);
146	region.PrintToStream();
147
148	region.MakeEmpty();
149	for (uint32 i = 0; i < info.clip_list_count; i++)
150		region.Include(info.clip_list[i]);
151
152	printf("- clip_list:\n");
153	region.PrintToStream();
154#		endif
155#	endif
156
157	OUTPUT("\n");
158}
159
160
161#endif	// DEBUG
162
163
164//	#pragma mark -
165
166
167BDirectWindow::BDirectWindow(BRect frame, const char *title, window_type type,
168		uint32 flags, uint32 workspace)
169	:
170	BWindow(frame, title, type, flags, workspace)
171{
172	_InitData();
173}
174
175
176BDirectWindow::BDirectWindow(BRect frame, const char *title, window_look look,
177		window_feel feel, uint32 flags, uint32 workspace)
178	:
179	BWindow(frame, title, look, feel, flags, workspace)
180{
181	_InitData();
182}
183
184
185BDirectWindow::~BDirectWindow()
186{
187	_DisposeData();
188}
189
190
191//	#pragma mark - BWindow API implementation
192
193
194BArchivable *
195BDirectWindow::Instantiate(BMessage *data)
196{
197	return NULL;
198}
199
200
201status_t
202BDirectWindow::Archive(BMessage *data, bool deep) const
203{
204	return inherited::Archive(data, deep);
205}
206
207
208void
209BDirectWindow::Quit()
210{
211	inherited::Quit();
212}
213
214
215void
216BDirectWindow::DispatchMessage(BMessage *message, BHandler *handler)
217{
218	inherited::DispatchMessage(message, handler);
219}
220
221
222void
223BDirectWindow::MessageReceived(BMessage *message)
224{
225	inherited::MessageReceived(message);
226}
227
228
229void
230BDirectWindow::FrameMoved(BPoint newPosition)
231{
232	inherited::FrameMoved(newPosition);
233}
234
235
236void
237BDirectWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
238{
239	inherited::WorkspacesChanged(oldWorkspaces, newWorkspaces);
240}
241
242
243void
244BDirectWindow::WorkspaceActivated(int32 index, bool state)
245{
246	inherited::WorkspaceActivated(index, state);
247}
248
249
250void
251BDirectWindow::FrameResized(float newWidth, float newHeight)
252{
253	inherited::FrameResized(newWidth, newHeight);
254}
255
256
257void
258BDirectWindow::Minimize(bool minimize)
259{
260	inherited::Minimize(minimize);
261}
262
263
264void
265BDirectWindow::Zoom(BPoint recPosition, float recWidth, float recHeight)
266{
267	inherited::Zoom(recPosition, recWidth, recHeight);
268}
269
270
271void
272BDirectWindow::ScreenChanged(BRect screenFrame, color_space depth)
273{
274	inherited::ScreenChanged(screenFrame, depth);
275}
276
277
278void
279BDirectWindow::MenusBeginning()
280{
281	inherited::MenusBeginning();
282}
283
284
285void
286BDirectWindow::MenusEnded()
287{
288	inherited::MenusEnded();
289}
290
291
292void
293BDirectWindow::WindowActivated(bool state)
294{
295	inherited::WindowActivated(state);
296}
297
298
299void
300BDirectWindow::Show()
301{
302	inherited::Show();
303}
304
305
306void
307BDirectWindow::Hide()
308{
309	inherited::Hide();
310}
311
312
313BHandler *
314BDirectWindow::ResolveSpecifier(BMessage *msg, int32 index,
315	BMessage *specifier, int32 form, const char *property)
316{
317	return inherited::ResolveSpecifier(msg, index, specifier, form, property);
318}
319
320
321status_t
322BDirectWindow::GetSupportedSuites(BMessage *data)
323{
324	return inherited::GetSupportedSuites(data);
325}
326
327
328status_t
329BDirectWindow::Perform(perform_code d, void *arg)
330{
331	return inherited::Perform(d, arg);
332}
333
334
335void
336BDirectWindow::task_looper()
337{
338	inherited::task_looper();
339}
340
341
342BMessage *
343BDirectWindow::ConvertToMessage(void *raw, int32 code)
344{
345	return inherited::ConvertToMessage(raw, code);
346}
347
348
349//	#pragma mark - BDirectWindow specific API
350
351
352void
353BDirectWindow::DirectConnected(direct_buffer_info *info)
354{
355	// implemented in subclasses
356}
357
358
359status_t
360BDirectWindow::GetClippingRegion(BRegion *region, BPoint *origin) const
361{
362	if (region == NULL)
363		return B_BAD_VALUE;
364
365	if (IsLocked() || !_LockDirect())
366		return B_ERROR;
367
368	if (!fInDirectConnect) {
369		_UnlockDirect();
370		return B_ERROR;
371	}
372
373	// BPoint's coordinates are floats. We can only work
374	// with integers._DaemonStarter
375	int32 originX, originY;
376	if (origin == NULL) {
377		originX = 0;
378		originY = 0;
379	} else {
380		originX = (int32)origin->x;
381		originY = (int32)origin->y;
382	}
383
384#ifndef HAIKU_TARGET_PLATFORM_DANO
385	// Since we are friend of BRegion, we can access its private members.
386	// Otherwise, we would need to call BRegion::Include(clipping_rect)
387	// for every clipping_rect in our clip_list, and that would be much
388	// more overkill than this (tested ).
389	if (!region->_SetSize(fBufferDesc->clip_list_count)) {
390		_UnlockDirect();
391		return B_NO_MEMORY;
392	}
393	region->fCount = fBufferDesc->clip_list_count;
394	region->fBounds = region->_ConvertToInternal(fBufferDesc->clip_bounds);
395	for (uint32 c = 0; c < fBufferDesc->clip_list_count; c++) {
396		region->fData[c] = region->_ConvertToInternal(
397			fBufferDesc->clip_list[c]);
398	}
399
400	// adjust bounds by the given origin point
401	region->OffsetBy(-originX, -originY);
402#endif
403
404	_UnlockDirect();
405
406	return B_OK;
407
408}
409
410
411status_t
412BDirectWindow::SetFullScreen(bool enable)
413{
414	if (fIsFullScreen == enable)
415		return B_OK;
416
417	status_t status = B_ERROR;
418	if (Lock()) {
419		fLink->StartMessage(AS_DIRECT_WINDOW_SET_FULLSCREEN);
420		fLink->Attach<bool>(enable);
421
422		if (fLink->FlushWithReply(status) == B_OK
423			&& status == B_OK) {
424			fIsFullScreen = enable;
425		}
426		Unlock();
427	}
428	return status;
429}
430
431
432bool
433BDirectWindow::IsFullScreen() const
434{
435	return fIsFullScreen;
436}
437
438
439/*static*/ bool
440BDirectWindow::SupportsWindowMode(screen_id id)
441{
442	display_mode mode;
443	status_t status = BScreen(id).GetMode(&mode);
444	if (status == B_OK)
445		return (mode.flags & B_PARALLEL_ACCESS) != 0;
446
447	return false;
448}
449
450
451//	#pragma mark - Private methods
452
453
454/*static*/ int32
455BDirectWindow::_daemon_thread(void *arg)
456{
457	return static_cast<BDirectWindow *>(arg)->_DirectDaemon();
458}
459
460
461int32
462BDirectWindow::_DirectDaemon()
463{
464	while (!fDaemonKiller) {
465		// This sem is released by the app_server when our
466		// clipping region changes, or when our window is moved,
467		// resized, etc. etc.
468		status_t status;
469		do {
470			status = acquire_sem(fDisableSem);
471		} while (status == B_INTERRUPTED);
472
473		if (status != B_OK) {
474			//fprintf(stderr, "DirectDaemon: failed to acquire direct sem: %s\n",
475				// strerror(status));
476			return -1;
477		}
478
479#if DEBUG
480		print_direct_buffer_info(*fBufferDesc);
481#endif
482
483		if (_LockDirect()) {
484			if ((fBufferDesc->buffer_state & B_DIRECT_MODE_MASK)
485					== B_DIRECT_START)
486				fConnectionEnable = true;
487
488			fInDirectConnect = true;
489			DirectConnected(fBufferDesc);
490			fInDirectConnect = false;
491
492			if ((fBufferDesc->buffer_state & B_DIRECT_MODE_MASK)
493					== B_DIRECT_STOP)
494				fConnectionEnable = false;
495
496			_UnlockDirect();
497		}
498
499		// The app_server then waits (with a timeout) on this sem.
500		// If we aren't quick enough to release this sem, our app
501		// will be terminated by the app_server
502		if ((status = release_sem(fDisableSemAck)) != B_OK) {
503			//fprintf(stderr, "DirectDaemon: failed to release sem: %s\n",
504				//strerror(status));
505			return -1;
506		}
507	}
508
509	return 0;
510}
511
512
513bool
514BDirectWindow::_LockDirect() const
515{
516	// LockDirect() and UnlockDirect() are no-op on BeOS. I tried to call BeOS's
517	// version repeatedly, from the same thread and from different threads,
518	// nothing happened.
519	// They're not needed though, as the direct_daemon_thread doesn't change
520	// any shared data. They are probably here for future enhancements.
521	status_t status = B_OK;
522
523#if DW_NEEDS_LOCKING
524	BDirectWindow *casted = const_cast<BDirectWindow *>(this);
525
526	if (atomic_add(&casted->fDirectLock, 1) > 0) {
527		do {
528			status = acquire_sem(casted->fDirectSem);
529		} while (status == B_INTERRUPTED);
530	}
531
532	if (status == B_OK) {
533		casted->fDirectLockOwner = find_thread(NULL);
534		casted->fDirectLockCount++;
535	}
536#endif
537
538	return status == B_OK;
539}
540
541
542void
543BDirectWindow::_UnlockDirect() const
544{
545#if DW_NEEDS_LOCKING
546	BDirectWindow *casted = const_cast<BDirectWindow *>(this);
547
548	if (atomic_add(&casted->fDirectLock, -1) > 1)
549		release_sem(casted->fDirectSem);
550
551	casted->fDirectLockCount--;
552#endif
553}
554
555
556void
557BDirectWindow::_InitData()
558{
559	fConnectionEnable = false;
560	fIsFullScreen = false;
561	fInDirectConnect = false;
562
563	fInitStatus = 0;
564
565	status_t status = B_ERROR;
566	struct direct_window_sync_data syncData;
567
568	fLink->StartMessage(AS_DIRECT_WINDOW_GET_SYNC_DATA);
569	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
570		fLink->Read<direct_window_sync_data>(&syncData);
571
572	if (status != B_OK)
573		return;
574
575#if DW_NEEDS_LOCKING
576	fDirectLock = 0;
577	fDirectLockCount = 0;
578	fDirectLockOwner = -1;
579	fDirectLockStack = NULL;
580	fDirectSem = create_sem(0, "direct sem");
581	if (fDirectSem > 0)
582		fInitStatus |= DW_STATUS_SEM_CREATED;
583#endif
584
585	fSourceClippingArea = syncData.area;
586	fDisableSem = syncData.disable_sem;
587	fDisableSemAck = syncData.disable_sem_ack;
588
589	fClonedClippingArea = clone_area("cloned direct area", (void**)&fBufferDesc,
590		B_ANY_ADDRESS, B_READ_AREA, fSourceClippingArea);
591
592	if (fClonedClippingArea > 0) {
593		fInitStatus |= DW_STATUS_AREA_CLONED;
594
595		fDirectDaemonId = spawn_thread(_daemon_thread, "direct daemon",
596			B_DISPLAY_PRIORITY, this);
597
598		if (fDirectDaemonId > 0) {
599			fDaemonKiller = false;
600			if (resume_thread(fDirectDaemonId) == B_OK)
601				fInitStatus |= DW_STATUS_THREAD_STARTED;
602			else
603				kill_thread(fDirectDaemonId);
604		}
605	}
606}
607
608
609void
610BDirectWindow::_DisposeData()
611{
612	// wait until the connection terminates: we can't destroy
613	// the object until the client receives the B_DIRECT_STOP
614	// notification, or bad things will happen
615	while (fConnectionEnable)
616		snooze(50000);
617
618	_LockDirect();
619
620	if (fInitStatus & DW_STATUS_THREAD_STARTED) {
621		fDaemonKiller = true;
622		// delete this sem, otherwise the Direct daemon thread
623		// will wait forever on it
624		delete_sem(fDisableSem);
625		status_t retVal;
626		wait_for_thread(fDirectDaemonId, &retVal);
627	}
628
629#if DW_NEEDS_LOCKING
630	if (fInitStatus & DW_STATUS_SEM_CREATED)
631		delete_sem(fDirectSem);
632#endif
633
634	if (fInitStatus & DW_STATUS_AREA_CLONED)
635		delete_area(fClonedClippingArea);
636}
637
638
639void BDirectWindow::_ReservedDirectWindow1() {}
640void BDirectWindow::_ReservedDirectWindow2() {}
641void BDirectWindow::_ReservedDirectWindow3() {}
642void BDirectWindow::_ReservedDirectWindow4() {}
643