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