xref: /haiku/src/kits/game/DirectWindow.cpp (revision 922e7ba1f3228e6f28db69b0ded8f86eb32dea17)
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 
33 enum 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 
43 static void
44 print_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 
68 static void
69 print_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 
87 static void
88 print_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 
100 static void
101 print_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 
123 static void
124 print_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 
167 BDirectWindow::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 
176 BDirectWindow::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 
185 BDirectWindow::~BDirectWindow()
186 {
187 	_DisposeData();
188 }
189 
190 
191 //	#pragma mark - BWindow API implementation
192 
193 
194 BArchivable *
195 BDirectWindow::Instantiate(BMessage *data)
196 {
197 	return NULL;
198 }
199 
200 
201 status_t
202 BDirectWindow::Archive(BMessage *data, bool deep) const
203 {
204 	return inherited::Archive(data, deep);
205 }
206 
207 
208 void
209 BDirectWindow::Quit()
210 {
211 	inherited::Quit();
212 }
213 
214 
215 void
216 BDirectWindow::DispatchMessage(BMessage *message, BHandler *handler)
217 {
218 	inherited::DispatchMessage(message, handler);
219 }
220 
221 
222 void
223 BDirectWindow::MessageReceived(BMessage *message)
224 {
225 	inherited::MessageReceived(message);
226 }
227 
228 
229 void
230 BDirectWindow::FrameMoved(BPoint newPosition)
231 {
232 	inherited::FrameMoved(newPosition);
233 }
234 
235 
236 void
237 BDirectWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
238 {
239 	inherited::WorkspacesChanged(oldWorkspaces, newWorkspaces);
240 }
241 
242 
243 void
244 BDirectWindow::WorkspaceActivated(int32 index, bool state)
245 {
246 	inherited::WorkspaceActivated(index, state);
247 }
248 
249 
250 void
251 BDirectWindow::FrameResized(float newWidth, float newHeight)
252 {
253 	inherited::FrameResized(newWidth, newHeight);
254 }
255 
256 
257 void
258 BDirectWindow::Minimize(bool minimize)
259 {
260 	inherited::Minimize(minimize);
261 }
262 
263 
264 void
265 BDirectWindow::Zoom(BPoint recPosition, float recWidth, float recHeight)
266 {
267 	inherited::Zoom(recPosition, recWidth, recHeight);
268 }
269 
270 
271 void
272 BDirectWindow::ScreenChanged(BRect screenFrame, color_space depth)
273 {
274 	inherited::ScreenChanged(screenFrame, depth);
275 }
276 
277 
278 void
279 BDirectWindow::MenusBeginning()
280 {
281 	inherited::MenusBeginning();
282 }
283 
284 
285 void
286 BDirectWindow::MenusEnded()
287 {
288 	inherited::MenusEnded();
289 }
290 
291 
292 void
293 BDirectWindow::WindowActivated(bool state)
294 {
295 	inherited::WindowActivated(state);
296 }
297 
298 
299 void
300 BDirectWindow::Show()
301 {
302 	inherited::Show();
303 }
304 
305 
306 void
307 BDirectWindow::Hide()
308 {
309 	inherited::Hide();
310 }
311 
312 
313 BHandler *
314 BDirectWindow::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 
321 status_t
322 BDirectWindow::GetSupportedSuites(BMessage *data)
323 {
324 	return inherited::GetSupportedSuites(data);
325 }
326 
327 
328 status_t
329 BDirectWindow::Perform(perform_code d, void *arg)
330 {
331 	return inherited::Perform(d, arg);
332 }
333 
334 
335 void
336 BDirectWindow::task_looper()
337 {
338 	inherited::task_looper();
339 }
340 
341 
342 BMessage *
343 BDirectWindow::ConvertToMessage(void *raw, int32 code)
344 {
345 	return inherited::ConvertToMessage(raw, code);
346 }
347 
348 
349 //	#pragma mark - BDirectWindow specific API
350 
351 
352 void
353 BDirectWindow::DirectConnected(direct_buffer_info *info)
354 {
355 	// implemented in subclasses
356 }
357 
358 
359 status_t
360 BDirectWindow::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 
411 status_t
412 BDirectWindow::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 
432 bool
433 BDirectWindow::IsFullScreen() const
434 {
435 	return fIsFullScreen;
436 }
437 
438 
439 /*static*/ bool
440 BDirectWindow::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
455 BDirectWindow::_daemon_thread(void *arg)
456 {
457 	return static_cast<BDirectWindow *>(arg)->_DirectDaemon();
458 }
459 
460 
461 int32
462 BDirectWindow::_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 
513 bool
514 BDirectWindow::_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 
542 void
543 BDirectWindow::_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 
556 void
557 BDirectWindow::_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 
609 void
610 BDirectWindow::_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 
639 void BDirectWindow::_ReservedDirectWindow1() {}
640 void BDirectWindow::_ReservedDirectWindow2() {}
641 void BDirectWindow::_ReservedDirectWindow3() {}
642 void BDirectWindow::_ReservedDirectWindow4() {}
643