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