Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /* |
2 | * Copyright © 2006 Keith Packard | |
3 | * Copyright 2010 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 "swaprep.h" | |
26 | #include "mipointer.h" | |
27 | ||
28 | RESTYPE RRCrtcType; | |
29 | ||
30 | /* | |
31 | * Notify the CRTC of some change | |
32 | */ | |
33 | void | |
34 | RRCrtcChanged(RRCrtcPtr crtc, Bool layoutChanged) | |
35 | { | |
36 | ScreenPtr pScreen = crtc->pScreen; | |
37 | ||
38 | crtc->changed = TRUE; | |
39 | if (pScreen) { | |
40 | rrScrPriv(pScreen); | |
41 | ||
42 | RRSetChanged(pScreen); | |
43 | /* | |
44 | * Send ConfigureNotify on any layout change | |
45 | */ | |
46 | if (layoutChanged) | |
47 | pScrPriv->layoutChanged = TRUE; | |
48 | } | |
49 | } | |
50 | ||
51 | /* | |
52 | * Create a CRTC | |
53 | */ | |
54 | RRCrtcPtr | |
55 | RRCrtcCreate(ScreenPtr pScreen, void *devPrivate) | |
56 | { | |
57 | RRCrtcPtr crtc; | |
58 | RRCrtcPtr *crtcs; | |
59 | rrScrPrivPtr pScrPriv; | |
60 | ||
61 | if (!RRInit()) | |
62 | return NULL; | |
63 | ||
64 | pScrPriv = rrGetScrPriv(pScreen); | |
65 | ||
66 | /* make space for the crtc pointer */ | |
67 | if (pScrPriv->numCrtcs) | |
68 | crtcs = realloc(pScrPriv->crtcs, | |
69 | (pScrPriv->numCrtcs + 1) * sizeof(RRCrtcPtr)); | |
70 | else | |
71 | crtcs = malloc(sizeof(RRCrtcPtr)); | |
72 | if (!crtcs) | |
73 | return FALSE; | |
74 | pScrPriv->crtcs = crtcs; | |
75 | ||
76 | crtc = calloc(1, sizeof(RRCrtcRec)); | |
77 | if (!crtc) | |
78 | return NULL; | |
79 | crtc->id = FakeClientID(0); | |
80 | crtc->pScreen = pScreen; | |
81 | crtc->mode = NULL; | |
82 | crtc->x = 0; | |
83 | crtc->y = 0; | |
84 | crtc->rotation = RR_Rotate_0; | |
85 | crtc->rotations = RR_Rotate_0; | |
86 | crtc->outputs = NULL; | |
87 | crtc->numOutputs = 0; | |
88 | crtc->gammaSize = 0; | |
89 | crtc->gammaRed = crtc->gammaBlue = crtc->gammaGreen = NULL; | |
90 | crtc->changed = FALSE; | |
91 | crtc->devPrivate = devPrivate; | |
92 | RRTransformInit(&crtc->client_pending_transform); | |
93 | RRTransformInit(&crtc->client_current_transform); | |
94 | pixman_transform_init_identity(&crtc->transform); | |
95 | pixman_f_transform_init_identity(&crtc->f_transform); | |
96 | pixman_f_transform_init_identity(&crtc->f_inverse); | |
97 | ||
98 | if (!AddResource(crtc->id, RRCrtcType, (pointer) crtc)) | |
99 | return NULL; | |
100 | ||
101 | /* attach the screen and crtc together */ | |
102 | crtc->pScreen = pScreen; | |
103 | pScrPriv->crtcs[pScrPriv->numCrtcs++] = crtc; | |
104 | ||
105 | RRResourcesChanged(pScreen); | |
106 | ||
107 | return crtc; | |
108 | } | |
109 | ||
110 | /* | |
111 | * Set the allowed rotations on a CRTC | |
112 | */ | |
113 | void | |
114 | RRCrtcSetRotations(RRCrtcPtr crtc, Rotation rotations) | |
115 | { | |
116 | crtc->rotations = rotations; | |
117 | } | |
118 | ||
119 | /* | |
120 | * Set whether transforms are allowed on a CRTC | |
121 | */ | |
122 | void | |
123 | RRCrtcSetTransformSupport(RRCrtcPtr crtc, Bool transforms) | |
124 | { | |
125 | crtc->transforms = transforms; | |
126 | } | |
127 | ||
128 | /* | |
129 | * Notify the extension that the Crtc has been reconfigured, | |
130 | * the driver calls this whenever it has updated the mode | |
131 | */ | |
132 | Bool | |
133 | RRCrtcNotify(RRCrtcPtr crtc, | |
134 | RRModePtr mode, | |
135 | int x, | |
136 | int y, | |
137 | Rotation rotation, | |
138 | RRTransformPtr transform, int numOutputs, RROutputPtr * outputs) | |
139 | { | |
140 | int i, j; | |
141 | ||
142 | /* | |
143 | * Check to see if any of the new outputs were | |
144 | * not in the old list and mark them as changed | |
145 | */ | |
146 | for (i = 0; i < numOutputs; i++) { | |
147 | for (j = 0; j < crtc->numOutputs; j++) | |
148 | if (outputs[i] == crtc->outputs[j]) | |
149 | break; | |
150 | if (j == crtc->numOutputs) { | |
151 | outputs[i]->crtc = crtc; | |
152 | RROutputChanged(outputs[i], FALSE); | |
153 | RRCrtcChanged(crtc, FALSE); | |
154 | } | |
155 | } | |
156 | /* | |
157 | * Check to see if any of the old outputs are | |
158 | * not in the new list and mark them as changed | |
159 | */ | |
160 | for (j = 0; j < crtc->numOutputs; j++) { | |
161 | for (i = 0; i < numOutputs; i++) | |
162 | if (outputs[i] == crtc->outputs[j]) | |
163 | break; | |
164 | if (i == numOutputs) { | |
165 | if (crtc->outputs[j]->crtc == crtc) | |
166 | crtc->outputs[j]->crtc = NULL; | |
167 | RROutputChanged(crtc->outputs[j], FALSE); | |
168 | RRCrtcChanged(crtc, FALSE); | |
169 | } | |
170 | } | |
171 | /* | |
172 | * Reallocate the crtc output array if necessary | |
173 | */ | |
174 | if (numOutputs != crtc->numOutputs) { | |
175 | RROutputPtr *newoutputs; | |
176 | ||
177 | if (numOutputs) { | |
178 | if (crtc->numOutputs) | |
179 | newoutputs = realloc(crtc->outputs, | |
180 | numOutputs * sizeof(RROutputPtr)); | |
181 | else | |
182 | newoutputs = malloc(numOutputs * sizeof(RROutputPtr)); | |
183 | if (!newoutputs) | |
184 | return FALSE; | |
185 | } | |
186 | else { | |
187 | free(crtc->outputs); | |
188 | newoutputs = NULL; | |
189 | } | |
190 | crtc->outputs = newoutputs; | |
191 | crtc->numOutputs = numOutputs; | |
192 | } | |
193 | /* | |
194 | * Copy the new list of outputs into the crtc | |
195 | */ | |
196 | memcpy(crtc->outputs, outputs, numOutputs * sizeof(RROutputPtr)); | |
197 | /* | |
198 | * Update remaining crtc fields | |
199 | */ | |
200 | if (mode != crtc->mode) { | |
201 | if (crtc->mode) | |
202 | RRModeDestroy(crtc->mode); | |
203 | crtc->mode = mode; | |
204 | if (mode != NULL) | |
205 | mode->refcnt++; | |
206 | RRCrtcChanged(crtc, TRUE); | |
207 | } | |
208 | if (x != crtc->x) { | |
209 | crtc->x = x; | |
210 | RRCrtcChanged(crtc, TRUE); | |
211 | } | |
212 | if (y != crtc->y) { | |
213 | crtc->y = y; | |
214 | RRCrtcChanged(crtc, TRUE); | |
215 | } | |
216 | if (rotation != crtc->rotation) { | |
217 | crtc->rotation = rotation; | |
218 | RRCrtcChanged(crtc, TRUE); | |
219 | } | |
220 | if (!RRTransformEqual(transform, &crtc->client_current_transform)) { | |
221 | RRTransformCopy(&crtc->client_current_transform, transform); | |
222 | RRCrtcChanged(crtc, TRUE); | |
223 | } | |
224 | if (crtc->changed && mode) { | |
225 | RRTransformCompute(x, y, | |
226 | mode->mode.width, mode->mode.height, | |
227 | rotation, | |
228 | &crtc->client_current_transform, | |
229 | &crtc->transform, &crtc->f_transform, | |
230 | &crtc->f_inverse); | |
231 | } | |
232 | return TRUE; | |
233 | } | |
234 | ||
235 | void | |
236 | RRDeliverCrtcEvent(ClientPtr client, WindowPtr pWin, RRCrtcPtr crtc) | |
237 | { | |
238 | ScreenPtr pScreen = pWin->drawable.pScreen; | |
239 | ||
240 | rrScrPriv(pScreen); | |
241 | RRModePtr mode = crtc->mode; | |
242 | ||
243 | xRRCrtcChangeNotifyEvent ce = { | |
244 | .type = RRNotify + RREventBase, | |
245 | .subCode = RRNotify_CrtcChange, | |
246 | .timestamp = pScrPriv->lastSetTime.milliseconds, | |
247 | .window = pWin->drawable.id, | |
248 | .crtc = crtc->id, | |
249 | .mode = mode ? mode->mode.id : None, | |
250 | .rotation = crtc->rotation, | |
251 | .x = mode ? crtc->x : 0, | |
252 | .y = mode ? crtc->y : 0, | |
253 | .width = mode ? mode->mode.width : 0, | |
254 | .height = mode ? mode->mode.height : 0 | |
255 | }; | |
256 | WriteEventsToClient(client, 1, (xEvent *) &ce); | |
257 | } | |
258 | ||
259 | static Bool | |
260 | RRCrtcPendingProperties(RRCrtcPtr crtc) | |
261 | { | |
262 | ScreenPtr pScreen = crtc->pScreen; | |
263 | ||
264 | rrScrPriv(pScreen); | |
265 | int o; | |
266 | ||
267 | for (o = 0; o < pScrPriv->numOutputs; o++) { | |
268 | RROutputPtr output = pScrPriv->outputs[o]; | |
269 | ||
270 | if (output->crtc == crtc && output->pendingProperties) | |
271 | return TRUE; | |
272 | } | |
273 | return FALSE; | |
274 | } | |
275 | ||
276 | static void | |
277 | crtc_bounds(RRCrtcPtr crtc, int *left, int *right, int *top, int *bottom) | |
278 | { | |
279 | *left = crtc->x; | |
280 | *top = crtc->y; | |
281 | ||
282 | switch (crtc->rotation) { | |
283 | case RR_Rotate_0: | |
284 | case RR_Rotate_180: | |
285 | default: | |
286 | *right = crtc->x + crtc->mode->mode.width; | |
287 | *bottom = crtc->y + crtc->mode->mode.height; | |
288 | return; | |
289 | case RR_Rotate_90: | |
290 | case RR_Rotate_270: | |
291 | *right = crtc->x + crtc->mode->mode.height; | |
292 | *bottom = crtc->y + crtc->mode->mode.width; | |
293 | return; | |
294 | } | |
295 | } | |
296 | ||
297 | /* overlapping counts as adjacent */ | |
298 | static Bool | |
299 | crtcs_adjacent(const RRCrtcPtr a, const RRCrtcPtr b) | |
300 | { | |
301 | /* left, right, top, bottom... */ | |
302 | int al, ar, at, ab; | |
303 | int bl, br, bt, bb; | |
304 | int cl, cr, ct, cb; /* the overlap, if any */ | |
305 | ||
306 | crtc_bounds(a, &al, &ar, &at, &ab); | |
307 | crtc_bounds(b, &bl, &br, &bt, &bb); | |
308 | ||
309 | cl = max(al, bl); | |
310 | cr = min(ar, br); | |
311 | ct = max(at, bt); | |
312 | cb = min(ab, bb); | |
313 | ||
314 | return (cl <= cr) && (ct <= cb); | |
315 | } | |
316 | ||
317 | /* Depth-first search and mark all CRTCs reachable from cur */ | |
318 | static void | |
319 | mark_crtcs(rrScrPrivPtr pScrPriv, int *reachable, int cur) | |
320 | { | |
321 | int i; | |
322 | ||
323 | reachable[cur] = TRUE; | |
324 | for (i = 0; i < pScrPriv->numCrtcs; ++i) { | |
325 | if (reachable[i] || !pScrPriv->crtcs[i]->mode) | |
326 | continue; | |
327 | if (crtcs_adjacent(pScrPriv->crtcs[cur], pScrPriv->crtcs[i])) | |
328 | mark_crtcs(pScrPriv, reachable, i); | |
329 | } | |
330 | } | |
331 | ||
332 | static void | |
333 | RRComputeContiguity(ScreenPtr pScreen) | |
334 | { | |
335 | rrScrPriv(pScreen); | |
336 | Bool discontiguous = TRUE; | |
337 | int i, n = pScrPriv->numCrtcs; | |
338 | ||
339 | int *reachable = calloc(n, sizeof(int)); | |
340 | ||
341 | if (!reachable) | |
342 | goto out; | |
343 | ||
344 | /* Find first enabled CRTC and start search for reachable CRTCs from it */ | |
345 | for (i = 0; i < n; ++i) { | |
346 | if (pScrPriv->crtcs[i]->mode) { | |
347 | mark_crtcs(pScrPriv, reachable, i); | |
348 | break; | |
349 | } | |
350 | } | |
351 | ||
352 | /* Check that all enabled CRTCs were marked as reachable */ | |
353 | for (i = 0; i < n; ++i) | |
354 | if (pScrPriv->crtcs[i]->mode && !reachable[i]) | |
355 | goto out; | |
356 | ||
357 | discontiguous = FALSE; | |
358 | ||
359 | out: | |
360 | free(reachable); | |
361 | pScrPriv->discontiguous = discontiguous; | |
362 | } | |
363 | ||
364 | void | |
365 | RRCrtcDetachScanoutPixmap(RRCrtcPtr crtc) | |
366 | { | |
367 | ScreenPtr master = crtc->pScreen->current_master; | |
368 | PixmapPtr mscreenpix; | |
369 | rrScrPriv(crtc->pScreen); | |
370 | ||
371 | mscreenpix = master->GetScreenPixmap(master); | |
372 | ||
373 | pScrPriv->rrCrtcSetScanoutPixmap(crtc, NULL); | |
374 | if (crtc->scanout_pixmap) { | |
375 | master->StopPixmapTracking(mscreenpix, crtc->scanout_pixmap); | |
376 | /* | |
377 | * Unref the pixmap twice: once for the original reference, and once | |
378 | * for the reference implicitly added by PixmapShareToSlave. | |
379 | */ | |
380 | master->DestroyPixmap(crtc->scanout_pixmap->master_pixmap); | |
381 | master->DestroyPixmap(crtc->scanout_pixmap->master_pixmap); | |
382 | crtc->pScreen->DestroyPixmap(crtc->scanout_pixmap); | |
383 | } | |
384 | crtc->scanout_pixmap = NULL; | |
385 | RRCrtcChanged(crtc, TRUE); | |
386 | } | |
387 | ||
388 | static Bool | |
389 | rrCreateSharedPixmap(RRCrtcPtr crtc, int width, int height, | |
390 | int x, int y) | |
391 | { | |
392 | PixmapPtr mpix, spix; | |
393 | ScreenPtr master = crtc->pScreen->current_master; | |
394 | Bool ret; | |
395 | int depth; | |
396 | PixmapPtr mscreenpix; | |
397 | PixmapPtr protopix = crtc->pScreen->current_master->GetScreenPixmap(crtc->pScreen->current_master); | |
398 | rrScrPriv(crtc->pScreen); | |
399 | ||
400 | /* create a pixmap on the master screen, | |
401 | then get a shared handle for it | |
402 | create a shared pixmap on the slave screen using the handle | |
403 | set the master screen to do dirty updates to the shared pixmap | |
404 | from the screen pixmap. | |
405 | set slave screen to scanout shared linear pixmap | |
406 | */ | |
407 | ||
408 | mscreenpix = master->GetScreenPixmap(master); | |
409 | depth = protopix->drawable.depth; | |
410 | ||
411 | if (crtc->scanout_pixmap) | |
412 | RRCrtcDetachScanoutPixmap(crtc); | |
413 | ||
414 | if (width == 0 && height == 0) { | |
415 | return TRUE; | |
416 | } | |
417 | ||
418 | mpix = master->CreatePixmap(master, width, height, depth, | |
419 | CREATE_PIXMAP_USAGE_SHARED); | |
420 | if (!mpix) | |
421 | return FALSE; | |
422 | ||
423 | spix = PixmapShareToSlave(mpix, crtc->pScreen); | |
424 | if (spix == NULL) { | |
425 | master->DestroyPixmap(mpix); | |
426 | return FALSE; | |
427 | } | |
428 | ||
429 | ret = pScrPriv->rrCrtcSetScanoutPixmap(crtc, spix); | |
430 | if (ret == FALSE) { | |
431 | ErrorF("failed to set shadow slave pixmap\n"); | |
432 | return FALSE; | |
433 | } | |
434 | ||
435 | crtc->scanout_pixmap = spix; | |
436 | ||
437 | master->StartPixmapTracking(mscreenpix, spix, x, y); | |
438 | return TRUE; | |
439 | } | |
440 | ||
441 | static Bool | |
442 | rrCheckPixmapBounding(ScreenPtr pScreen, | |
443 | RRCrtcPtr rr_crtc, int x, int y, int w, int h) | |
444 | { | |
445 | RegionRec root_pixmap_region, total_region, new_crtc_region; | |
446 | int c; | |
447 | BoxRec newbox; | |
448 | BoxPtr newsize; | |
449 | ScreenPtr slave; | |
450 | int new_width, new_height; | |
451 | PixmapPtr screen_pixmap = pScreen->GetScreenPixmap(pScreen); | |
452 | rrScrPriv(pScreen); | |
453 | ||
454 | PixmapRegionInit(&root_pixmap_region, screen_pixmap); | |
455 | RegionInit(&total_region, NULL, 0); | |
456 | ||
457 | /* have to iterate all the crtcs of the attached gpu masters | |
458 | and all their output slaves */ | |
459 | for (c = 0; c < pScrPriv->numCrtcs; c++) { | |
460 | if (pScrPriv->crtcs[c] == rr_crtc) { | |
461 | newbox.x1 = x; | |
462 | newbox.x2 = x + w; | |
463 | newbox.y1 = y; | |
464 | newbox.y2 = y + h; | |
465 | } else { | |
466 | if (!pScrPriv->crtcs[c]->mode) | |
467 | continue; | |
468 | newbox.x1 = pScrPriv->crtcs[c]->x; | |
469 | newbox.x2 = pScrPriv->crtcs[c]->x + pScrPriv->crtcs[c]->mode->mode.width; | |
470 | newbox.y1 = pScrPriv->crtcs[c]->y; | |
471 | newbox.y2 = pScrPriv->crtcs[c]->y + pScrPriv->crtcs[c]->mode->mode.height; | |
472 | } | |
473 | RegionInit(&new_crtc_region, &newbox, 1); | |
474 | RegionUnion(&total_region, &total_region, &new_crtc_region); | |
475 | } | |
476 | ||
477 | xorg_list_for_each_entry(slave, &pScreen->output_slave_list, output_head) { | |
478 | rrScrPriv(slave); | |
479 | for (c = 0; c < pScrPriv->numCrtcs; c++) | |
480 | if (pScrPriv->crtcs[c] == rr_crtc) { | |
481 | newbox.x1 = x; | |
482 | newbox.x2 = x + w; | |
483 | newbox.y1 = y; | |
484 | newbox.y2 = y + h; | |
485 | } | |
486 | else { | |
487 | if (!pScrPriv->crtcs[c]->mode) | |
488 | continue; | |
489 | newbox.x1 = pScrPriv->crtcs[c]->x; | |
490 | newbox.x2 = pScrPriv->crtcs[c]->x + pScrPriv->crtcs[c]->mode->mode.width; | |
491 | newbox.y1 = pScrPriv->crtcs[c]->y; | |
492 | newbox.y2 = pScrPriv->crtcs[c]->y + pScrPriv->crtcs[c]->mode->mode.height; | |
493 | } | |
494 | RegionInit(&new_crtc_region, &newbox, 1); | |
495 | RegionUnion(&total_region, &total_region, &new_crtc_region); | |
496 | } | |
497 | ||
498 | newsize = RegionExtents(&total_region); | |
499 | new_width = newsize->x2 - newsize->x1; | |
500 | new_height = newsize->y2 - newsize->y1; | |
501 | ||
502 | if (new_width == screen_pixmap->drawable.width && | |
503 | new_height == screen_pixmap->drawable.height) { | |
504 | ErrorF("adjust shatters %d %d\n", newsize->x1, newsize->x2); | |
505 | } else { | |
506 | rrScrPriv(pScreen); | |
507 | pScrPriv->rrScreenSetSize(pScreen, new_width, new_height, 0, 0); | |
508 | } | |
509 | ||
510 | /* set shatters TODO */ | |
511 | return TRUE; | |
512 | } | |
513 | ||
514 | /* | |
515 | * Request that the Crtc be reconfigured | |
516 | */ | |
517 | Bool | |
518 | RRCrtcSet(RRCrtcPtr crtc, | |
519 | RRModePtr mode, | |
520 | int x, | |
521 | int y, Rotation rotation, int numOutputs, RROutputPtr * outputs) | |
522 | { | |
523 | ScreenPtr pScreen = crtc->pScreen; | |
524 | Bool ret = FALSE; | |
525 | Bool recompute = TRUE; | |
526 | ||
527 | rrScrPriv(pScreen); | |
528 | ||
529 | /* See if nothing changed */ | |
530 | if (crtc->mode == mode && | |
531 | crtc->x == x && | |
532 | crtc->y == y && | |
533 | crtc->rotation == rotation && | |
534 | crtc->numOutputs == numOutputs && | |
535 | !memcmp(crtc->outputs, outputs, numOutputs * sizeof(RROutputPtr)) && | |
536 | !RRCrtcPendingProperties(crtc) && !RRCrtcPendingTransform(crtc)) { | |
537 | recompute = FALSE; | |
538 | ret = TRUE; | |
539 | } | |
540 | else { | |
541 | if (pScreen->isGPU) { | |
542 | ScreenPtr master = pScreen->current_master; | |
543 | int width = 0, height = 0; | |
544 | ||
545 | if (mode) { | |
546 | width = mode->mode.width; | |
547 | height = mode->mode.height; | |
548 | } | |
549 | ErrorF("have a master to look out for\n"); | |
550 | ret = rrCheckPixmapBounding(master, crtc, | |
551 | x, y, width, height); | |
552 | if (!ret) | |
553 | return FALSE; | |
554 | ||
555 | if (pScreen->current_master) { | |
556 | ret = rrCreateSharedPixmap(crtc, width, height, x, y); | |
557 | ErrorF("need to create shared pixmap %d", ret); | |
558 | ||
559 | } | |
560 | } | |
561 | #if RANDR_12_INTERFACE | |
562 | if (pScrPriv->rrCrtcSet) { | |
563 | ret = (*pScrPriv->rrCrtcSet) (pScreen, crtc, mode, x, y, | |
564 | rotation, numOutputs, outputs); | |
565 | } | |
566 | else | |
567 | #endif | |
568 | { | |
569 | #if RANDR_10_INTERFACE | |
570 | if (pScrPriv->rrSetConfig) { | |
571 | RRScreenSize size; | |
572 | RRScreenRate rate; | |
573 | ||
574 | if (!mode) { | |
575 | RRCrtcNotify(crtc, NULL, x, y, rotation, NULL, 0, NULL); | |
576 | ret = TRUE; | |
577 | } | |
578 | else { | |
579 | size.width = mode->mode.width; | |
580 | size.height = mode->mode.height; | |
581 | if (outputs[0]->mmWidth && outputs[0]->mmHeight) { | |
582 | size.mmWidth = outputs[0]->mmWidth; | |
583 | size.mmHeight = outputs[0]->mmHeight; | |
584 | } | |
585 | else { | |
586 | size.mmWidth = pScreen->mmWidth; | |
587 | size.mmHeight = pScreen->mmHeight; | |
588 | } | |
589 | size.nRates = 1; | |
590 | rate.rate = RRVerticalRefresh(&mode->mode); | |
591 | size.pRates = &rate; | |
592 | ret = | |
593 | (*pScrPriv->rrSetConfig) (pScreen, rotation, rate.rate, | |
594 | &size); | |
595 | /* | |
596 | * Old 1.0 interface tied screen size to mode size | |
597 | */ | |
598 | if (ret) { | |
599 | RRCrtcNotify(crtc, mode, x, y, rotation, NULL, 1, | |
600 | outputs); | |
601 | RRScreenSizeNotify(pScreen); | |
602 | } | |
603 | } | |
604 | } | |
605 | #endif | |
606 | } | |
607 | if (ret) { | |
608 | int o; | |
609 | ||
610 | RRTellChanged(pScreen); | |
611 | ||
612 | for (o = 0; o < numOutputs; o++) | |
613 | RRPostPendingProperties(outputs[o]); | |
614 | } | |
615 | } | |
616 | ||
617 | if (recompute) | |
618 | RRComputeContiguity(pScreen); | |
619 | ||
620 | return ret; | |
621 | } | |
622 | ||
623 | /* | |
624 | * Return crtc transform | |
625 | */ | |
626 | RRTransformPtr | |
627 | RRCrtcGetTransform(RRCrtcPtr crtc) | |
628 | { | |
629 | RRTransformPtr transform = &crtc->client_pending_transform; | |
630 | ||
631 | if (pixman_transform_is_identity(&transform->transform)) | |
632 | return NULL; | |
633 | return transform; | |
634 | } | |
635 | ||
636 | /* | |
637 | * Check whether the pending and current transforms are the same | |
638 | */ | |
639 | Bool | |
640 | RRCrtcPendingTransform(RRCrtcPtr crtc) | |
641 | { | |
642 | return memcmp(&crtc->client_current_transform.transform, | |
643 | &crtc->client_pending_transform.transform, | |
644 | sizeof(PictTransform)) != 0; | |
645 | } | |
646 | ||
647 | /* | |
648 | * Destroy a Crtc at shutdown | |
649 | */ | |
650 | void | |
651 | RRCrtcDestroy(RRCrtcPtr crtc) | |
652 | { | |
653 | FreeResource(crtc->id, 0); | |
654 | } | |
655 | ||
656 | static int | |
657 | RRCrtcDestroyResource(pointer value, XID pid) | |
658 | { | |
659 | RRCrtcPtr crtc = (RRCrtcPtr) value; | |
660 | ScreenPtr pScreen = crtc->pScreen; | |
661 | ||
662 | if (pScreen) { | |
663 | rrScrPriv(pScreen); | |
664 | int i; | |
665 | ||
666 | for (i = 0; i < pScrPriv->numCrtcs; i++) { | |
667 | if (pScrPriv->crtcs[i] == crtc) { | |
668 | memmove(pScrPriv->crtcs + i, pScrPriv->crtcs + i + 1, | |
669 | (pScrPriv->numCrtcs - (i + 1)) * sizeof(RRCrtcPtr)); | |
670 | --pScrPriv->numCrtcs; | |
671 | break; | |
672 | } | |
673 | } | |
674 | ||
675 | RRResourcesChanged(pScreen); | |
676 | } | |
677 | ||
678 | if (crtc->scanout_pixmap) | |
679 | RRCrtcDetachScanoutPixmap(crtc); | |
680 | free(crtc->gammaRed); | |
681 | if (crtc->mode) | |
682 | RRModeDestroy(crtc->mode); | |
683 | free(crtc); | |
684 | return 1; | |
685 | } | |
686 | ||
687 | /* | |
688 | * Request that the Crtc gamma be changed | |
689 | */ | |
690 | ||
691 | Bool | |
692 | RRCrtcGammaSet(RRCrtcPtr crtc, CARD16 *red, CARD16 *green, CARD16 *blue) | |
693 | { | |
694 | Bool ret = TRUE; | |
695 | ||
696 | #if RANDR_12_INTERFACE | |
697 | ScreenPtr pScreen = crtc->pScreen; | |
698 | #endif | |
699 | ||
700 | memcpy(crtc->gammaRed, red, crtc->gammaSize * sizeof(CARD16)); | |
701 | memcpy(crtc->gammaGreen, green, crtc->gammaSize * sizeof(CARD16)); | |
702 | memcpy(crtc->gammaBlue, blue, crtc->gammaSize * sizeof(CARD16)); | |
703 | #if RANDR_12_INTERFACE | |
704 | if (pScreen) { | |
705 | rrScrPriv(pScreen); | |
706 | if (pScrPriv->rrCrtcSetGamma) | |
707 | ret = (*pScrPriv->rrCrtcSetGamma) (pScreen, crtc); | |
708 | } | |
709 | #endif | |
710 | return ret; | |
711 | } | |
712 | ||
713 | /* | |
714 | * Request current gamma back from the DDX (if possible). | |
715 | * This includes gamma size. | |
716 | */ | |
717 | Bool | |
718 | RRCrtcGammaGet(RRCrtcPtr crtc) | |
719 | { | |
720 | Bool ret = TRUE; | |
721 | ||
722 | #if RANDR_12_INTERFACE | |
723 | ScreenPtr pScreen = crtc->pScreen; | |
724 | #endif | |
725 | ||
726 | #if RANDR_12_INTERFACE | |
727 | if (pScreen) { | |
728 | rrScrPriv(pScreen); | |
729 | if (pScrPriv->rrCrtcGetGamma) | |
730 | ret = (*pScrPriv->rrCrtcGetGamma) (pScreen, crtc); | |
731 | } | |
732 | #endif | |
733 | return ret; | |
734 | } | |
735 | ||
736 | /* | |
737 | * Notify the extension that the Crtc gamma has been changed | |
738 | * The driver calls this whenever it has changed the gamma values | |
739 | * in the RRCrtcRec | |
740 | */ | |
741 | ||
742 | Bool | |
743 | RRCrtcGammaNotify(RRCrtcPtr crtc) | |
744 | { | |
745 | return TRUE; /* not much going on here */ | |
746 | } | |
747 | ||
748 | static void | |
749 | RRModeGetScanoutSize(RRModePtr mode, PictTransformPtr transform, | |
750 | int *width, int *height) | |
751 | { | |
752 | BoxRec box; | |
753 | ||
754 | if (mode == NULL) { | |
755 | *width = 0; | |
756 | *height = 0; | |
757 | return; | |
758 | } | |
759 | ||
760 | box.x1 = 0; | |
761 | box.y1 = 0; | |
762 | box.x2 = mode->mode.width; | |
763 | box.y2 = mode->mode.height; | |
764 | ||
765 | pixman_transform_bounds(transform, &box); | |
766 | *width = box.x2 - box.x1; | |
767 | *height = box.y2 - box.y1; | |
768 | } | |
769 | ||
770 | /** | |
771 | * Returns the width/height that the crtc scans out from the framebuffer | |
772 | */ | |
773 | void | |
774 | RRCrtcGetScanoutSize(RRCrtcPtr crtc, int *width, int *height) | |
775 | { | |
776 | RRModeGetScanoutSize(crtc->mode, &crtc->transform, width, height); | |
777 | } | |
778 | ||
779 | /* | |
780 | * Set the size of the gamma table at server startup time | |
781 | */ | |
782 | ||
783 | Bool | |
784 | RRCrtcGammaSetSize(RRCrtcPtr crtc, int size) | |
785 | { | |
786 | CARD16 *gamma; | |
787 | ||
788 | if (size == crtc->gammaSize) | |
789 | return TRUE; | |
790 | if (size) { | |
791 | gamma = malloc(size * 3 * sizeof(CARD16)); | |
792 | if (!gamma) | |
793 | return FALSE; | |
794 | } | |
795 | else | |
796 | gamma = NULL; | |
797 | free(crtc->gammaRed); | |
798 | crtc->gammaRed = gamma; | |
799 | crtc->gammaGreen = gamma + size; | |
800 | crtc->gammaBlue = gamma + size * 2; | |
801 | crtc->gammaSize = size; | |
802 | return TRUE; | |
803 | } | |
804 | ||
805 | /* | |
806 | * Set the pending CRTC transformation | |
807 | */ | |
808 | ||
809 | int | |
810 | RRCrtcTransformSet(RRCrtcPtr crtc, | |
811 | PictTransformPtr transform, | |
812 | struct pixman_f_transform *f_transform, | |
813 | struct pixman_f_transform *f_inverse, | |
814 | char *filter_name, | |
815 | int filter_len, xFixed * params, int nparams) | |
816 | { | |
817 | PictFilterPtr filter = NULL; | |
818 | int width = 0, height = 0; | |
819 | ||
820 | if (!crtc->transforms) | |
821 | return BadValue; | |
822 | ||
823 | if (filter_len) { | |
824 | filter = PictureFindFilter(crtc->pScreen, filter_name, filter_len); | |
825 | if (!filter) | |
826 | return BadName; | |
827 | if (filter->ValidateParams) { | |
828 | if (!filter->ValidateParams(crtc->pScreen, filter->id, | |
829 | params, nparams, &width, &height)) | |
830 | return BadMatch; | |
831 | } | |
832 | else { | |
833 | width = filter->width; | |
834 | height = filter->height; | |
835 | } | |
836 | } | |
837 | else { | |
838 | if (nparams) | |
839 | return BadMatch; | |
840 | } | |
841 | if (!RRTransformSetFilter(&crtc->client_pending_transform, | |
842 | filter, params, nparams, width, height)) | |
843 | return BadAlloc; | |
844 | ||
845 | crtc->client_pending_transform.transform = *transform; | |
846 | crtc->client_pending_transform.f_transform = *f_transform; | |
847 | crtc->client_pending_transform.f_inverse = *f_inverse; | |
848 | return Success; | |
849 | } | |
850 | ||
851 | /* | |
852 | * Initialize crtc type | |
853 | */ | |
854 | Bool | |
855 | RRCrtcInit(void) | |
856 | { | |
857 | RRCrtcType = CreateNewResourceType(RRCrtcDestroyResource, "CRTC"); | |
858 | if (!RRCrtcType) | |
859 | return FALSE; | |
860 | ||
861 | return TRUE; | |
862 | } | |
863 | ||
864 | /* | |
865 | * Initialize crtc type error value | |
866 | */ | |
867 | void | |
868 | RRCrtcInitErrorValue(void) | |
869 | { | |
870 | SetResourceTypeErrorValue(RRCrtcType, RRErrorBase + BadRRCrtc); | |
871 | } | |
872 | ||
873 | int | |
874 | ProcRRGetCrtcInfo(ClientPtr client) | |
875 | { | |
876 | REQUEST(xRRGetCrtcInfoReq); | |
877 | xRRGetCrtcInfoReply rep; | |
878 | RRCrtcPtr crtc; | |
879 | CARD8 *extra; | |
880 | unsigned long extraLen; | |
881 | ScreenPtr pScreen; | |
882 | rrScrPrivPtr pScrPriv; | |
883 | RRModePtr mode; | |
884 | RROutput *outputs; | |
885 | RROutput *possible; | |
886 | int i, j, k; | |
887 | int width, height; | |
888 | BoxRec panned_area; | |
889 | ||
890 | REQUEST_SIZE_MATCH(xRRGetCrtcInfoReq); | |
891 | VERIFY_RR_CRTC(stuff->crtc, crtc, DixReadAccess); | |
892 | ||
893 | /* All crtcs must be associated with screens before client | |
894 | * requests are processed | |
895 | */ | |
896 | pScreen = crtc->pScreen; | |
897 | pScrPriv = rrGetScrPriv(pScreen); | |
898 | ||
899 | mode = crtc->mode; | |
900 | ||
901 | rep = (xRRGetCrtcInfoReply) { | |
902 | .type = X_Reply, | |
903 | .status = RRSetConfigSuccess, | |
904 | .sequenceNumber = client->sequence, | |
905 | .length = 0, | |
906 | .timestamp = pScrPriv->lastSetTime.milliseconds | |
907 | }; | |
908 | if (pScrPriv->rrGetPanning && | |
909 | pScrPriv->rrGetPanning(pScreen, crtc, &panned_area, NULL, NULL) && | |
910 | (panned_area.x2 > panned_area.x1) && (panned_area.y2 > panned_area.y1)) | |
911 | { | |
912 | rep.x = panned_area.x1; | |
913 | rep.y = panned_area.y1; | |
914 | rep.width = panned_area.x2 - panned_area.x1; | |
915 | rep.height = panned_area.y2 - panned_area.y1; | |
916 | } | |
917 | else { | |
918 | RRCrtcGetScanoutSize(crtc, &width, &height); | |
919 | rep.x = crtc->x; | |
920 | rep.y = crtc->y; | |
921 | rep.width = width; | |
922 | rep.height = height; | |
923 | } | |
924 | rep.mode = mode ? mode->mode.id : 0; | |
925 | rep.rotation = crtc->rotation; | |
926 | rep.rotations = crtc->rotations; | |
927 | rep.nOutput = crtc->numOutputs; | |
928 | k = 0; | |
929 | for (i = 0; i < pScrPriv->numOutputs; i++) | |
930 | for (j = 0; j < pScrPriv->outputs[i]->numCrtcs; j++) | |
931 | if (pScrPriv->outputs[i]->crtcs[j] == crtc) | |
932 | k++; | |
933 | rep.nPossibleOutput = k; | |
934 | ||
935 | rep.length = rep.nOutput + rep.nPossibleOutput; | |
936 | ||
937 | extraLen = rep.length << 2; | |
938 | if (extraLen) { | |
939 | extra = malloc(extraLen); | |
940 | if (!extra) | |
941 | return BadAlloc; | |
942 | } | |
943 | else | |
944 | extra = NULL; | |
945 | ||
946 | outputs = (RROutput *) extra; | |
947 | possible = (RROutput *) (outputs + rep.nOutput); | |
948 | ||
949 | for (i = 0; i < crtc->numOutputs; i++) { | |
950 | outputs[i] = crtc->outputs[i]->id; | |
951 | if (client->swapped) | |
952 | swapl(&outputs[i]); | |
953 | } | |
954 | k = 0; | |
955 | for (i = 0; i < pScrPriv->numOutputs; i++) | |
956 | for (j = 0; j < pScrPriv->outputs[i]->numCrtcs; j++) | |
957 | if (pScrPriv->outputs[i]->crtcs[j] == crtc) { | |
958 | possible[k] = pScrPriv->outputs[i]->id; | |
959 | if (client->swapped) | |
960 | swapl(&possible[k]); | |
961 | k++; | |
962 | } | |
963 | ||
964 | if (client->swapped) { | |
965 | swaps(&rep.sequenceNumber); | |
966 | swapl(&rep.length); | |
967 | swapl(&rep.timestamp); | |
968 | swaps(&rep.x); | |
969 | swaps(&rep.y); | |
970 | swaps(&rep.width); | |
971 | swaps(&rep.height); | |
972 | swapl(&rep.mode); | |
973 | swaps(&rep.rotation); | |
974 | swaps(&rep.rotations); | |
975 | swaps(&rep.nOutput); | |
976 | swaps(&rep.nPossibleOutput); | |
977 | } | |
978 | WriteToClient(client, sizeof(xRRGetCrtcInfoReply), &rep); | |
979 | if (extraLen) { | |
980 | WriteToClient(client, extraLen, extra); | |
981 | free(extra); | |
982 | } | |
983 | ||
984 | return Success; | |
985 | } | |
986 | ||
987 | int | |
988 | ProcRRSetCrtcConfig(ClientPtr client) | |
989 | { | |
990 | REQUEST(xRRSetCrtcConfigReq); | |
991 | xRRSetCrtcConfigReply rep; | |
992 | ScreenPtr pScreen; | |
993 | rrScrPrivPtr pScrPriv; | |
994 | RRCrtcPtr crtc; | |
995 | RRModePtr mode; | |
996 | int numOutputs; | |
997 | RROutputPtr *outputs = NULL; | |
998 | RROutput *outputIds; | |
999 | TimeStamp time; | |
1000 | Rotation rotation; | |
1001 | int ret, i, j; | |
1002 | CARD8 status; | |
1003 | ||
1004 | REQUEST_AT_LEAST_SIZE(xRRSetCrtcConfigReq); | |
1005 | numOutputs = (stuff->length - bytes_to_int32(SIZEOF(xRRSetCrtcConfigReq))); | |
1006 | ||
1007 | VERIFY_RR_CRTC(stuff->crtc, crtc, DixSetAttrAccess); | |
1008 | ||
1009 | if (stuff->mode == None) { | |
1010 | mode = NULL; | |
1011 | if (numOutputs > 0) | |
1012 | return BadMatch; | |
1013 | } | |
1014 | else { | |
1015 | VERIFY_RR_MODE(stuff->mode, mode, DixSetAttrAccess); | |
1016 | if (numOutputs == 0) | |
1017 | return BadMatch; | |
1018 | } | |
1019 | if (numOutputs) { | |
1020 | outputs = malloc(numOutputs * sizeof(RROutputPtr)); | |
1021 | if (!outputs) | |
1022 | return BadAlloc; | |
1023 | } | |
1024 | else | |
1025 | outputs = NULL; | |
1026 | ||
1027 | outputIds = (RROutput *) (stuff + 1); | |
1028 | for (i = 0; i < numOutputs; i++) { | |
1029 | ret = dixLookupResourceByType((pointer *) (outputs + i), outputIds[i], | |
1030 | RROutputType, client, DixSetAttrAccess); | |
1031 | if (ret != Success) { | |
1032 | free(outputs); | |
1033 | return ret; | |
1034 | } | |
1035 | /* validate crtc for this output */ | |
1036 | for (j = 0; j < outputs[i]->numCrtcs; j++) | |
1037 | if (outputs[i]->crtcs[j] == crtc) | |
1038 | break; | |
1039 | if (j == outputs[i]->numCrtcs) { | |
1040 | free(outputs); | |
1041 | return BadMatch; | |
1042 | } | |
1043 | /* validate mode for this output */ | |
1044 | for (j = 0; j < outputs[i]->numModes + outputs[i]->numUserModes; j++) { | |
1045 | RRModePtr m = (j < outputs[i]->numModes ? | |
1046 | outputs[i]->modes[j] : | |
1047 | outputs[i]->userModes[j - outputs[i]->numModes]); | |
1048 | if (m == mode) | |
1049 | break; | |
1050 | } | |
1051 | if (j == outputs[i]->numModes + outputs[i]->numUserModes) { | |
1052 | free(outputs); | |
1053 | return BadMatch; | |
1054 | } | |
1055 | } | |
1056 | /* validate clones */ | |
1057 | for (i = 0; i < numOutputs; i++) { | |
1058 | for (j = 0; j < numOutputs; j++) { | |
1059 | int k; | |
1060 | ||
1061 | if (i == j) | |
1062 | continue; | |
1063 | for (k = 0; k < outputs[i]->numClones; k++) { | |
1064 | if (outputs[i]->clones[k] == outputs[j]) | |
1065 | break; | |
1066 | } | |
1067 | if (k == outputs[i]->numClones) { | |
1068 | free(outputs); | |
1069 | return BadMatch; | |
1070 | } | |
1071 | } | |
1072 | } | |
1073 | ||
1074 | pScreen = crtc->pScreen; | |
1075 | pScrPriv = rrGetScrPriv(pScreen); | |
1076 | ||
1077 | time = ClientTimeToServerTime(stuff->timestamp); | |
1078 | ||
1079 | if (!pScrPriv) { | |
1080 | time = currentTime; | |
1081 | status = RRSetConfigFailed; | |
1082 | goto sendReply; | |
1083 | } | |
1084 | ||
1085 | /* | |
1086 | * Validate requested rotation | |
1087 | */ | |
1088 | rotation = (Rotation) stuff->rotation; | |
1089 | ||
1090 | /* test the rotation bits only! */ | |
1091 | switch (rotation & 0xf) { | |
1092 | case RR_Rotate_0: | |
1093 | case RR_Rotate_90: | |
1094 | case RR_Rotate_180: | |
1095 | case RR_Rotate_270: | |
1096 | break; | |
1097 | default: | |
1098 | /* | |
1099 | * Invalid rotation | |
1100 | */ | |
1101 | client->errorValue = stuff->rotation; | |
1102 | free(outputs); | |
1103 | return BadValue; | |
1104 | } | |
1105 | ||
1106 | if (mode) { | |
1107 | if ((~crtc->rotations) & rotation) { | |
1108 | /* | |
1109 | * requested rotation or reflection not supported by screen | |
1110 | */ | |
1111 | client->errorValue = stuff->rotation; | |
1112 | free(outputs); | |
1113 | return BadMatch; | |
1114 | } | |
1115 | ||
1116 | #ifdef RANDR_12_INTERFACE | |
1117 | /* | |
1118 | * Check screen size bounds if the DDX provides a 1.2 interface | |
1119 | * for setting screen size. Else, assume the CrtcSet sets | |
1120 | * the size along with the mode. If the driver supports transforms, | |
1121 | * then it must allow crtcs to display a subset of the screen, so | |
1122 | * only do this check for drivers without transform support. | |
1123 | */ | |
1124 | if (pScrPriv->rrScreenSetSize && !crtc->transforms) { | |
1125 | int source_width; | |
1126 | int source_height; | |
1127 | PictTransform transform; | |
1128 | struct pixman_f_transform f_transform, f_inverse; | |
1129 | int width, height; | |
1130 | ||
1131 | if (pScreen->isGPU) { | |
1132 | width = pScreen->current_master->width; | |
1133 | height = pScreen->current_master->height; | |
1134 | } | |
1135 | else { | |
1136 | width = pScreen->width; | |
1137 | height = pScreen->height; | |
1138 | } | |
1139 | ||
1140 | RRTransformCompute(stuff->x, stuff->y, | |
1141 | mode->mode.width, mode->mode.height, | |
1142 | rotation, | |
1143 | &crtc->client_pending_transform, | |
1144 | &transform, &f_transform, &f_inverse); | |
1145 | ||
1146 | RRModeGetScanoutSize(mode, &transform, &source_width, | |
1147 | &source_height); | |
1148 | if (stuff->x + source_width > width) { | |
1149 | client->errorValue = stuff->x; | |
1150 | free(outputs); | |
1151 | return BadValue; | |
1152 | } | |
1153 | ||
1154 | if (stuff->y + source_height > height) { | |
1155 | client->errorValue = stuff->y; | |
1156 | free(outputs); | |
1157 | return BadValue; | |
1158 | } | |
1159 | } | |
1160 | #endif | |
1161 | } | |
1162 | ||
1163 | if (!RRCrtcSet(crtc, mode, stuff->x, stuff->y, | |
1164 | rotation, numOutputs, outputs)) { | |
1165 | status = RRSetConfigFailed; | |
1166 | goto sendReply; | |
1167 | } | |
1168 | status = RRSetConfigSuccess; | |
1169 | pScrPriv->lastSetTime = time; | |
1170 | ||
1171 | sendReply: | |
1172 | free(outputs); | |
1173 | ||
1174 | rep = (xRRSetCrtcConfigReply) { | |
1175 | .type = X_Reply, | |
1176 | .status = status, | |
1177 | .sequenceNumber = client->sequence, | |
1178 | .length = 0, | |
1179 | .newTimestamp = pScrPriv->lastSetTime.milliseconds | |
1180 | }; | |
1181 | ||
1182 | if (client->swapped) { | |
1183 | swaps(&rep.sequenceNumber); | |
1184 | swapl(&rep.length); | |
1185 | swapl(&rep.newTimestamp); | |
1186 | } | |
1187 | WriteToClient(client, sizeof(xRRSetCrtcConfigReply), &rep); | |
1188 | ||
1189 | return Success; | |
1190 | } | |
1191 | ||
1192 | int | |
1193 | ProcRRGetPanning(ClientPtr client) | |
1194 | { | |
1195 | REQUEST(xRRGetPanningReq); | |
1196 | xRRGetPanningReply rep; | |
1197 | RRCrtcPtr crtc; | |
1198 | ScreenPtr pScreen; | |
1199 | rrScrPrivPtr pScrPriv; | |
1200 | BoxRec total; | |
1201 | BoxRec tracking; | |
1202 | INT16 border[4]; | |
1203 | ||
1204 | REQUEST_SIZE_MATCH(xRRGetPanningReq); | |
1205 | VERIFY_RR_CRTC(stuff->crtc, crtc, DixReadAccess); | |
1206 | ||
1207 | /* All crtcs must be associated with screens before client | |
1208 | * requests are processed | |
1209 | */ | |
1210 | pScreen = crtc->pScreen; | |
1211 | pScrPriv = rrGetScrPriv(pScreen); | |
1212 | ||
1213 | if (!pScrPriv) | |
1214 | return RRErrorBase + BadRRCrtc; | |
1215 | ||
1216 | rep = (xRRGetPanningReply) { | |
1217 | .type = X_Reply, | |
1218 | .status = RRSetConfigSuccess, | |
1219 | .sequenceNumber = client->sequence, | |
1220 | .length = 1, | |
1221 | .timestamp = pScrPriv->lastSetTime.milliseconds | |
1222 | }; | |
1223 | ||
1224 | if (pScrPriv->rrGetPanning && | |
1225 | pScrPriv->rrGetPanning(pScreen, crtc, &total, &tracking, border)) { | |
1226 | rep.left = total.x1; | |
1227 | rep.top = total.y1; | |
1228 | rep.width = total.x2 - total.x1; | |
1229 | rep.height = total.y2 - total.y1; | |
1230 | rep.track_left = tracking.x1; | |
1231 | rep.track_top = tracking.y1; | |
1232 | rep.track_width = tracking.x2 - tracking.x1; | |
1233 | rep.track_height = tracking.y2 - tracking.y1; | |
1234 | rep.border_left = border[0]; | |
1235 | rep.border_top = border[1]; | |
1236 | rep.border_right = border[2]; | |
1237 | rep.border_bottom = border[3]; | |
1238 | } | |
1239 | ||
1240 | if (client->swapped) { | |
1241 | swaps(&rep.sequenceNumber); | |
1242 | swapl(&rep.length); | |
1243 | swapl(&rep.timestamp); | |
1244 | swaps(&rep.left); | |
1245 | swaps(&rep.top); | |
1246 | swaps(&rep.width); | |
1247 | swaps(&rep.height); | |
1248 | swaps(&rep.track_left); | |
1249 | swaps(&rep.track_top); | |
1250 | swaps(&rep.track_width); | |
1251 | swaps(&rep.track_height); | |
1252 | swaps(&rep.border_left); | |
1253 | swaps(&rep.border_top); | |
1254 | swaps(&rep.border_right); | |
1255 | swaps(&rep.border_bottom); | |
1256 | } | |
1257 | WriteToClient(client, sizeof(xRRGetPanningReply), &rep); | |
1258 | return Success; | |
1259 | } | |
1260 | ||
1261 | int | |
1262 | ProcRRSetPanning(ClientPtr client) | |
1263 | { | |
1264 | REQUEST(xRRSetPanningReq); | |
1265 | xRRSetPanningReply rep; | |
1266 | RRCrtcPtr crtc; | |
1267 | ScreenPtr pScreen; | |
1268 | rrScrPrivPtr pScrPriv; | |
1269 | TimeStamp time; | |
1270 | BoxRec total; | |
1271 | BoxRec tracking; | |
1272 | INT16 border[4]; | |
1273 | CARD8 status; | |
1274 | ||
1275 | REQUEST_SIZE_MATCH(xRRSetPanningReq); | |
1276 | VERIFY_RR_CRTC(stuff->crtc, crtc, DixReadAccess); | |
1277 | ||
1278 | /* All crtcs must be associated with screens before client | |
1279 | * requests are processed | |
1280 | */ | |
1281 | pScreen = crtc->pScreen; | |
1282 | pScrPriv = rrGetScrPriv(pScreen); | |
1283 | ||
1284 | if (!pScrPriv) { | |
1285 | time = currentTime; | |
1286 | status = RRSetConfigFailed; | |
1287 | goto sendReply; | |
1288 | } | |
1289 | ||
1290 | time = ClientTimeToServerTime(stuff->timestamp); | |
1291 | ||
1292 | if (!pScrPriv->rrGetPanning) | |
1293 | return RRErrorBase + BadRRCrtc; | |
1294 | ||
1295 | total.x1 = stuff->left; | |
1296 | total.y1 = stuff->top; | |
1297 | total.x2 = total.x1 + stuff->width; | |
1298 | total.y2 = total.y1 + stuff->height; | |
1299 | tracking.x1 = stuff->track_left; | |
1300 | tracking.y1 = stuff->track_top; | |
1301 | tracking.x2 = tracking.x1 + stuff->track_width; | |
1302 | tracking.y2 = tracking.y1 + stuff->track_height; | |
1303 | border[0] = stuff->border_left; | |
1304 | border[1] = stuff->border_top; | |
1305 | border[2] = stuff->border_right; | |
1306 | border[3] = stuff->border_bottom; | |
1307 | ||
1308 | if (!pScrPriv->rrSetPanning(pScreen, crtc, &total, &tracking, border)) | |
1309 | return BadMatch; | |
1310 | ||
1311 | pScrPriv->lastSetTime = time; | |
1312 | ||
1313 | status = RRSetConfigSuccess; | |
1314 | ||
1315 | sendReply: | |
1316 | rep = (xRRSetPanningReply) { | |
1317 | .type = X_Reply, | |
1318 | .status = status, | |
1319 | .sequenceNumber = client->sequence, | |
1320 | .length = 0, | |
1321 | .newTimestamp = pScrPriv->lastSetTime.milliseconds | |
1322 | }; | |
1323 | ||
1324 | if (client->swapped) { | |
1325 | swaps(&rep.sequenceNumber); | |
1326 | swapl(&rep.length); | |
1327 | swapl(&rep.newTimestamp); | |
1328 | } | |
1329 | WriteToClient(client, sizeof(xRRSetPanningReply), &rep); | |
1330 | return Success; | |
1331 | } | |
1332 | ||
1333 | int | |
1334 | ProcRRGetCrtcGammaSize(ClientPtr client) | |
1335 | { | |
1336 | REQUEST(xRRGetCrtcGammaSizeReq); | |
1337 | xRRGetCrtcGammaSizeReply reply; | |
1338 | RRCrtcPtr crtc; | |
1339 | ||
1340 | REQUEST_SIZE_MATCH(xRRGetCrtcGammaSizeReq); | |
1341 | VERIFY_RR_CRTC(stuff->crtc, crtc, DixReadAccess); | |
1342 | ||
1343 | /* Gamma retrieval failed, any better error? */ | |
1344 | if (!RRCrtcGammaGet(crtc)) | |
1345 | return RRErrorBase + BadRRCrtc; | |
1346 | ||
1347 | reply = (xRRGetCrtcGammaSizeReply) { | |
1348 | .type = X_Reply, | |
1349 | .sequenceNumber = client->sequence, | |
1350 | .length = 0, | |
1351 | .size = crtc->gammaSize | |
1352 | }; | |
1353 | if (client->swapped) { | |
1354 | swaps(&reply.sequenceNumber); | |
1355 | swapl(&reply.length); | |
1356 | swaps(&reply.size); | |
1357 | } | |
1358 | WriteToClient(client, sizeof(xRRGetCrtcGammaSizeReply), &reply); | |
1359 | return Success; | |
1360 | } | |
1361 | ||
1362 | int | |
1363 | ProcRRGetCrtcGamma(ClientPtr client) | |
1364 | { | |
1365 | REQUEST(xRRGetCrtcGammaReq); | |
1366 | xRRGetCrtcGammaReply reply; | |
1367 | RRCrtcPtr crtc; | |
1368 | unsigned long len; | |
1369 | char *extra = NULL; | |
1370 | ||
1371 | REQUEST_SIZE_MATCH(xRRGetCrtcGammaReq); | |
1372 | VERIFY_RR_CRTC(stuff->crtc, crtc, DixReadAccess); | |
1373 | ||
1374 | /* Gamma retrieval failed, any better error? */ | |
1375 | if (!RRCrtcGammaGet(crtc)) | |
1376 | return RRErrorBase + BadRRCrtc; | |
1377 | ||
1378 | len = crtc->gammaSize * 3 * 2; | |
1379 | ||
1380 | if (crtc->gammaSize) { | |
1381 | extra = malloc(len); | |
1382 | if (!extra) | |
1383 | return BadAlloc; | |
1384 | } | |
1385 | ||
1386 | reply = (xRRGetCrtcGammaReply) { | |
1387 | .type = X_Reply, | |
1388 | .sequenceNumber = client->sequence, | |
1389 | .length = bytes_to_int32(len), | |
1390 | .size = crtc->gammaSize | |
1391 | }; | |
1392 | if (client->swapped) { | |
1393 | swaps(&reply.sequenceNumber); | |
1394 | swapl(&reply.length); | |
1395 | swaps(&reply.size); | |
1396 | } | |
1397 | WriteToClient(client, sizeof(xRRGetCrtcGammaReply), &reply); | |
1398 | if (crtc->gammaSize) { | |
1399 | memcpy(extra, crtc->gammaRed, len); | |
1400 | client->pSwapReplyFunc = (ReplySwapPtr) CopySwap16Write; | |
1401 | WriteSwappedDataToClient(client, len, extra); | |
1402 | free(extra); | |
1403 | } | |
1404 | return Success; | |
1405 | } | |
1406 | ||
1407 | int | |
1408 | ProcRRSetCrtcGamma(ClientPtr client) | |
1409 | { | |
1410 | REQUEST(xRRSetCrtcGammaReq); | |
1411 | RRCrtcPtr crtc; | |
1412 | unsigned long len; | |
1413 | CARD16 *red, *green, *blue; | |
1414 | ||
1415 | REQUEST_AT_LEAST_SIZE(xRRSetCrtcGammaReq); | |
1416 | VERIFY_RR_CRTC(stuff->crtc, crtc, DixReadAccess); | |
1417 | ||
1418 | len = client->req_len - bytes_to_int32(sizeof(xRRSetCrtcGammaReq)); | |
1419 | if (len < (stuff->size * 3 + 1) >> 1) | |
1420 | return BadLength; | |
1421 | ||
1422 | if (stuff->size != crtc->gammaSize) | |
1423 | return BadMatch; | |
1424 | ||
1425 | red = (CARD16 *) (stuff + 1); | |
1426 | green = red + crtc->gammaSize; | |
1427 | blue = green + crtc->gammaSize; | |
1428 | ||
1429 | RRCrtcGammaSet(crtc, red, green, blue); | |
1430 | ||
1431 | return Success; | |
1432 | } | |
1433 | ||
1434 | /* Version 1.3 additions */ | |
1435 | ||
1436 | int | |
1437 | ProcRRSetCrtcTransform(ClientPtr client) | |
1438 | { | |
1439 | REQUEST(xRRSetCrtcTransformReq); | |
1440 | RRCrtcPtr crtc; | |
1441 | PictTransform transform; | |
1442 | struct pixman_f_transform f_transform, f_inverse; | |
1443 | char *filter; | |
1444 | int nbytes; | |
1445 | xFixed *params; | |
1446 | int nparams; | |
1447 | ||
1448 | REQUEST_AT_LEAST_SIZE(xRRSetCrtcTransformReq); | |
1449 | VERIFY_RR_CRTC(stuff->crtc, crtc, DixReadAccess); | |
1450 | ||
1451 | PictTransform_from_xRenderTransform(&transform, &stuff->transform); | |
1452 | pixman_f_transform_from_pixman_transform(&f_transform, &transform); | |
1453 | if (!pixman_f_transform_invert(&f_inverse, &f_transform)) | |
1454 | return BadMatch; | |
1455 | ||
1456 | filter = (char *) (stuff + 1); | |
1457 | nbytes = stuff->nbytesFilter; | |
1458 | params = (xFixed *) (filter + pad_to_int32(nbytes)); | |
1459 | nparams = ((xFixed *) stuff + client->req_len) - params; | |
1460 | if (nparams < 0) | |
1461 | return BadLength; | |
1462 | ||
1463 | return RRCrtcTransformSet(crtc, &transform, &f_transform, &f_inverse, | |
1464 | filter, nbytes, params, nparams); | |
1465 | } | |
1466 | ||
1467 | #define CrtcTransformExtra (SIZEOF(xRRGetCrtcTransformReply) - 32) | |
1468 | ||
1469 | static int | |
1470 | transform_filter_length(RRTransformPtr transform) | |
1471 | { | |
1472 | int nbytes, nparams; | |
1473 | ||
1474 | if (transform->filter == NULL) | |
1475 | return 0; | |
1476 | nbytes = strlen(transform->filter->name); | |
1477 | nparams = transform->nparams; | |
1478 | return pad_to_int32(nbytes) + (nparams * sizeof(xFixed)); | |
1479 | } | |
1480 | ||
1481 | static int | |
1482 | transform_filter_encode(ClientPtr client, char *output, | |
1483 | CARD16 *nbytesFilter, | |
1484 | CARD16 *nparamsFilter, RRTransformPtr transform) | |
1485 | { | |
1486 | int nbytes, nparams; | |
1487 | ||
1488 | if (transform->filter == NULL) { | |
1489 | *nbytesFilter = 0; | |
1490 | *nparamsFilter = 0; | |
1491 | return 0; | |
1492 | } | |
1493 | nbytes = strlen(transform->filter->name); | |
1494 | nparams = transform->nparams; | |
1495 | *nbytesFilter = nbytes; | |
1496 | *nparamsFilter = nparams; | |
1497 | memcpy(output, transform->filter->name, nbytes); | |
1498 | while ((nbytes & 3) != 0) | |
1499 | output[nbytes++] = 0; | |
1500 | memcpy(output + nbytes, transform->params, nparams * sizeof(xFixed)); | |
1501 | if (client->swapped) { | |
1502 | swaps(nbytesFilter); | |
1503 | swaps(nparamsFilter); | |
1504 | SwapLongs((CARD32 *) (output + nbytes), nparams); | |
1505 | } | |
1506 | nbytes += nparams * sizeof(xFixed); | |
1507 | return nbytes; | |
1508 | } | |
1509 | ||
1510 | static void | |
1511 | transform_encode(ClientPtr client, xRenderTransform * wire, | |
1512 | PictTransform * pict) | |
1513 | { | |
1514 | xRenderTransform_from_PictTransform(wire, pict); | |
1515 | if (client->swapped) | |
1516 | SwapLongs((CARD32 *) wire, bytes_to_int32(sizeof(xRenderTransform))); | |
1517 | } | |
1518 | ||
1519 | int | |
1520 | ProcRRGetCrtcTransform(ClientPtr client) | |
1521 | { | |
1522 | REQUEST(xRRGetCrtcTransformReq); | |
1523 | xRRGetCrtcTransformReply *reply; | |
1524 | RRCrtcPtr crtc; | |
1525 | int nextra; | |
1526 | RRTransformPtr current, pending; | |
1527 | char *extra; | |
1528 | ||
1529 | REQUEST_SIZE_MATCH(xRRGetCrtcTransformReq); | |
1530 | VERIFY_RR_CRTC(stuff->crtc, crtc, DixReadAccess); | |
1531 | ||
1532 | pending = &crtc->client_pending_transform; | |
1533 | current = &crtc->client_current_transform; | |
1534 | ||
1535 | nextra = (transform_filter_length(pending) + | |
1536 | transform_filter_length(current)); | |
1537 | ||
1538 | reply = calloc(1, sizeof(xRRGetCrtcTransformReply) + nextra); | |
1539 | if (!reply) | |
1540 | return BadAlloc; | |
1541 | ||
1542 | extra = (char *) (reply + 1); | |
1543 | reply->type = X_Reply; | |
1544 | reply->sequenceNumber = client->sequence; | |
1545 | reply->length = bytes_to_int32(CrtcTransformExtra + nextra); | |
1546 | ||
1547 | reply->hasTransforms = crtc->transforms; | |
1548 | ||
1549 | transform_encode(client, &reply->pendingTransform, &pending->transform); | |
1550 | extra += transform_filter_encode(client, extra, | |
1551 | &reply->pendingNbytesFilter, | |
1552 | &reply->pendingNparamsFilter, pending); | |
1553 | ||
1554 | transform_encode(client, &reply->currentTransform, ¤t->transform); | |
1555 | extra += transform_filter_encode(client, extra, | |
1556 | &reply->currentNbytesFilter, | |
1557 | &reply->currentNparamsFilter, current); | |
1558 | ||
1559 | if (client->swapped) { | |
1560 | swaps(&reply->sequenceNumber); | |
1561 | swapl(&reply->length); | |
1562 | } | |
1563 | WriteToClient(client, sizeof(xRRGetCrtcTransformReply) + nextra, reply); | |
1564 | free(reply); | |
1565 | return Success; | |
1566 | } | |
1567 | ||
1568 | static Bool check_all_screen_crtcs(ScreenPtr pScreen, int *x, int *y) | |
1569 | { | |
1570 | rrScrPriv(pScreen); | |
1571 | int i; | |
1572 | for (i = 0; i < pScrPriv->numCrtcs; i++) { | |
1573 | RRCrtcPtr crtc = pScrPriv->crtcs[i]; | |
1574 | ||
1575 | int left, right, top, bottom; | |
1576 | ||
1577 | if (!crtc->mode) | |
1578 | continue; | |
1579 | ||
1580 | crtc_bounds(crtc, &left, &right, &top, &bottom); | |
1581 | ||
1582 | if ((*x >= left) && (*x < right) && (*y >= top) && (*y < bottom)) | |
1583 | return TRUE; | |
1584 | } | |
1585 | return FALSE; | |
1586 | } | |
1587 | ||
1588 | static Bool constrain_all_screen_crtcs(DeviceIntPtr pDev, ScreenPtr pScreen, int *x, int *y) | |
1589 | { | |
1590 | rrScrPriv(pScreen); | |
1591 | int i; | |
1592 | ||
1593 | /* if we're trying to escape, clamp to the CRTC we're coming from */ | |
1594 | for (i = 0; i < pScrPriv->numCrtcs; i++) { | |
1595 | RRCrtcPtr crtc = pScrPriv->crtcs[i]; | |
1596 | int nx, ny; | |
1597 | int left, right, top, bottom; | |
1598 | ||
1599 | if (!crtc->mode) | |
1600 | continue; | |
1601 | ||
1602 | crtc_bounds(crtc, &left, &right, &top, &bottom); | |
1603 | miPointerGetPosition(pDev, &nx, &ny); | |
1604 | ||
1605 | if ((nx >= left) && (nx < right) && (ny >= top) && (ny < bottom)) { | |
1606 | if (*x < left) | |
1607 | *x = left; | |
1608 | if (*x >= right) | |
1609 | *x = right - 1; | |
1610 | if (*y < top) | |
1611 | *y = top; | |
1612 | if (*y >= bottom) | |
1613 | *y = bottom - 1; | |
1614 | ||
1615 | return TRUE; | |
1616 | } | |
1617 | } | |
1618 | return FALSE; | |
1619 | } | |
1620 | ||
1621 | void | |
1622 | RRConstrainCursorHarder(DeviceIntPtr pDev, ScreenPtr pScreen, int mode, int *x, | |
1623 | int *y) | |
1624 | { | |
1625 | rrScrPriv(pScreen); | |
1626 | Bool ret; | |
1627 | ScreenPtr slave; | |
1628 | ||
1629 | /* intentional dead space -> let it float */ | |
1630 | if (pScrPriv->discontiguous) | |
1631 | return; | |
1632 | ||
1633 | /* if we're moving inside a crtc, we're fine */ | |
1634 | ret = check_all_screen_crtcs(pScreen, x, y); | |
1635 | if (ret == TRUE) | |
1636 | return; | |
1637 | ||
1638 | xorg_list_for_each_entry(slave, &pScreen->output_slave_list, output_head) { | |
1639 | ret = check_all_screen_crtcs(slave, x, y); | |
1640 | if (ret == TRUE) | |
1641 | return; | |
1642 | } | |
1643 | ||
1644 | /* if we're trying to escape, clamp to the CRTC we're coming from */ | |
1645 | ret = constrain_all_screen_crtcs(pDev, pScreen, x, y); | |
1646 | if (ret == TRUE) | |
1647 | return; | |
1648 | ||
1649 | xorg_list_for_each_entry(slave, &pScreen->output_slave_list, output_head) { | |
1650 | ret = constrain_all_screen_crtcs(pDev, slave, x, y); | |
1651 | if (ret == TRUE) | |
1652 | return; | |
1653 | } | |
1654 | } | |
1655 | ||
1656 | Bool | |
1657 | RRReplaceScanoutPixmap(DrawablePtr pDrawable, PixmapPtr pPixmap, Bool enable) | |
1658 | { | |
1659 | rrScrPriv(pDrawable->pScreen); | |
1660 | int i; | |
1661 | Bool size_fits = FALSE; | |
1662 | Bool changed = FALSE; | |
1663 | Bool ret = TRUE; | |
1664 | ||
1665 | for (i = 0; i < pScrPriv->numCrtcs; i++) { | |
1666 | RRCrtcPtr crtc = pScrPriv->crtcs[i]; | |
1667 | ||
1668 | if (!crtc->mode && enable) | |
1669 | continue; | |
1670 | ||
1671 | changed = FALSE; | |
1672 | if (crtc->mode && crtc->x == pDrawable->x && | |
1673 | crtc->y == pDrawable->y && | |
1674 | crtc->mode->mode.width == pDrawable->width && | |
1675 | crtc->mode->mode.height == pDrawable->height) | |
1676 | size_fits = TRUE; | |
1677 | ||
1678 | /* is the pixmap already set? */ | |
1679 | if (crtc->scanout_pixmap == pPixmap) { | |
1680 | /* if its a disable then don't care about size */ | |
1681 | if (enable == FALSE) { | |
1682 | /* set scanout to NULL */ | |
1683 | crtc->scanout_pixmap = NULL; | |
1684 | changed = TRUE; | |
1685 | } else { | |
1686 | /* if the size fits then we are already setup */ | |
1687 | if (size_fits) | |
1688 | return TRUE; | |
1689 | /* if the size no longer fits then drop off */ | |
1690 | crtc->scanout_pixmap = NULL; | |
1691 | changed = TRUE; | |
1692 | ret = FALSE; | |
1693 | } | |
1694 | } else { | |
1695 | if (!size_fits) | |
1696 | return FALSE; | |
1697 | if (enable) { | |
1698 | crtc->scanout_pixmap = pPixmap; | |
1699 | pScrPriv->rrCrtcSetScanoutPixmap(crtc, pPixmap); | |
1700 | changed = TRUE; | |
1701 | } | |
1702 | } | |
1703 | ||
1704 | if (changed && pScrPriv->rrCrtcSet) { | |
1705 | pScrPriv->rrCrtcSetScanoutPixmap(crtc, crtc->scanout_pixmap); | |
1706 | ||
1707 | (*pScrPriv->rrCrtcSet) (pDrawable->pScreen, crtc, crtc->mode, crtc->x, crtc->y, | |
1708 | crtc->rotation, crtc->numOutputs, crtc->outputs); | |
1709 | } | |
1710 | } | |
1711 | return ret; | |
1712 | } |