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