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