Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /* |
2 | * Copyright © 2006 Keith Packard | |
3 | * | |
4 | * Permission to use, copy, modify, distribute, and sell this software and its | |
5 | * documentation for any purpose is hereby granted without fee, provided that | |
6 | * the above copyright notice appear in all copies and that both that copyright | |
7 | * notice and this permission notice appear in supporting documentation, and | |
8 | * that the name of the copyright holders not be used in advertising or | |
9 | * publicity pertaining to distribution of the software without specific, | |
10 | * written prior permission. The copyright holders make no representations | |
11 | * about the suitability of this software for any purpose. It is provided "as | |
12 | * is" without express or implied warranty. | |
13 | * | |
14 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | |
15 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | |
16 | * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR | |
17 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | |
18 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | |
19 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | |
20 | * OF THIS SOFTWARE. | |
21 | */ | |
22 | /* | |
23 | * This Xinerama implementation comes from the SiS driver which has | |
24 | * the following notice: | |
25 | */ | |
26 | /* | |
27 | * SiS driver main code | |
28 | * | |
29 | * Copyright (C) 2001-2005 by Thomas Winischhofer, Vienna, Austria. | |
30 | * | |
31 | * Redistribution and use in source and binary forms, with or without | |
32 | * modification, are permitted provided that the following conditions | |
33 | * are met: | |
34 | * 1) Redistributions of source code must retain the above copyright | |
35 | * notice, this list of conditions and the following disclaimer. | |
36 | * 2) Redistributions in binary form must reproduce the above copyright | |
37 | * notice, this list of conditions and the following disclaimer in the | |
38 | * documentation and/or other materials provided with the distribution. | |
39 | * 3) The name of the author may not be used to endorse or promote products | |
40 | * derived from this software without specific prior written permission. | |
41 | * | |
42 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
43 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
44 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
45 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
46 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
47 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
48 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
49 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
50 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
51 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
52 | * | |
53 | * Author: Thomas Winischhofer <thomas@winischhofer.net> | |
54 | * - driver entirely rewritten since 2001, only basic structure taken from | |
55 | * old code (except sis_dri.c, sis_shadow.c, sis_accel.c and parts of | |
56 | * sis_dga.c; these were mostly taken over; sis_dri.c was changed for | |
57 | * new versions of the DRI layer) | |
58 | * | |
59 | * This notice covers the entire driver code unless indicated otherwise. | |
60 | * | |
61 | * Formerly based on code which was | |
62 | * Copyright (C) 1998, 1999 by Alan Hourihane, Wigan, England. | |
63 | * Written by: | |
64 | * Alan Hourihane <alanh@fairlite.demon.co.uk>, | |
65 | * Mike Chapman <mike@paranoia.com>, | |
66 | * Juanjo Santamarta <santamarta@ctv.es>, | |
67 | * Mitani Hiroshi <hmitani@drl.mei.co.jp>, | |
68 | * David Thomas <davtom@dream.org.uk>. | |
69 | */ | |
70 | ||
71 | #include "randrstr.h" | |
72 | #include "swaprep.h" | |
73 | #include <X11/extensions/panoramiXproto.h> | |
74 | #include "protocol-versions.h" | |
75 | ||
76 | /* Xinerama is not multi-screen capable; just report about screen 0 */ | |
77 | #define RR_XINERAMA_SCREEN 0 | |
78 | ||
79 | static int ProcRRXineramaQueryVersion(ClientPtr client); | |
80 | static int ProcRRXineramaGetState(ClientPtr client); | |
81 | static int ProcRRXineramaGetScreenCount(ClientPtr client); | |
82 | static int ProcRRXineramaGetScreenSize(ClientPtr client); | |
83 | static int ProcRRXineramaIsActive(ClientPtr client); | |
84 | static int ProcRRXineramaQueryScreens(ClientPtr client); | |
85 | static int SProcRRXineramaDispatch(ClientPtr client); | |
86 | ||
87 | /* Proc */ | |
88 | ||
89 | int | |
90 | ProcRRXineramaQueryVersion(ClientPtr client) | |
91 | { | |
92 | xPanoramiXQueryVersionReply rep = { | |
93 | .type = X_Reply, | |
94 | .sequenceNumber = client->sequence, | |
95 | .length = 0, | |
96 | .majorVersion = SERVER_RRXINERAMA_MAJOR_VERSION, | |
97 | .minorVersion = SERVER_RRXINERAMA_MINOR_VERSION | |
98 | }; | |
99 | ||
100 | REQUEST_SIZE_MATCH(xPanoramiXQueryVersionReq); | |
101 | if (client->swapped) { | |
102 | swaps(&rep.sequenceNumber); | |
103 | swapl(&rep.length); | |
104 | swaps(&rep.majorVersion); | |
105 | swaps(&rep.minorVersion); | |
106 | } | |
107 | WriteToClient(client, sizeof(xPanoramiXQueryVersionReply), &rep); | |
108 | return Success; | |
109 | } | |
110 | ||
111 | int | |
112 | ProcRRXineramaGetState(ClientPtr client) | |
113 | { | |
114 | REQUEST(xPanoramiXGetStateReq); | |
115 | WindowPtr pWin; | |
116 | xPanoramiXGetStateReply rep; | |
117 | register int rc; | |
118 | ScreenPtr pScreen; | |
119 | rrScrPrivPtr pScrPriv; | |
120 | Bool active = FALSE; | |
121 | ||
122 | REQUEST_SIZE_MATCH(xPanoramiXGetStateReq); | |
123 | rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); | |
124 | if (rc != Success) | |
125 | return rc; | |
126 | ||
127 | pScreen = pWin->drawable.pScreen; | |
128 | pScrPriv = rrGetScrPriv(pScreen); | |
129 | if (pScrPriv) { | |
130 | /* XXX do we need more than this? */ | |
131 | active = TRUE; | |
132 | } | |
133 | ||
134 | rep = (xPanoramiXGetStateReply) { | |
135 | .type = X_Reply, | |
136 | .state = active, | |
137 | .sequenceNumber = client->sequence, | |
138 | .length = 0, | |
139 | .window = stuff->window | |
140 | }; | |
141 | if (client->swapped) { | |
142 | swaps(&rep.sequenceNumber); | |
143 | swapl(&rep.length); | |
144 | swapl(&rep.window); | |
145 | } | |
146 | WriteToClient(client, sizeof(xPanoramiXGetStateReply), &rep); | |
147 | return Success; | |
148 | } | |
149 | ||
150 | static Bool | |
151 | RRXineramaCrtcActive(RRCrtcPtr crtc) | |
152 | { | |
153 | return crtc->mode != NULL && crtc->numOutputs > 0; | |
154 | } | |
155 | ||
156 | static int | |
157 | RRXineramaScreenCount(ScreenPtr pScreen) | |
158 | { | |
159 | int i, n; | |
160 | ScreenPtr slave; | |
161 | ||
162 | n = 0; | |
163 | if (rrGetScrPriv(pScreen)) { | |
164 | rrScrPriv(pScreen); | |
165 | for (i = 0; i < pScrPriv->numCrtcs; i++) | |
166 | if (RRXineramaCrtcActive(pScrPriv->crtcs[i])) | |
167 | n++; | |
168 | } | |
169 | ||
170 | xorg_list_for_each_entry(slave, &pScreen->output_slave_list, output_head) { | |
171 | rrScrPrivPtr pSlavePriv; | |
172 | pSlavePriv = rrGetScrPriv(slave); | |
173 | for (i = 0; i < pSlavePriv->numCrtcs; i++) | |
174 | if (RRXineramaCrtcActive(pSlavePriv->crtcs[i])) | |
175 | n++; | |
176 | } | |
177 | ||
178 | return n; | |
179 | } | |
180 | ||
181 | static Bool | |
182 | RRXineramaScreenActive(ScreenPtr pScreen) | |
183 | { | |
184 | return RRXineramaScreenCount(pScreen) > 0; | |
185 | } | |
186 | ||
187 | int | |
188 | ProcRRXineramaGetScreenCount(ClientPtr client) | |
189 | { | |
190 | REQUEST(xPanoramiXGetScreenCountReq); | |
191 | WindowPtr pWin; | |
192 | xPanoramiXGetScreenCountReply rep; | |
193 | register int rc; | |
194 | ||
195 | REQUEST_SIZE_MATCH(xPanoramiXGetScreenCountReq); | |
196 | rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); | |
197 | if (rc != Success) | |
198 | return rc; | |
199 | ||
200 | rep = (xPanoramiXGetScreenCountReply) { | |
201 | .type = X_Reply, | |
202 | .ScreenCount = RRXineramaScreenCount(pWin->drawable.pScreen), | |
203 | .sequenceNumber = client->sequence, | |
204 | .length = 0, | |
205 | .window = stuff->window | |
206 | }; | |
207 | if (client->swapped) { | |
208 | swaps(&rep.sequenceNumber); | |
209 | swapl(&rep.length); | |
210 | swapl(&rep.window); | |
211 | } | |
212 | WriteToClient(client, sizeof(xPanoramiXGetScreenCountReply), &rep); | |
213 | return Success; | |
214 | } | |
215 | ||
216 | int | |
217 | ProcRRXineramaGetScreenSize(ClientPtr client) | |
218 | { | |
219 | REQUEST(xPanoramiXGetScreenSizeReq); | |
220 | WindowPtr pWin, pRoot; | |
221 | ScreenPtr pScreen; | |
222 | xPanoramiXGetScreenSizeReply rep; | |
223 | register int rc; | |
224 | ||
225 | REQUEST_SIZE_MATCH(xPanoramiXGetScreenSizeReq); | |
226 | rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); | |
227 | if (rc != Success) | |
228 | return rc; | |
229 | ||
230 | pScreen = pWin->drawable.pScreen; | |
231 | pRoot = pScreen->root; | |
232 | ||
233 | rep = (xPanoramiXGetScreenSizeReply) { | |
234 | .type = X_Reply, | |
235 | .sequenceNumber = client->sequence, | |
236 | .length = 0, | |
237 | .width = pRoot->drawable.width, | |
238 | .height = pRoot->drawable.height, | |
239 | .window = stuff->window, | |
240 | .screen = stuff->screen | |
241 | }; | |
242 | if (client->swapped) { | |
243 | swaps(&rep.sequenceNumber); | |
244 | swapl(&rep.length); | |
245 | swapl(&rep.width); | |
246 | swapl(&rep.height); | |
247 | swapl(&rep.window); | |
248 | swapl(&rep.screen); | |
249 | } | |
250 | WriteToClient(client, sizeof(xPanoramiXGetScreenSizeReply), &rep); | |
251 | return Success; | |
252 | } | |
253 | ||
254 | int | |
255 | ProcRRXineramaIsActive(ClientPtr client) | |
256 | { | |
257 | xXineramaIsActiveReply rep; | |
258 | ||
259 | REQUEST_SIZE_MATCH(xXineramaIsActiveReq); | |
260 | ||
261 | rep = (xXineramaIsActiveReply) { | |
262 | .type = X_Reply, | |
263 | .length = 0, | |
264 | .sequenceNumber = client->sequence, | |
265 | .state = RRXineramaScreenActive(screenInfo.screens[RR_XINERAMA_SCREEN]) | |
266 | }; | |
267 | if (client->swapped) { | |
268 | swaps(&rep.sequenceNumber); | |
269 | swapl(&rep.length); | |
270 | swapl(&rep.state); | |
271 | } | |
272 | WriteToClient(client, sizeof(xXineramaIsActiveReply), &rep); | |
273 | return Success; | |
274 | } | |
275 | ||
276 | static void | |
277 | RRXineramaWriteCrtc(ClientPtr client, RRCrtcPtr crtc) | |
278 | { | |
279 | xXineramaScreenInfo scratch; | |
280 | ||
281 | if (RRXineramaCrtcActive(crtc)) { | |
282 | ScreenPtr pScreen = crtc->pScreen; | |
283 | rrScrPrivPtr pScrPriv = rrGetScrPriv(pScreen); | |
284 | BoxRec panned_area; | |
285 | ||
286 | /* Check to see if crtc is panned and return the full area when applicable. */ | |
287 | if (pScrPriv && pScrPriv->rrGetPanning && | |
288 | pScrPriv->rrGetPanning(pScreen, crtc, &panned_area, NULL, NULL) && | |
289 | (panned_area.x2 > panned_area.x1) && | |
290 | (panned_area.y2 > panned_area.y1)) { | |
291 | scratch.x_org = panned_area.x1; | |
292 | scratch.y_org = panned_area.y1; | |
293 | scratch.width = panned_area.x2 - panned_area.x1; | |
294 | scratch.height = panned_area.y2 - panned_area.y1; | |
295 | } | |
296 | else { | |
297 | int width, height; | |
298 | ||
299 | RRCrtcGetScanoutSize(crtc, &width, &height); | |
300 | scratch.x_org = crtc->x; | |
301 | scratch.y_org = crtc->y; | |
302 | scratch.width = width; | |
303 | scratch.height = height; | |
304 | } | |
305 | if (client->swapped) { | |
306 | swaps(&scratch.x_org); | |
307 | swaps(&scratch.y_org); | |
308 | swaps(&scratch.width); | |
309 | swaps(&scratch.height); | |
310 | } | |
311 | WriteToClient(client, sz_XineramaScreenInfo, &scratch); | |
312 | } | |
313 | } | |
314 | ||
315 | int | |
316 | ProcRRXineramaQueryScreens(ClientPtr client) | |
317 | { | |
318 | xXineramaQueryScreensReply rep; | |
319 | ScreenPtr pScreen = screenInfo.screens[RR_XINERAMA_SCREEN]; | |
320 | int n = 0; | |
321 | int i; | |
322 | ||
323 | REQUEST_SIZE_MATCH(xXineramaQueryScreensReq); | |
324 | ||
325 | if (RRXineramaScreenActive(pScreen)) { | |
326 | RRGetInfo(pScreen, FALSE); | |
327 | n = RRXineramaScreenCount(pScreen); | |
328 | } | |
329 | ||
330 | rep = (xXineramaQueryScreensReply) { | |
331 | .type = X_Reply, | |
332 | .sequenceNumber = client->sequence, | |
333 | .length = bytes_to_int32(n * sz_XineramaScreenInfo), | |
334 | .number = n | |
335 | }; | |
336 | if (client->swapped) { | |
337 | swaps(&rep.sequenceNumber); | |
338 | swapl(&rep.length); | |
339 | swapl(&rep.number); | |
340 | } | |
341 | WriteToClient(client, sizeof(xXineramaQueryScreensReply), &rep); | |
342 | ||
343 | if (n) { | |
344 | ScreenPtr slave; | |
345 | rrScrPriv(pScreen); | |
346 | int has_primary = 0; | |
347 | ||
348 | if (pScrPriv->primaryOutput && pScrPriv->primaryOutput->crtc) { | |
349 | has_primary = 1; | |
350 | RRXineramaWriteCrtc(client, pScrPriv->primaryOutput->crtc); | |
351 | } | |
352 | ||
353 | for (i = 0; i < pScrPriv->numCrtcs; i++) { | |
354 | if (has_primary && | |
355 | pScrPriv->primaryOutput->crtc == pScrPriv->crtcs[i]) { | |
356 | has_primary = 0; | |
357 | continue; | |
358 | } | |
359 | RRXineramaWriteCrtc(client, pScrPriv->crtcs[i]); | |
360 | } | |
361 | ||
362 | xorg_list_for_each_entry(slave, &pScreen->output_slave_list, output_head) { | |
363 | rrScrPrivPtr pSlavePriv; | |
364 | pSlavePriv = rrGetScrPriv(slave); | |
365 | for (i = 0; i < pSlavePriv->numCrtcs; i++) | |
366 | RRXineramaWriteCrtc(client, pSlavePriv->crtcs[i]); | |
367 | } | |
368 | } | |
369 | ||
370 | return Success; | |
371 | } | |
372 | ||
373 | static int | |
374 | ProcRRXineramaDispatch(ClientPtr client) | |
375 | { | |
376 | REQUEST(xReq); | |
377 | switch (stuff->data) { | |
378 | case X_PanoramiXQueryVersion: | |
379 | return ProcRRXineramaQueryVersion(client); | |
380 | case X_PanoramiXGetState: | |
381 | return ProcRRXineramaGetState(client); | |
382 | case X_PanoramiXGetScreenCount: | |
383 | return ProcRRXineramaGetScreenCount(client); | |
384 | case X_PanoramiXGetScreenSize: | |
385 | return ProcRRXineramaGetScreenSize(client); | |
386 | case X_XineramaIsActive: | |
387 | return ProcRRXineramaIsActive(client); | |
388 | case X_XineramaQueryScreens: | |
389 | return ProcRRXineramaQueryScreens(client); | |
390 | } | |
391 | return BadRequest; | |
392 | } | |
393 | ||
394 | /* SProc */ | |
395 | ||
396 | static int | |
397 | SProcRRXineramaQueryVersion(ClientPtr client) | |
398 | { | |
399 | REQUEST(xPanoramiXQueryVersionReq); | |
400 | swaps(&stuff->length); | |
401 | REQUEST_SIZE_MATCH(xPanoramiXQueryVersionReq); | |
402 | return ProcRRXineramaQueryVersion(client); | |
403 | } | |
404 | ||
405 | static int | |
406 | SProcRRXineramaGetState(ClientPtr client) | |
407 | { | |
408 | REQUEST(xPanoramiXGetStateReq); | |
409 | swaps(&stuff->length); | |
410 | REQUEST_SIZE_MATCH(xPanoramiXGetStateReq); | |
411 | swapl(&stuff->window); | |
412 | return ProcRRXineramaGetState(client); | |
413 | } | |
414 | ||
415 | static int | |
416 | SProcRRXineramaGetScreenCount(ClientPtr client) | |
417 | { | |
418 | REQUEST(xPanoramiXGetScreenCountReq); | |
419 | swaps(&stuff->length); | |
420 | REQUEST_SIZE_MATCH(xPanoramiXGetScreenCountReq); | |
421 | swapl(&stuff->window); | |
422 | return ProcRRXineramaGetScreenCount(client); | |
423 | } | |
424 | ||
425 | static int | |
426 | SProcRRXineramaGetScreenSize(ClientPtr client) | |
427 | { | |
428 | REQUEST(xPanoramiXGetScreenSizeReq); | |
429 | swaps(&stuff->length); | |
430 | REQUEST_SIZE_MATCH(xPanoramiXGetScreenSizeReq); | |
431 | swapl(&stuff->window); | |
432 | swapl(&stuff->screen); | |
433 | return ProcRRXineramaGetScreenSize(client); | |
434 | } | |
435 | ||
436 | static int | |
437 | SProcRRXineramaIsActive(ClientPtr client) | |
438 | { | |
439 | REQUEST(xXineramaIsActiveReq); | |
440 | swaps(&stuff->length); | |
441 | REQUEST_SIZE_MATCH(xXineramaIsActiveReq); | |
442 | return ProcRRXineramaIsActive(client); | |
443 | } | |
444 | ||
445 | static int | |
446 | SProcRRXineramaQueryScreens(ClientPtr client) | |
447 | { | |
448 | REQUEST(xXineramaQueryScreensReq); | |
449 | swaps(&stuff->length); | |
450 | REQUEST_SIZE_MATCH(xXineramaQueryScreensReq); | |
451 | return ProcRRXineramaQueryScreens(client); | |
452 | } | |
453 | ||
454 | int | |
455 | SProcRRXineramaDispatch(ClientPtr client) | |
456 | { | |
457 | REQUEST(xReq); | |
458 | switch (stuff->data) { | |
459 | case X_PanoramiXQueryVersion: | |
460 | return SProcRRXineramaQueryVersion(client); | |
461 | case X_PanoramiXGetState: | |
462 | return SProcRRXineramaGetState(client); | |
463 | case X_PanoramiXGetScreenCount: | |
464 | return SProcRRXineramaGetScreenCount(client); | |
465 | case X_PanoramiXGetScreenSize: | |
466 | return SProcRRXineramaGetScreenSize(client); | |
467 | case X_XineramaIsActive: | |
468 | return SProcRRXineramaIsActive(client); | |
469 | case X_XineramaQueryScreens: | |
470 | return SProcRRXineramaQueryScreens(client); | |
471 | } | |
472 | return BadRequest; | |
473 | } | |
474 | ||
475 | void | |
476 | RRXineramaExtensionInit(void) | |
477 | { | |
478 | #ifdef PANORAMIX | |
479 | if (!noPanoramiXExtension) | |
480 | return; | |
481 | #endif | |
482 | ||
483 | /* | |
484 | * Xinerama isn't capable enough to have multiple protocol screens each | |
485 | * with their own output geometry. So if there's more than one protocol | |
486 | * screen, just don't even try. | |
487 | */ | |
488 | if (screenInfo.numScreens > 1) | |
489 | return; | |
490 | ||
491 | (void) AddExtension(PANORAMIX_PROTOCOL_NAME, 0, 0, | |
492 | ProcRRXineramaDispatch, | |
493 | SProcRRXineramaDispatch, NULL, StandardMinorOpcode); | |
494 | } |