1/*
2 * Copyright 2003-2009 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Stefano Ceccherini <stefano.ceccherini@gmail.com>
7 *		Carwyn Jones <turok2@currantbun.com>
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* message, int32 index,
315	BMessage* specifier, int32 what, const char* property)
316{
317	return inherited::ResolveSpecifier(message, index, specifier, what,
318		property);
319}
320
321
322status_t
323BDirectWindow::GetSupportedSuites(BMessage* data)
324{
325	return inherited::GetSupportedSuites(data);
326}
327
328
329status_t
330BDirectWindow::Perform(perform_code d, void* arg)
331{
332	return inherited::Perform(d, arg);
333}
334
335
336void
337BDirectWindow::task_looper()
338{
339	inherited::task_looper();
340}
341
342
343BMessage*
344BDirectWindow::ConvertToMessage(void* raw, int32 code)
345{
346	return inherited::ConvertToMessage(raw, code);
347}
348
349
350//	#pragma mark - BDirectWindow specific API
351
352
353void
354BDirectWindow::DirectConnected(direct_buffer_info* info)
355{
356	// implemented in subclasses
357}
358
359
360status_t
361BDirectWindow::GetClippingRegion(BRegion* region, BPoint* origin) const
362{
363	if (region == NULL)
364		return B_BAD_VALUE;
365
366	if (IsLocked() || !_LockDirect())
367		return B_ERROR;
368
369	if (!fInDirectConnect) {
370		_UnlockDirect();
371		return B_ERROR;
372	}
373
374	// BPoint's coordinates are floats. We can only work
375	// with integers._DaemonStarter
376	int32 originX, originY;
377	if (origin == NULL) {
378		originX = 0;
379		originY = 0;
380	} else {
381		originX = (int32)origin->x;
382		originY = (int32)origin->y;
383	}
384
385#ifndef HAIKU_TARGET_PLATFORM_DANO
386	// Since we are friend of BRegion, we can access its private members.
387	// Otherwise, we would need to call BRegion::Include(clipping_rect)
388	// for every clipping_rect in our clip_list, and that would be much
389	// more overkill than this (tested ).
390	if (!region->_SetSize(fBufferDesc->clip_list_count)) {
391		_UnlockDirect();
392		return B_NO_MEMORY;
393	}
394	region->fCount = fBufferDesc->clip_list_count;
395	region->fBounds = region->_ConvertToInternal(fBufferDesc->clip_bounds);
396	for (uint32 c = 0; c < fBufferDesc->clip_list_count; c++) {
397		region->fData[c] = region->_ConvertToInternal(
398			fBufferDesc->clip_list[c]);
399	}
400
401	// adjust bounds by the given origin point
402	region->OffsetBy(-originX, -originY);
403#endif
404
405	_UnlockDirect();
406
407	return B_OK;
408
409}
410
411
412status_t
413BDirectWindow::SetFullScreen(bool enable)
414{
415	if (fIsFullScreen == enable)
416		return B_OK;
417
418	status_t status = B_ERROR;
419	if (Lock()) {
420		fLink->StartMessage(AS_DIRECT_WINDOW_SET_FULLSCREEN);
421		fLink->Attach<bool>(enable);
422
423		if (fLink->FlushWithReply(status) == B_OK
424			&& status == B_OK) {
425			fIsFullScreen = enable;
426		}
427		Unlock();
428	}
429	return status;
430}
431
432
433bool
434BDirectWindow::IsFullScreen() const
435{
436	return fIsFullScreen;
437}
438
439
440/*static*/ bool
441BDirectWindow::SupportsWindowMode(screen_id id)
442{
443	display_mode mode;
444	status_t status = BScreen(id).GetMode(&mode);
445	if (status == B_OK)
446		return (mode.flags & B_PARALLEL_ACCESS) != 0;
447
448	return false;
449}
450
451
452//	#pragma mark - Private methods
453
454
455/*static*/ int32
456BDirectWindow::_daemon_thread(void* arg)
457{
458	return static_cast<BDirectWindow*>(arg)->_DirectDaemon();
459}
460
461
462int32
463BDirectWindow::_DirectDaemon()
464{
465	while (!fDaemonKiller) {
466		// This sem is released by the app_server when our
467		// clipping region changes, or when our window is moved,
468		// resized, etc. etc.
469		status_t status;
470		do {
471			status = acquire_sem(fDisableSem);
472		} while (status == B_INTERRUPTED);
473
474		if (status != B_OK) {
475			//fprintf(stderr, "DirectDaemon: failed to acquire direct sem: %s\n",
476				// strerror(status));
477			return -1;
478		}
479
480#if DEBUG
481		print_direct_buffer_info(*fBufferDesc);
482#endif
483
484		if (_LockDirect()) {
485			if ((fBufferDesc->buffer_state & B_DIRECT_MODE_MASK)
486					== B_DIRECT_START)
487				fConnectionEnable = true;
488
489			fInDirectConnect = true;
490			DirectConnected(fBufferDesc);
491			fInDirectConnect = false;
492
493			if ((fBufferDesc->buffer_state & B_DIRECT_MODE_MASK)
494					== B_DIRECT_STOP)
495				fConnectionEnable = false;
496
497			_UnlockDirect();
498		}
499
500		// The app_server then waits (with a timeout) on this sem.
501		// If we aren't quick enough to release this sem, our app
502		// will be terminated by the app_server
503		if ((status = release_sem(fDisableSemAck)) != B_OK) {
504			//fprintf(stderr, "DirectDaemon: failed to release sem: %s\n",
505				//strerror(status));
506			return -1;
507		}
508	}
509
510	return 0;
511}
512
513
514bool
515BDirectWindow::_LockDirect() const
516{
517	// LockDirect() and UnlockDirect() are no-op on BeOS. I tried to call BeOS's
518	// version repeatedly, from the same thread and from different threads,
519	// nothing happened.
520	// They're not needed though, as the direct_daemon_thread doesn't change
521	// any shared data. They are probably here for future enhancements.
522	status_t status = B_OK;
523
524#if DW_NEEDS_LOCKING
525	BDirectWindow* casted = const_cast<BDirectWindow*>(this);
526
527	if (atomic_add(&casted->fDirectLock, 1) > 0) {
528		do {
529			status = acquire_sem(casted->fDirectSem);
530		} while (status == B_INTERRUPTED);
531	}
532
533	if (status == B_OK) {
534		casted->fDirectLockOwner = find_thread(NULL);
535		casted->fDirectLockCount++;
536	}
537#endif
538
539	return status == B_OK;
540}
541
542
543void
544BDirectWindow::_UnlockDirect() const
545{
546#if DW_NEEDS_LOCKING
547	BDirectWindow* casted = const_cast<BDirectWindow*>(this);
548
549	if (atomic_add(&casted->fDirectLock, -1) > 1)
550		release_sem(casted->fDirectSem);
551
552	casted->fDirectLockCount--;
553#endif
554}
555
556
557void
558BDirectWindow::_InitData()
559{
560	fConnectionEnable = false;
561	fIsFullScreen = false;
562	fInDirectConnect = false;
563
564	fInitStatus = 0;
565
566	status_t status = B_ERROR;
567	struct direct_window_sync_data syncData;
568
569	fLink->StartMessage(AS_DIRECT_WINDOW_GET_SYNC_DATA);
570	if (fLink->FlushWithReply(status) == B_OK && status == B_OK)
571		fLink->Read<direct_window_sync_data>(&syncData);
572
573	if (status != B_OK)
574		return;
575
576#if DW_NEEDS_LOCKING
577	fDirectLock = 0;
578	fDirectLockCount = 0;
579	fDirectLockOwner = -1;
580	fDirectLockStack = NULL;
581	fDirectSem = create_sem(0, "direct sem");
582	if (fDirectSem > 0)
583		fInitStatus |= DW_STATUS_SEM_CREATED;
584#endif
585
586	fSourceClippingArea = syncData.area;
587	fDisableSem = syncData.disable_sem;
588	fDisableSemAck = syncData.disable_sem_ack;
589
590	fClonedClippingArea = clone_area("cloned direct area", (void**)&fBufferDesc,
591		B_ANY_ADDRESS, B_READ_AREA, fSourceClippingArea);
592
593	if (fClonedClippingArea > 0) {
594		fInitStatus |= DW_STATUS_AREA_CLONED;
595
596		fDirectDaemonId = spawn_thread(_daemon_thread, "direct daemon",
597			B_DISPLAY_PRIORITY, this);
598
599		if (fDirectDaemonId > 0) {
600			fDaemonKiller = false;
601			if (resume_thread(fDirectDaemonId) == B_OK)
602				fInitStatus |= DW_STATUS_THREAD_STARTED;
603			else
604				kill_thread(fDirectDaemonId);
605		}
606	}
607}
608
609
610void
611BDirectWindow::_DisposeData()
612{
613	// wait until the connection terminates: we can't destroy
614	// the object until the client receives the B_DIRECT_STOP
615	// notification, or bad things will happen
616	while (fConnectionEnable)
617		snooze(50000);
618
619	_LockDirect();
620
621	if (fInitStatus & DW_STATUS_THREAD_STARTED) {
622		fDaemonKiller = true;
623		// delete this sem, otherwise the Direct daemon thread
624		// will wait forever on it
625		delete_sem(fDisableSem);
626		status_t retVal;
627		wait_for_thread(fDirectDaemonId, &retVal);
628	}
629
630#if DW_NEEDS_LOCKING
631	if (fInitStatus & DW_STATUS_SEM_CREATED)
632		delete_sem(fDirectSem);
633#endif
634
635	if (fInitStatus & DW_STATUS_AREA_CLONED)
636		delete_area(fClonedClippingArea);
637}
638
639
640void BDirectWindow::_ReservedDirectWindow1() {}
641void BDirectWindow::_ReservedDirectWindow2() {}
642void BDirectWindow::_ReservedDirectWindow3() {}
643void BDirectWindow::_ReservedDirectWindow4() {}
644