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
36using namespace BPrivate;
37
38
39namespace {
40
41struct Screens {
42	BObjectList<BPrivateScreen>	list;
43
44	Screens()
45		:
46		list(2, true),
47		fLock("screen list")
48	{
49	}
50
51	bool Lock()
52	{
53		return fLock.Lock();
54	}
55
56	void Unlock()
57	{
58		fLock.Unlock();
59	}
60
61	static Screens* Default()
62	{
63		if (sDefaultInstance == NULL)
64			pthread_once(&sDefaultInitOnce, &_InitSingleton);
65
66		return sDefaultInstance;
67	}
68
69private:
70	static void _InitSingleton()
71	{
72		sDefaultInstance = new Screens;
73	}
74
75private:
76	BLocker					fLock;
77
78	static pthread_once_t	sDefaultInitOnce;
79	static Screens*			sDefaultInstance;
80};
81
82pthread_once_t Screens::sDefaultInitOnce = PTHREAD_ONCE_INIT;
83Screens* Screens::sDefaultInstance = NULL;
84
85}	// unnamed namespace
86
87
88BPrivateScreen*
89BPrivateScreen::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
107BPrivateScreen*
108BPrivateScreen::Get(int32 id)
109{
110	return _Get(id, true);
111}
112
113
114BPrivateScreen*
115BPrivateScreen::_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
152void
153BPrivateScreen::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
171BPrivateScreen*
172BPrivateScreen::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
191bool
192BPrivateScreen::_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
209color_space
210BPrivateScreen::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
220BRect
221BPrivateScreen::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
241bool
242BPrivateScreen::IsValid() const
243{
244	return BPrivateScreen::_IsValid(ID());
245}
246
247
248status_t
249BPrivateScreen::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
265status_t
266BPrivateScreen::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
287uint8
288BPrivateScreen::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
305rgb_color
306BPrivateScreen::ColorForIndex(const uint8 index)
307{
308	if (ColorMap())
309		return fColorMap->color_list[index];
310
311	return rgb_color();
312}
313
314
315uint8
316BPrivateScreen::InvertIndex(uint8 index)
317{
318	if (ColorMap())
319		return fColorMap->inversion_map[index];
320
321	return 0;
322}
323
324
325const color_map*
326BPrivateScreen::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
356status_t
357BPrivateScreen::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
385status_t
386BPrivateScreen::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
411rgb_color
412BPrivateScreen::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
429void
430BPrivateScreen::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
443status_t
444BPrivateScreen::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
474status_t
475BPrivateScreen::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
509status_t
510BPrivateScreen::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
530status_t
531BPrivateScreen::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
550status_t
551BPrivateScreen::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
570status_t
571BPrivateScreen::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
590status_t
591BPrivateScreen::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
612status_t
613BPrivateScreen::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
632status_t
633BPrivateScreen::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
647uint32
648BPrivateScreen::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
664uint32
665BPrivateScreen::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
681void *
682BPrivateScreen::BaseAddress()
683{
684	frame_buffer_config config;
685	if (_GetFrameBufferConfig(config) != B_OK)
686		return NULL;
687
688	return config.frame_buffer;
689}
690
691
692uint32
693BPrivateScreen::BytesPerRow()
694{
695	frame_buffer_config config;
696	if (_GetFrameBufferConfig(config) != B_OK)
697		return 0;
698
699	return config.bytes_per_row;
700}
701
702
703// #pragma mark - private methods
704
705
706void
707BPrivateScreen::_Acquire()
708{
709	fReferenceCount++;
710
711	fLastUpdate = 0;
712		// force an update for the new BScreen object
713}
714
715
716bool
717BPrivateScreen::_Release()
718{
719	return --fReferenceCount == 0;
720}
721
722
723sem_id
724BPrivateScreen::_RetraceSemaphore()
725{
726	BPrivate::AppServerLink link;
727	link.StartMessage(AS_GET_RETRACE_SEMAPHORE);
728	link.Attach<int32>(ID());
729
730	sem_id id = B_BAD_SEM_ID;
731	status_t status = B_ERROR;
732	if (link.FlushWithReply(status) == B_OK && status == B_OK) {
733		link.Read<sem_id>(&id);
734		fRetraceSemValid = true;
735	}
736
737	return id;
738}
739
740
741status_t
742BPrivateScreen::_GetFrameBufferConfig(frame_buffer_config& config)
743{
744	BPrivate::AppServerLink link;
745	link.StartMessage(AS_GET_FRAME_BUFFER_CONFIG);
746	link.Attach<int32>(ID());
747
748	status_t status = B_ERROR;
749	if (link.FlushWithReply(status) == B_OK && status == B_OK) {
750		link.Read<frame_buffer_config>(&config);
751		return B_OK;
752	}
753
754	return status;
755}
756
757
758BPrivateScreen::BPrivateScreen(int32 id)
759	:
760	fID(id),
761	fReferenceCount(0),
762	fColorMap(NULL),
763	fRetraceSem(-1),
764	fRetraceSemValid(false),
765	fOwnsColorMap(false),
766	fFrame(0, 0, 0, 0),
767	fLastUpdate(0)
768{
769}
770
771
772BPrivateScreen::~BPrivateScreen()
773{
774	if (fOwnsColorMap)
775		free(fColorMap);
776}
777