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