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
26RESTYPE	RROutputType;
27
28/*
29 * Notify the output of some change
30 */
31void
32RROutputChanged (RROutputPtr output, Bool configChanged)
33{
34    ScreenPtr	pScreen = output->pScreen;
35
36    output->changed = TRUE;
37    if (pScreen)
38    {
39	rrScrPriv (pScreen);
40	pScrPriv->changed = TRUE;
41	if (configChanged)
42	    pScrPriv->configChanged = TRUE;
43    }
44}
45
46/*
47 * Create an output
48 */
49
50RROutputPtr
51RROutputCreate (ScreenPtr   pScreen,
52		const char  *name,
53		int	    nameLength,
54		void	    *devPrivate)
55{
56    RROutputPtr	    output;
57    RROutputPtr	    *outputs;
58    rrScrPrivPtr    pScrPriv;
59
60    if (!RRInit())
61	return NULL;
62
63    pScrPriv = rrGetScrPriv(pScreen);
64
65    if (pScrPriv->numOutputs)
66	outputs = realloc(pScrPriv->outputs,
67			    (pScrPriv->numOutputs + 1) * sizeof (RROutputPtr));
68    else
69	outputs = malloc(sizeof (RROutputPtr));
70    if (!outputs)
71	return FALSE;
72
73    pScrPriv->outputs = outputs;
74
75    output = malloc(sizeof (RROutputRec) + nameLength + 1);
76    if (!output)
77	return NULL;
78    output->id = FakeClientID (0);
79    output->pScreen = pScreen;
80    output->name = (char *) (output + 1);
81    output->nameLength = nameLength;
82    memcpy (output->name, name, nameLength);
83    output->name[nameLength] = '\0';
84    output->connection = RR_UnknownConnection;
85    output->subpixelOrder = SubPixelUnknown;
86    output->mmWidth = 0;
87    output->mmHeight = 0;
88    output->crtc = NULL;
89    output->numCrtcs = 0;
90    output->crtcs = NULL;
91    output->numClones = 0;
92    output->clones = NULL;
93    output->numModes = 0;
94    output->numPreferred = 0;
95    output->modes = NULL;
96    output->numUserModes = 0;
97    output->userModes = NULL;
98    output->properties = NULL;
99    output->pendingProperties = FALSE;
100    output->changed = FALSE;
101    output->devPrivate = devPrivate;
102
103    if (!AddResource (output->id, RROutputType, (pointer) output))
104	return NULL;
105
106    pScrPriv->outputs[pScrPriv->numOutputs++] = output;
107    return output;
108}
109
110/*
111 * Notify extension that output parameters have been changed
112 */
113Bool
114RROutputSetClones (RROutputPtr  output,
115		   RROutputPtr  *clones,
116		   int		numClones)
117{
118    RROutputPtr	*newClones;
119    int		i;
120
121    if (numClones == output->numClones)
122    {
123	for (i = 0; i < numClones; i++)
124	    if (output->clones[i] != clones[i])
125		break;
126	if (i == numClones)
127	    return TRUE;
128    }
129    if (numClones)
130    {
131	newClones = malloc(numClones * sizeof (RROutputPtr));
132	if (!newClones)
133	    return FALSE;
134    }
135    else
136	newClones = NULL;
137    free(output->clones);
138    memcpy (newClones, clones, numClones * sizeof (RROutputPtr));
139    output->clones = newClones;
140    output->numClones = numClones;
141    RROutputChanged (output, TRUE);
142    return TRUE;
143}
144
145Bool
146RROutputSetModes (RROutputPtr	output,
147		  RRModePtr	*modes,
148		  int		numModes,
149		  int		numPreferred)
150{
151    RRModePtr	*newModes;
152    int		i;
153
154    if (numModes == output->numModes && numPreferred == output->numPreferred)
155    {
156	for (i = 0; i < numModes; i++)
157	    if (output->modes[i] != modes[i])
158		break;
159	if (i == numModes)
160	{
161	    for (i = 0; i < numModes; i++)
162		RRModeDestroy (modes[i]);
163	    return TRUE;
164	}
165    }
166
167    if (numModes)
168    {
169	newModes = malloc(numModes * sizeof (RRModePtr));
170	if (!newModes)
171	    return FALSE;
172    }
173    else
174	newModes = NULL;
175    if (output->modes)
176    {
177	for (i = 0; i < output->numModes; i++)
178	    RRModeDestroy (output->modes[i]);
179	free(output->modes);
180    }
181    memcpy (newModes, modes, numModes * sizeof (RRModePtr));
182    output->modes = newModes;
183    output->numModes = numModes;
184    output->numPreferred = numPreferred;
185    RROutputChanged (output, TRUE);
186    return TRUE;
187}
188
189int
190RROutputAddUserMode (RROutputPtr    output,
191		     RRModePtr	    mode)
192{
193    int		m;
194    ScreenPtr	pScreen = output->pScreen;
195    rrScrPriv(pScreen);
196    RRModePtr	*newModes;
197
198    /* Check to see if this mode is already listed for this output */
199    for (m = 0; m < output->numModes + output->numUserModes; m++)
200    {
201	RRModePtr   e = (m < output->numModes ?
202			 output->modes[m] :
203			 output->userModes[m - output->numModes]);
204	if (mode == e)
205	    return Success;
206    }
207
208    /* Check with the DDX to see if this mode is OK */
209    if (pScrPriv->rrOutputValidateMode)
210	if (!pScrPriv->rrOutputValidateMode (pScreen, output, mode))
211	    return BadMatch;
212
213    if (output->userModes)
214	newModes = realloc(output->userModes,
215			     (output->numUserModes + 1) * sizeof (RRModePtr));
216    else
217	newModes = malloc(sizeof (RRModePtr));
218    if (!newModes)
219	return BadAlloc;
220
221    output->userModes = newModes;
222    output->userModes[output->numUserModes++] = mode;
223    ++mode->refcnt;
224    RROutputChanged (output, TRUE);
225    RRTellChanged (pScreen);
226    return Success;
227}
228
229int
230RROutputDeleteUserMode (RROutputPtr output,
231			RRModePtr   mode)
232{
233    int		m;
234
235    /* Find this mode in the user mode list */
236    for (m = 0; m < output->numUserModes; m++)
237    {
238	RRModePtr   e = output->userModes[m];
239
240	if (mode == e)
241	    break;
242    }
243    /* Not there, access error */
244    if (m == output->numUserModes)
245	return BadAccess;
246
247    /* make sure the mode isn't active for this output */
248    if (output->crtc && output->crtc->mode == mode)
249	return BadMatch;
250
251    memmove (output->userModes + m, output->userModes + m + 1,
252	     (output->numUserModes - m - 1) * sizeof (RRModePtr));
253    output->numUserModes--;
254    RRModeDestroy (mode);
255    return Success;
256}
257
258Bool
259RROutputSetCrtcs (RROutputPtr	output,
260		  RRCrtcPtr	*crtcs,
261		  int		numCrtcs)
262{
263    RRCrtcPtr	*newCrtcs;
264    int		i;
265
266    if (numCrtcs == output->numCrtcs)
267    {
268	for (i = 0; i < numCrtcs; i++)
269	    if (output->crtcs[i] != crtcs[i])
270		break;
271	if (i == numCrtcs)
272	    return TRUE;
273    }
274    if (numCrtcs)
275    {
276	newCrtcs = malloc(numCrtcs * sizeof (RRCrtcPtr));
277	if (!newCrtcs)
278	    return FALSE;
279    }
280    else
281	newCrtcs = NULL;
282    free(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.timestamp = pScrPriv->lastSetTime.milliseconds;
339    oe.configTimestamp = pScrPriv->lastConfigTime.milliseconds;
340    oe.window = pWin->drawable.id;
341    oe.output = output->id;
342    if (crtc)
343    {
344	oe.crtc = crtc->id;
345	oe.mode = mode ? mode->mode.id : None;
346	oe.rotation = crtc->rotation;
347    }
348    else
349    {
350	oe.crtc = None;
351	oe.mode = None;
352	oe.rotation = RR_Rotate_0;
353    }
354    oe.connection = output->connection;
355    oe.subpixelOrder = output->subpixelOrder;
356    WriteEventsToClient (client, 1, (xEvent *) &oe);
357}
358
359/*
360 * Destroy a Output at shutdown
361 */
362void
363RROutputDestroy (RROutputPtr output)
364{
365    FreeResource (output->id, 0);
366}
367
368static int
369RROutputDestroyResource (pointer value, XID pid)
370{
371    RROutputPtr	output = (RROutputPtr) value;
372    ScreenPtr	pScreen = output->pScreen;
373    int		m;
374
375    if (pScreen)
376    {
377	rrScrPriv(pScreen);
378	int		i;
379
380	if (pScrPriv->primaryOutput == output)
381	    pScrPriv->primaryOutput = NULL;
382
383	for (i = 0; i < pScrPriv->numOutputs; i++)
384	{
385	    if (pScrPriv->outputs[i] == output)
386	    {
387		memmove (pScrPriv->outputs + i, pScrPriv->outputs + i + 1,
388			 (pScrPriv->numOutputs - (i + 1)) * sizeof (RROutputPtr));
389		--pScrPriv->numOutputs;
390		break;
391	    }
392	}
393    }
394    if (output->modes)
395    {
396	for (m = 0; m < output->numModes; m++)
397	    RRModeDestroy (output->modes[m]);
398	free(output->modes);
399    }
400
401    for (m = 0; m < output->numUserModes; m++)
402	RRModeDestroy (output->userModes[m]);
403    free(output->userModes);
404
405    free(output->crtcs);
406    free(output->clones);
407    RRDeleteAllOutputProperties (output);
408    free(output);
409    return 1;
410}
411
412/*
413 * Initialize output type
414 */
415Bool
416RROutputInit (void)
417{
418    RROutputType = CreateNewResourceType (RROutputDestroyResource, "OUTPUT");
419    if (!RROutputType)
420	return FALSE;
421
422    return TRUE;
423}
424
425/*
426 * Initialize output type error value
427 */
428void
429RROutputInitErrorValue(void)
430{
431    SetResourceTypeErrorValue(RROutputType, RRErrorBase + BadRROutput);
432}
433
434#define OutputInfoExtra	(SIZEOF(xRRGetOutputInfoReply) - 32)
435
436int
437ProcRRGetOutputInfo (ClientPtr client)
438{
439    REQUEST(xRRGetOutputInfoReq);
440    xRRGetOutputInfoReply	rep;
441    RROutputPtr			output;
442    CARD8			*extra;
443    unsigned long		extraLen;
444    ScreenPtr			pScreen;
445    rrScrPrivPtr		pScrPriv;
446    RRCrtc			*crtcs;
447    RRMode			*modes;
448    RROutput			*clones;
449    char			*name;
450    int				i, n;
451
452    REQUEST_SIZE_MATCH(xRRGetOutputInfoReq);
453    VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
454
455    pScreen = output->pScreen;
456    pScrPriv = rrGetScrPriv(pScreen);
457
458    rep.type = X_Reply;
459    rep.sequenceNumber = client->sequence;
460    rep.length = bytes_to_int32(OutputInfoExtra);
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		 bytes_to_int32(rep.nameLength)) << 2);
477
478    if (extraLen)
479    {
480	rep.length += bytes_to_int32(extraLen);
481	extra = malloc(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	free(extra);
532    }
533
534    return Success;
535}
536
537static void
538RRSetPrimaryOutput(ScreenPtr pScreen, rrScrPrivPtr pScrPriv,
539		   RROutputPtr output)
540{
541    if (pScrPriv->primaryOutput == output)
542	return;
543
544    /* clear the old primary */
545    if (pScrPriv->primaryOutput) {
546	RROutputChanged(pScrPriv->primaryOutput, 0);
547	pScrPriv->primaryOutput = NULL;
548    }
549
550    /* set the new primary */
551    if (output) {
552	pScrPriv->primaryOutput = output;
553	RROutputChanged(output, 0);
554    }
555
556    pScrPriv->layoutChanged = TRUE;
557
558    RRTellChanged(pScreen);
559}
560
561int
562ProcRRSetOutputPrimary(ClientPtr client)
563{
564    REQUEST(xRRSetOutputPrimaryReq);
565    RROutputPtr output = NULL;
566    WindowPtr pWin;
567    rrScrPrivPtr pScrPriv;
568    int rc;
569
570    REQUEST_SIZE_MATCH(xRRSetOutputPrimaryReq);
571
572    rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
573    if (rc != Success)
574	return rc;
575
576    if (stuff->output) {
577	VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
578
579	if (output->pScreen != pWin->drawable.pScreen) {
580	    client->errorValue = stuff->window;
581	    return BadMatch;
582	}
583    }
584
585    pScrPriv = rrGetScrPriv(pWin->drawable.pScreen);
586    RRSetPrimaryOutput(pWin->drawable.pScreen, pScrPriv, output);
587
588    return Success;
589}
590
591int
592ProcRRGetOutputPrimary(ClientPtr client)
593{
594    REQUEST(xRRGetOutputPrimaryReq);
595    WindowPtr pWin;
596    rrScrPrivPtr pScrPriv;
597    xRRGetOutputPrimaryReply rep;
598    RROutputPtr primary = NULL;
599    int rc;
600
601    REQUEST_SIZE_MATCH(xRRGetOutputPrimaryReq);
602
603    rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
604    if (rc != Success)
605	return rc;
606
607    pScrPriv = rrGetScrPriv(pWin->drawable.pScreen);
608    if (pScrPriv)
609	primary = pScrPriv->primaryOutput;
610
611    memset(&rep, 0, sizeof(rep));
612    rep.type = X_Reply;
613    rep.sequenceNumber = client->sequence;
614    rep.output = primary ? primary->id : None;
615
616    if (client->swapped) {
617	int n;
618	swaps(&rep.sequenceNumber, n);
619	swapl(&rep.output, n);
620    }
621
622    WriteToClient(client, sizeof(xRRGetOutputPrimaryReply), &rep);
623
624    return Success;
625}
626