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