xref: /haiku/src/kits/game/DirectWindow.cpp (revision 17889a8c70dbb3d59c1412f6431968753c767bab)
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 
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* 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 
322 status_t
323 BDirectWindow::GetSupportedSuites(BMessage* data)
324 {
325 	return inherited::GetSupportedSuites(data);
326 }
327 
328 
329 status_t
330 BDirectWindow::Perform(perform_code d, void* arg)
331 {
332 	return inherited::Perform(d, arg);
333 }
334 
335 
336 void
337 BDirectWindow::task_looper()
338 {
339 	inherited::task_looper();
340 }
341 
342 
343 BMessage*
344 BDirectWindow::ConvertToMessage(void* raw, int32 code)
345 {
346 	return inherited::ConvertToMessage(raw, code);
347 }
348 
349 
350 //	#pragma mark - BDirectWindow specific API
351 
352 
353 void
354 BDirectWindow::DirectConnected(direct_buffer_info* info)
355 {
356 	// implemented in subclasses
357 }
358 
359 
360 status_t
361 BDirectWindow::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 
412 status_t
413 BDirectWindow::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 
433 bool
434 BDirectWindow::IsFullScreen() const
435 {
436 	return fIsFullScreen;
437 }
438 
439 
440 /*static*/ bool
441 BDirectWindow::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
456 BDirectWindow::_daemon_thread(void* arg)
457 {
458 	return static_cast<BDirectWindow*>(arg)->_DirectDaemon();
459 }
460 
461 
462 int32
463 BDirectWindow::_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 
514 bool
515 BDirectWindow::_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 
543 void
544 BDirectWindow::_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 
557 void
558 BDirectWindow::_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 
610 void
611 BDirectWindow::_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 
640 void BDirectWindow::_ReservedDirectWindow1() {}
641 void BDirectWindow::_ReservedDirectWindow2() {}
642 void BDirectWindow::_ReservedDirectWindow3() {}
643 void BDirectWindow::_ReservedDirectWindow4() {}
644