Imported Upstream version 1.15.1
[deb_xorg-server.git] / hw / dmx / dmxprop.c
CommitLineData
a09e091a
JB
1/*
2 * Copyright 2002-2003 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/** \file
35 *
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
41 * fashion.
42 *
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.
50 *
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.
55 *
56 * The routines in this file handle the property management. */
57
58#ifdef HAVE_DMX_CONFIG_H
59#include <dmx-config.h>
60#endif
61
62#include "dmx.h"
63#include "dmxprop.h"
64#include "dmxlog.h"
65#include <X11/Xmu/SysUtil.h> /* For XmuGetHostname */
66
67/** Holds the window id of all DMX windows on the backend X server. */
68#define DMX_ATOMNAME "DMX_NAME"
69
70/** The identification string of this DMX server */
71#define DMX_IDENT "Xdmx"
72
73extern char *display;
74
75static int
76dmxPropertyErrorHandler(Display * dpy, XErrorEvent * ev)
77{
78 return 0;
79}
80
81static const unsigned char *
82dmxPropertyIdentifier(void)
83{
84 /* RATS: These buffers are only used in
85 * length-limited calls. */
86 char hostname[256];
87 static char buf[128];
88 static int initialized = 0;
89
90 if (initialized++)
91 return (unsigned char *) buf;
92
93 XmuGetHostname(hostname, sizeof(hostname));
94 snprintf(buf, sizeof(buf), "%s:%s:%s", DMX_IDENT, hostname, display);
95 return (unsigned char *) buf;
96}
97
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.) */
102void *
103dmxPropertyIterate(DMXScreenInfo * start,
104 void *(*f) (DMXScreenInfo * dmxScreen, void *),
105 void *closure)
106{
107 DMXScreenInfo *pt;
108
109 if (!start->next) {
110 if (!start->beDisplay)
111 return NULL;
112 return f(start, closure);
113 }
114
115 for (pt = start->next; /* condition at end of loop */ ; pt = pt->next) {
116 void *retval;
117
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)))
122 return retval;
123 if (pt == start)
124 break;
125 }
126 return NULL;
127}
128
129/** Returns 0 if this is the only Xdmx session on the display; 1
130 * otherwise. */
131static int
132dmxPropertyCheckOtherServers(DMXScreenInfo * dmxScreen, Atom atom)
133{
134 Display *dpy = dmxScreen->beDisplay;
135 XTextProperty tp;
136 XTextProperty tproot;
137 const char *pt;
138 int retcode = 0;
139 char **list = NULL;
140 int count = 0;
141 int i;
142 int (*dmxOldHandler) (Display *, XErrorEvent *);
143
144 if (!dpy)
145 return 0;
146
147 if (!XGetTextProperty(dpy, RootWindow(dpy, 0), &tproot, atom)
148 || !tproot.nitems)
149 return 0;
150
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);
158
159 if (XGetTextProperty(dpy, win, &tp, atom) && tp.nitems) {
160 if (!strncmp((char *) tp.value, DMX_IDENT, strlen(DMX_IDENT))) {
161 int flag = 0;
162
163 for (i = 0; i < count; i++)
164 if (!strcmp(list[i], (char *) tp.value)) {
165 ++flag;
166 break;
167 }
168 if (flag)
169 continue;
170 ++retcode;
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);
177 }
178 XFree(tp.value);
179 }
180 }
181 }
182 XSetErrorHandler(dmxOldHandler);
183
184 for (i = 0; i < count; i++)
185 free(list[i]);
186 free(list);
187 XFree(tproot.value);
188 if (!retcode)
189 dmxLogOutput(dmxScreen, "No Xdmx server running on backend\n");
190 return retcode;
191}
192
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
195 * the display. */
196static DMXScreenInfo *
197dmxPropertyCheckOtherWindows(DMXScreenInfo * dmxScreen, Atom atom)
198{
199 Display *dpy = dmxScreen->beDisplay;
200 const unsigned char *id = dmxPropertyIdentifier();
201 XTextProperty tproot;
202 XTextProperty tp;
203 const char *pt;
204 int (*dmxOldHandler) (Display *, XErrorEvent *);
205
206 if (!dpy)
207 return NULL;
208
209 if (!XGetTextProperty(dpy, RootWindow(dpy, 0), &tproot, atom)
210 || !tproot.nitems)
211 return 0;
212
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);
220
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))) {
226 int idx;
227
228 if (!(pt = strchr((char *) tp.value, ',')))
229 continue;
230 idx = strtol(pt + 1, NULL, 10);
231 if (idx < 0 || idx >= dmxNumScreens)
232 continue;
233 if (dmxScreens[idx].scrnWin != win)
234 continue;
235 XSetErrorHandler(dmxOldHandler);
236 return &dmxScreens[idx];
237 }
238 XFree(tp.value);
239 }
240 }
241 }
242 XSetErrorHandler(dmxOldHandler);
243 XFree(tproot.value);
244 return 0;
245}
246
247/** Returns 0 if this is the only Xdmx session on the display; 1
248 * otherwise. */
249int
250dmxPropertyDisplay(DMXScreenInfo * dmxScreen)
251{
252 Atom atom;
253 const unsigned char *id = dmxPropertyIdentifier();
254 Display *dpy = dmxScreen->beDisplay;
255
256 if (!dpy)
257 return 0;
258
259 atom = XInternAtom(dpy, DMX_ATOMNAME, False);
260 if (dmxPropertyCheckOtherServers(dmxScreen, atom)) {
261 dmxScreen->shared = 1;
262 return 1;
263 }
264 XChangeProperty(dpy, RootWindow(dpy, 0), atom, XA_STRING, 8,
265 PropModeReplace, id, strlen((char *) id));
266 return 0;
267}
268
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). */
274int
275dmxPropertySameDisplay(DMXScreenInfo * dmxScreen, const char *name)
276{
277 Display *dpy0 = dmxScreen->beDisplay;
278 Atom atom0;
279 XTextProperty tp0;
280 Display *dpy1 = NULL;
281 Atom atom1;
282 XTextProperty tp1;
283 int retval = 0;
284
285 if (!dpy0)
286 return 0;
287
288 tp0.nitems = 0;
289 tp1.nitems = 0;
290
291 if ((atom0 = XInternAtom(dpy0, DMX_ATOMNAME, True)) == None) {
292 dmxLog(dmxWarning, "No atom on %s\n", dmxScreen->name);
293 return 0;
294 }
295 if (!XGetTextProperty(dpy0, RootWindow(dpy0, 0), &tp0, atom0)
296 || !tp0.nitems) {
297 dmxLog(dmxWarning, "No text property on %s\n", dmxScreen->name);
298 return 0;
299 }
300
301 if (!(dpy1 = XOpenDisplay(name))) {
302 dmxLog(dmxWarning, "Cannot open %s\n", name);
303 goto cleanup;
304 }
305 atom1 = XInternAtom(dpy1, DMX_ATOMNAME, True);
306 if (atom1 == None) {
307 dmxLog(dmxDebug, "No atom on %s\n", name);
308 goto cleanup;
309 }
310 if (!XGetTextProperty(dpy1, RootWindow(dpy1, 0), &tp1, atom1)
311 || !tp1.nitems) {
312 dmxLog(dmxDebug, "No text property on %s\n", name);
313 goto cleanup;
314 }
315 if (!strcmp((char *) tp0.value, (char *) tp1.value))
316 retval = 1;
317
318 cleanup:
319 if (tp0.nitems)
320 XFree(tp0.value);
321 if (tp1.nitems)
322 XFree(tp1.value);
323 if (dpy1)
324 XCloseDisplay(dpy1);
325 return retval;
326}
327
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
331 * dmxScreen.
332 *
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).
335 *
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):
338 *
339 * "#DMX_IDENT:<hostname running DMX>:<display name of DMX>,<screen number>"
340 */
341void
342dmxPropertyWindow(DMXScreenInfo * dmxScreen)
343{
344 Atom atom;
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 */
350
351 if (!dpy)
352 return; /* FIXME: What should be done here if Xdmx is started
353 * with this screen initially detached?
354 */
355
356 atom = XInternAtom(dpy, DMX_ATOMNAME, False);
357 if ((other = dmxPropertyCheckOtherWindows(dmxScreen, atom))) {
358 DMXScreenInfo *tmp = dmxScreen->next;
359
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);
365 }
366
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));
371
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));
375}