2 * Copyright 2002-2003 Red Hat Inc., Durham, North Carolina.
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:
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.
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
30 * Rickard E. (Rik) Faith <faith@redhat.com>
36 * It is possible for one of the DMX "backend displays" to actually be
37 * smaller than the dimensions of the backend X server. Therefore, it
38 * is possible for more than one of the DMX "backend displays" to be
39 * physically located on the same backend X server. This situation must
40 * be detected so that cursor motion can be handled in an expected
43 * We could analyze the names used for the DMX "backend displays" (e.g.,
44 * the names passed to the -display command-line parameter), but there
45 * are many possible names for a single X display, and failing to detect
46 * sameness leads to very unexpected results. Therefore, whenever the
47 * DMX server opens a window on a backend X server, a property value is
48 * queried and set on that backend to detect when another window is
49 * already open on that server.
51 * Further, it is possible that two different DMX server instantiations
52 * both have windows on the same physical backend X server. This case
53 * is also detected so that pointer input is not taken from that
54 * particular backend X server.
56 * The routines in this file handle the property management. */
58 #ifdef HAVE_DMX_CONFIG_H
59 #include <dmx-config.h>
65 #include <X11/Xmu/SysUtil.h> /* For XmuGetHostname */
67 /** Holds the window id of all DMX windows on the backend X server. */
68 #define DMX_ATOMNAME "DMX_NAME"
70 /** The identification string of this DMX server */
71 #define DMX_IDENT "Xdmx"
76 dmxPropertyErrorHandler(Display
* dpy
, XErrorEvent
* ev
)
81 static const unsigned char *
82 dmxPropertyIdentifier(void)
84 /* RATS: These buffers are only used in
85 * length-limited calls. */
88 static int initialized
= 0;
91 return (unsigned char *) buf
;
93 XmuGetHostname(hostname
, sizeof(hostname
));
94 snprintf(buf
, sizeof(buf
), "%s:%s:%s", DMX_IDENT
, hostname
, display
);
95 return (unsigned char *) buf
;
98 /** Starting with the \a start screen, iterate over all of the screens
99 * on the same physical X server as \a start, calling \a f with the
100 * screen and the \a closure. (The common case is that \a start is the
101 * only DMX window on the backend X server.) */
103 dmxPropertyIterate(DMXScreenInfo
* start
,
104 void *(*f
) (DMXScreenInfo
* dmxScreen
, void *),
110 if (!start
->beDisplay
)
112 return f(start
, closure
);
115 for (pt
= start
->next
; /* condition at end of loop */ ; pt
= pt
->next
) {
118 /* beDisplay ban be NULL if a screen was detached */
119 dmxLog(dmxDebug
, "pt = %p\n", pt
);
120 dmxLog(dmxDebug
, "pt->beDisplay = %p\n", pt
->beDisplay
);
121 if (pt
->beDisplay
&& (retval
= f(pt
, closure
)))
129 /** Returns 0 if this is the only Xdmx session on the display; 1
132 dmxPropertyCheckOtherServers(DMXScreenInfo
* dmxScreen
, Atom atom
)
134 Display
*dpy
= dmxScreen
->beDisplay
;
136 XTextProperty tproot
;
142 int (*dmxOldHandler
) (Display
*, XErrorEvent
*);
147 if (!XGetTextProperty(dpy
, RootWindow(dpy
, 0), &tproot
, atom
)
151 /* Ignore BadWindow errors for this
152 * routine because the window id stored
153 * in the property might be old */
154 dmxOldHandler
= XSetErrorHandler(dmxPropertyErrorHandler
);
155 for (pt
= (const char *) tproot
.value
; pt
&& *pt
; pt
= pt
? pt
+ 1 : NULL
) {
156 if ((pt
= strchr(pt
, ','))) {
157 Window win
= strtol(pt
+ 1, NULL
, 10);
159 if (XGetTextProperty(dpy
, win
, &tp
, atom
) && tp
.nitems
) {
160 if (!strncmp((char *) tp
.value
, DMX_IDENT
, strlen(DMX_IDENT
))) {
163 for (i
= 0; i
< count
; i
++)
164 if (!strcmp(list
[i
], (char *) tp
.value
)) {
171 dmxLogOutputWarning(dmxScreen
,
172 "%s also running on %s\n",
173 tp
.value
, dmxScreen
->name
);
174 list
= realloc(list
, ++count
* sizeof(*list
));
175 list
[count
- 1] = malloc(tp
.nitems
+ 2);
176 strncpy(list
[count
- 1], (char *) tp
.value
, tp
.nitems
+ 1);
182 XSetErrorHandler(dmxOldHandler
);
184 for (i
= 0; i
< count
; i
++)
189 dmxLogOutput(dmxScreen
, "No Xdmx server running on backend\n");
193 /** Returns NULL if this is the only Xdmx window on the display.
194 * Otherwise, returns a pointer to the dmxScreen of the other windows on
196 static DMXScreenInfo
*
197 dmxPropertyCheckOtherWindows(DMXScreenInfo
* dmxScreen
, Atom atom
)
199 Display
*dpy
= dmxScreen
->beDisplay
;
200 const unsigned char *id
= dmxPropertyIdentifier();
201 XTextProperty tproot
;
204 int (*dmxOldHandler
) (Display
*, XErrorEvent
*);
209 if (!XGetTextProperty(dpy
, RootWindow(dpy
, 0), &tproot
, atom
)
213 /* Ignore BadWindow errors for this
214 * routine because the window id stored
215 * in the property might be old */
216 dmxOldHandler
= XSetErrorHandler(dmxPropertyErrorHandler
);
217 for (pt
= (const char *) tproot
.value
; pt
&& *pt
; pt
= pt
? pt
+ 1 : NULL
) {
218 if ((pt
= strchr(pt
, ','))) {
219 Window win
= strtol(pt
+ 1, NULL
, 10);
221 if (XGetTextProperty(dpy
, win
, &tp
, atom
) && tp
.nitems
) {
222 dmxLog(dmxDebug
, "On %s/%lu: %s\n",
223 dmxScreen
->name
, win
, tp
.value
);
224 if (!strncmp((char *) tp
.value
, (char *) id
,
225 strlen((char *) id
))) {
228 if (!(pt
= strchr((char *) tp
.value
, ',')))
230 idx
= strtol(pt
+ 1, NULL
, 10);
231 if (idx
< 0 || idx
>= dmxNumScreens
)
233 if (dmxScreens
[idx
].scrnWin
!= win
)
235 XSetErrorHandler(dmxOldHandler
);
236 return &dmxScreens
[idx
];
242 XSetErrorHandler(dmxOldHandler
);
247 /** Returns 0 if this is the only Xdmx session on the display; 1
250 dmxPropertyDisplay(DMXScreenInfo
* dmxScreen
)
253 const unsigned char *id
= dmxPropertyIdentifier();
254 Display
*dpy
= dmxScreen
->beDisplay
;
259 atom
= XInternAtom(dpy
, DMX_ATOMNAME
, False
);
260 if (dmxPropertyCheckOtherServers(dmxScreen
, atom
)) {
261 dmxScreen
->shared
= 1;
264 XChangeProperty(dpy
, RootWindow(dpy
, 0), atom
, XA_STRING
, 8,
265 PropModeReplace
, id
, strlen((char *) id
));
269 /** Returns 1 if the dmxScreen and the display in \a name are on the
270 * same display, or 0 otherwise. We can't just compare the display
271 * names because there can be multiple synonyms for the same display,
272 * some of which cannot be determined without accessing the display
273 * itself (e.g., domain aliases or machines with multiple NICs). */
275 dmxPropertySameDisplay(DMXScreenInfo
* dmxScreen
, const char *name
)
277 Display
*dpy0
= dmxScreen
->beDisplay
;
280 Display
*dpy1
= NULL
;
291 if ((atom0
= XInternAtom(dpy0
, DMX_ATOMNAME
, True
)) == None
) {
292 dmxLog(dmxWarning
, "No atom on %s\n", dmxScreen
->name
);
295 if (!XGetTextProperty(dpy0
, RootWindow(dpy0
, 0), &tp0
, atom0
)
297 dmxLog(dmxWarning
, "No text property on %s\n", dmxScreen
->name
);
301 if (!(dpy1
= XOpenDisplay(name
))) {
302 dmxLog(dmxWarning
, "Cannot open %s\n", name
);
305 atom1
= XInternAtom(dpy1
, DMX_ATOMNAME
, True
);
307 dmxLog(dmxDebug
, "No atom on %s\n", name
);
310 if (!XGetTextProperty(dpy1
, RootWindow(dpy1
, 0), &tp1
, atom1
)
312 dmxLog(dmxDebug
, "No text property on %s\n", name
);
315 if (!strcmp((char *) tp0
.value
, (char *) tp1
.value
))
328 /** Prints a log message if \a dmxScreen is on the same backend X server
329 * as some other DMX backend (output) screen. Modifies the property
330 * (#DMX_ATOMNAME) on the backend X server to reflect the creation of \a
333 * The root window of the backend X server holds a list of window ids
334 * for all DMX windows (on this DMX server or some other DMX server).
336 * This list can then be iterated, and the property for each window can
337 * be examined. This property contains the following tuple (no quotes):
339 * "#DMX_IDENT:<hostname running DMX>:<display name of DMX>,<screen number>"
342 dmxPropertyWindow(DMXScreenInfo
* dmxScreen
)
345 const unsigned char *id
= dmxPropertyIdentifier();
346 Display
*dpy
= dmxScreen
->beDisplay
;
347 Window win
= dmxScreen
->scrnWin
;
348 DMXScreenInfo
*other
;
349 char buf
[128]; /* RATS: only used with snprintf */
352 return; /* FIXME: What should be done here if Xdmx is started
353 * with this screen initially detached?
356 atom
= XInternAtom(dpy
, DMX_ATOMNAME
, False
);
357 if ((other
= dmxPropertyCheckOtherWindows(dmxScreen
, atom
))) {
358 DMXScreenInfo
*tmp
= dmxScreen
->next
;
360 dmxScreen
->next
= (other
->next
? other
->next
: other
);
361 other
->next
= (tmp
? tmp
: dmxScreen
);
362 dmxLog(dmxDebug
, "%d/%s/%lu and %d/%s/%lu are on the same backend\n",
363 dmxScreen
->index
, dmxScreen
->name
, dmxScreen
->scrnWin
,
364 other
->index
, other
->name
, other
->scrnWin
);
367 snprintf(buf
, sizeof(buf
), ".%d,%lu", dmxScreen
->index
,
368 (long unsigned) win
);
369 XChangeProperty(dpy
, RootWindow(dpy
, 0), atom
, XA_STRING
, 8,
370 PropModeAppend
, (unsigned char *) buf
, strlen(buf
));
372 snprintf(buf
, sizeof(buf
), "%s,%d", id
, dmxScreen
->index
);
373 XChangeProperty(dpy
, win
, atom
, XA_STRING
, 8,
374 PropModeAppend
, (unsigned char *) buf
, strlen(buf
));