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