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