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
print_direct_buffer_state(const direct_buffer_state & state)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
print_direct_driver_state(const direct_driver_state & state)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
print_direct_buffer_layout(const buffer_layout & layout)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
print_direct_buffer_orientation(const buffer_orientation & orientation)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
print_direct_buffer_info(const direct_buffer_info & info)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
BDirectWindow(BRect frame,const char * title,window_type type,uint32 flags,uint32 workspace)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
BDirectWindow(BRect frame,const char * title,window_look look,window_feel feel,uint32 flags,uint32 workspace)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
~BDirectWindow()185 BDirectWindow::~BDirectWindow()
186 {
187 _DisposeData();
188 }
189
190
191 // #pragma mark - BWindow API implementation
192
193
194 BArchivable*
Instantiate(BMessage * data)195 BDirectWindow::Instantiate(BMessage* data)
196 {
197 return NULL;
198 }
199
200
201 status_t
Archive(BMessage * data,bool deep) const202 BDirectWindow::Archive(BMessage* data, bool deep) const
203 {
204 return inherited::Archive(data, deep);
205 }
206
207
208 void
Quit()209 BDirectWindow::Quit()
210 {
211 inherited::Quit();
212 }
213
214
215 void
DispatchMessage(BMessage * message,BHandler * handler)216 BDirectWindow::DispatchMessage(BMessage* message, BHandler* handler)
217 {
218 inherited::DispatchMessage(message, handler);
219 }
220
221
222 void
MessageReceived(BMessage * message)223 BDirectWindow::MessageReceived(BMessage* message)
224 {
225 inherited::MessageReceived(message);
226 }
227
228
229 void
FrameMoved(BPoint newPosition)230 BDirectWindow::FrameMoved(BPoint newPosition)
231 {
232 inherited::FrameMoved(newPosition);
233 }
234
235
236 void
WorkspacesChanged(uint32 oldWorkspaces,uint32 newWorkspaces)237 BDirectWindow::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
238 {
239 inherited::WorkspacesChanged(oldWorkspaces, newWorkspaces);
240 }
241
242
243 void
WorkspaceActivated(int32 index,bool state)244 BDirectWindow::WorkspaceActivated(int32 index, bool state)
245 {
246 inherited::WorkspaceActivated(index, state);
247 }
248
249
250 void
FrameResized(float newWidth,float newHeight)251 BDirectWindow::FrameResized(float newWidth, float newHeight)
252 {
253 inherited::FrameResized(newWidth, newHeight);
254 }
255
256
257 void
Minimize(bool minimize)258 BDirectWindow::Minimize(bool minimize)
259 {
260 inherited::Minimize(minimize);
261 }
262
263
264 void
Zoom(BPoint recPosition,float recWidth,float recHeight)265 BDirectWindow::Zoom(BPoint recPosition, float recWidth, float recHeight)
266 {
267 inherited::Zoom(recPosition, recWidth, recHeight);
268 }
269
270
271 void
ScreenChanged(BRect screenFrame,color_space depth)272 BDirectWindow::ScreenChanged(BRect screenFrame, color_space depth)
273 {
274 inherited::ScreenChanged(screenFrame, depth);
275 }
276
277
278 void
MenusBeginning()279 BDirectWindow::MenusBeginning()
280 {
281 inherited::MenusBeginning();
282 }
283
284
285 void
MenusEnded()286 BDirectWindow::MenusEnded()
287 {
288 inherited::MenusEnded();
289 }
290
291
292 void
WindowActivated(bool state)293 BDirectWindow::WindowActivated(bool state)
294 {
295 inherited::WindowActivated(state);
296 }
297
298
299 void
Show()300 BDirectWindow::Show()
301 {
302 inherited::Show();
303 }
304
305
306 void
Hide()307 BDirectWindow::Hide()
308 {
309 inherited::Hide();
310 }
311
312
313 BHandler*
ResolveSpecifier(BMessage * message,int32 index,BMessage * specifier,int32 what,const char * property)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
GetSupportedSuites(BMessage * data)323 BDirectWindow::GetSupportedSuites(BMessage* data)
324 {
325 return inherited::GetSupportedSuites(data);
326 }
327
328
329 status_t
Perform(perform_code d,void * arg)330 BDirectWindow::Perform(perform_code d, void* arg)
331 {
332 return inherited::Perform(d, arg);
333 }
334
335
336 void
task_looper()337 BDirectWindow::task_looper()
338 {
339 inherited::task_looper();
340 }
341
342
343 BMessage*
ConvertToMessage(void * raw,int32 code)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
DirectConnected(direct_buffer_info * info)354 BDirectWindow::DirectConnected(direct_buffer_info* info)
355 {
356 // implemented in subclasses
357 }
358
359
360 status_t
GetClippingRegion(BRegion * region,BPoint * origin) const361 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
SetFullScreen(bool enable)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
IsFullScreen() const434 BDirectWindow::IsFullScreen() const
435 {
436 return fIsFullScreen;
437 }
438
439
440 /*static*/ bool
SupportsWindowMode(screen_id id)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
_daemon_thread(void * arg)456 BDirectWindow::_daemon_thread(void* arg)
457 {
458 return static_cast<BDirectWindow*>(arg)->_DirectDaemon();
459 }
460
461
462 int32
_DirectDaemon()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
_LockDirect() const515 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
_UnlockDirect() const544 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
_InitData()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
_DisposeData()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
_ReservedDirectWindow1()640 void BDirectWindow::_ReservedDirectWindow1() {}
_ReservedDirectWindow2()641 void BDirectWindow::_ReservedDirectWindow2() {}
_ReservedDirectWindow3()642 void BDirectWindow::_ReservedDirectWindow3() {}
_ReservedDirectWindow4()643 void BDirectWindow::_ReservedDirectWindow4() {}
644