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
79static int ProcRRXineramaQueryVersion(ClientPtr client);
80static int ProcRRXineramaGetState(ClientPtr client);
81static int ProcRRXineramaGetScreenCount(ClientPtr client);
82static int ProcRRXineramaGetScreenSize(ClientPtr client);
83static int ProcRRXineramaIsActive(ClientPtr client);
84static int ProcRRXineramaQueryScreens(ClientPtr client);
85static int _X_COLD SProcRRXineramaDispatch(ClientPtr client);
86
87Bool noRRXineramaExtension = FALSE;
88
89/* Proc */
90
91int
92ProcRRXineramaQueryVersion(ClientPtr client)
93{
94    xPanoramiXQueryVersionReply rep = {
95        .type = X_Reply,
96        .sequenceNumber = client->sequence,
97        .length = 0,
98        .majorVersion = SERVER_RRXINERAMA_MAJOR_VERSION,
99        .minorVersion = SERVER_RRXINERAMA_MINOR_VERSION
100    };
101
102    REQUEST_SIZE_MATCH(xPanoramiXQueryVersionReq);
103    if (client->swapped) {
104        swaps(&rep.sequenceNumber);
105        swapl(&rep.length);
106        swaps(&rep.majorVersion);
107        swaps(&rep.minorVersion);
108    }
109    WriteToClient(client, sizeof(xPanoramiXQueryVersionReply), &rep);
110    return Success;
111}
112
113int
114ProcRRXineramaGetState(ClientPtr client)
115{
116    REQUEST(xPanoramiXGetStateReq);
117    WindowPtr pWin;
118    xPanoramiXGetStateReply rep;
119    register int rc;
120    ScreenPtr pScreen;
121    rrScrPrivPtr pScrPriv;
122    Bool active = FALSE;
123
124    REQUEST_SIZE_MATCH(xPanoramiXGetStateReq);
125    rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
126    if (rc != Success)
127        return rc;
128
129    pScreen = pWin->drawable.pScreen;
130    pScrPriv = rrGetScrPriv(pScreen);
131    if (pScrPriv) {
132        /* XXX do we need more than this? */
133        active = TRUE;
134    }
135
136    rep = (xPanoramiXGetStateReply) {
137        .type = X_Reply,
138        .state = active,
139        .sequenceNumber = client->sequence,
140        .length = 0,
141        .window = stuff->window
142    };
143    if (client->swapped) {
144        swaps(&rep.sequenceNumber);
145        swapl(&rep.length);
146        swapl(&rep.window);
147    }
148    WriteToClient(client, sizeof(xPanoramiXGetStateReply), &rep);
149    return Success;
150}
151
152static int
153RRXineramaScreenCount(ScreenPtr pScreen)
154{
155    return RRMonitorCountList(pScreen);
156}
157
158static Bool
159RRXineramaScreenActive(ScreenPtr pScreen)
160{
161    return RRXineramaScreenCount(pScreen) > 0;
162}
163
164int
165ProcRRXineramaGetScreenCount(ClientPtr client)
166{
167    REQUEST(xPanoramiXGetScreenCountReq);
168    WindowPtr pWin;
169    xPanoramiXGetScreenCountReply rep;
170    register int rc;
171
172    REQUEST_SIZE_MATCH(xPanoramiXGetScreenCountReq);
173    rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
174    if (rc != Success)
175        return rc;
176
177    rep = (xPanoramiXGetScreenCountReply)  {
178        .type = X_Reply,
179        .ScreenCount = RRXineramaScreenCount(pWin->drawable.pScreen),
180        .sequenceNumber = client->sequence,
181        .length = 0,
182        .window = stuff->window
183    };
184    if (client->swapped) {
185        swaps(&rep.sequenceNumber);
186        swapl(&rep.length);
187        swapl(&rep.window);
188    }
189    WriteToClient(client, sizeof(xPanoramiXGetScreenCountReply), &rep);
190    return Success;
191}
192
193int
194ProcRRXineramaGetScreenSize(ClientPtr client)
195{
196    REQUEST(xPanoramiXGetScreenSizeReq);
197    WindowPtr pWin, pRoot;
198    ScreenPtr pScreen;
199    xPanoramiXGetScreenSizeReply rep;
200    register int rc;
201
202    REQUEST_SIZE_MATCH(xPanoramiXGetScreenSizeReq);
203    rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
204    if (rc != Success)
205        return rc;
206
207    pScreen = pWin->drawable.pScreen;
208    pRoot = pScreen->root;
209
210    rep = (xPanoramiXGetScreenSizeReply) {
211        .type = X_Reply,
212        .sequenceNumber = client->sequence,
213        .length = 0,
214        .width = pRoot->drawable.width,
215        .height = pRoot->drawable.height,
216        .window = stuff->window,
217        .screen = stuff->screen
218    };
219    if (client->swapped) {
220        swaps(&rep.sequenceNumber);
221        swapl(&rep.length);
222        swapl(&rep.width);
223        swapl(&rep.height);
224        swapl(&rep.window);
225        swapl(&rep.screen);
226    }
227    WriteToClient(client, sizeof(xPanoramiXGetScreenSizeReply), &rep);
228    return Success;
229}
230
231int
232ProcRRXineramaIsActive(ClientPtr client)
233{
234    xXineramaIsActiveReply rep;
235
236    REQUEST_SIZE_MATCH(xXineramaIsActiveReq);
237
238    rep = (xXineramaIsActiveReply) {
239        .type = X_Reply,
240        .length = 0,
241        .sequenceNumber = client->sequence,
242        .state = RRXineramaScreenActive(screenInfo.screens[RR_XINERAMA_SCREEN])
243    };
244    if (client->swapped) {
245        swaps(&rep.sequenceNumber);
246        swapl(&rep.length);
247        swapl(&rep.state);
248    }
249    WriteToClient(client, sizeof(xXineramaIsActiveReply), &rep);
250    return Success;
251}
252
253static void
254RRXineramaWriteMonitor(ClientPtr client, RRMonitorPtr monitor)
255{
256    xXineramaScreenInfo scratch;
257
258    scratch.x_org = monitor->geometry.box.x1;
259    scratch.y_org = monitor->geometry.box.y1;
260    scratch.width = monitor->geometry.box.x2 - monitor->geometry.box.x1;
261    scratch.height = monitor->geometry.box.y2 - monitor->geometry.box.y1;
262
263    if (client->swapped) {
264        swaps(&scratch.x_org);
265        swaps(&scratch.y_org);
266        swaps(&scratch.width);
267        swaps(&scratch.height);
268    }
269
270    WriteToClient(client, sz_XineramaScreenInfo, &scratch);
271}
272
273int
274ProcRRXineramaQueryScreens(ClientPtr client)
275{
276    xXineramaQueryScreensReply rep;
277    ScreenPtr pScreen = screenInfo.screens[RR_XINERAMA_SCREEN];
278    int m;
279    RRMonitorPtr monitors = NULL;
280    int nmonitors = 0;
281
282    REQUEST_SIZE_MATCH(xXineramaQueryScreensReq);
283
284    if (RRXineramaScreenActive(pScreen)) {
285        RRGetInfo(pScreen, FALSE);
286        if (!RRMonitorMakeList(pScreen, TRUE, &monitors, &nmonitors))
287            return BadAlloc;
288    }
289
290    rep = (xXineramaQueryScreensReply) {
291        .type = X_Reply,
292        .sequenceNumber = client->sequence,
293        .length = bytes_to_int32(nmonitors * sz_XineramaScreenInfo),
294        .number = nmonitors
295    };
296    if (client->swapped) {
297        swaps(&rep.sequenceNumber);
298        swapl(&rep.length);
299        swapl(&rep.number);
300    }
301    WriteToClient(client, sizeof(xXineramaQueryScreensReply), &rep);
302
303    for (m = 0; m < nmonitors; m++)
304        RRXineramaWriteMonitor(client, &monitors[m]);
305
306    if (monitors)
307        RRMonitorFreeList(monitors, nmonitors);
308
309    return Success;
310}
311
312static int
313ProcRRXineramaDispatch(ClientPtr client)
314{
315    REQUEST(xReq);
316    switch (stuff->data) {
317    case X_PanoramiXQueryVersion:
318        return ProcRRXineramaQueryVersion(client);
319    case X_PanoramiXGetState:
320        return ProcRRXineramaGetState(client);
321    case X_PanoramiXGetScreenCount:
322        return ProcRRXineramaGetScreenCount(client);
323    case X_PanoramiXGetScreenSize:
324        return ProcRRXineramaGetScreenSize(client);
325    case X_XineramaIsActive:
326        return ProcRRXineramaIsActive(client);
327    case X_XineramaQueryScreens:
328        return ProcRRXineramaQueryScreens(client);
329    }
330    return BadRequest;
331}
332
333/* SProc */
334
335static int _X_COLD
336SProcRRXineramaQueryVersion(ClientPtr client)
337{
338    REQUEST(xPanoramiXQueryVersionReq);
339    swaps(&stuff->length);
340    REQUEST_SIZE_MATCH(xPanoramiXQueryVersionReq);
341    return ProcRRXineramaQueryVersion(client);
342}
343
344static int _X_COLD
345SProcRRXineramaGetState(ClientPtr client)
346{
347    REQUEST(xPanoramiXGetStateReq);
348    swaps(&stuff->length);
349    REQUEST_SIZE_MATCH(xPanoramiXGetStateReq);
350    swapl(&stuff->window);
351    return ProcRRXineramaGetState(client);
352}
353
354static int _X_COLD
355SProcRRXineramaGetScreenCount(ClientPtr client)
356{
357    REQUEST(xPanoramiXGetScreenCountReq);
358    swaps(&stuff->length);
359    REQUEST_SIZE_MATCH(xPanoramiXGetScreenCountReq);
360    swapl(&stuff->window);
361    return ProcRRXineramaGetScreenCount(client);
362}
363
364static int _X_COLD
365SProcRRXineramaGetScreenSize(ClientPtr client)
366{
367    REQUEST(xPanoramiXGetScreenSizeReq);
368    swaps(&stuff->length);
369    REQUEST_SIZE_MATCH(xPanoramiXGetScreenSizeReq);
370    swapl(&stuff->window);
371    swapl(&stuff->screen);
372    return ProcRRXineramaGetScreenSize(client);
373}
374
375static int _X_COLD
376SProcRRXineramaIsActive(ClientPtr client)
377{
378    REQUEST(xXineramaIsActiveReq);
379    swaps(&stuff->length);
380    REQUEST_SIZE_MATCH(xXineramaIsActiveReq);
381    return ProcRRXineramaIsActive(client);
382}
383
384static int _X_COLD
385SProcRRXineramaQueryScreens(ClientPtr client)
386{
387    REQUEST(xXineramaQueryScreensReq);
388    swaps(&stuff->length);
389    REQUEST_SIZE_MATCH(xXineramaQueryScreensReq);
390    return ProcRRXineramaQueryScreens(client);
391}
392
393int
394SProcRRXineramaDispatch(ClientPtr client)
395{
396    REQUEST(xReq);
397    switch (stuff->data) {
398    case X_PanoramiXQueryVersion:
399        return SProcRRXineramaQueryVersion(client);
400    case X_PanoramiXGetState:
401        return SProcRRXineramaGetState(client);
402    case X_PanoramiXGetScreenCount:
403        return SProcRRXineramaGetScreenCount(client);
404    case X_PanoramiXGetScreenSize:
405        return SProcRRXineramaGetScreenSize(client);
406    case X_XineramaIsActive:
407        return SProcRRXineramaIsActive(client);
408    case X_XineramaQueryScreens:
409        return SProcRRXineramaQueryScreens(client);
410    }
411    return BadRequest;
412}
413
414void
415RRXineramaExtensionInit(void)
416{
417#ifdef PANORAMIX
418    if (!noPanoramiXExtension)
419        return;
420#endif
421
422    if (noRRXineramaExtension)
423      return;
424
425    /*
426     * Xinerama isn't capable enough to have multiple protocol screens each
427     * with their own output geometry.  So if there's more than one protocol
428     * screen, just don't even try.
429     */
430    if (screenInfo.numScreens > 1)
431        return;
432
433    (void) AddExtension(PANORAMIX_PROTOCOL_NAME, 0, 0,
434                        ProcRRXineramaDispatch,
435                        SProcRRXineramaDispatch, NULL, StandardMinorOpcode);
436}
437