rroutput.c revision 35c4bbdf
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    /* set changed bits on the master screen only */
35    ScreenPtr pScreen = output->pScreen;
36    rrScrPrivPtr mastersp;
37
38    output->changed = TRUE;
39    if (!pScreen)
40        return;
41
42    if (pScreen->isGPU) {
43        ScreenPtr master = pScreen->current_master;
44        if (!master)
45            return;
46        mastersp = rrGetScrPriv(master);
47    }
48    else {
49        mastersp = rrGetScrPriv(pScreen);
50    }
51
52    RRSetChanged(pScreen);
53    if (configChanged)
54        mastersp->configChanged = TRUE;
55}
56
57/*
58 * Create an output
59 */
60
61RROutputPtr
62RROutputCreate(ScreenPtr pScreen,
63               const char *name, int nameLength, void *devPrivate)
64{
65    RROutputPtr output;
66    RROutputPtr *outputs;
67    rrScrPrivPtr pScrPriv;
68
69    if (!RRInit())
70        return NULL;
71
72    pScrPriv = rrGetScrPriv(pScreen);
73
74    if (pScrPriv->numOutputs)
75        outputs = reallocarray(pScrPriv->outputs,
76                               pScrPriv->numOutputs + 1, sizeof(RROutputPtr));
77    else
78        outputs = malloc(sizeof(RROutputPtr));
79    if (!outputs)
80        return FALSE;
81
82    pScrPriv->outputs = outputs;
83
84    output = malloc(sizeof(RROutputRec) + nameLength + 1);
85    if (!output)
86        return NULL;
87    output->id = FakeClientID(0);
88    output->pScreen = pScreen;
89    output->name = (char *) (output + 1);
90    output->nameLength = nameLength;
91    memcpy(output->name, name, nameLength);
92    output->name[nameLength] = '\0';
93    output->connection = RR_UnknownConnection;
94    output->subpixelOrder = SubPixelUnknown;
95    output->mmWidth = 0;
96    output->mmHeight = 0;
97    output->crtc = NULL;
98    output->numCrtcs = 0;
99    output->crtcs = NULL;
100    output->numClones = 0;
101    output->clones = NULL;
102    output->numModes = 0;
103    output->numPreferred = 0;
104    output->modes = NULL;
105    output->numUserModes = 0;
106    output->userModes = NULL;
107    output->properties = NULL;
108    output->pendingProperties = FALSE;
109    output->changed = 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    RRResourcesChanged(pScreen);
118
119    return output;
120}
121
122/*
123 * Notify extension that output parameters have been changed
124 */
125Bool
126RROutputSetClones(RROutputPtr output, RROutputPtr * clones, int numClones)
127{
128    RROutputPtr *newClones;
129    int i;
130
131    if (numClones == output->numClones) {
132        for (i = 0; i < numClones; i++)
133            if (output->clones[i] != clones[i])
134                break;
135        if (i == numClones)
136            return TRUE;
137    }
138    if (numClones) {
139        newClones = xallocarray(numClones, sizeof(RROutputPtr));
140        if (!newClones)
141            return FALSE;
142    }
143    else
144        newClones = NULL;
145    free(output->clones);
146    memcpy(newClones, clones, numClones * sizeof(RROutputPtr));
147    output->clones = newClones;
148    output->numClones = numClones;
149    RROutputChanged(output, TRUE);
150    return TRUE;
151}
152
153Bool
154RROutputSetModes(RROutputPtr output,
155                 RRModePtr * modes, int numModes, int numPreferred)
156{
157    RRModePtr *newModes;
158    int i;
159
160    if (numModes == output->numModes && numPreferred == output->numPreferred) {
161        for (i = 0; i < numModes; i++)
162            if (output->modes[i] != modes[i])
163                break;
164        if (i == numModes) {
165            for (i = 0; i < numModes; i++)
166                RRModeDestroy(modes[i]);
167            return TRUE;
168        }
169    }
170
171    if (numModes) {
172        newModes = xallocarray(numModes, sizeof(RRModePtr));
173        if (!newModes)
174            return FALSE;
175    }
176    else
177        newModes = NULL;
178    if (output->modes) {
179        for (i = 0; i < output->numModes; i++)
180            RRModeDestroy(output->modes[i]);
181        free(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, RRModePtr mode)
193{
194    int m;
195    ScreenPtr pScreen = output->pScreen;
196
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        RRModePtr e = (m < output->numModes ?
203                       output->modes[m] :
204                       output->userModes[m - output->numModes]);
205        if (mode == e)
206            return Success;
207    }
208
209    /* Check with the DDX to see if this mode is OK */
210    if (pScrPriv->rrOutputValidateMode)
211        if (!pScrPriv->rrOutputValidateMode(pScreen, output, mode))
212            return BadMatch;
213
214    if (output->userModes)
215        newModes = reallocarray(output->userModes,
216                                output->numUserModes + 1, sizeof(RRModePtr));
217    else
218        newModes = malloc(sizeof(RRModePtr));
219    if (!newModes)
220        return BadAlloc;
221
222    output->userModes = newModes;
223    output->userModes[output->numUserModes++] = mode;
224    ++mode->refcnt;
225    RROutputChanged(output, TRUE);
226    RRTellChanged(pScreen);
227    return Success;
228}
229
230int
231RROutputDeleteUserMode(RROutputPtr output, 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        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, RRCrtcPtr * crtcs, int numCrtcs)
259{
260    RRCrtcPtr *newCrtcs;
261    int i;
262
263    if (numCrtcs == output->numCrtcs) {
264        for (i = 0; i < numCrtcs; i++)
265            if (output->crtcs[i] != crtcs[i])
266                break;
267        if (i == numCrtcs)
268            return TRUE;
269    }
270    if (numCrtcs) {
271        newCrtcs = xallocarray(numCrtcs, sizeof(RRCrtcPtr));
272        if (!newCrtcs)
273            return FALSE;
274    }
275    else
276        newCrtcs = NULL;
277    free(output->crtcs);
278    memcpy(newCrtcs, crtcs, numCrtcs * sizeof(RRCrtcPtr));
279    output->crtcs = newCrtcs;
280    output->numCrtcs = numCrtcs;
281    RROutputChanged(output, TRUE);
282    return TRUE;
283}
284
285Bool
286RROutputSetConnection(RROutputPtr output, CARD8 connection)
287{
288    if (output->connection == connection)
289        return TRUE;
290    output->connection = connection;
291    RROutputChanged(output, TRUE);
292    return TRUE;
293}
294
295Bool
296RROutputSetSubpixelOrder(RROutputPtr output, int subpixelOrder)
297{
298    if (output->subpixelOrder == subpixelOrder)
299        return TRUE;
300
301    output->subpixelOrder = subpixelOrder;
302    RROutputChanged(output, FALSE);
303    return TRUE;
304}
305
306Bool
307RROutputSetPhysicalSize(RROutputPtr output, int mmWidth, int mmHeight)
308{
309    if (output->mmWidth == mmWidth && output->mmHeight == mmHeight)
310        return TRUE;
311    output->mmWidth = mmWidth;
312    output->mmHeight = mmHeight;
313    RROutputChanged(output, FALSE);
314    return TRUE;
315}
316
317void
318RRDeliverOutputEvent(ClientPtr client, WindowPtr pWin, RROutputPtr output)
319{
320    ScreenPtr pScreen = pWin->drawable.pScreen;
321
322    rrScrPriv(pScreen);
323    RRCrtcPtr crtc = output->crtc;
324    RRModePtr mode = crtc ? crtc->mode : NULL;
325
326    xRROutputChangeNotifyEvent oe = {
327        .type = RRNotify + RREventBase,
328        .subCode = RRNotify_OutputChange,
329        .timestamp = pScrPriv->lastSetTime.milliseconds,
330        .configTimestamp = pScrPriv->lastConfigTime.milliseconds,
331        .window = pWin->drawable.id,
332        .output = output->id,
333        .crtc = crtc ? crtc->id : None,
334        .mode = mode ? mode->mode.id : None,
335        .rotation = crtc ? crtc->rotation : RR_Rotate_0,
336        .connection = output->connection,
337        .subpixelOrder = output->subpixelOrder
338    };
339    WriteEventsToClient(client, 1, (xEvent *) &oe);
340}
341
342/*
343 * Destroy a Output at shutdown
344 */
345void
346RROutputDestroy(RROutputPtr output)
347{
348    FreeResource(output->id, 0);
349}
350
351static int
352RROutputDestroyResource(void *value, XID pid)
353{
354    RROutputPtr output = (RROutputPtr) value;
355    ScreenPtr pScreen = output->pScreen;
356    int m;
357
358    if (pScreen) {
359        rrScrPriv(pScreen);
360        int i;
361
362        if (pScrPriv->primaryOutput == output)
363            pScrPriv->primaryOutput = NULL;
364
365        for (i = 0; i < pScrPriv->numOutputs; i++) {
366            if (pScrPriv->outputs[i] == output) {
367                memmove(pScrPriv->outputs + i, pScrPriv->outputs + i + 1,
368                        (pScrPriv->numOutputs - (i + 1)) * sizeof(RROutputPtr));
369                --pScrPriv->numOutputs;
370                break;
371            }
372        }
373
374        RRResourcesChanged(pScreen);
375    }
376    if (output->modes) {
377        for (m = 0; m < output->numModes; m++)
378            RRModeDestroy(output->modes[m]);
379        free(output->modes);
380    }
381
382    for (m = 0; m < output->numUserModes; m++)
383        RRModeDestroy(output->userModes[m]);
384    free(output->userModes);
385
386    free(output->crtcs);
387    free(output->clones);
388    RRDeleteAllOutputProperties(output);
389    free(output);
390    return 1;
391}
392
393/*
394 * Initialize output type
395 */
396Bool
397RROutputInit(void)
398{
399    RROutputType = CreateNewResourceType(RROutputDestroyResource, "OUTPUT");
400    if (!RROutputType)
401        return FALSE;
402
403    return TRUE;
404}
405
406/*
407 * Initialize output type error value
408 */
409void
410RROutputInitErrorValue(void)
411{
412    SetResourceTypeErrorValue(RROutputType, RRErrorBase + BadRROutput);
413}
414
415#define OutputInfoExtra	(SIZEOF(xRRGetOutputInfoReply) - 32)
416
417int
418ProcRRGetOutputInfo(ClientPtr client)
419{
420    REQUEST(xRRGetOutputInfoReq);
421    xRRGetOutputInfoReply rep;
422    RROutputPtr output;
423    CARD8 *extra;
424    unsigned long extraLen;
425    ScreenPtr pScreen;
426    rrScrPrivPtr pScrPriv;
427    RRCrtc *crtcs;
428    RRMode *modes;
429    RROutput *clones;
430    char *name;
431    int i;
432
433    REQUEST_SIZE_MATCH(xRRGetOutputInfoReq);
434    VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
435
436    pScreen = output->pScreen;
437    pScrPriv = rrGetScrPriv(pScreen);
438
439    rep = (xRRGetOutputInfoReply) {
440        .type = X_Reply,
441        .status = RRSetConfigSuccess,
442        .sequenceNumber = client->sequence,
443        .length = bytes_to_int32(OutputInfoExtra),
444        .timestamp = pScrPriv->lastSetTime.milliseconds,
445        .crtc = output->crtc ? output->crtc->id : None,
446        .mmWidth = output->mmWidth,
447        .mmHeight = output->mmHeight,
448        .connection = output->connection,
449        .subpixelOrder = output->subpixelOrder,
450        .nCrtcs = output->numCrtcs,
451        .nModes = output->numModes + output->numUserModes,
452        .nPreferred = output->numPreferred,
453        .nClones = output->numClones,
454        .nameLength = output->nameLength
455    };
456    extraLen = ((output->numCrtcs +
457                 output->numModes + output->numUserModes +
458                 output->numClones + bytes_to_int32(rep.nameLength)) << 2);
459
460    if (extraLen) {
461        rep.length += bytes_to_int32(extraLen);
462        extra = malloc(extraLen);
463        if (!extra)
464            return BadAlloc;
465    }
466    else
467        extra = NULL;
468
469    crtcs = (RRCrtc *) extra;
470    modes = (RRMode *) (crtcs + output->numCrtcs);
471    clones = (RROutput *) (modes + output->numModes + output->numUserModes);
472    name = (char *) (clones + output->numClones);
473
474    for (i = 0; i < output->numCrtcs; i++) {
475        crtcs[i] = output->crtcs[i]->id;
476        if (client->swapped)
477            swapl(&crtcs[i]);
478    }
479    for (i = 0; i < output->numModes + output->numUserModes; i++) {
480        if (i < output->numModes)
481            modes[i] = output->modes[i]->mode.id;
482        else
483            modes[i] = output->userModes[i - output->numModes]->mode.id;
484        if (client->swapped)
485            swapl(&modes[i]);
486    }
487    for (i = 0; i < output->numClones; i++) {
488        clones[i] = output->clones[i]->id;
489        if (client->swapped)
490            swapl(&clones[i]);
491    }
492    memcpy(name, output->name, output->nameLength);
493    if (client->swapped) {
494        swaps(&rep.sequenceNumber);
495        swapl(&rep.length);
496        swapl(&rep.timestamp);
497        swapl(&rep.crtc);
498        swapl(&rep.mmWidth);
499        swapl(&rep.mmHeight);
500        swaps(&rep.nCrtcs);
501        swaps(&rep.nModes);
502        swaps(&rep.nPreferred);
503        swaps(&rep.nClones);
504        swaps(&rep.nameLength);
505    }
506    WriteToClient(client, sizeof(xRRGetOutputInfoReply), &rep);
507    if (extraLen) {
508        WriteToClient(client, extraLen, extra);
509        free(extra);
510    }
511
512    return Success;
513}
514
515static void
516RRSetPrimaryOutput(ScreenPtr pScreen, rrScrPrivPtr pScrPriv, RROutputPtr output)
517{
518    if (pScrPriv->primaryOutput == output)
519        return;
520
521    /* clear the old primary */
522    if (pScrPriv->primaryOutput) {
523        RROutputChanged(pScrPriv->primaryOutput, 0);
524        pScrPriv->primaryOutput = NULL;
525    }
526
527    /* set the new primary */
528    if (output) {
529        pScrPriv->primaryOutput = output;
530        RROutputChanged(output, 0);
531    }
532
533    pScrPriv->layoutChanged = TRUE;
534
535    RRTellChanged(pScreen);
536}
537
538int
539ProcRRSetOutputPrimary(ClientPtr client)
540{
541    REQUEST(xRRSetOutputPrimaryReq);
542    RROutputPtr output = NULL;
543    WindowPtr pWin;
544    rrScrPrivPtr pScrPriv;
545    int ret;
546    ScreenPtr slave;
547
548    REQUEST_SIZE_MATCH(xRRSetOutputPrimaryReq);
549
550    ret = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
551    if (ret != Success)
552        return ret;
553
554    if (stuff->output) {
555        VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
556
557        if (!output->pScreen->isGPU && output->pScreen != pWin->drawable.pScreen) {
558            client->errorValue = stuff->window;
559            return BadMatch;
560        }
561        if (output->pScreen->isGPU && output->pScreen->current_master != pWin->drawable.pScreen) {
562            client->errorValue = stuff->window;
563            return BadMatch;
564        }
565    }
566
567    pScrPriv = rrGetScrPriv(pWin->drawable.pScreen);
568    if (pScrPriv)
569    {
570        RRSetPrimaryOutput(pWin->drawable.pScreen, pScrPriv, output);
571
572        xorg_list_for_each_entry(slave,
573                                 &pWin->drawable.pScreen->output_slave_list,
574                                 output_head) {
575            rrScrPrivPtr pSlavePriv;
576            pSlavePriv = rrGetScrPriv(slave);
577
578            RRSetPrimaryOutput(slave, pSlavePriv, output);
579        }
580    }
581
582    return Success;
583}
584
585int
586ProcRRGetOutputPrimary(ClientPtr client)
587{
588    REQUEST(xRRGetOutputPrimaryReq);
589    WindowPtr pWin;
590    rrScrPrivPtr pScrPriv;
591    xRRGetOutputPrimaryReply rep;
592    RROutputPtr primary = NULL;
593    int rc;
594
595    REQUEST_SIZE_MATCH(xRRGetOutputPrimaryReq);
596
597    rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
598    if (rc != Success)
599        return rc;
600
601    pScrPriv = rrGetScrPriv(pWin->drawable.pScreen);
602    if (pScrPriv)
603        primary = pScrPriv->primaryOutput;
604
605    rep = (xRRGetOutputPrimaryReply) {
606        .type = X_Reply,
607        .sequenceNumber = client->sequence,
608        .output = primary ? primary->id : None
609    };
610
611    if (client->swapped) {
612        swaps(&rep.sequenceNumber);
613        swapl(&rep.output);
614    }
615
616    WriteToClient(client, sizeof(xRRGetOutputPrimaryReply), &rep);
617
618    return Success;
619}
620