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 */
24 #include "randrstr.h"
26 RESTYPE RROutputType;
28 /*
29 * Notify the output of some change
30 */
31 void
32 RROutputChanged (RROutputPtr output, Bool configChanged)
33 {
34 ScreenPtr pScreen = output->pScreen;
36 output->changed = TRUE;
37 if (pScreen)
38 {
39 rrScrPriv (pScreen);
40 pScrPriv->changed = TRUE;
41 if (configChanged)
42 pScrPriv->configChanged = TRUE;
43 }
44 }
46 /*
47 * Create an output
48 */
50 RROutputPtr
51 RROutputCreate (ScreenPtr pScreen,
52 const char *name,
53 int nameLength,
54 void *devPrivate)
55 {
56 RROutputPtr output;
57 RROutputPtr *outputs;
58 rrScrPrivPtr pScrPriv;
60 if (!RRInit())
61 return NULL;
63 pScrPriv = rrGetScrPriv(pScreen);
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;
73 pScrPriv->outputs = outputs;
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;
103 if (!AddResource (output->id, RROutputType, (pointer) output))
104 return NULL;
106 pScrPriv->outputs[pScrPriv->numOutputs++] = output;
107 return output;
108 }
110 /*
111 * Notify extension that output parameters have been changed
112 */
113 Bool
114 RROutputSetClones (RROutputPtr output,
115 RROutputPtr *clones,
116 int numClones)
117 {
118 RROutputPtr *newClones;
119 int i;
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 }
145 Bool
146 RROutputSetModes (RROutputPtr output,
147 RRModePtr *modes,
148 int numModes,
149 int numPreferred)
150 {
151 RRModePtr *newModes;
152 int i;
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 }
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 }
189 int
190 RROutputAddUserMode (RROutputPtr output,
191 RRModePtr mode)
192 {
193 int m;
194 ScreenPtr pScreen = output->pScreen;
195 rrScrPriv(pScreen);
196 RRModePtr *newModes;
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 }
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;
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;
221 output->userModes = newModes;
222 output->userModes[output->numUserModes++] = mode;
223 ++mode->refcnt;
224 RROutputChanged (output, TRUE);
225 RRTellChanged (pScreen);
226 return Success;
227 }
229 int
230 RROutputDeleteUserMode (RROutputPtr output,
231 RRModePtr mode)
232 {
233 int m;
235 /* Find this mode in the user mode list */
236 for (m = 0; m < output->numUserModes; m++)
237 {
238 RRModePtr e = output->userModes[m];
240 if (mode == e)
241 break;
242 }
243 /* Not there, access error */
244 if (m == output->numUserModes)
245 return BadAccess;
247 /* make sure the mode isn't active for this output */
248 if (output->crtc && output->crtc->mode == mode)
249 return BadMatch;
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 }
258 Bool
259 RROutputSetCrtcs (RROutputPtr output,
260 RRCrtcPtr *crtcs,
261 int numCrtcs)
262 {
263 RRCrtcPtr *newCrtcs;
264 int i;
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 }
290 Bool
291 RROutputSetConnection (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 }
301 Bool
302 RROutputSetSubpixelOrder (RROutputPtr output,
303 int subpixelOrder)
304 {
305 if (output->subpixelOrder == subpixelOrder)
306 return TRUE;
308 output->subpixelOrder = subpixelOrder;
309 RROutputChanged (output, FALSE);
310 return TRUE;
311 }
313 Bool
314 RROutputSetPhysicalSize (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 }
327 void
328 RRDeliverOutputEvent(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;
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 }
359 /*
360 * Destroy a Output at shutdown
361 */
362 void
363 RROutputDestroy (RROutputPtr output)
364 {
365 FreeResource (output->id, 0);
366 }
368 static int
369 RROutputDestroyResource (pointer value, XID pid)
370 {
371 RROutputPtr output = (RROutputPtr) value;
372 ScreenPtr pScreen = output->pScreen;
373 int m;
375 if (pScreen)
376 {
377 rrScrPriv(pScreen);
378 int i;
380 if (pScrPriv->primaryOutput == output)
381 pScrPriv->primaryOutput = NULL;
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 }
401 for (m = 0; m < output->numUserModes; m++)
402 RRModeDestroy (output->userModes[m]);
403 free(output->userModes);
405 free(output->crtcs);
406 free(output->clones);
407 RRDeleteAllOutputProperties (output);
408 free(output);
409 return 1;
410 }
412 /*
413 * Initialize output type
414 */
415 Bool
416 RROutputInit (void)
417 {
418 RROutputType = CreateNewResourceType (RROutputDestroyResource, "OUTPUT");
419 if (!RROutputType)
420 return FALSE;
422 return TRUE;
423 }
425 /*
426 * Initialize output type error value
427 */
428 void
429 RROutputInitErrorValue(void)
430 {
431 SetResourceTypeErrorValue(RROutputType, RRErrorBase + BadRROutput);
432 }
434 #define OutputInfoExtra (SIZEOF(xRRGetOutputInfoReply) - 32)
436 int
437 ProcRRGetOutputInfo (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;
452 REQUEST_SIZE_MATCH(xRRGetOutputInfoReq);
453 VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
455 pScreen = output->pScreen;
456 pScrPriv = rrGetScrPriv(pScreen);
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;
473 extraLen = ((output->numCrtcs +
474 output->numModes + output->numUserModes +
475 output->numClones +
476 bytes_to_int32(rep.nameLength)) << 2);
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;
488 crtcs = (RRCrtc *) extra;
489 modes = (RRMode *) (crtcs + output->numCrtcs);
490 clones = (RROutput *) (modes + output->numModes + output->numUserModes);
491 name = (char *) (clones + output->numClones);
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 }
534 return Success;
535 }
537 static void
538 RRSetPrimaryOutput(ScreenPtr pScreen, rrScrPrivPtr pScrPriv,
539 RROutputPtr output)
540 {
541 if (pScrPriv->primaryOutput == output)
542 return;
544 /* clear the old primary */
545 if (pScrPriv->primaryOutput) {
546 RROutputChanged(pScrPriv->primaryOutput, 0);
547 pScrPriv->primaryOutput = NULL;
548 }
550 /* set the new primary */
551 if (output) {
552 pScrPriv->primaryOutput = output;
553 RROutputChanged(output, 0);
554 }
556 pScrPriv->layoutChanged = TRUE;
558 RRTellChanged(pScreen);
559 }
561 int
562 ProcRRSetOutputPrimary(ClientPtr client)
563 {
564 REQUEST(xRRSetOutputPrimaryReq);
565 RROutputPtr output = NULL;
566 WindowPtr pWin;
567 rrScrPrivPtr pScrPriv;
568 int rc;
570 REQUEST_SIZE_MATCH(xRRSetOutputPrimaryReq);
572 rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
573 if (rc != Success)
574 return rc;
576 if (stuff->output) {
577 VERIFY_RR_OUTPUT(stuff->output, output, DixReadAccess);
579 if (output->pScreen != pWin->drawable.pScreen) {
580 client->errorValue = stuff->window;
581 return BadMatch;
582 }
583 }
585 pScrPriv = rrGetScrPriv(pWin->drawable.pScreen);
586 if (pScrPriv)
587 RRSetPrimaryOutput(pWin->drawable.pScreen, pScrPriv, output);
589 return Success;
590 }
592 int
593 ProcRRGetOutputPrimary(ClientPtr client)
594 {
595 REQUEST(xRRGetOutputPrimaryReq);
596 WindowPtr pWin;
597 rrScrPrivPtr pScrPriv;
598 xRRGetOutputPrimaryReply rep;
599 RROutputPtr primary = NULL;
600 int rc;
602 REQUEST_SIZE_MATCH(xRRGetOutputPrimaryReq);
604 rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
605 if (rc != Success)
606 return rc;
608 pScrPriv = rrGetScrPriv(pWin->drawable.pScreen);
609 if (pScrPriv)
610 primary = pScrPriv->primaryOutput;
612 memset(&rep, 0, sizeof(rep));
613 rep.type = X_Reply;
614 rep.sequenceNumber = client->sequence;
615 rep.output = primary ? primary->id : None;
617 if (client->swapped) {
618 int n;
619 swaps(&rep.sequenceNumber, n);
620 swapl(&rep.output, n);
621 }
623 WriteToClient(client, sizeof(xRRGetOutputPrimaryReply), &rep);
625 return Success;
626 }