/** 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 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->name); return 0; } if (!XGetTextProperty(dpy0, RootWindow(dpy0, 0), &tp0, atom0) || !tp0.nitems) { dmxLog(dmxWarning, "No text property on %s\n", dmxScreen->name); 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; }
/** Initialize the XSync() batching optimization, but only if * #dmxSyncActivate was last called with a non-negative value. */ void dmxSyncInit(void) { if (dmxSyncInterval) { RegisterBlockAndWakeupHandlers(dmxSyncBlockHandler, dmxSyncWakeupHandler, NULL); dmxLog(dmxInfo, "XSync batching with %d ms interval\n", dmxSyncInterval); } else { dmxLog(dmxInfo, "XSync batching disabled\n"); } }
/* Actually do the work of printing out the human-readable message. */ static CARD32 dmxStatCallback(OsTimerPtr timer, CARD32 t, void *arg) { int i, j; static int header = 0; int limit = dmxNumScreens; if (!dmxNumScreens) { header = 0; return DMX_STAT_INTERVAL; } if (!header++ || !(header % 10)) { dmxLog(dmxDebug, " S SyncCount Sync/s avSync mxSync avPend mxPend | " "<10ms <1s >1s\n"); } if (dmxStatDisplays && dmxStatDisplays < limit) limit = dmxStatDisplays; for (i = 0; i < limit; i++) { DMXScreenInfo *dmxScreen = &dmxScreens[i]; DMXStatInfo *s = dmxScreen->stat; unsigned long aSync, mSync; unsigned long aPend, mPend; if (!s) continue; aSync = avg(&s->usec, &mSync); aPend = avg(&s->pending, &mPend); dmxLog(dmxDebug, "%2d %9lu %7lu %6lu %6lu %6lu %6lu |", i, /* S */ s->syncCount, /* SyncCount */ (s->syncCount - s->oldSyncCount) * 1000 / dmxStatInterval, /* Sync/s */ aSync, /* us/Sync */ mSync, /* max/Sync */ aPend, /* avgPend */ mPend); /* maxPend */ for (j = 0; j < DMX_STAT_BINS; j++) dmxLogCont(dmxDebug, " %5lu", s->bins[j]); dmxLogCont(dmxDebug, "\n"); /* Reset/clear */ s->oldSyncCount = s->syncCount; for (j = 0; j < DMX_STAT_BINS; j++) s->bins[j] = 0; } return DMX_STAT_INTERVAL; /* Place on queue again */ }
/** 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 snprintf */ 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->name, dmxScreen->scrnWin, other->index, other->name, other->scrnWin); } snprintf(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)); snprintf(buf, sizeof(buf), "%s,%d", id, dmxScreen->index); XChangeProperty(dpy, win, atom, XA_STRING, 8, PropModeAppend, (unsigned char *) buf, strlen(buf)); }
/** Create a mapping from \a remoteEvent to \a serverEvent. The \a * remoteEvent is the type returned from the remote server. The \a * serverEvent is from the XI_* list of events in * include/extensions/XIproto.h. */ void dmxMapInsert(DMXLocalInputInfoPtr dmxLocal, int remoteEvent, int serverEvent) { int hash = remoteEvent & DMX_MAP_MASK; int i; /* Return if this has already been mapped */ if (dmxLocal->map[hash].remote == remoteEvent && dmxLocal->map[hash].server == serverEvent) return; if (dmxLocal->map[hash].remote) { dmxLocal->mapOptimize = 0; for (i = 0; i < DMX_MAP_ENTRIES; i++) { if (!dmxLocal->map[i].remote) { dmxLocal->map[i].remote = remoteEvent; dmxLocal->map[i].server = serverEvent; return; } } dmxLog(dmxWarning, "Out of map entries, cannot map remove event type %d\n", remoteEvent); } else { dmxLocal->map[hash].remote = remoteEvent; dmxLocal->map[hash].server = serverEvent; } }
/** Make a note that \a config should be used as the configuration for * current instantiation of the DMX server. */ void dmxConfigStoreConfig(const char *config) { if (dmxConfigCmd.config) dmxLog(dmxFatal, "Only one -config allowed\n"); dmxConfigCmd.config = strdup(config); }
/** Make a note that \a file is the configuration file. */ void dmxConfigStoreFile(const char *file) { if (dmxConfigCmd.filename) dmxLog(dmxFatal, "Only one -configfile allowed\n"); dmxConfigCmd.filename = strdup(file); }
/** 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->name, 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; }
static int dmxConfigReadFile(const char *filename, int debug) { FILE *str; if (!(str = fopen(filename, "r"))) return -1; dmxLog(dmxInfo, "Reading configuration file \"%s\"\n", filename); yyin = str; yydebug = debug; yyparse(); fclose(str); return 0; }
/** Make a note that \a input is the name of an X11 display that should * be used for input from XInput extension devices. */ void dmxConfigStoreXInput(const char *input) { DMXConfigListPtr entry = malloc(sizeof(*entry)); entry->name = strdup(input); entry->next = NULL; if (!dmxConfigCmd.xinputs) dmxConfigCmd.xinputs = entry; else { DMXConfigList *pt; for (pt = dmxConfigCmd.xinputs; pt->next; pt = pt->next); if (!pt) dmxLog(dmxFatal, "dmxConfigStoreXInput: end of list non-NULL\n"); pt->next = entry; } }
/** Request an XSync() to the display used by \a dmxScreen. If \a now * is TRUE, call XSync() immediately instead of waiting for the next * XSync() batching point. Note that if XSync() batching was deselected * with #dmxSyncActivate() before #dmxSyncInit() was called, then no * XSync() batching is performed and this function always calles XSync() * immediately. * * (Note that this function uses TimerSet but works correctly in the * face of a server generation. See the source for details.) * * If \a dmxScreen is \a NULL, then all pending syncs will be flushed * immediately. */ void dmxSync(DMXScreenInfo *dmxScreen, Bool now) { static unsigned long dmxGeneration = 0; if (dmxSyncInterval) { if (dmxGeneration != serverGeneration) { /* Server generation does a TimerInit, which frees all * timers. So, at this point dmxSyncTimer is either: * 1) NULL, iff dmxGeneration == 0, * 2) freed, if it was on a queue (dmxSyncPending != 0), or * 3) allocated, if it wasn't on a queue (dmxSyncPending == 0) */ if (dmxSyncTimer && !dmxSyncPending) xfree(dmxSyncTimer); dmxSyncTimer = NULL; now = TRUE; dmxGeneration = serverGeneration; } /* Queue sync */ if (dmxScreen) { dmxScreen->needsSync = TRUE; ++dmxSyncPending; } /* Do sync or set time for later */ if (now || !dmxScreen) { if (!TimerForce(dmxSyncTimer)) dmxSyncCallback(NULL, 0, NULL); /* At this point, dmxSyncPending == 0 because * dmxSyncCallback must have been called. */ if (dmxSyncPending) dmxLog(dmxFatal, "dmxSync(%s,%d): dmxSyncPending = %d\n", dmxScreen ? dmxScreen->name : "", now, dmxSyncPending); } else { dmxScreen->needsSync = TRUE; if (dmxSyncPending == 1) dmxSyncTimer = TimerSet(dmxSyncTimer, 0, dmxSyncInterval, dmxSyncCallback, NULL); } } else { /* If dmxSyncInterval is not being used, * then all the backends are already * up-to-date. */ if (dmxScreen) dmxDoSync(dmxScreen); } }
/** Make a note that \a display is the name of an X11 display that * should be initialized as a backend (output) display. Called from * #ddxProcessArgument. */ void dmxConfigStoreDisplay(const char *display) { DMXConfigListPtr entry = malloc(sizeof(*entry)); entry->name = strdup(display); entry->next = NULL; if (!dmxConfigCmd.displays) dmxConfigCmd.displays = entry; else { DMXConfigList *pt; for (pt = dmxConfigCmd.displays; pt->next; pt = pt->next); if (!pt) dmxLog(dmxFatal, "dmxConfigStoreDisplay: end of list non-NULL\n"); pt->next = entry; } ++dmxDisplaysFromCommandLine; }
/** Load the font, \a pFont, on the back-end server associated with \a * pScreen. When a font is loaded, the font path on back-end server is * first initialized to that specified on the command line with the * -fontpath options, and then the font is loaded. */ Bool dmxBELoadFont(ScreenPtr pScreen, FontPtr pFont) { DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum]; dmxFontPrivPtr pFontPriv = FontGetPrivate(pFont, dmxFontPrivateIndex); const char *name; char **oldFontPath = NULL; int nOldPaths; Atom name_atom, value_atom; int i; /* Make sure we have a font private struct to work with */ if (!pFontPriv) return FALSE; /* Don't load a font over top of itself */ if (pFontPriv->font[pScreen->myNum]) { return TRUE; /* Already loaded font */ } /* Save old font path */ oldFontPath = XGetFontPath(dmxScreen->beDisplay, &nOldPaths); /* Set the font path for the font about to be loaded on the back-end */ if (dmxSetFontPath(dmxScreen)) { char **fp; int npaths; Bool *goodfps; /* This could fail only when first starting the X server and * loading the default font. If it fails here, then the default * font path is invalid, no default font path will be set, the * DMX server will fail to load the default font, and it will * exit with an error unless we remove the offending font paths * with the -ignorebadfontpaths command line option. */ fp = dmxGetFontPath(&npaths); if (!fp) { dmxLog(dmxError, "No default font path set.\n"); dmxLog(dmxError, "Please see the Xdmx man page for information on how to\n"); dmxLog(dmxError, "initialize the DMX server's default font path.\n"); XFreeFontPath(oldFontPath); return FALSE; } if (!dmxFontPath) dmxLog(dmxWarning, "No default font path is set.\n"); goodfps = xallocarray(npaths, sizeof(*goodfps)); dmxLog(dmxError, "The DMX server failed to set the following font paths on " "screen #%d:\n", pScreen->myNum); for (i = 0; i < npaths; i++) if (!(goodfps[i] = dmxCheckFontPathElement(dmxScreen, fp[i]))) dmxLog(dmxError, " %s\n", fp[i]); if (dmxIgnoreBadFontPaths) { char *newfp; int newnpaths = 0; int len = 0; int j = 0; dmxLog(dmxError, "These font paths will not be used because the " "\"-ignorebadfontpaths\"\n"); dmxLog(dmxError, "option is set.\n"); for (i = 0; i < npaths; i++) if (goodfps[i]) { len += strlen(fp[i]) + 1; newnpaths++; } if (!newnpaths) { /* No valid font paths were found */ dmxLog(dmxError, "After removing the font paths above, no valid font " "paths were\n"); dmxLog(dmxError, "available. Please check that the font paths set on " "the command\n"); dmxLog(dmxError, "line or in the configuration file via the " "\"-fontpath\" option\n"); dmxLog(dmxError, "are valid on all back-end servers. See the Xdmx man " "page for\n"); dmxLog(dmxError, "more information on font paths.\n"); dmxFreeFontPath(fp); XFreeFontPath(oldFontPath); free(goodfps); return FALSE; } newfp = xallocarray(len, sizeof(*newfp)); for (i = 0; i < npaths; i++) { if (goodfps[i]) { int n = strlen(fp[i]); newfp[j++] = n; strncpy(&newfp[j], fp[i], n); j += n; } } if (SetFontPath(serverClient, newnpaths, (unsigned char *) newfp)) { /* Note that this should never happen since all of the * FPEs were previously valid. */ dmxLog(dmxError, "Cannot reset the default font path.\n"); } } else if (dmxFontPath) { dmxLog(dmxError, "Please remove these font paths from the command line " "or\n"); dmxLog(dmxError, "configuration file, or set the \"-ignorebadfontpaths\" " "option to\n"); dmxLog(dmxError, "ignore them. For more information on these options, see " "the\n"); dmxLog(dmxError, "Xdmx man page.\n"); } else { dmxLog(dmxError, "Please specify the font paths that are available on all " "back-end\n"); dmxLog(dmxError, "servers with the \"-fontpath\" option, or use the " "\"-ignorebadfontpaths\"\n"); dmxLog(dmxError, "to ignore bad defaults. For more information on " "these and other\n"); dmxLog(dmxError, "font-path-related options, see the Xdmx man page.\n"); } free(goodfps); if (!dmxIgnoreBadFontPaths || (dmxIgnoreBadFontPaths && dmxSetFontPath(dmxScreen))) { /* We still have errors so return with error */ dmxFreeFontPath(fp); XFreeFontPath(oldFontPath); return FALSE; } } /* Find requested font on back-end server */ name_atom = MakeAtom("FONT", 4, TRUE); value_atom = 0L; for (i = 0; i < pFont->info.nprops; i++) { if ((Atom) pFont->info.props[i].name == name_atom) { value_atom = pFont->info.props[i].value; break; } } if (!value_atom) return FALSE; name = NameForAtom(value_atom); if (!name) return FALSE; pFontPriv->font[pScreen->myNum] = XLoadQueryFont(dmxScreen->beDisplay, name); /* Restore old font path */ XSetFontPath(dmxScreen->beDisplay, oldFontPath, nOldPaths); XFreeFontPath(oldFontPath); dmxSync(dmxScreen, FALSE); if (!pFontPriv->font[pScreen->myNum]) return FALSE; return TRUE; }
/** Print \a argc messages, each describing an element in \a argv. This * is maingly for debugging purposes. */ void dmxLogArgs(dmxLogLevel logLevel, int argc, char **argv) { int i; for (i = 0; i < argc; i++) dmxLog(logLevel, " Arg[%d] = \"%s\"\n", i, argv[i]); }
void __glXScreenInit(GLint numscreens) { int s; int c; DMXScreenInfo *dmxScreen0 = &dmxScreens[0]; __glXNumActiveScreens = numscreens; CalcServerVersionAndExtensions(); __glXFBConfigs = NULL; __glXNumFBConfigs = 0; if ((__glXVersionMajor == 1 && __glXVersionMinor >= 3) || (__glXVersionMajor > 1) || (strstr(ExtensionsString, "GLX_SGIX_fbconfig"))) { /* // Initialize FBConfig info. // find the set of FBConfigs that are present on all back-end // servers - only those configs will be supported */ __glXFBConfigs = (__GLXFBConfig **) malloc(dmxScreen0->numFBConfigs * (numscreens + 1) * sizeof(__GLXFBConfig *)); __glXNumFBConfigs = 0; for (c = 0; c < dmxScreen0->numFBConfigs; c++) { __GLXFBConfig *cfg = NULL; if (numscreens > 1) { for (s = 1; s < numscreens; s++) { DMXScreenInfo *dmxScreen = &dmxScreens[s]; cfg = FindMatchingFBConfig(&dmxScreen0->fbconfigs[c], dmxScreen->fbconfigs, dmxScreen->numFBConfigs); __glXFBConfigs[__glXNumFBConfigs * (numscreens + 1) + s + 1] = cfg; if (!cfg) { dmxLog(dmxInfo, "screen0 FBConfig 0x%x is missing on screen#%d\n", dmxScreen0->fbconfigs[c].id, s); break; } else { dmxLog(dmxInfo, "screen0 FBConfig 0x%x matched to 0x%x on screen#%d\n", dmxScreen0->fbconfigs[c].id, cfg->id, s); } } } else { cfg = &dmxScreen0->fbconfigs[c]; } if (cfg) { /* filter out overlay visuals */ if (cfg->level == 0) { __GLXFBConfig *proxy_cfg; __glXFBConfigs[__glXNumFBConfigs * (numscreens + 1) + 1] = &dmxScreen0->fbconfigs[c]; proxy_cfg = malloc(sizeof(__GLXFBConfig)); memcpy(proxy_cfg, cfg, sizeof(__GLXFBConfig)); proxy_cfg->id = FakeClientID(0); /* visual will be associated later in __glXGetFBConfigs */ proxy_cfg->associatedVisualId = (unsigned int) -1; __glXFBConfigs[__glXNumFBConfigs * (numscreens + 1) + 0] = proxy_cfg; __glXNumFBConfigs++; } } } } }
/** Fill the \a info structure with information needed to initialize \a * pDev. */ void othUSBGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info) { GETPRIV; int i, j; static KeySym keyboard_mapping = NoSymbol; int absolute[5]; #define test_bit(bit) (priv->mask[(bit)/8] & (1 << ((bit)%8))) /* Some USB mice devices return key * events from their pair'd * keyboard... */ info->keyClass = 1; info->keySyms.minKeyCode = 8; info->keySyms.maxKeyCode = 8; info->keySyms.mapWidth = 1; info->keySyms.map = &keyboard_mapping; for (i = 0; i < EV_MAX; i++) { if (test_bit(i)) { switch (i) { case EV_KEY: /* See above */ break; case EV_REL: info->valuatorClass = 1; if (info->numRelAxes + info->numAbsAxes > DMX_MAX_AXES - 1) { info->numRelAxes = DMX_MAX_AXES - info->numAbsAxes - 1; dmxLog(dmxWarning, "Can only use %d relative axes\n", info->numRelAxes); } else info->numRelAxes = priv->numRel; info->minval[0] = 0; info->maxval[0] = 0; info->res[0] = 1; info->minres[0] = 0; info->maxres[0] = 1; break; case EV_ABS: info->valuatorClass = 1; if (info->numRelAxes + info->numAbsAxes > DMX_MAX_AXES - 1) { info->numAbsAxes = DMX_MAX_AXES - info->numRelAxes - 1; dmxLog(dmxWarning, "Can only use %d absolute axes\n", info->numAbsAxes); } else info->numAbsAxes = priv->numAbs; for (j = 0; j < info->numAbsAxes; j++) { ioctl(priv->fd, EVIOCGABS(j), abs); info->minval[1+j] = absolute[1]; info->maxval[1+j] = absolute[2]; info->res[1+j] = absolute[3]; info->minres[1+j] = absolute[3]; info->maxres[1+j] = absolute[3]; } break; case EV_LED: info->ledFeedbackClass = 0; /* Not supported at this time */ break; case EV_SND: info->belFeedbackClass = 0; /* Not supported at this time */ break; } } } }