1 /*
2 * Copyright 2002-2009, Haiku Inc.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Stefano Ceccherini (burton666@libero.it)
7 * Axel Dörfler, axeld@pinc-software.de
8 */
9
10
11 /*! BPrivateScreen is the class which does the real work for
12 the proxy class BScreen (it interacts with the app_server).
13 */
14
15
16 #include <PrivateScreen.h>
17
18 #include <new>
19 #include <pthread.h>
20 #include <stdlib.h>
21
22 #include <Application.h>
23 #include <Autolock.h>
24 #include <Bitmap.h>
25 #include <Locker.h>
26 #include <ObjectList.h>
27 #include <Window.h>
28
29 #include <AutoLocker.h>
30
31 #include <AppMisc.h>
32 #include <AppServerLink.h>
33 #include <ServerProtocol.h>
34
35
36 using namespace BPrivate;
37
38
39 namespace {
40
41 struct Screens {
42 BObjectList<BPrivateScreen> list;
43
Screens__anon3c02a1cd0111::Screens44 Screens()
45 :
46 list(2, true),
47 fLock("screen list")
48 {
49 }
50
Lock__anon3c02a1cd0111::Screens51 bool Lock()
52 {
53 return fLock.Lock();
54 }
55
Unlock__anon3c02a1cd0111::Screens56 void Unlock()
57 {
58 fLock.Unlock();
59 }
60
Default__anon3c02a1cd0111::Screens61 static Screens* Default()
62 {
63 if (sDefaultInstance == NULL)
64 pthread_once(&sDefaultInitOnce, &_InitSingleton);
65
66 return sDefaultInstance;
67 }
68
69 private:
_InitSingleton__anon3c02a1cd0111::Screens70 static void _InitSingleton()
71 {
72 sDefaultInstance = new Screens;
73 }
74
75 private:
76 BLocker fLock;
77
78 static pthread_once_t sDefaultInitOnce;
79 static Screens* sDefaultInstance;
80 };
81
82 pthread_once_t Screens::sDefaultInitOnce = PTHREAD_ONCE_INIT;
83 Screens* Screens::sDefaultInstance = NULL;
84
85 } // unnamed namespace
86
87
88 BPrivateScreen*
Get(BWindow * window)89 BPrivateScreen::Get(BWindow* window)
90 {
91 int32 id = B_MAIN_SCREEN_ID.id;
92
93 if (window != NULL) {
94 BPrivate::AppServerLink link;
95 link.StartMessage(AS_GET_SCREEN_ID_FROM_WINDOW);
96 link.Attach<int32>(_get_object_token_(window));
97
98 status_t status;
99 if (link.FlushWithReply(status) == B_OK && status == B_OK)
100 link.Read<int32>(&id);
101 }
102
103 return _Get(id, false);
104 }
105
106
107 BPrivateScreen*
Get(int32 id)108 BPrivateScreen::Get(int32 id)
109 {
110 return _Get(id, true);
111 }
112
113
114 BPrivateScreen*
_Get(int32 id,bool check)115 BPrivateScreen::_Get(int32 id, bool check)
116 {
117 // Nothing works without an app_server connection
118 if (be_app == NULL)
119 return NULL;
120
121 Screens* screens = Screens::Default();
122 AutoLocker<Screens> locker(screens);
123
124 // search for the screen ID
125
126 for (int32 i = screens->list.CountItems(); i-- > 0;) {
127 BPrivateScreen* screen = screens->list.ItemAt(i);
128
129 if (screen->ID() == id) {
130 screen->_Acquire();
131 return screen;
132 }
133 }
134
135 if (check) {
136 // check if ID is valid
137 if (!_IsValid(id))
138 return NULL;
139 }
140
141 // we need to allocate a new one
142
143 BPrivateScreen* screen = new (std::nothrow) BPrivateScreen(id);
144 if (screen == NULL)
145 return NULL;
146
147 screens->list.AddItem(screen);
148 return screen;
149 }
150
151
152 void
Put(BPrivateScreen * screen)153 BPrivateScreen::Put(BPrivateScreen* screen)
154 {
155 if (screen == NULL)
156 return;
157
158 Screens* screens = Screens::Default();
159 AutoLocker<Screens> locker(screens);
160
161 if (screen->_Release()) {
162 if (screen->ID() != B_MAIN_SCREEN_ID.id) {
163 // we always keep the main screen object around - it will
164 // never go away, even if you disconnect all monitors.
165 screens->list.RemoveItem(screen);
166 }
167 }
168 }
169
170
171 BPrivateScreen*
GetNext(BPrivateScreen * screen)172 BPrivateScreen::GetNext(BPrivateScreen* screen)
173 {
174 Screens* screens = Screens::Default();
175 AutoLocker<Screens> locker(screens);
176
177 int32 id;
178 status_t status = screen->GetNextID(id);
179 if (status != B_OK)
180 return NULL;
181
182 BPrivateScreen* nextScreen = Get(id);
183 if (nextScreen == NULL)
184 return NULL;
185
186 Put(screen);
187 return nextScreen;
188 }
189
190
191 bool
_IsValid(int32 id)192 BPrivateScreen::_IsValid(int32 id)
193 {
194 BPrivate::AppServerLink link;
195 link.StartMessage(AS_VALID_SCREEN_ID);
196 link.Attach<int32>(id);
197
198 status_t status;
199 if (link.FlushWithReply(status) != B_OK || status < B_OK)
200 return false;
201
202 return true;
203 }
204
205
206 // #pragma mark -
207
208
209 color_space
ColorSpace()210 BPrivateScreen::ColorSpace()
211 {
212 display_mode mode;
213 if (GetMode(B_CURRENT_WORKSPACE_INDEX, &mode) == B_OK)
214 return (color_space)mode.space;
215
216 return B_NO_COLOR_SPACE;
217 }
218
219
220 BRect
Frame()221 BPrivateScreen::Frame()
222 {
223 if (system_time() > fLastUpdate + 10000) {
224 // invalidate the settings after 10 msecs
225 BPrivate::AppServerLink link;
226 link.StartMessage(AS_GET_SCREEN_FRAME);
227 link.Attach<int32>(ID());
228 link.Attach<uint32>(B_CURRENT_WORKSPACE_INDEX);
229
230 status_t status = B_ERROR;
231 if (link.FlushWithReply(status) == B_OK && status == B_OK) {
232 link.Read<BRect>(&fFrame);
233 fLastUpdate = system_time();
234 }
235 }
236
237 return fFrame;
238 }
239
240
241 bool
IsValid() const242 BPrivateScreen::IsValid() const
243 {
244 return BPrivateScreen::_IsValid(ID());
245 }
246
247
248 status_t
GetNextID(int32 & id)249 BPrivateScreen::GetNextID(int32& id)
250 {
251 BPrivate::AppServerLink link;
252 link.StartMessage(AS_GET_NEXT_SCREEN_ID);
253 link.Attach<int32>(ID());
254
255 status_t status;
256 if (link.FlushWithReply(status) == B_OK && status == B_OK) {
257 link.Read<int32>(&id);
258 return B_OK;
259 }
260
261 return status;
262 }
263
264
265 status_t
WaitForRetrace(bigtime_t timeout)266 BPrivateScreen::WaitForRetrace(bigtime_t timeout)
267 {
268 // Get the retrace semaphore if it's the first time
269 // we are called. Cache the value then.
270 if (!fRetraceSemValid)
271 fRetraceSem = _RetraceSemaphore();
272
273 if (fRetraceSem < 0) {
274 // syncing to retrace is not supported by the accelerant
275 return fRetraceSem;
276 }
277
278 status_t status;
279 do {
280 status = acquire_sem_etc(fRetraceSem, 1, B_RELATIVE_TIMEOUT, timeout);
281 } while (status == B_INTERRUPTED);
282
283 return status;
284 }
285
286
287 uint8
IndexForColor(uint8 red,uint8 green,uint8 blue,uint8 alpha)288 BPrivateScreen::IndexForColor(uint8 red, uint8 green, uint8 blue, uint8 alpha)
289 {
290 // Looks like this check is necessary
291 if (red == B_TRANSPARENT_COLOR.red
292 && green == B_TRANSPARENT_COLOR.green
293 && blue == B_TRANSPARENT_COLOR.blue
294 && alpha == B_TRANSPARENT_COLOR.alpha)
295 return B_TRANSPARENT_8_BIT;
296
297 uint16 index = ((red & 0xf8) << 7) | ((green & 0xf8) << 2) | (blue >> 3);
298 if (ColorMap())
299 return fColorMap->index_map[index];
300
301 return 0;
302 }
303
304
305 rgb_color
ColorForIndex(const uint8 index)306 BPrivateScreen::ColorForIndex(const uint8 index)
307 {
308 if (ColorMap())
309 return fColorMap->color_list[index];
310
311 return rgb_color();
312 }
313
314
315 uint8
InvertIndex(uint8 index)316 BPrivateScreen::InvertIndex(uint8 index)
317 {
318 if (ColorMap())
319 return fColorMap->inversion_map[index];
320
321 return 0;
322 }
323
324
325 const color_map*
ColorMap()326 BPrivateScreen::ColorMap()
327 {
328 if (fColorMap == NULL) {
329 Screens* screens = Screens::Default();
330 AutoLocker<Screens> locker(screens);
331
332 if (fColorMap != NULL) {
333 // someone could have been faster than us
334 return fColorMap;
335 }
336
337 // TODO: BeOS R5 here gets the colormap pointer
338 // (with BApplication::ro_offset_to_ptr() ?)
339 // which is contained in a shared area created by the server.
340 BPrivate::AppServerLink link;
341 link.StartMessage(AS_SCREEN_GET_COLORMAP);
342 link.Attach<int32>(ID());
343
344 status_t status;
345 if (link.FlushWithReply(status) == B_OK && status == B_OK) {
346 fColorMap = (color_map*)malloc(sizeof(color_map));
347 fOwnsColorMap = true;
348 link.Read<color_map>(fColorMap);
349 }
350 }
351
352 return fColorMap;
353 }
354
355
356 status_t
GetBitmap(BBitmap ** _bitmap,bool drawCursor,BRect * bounds)357 BPrivateScreen::GetBitmap(BBitmap**_bitmap, bool drawCursor, BRect* bounds)
358 {
359 if (_bitmap == NULL)
360 return B_BAD_VALUE;
361
362 BRect rect;
363 if (bounds != NULL)
364 rect = *bounds;
365 else
366 rect = Frame();
367
368 BBitmap* bitmap = new (std::nothrow) BBitmap(rect, ColorSpace());
369 if (bitmap == NULL)
370 return B_NO_MEMORY;
371
372 status_t status = bitmap->InitCheck();
373 if (status == B_OK)
374 status = ReadBitmap(bitmap, drawCursor, &rect);
375 if (status != B_OK) {
376 delete bitmap;
377 return status;
378 }
379
380 *_bitmap = bitmap;
381 return B_OK;
382 }
383
384
385 status_t
ReadBitmap(BBitmap * bitmap,bool drawCursor,BRect * bounds)386 BPrivateScreen::ReadBitmap(BBitmap* bitmap, bool drawCursor, BRect* bounds)
387 {
388 if (bitmap == NULL)
389 return B_BAD_VALUE;
390
391 BRect rect;
392 if (bounds != NULL)
393 rect = *bounds;
394 else
395 rect = Frame();
396
397 BPrivate::AppServerLink link;
398 link.StartMessage(AS_READ_BITMAP);
399 link.Attach<int32>(bitmap->_ServerToken());
400 link.Attach<bool>(drawCursor);
401 link.Attach<BRect>(rect);
402
403 status_t status = B_ERROR;
404 if (link.FlushWithReply(status) < B_OK || status != B_OK)
405 return status;
406
407 return B_OK;
408 }
409
410
411 rgb_color
DesktopColor(uint32 workspace)412 BPrivateScreen::DesktopColor(uint32 workspace)
413 {
414 rgb_color color = { 51, 102, 152, 255 };
415 BPrivate::AppServerLink link;
416
417 link.StartMessage(AS_GET_DESKTOP_COLOR);
418 link.Attach<uint32>(workspace);
419
420 int32 code;
421 if (link.FlushWithReply(code) == B_OK
422 && code == B_OK)
423 link.Read<rgb_color>(&color);
424
425 return color;
426 }
427
428
429 void
SetDesktopColor(rgb_color color,uint32 workspace,bool makeDefault)430 BPrivateScreen::SetDesktopColor(rgb_color color, uint32 workspace,
431 bool makeDefault)
432 {
433 BPrivate::AppServerLink link;
434
435 link.StartMessage(AS_SET_DESKTOP_COLOR);
436 link.Attach<rgb_color>(color);
437 link.Attach<uint32>(workspace);
438 link.Attach<bool>(makeDefault);
439 link.Flush();
440 }
441
442
443 status_t
ProposeMode(display_mode * target,const display_mode * low,const display_mode * high)444 BPrivateScreen::ProposeMode(display_mode* target,
445 const display_mode* low, const display_mode* high)
446 {
447 // We can't return B_BAD_VALUE here, because it's used to indicate
448 // that the mode returned is supported, but it doesn't fall
449 // within the limit (see ProposeMode() documentation)
450 if (target == NULL || low == NULL || high == NULL)
451 return B_ERROR;
452
453 BPrivate::AppServerLink link;
454 link.StartMessage(AS_PROPOSE_MODE);
455 link.Attach<int32>(ID());
456 link.Attach<display_mode>(*target);
457 link.Attach<display_mode>(*low);
458 link.Attach<display_mode>(*high);
459
460 status_t status = B_ERROR;
461 if (link.FlushWithReply(status) == B_OK && status == B_OK) {
462 link.Read<display_mode>(target);
463
464 bool withinLimits;
465 link.Read<bool>(&withinLimits);
466 if (!withinLimits)
467 status = B_BAD_VALUE;
468 }
469
470 return status;
471 }
472
473
474 status_t
GetModeList(display_mode ** _modeList,uint32 * _count)475 BPrivateScreen::GetModeList(display_mode** _modeList, uint32* _count)
476 {
477 if (_modeList == NULL || _count == NULL)
478 return B_BAD_VALUE;
479
480 BPrivate::AppServerLink link;
481 link.StartMessage(AS_GET_MODE_LIST);
482 link.Attach<int32>(ID());
483
484 status_t status = B_ERROR;
485 if (link.FlushWithReply(status) == B_OK && status == B_OK) {
486 uint32 count;
487 if (link.Read<uint32>(&count) < B_OK)
488 return B_ERROR;
489
490 // TODO: this could get too big for the link
491 int32 size = count * sizeof(display_mode);
492 display_mode* modeList = (display_mode *)malloc(size);
493 if (modeList == NULL)
494 return B_NO_MEMORY;
495
496 if (link.Read(modeList, size) < B_OK) {
497 free(modeList);
498 return B_ERROR;
499 }
500
501 *_modeList = modeList;
502 *_count = count;
503 }
504
505 return status;
506 }
507
508
509 status_t
GetMode(uint32 workspace,display_mode * mode)510 BPrivateScreen::GetMode(uint32 workspace, display_mode *mode)
511 {
512 if (mode == NULL)
513 return B_BAD_VALUE;
514
515 BPrivate::AppServerLink link;
516 link.StartMessage(AS_SCREEN_GET_MODE);
517 link.Attach<int32>(ID());
518 link.Attach<uint32>(workspace);
519
520 status_t status = B_ERROR;
521 if (link.FlushWithReply(status) != B_OK
522 || status != B_OK)
523 return status;
524
525 link.Read<display_mode>(mode);
526 return B_OK;
527 }
528
529
530 status_t
SetMode(uint32 workspace,display_mode * mode,bool makeDefault)531 BPrivateScreen::SetMode(uint32 workspace, display_mode *mode, bool makeDefault)
532 {
533 if (mode == NULL)
534 return B_BAD_VALUE;
535
536 BPrivate::AppServerLink link;
537 link.StartMessage(AS_SCREEN_SET_MODE);
538 link.Attach<int32>(ID());
539 link.Attach<uint32>(workspace);
540 link.Attach<display_mode>(*mode);
541 link.Attach<bool>(makeDefault);
542
543 status_t status = B_ERROR;
544 link.FlushWithReply(status);
545
546 return status;
547 }
548
549
550 status_t
GetDeviceInfo(accelerant_device_info * info)551 BPrivateScreen::GetDeviceInfo(accelerant_device_info *info)
552 {
553 if (info == NULL)
554 return B_BAD_VALUE;
555
556 BPrivate::AppServerLink link;
557 link.StartMessage(AS_GET_ACCELERANT_INFO);
558 link.Attach<int32>(ID());
559
560 status_t status = B_ERROR;
561 if (link.FlushWithReply(status) == B_OK && status == B_OK) {
562 link.Read<accelerant_device_info>(info);
563 return B_OK;
564 }
565
566 return status;
567 }
568
569
570 status_t
GetMonitorInfo(monitor_info * info)571 BPrivateScreen::GetMonitorInfo(monitor_info* info)
572 {
573 if (info == NULL)
574 return B_BAD_VALUE;
575
576 BPrivate::AppServerLink link;
577 link.StartMessage(AS_GET_MONITOR_INFO);
578 link.Attach<int32>(ID());
579
580 status_t status = B_ERROR;
581 if (link.FlushWithReply(status) == B_OK && status == B_OK) {
582 link.Read<monitor_info>(info);
583 return B_OK;
584 }
585
586 return status;
587 }
588
589
590 status_t
GetPixelClockLimits(display_mode * mode,uint32 * low,uint32 * high)591 BPrivateScreen::GetPixelClockLimits(display_mode *mode, uint32 *low, uint32 *high)
592 {
593 if (mode == NULL || low == NULL || high == NULL)
594 return B_BAD_VALUE;
595
596 BPrivate::AppServerLink link;
597 link.StartMessage(AS_GET_PIXEL_CLOCK_LIMITS);
598 link.Attach<int32>(ID());
599 link.Attach<display_mode>(*mode);
600
601 status_t status;
602 if (link.FlushWithReply(status) == B_OK && status == B_OK) {
603 link.Read<uint32>(low);
604 link.Read<uint32>(high);
605 return B_OK;
606 }
607
608 return status;
609 }
610
611
612 status_t
GetTimingConstraints(display_timing_constraints * constraints)613 BPrivateScreen::GetTimingConstraints(display_timing_constraints *constraints)
614 {
615 if (constraints == NULL)
616 return B_BAD_VALUE;
617
618 BPrivate::AppServerLink link;
619 link.StartMessage(AS_GET_TIMING_CONSTRAINTS);
620 link.Attach<int32>(ID());
621
622 status_t status = B_ERROR;
623 if (link.FlushWithReply(status) == B_OK && status == B_OK) {
624 link.Read<display_timing_constraints>(constraints);
625 return B_OK;
626 }
627
628 return status;
629 }
630
631
632 status_t
SetDPMS(uint32 dpmsState)633 BPrivateScreen::SetDPMS(uint32 dpmsState)
634 {
635 BPrivate::AppServerLink link;
636 link.StartMessage(AS_SET_DPMS);
637 link.Attach<int32>(ID());
638 link.Attach<uint32>(dpmsState);
639
640 status_t status = B_ERROR;
641 link.FlushWithReply(status);
642
643 return status;
644 }
645
646
647 uint32
DPMSState()648 BPrivateScreen::DPMSState()
649 {
650 uint32 state = 0;
651
652 BPrivate::AppServerLink link;
653 link.StartMessage(AS_GET_DPMS_STATE);
654 link.Attach<int32>(ID());
655
656 status_t status;
657 if (link.FlushWithReply(status) == B_OK && status == B_OK)
658 link.Read<uint32>(&state);
659
660 return state;
661 }
662
663
664 uint32
DPMSCapabilites()665 BPrivateScreen::DPMSCapabilites()
666 {
667 uint32 capabilities = 0;
668
669 BPrivate::AppServerLink link;
670 link.StartMessage(AS_GET_DPMS_CAPABILITIES);
671 link.Attach<int32>(ID());
672
673 status_t status;
674 if (link.FlushWithReply(status) == B_OK && status == B_OK)
675 link.Read<uint32>(&capabilities);
676
677 return capabilities;
678 }
679
680
681 status_t
GetBrightness(float * brightness)682 BPrivateScreen::GetBrightness(float* brightness)
683 {
684 if (brightness == NULL)
685 return B_BAD_VALUE;
686
687 BPrivate::AppServerLink link;
688 link.StartMessage(AS_SCREEN_GET_BRIGHTNESS);
689 link.Attach<int32>(ID());
690
691 status_t status;
692 if (link.FlushWithReply(status) == B_OK && status == B_OK)
693 link.Read<float>(brightness);
694
695 return status;
696 }
697
698
699 status_t
SetBrightness(float brightness)700 BPrivateScreen::SetBrightness(float brightness)
701 {
702 BPrivate::AppServerLink link;
703 link.StartMessage(AS_SCREEN_SET_BRIGHTNESS);
704 link.Attach<int32>(ID());
705 link.Attach<float>(brightness);
706
707 status_t status = B_ERROR;
708 link.FlushWithReply(status);
709
710 return status;
711 }
712
713
714 void *
BaseAddress()715 BPrivateScreen::BaseAddress()
716 {
717 frame_buffer_config config;
718 if (_GetFrameBufferConfig(config) != B_OK)
719 return NULL;
720
721 return config.frame_buffer;
722 }
723
724
725 uint32
BytesPerRow()726 BPrivateScreen::BytesPerRow()
727 {
728 frame_buffer_config config;
729 if (_GetFrameBufferConfig(config) != B_OK)
730 return 0;
731
732 return config.bytes_per_row;
733 }
734
735
736 // #pragma mark - private methods
737
738
739 void
_Acquire()740 BPrivateScreen::_Acquire()
741 {
742 fReferenceCount++;
743
744 fLastUpdate = 0;
745 // force an update for the new BScreen object
746 }
747
748
749 bool
_Release()750 BPrivateScreen::_Release()
751 {
752 return --fReferenceCount == 0;
753 }
754
755
756 sem_id
_RetraceSemaphore()757 BPrivateScreen::_RetraceSemaphore()
758 {
759 BPrivate::AppServerLink link;
760 link.StartMessage(AS_GET_RETRACE_SEMAPHORE);
761 link.Attach<int32>(ID());
762
763 sem_id id = B_BAD_SEM_ID;
764 status_t status = B_ERROR;
765 if (link.FlushWithReply(status) == B_OK && status == B_OK) {
766 link.Read<sem_id>(&id);
767 fRetraceSemValid = true;
768 }
769
770 return id;
771 }
772
773
774 status_t
_GetFrameBufferConfig(frame_buffer_config & config)775 BPrivateScreen::_GetFrameBufferConfig(frame_buffer_config& config)
776 {
777 BPrivate::AppServerLink link;
778 link.StartMessage(AS_GET_FRAME_BUFFER_CONFIG);
779 link.Attach<int32>(ID());
780
781 status_t status = B_ERROR;
782 if (link.FlushWithReply(status) == B_OK && status == B_OK) {
783 link.Read<frame_buffer_config>(&config);
784 return B_OK;
785 }
786
787 return status;
788 }
789
790
BPrivateScreen(int32 id)791 BPrivateScreen::BPrivateScreen(int32 id)
792 :
793 fID(id),
794 fReferenceCount(0),
795 fColorMap(NULL),
796 fRetraceSem(-1),
797 fRetraceSemValid(false),
798 fOwnsColorMap(false),
799 fFrame(0, 0, 0, 0),
800 fLastUpdate(0)
801 {
802 }
803
804
~BPrivateScreen()805 BPrivateScreen::~BPrivateScreen()
806 {
807 if (fOwnsColorMap)
808 free(fColorMap);
809 }
810