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 <X11/Xatom.h>
26
27RESTYPE RROutputType;
28
29/*
30 * Notify the output of some change
31 */
32void
33RROutputChanged(RROutputPtr output, Bool configChanged)
34{
35    /* set changed bits on the primary screen only */
36    ScreenPtr pScreen = output->pScreen;
37    rrScrPrivPtr primarysp;
38
39    output->changed = TRUE;
40    if (!pScreen)
41        return;
42
43    if (pScreen->isGPU) {
44        ScreenPtr primary = pScreen->current_primary;
45        if (!primary)
46            return;
47        primarysp = rrGetScrPriv(primary);
48    }
49    else {
50        primarysp = rrGetScrPriv(pScreen);
51    }
52
53    RRSetChanged(pScreen);
54    if (configChanged)
55        primarysp->configChanged = TRUE;
56}
57
58/*
59 * Create an output
60 */
61
62RROutputPtr
63RROutputCreate(ScreenPtr pScreen,
64               const char *name, int nameLength, void *devPrivate)
65{
66    RROutputPtr output;
67    RROutputPtr *outputs;
68    rrScrPrivPtr pScrPriv;
69    Atom nonDesktopAtom;
70
71    if (!RRInit())
72        return NULL;
73
74    pScrPriv = rrGetScrPriv(pScreen);
75
76    outputs = reallocarray(pScrPriv->outputs,
77                           pScrPriv->numOutputs + 1, sizeof(RROutputPtr));
78    if (!outputs)
79        return NULL;
80
81    pScrPriv->outputs = outputs;
82
83    output = malloc(sizeof(RROutputRec) + nameLength + 1);
84    if (!output)
85        return NULL;
86    output->id = FakeClientID(0);
87    output->pScreen = pScreen;
88    output->name = (char *) (output + 1);
89    output->nameLength = nameLength;
90    memcpy(output->name, name, nameLength);
91    output->name[nameLength] = '\0';
92    output->connection = RR_UnknownConnection;
93    output->subpixelOrder = SubPixelUnknown;
94    output->mmWidth = 0;
95    output->mmHeight = 0;
96    output->crtc = NULL;
97    output->numCrtcs = 0;
98    output->crtcs = NULL;
99    output->numClones = 0;
100    output->clones = NULL;
101    output->numModes = 0;
102    output->numPreferred = 0;
103    output->modes = NULL;
104    output->numUserModes = 0;
105    output->userModes = NULL;
106    output->properties = NULL;
107    output->pendingProperties = FALSE;
108    output->changed = FALSE;
109    output->nonDesktop = FALSE;
110    output->devPrivate = devPrivate;
111
112    if (!AddResource(output->id, RROutputType, (void *) output))
113        return NULL;
114
115    pScrPriv->outputs[pScrPriv->numOutputs++] = output;
116
117    nonDesktopAtom = MakeAtom(RR_PROPERTY_NON_DESKTOP, strlen(RR_PROPERTY_NON_DESKTOP), TRUE);
118    if (nonDesktopAtom != BAD_RESOURCE) {
119        static const INT32 values[2] = { 0, 1 };
120        (void) RRConfigureOutputProperty(output, nonDesktopAtom, FALSE, FALSE, FALSE,
121                                            2, values);
122    }
123    RROutputSetNonDesktop(output, FALSE);
124    RRResourcesChanged(pScreen);
125
126    return output;
127}
128
129/*
130 * Notify extension that output parameters have been changed
131 */
132Bool
133RROutputSetClones(RROutputPtr output, RROutputPtr * clones, int numClones)
134{
135    RROutputPtr *newClones;
136    int i;
137
138    if (numClones == output->numClones) {
139        for (i = 0; i < numClones; i++)
140            if (output->clones[i] != clones[i])
141                break;
142        if (i == numClones)
143            return TRUE;
144    }
145    if (numClones) {
146        newClones = xallocarray(numClones, sizeof(RROutputPtr));
147        if (!newClones)
148            return FALSE;
149    }
150    else
151        newClones = NULL;
152    free(output->clones);
153    memcpy(newClones, clones, numClones * sizeof(RROutputPtr));
154    output->clones = newClones;
155    output->numClones = numClones;
156    RROutputChanged(output, TRUE);
157    return TRUE;
158}
159
160Bool
161RROutputSetModes(RROutputPtr output,
162                 RRModePtr * modes, int numModes, int numPreferred)
163{
164    RRModePtr *newModes;
165    int i;
166
167    if (numModes == output->numModes && numPreferred == output->numPreferred) {
168        for (i = 0; i < numModes; i++)
169            if (output->modes[i] != modes[i])
170                break;
171        if (i == numModes) {
172            for (i = 0; i < numModes; i++)
173                RRModeDestroy(modes[i]);
174            return TRUE;
175        }
176    }
177
178    if (numModes) {
179        newModes = xallocarray(numModes, sizeof(RRModePtr));
180        if (!newModes)
181            return FALSE;
182    }
183    else
184        newModes = NULL;
185    if (output->modes) {
186        for (i = 0; i < output->numModes; i++)
187            RRModeDestroy(output->modes[i]);
188        free(output->modes);
189    }
190    memcpy(newModes, modes, numModes * sizeof(RRModePtr));
191    output->modes = newModes;
192    output->numModes = numModes;
193    output->numPreferred = numPreferred;
194    RROutputChanged(output, TRUE);
195    return TRUE;
196}
197
198int
199RROutputAddUserMode(RROutputPtr output, RRModePtr mode)
200{
201    int m;
202    ScreenPtr pScreen = output->pScreen;
203
204    rrScrPriv(pScreen);
205    RRModePtr *newModes;
206
207    /* Check to see if this mode is already listed for this output */
208    for (m = 0; m < output->numModes + output->numUserModes; m++) {
209        RRModePtr e = (m < output->numModes ?
210                       output->modes[m] :
211                       output->userModes[m - output->numModes]);
212        if (mode == e)
213            return Success;
214    }
215
216    /* Check with the DDX to see if this mode is OK */
217    if (pScrPriv->rrOutputValidateMode)
218        if (!pScrPriv->rrOutputValidateMode(pScreen, output, mode))
219            return BadMatch;
220
221    if (output->userModes)
222        newModes = reallocarray(output->userModes,
223                                output->numUserModes + 1, sizeof(RRModePtr));
224    else
225        newModes = malloc(sizeof(RRModePtr));
226    if (!newModes)
227        return BadAlloc;
228
229    output->userModes = newModes;
230    output->userModes[output->numUserModes++] = mode;
231    ++mode->refcnt;
232    RROutputChanged(output, TRUE);
233    RRTellChanged(pScreen);
234    return Success;
235}
236
237int
238RROutputDeleteUserMode(RROutputPtr output, RRModePtr mode)
239{
240    int m;
241
242    /* Find this mode in the user mode list */
243    for (m = 0; m < output->numUserModes; m++) {
244        RRModePtr e = output->userModes[m];
245
246        if (mode == e)
247            break;
248    }
249    /* Not there, access error */
250    if (m == output->numUserModes)
251        return BadAccess;
252
253    /* make sure the mode isn't active for this output */
254    if (output->crtc && output->crtc->mode == mode)
255        return BadMatch;
256
257    memmove(output->userModes + m, output->userModes + m + 1,
258            (output->numUserModes - m - 1) * sizeof(RRModePtr));
259    output->numUserModes--;
260    RRModeDestroy(mode);
261    return Success;
262}
263
264Bool
265RROutputSetCrtcs(RROutputPtr output, RRCrtcPtr * crtcs, int numCrtcs)
266{
267    RRCrtcPtr *newCrtcs;
268    int i;
269
270    if (numCrtcs == output->numCrtcs) {
271        for (i = 0; i < numCrtcs; i++)
272            if (output->crtcs[i] != crtcs[i])
273                break;
274        if (i == numCrtcs)
275            return TRUE;
276    }
277    if (numCrtcs) {
278        newCrtcs = xallocarray(numCrtcs, sizeof(RRCrtcPtr));
279        if (!newCrtcs)
280            return FALSE;
281    }
282    else
283        newCrtcs = NULL;
284    free(output->crtcs);
285    memcpy(newCrtcs, crtcs, numCrtcs * sizeof(RRCrtcPtr));
286    output->crtcs = newCrtcs;
287    output->numCrtcs = numCrtcs;
288    RROutputChanged(output, TRUE);
289    return TRUE;
290}
291
292Bool
293RROutputSetConnection(RROutputPtr output, CARD8 connection)
294{
295    if (output->connection == connection)
296        return TRUE;
297    output->connection = connection;
298    RROutputChanged(output, TRUE);
299    return TRUE;
300}
301
302Bool
303RROutputSetSubpixelOrder(RROutputPtr output, 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, int mmWidth, int mmHeight)
315{
316    if (output->mmWidth == mmWidth && output->mmHeight == mmHeight)
317        return TRUE;
318    output->mmWidth = mmWidth;
319    output->mmHeight = mmHeight;
320    RROutputChanged(output, FALSE);
321    return TRUE;
322}
323
324Bool
325RROutputSetNonDesktop(RROutputPtr output, Bool nonDesktop)
326{
327    const char *nonDesktopStr = RR_PROPERTY_NON_DESKTOP;
328    Atom nonDesktopProp = MakeAtom(nonDesktopStr, strlen(nonDesktopStr), TRUE);
329    uint32_t value = nonDesktop ? 1 : 0;
330
331    if (nonDesktopProp == None || nonDesktopProp == BAD_RESOURCE)
332        return FALSE;
333
334    return RRChangeOutputProperty(output, nonDesktopProp, XA_INTEGER, 32,
335                                  PropModeReplace, 1, &value, TRUE, FALSE) == Success;
336}
337
338void
339RRDeliverOutputEvent(ClientPtr client, WindowPtr pWin, RROutputPtr output)
340{
341    ScreenPtr pScreen = pWin->drawable.pScreen;
342
343    rrScrPriv(pScreen);
344    RRCrtcPtr crtc = output->crtc;
345    RRModePtr mode = crtc ? crtc->mode : NULL;
346
347    xRROutputChangeNotifyEvent oe = {
348        .type = RRNotify + RREventBase,
349        .subCode = RRNotify_OutputChange,
350        .timestamp = pScrPriv->lastSetTime.milliseconds,
351        .configTimestamp = pScrPriv->lastConfigTime.milliseconds,
352        .window = pWin->drawable.id,
353        .output = output->id,
354        .crtc = crtc ? crtc->id : None,
355        .mode = mode ? mode->mode.id : None,
356        .rotation = crtc ? crtc->rotation : RR_Rotate_0,
357        .connection = output->nonDesktop ? RR_Disconnected : output->connection,
358        .subpixelOrder = output->subpixelOrder
359    };
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(void *value, XID pid)
374{
375    RROutputPtr output = (RROutputPtr) value;
376    ScreenPtr pScreen = output->pScreen;
377    int m;
378
379    if (pScreen) {
380        rrScrPriv(pScreen);
381        int i;
382        RRLeasePtr lease, next;
383
384        xorg_list_for_each_entry_safe(lease, next, &pScrPriv->leases, list) {
385            int o;
386            for (o = 0; o < lease->numOutputs; o++) {
387                if (lease->outputs[o] == output) {
388                    RRTerminateLease(lease);
389                    break;
390                }
391            }
392        }
393
394        if (pScrPriv->primaryOutput == output)
395            pScrPriv->primaryOutput = NULL;
396
397        for (i = 0; i < pScrPriv->numOutputs; i++) {
398            if (pScrPriv->outputs[i] == output) {
399                memmove(pScrPriv->outputs + i, pScrPriv->outputs + i + 1,
400                        (pScrPriv->numOutputs - (i + 1)) * sizeof(RROutputPtr));
401                --pScrPriv->numOutputs;
402                break;
403            }
404        }
405
406        RRResourcesChanged(pScreen);
407    }
408    if (output->modes) {
409        for (m = 0; m < output->numModes; m++)
410            RRModeDestroy(output->modes[m]);
411        free(output->modes);
412    }
413
414    for (m = 0; m < output->numUserModes; m++)
415        RRModeDestroy(output->userModes[m]);
416    free(output->userModes);
417
418    free(output->crtcs);
419    free(output->clones);
420    RRDeleteAllOutputProperties(output);
421    free(output);
422    return 1;
423}
424
425/*
426 * Initialize output type
427 */
428Bool
429RROutputInit(void)
430{
431    RROutputType = CreateNewResourceType(RROutputDestroyResource, "OUTPUT");
432    if (!RROutputType)
433        return FALSE;
434
435    return TRUE;
436}
437
438/*
439 * Initialize output type error value
440 */
441void
442RROutputInitErrorValue(void)
443{
444    SetResourceTypeErrorValue(RROutputType, RRErrorBase + BadRROutput);
445}
446
447#define OutputInfoExtra	(SIZEOF(xRRGetOutputInfoReply) - 32)
448
449int
450ProcRRGetOutputInfo(ClientPtr client)
451{
452    REQUEST(xRRGetOutputInfoReq);
453    xRRGetOutputInfoReply rep;
454    RROutputPtr output;
455    CARD8 *extra;
456    unsigned long extraLen;
457    ScreenPtr pScreen;
458    rrScrPrivPtr pScrPriv;
459    RRCrtc *crtcs;
460    RRMode *modes;
461    RROutput *clones;
462    char *name;
463    int i;
464    Bool leased;
465
466    REQUEST_SIZE_MATCH(xRRGetOutputInfoReq);
467    VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
468
469    leased = RROutputIsLeased(output);
470
471    pScreen = output->pScreen;
472    pScrPriv = rrGetScrPriv(pScreen);
473
474    if (leased) {
475        rep = (xRRGetOutputInfoReply) {
476            .type = X_Reply,
477            .status = RRSetConfigSuccess,
478            .sequenceNumber = client->sequence,
479            .length = bytes_to_int32(OutputInfoExtra),
480            .timestamp = pScrPriv->lastSetTime.milliseconds,
481            .crtc = None,
482            .mmWidth = 0,
483            .mmHeight = 0,
484            .connection = RR_Disconnected,
485            .subpixelOrder = SubPixelUnknown,
486            .nCrtcs = 0,
487            .nModes = 0,
488            .nPreferred = 0,
489            .nClones = 0,
490            .nameLength = output->nameLength
491        };
492        extraLen = bytes_to_int32(rep.nameLength) << 2;
493        if (extraLen) {
494            rep.length += bytes_to_int32(extraLen);
495            extra = calloc(1, extraLen);
496            if (!extra)
497                return BadAlloc;
498        }
499        else
500            extra = NULL;
501
502        name = (char *) extra;
503    } else {
504        rep = (xRRGetOutputInfoReply) {
505            .type = X_Reply,
506            .status = RRSetConfigSuccess,
507            .sequenceNumber = client->sequence,
508            .length = bytes_to_int32(OutputInfoExtra),
509            .timestamp = pScrPriv->lastSetTime.milliseconds,
510            .crtc = output->crtc ? output->crtc->id : None,
511            .mmWidth = output->mmWidth,
512            .mmHeight = output->mmHeight,
513            .connection = output->nonDesktop ? RR_Disconnected : output->connection,
514            .subpixelOrder = output->subpixelOrder,
515            .nCrtcs = output->numCrtcs,
516            .nModes = output->numModes + output->numUserModes,
517            .nPreferred = output->numPreferred,
518            .nClones = output->numClones,
519            .nameLength = output->nameLength
520        };
521        extraLen = ((output->numCrtcs +
522                     output->numModes + output->numUserModes +
523                     output->numClones + bytes_to_int32(rep.nameLength)) << 2);
524
525        if (extraLen) {
526            rep.length += bytes_to_int32(extraLen);
527            extra = calloc(1, extraLen);
528            if (!extra)
529                return BadAlloc;
530        }
531        else
532            extra = NULL;
533
534        crtcs = (RRCrtc *) extra;
535        modes = (RRMode *) (crtcs + output->numCrtcs);
536        clones = (RROutput *) (modes + output->numModes + output->numUserModes);
537        name = (char *) (clones + output->numClones);
538
539        for (i = 0; i < output->numCrtcs; i++) {
540            crtcs[i] = output->crtcs[i]->id;
541            if (client->swapped)
542                swapl(&crtcs[i]);
543        }
544        for (i = 0; i < output->numModes + output->numUserModes; i++) {
545            if (i < output->numModes)
546                modes[i] = output->modes[i]->mode.id;
547            else
548                modes[i] = output->userModes[i - output->numModes]->mode.id;
549            if (client->swapped)
550                swapl(&modes[i]);
551        }
552        for (i = 0; i < output->numClones; i++) {
553            clones[i] = output->clones[i]->id;
554            if (client->swapped)
555                swapl(&clones[i]);
556        }
557    }
558    memcpy(name, output->name, output->nameLength);
559    if (client->swapped) {
560        swaps(&rep.sequenceNumber);
561        swapl(&rep.length);
562        swapl(&rep.timestamp);
563        swapl(&rep.crtc);
564        swapl(&rep.mmWidth);
565        swapl(&rep.mmHeight);
566        swaps(&rep.nCrtcs);
567        swaps(&rep.nModes);
568        swaps(&rep.nPreferred);
569        swaps(&rep.nClones);
570        swaps(&rep.nameLength);
571    }
572    WriteToClient(client, sizeof(xRRGetOutputInfoReply), &rep);
573    if (extraLen) {
574        WriteToClient(client, extraLen, extra);
575        free(extra);
576    }
577
578    return Success;
579}
580
581static void
582RRSetPrimaryOutput(ScreenPtr pScreen, rrScrPrivPtr pScrPriv, RROutputPtr output)
583{
584    if (pScrPriv->primaryOutput == output)
585        return;
586
587    /* clear the old primary */
588    if (pScrPriv->primaryOutput) {
589        RROutputChanged(pScrPriv->primaryOutput, 0);
590        pScrPriv->primaryOutput = NULL;
591    }
592
593    /* set the new primary */
594    if (output) {
595        pScrPriv->primaryOutput = output;
596        RROutputChanged(output, 0);
597    }
598
599    pScrPriv->layoutChanged = TRUE;
600
601    RRTellChanged(pScreen);
602}
603
604int
605ProcRRSetOutputPrimary(ClientPtr client)
606{
607    REQUEST(xRRSetOutputPrimaryReq);
608    RROutputPtr output = NULL;
609    WindowPtr pWin;
610    rrScrPrivPtr pScrPriv;
611    int ret;
612    ScreenPtr secondary;
613
614    REQUEST_SIZE_MATCH(xRRSetOutputPrimaryReq);
615
616    ret = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
617    if (ret != Success)
618        return ret;
619
620    if (stuff->output) {
621        VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
622
623        if (RROutputIsLeased(output))
624            return BadAccess;
625
626        if (!output->pScreen->isGPU && output->pScreen != pWin->drawable.pScreen) {
627            client->errorValue = stuff->window;
628            return BadMatch;
629        }
630        if (output->pScreen->isGPU && output->pScreen->current_primary != pWin->drawable.pScreen) {
631            client->errorValue = stuff->window;
632            return BadMatch;
633        }
634    }
635
636    pScrPriv = rrGetScrPriv(pWin->drawable.pScreen);
637    if (pScrPriv)
638    {
639        RRSetPrimaryOutput(pWin->drawable.pScreen, pScrPriv, output);
640
641        xorg_list_for_each_entry(secondary,
642                                 &pWin->drawable.pScreen->secondary_list,
643                                 secondary_head) {
644            if (secondary->is_output_secondary)
645                RRSetPrimaryOutput(secondary, rrGetScrPriv(secondary), output);
646        }
647    }
648
649    return Success;
650}
651
652int
653ProcRRGetOutputPrimary(ClientPtr client)
654{
655    REQUEST(xRRGetOutputPrimaryReq);
656    WindowPtr pWin;
657    rrScrPrivPtr pScrPriv;
658    xRRGetOutputPrimaryReply rep;
659    RROutputPtr primary = NULL;
660    int rc;
661
662    REQUEST_SIZE_MATCH(xRRGetOutputPrimaryReq);
663
664    rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
665    if (rc != Success)
666        return rc;
667
668    pScrPriv = rrGetScrPriv(pWin->drawable.pScreen);
669    if (pScrPriv)
670        primary = pScrPriv->primaryOutput;
671
672    rep = (xRRGetOutputPrimaryReply) {
673        .type = X_Reply,
674        .sequenceNumber = client->sequence,
675        .output = primary ? primary->id : None
676    };
677
678    if (client->swapped) {
679        swaps(&rep.sequenceNumber);
680        swapl(&rep.output);
681    }
682
683    WriteToClient(client, sizeof(xRRGetOutputPrimaryReply), &rep);
684
685    return Success;
686}
687