xserver/hw/dmx/dmxprop.c

581 lines
18 KiB
C
Raw Normal View History

/*
* Copyright 2002-2003 Red Hat Inc., Durham, North Carolina.
*
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation on the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
/*
* Authors:
* Rickard E. (Rik) Faith <faith@redhat.com>
*
*/
/** \file
*
* It is possible for one of the DMX "backend displays" to actually be
* smaller than the dimensions of the backend X server. Therefore, it
* is possible for more than one of the DMX "backend displays" to be
* physically located on the same backend X server. This situation must
* be detected so that cursor motion can be handled in an expected
* fashion.
*
* We could analyze the names used for the DMX "backend displays" (e.g.,
* the names passed to the -display command-line parameter), but there
* are many possible names for a single X display, and failing to detect
* sameness leads to very unexpected results. Therefore, whenever the
* DMX server opens a window on a backend X server, a property value is
* queried and set on that backend to detect when another window is
* already open on that server.
*
* Further, it is possible that two different DMX server instantiations
* both have windows on the same physical backend X server. This case
* is also detected so that pointer input is not taken from that
* particular backend X server.
*
* The routines in this file handle the property management. */
#ifdef HAVE_DMX_CONFIG_H
#include <dmx-config.h>
#endif
#include "dmx.h"
#include "dmxprop.h"
#include "dmxwindow.h"
#include "dmxlog.h"
2008-08-02 10:56:01 -04:00
#include "dmxatom.h"
#ifdef PANORAMIX
#include "panoramiX.h"
#include "panoramiXsrv.h"
#endif
/** Holds the window id of all DMX windows on the backend X server. */
#define DMX_ATOMNAME "DMX_NAME"
/** The identification string of this DMX server */
#define DMX_IDENT "Xdmx"
extern char *display;
static int dmxPropertyErrorHandler(Display *dpy, XErrorEvent *ev)
{
return 0;
}
static const unsigned char *dmxPropertyIdentifier(void)
{
/* RATS: These buffers are only used in
* length-limited calls. */
char hostname[256];
static char buf[128];
static int initialized = 0;
if (initialized++) return (unsigned char *)buf;
XmuGetHostname(hostname, sizeof(hostname));
XmuSnprintf(buf, sizeof(buf), "%s:%s:%s", DMX_IDENT, hostname, display);
return (unsigned char *)buf;
}
/** Starting with the \a start screen, iterate over all of the screens
* on the same physical X server as \a start, calling \a f with the
* screen and the \a closure. (The common case is that \a start is the
* only DMX window on the backend X server.) */
void *dmxPropertyIterate(DMXScreenInfo *start,
void *(*f)(DMXScreenInfo *dmxScreen, void *),
void *closure)
{
DMXScreenInfo *pt;
if (!start->next) {
if (!start->beDisplay) return NULL;
return f(start, closure);
}
for (pt = start->next; /* condition at end of loop */; pt = pt->next) {
void *retval;
/* beDisplay ban be NULL if a screen was detached */
dmxLog(dmxDebug, "pt = %p\n", pt);
dmxLog(dmxDebug, "pt->beDisplay = %p\n", pt->beDisplay);
if (pt->beDisplay && (retval = f(pt, closure))) return retval;
if (pt == start) break;
}
return NULL;
}
/** Returns 0 if this is the only Xdmx session on the display; 1
* otherwise. */
static int dmxPropertyCheckOtherServers(DMXScreenInfo *dmxScreen, Atom atom)
{
Display *dpy = dmxScreen->beDisplay;
XTextProperty tp;
XTextProperty tproot;
const char *pt;
int retcode = 0;
char **list = NULL;
int count = 0;
int i;
int (*dmxOldHandler)(Display *, XErrorEvent *);
if (!dpy)
return 0;
if (!XGetTextProperty(dpy, RootWindow(dpy,0), &tproot, atom)
|| !tproot.nitems) return 0;
/* Ignore BadWindow errors for this
* routine because the window id stored
* in the property might be old */
dmxOldHandler = XSetErrorHandler(dmxPropertyErrorHandler);
for (pt = (const char *)tproot.value; pt && *pt; pt = pt ? pt + 1 : NULL) {
if ((pt = strchr(pt, ','))) {
Window win = strtol(pt+1, NULL, 10);
if (XGetTextProperty(dpy, win, &tp, atom) && tp.nitems) {
if (!strncmp((char *)tp.value, DMX_IDENT, strlen(DMX_IDENT))) {
int flag = 0;
for (i = 0; i < count; i++)
if (!strcmp(list[i], (char *)tp.value)) {
++flag;
break;
}
if (flag) continue;
++retcode;
dmxLogOutputWarning(dmxScreen,
"%s also running on %s\n",
tp.value, dmxScreen->display);
list = xrealloc(list, ++count * sizeof(*list));
list[count-1] = xalloc(tp.nitems + 2);
strncpy(list[count-1], (char *)tp.value, tp.nitems + 1);
}
XFree(tp.value);
}
}
}
XSetErrorHandler(dmxOldHandler);
for (i = 0; i < count; i++) xfree(list[i]);
xfree(list);
XFree(tproot.value);
if (!retcode)
dmxLogOutput(dmxScreen, "No Xdmx server running on backend\n");
return retcode;
}
/** Returns NULL if this is the only Xdmx window on the display.
* Otherwise, returns a pointer to the dmxScreen of the other windows on
* the display. */
static DMXScreenInfo *dmxPropertyCheckOtherWindows(DMXScreenInfo *dmxScreen,
Atom atom)
{
Display *dpy = dmxScreen->beDisplay;
const unsigned char *id = dmxPropertyIdentifier();
XTextProperty tproot;
XTextProperty tp;
const char *pt;
int (*dmxOldHandler)(Display *, XErrorEvent *);
if (!dpy)
return NULL;
if (!XGetTextProperty(dpy, RootWindow(dpy,0), &tproot, atom)
|| !tproot.nitems) return 0;
/* Ignore BadWindow errors for this
* routine because the window id stored
* in the property might be old */
dmxOldHandler = XSetErrorHandler(dmxPropertyErrorHandler);
for (pt = (const char *)tproot.value; pt && *pt; pt = pt ? pt + 1 : NULL) {
if ((pt = strchr(pt, ','))) {
Window win = strtol(pt+1, NULL, 10);
if (XGetTextProperty(dpy, win, &tp, atom) && tp.nitems) {
dmxLog(dmxDebug,"On %s/%lu: %s\n",
dmxScreen->display, win, tp.value);
if (!strncmp((char *)tp.value, (char *)id,
strlen((char *)id))) {
int idx;
if (!(pt = strchr((char *)tp.value, ','))) continue;
idx = strtol(pt+1, NULL, 10);
if (idx < 0 || idx >= dmxNumScreens) continue;
if (dmxScreens[idx].scrnWin != win) continue;
XSetErrorHandler(dmxOldHandler);
return &dmxScreens[idx];
}
XFree(tp.value);
}
}
}
XSetErrorHandler(dmxOldHandler);
XFree(tproot.value);
return 0;
}
/** Returns 0 if this is the only Xdmx session on the display; 1
* otherwise. */
int dmxPropertyDisplay(DMXScreenInfo *dmxScreen)
{
Atom atom;
const unsigned char *id = dmxPropertyIdentifier();
Display *dpy = dmxScreen->beDisplay;
if (!dpy)
return 0;
atom = XInternAtom(dpy, DMX_ATOMNAME, False);
if (dmxPropertyCheckOtherServers(dmxScreen, atom)) {
dmxScreen->shared = 1;
return 1;
}
XChangeProperty(dpy, RootWindow(dpy,0), atom, XA_STRING, 8,
PropModeReplace, id, strlen((char *)id));
return 0;
}
/** Returns 1 if the dmxScreen and the display in \a name are on the
* same display, or 0 otherwise. We can't just compare the display
* names because there can be multiple synonyms for the same display,
* some of which cannot be determined without accessing the display
* itself (e.g., domain aliases or machines with multiple NICs). */
int dmxPropertySameDisplay(DMXScreenInfo *dmxScreen, const char *name)
{
Display *dpy0 = dmxScreen->beDisplay;
Atom atom0;
XTextProperty tp0;
Display *dpy1 = NULL;
Atom atom1;
XTextProperty tp1;
int retval = 0;
if (!dpy0)
return 0;
tp0.nitems = 0;
tp1.nitems = 0;
if ((atom0 = XInternAtom(dpy0, DMX_ATOMNAME, True)) == None) {
dmxLog(dmxWarning, "No atom on %s\n", dmxScreen->display);
return 0;
}
if (!XGetTextProperty(dpy0, RootWindow(dpy0,0), &tp0, atom0)
|| !tp0.nitems) {
dmxLog(dmxWarning, "No text property on %s\n", dmxScreen->display);
return 0;
}
if (!(dpy1 = XOpenDisplay(name))) {
dmxLog(dmxWarning, "Cannot open %s\n", name);
goto cleanup;
}
atom1 = XInternAtom(dpy1, DMX_ATOMNAME, True);
if (atom1 == None) {
dmxLog(dmxDebug, "No atom on %s\n", name);
goto cleanup;
}
if (!XGetTextProperty(dpy1, RootWindow(dpy1,0), &tp1, atom1)
|| !tp1.nitems) {
dmxLog(dmxDebug, "No text property on %s\n", name);
goto cleanup;
}
if (!strcmp((char *)tp0.value, (char *)tp1.value)) retval = 1;
cleanup:
if (tp0.nitems) XFree(tp0.value);
if (tp1.nitems) XFree(tp1.value);
if (dpy1) XCloseDisplay(dpy1);
return retval;
}
/** Prints a log message if \a dmxScreen is on the same backend X server
* as some other DMX backend (output) screen. Modifies the property
* (#DMX_ATOMNAME) on the backend X server to reflect the creation of \a
* dmxScreen.
*
* The root window of the backend X server holds a list of window ids
* for all DMX windows (on this DMX server or some other DMX server).
*
* This list can then be iterated, and the property for each window can
* be examined. This property contains the following tuple (no quotes):
*
* "#DMX_IDENT:<hostname running DMX>:<display name of DMX>,<screen number>"
*/
void dmxPropertyWindow(DMXScreenInfo *dmxScreen)
{
Atom atom;
const unsigned char *id = dmxPropertyIdentifier();
Display *dpy = dmxScreen->beDisplay;
Window win = dmxScreen->scrnWin;
DMXScreenInfo *other;
char buf[128]; /* RATS: only used with XmuSnprintf */
if (!dpy)
return; /* FIXME: What should be done here if Xdmx is started
* with this screen initially detached?
*/
atom = XInternAtom(dpy, DMX_ATOMNAME, False);
if ((other = dmxPropertyCheckOtherWindows(dmxScreen, atom))) {
DMXScreenInfo *tmp = dmxScreen->next;
dmxScreen->next = (other->next ? other->next : other);
other->next = (tmp ? tmp : dmxScreen);
dmxLog(dmxDebug, "%d/%s/%lu and %d/%s/%lu are on the same backend\n",
dmxScreen->index, dmxScreen->display, dmxScreen->scrnWin,
other->index, other->display, other->scrnWin);
}
XmuSnprintf(buf, sizeof(buf), ".%d,%lu", dmxScreen->index,
(long unsigned)win);
XChangeProperty(dpy, RootWindow(dpy,0), atom, XA_STRING, 8,
PropModeAppend, (unsigned char *)buf, strlen(buf));
XmuSnprintf(buf, sizeof(buf), "%s,%d", id, dmxScreen->index);
XChangeProperty(dpy, win, atom, XA_STRING, 8,
PropModeAppend, (unsigned char *)buf, strlen(buf));
}
static int (*dmxSaveProcVector[256]) (ClientPtr);
static int
dmxProcChangeProperty (ClientPtr client)
{
WindowPtr pWin;
PropertyPtr pProp;
int err;
REQUEST(xChangePropertyReq);
err = (*dmxSaveProcVector[X_ChangeProperty]) (client);
if (err != Success)
return err;
if (dixLookupWindow (&pWin,
stuff->window,
serverClient,
DixReadAccess) != Success ||
dixLookupProperty (&pProp,
pWin,
stuff->property,
serverClient,
DixReadAccess) != Success)
return Success;
#ifdef PANORAMIX
if (!noPanoramiXExtension)
{
PanoramiXRes *win;
int j;
if ((win = (PanoramiXRes *) SecurityLookupIDByType (serverClient,
stuff->window,
XRT_WINDOW,
DixReadAccess)))
{
FOR_NSCREENS_FORWARD(j) {
if (dixLookupWindow (&pWin,
win->info[j].id,
serverClient,
DixReadAccess) == Success)
dmxBESetWindowProperty (pWin, pProp);
}
}
return Success;
}
#endif
dmxBESetWindowProperty (pWin, pProp);
return Success;
}
static void
dmxDeleteProperty (WindowPtr pWin,
Atom property)
{
ScreenPtr pScreen = pWin->drawable.pScreen;
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV (pWin);
if (!pWinPriv->window)
return;
XLIB_PROLOGUE (dmxScreen);
XDeleteProperty (dmxScreen->beDisplay,
pWinPriv->window,
2008-08-02 10:56:01 -04:00
dmxBEAtom (dmxScreen, property));
XLIB_EPILOGUE (dmxScreen);
}
static int
dmxProcDeleteProperty (ClientPtr client)
{
WindowPtr pWin;
int err;
REQUEST(xDeletePropertyReq);
err = (*dmxSaveProcVector[X_DeleteProperty]) (client);
if (err != Success)
return err;
#ifdef PANORAMIX
if (!noPanoramiXExtension)
{
PanoramiXRes *win;
int j;
if ((win = (PanoramiXRes *) SecurityLookupIDByType (serverClient,
stuff->window,
XRT_WINDOW,
DixReadAccess)))
{
FOR_NSCREENS_FORWARD(j) {
if (dixLookupWindow (&pWin,
win->info[j].id,
serverClient,
DixReadAccess) == Success)
dmxDeleteProperty (pWin, stuff->property);
}
}
return Success;
}
#endif
if (dixLookupWindow (&pWin,
stuff->window,
serverClient,
DixReadAccess) == Success)
dmxDeleteProperty (pWin, stuff->property);
return Success;
}
static void
dmxRotateProperties (WindowPtr pWin,
Atom *atoms,
Atom *buf,
int nAtoms,
int nPositions)
{
ScreenPtr pScreen = pWin->drawable.pScreen;
DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
dmxWinPrivPtr pWinPriv = DMX_GET_WINDOW_PRIV (pWin);
int i;
if (!pWinPriv->window)
return;
XLIB_PROLOGUE (dmxScreen);
for (i = 0; i < nAtoms; i++)
2008-08-02 10:56:01 -04:00
buf[i] = dmxBEAtom (dmxScreen, atoms[i]);
XRotateWindowProperties (dmxScreen->beDisplay,
pWinPriv->window,
buf,
nAtoms,
nPositions);
XLIB_EPILOGUE (dmxScreen);
}
static int
dmxProcRotateProperties (ClientPtr client)
{
WindowPtr pWin;
int err;
Atom *buf, *atoms;
REQUEST(xRotatePropertiesReq);
err = (*dmxSaveProcVector[X_RotateProperties]) (client);
if (err != Success)
return err;
atoms = (Atom *) & stuff[1];
buf = (Atom *) xalloc (stuff->nAtoms * sizeof (Atom));
if (!buf)
return Success;
#ifdef PANORAMIX
if (!noPanoramiXExtension)
{
PanoramiXRes *win;
int j;
if ((win = (PanoramiXRes *) SecurityLookupIDByType (serverClient,
stuff->window,
XRT_WINDOW,
DixReadAccess)))
{
FOR_NSCREENS_FORWARD(j) {
if (dixLookupWindow (&pWin,
win->info[j].id,
serverClient,
DixReadAccess) == Success)
dmxRotateProperties (pWin, atoms, buf, stuff->nAtoms,
stuff->nPositions);
}
}
xfree (buf);
return Success;
}
#endif
if (dixLookupWindow (&pWin,
stuff->window,
serverClient,
DixReadAccess) == Success)
dmxRotateProperties (pWin, atoms, buf, stuff->nAtoms,
stuff->nPositions);
xfree (buf);
return Success;
}
/** Initialize property support. In addition to the screen function call
* pointers, DMX also hooks in at the ProcVector[] level. Here the old
* ProcVector function pointers are saved and the new ProcVector
* function pointers are initialized. */
void dmxInitProps (void)
{
int i;
for (i = 0; i < 256; i++)
dmxSaveProcVector[i] = ProcVector[i];
ProcVector[X_ChangeProperty] = dmxProcChangeProperty;
ProcVector[X_DeleteProperty] = dmxProcDeleteProperty;
ProcVector[X_RotateProperties] = dmxProcRotateProperties;
}
/** Reset property support by restoring the original ProcVector function
* pointers. */
void dmxResetProps (void)
{
int i;
for (i = 0; i < 256; i++)
ProcVector[i] = dmxSaveProcVector[i];
}