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