rroutput.c revision 4642e01f
1/*
2 * Copyright © 2006 Keith Packard
3 * Copyright © 2008 Red Hat, Inc.
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that copyright
8 * notice and this permission notice appear in supporting documentation, and
9 * that the name of the copyright holders not be used in advertising or
10 * publicity pertaining to distribution of the software without specific,
11 * written prior permission.  The copyright holders make no representations
12 * about the suitability of this software for any purpose.  It is provided "as
13 * is" without express or implied warranty.
14 *
15 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21 * OF THIS SOFTWARE.
22 */
23
24#include "randrstr.h"
25#include "registry.h"
26
27RESTYPE	RROutputType;
28
29/*
30 * Notify the output of some change
31 */
32void
33RROutputChanged (RROutputPtr output, Bool configChanged)
34{
35    ScreenPtr	pScreen = output->pScreen;
36
37    output->changed = TRUE;
38    if (pScreen)
39    {
40	rrScrPriv (pScreen);
41	pScrPriv->changed = TRUE;
42	if (configChanged)
43	    pScrPriv->configChanged = TRUE;
44    }
45}
46
47/*
48 * Create an output
49 */
50
51RROutputPtr
52RROutputCreate (ScreenPtr   pScreen,
53		const char  *name,
54		int	    nameLength,
55		void	    *devPrivate)
56{
57    RROutputPtr	    output;
58    RROutputPtr	    *outputs;
59    rrScrPrivPtr    pScrPriv;
60
61    if (!RRInit())
62	return NULL;
63
64    pScrPriv = rrGetScrPriv(pScreen);
65
66    if (pScrPriv->numOutputs)
67	outputs = xrealloc (pScrPriv->outputs,
68			    (pScrPriv->numOutputs + 1) * sizeof (RROutputPtr));
69    else
70	outputs = xalloc (sizeof (RROutputPtr));
71    if (!outputs)
72	return FALSE;
73
74    pScrPriv->outputs = outputs;
75
76    output = xalloc (sizeof (RROutputRec) + nameLength + 1);
77    if (!output)
78	return NULL;
79    output->id = FakeClientID (0);
80    output->pScreen = pScreen;
81    output->name = (char *) (output + 1);
82    output->nameLength = nameLength;
83    memcpy (output->name, name, nameLength);
84    output->name[nameLength] = '\0';
85    output->connection = RR_UnknownConnection;
86    output->subpixelOrder = SubPixelUnknown;
87    output->mmWidth = 0;
88    output->mmHeight = 0;
89    output->crtc = NULL;
90    output->numCrtcs = 0;
91    output->crtcs = NULL;
92    output->numClones = 0;
93    output->clones = NULL;
94    output->numModes = 0;
95    output->numPreferred = 0;
96    output->modes = NULL;
97    output->numUserModes = 0;
98    output->userModes = NULL;
99    output->properties = NULL;
100    output->pendingProperties = FALSE;
101    output->changed = FALSE;
102    output->devPrivate = devPrivate;
103
104    if (!AddResource (output->id, RROutputType, (pointer) output))
105	return NULL;
106
107    pScrPriv->outputs[pScrPriv->numOutputs++] = output;
108    return output;
109}
110
111/*
112 * Notify extension that output parameters have been changed
113 */
114Bool
115RROutputSetClones (RROutputPtr  output,
116		   RROutputPtr  *clones,
117		   int		numClones)
118{
119    RROutputPtr	*newClones;
120    int		i;
121
122    if (numClones == output->numClones)
123    {
124	for (i = 0; i < numClones; i++)
125	    if (output->clones[i] != clones[i])
126		break;
127	if (i == numClones)
128	    return TRUE;
129    }
130    if (numClones)
131    {
132	newClones = xalloc (numClones * sizeof (RROutputPtr));
133	if (!newClones)
134	    return FALSE;
135    }
136    else
137	newClones = NULL;
138    if (output->clones)
139	xfree (output->clones);
140    memcpy (newClones, clones, numClones * sizeof (RROutputPtr));
141    output->clones = newClones;
142    output->numClones = numClones;
143    RROutputChanged (output, TRUE);
144    return TRUE;
145}
146
147Bool
148RROutputSetModes (RROutputPtr	output,
149		  RRModePtr	*modes,
150		  int		numModes,
151		  int		numPreferred)
152{
153    RRModePtr	*newModes;
154    int		i;
155
156    if (numModes == output->numModes && numPreferred == output->numPreferred)
157    {
158	for (i = 0; i < numModes; i++)
159	    if (output->modes[i] != modes[i])
160		break;
161	if (i == numModes)
162	{
163	    for (i = 0; i < numModes; i++)
164		RRModeDestroy (modes[i]);
165	    return TRUE;
166	}
167    }
168
169    if (numModes)
170    {
171	newModes = xalloc (numModes * sizeof (RRModePtr));
172	if (!newModes)
173	    return FALSE;
174    }
175    else
176	newModes = NULL;
177    if (output->modes)
178    {
179	for (i = 0; i < output->numModes; i++)
180	    RRModeDestroy (output->modes[i]);
181	xfree (output->modes);
182    }
183    memcpy (newModes, modes, numModes * sizeof (RRModePtr));
184    output->modes = newModes;
185    output->numModes = numModes;
186    output->numPreferred = numPreferred;
187    RROutputChanged (output, TRUE);
188    return TRUE;
189}
190
191int
192RROutputAddUserMode (RROutputPtr    output,
193		     RRModePtr	    mode)
194{
195    int		m;
196    ScreenPtr	pScreen = output->pScreen;
197    rrScrPriv(pScreen);
198    RRModePtr	*newModes;
199
200    /* Check to see if this mode is already listed for this output */
201    for (m = 0; m < output->numModes + output->numUserModes; m++)
202    {
203	RRModePtr   e = (m < output->numModes ?
204			 output->modes[m] :
205			 output->userModes[m - output->numModes]);
206	if (mode == e)
207	    return Success;
208    }
209
210    /* Check with the DDX to see if this mode is OK */
211    if (pScrPriv->rrOutputValidateMode)
212	if (!pScrPriv->rrOutputValidateMode (pScreen, output, mode))
213	    return BadMatch;
214
215    if (output->userModes)
216	newModes = xrealloc (output->userModes,
217			     (output->numUserModes + 1) * sizeof (RRModePtr));
218    else
219	newModes = xalloc (sizeof (RRModePtr));
220    if (!newModes)
221	return BadAlloc;
222
223    output->userModes = newModes;
224    output->userModes[output->numUserModes++] = mode;
225    ++mode->refcnt;
226    RROutputChanged (output, TRUE);
227    RRTellChanged (pScreen);
228    return Success;
229}
230
231int
232RROutputDeleteUserMode (RROutputPtr output,
233			RRModePtr   mode)
234{
235    int		m;
236
237    /* Find this mode in the user mode list */
238    for (m = 0; m < output->numUserModes; m++)
239    {
240	RRModePtr   e = output->userModes[m];
241
242	if (mode == e)
243	    break;
244    }
245    /* Not there, access error */
246    if (m == output->numUserModes)
247	return BadAccess;
248
249    /* make sure the mode isn't active for this output */
250    if (output->crtc && output->crtc->mode == mode)
251	return BadMatch;
252
253    memmove (output->userModes + m, output->userModes + m + 1,
254	     (output->numUserModes - m - 1) * sizeof (RRModePtr));
255    output->numUserModes--;
256    RRModeDestroy (mode);
257    return Success;
258}
259
260Bool
261RROutputSetCrtcs (RROutputPtr	output,
262		  RRCrtcPtr	*crtcs,
263		  int		numCrtcs)
264{
265    RRCrtcPtr	*newCrtcs;
266    int		i;
267
268    if (numCrtcs == output->numCrtcs)
269    {
270	for (i = 0; i < numCrtcs; i++)
271	    if (output->crtcs[i] != crtcs[i])
272		break;
273	if (i == numCrtcs)
274	    return TRUE;
275    }
276    if (numCrtcs)
277    {
278	newCrtcs = xalloc (numCrtcs * sizeof (RRCrtcPtr));
279	if (!newCrtcs)
280	    return FALSE;
281    }
282    else
283	newCrtcs = NULL;
284    if (output->crtcs)
285	xfree (output->crtcs);
286    memcpy (newCrtcs, crtcs, numCrtcs * sizeof (RRCrtcPtr));
287    output->crtcs = newCrtcs;
288    output->numCrtcs = numCrtcs;
289    RROutputChanged (output, TRUE);
290    return TRUE;
291}
292
293Bool
294RROutputSetConnection (RROutputPtr  output,
295		       CARD8	    connection)
296{
297    if (output->connection == connection)
298	return TRUE;
299    output->connection = connection;
300    RROutputChanged (output, TRUE);
301    return TRUE;
302}
303
304Bool
305RROutputSetSubpixelOrder (RROutputPtr output,
306			  int	      subpixelOrder)
307{
308    if (output->subpixelOrder == subpixelOrder)
309	return TRUE;
310
311    output->subpixelOrder = subpixelOrder;
312    RROutputChanged (output, FALSE);
313    return TRUE;
314}
315
316Bool
317RROutputSetPhysicalSize (RROutputPtr	output,
318			 int		mmWidth,
319			 int		mmHeight)
320{
321    if (output->mmWidth == mmWidth && output->mmHeight == mmHeight)
322	return TRUE;
323    output->mmWidth = mmWidth;
324    output->mmHeight = mmHeight;
325    RROutputChanged (output, FALSE);
326    return TRUE;
327}
328
329
330void
331RRDeliverOutputEvent(ClientPtr client, WindowPtr pWin, RROutputPtr output)
332{
333    ScreenPtr pScreen = pWin->drawable.pScreen;
334    rrScrPriv (pScreen);
335    xRROutputChangeNotifyEvent	oe;
336    RRCrtcPtr	crtc = output->crtc;
337    RRModePtr	mode = crtc ? crtc->mode : 0;
338
339    oe.type = RRNotify + RREventBase;
340    oe.subCode = RRNotify_OutputChange;
341    oe.sequenceNumber = client->sequence;
342    oe.timestamp = pScrPriv->lastSetTime.milliseconds;
343    oe.configTimestamp = pScrPriv->lastConfigTime.milliseconds;
344    oe.window = pWin->drawable.id;
345    oe.output = output->id;
346    if (crtc)
347    {
348	oe.crtc = crtc->id;
349	oe.mode = mode ? mode->mode.id : None;
350	oe.rotation = crtc->rotation;
351    }
352    else
353    {
354	oe.crtc = None;
355	oe.mode = None;
356	oe.rotation = RR_Rotate_0;
357    }
358    oe.connection = output->connection;
359    oe.subpixelOrder = output->subpixelOrder;
360    WriteEventsToClient (client, 1, (xEvent *) &oe);
361}
362
363/*
364 * Destroy a Output at shutdown
365 */
366void
367RROutputDestroy (RROutputPtr output)
368{
369    FreeResource (output->id, 0);
370}
371
372static int
373RROutputDestroyResource (pointer value, XID pid)
374{
375    RROutputPtr	output = (RROutputPtr) value;
376    ScreenPtr	pScreen = output->pScreen;
377    int		m;
378
379    if (pScreen)
380    {
381	rrScrPriv(pScreen);
382	int		i;
383
384	if (pScrPriv->primaryOutput == output)
385	    pScrPriv->primaryOutput = NULL;
386
387	for (i = 0; i < pScrPriv->numOutputs; i++)
388	{
389	    if (pScrPriv->outputs[i] == output)
390	    {
391		memmove (pScrPriv->outputs + i, pScrPriv->outputs + i + 1,
392			 (pScrPriv->numOutputs - (i + 1)) * sizeof (RROutputPtr));
393		--pScrPriv->numOutputs;
394		break;
395	    }
396	}
397    }
398    if (output->modes)
399    {
400	for (m = 0; m < output->numModes; m++)
401	    RRModeDestroy (output->modes[m]);
402	xfree (output->modes);
403    }
404
405    for (m = 0; m < output->numUserModes; m++)
406	RRModeDestroy (output->userModes[m]);
407    if (output->userModes)
408	xfree (output->userModes);
409
410    if (output->crtcs)
411	xfree (output->crtcs);
412    if (output->clones)
413	xfree (output->clones);
414    RRDeleteAllOutputProperties (output);
415    xfree (output);
416    return 1;
417}
418
419/*
420 * Initialize output type
421 */
422Bool
423RROutputInit (void)
424{
425    RROutputType = CreateNewResourceType (RROutputDestroyResource);
426    if (!RROutputType)
427	return FALSE;
428    RegisterResourceName (RROutputType, "OUTPUT");
429    return TRUE;
430}
431
432#define OutputInfoExtra	(SIZEOF(xRRGetOutputInfoReply) - 32)
433
434int
435ProcRRGetOutputInfo (ClientPtr client)
436{
437    REQUEST(xRRGetOutputInfoReq);
438    xRRGetOutputInfoReply	rep;
439    RROutputPtr			output;
440    CARD8			*extra;
441    unsigned long		extraLen;
442    ScreenPtr			pScreen;
443    rrScrPrivPtr		pScrPriv;
444    RRCrtc			*crtcs;
445    RRMode			*modes;
446    RROutput			*clones;
447    char			*name;
448    int				i, n;
449
450    REQUEST_SIZE_MATCH(xRRGetOutputInfoReq);
451    output = LookupOutput(client, stuff->output, DixReadAccess);
452
453    if (!output)
454    {
455	client->errorValue = stuff->output;
456	return RRErrorBase + BadRROutput;
457    }
458
459    pScreen = output->pScreen;
460    pScrPriv = rrGetScrPriv(pScreen);
461
462    rep.type = X_Reply;
463    rep.sequenceNumber = client->sequence;
464    rep.length = OutputInfoExtra >> 2;
465    rep.timestamp = pScrPriv->lastSetTime.milliseconds;
466    rep.crtc = output->crtc ? output->crtc->id : None;
467    rep.mmWidth = output->mmWidth;
468    rep.mmHeight = output->mmHeight;
469    rep.connection = output->connection;
470    rep.subpixelOrder = output->subpixelOrder;
471    rep.nCrtcs = output->numCrtcs;
472    rep.nModes = output->numModes + output->numUserModes;
473    rep.nPreferred = output->numPreferred;
474    rep.nClones = output->numClones;
475    rep.nameLength = output->nameLength;
476
477    extraLen = ((output->numCrtcs +
478		 output->numModes + output->numUserModes +
479		 output->numClones +
480		 ((rep.nameLength + 3) >> 2)) << 2);
481
482    if (extraLen)
483    {
484	rep.length += extraLen >> 2;
485	extra = xalloc (extraLen);
486	if (!extra)
487	    return BadAlloc;
488    }
489    else
490	extra = NULL;
491
492    crtcs = (RRCrtc *) extra;
493    modes = (RRMode *) (crtcs + output->numCrtcs);
494    clones = (RROutput *) (modes + output->numModes + output->numUserModes);
495    name = (char *) (clones + output->numClones);
496
497    for (i = 0; i < output->numCrtcs; i++)
498    {
499	crtcs[i] = output->crtcs[i]->id;
500	if (client->swapped)
501	    swapl (&crtcs[i], n);
502    }
503    for (i = 0; i < output->numModes + output->numUserModes; i++)
504    {
505	if (i < output->numModes)
506	    modes[i] = output->modes[i]->mode.id;
507	else
508	    modes[i] = output->userModes[i - output->numModes]->mode.id;
509	if (client->swapped)
510	    swapl (&modes[i], n);
511    }
512    for (i = 0; i < output->numClones; i++)
513    {
514	clones[i] = output->clones[i]->id;
515	if (client->swapped)
516	    swapl (&clones[i], n);
517    }
518    memcpy (name, output->name, output->nameLength);
519    if (client->swapped) {
520	swaps(&rep.sequenceNumber, n);
521	swapl(&rep.length, n);
522	swapl(&rep.timestamp, n);
523	swapl(&rep.crtc, n);
524	swapl(&rep.mmWidth, n);
525	swapl(&rep.mmHeight, n);
526	swaps(&rep.nCrtcs, n);
527	swaps(&rep.nModes, n);
528	swaps(&rep.nClones, n);
529	swaps(&rep.nameLength, n);
530    }
531    WriteToClient(client, sizeof(xRRGetOutputInfoReply), (char *)&rep);
532    if (extraLen)
533    {
534	WriteToClient (client, extraLen, (char *) extra);
535	xfree (extra);
536    }
537
538    return client->noClientException;
539}
540
541void
542RRSetPrimaryOutput(ScreenPtr pScreen, rrScrPrivPtr pScrPriv,
543		   RROutputPtr output)
544{
545    if (pScrPriv->primaryOutput == output)
546	return;
547
548    /* clear the old primary */
549    if (pScrPriv->primaryOutput) {
550	RROutputChanged(pScrPriv->primaryOutput, 0);
551	pScrPriv->primaryOutput = NULL;
552    }
553
554    /* set the new primary */
555    if (output) {
556	pScrPriv->primaryOutput = output;
557	RROutputChanged(output, 0);
558    }
559
560    pScrPriv->layoutChanged = TRUE;
561
562    RRTellChanged(pScreen);
563}
564
565int
566ProcRRSetOutputPrimary(ClientPtr client)
567{
568    REQUEST(xRRSetOutputPrimaryReq);
569    RROutputPtr output = NULL;
570    WindowPtr pWin;
571    rrScrPrivPtr pScrPriv;
572
573    REQUEST_SIZE_MATCH(xRRSetOutputPrimaryReq);
574
575    pWin = SecurityLookupIDByType(client, stuff->window, RT_WINDOW,
576				  DixReadAccess);
577
578    if (!pWin) {
579	client->errorValue = stuff->window;
580	return BadWindow;
581    }
582
583    if (stuff->output) {
584	output = LookupOutput(client, stuff->output, DixReadAccess);
585
586	if (!output) {
587	    client->errorValue = stuff->output;
588	    return RRErrorBase + BadRROutput;
589	}
590
591	if (output->pScreen != pWin->drawable.pScreen) {
592	    client->errorValue = stuff->window;
593	    return BadMatch;
594	}
595    }
596
597    pScrPriv = rrGetScrPriv(pWin->drawable.pScreen);
598    RRSetPrimaryOutput(pWin->drawable.pScreen, pScrPriv, output);
599
600    return client->noClientException;
601}
602
603int
604ProcRRGetOutputPrimary(ClientPtr client)
605{
606    REQUEST(xRRGetOutputPrimaryReq);
607    WindowPtr pWin;
608    rrScrPrivPtr pScrPriv;
609    xRRGetOutputPrimaryReply rep;
610    RROutputPtr primary = NULL;
611
612    REQUEST_SIZE_MATCH(xRRGetOutputPrimaryReq);
613
614    pWin = SecurityLookupIDByType(client, stuff->window, RT_WINDOW,
615				  DixReadAccess);
616
617    if (!pWin) {
618	client->errorValue = stuff->window;
619	return BadWindow;
620    }
621
622    pScrPriv = rrGetScrPriv(pWin->drawable.pScreen);
623    if (pScrPriv)
624	primary = pScrPriv->primaryOutput;
625
626    memset(&rep, 0, sizeof(rep));
627    rep.type = X_Reply;
628    rep.sequenceNumber = client->sequence;
629    rep.output = primary ? primary->id : None;
630
631    if (client->swapped) {
632	int n;
633	swaps(&rep.sequenceNumber, n);
634	swapl(&rep.output, n);
635    }
636
637    WriteToClient(client, sizeof(xRRGetOutputPrimaryReply), &rep);
638
639    return client->noClientException;
640}
641