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