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
27 RESTYPE RROutputType;
28
29 /*
30 * Notify the output of some change
31 */
32 void
RROutputChanged(RROutputPtr output,Bool configChanged)33 RROutputChanged(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
62 RROutputPtr
RROutputCreate(ScreenPtr pScreen,const char * name,int nameLength,void * devPrivate)63 RROutputCreate(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 */
132 Bool
RROutputSetClones(RROutputPtr output,RROutputPtr * clones,int numClones)133 RROutputSetClones(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
160 Bool
RROutputSetModes(RROutputPtr output,RRModePtr * modes,int numModes,int numPreferred)161 RROutputSetModes(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
198 int
RROutputAddUserMode(RROutputPtr output,RRModePtr mode)199 RROutputAddUserMode(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
237 int
RROutputDeleteUserMode(RROutputPtr output,RRModePtr mode)238 RROutputDeleteUserMode(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
264 Bool
RROutputSetCrtcs(RROutputPtr output,RRCrtcPtr * crtcs,int numCrtcs)265 RROutputSetCrtcs(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
292 Bool
RROutputSetConnection(RROutputPtr output,CARD8 connection)293 RROutputSetConnection(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
302 Bool
RROutputSetSubpixelOrder(RROutputPtr output,int subpixelOrder)303 RROutputSetSubpixelOrder(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
313 Bool
RROutputSetPhysicalSize(RROutputPtr output,int mmWidth,int mmHeight)314 RROutputSetPhysicalSize(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
324 Bool
RROutputSetNonDesktop(RROutputPtr output,Bool nonDesktop)325 RROutputSetNonDesktop(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
338 void
RRDeliverOutputEvent(ClientPtr client,WindowPtr pWin,RROutputPtr output)339 RRDeliverOutputEvent(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 */
366 void
RROutputDestroy(RROutputPtr output)367 RROutputDestroy(RROutputPtr output)
368 {
369 FreeResource(output->id, 0);
370 }
371
372 static int
RROutputDestroyResource(void * value,XID pid)373 RROutputDestroyResource(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 */
428 Bool
RROutputInit(void)429 RROutputInit(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 */
441 void
RROutputInitErrorValue(void)442 RROutputInitErrorValue(void)
443 {
444 SetResourceTypeErrorValue(RROutputType, RRErrorBase + BadRROutput);
445 }
446
447 #define OutputInfoExtra (SIZEOF(xRRGetOutputInfoReply) - 32)
448
449 int
ProcRRGetOutputInfo(ClientPtr client)450 ProcRRGetOutputInfo(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
581 static void
RRSetPrimaryOutput(ScreenPtr pScreen,rrScrPrivPtr pScrPriv,RROutputPtr output)582 RRSetPrimaryOutput(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
604 int
ProcRRSetOutputPrimary(ClientPtr client)605 ProcRRSetOutputPrimary(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
652 int
ProcRRGetOutputPrimary(ClientPtr client)653 ProcRRGetOutputPrimary(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