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