1 /*
2  * Copyright 2002-2004 Red Hat Inc., Durham, North Carolina.
3  *
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation on the rights to use, copy, modify, merge,
10  * publish, distribute, sublicense, and/or sell copies of the Software,
11  * and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial
16  * portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
22  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25  * SOFTWARE.
26  */
27 
28 /*
29  * Authors:
30  *   Rickard E. (Rik) Faith <faith@redhat.com>
31  *
32  */
33 
34 /* THIS IS NOT AN X CONSORTIUM STANDARD */
35 
36 /** \file
37  * This file implements the client-side part of the DMX protocol.  It
38  * can be included in client applications by linking with the libdmx.a
39  * library. */
40 
41 #ifdef HAVE_CONFIG_H
42 # include "config.h"
43 #endif
44 #include <X11/Xlibint.h>
45 #include <X11/extensions/Xext.h>
46 #define EXTENSION_PROC_ARGS void *
47 #include <X11/extensions/extutil.h>
48 #include <X11/extensions/dmxproto.h>
49 #include <X11/extensions/dmxext.h>
50 #include <limits.h>
51 
52 static XExtensionInfo dmx_extension_info_data;
53 static XExtensionInfo *dmx_extension_info = &dmx_extension_info_data;
54 static const char     *dmx_extension_name = DMX_EXTENSION_NAME;
55 
56 #define DMXCheckExtension(dpy,i,val) \
57   XextCheckExtension(dpy, i, dmx_extension_name, val)
58 #define DMXSimpleCheckExtension(dpy,i) \
59   XextSimpleCheckExtension(dpy, i, dmx_extension_name)
60 
61 /*****************************************************************************
62  *                                                                           *
63  *                         private utility routines                          *
64  *                                                                           *
65  *****************************************************************************/
66 
67 static int close_display(Display *dpy, XExtCodes *extCodes);
68 static /* const */ XExtensionHooks dmx_extension_hooks = {
69     NULL,				/* create_gc */
70     NULL,				/* copy_gc */
71     NULL,				/* flush_gc */
72     NULL,				/* free_gc */
73     NULL,				/* create_font */
74     NULL,				/* free_font */
75     close_display,			/* close_display */
76     NULL,				/* wire_to_event */
77     NULL,				/* event_to_wire */
78     NULL,				/* error */
79     NULL,				/* error_string */
80 };
81 
82 static XEXT_GENERATE_FIND_DISPLAY(find_display, dmx_extension_info,
83                                   dmx_extension_name,
84                                   &dmx_extension_hooks,
85                                   0, NULL)
86 
XEXT_GENERATE_CLOSE_DISPLAY(close_display,dmx_extension_info)87 static XEXT_GENERATE_CLOSE_DISPLAY(close_display, dmx_extension_info)
88 
89 
90 /*****************************************************************************
91  *                                                                           *
92  *                  public DMX Extension routines                            *
93  *                                                                           *
94  *****************************************************************************/
95 
96 /** If the server has the DMX extension, the event and error bases will
97  * be placed in \a event_basep and \a error_basep, and True will be
98  * returned.  Otherwise, False will be returned.
99  *
100  * Available in DMX Protocol Version 1.0 */
101 Bool DMXQueryExtension(Display *dpy, int *event_basep, int *error_basep)
102 {
103     XExtDisplayInfo *info = find_display(dpy);
104 
105     if (XextHasExtension(info)) {
106 	*event_basep = info->codes->first_event;
107 	*error_basep = info->codes->first_error;
108 	return True;
109     } else {
110 	return False;
111     }
112 }
113 
114 /** If the DMXQueryVersion protocol request returns version information
115  * from the server, \a majorVersion, \a minorVersion, and \a
116  * patchVersion are filled in with the appropriate information and True
117  * is returned.  Otherwise, False will be returned.
118  *
119  * Available in DMX Protocol Version 1.0 */
DMXQueryVersion(Display * dpy,int * majorVersion,int * minorVersion,int * patchVersion)120 Bool DMXQueryVersion(Display *dpy,
121                      int *majorVersion, int *minorVersion, int *patchVersion)
122 {
123     XExtDisplayInfo       *info = find_display(dpy);
124     xDMXQueryVersionReply rep;
125     xDMXQueryVersionReq   *req;
126 
127     DMXCheckExtension(dpy, info, False);
128 
129     LockDisplay(dpy);
130     GetReq(DMXQueryVersion, req);
131     req->reqType     = info->codes->major_opcode;
132     req->dmxReqType  = X_DMXQueryVersion;
133     if (!_XReply(dpy, (xReply *)&rep, 0, xTrue)) {
134 	UnlockDisplay(dpy);
135 	SyncHandle();
136 	return False;
137     }
138     *majorVersion = rep.majorVersion;
139     *minorVersion = rep.minorVersion;
140     *patchVersion = rep.patchVersion;
141     UnlockDisplay(dpy);
142     SyncHandle();
143     return True;
144 }
145 
146 /** Flush all pending dmxSync requests in DMX server.
147  *
148  * Available in DMX Protocol Version 1.5 */
DMXSync(Display * dpy)149 Bool DMXSync(Display *dpy)
150 {
151     XExtDisplayInfo *info = find_display(dpy);
152     xDMXSyncReply   rep;
153     xDMXSyncReq     *req;
154 
155     DMXCheckExtension(dpy, info, False);
156 
157     LockDisplay(dpy);
158     GetReq(DMXSync, req);
159     req->reqType    = info->codes->major_opcode;
160     req->dmxReqType = X_DMXSync;
161     if (!_XReply(dpy, (xReply *)&rep, 0, xTrue)) {
162         UnlockDisplay(dpy);
163         SyncHandle();
164         return False;
165     }
166 
167     UnlockDisplay(dpy);
168     SyncHandle();
169     return rep.status == Success ? True : False;
170 }
171 
172 /** The creation of the specified \a window will be forced.
173  *
174  * Available in DMX Protocol Version 1.2
175  * Reply added in DMX Protocol Version 2.0 */
DMXForceWindowCreation(Display * dpy,Window window)176 Bool DMXForceWindowCreation(Display *dpy, Window window)
177 {
178     XExtDisplayInfo              *info = find_display(dpy);
179     xDMXForceWindowCreationReq   *req;
180     xDMXForceWindowCreationReply rep;
181 
182     DMXCheckExtension(dpy, info, False);
183 
184     LockDisplay(dpy);
185     GetReq(DMXForceWindowCreation, req);
186     req->reqType    = info->codes->major_opcode;
187     req->dmxReqType = X_DMXForceWindowCreation;
188     req->window     = window;
189     if (!_XReply(dpy, (xReply *)&rep, 0, xTrue)) {
190         UnlockDisplay(dpy);
191         SyncHandle();
192         return False;
193     }
194 
195     UnlockDisplay(dpy);
196     SyncHandle();
197     return rep.status == Success ? True : False;
198 }
199 
200 /** If the DMXGetScreenCount protocol request returns the screen count,
201  * the value will be placed in \a screen_count, and True will be
202  * returned.  Otherwise, False will be returned.
203  *
204  * Available in DMX Protocol Version 1.0 */
DMXGetScreenCount(Display * dpy,int * screen_count)205 Bool DMXGetScreenCount(Display *dpy, int *screen_count)
206 {
207     XExtDisplayInfo         *info = find_display(dpy);
208     xDMXGetScreenCountReply rep;
209     xDMXGetScreenCountReq   *req;
210 
211     DMXCheckExtension(dpy, info, False);
212 
213     LockDisplay(dpy);
214     GetReq(DMXGetScreenCount, req);
215     req->reqType    = info->codes->major_opcode;
216     req->dmxReqType = X_DMXGetScreenCount;
217     if (!_XReply(dpy, (xReply *)&rep, 0, xTrue)) {
218         UnlockDisplay(dpy);
219         SyncHandle();
220         return False;
221     }
222     *screen_count = rep.screenCount;
223     UnlockDisplay(dpy);
224     SyncHandle();
225     return True;
226 }
227 
228 /** If the DMXGetScreenAttributes protocol request returns information
229  * for the specified \a physical_screen, information about the screen
230  * will be placed in \a attr, and True will be returned.  Otherwise,
231  * False will be returned.
232  *
233  * Available in DMX Protocol Version 1.0; Modified in Version 2.0 */
DMXGetScreenAttributes(Display * dpy,int physical_screen,DMXScreenAttributes * attr)234 Bool DMXGetScreenAttributes(Display *dpy, int physical_screen,
235                             DMXScreenAttributes *attr)
236 {
237     XExtDisplayInfo              *info = find_display(dpy);
238     xDMXGetScreenAttributesReply rep;
239     xDMXGetScreenAttributesReq   *req;
240     Bool                          ret = False;
241 
242     DMXCheckExtension(dpy, info, False);
243 
244     LockDisplay(dpy);
245     GetReq(DMXGetScreenAttributes, req);
246     req->reqType        = info->codes->major_opcode;
247     req->dmxReqType     = X_DMXGetScreenAttributes;
248     req->physicalScreen = physical_screen;
249     if (!_XReply(dpy, (xReply *)&rep,
250                  (SIZEOF(xDMXGetScreenAttributesReply) - 32) >> 2, xFalse)) {
251         UnlockDisplay(dpy);
252         SyncHandle();
253         return False;
254     }
255 
256     if (rep.displayNameLength < 1024)
257         attr->displayName = Xmalloc(rep.displayNameLength + 1 + 4 /* for pad */);
258     else
259         attr->displayName = NULL;  /* name length is unbelievable, reject */
260     if (attr->displayName == NULL) {
261         _XEatDataWords(dpy, rep.length);
262         goto end;
263     }
264     _XReadPad(dpy, attr->displayName, rep.displayNameLength);
265     attr->displayName[rep.displayNameLength] = '\0';
266     attr->logicalScreen       = rep.logicalScreen;
267 
268     attr->screenWindowWidth   = rep.screenWindowWidth;
269     attr->screenWindowHeight  = rep.screenWindowHeight;
270     attr->screenWindowXoffset = rep.screenWindowXoffset;
271     attr->screenWindowYoffset = rep.screenWindowYoffset;
272 
273     attr->rootWindowWidth     = rep.rootWindowWidth;
274     attr->rootWindowHeight    = rep.rootWindowHeight;
275     attr->rootWindowXoffset   = rep.rootWindowXoffset;
276     attr->rootWindowYoffset   = rep.rootWindowYoffset;
277     attr->rootWindowXorigin   = rep.rootWindowXorigin;
278     attr->rootWindowYorigin   = rep.rootWindowYorigin;
279 
280     ret = True;
281 
282   end:
283     UnlockDisplay(dpy);
284     SyncHandle();
285     return ret;
286 }
287 
_DMXGetScreenAttribute(int bit,DMXScreenAttributes * attr)288 static CARD32 _DMXGetScreenAttribute(int bit, DMXScreenAttributes *attr)
289 {
290     switch (1 << bit) {
291     case DMXScreenWindowWidth:   return attr->screenWindowWidth;
292     case DMXScreenWindowHeight:  return attr->screenWindowHeight;
293     case DMXScreenWindowXoffset: return attr->screenWindowXoffset;
294     case DMXScreenWindowYoffset: return attr->screenWindowYoffset;
295     case DMXRootWindowWidth:     return attr->rootWindowWidth;
296     case DMXRootWindowHeight:    return attr->rootWindowHeight;
297     case DMXRootWindowXoffset:   return attr->rootWindowXoffset;
298     case DMXRootWindowYoffset:   return attr->rootWindowYoffset;
299     case DMXRootWindowXorigin:   return attr->rootWindowXorigin;
300     case DMXRootWindowYorigin:   return attr->rootWindowYorigin;
301     default:                     return 0;
302     }
303 }
304 
_DMXDumpScreenAttributes(Display * dpy,unsigned long mask,DMXScreenAttributes * attr)305 static int _DMXDumpScreenAttributes(Display *dpy,
306                                     unsigned long mask,
307                                     DMXScreenAttributes *attr)
308 {
309     int           i;
310     unsigned long value_list[32];
311     unsigned long *value = value_list;
312     int           count  = 0;
313 
314     for (i = 0; i < 32; i++) {
315         if (mask & (1 << i)) {
316             *value++ = _DMXGetScreenAttribute(i, attr);
317             ++count;
318         }
319     }
320     Data32(dpy, value_list, count * sizeof(CARD32));
321     return count;
322 }
323 
_DMXGetInputAttribute(int bit,DMXInputAttributes * attr)324 static CARD32 _DMXGetInputAttribute(int bit, DMXInputAttributes *attr)
325 {
326     switch (1 << bit) {
327     case DMXInputType:
328         switch (attr->inputType) {
329         case DMXLocalInputType:   return 0;
330         case DMXConsoleInputType: return 1;
331         case DMXBackendInputType: return 2;
332         }
333         return attr->inputType;
334     case DMXInputPhysicalScreen: return attr->physicalScreen;
335     case DMXInputSendsCore:      return attr->sendsCore;
336     default:                     return 0;
337     }
338 }
339 
_DMXDumpInputAttributes(Display * dpy,unsigned long mask,DMXInputAttributes * attr)340 static int _DMXDumpInputAttributes(Display *dpy,
341                                    unsigned long mask,
342                                    DMXInputAttributes *attr)
343 {
344     int           i;
345     unsigned long value_list[32];
346     unsigned long *value = value_list;
347     int           count = 0;
348 
349     for (i = 0; i < 32; i++) {
350         if (mask & (1 << i)) {
351             *value++ = _DMXGetInputAttribute(i, attr);
352             ++count;
353         }
354     }
355     Data32(dpy, value_list, count * sizeof(CARD32));
356     return count;
357 }
358 
359 /** Change geometries and positions of the DMX screen and root windows
360  * on the back-end X server. */
DMXChangeScreensAttributes(Display * dpy,int screen_count,int * screens,int mask_count,unsigned int * masks,DMXScreenAttributes * attrs,int * error_screen)361 int DMXChangeScreensAttributes(Display *dpy,
362                                int screen_count,
363                                int *screens,
364                                int mask_count,
365                                unsigned int *masks,
366                                DMXScreenAttributes *attrs, /* vector */
367                                int *error_screen)
368 {
369     XExtDisplayInfo                  *info = find_display(dpy);
370     xDMXChangeScreensAttributesReply rep;
371     xDMXChangeScreensAttributesReq   *req;
372     int                              i;
373     unsigned int                     mask  = 0;
374     CARD32                           *screen_list;
375     CARD32                           *mask_list;
376 
377     DMXCheckExtension(dpy, info, False);
378 
379     if (screen_count < 1 || mask_count < 1) return DmxBadValue;
380 
381     LockDisplay(dpy);
382     GetReq(DMXChangeScreensAttributes, req);
383     req->reqType      = info->codes->major_opcode;
384     req->dmxReqType   = X_DMXChangeScreensAttributes;
385     req->screenCount  = screen_count;
386     req->maskCount    = mask_count;
387     req->length      += screen_count + mask_count;
388 
389     screen_list = (CARD32 *)Xmalloc(sizeof(*screen_list) * screen_count);
390     for (i = 0; i < screen_count; i++) screen_list[i] = screens[i];
391     Data32(dpy, screen_list, screen_count * sizeof(CARD32));
392     Xfree(screen_list);
393 
394     mask_list = (CARD32 *)Xmalloc(sizeof(*mask_list) * mask_count);
395     for (i = 0; i < mask_count;   i++) mask_list[i]   = masks[i];
396     Data32(dpy, mask_list, mask_count * sizeof(CARD32));
397     Xfree(mask_list);
398 
399     for (i = 0; i < screen_count; i++) {
400         if (i < mask_count) mask = masks[i];
401         req->length += _DMXDumpScreenAttributes(dpy, mask, attrs + i);
402     }
403 
404     if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
405         UnlockDisplay(dpy);
406         SyncHandle();
407         return DmxBadReply;
408     }
409     if (error_screen) *error_screen = rep.errorScreen;
410     UnlockDisplay(dpy);
411     SyncHandle();
412     return rep.status;
413 }
414 
415 /** Add a screen. */
DMXAddScreen(Display * dpy,const char * displayName,unsigned int mask,DMXScreenAttributes * attr,int * screen)416 Bool DMXAddScreen(Display *dpy, const char *displayName, unsigned int mask,
417                   DMXScreenAttributes *attr, int *screen)
418 {
419     XExtDisplayInfo    *info = find_display(dpy);
420     xDMXAddScreenReply rep;
421     xDMXAddScreenReq   *req;
422     int                length;
423     int                paddedLength;
424 
425     if (!screen)
426 	return False;
427 
428     DMXCheckExtension(dpy, info, False);
429 
430     LockDisplay(dpy);
431     GetReq(DMXAddScreen, req);
432     length                 = displayName ? strlen(displayName) : 0;
433     paddedLength           = (length + 3) & ~3;
434     req->reqType           = info->codes->major_opcode;
435     req->dmxReqType        = X_DMXAddScreen;
436     req->displayNameLength = length;
437     req->physicalScreen    = *screen;
438     req->valueMask         = mask;
439     req->length           += paddedLength/4;
440     req->length           += _DMXDumpScreenAttributes(dpy, mask, attr);
441 
442     if (length) {
443         char *buffer       = Xcalloc(paddedLength, 1);
444         memcpy(buffer, displayName, length);
445         Data32(dpy, buffer, paddedLength);
446         Xfree(buffer);
447     }
448 
449     if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
450         UnlockDisplay(dpy);
451         SyncHandle();
452         return False;
453     }
454     if (screen) *screen = rep.physicalScreen;
455     UnlockDisplay(dpy);
456     SyncHandle();
457     return rep.status == Success ? True : False;
458 }
459 
460 /** Remove a screen. */
DMXRemoveScreen(Display * dpy,int screen)461 Bool DMXRemoveScreen(Display *dpy, int screen)
462 {
463     XExtDisplayInfo       *info = find_display(dpy);
464     xDMXRemoveScreenReply rep;
465     xDMXRemoveScreenReq   *req;
466 
467     DMXCheckExtension(dpy, info, False);
468 
469     LockDisplay(dpy);
470     GetReq(DMXRemoveScreen, req);
471     req->reqType           = info->codes->major_opcode;
472     req->dmxReqType        = X_DMXRemoveScreen;
473     req->physicalScreen    = screen;
474 
475     if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
476         UnlockDisplay(dpy);
477         SyncHandle();
478         return False;
479     }
480     UnlockDisplay(dpy);
481     SyncHandle();
482     return rep.status == Success ? True : False;
483 }
484 
485 /** If the DMXGetWindowAttributes protocol request returns information
486  * about the specified \a window, the number of screens for which
487  * information is available will be returned in \a screen_count and
488  * information about the first \a available_count of those screens will
489  * be placed in \a inf.  Because this call transports a great deal of
490  * information over the wire, please call #DMXGetScreenCount first, and
491  * make sure \a inf is that large.
492  *
493  * Note that if the specified \a window has not yet been mapped when
494  * #DMXGetWindowAttributes is called, then a subsequent XMapWindow call
495  * might be buffered in xlib while requests directly to the back-end X
496  * servers are processed.  This race condition can be solved by calling
497  * #DMXSync before talking directly to the back-end X servers.
498  *
499  * Available in DMX Protocol Version 1.0, but not working correctly
500  * until DMX Protocol Version 1.4 */
DMXGetWindowAttributes(Display * dpy,Window window,int * screen_count,int available_count,DMXWindowAttributes * inf)501 Bool DMXGetWindowAttributes(Display *dpy, Window window,
502                             int *screen_count, int available_count,
503                             DMXWindowAttributes *inf)
504 {
505     XExtDisplayInfo              *info = find_display(dpy);
506     xDMXGetWindowAttributesReply rep;
507     xDMXGetWindowAttributesReq   *req;
508     unsigned long                current;
509     CARD32                       *screens; /* Must match protocol size */
510     CARD32                       *windows; /* Must match protocol size */
511     XRectangle                   *pos;     /* Must match protocol size */
512     XRectangle                   *vis;     /* Must match protocol size */
513     Bool                          ret = False;
514 
515     DMXCheckExtension(dpy, info, False);
516 
517     LockDisplay(dpy);
518     GetReq(DMXGetWindowAttributes, req);
519     req->reqType    = info->codes->major_opcode;
520     req->dmxReqType = X_DMXGetWindowAttributes;
521     req->window     = window;
522     if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
523         UnlockDisplay(dpy);
524         SyncHandle();
525         return False;
526     }
527 
528     /*
529      * rep.screenCount is a CARD32 so could be as large as 2^32
530      * The X11 protocol limits the total screen size to 64k x 64k,
531      * and no screen can be smaller than a pixel.  While technically
532      * that means we could theoretically reach 2^32 screens, and that's
533      * not even taking overlap into account, 64k seems far larger than
534      * any reasonable configuration, so we limit to that to prevent both
535      * integer overflow in the size calculations, and bad X server
536      * responses causing massive memory allocation.
537      */
538     if (rep.screenCount < 65536) {
539         screens    = Xmalloc(rep.screenCount * sizeof(*screens));
540         windows    = Xmalloc(rep.screenCount * sizeof(*windows));
541         pos        = Xmalloc(rep.screenCount * sizeof(*pos));
542         vis        = Xmalloc(rep.screenCount * sizeof(*vis));
543     } else {
544         screens = windows = NULL;
545         pos = vis = NULL;
546     }
547 
548     if (!screens || !windows || !pos || !vis) {
549         _XEatDataWords(dpy, rep.length);
550         goto end;
551     }
552 
553     _XRead(dpy, (char *)screens, rep.screenCount * sizeof(*screens));
554     _XRead(dpy, (char *)windows, rep.screenCount * sizeof(*windows));
555     _XRead(dpy, (char *)pos,     rep.screenCount * sizeof(*pos));
556     _XRead(dpy, (char *)vis,     rep.screenCount * sizeof(*vis));
557 
558     *screen_count = rep.screenCount;
559     for (current = 0;
560          current < rep.screenCount && current < (unsigned)available_count;
561          current++, inf++) {
562         inf->screen    = screens[current];
563         inf->window    = windows[current];
564         inf->pos       = pos[current];
565         inf->vis       = vis[current];
566     }
567     ret = True;
568 
569   end:
570     Xfree(vis);
571     Xfree(pos);
572     Xfree(windows);
573     Xfree(screens);
574 
575     UnlockDisplay(dpy);
576     SyncHandle();
577     return ret;
578 }
579 
580 /** If the DMXGetDesktopAttributes protocol request returns information
581  * correctly, the information will be placed in \a attr, and True will
582  * be returned.  Otherwise, False will be returned.
583  *
584  * Available in DMX Protocol Version 2.0 */
DMXGetDesktopAttributes(Display * dpy,DMXDesktopAttributes * attr)585 Bool DMXGetDesktopAttributes(Display *dpy, DMXDesktopAttributes *attr)
586 {
587     XExtDisplayInfo               *info = find_display(dpy);
588     xDMXGetDesktopAttributesReply rep;
589     xDMXGetDesktopAttributesReq   *req;
590 
591     DMXCheckExtension(dpy, info, False);
592 
593     LockDisplay(dpy);
594     GetReq(DMXGetDesktopAttributes, req);
595     req->reqType        = info->codes->major_opcode;
596     req->dmxReqType     = X_DMXGetDesktopAttributes;
597     if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
598         UnlockDisplay(dpy);
599         SyncHandle();
600         return False;
601     }
602     attr->width  = rep.width;
603     attr->height = rep.height;
604     attr->shiftX = rep.shiftX;
605     attr->shiftY = rep.shiftY;
606 
607     UnlockDisplay(dpy);
608     SyncHandle();
609     return True;
610 }
611 
_DMXGetDesktopAttribute(int bit,DMXDesktopAttributes * attr)612 static CARD32 _DMXGetDesktopAttribute(int bit, DMXDesktopAttributes *attr)
613 {
614     switch (1 << bit) {
615     case DMXDesktopWidth:  return attr->width;
616     case DMXDesktopHeight: return attr->height;
617     case DMXDesktopShiftX: return attr->shiftX;
618     case DMXDesktopShiftY: return attr->shiftY;
619     default:               return 0;
620     }
621 }
622 
_DMXDumpDesktopAttributes(Display * dpy,unsigned long mask,DMXDesktopAttributes * attr)623 static int _DMXDumpDesktopAttributes(Display *dpy,
624                                      unsigned long mask,
625                                      DMXDesktopAttributes *attr)
626 {
627     int           i;
628     unsigned long value_list[32];
629     unsigned long *value = value_list;
630     int           count  = 0;
631 
632     for (i = 0; i < 32; i++) {
633         if (mask & (1 << i)) {
634             *value++ = _DMXGetDesktopAttribute(i, attr);
635             ++count;
636         }
637     }
638     Data32(dpy, value_list, count * sizeof(CARD32));
639     return count;
640 }
641 
642 /** Change the global bounding box and origin offset.
643  *
644  * Available in DMX Protocol Version 2.0 */
DMXChangeDesktopAttributes(Display * dpy,unsigned int mask,DMXDesktopAttributes * attr)645 int DMXChangeDesktopAttributes(Display *dpy,
646                                unsigned int mask,
647                                DMXDesktopAttributes *attr)
648 {
649     XExtDisplayInfo                  *info = find_display(dpy);
650     xDMXChangeDesktopAttributesReply rep;
651     xDMXChangeDesktopAttributesReq   *req;
652 
653     DMXCheckExtension(dpy, info, False);
654 
655     LockDisplay(dpy);
656     GetReq(DMXChangeDesktopAttributes, req);
657     req->reqType      = info->codes->major_opcode;
658     req->dmxReqType   = X_DMXChangeDesktopAttributes;
659     req->valueMask    = mask;
660     req->length      +=_DMXDumpDesktopAttributes(dpy, mask, attr);
661 
662     if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
663         UnlockDisplay(dpy);
664         SyncHandle();
665         return DmxBadReply;
666     }
667     UnlockDisplay(dpy);
668     SyncHandle();
669     return rep.status;
670 }
671 
672 /** If the DMXGetInputCount protocol request returns the input count,
673  * the value will be placed in \a input_count, and True will be
674  * returned.  Otherwise, False will be returned.
675  *
676  * Available in DMX Protocol Version 1.1 */
DMXGetInputCount(Display * dpy,int * input_count)677 Bool DMXGetInputCount(Display *dpy, int *input_count)
678 {
679     XExtDisplayInfo         *info = find_display(dpy);
680     xDMXGetInputCountReply  rep;
681     xDMXGetInputCountReq    *req;
682 
683     DMXCheckExtension(dpy, info, False);
684 
685     LockDisplay(dpy);
686     GetReq(DMXGetInputCount, req);
687     req->reqType    = info->codes->major_opcode;
688     req->dmxReqType = X_DMXGetInputCount;
689     if (!_XReply(dpy, (xReply *)&rep, 0, xTrue)) {
690         UnlockDisplay(dpy);
691         SyncHandle();
692         return False;
693     }
694     *input_count = rep.inputCount;
695     UnlockDisplay(dpy);
696     SyncHandle();
697     return True;
698 }
699 
700 /** If the DMXGetInputAttributes protocol request returns information
701  * about the input device with the specified \a id, information about
702  * the input device will be placed in \a inf, and True will be returned.
703  * Otherwise, False will be returned.
704  *
705  * Available in DMX Protocol Version 1.1 */
DMXGetInputAttributes(Display * dpy,int id,DMXInputAttributes * inf)706 Bool DMXGetInputAttributes(Display *dpy, int id, DMXInputAttributes *inf)
707 {
708     XExtDisplayInfo             *info = find_display(dpy);
709     xDMXGetInputAttributesReply rep;
710     xDMXGetInputAttributesReq   *req;
711     char                        *buffer;
712     Bool                         ret = False;
713 
714     DMXCheckExtension(dpy, info, False);
715 
716     LockDisplay(dpy);
717     GetReq(DMXGetInputAttributes, req);
718     req->reqType    = info->codes->major_opcode;
719     req->dmxReqType = X_DMXGetInputAttributes;
720     req->deviceId   = id;
721     if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
722         UnlockDisplay(dpy);
723         SyncHandle();
724         return False;
725     }
726 
727     if (rep.nameLength < 1024)
728         buffer      = Xmalloc(rep.nameLength + 1 + 4 /* for pad */);
729     else
730         buffer      = NULL;  /* name length is unbelievable, reject */
731 
732     if (buffer == NULL) {
733         _XEatDataWords(dpy, rep.length);
734         goto end;
735     }
736 
737     switch (rep.inputType) {
738     case 0: inf->inputType = DMXLocalInputType;   break;
739     case 1: inf->inputType = DMXConsoleInputType; break;
740     case 2: inf->inputType = DMXBackendInputType; break;
741     }
742 
743     inf->physicalScreen = rep.physicalScreen;
744     inf->physicalId     = rep.physicalId;
745     inf->isCore         = rep.isCore;
746     inf->sendsCore      = rep.sendsCore;
747     inf->detached       = rep.detached;
748     _XReadPad(dpy, buffer, rep.nameLength);
749     buffer[rep.nameLength] = '\0';
750     inf->name           = buffer;
751     ret = True;
752   end:
753     UnlockDisplay(dpy);
754     SyncHandle();
755     return ret;
756 }
757 
758 /** Add input. */
DMXAddInput(Display * dpy,unsigned int mask,DMXInputAttributes * attr,int * id)759 Bool DMXAddInput(Display *dpy, unsigned int mask, DMXInputAttributes *attr,
760                  int *id)
761 {
762     XExtDisplayInfo         *info = find_display(dpy);
763     xDMXAddInputReply       rep;
764     xDMXAddInputReq         *req;
765     int                     length;
766     int                     paddedLength;
767 
768     DMXCheckExtension(dpy, info, False);
769 
770     LockDisplay(dpy);
771     GetReq(DMXAddInput, req);
772     length                 = attr->name ? strlen(attr->name) : 0;
773     paddedLength           = (length + 3) & ~3;
774     req->reqType           = info->codes->major_opcode;
775     req->dmxReqType        = X_DMXAddInput;
776     req->displayNameLength = length;
777     req->valueMask         = mask;
778     req->length           += paddedLength/4;
779     req->length           += _DMXDumpInputAttributes(dpy, mask, attr);
780 
781     if (length) {
782         char *buffer       = Xcalloc(paddedLength, 1);
783         memcpy(buffer, attr->name, paddedLength);
784         Data32(dpy, buffer, paddedLength);
785         Xfree(buffer);
786     }
787 
788     if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
789         UnlockDisplay(dpy);
790         SyncHandle();
791         return False;
792     }
793     if (id) *id = rep.physicalId;
794     UnlockDisplay(dpy);
795     SyncHandle();
796     return rep.status == Success ? True : False;
797 }
798 
799 /** Add backend input (a helper function that calls #DMXAddInput). */
DMXAddBackendInput(Display * dpy,int screen,int sendsCore,int * newId)800 Bool DMXAddBackendInput(Display *dpy, int screen, int sendsCore, int *newId)
801 {
802     DMXInputAttributes attr;
803     unsigned int       mask = (DMXInputType
804                                | DMXInputPhysicalScreen
805                                | DMXInputSendsCore);
806 
807     attr.inputType        = DMXBackendInputType;
808     attr.physicalScreen   = screen;
809     attr.sendsCore        = sendsCore;
810     attr.name             = NULL;
811     return DMXAddInput(dpy, mask, &attr, newId);
812 }
813 
814 /** Add console input (a helper function that calls #DMXAddInput). */
DMXAddConsoleInput(Display * dpy,const char * name,int sendsCore,int * newId)815 Bool DMXAddConsoleInput(Display *dpy, const char *name, int sendsCore,
816                         int *newId)
817 {
818     DMXInputAttributes attr;
819     unsigned int       mask = (DMXInputType
820                                | DMXInputSendsCore);
821 
822     attr.inputType        = DMXConsoleInputType;
823     attr.physicalScreen   = 0;
824     attr.sendsCore        = sendsCore;
825     attr.name             = name;
826     return DMXAddInput(dpy, mask, &attr, newId);
827 }
828 
829 /** Remove an input. */
DMXRemoveInput(Display * dpy,int id)830 Bool DMXRemoveInput(Display *dpy, int id)
831 {
832     XExtDisplayInfo      *info = find_display(dpy);
833     xDMXRemoveInputReply rep;
834     xDMXRemoveInputReq   *req;
835 
836     DMXCheckExtension(dpy, info, False);
837 
838     LockDisplay(dpy);
839     GetReq(DMXRemoveInput, req);
840     req->reqType          = info->codes->major_opcode;
841     req->dmxReqType       = X_DMXRemoveInput;
842     req->physicalId       = id;
843 
844     if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
845         UnlockDisplay(dpy);
846         SyncHandle();
847         return False;
848     }
849     UnlockDisplay(dpy);
850     SyncHandle();
851     return rep.status == Success ? True : False;
852 }
853