// Copyright (C) 1996 Keith Whitwell. // This file may only be copied under the terms of the GNU Library General // Public License - see the file COPYING in the lib3d distribution. #include #include #include PointerArray *XDevice::children = 0; XDevice::XDevice( Exemplar e ) : Device(e, (MouseCapability) ), initialized(true), display(0), colours(0), window(0), visual(0), gc(0) { } // Platform independent entry point. XDevice::XDevice( uint width, uint height, uint min_depth ) : Device( width, height, min_depth ), initialized(false), display(0), colours(0), window(0), visual(0), gc(0), syncOnSwaps(true) { if (isBad()) return; // Abort if the parent constructor failed. if ((display = XOpenDisplay(NULL)) == 0) { cout << "Unable to open display.\n" << endl; active = false; setBad(); return; } screenNr = DefaultScreen(display); screen = DefaultScreenOfDisplay(display); if (HeightOfScreen(screen) < int(height) || WidthOfScreen(screen) < int(width)) { debug() << "Screen too small for requested window" << endlog; setBad(); return; } XVisualInfo templ; templ.screen = screenNr; int nrVisuals; XVisualInfo *visuals = XGetVisualInfo(display, VisualScreenMask, &templ, &nrVisuals); if_debug { debug() << "There are " << nrVisuals << " visuals. " << endlog; for( int i = 0 ; i < nrVisuals ; i++ ) { debug() << i << ":" << " class=" << visuals[i].c_class << " depth=" << visuals[i].depth << endlog; } } // Take the first, deepest acceptable visual, for displays with // multiple visual depths. visual = visuals[0].visual; depth = visuals[0].depth; /* for( int i = 1 ; i < nrVisuals ; i++ ) { if ((min_depth <= uint(visuals[i].depth)) && (depth < uint(visuals[i].depth))) { depth = visuals[i].depth; visual = visuals[i].visual; } } */ pixelSize = (depth > 16) ? 4 : ((depth > 8) ? 2 : 1); rowSize = width * pixelSize; debug() << "Using:" << " depth=" << depth << " pixelSize=" << pixelSize << endlog; if (!visual) setBad(); } // X11-specific entry point. // // All the work has been done for us. Just interrogate the window for // the values which we will have to work with. XDevice::XDevice( Display *display, Window window ) : Device(), initialized(false), display(display), colours(0), window(window), gc(0), syncOnSwaps(true) { if (isBad()) return; XWindowAttributes attrib; XGetWindowAttributes( display, window, &attrib ); visual = attrib.visual; screen = attrib.screen; colourMap = attrib.colormap; depth = attrib.depth; for (screenNr = ScreenCount(display) ; --screenNr ; ) { if (screen == ScreenOfDisplay(display, screenNr)) break; } pixelSize = (depth > 16) ? 4 : ((depth > 8) ? 2 : 1); setSize(attrib.width, attrib.height); } void XDevice::notifyResize() { XWindowAttributes attrib; XGetWindowAttributes( display, window, &attrib ); setSize(attrib.width, attrib.height); } XDevice * XDevice::create( Display *display, Window window, uint flags ) { if (!children) return 0; const char *deviceString; PointerArray &c = *children; c.sort((int(*)(const XDevice**, const XDevice**))&Device::compare); if ((deviceString = getenv("R_DEVICE")) != 0) { int i = c.getNr(); while (i--) { if (strcmp(deviceString, c[i]->getName()) == 0) { if (c[i]->capabilities & flags != flags) continue; ::debug() << "Trying device: " << c[i]->getName() << endlog; XDevice *dev = c[i]->clone(display, window); if (dev && dev->isGood() && dev->initialize()) { return dev; } delete dev; } } } int i = c.getNr(); while (i--) { if (c[i]->capabilities & flags != flags) continue; ::debug() << "Trying device: " << c[i]->getName() << endlog; XDevice *dev = c[i]->clone( display, window ); if (dev && dev->isGood() && dev->initialize()) { return dev; } delete dev; } return 0; } void XDevice::registerChildClass( XDevice *exemplar ) { if (children == 0) children = new PointerArray(1); children->nextFree() = exemplar; } bool XDevice::initialize() { if (!initialized && !window) { // Don't do this in the constructor because that code // will be called before we try to autodetect XShm. If // XShm is unavailable, we don't want to flash a // stillborn window at the user. unsigned long valuemask; XSetWindowAttributes attrib; valuemask = (CWColormap | CWBackPixel | CWBorderPixel | CWEventMask); attrib.colormap = XCreateColormap(display, RootWindow(display, screenNr), visual, AllocNone); attrib.background_pixel = BlackPixel(display, screenNr); attrib.border_pixel = BlackPixel(display, screenNr); attrib.event_mask = (KeyPressMask|PointerMotionMask); window = XCreateWindow(display, DefaultRootWindow(display), 0, 0, width, height, 2, depth, InputOutput, visual, valuemask, &attrib); XSizeHints size; size.flags = PSize; size.width = width; size.height = height; char *argv[2] = { (char *)"XDevice", 0 }; XSetWMProperties(display, window, 0, 0, argv, 1, &size, 0, 0); XMapWindow(display, window); colourMap = attrib.colormap; } if (!initialized) { /* XGCValues gcv; gcv.graphics_exposures = False; gc = XCreateGC(display, window, GCGraphicsExposures, &gcv); */ gc = DefaultGCOfScreen(screen); initialized = true; } XSync(display, 0); return initialized; } XDevice::~XDevice() { if (isActive()) { XCloseDisplay(display); delete colours; } } static inline uint distance2( int a, int b ) { return (a - b) * (a - b); } uint XDevice::allocateColour( uint red, uint green, uint blue ) { XColor xcol; xcol.red = red; xcol.green = green; xcol.blue = blue; xcol.flags = DoRed | DoGreen | DoBlue; if (XAllocColor(display, colourMap, &xcol)) { return xcol.pixel; } if_debug { debug() << "Failed to allocate colour: " << endl << "\tSearching for closest available colour." << endlog; } if (!colours) { colours = new XColor [visual->map_entries]; for (int p = 0; p < visual->map_entries; p++) { colours[p].pixel = p; } XQueryColors (display, colourMap, colours, visual->map_entries); } // This seems to work quite poorly. uint mindist = 0xFFFFFFFF; int bestmatch = 0; for (int p = 0 ; p < visual->map_entries ; p++) { uint dist = (distance2(colours[p].red, red) + distance2(colours[p].green, green) + distance2(colours[p].blue, blue)); if (dist < mindist) { mindist = dist; bestmatch = p; } } if_debug { debug() << "Using approximate colour: " << xcol.pixel << "\tDistance = " << mindist << endlog; } return colours[bestmatch].pixel; } void XDevice::processPendingEvents() { XEvent event; XMotionEvent *mevent = (XMotionEvent *) &event; /* char buf[21]; KeySym ks; int n_chars; */ while (XCheckMaskEvent(display, (KeyPressMask|PointerMotionMask), &event)) { switch(event.type) { case KeyPress: /* ks = 0; n_chars = XLookupString(&event.xkey, buf, 20, &ks, NULL); buf[n_chars] = '\0'; fprintf(stderr,"KeyPress [%d]: %s\n", event.xkey.keycode, buf); */ XUngrabPointer(display, CurrentTime); break; case MotionNotify: mouseXRel += mevent->x_root - lastX; mouseYRel += mevent->y_root - lastY; lastX = mevent->x_root; lastY = mevent->y_root; break; } } } void XDevice::enableMouseCapability() { int i; // Temporary escape mechanism... cout << "Grabbing your display. Hit any key to ungrab" << endl; if ((i = XGrabPointer(display, window, True, PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime)) != GrabSuccess) { if (i == BadCursor) { cout << "Bad cursor"; } else if (i == BadValue) { cout << "Bad value"; } else if (i == BadWindow) { cout << "Bad window"; } cout << " - Grab failed" << endl; exit(1); } processPendingEvents(); mouseXRel = mouseYRel = 0; } void XDevice::enableKeyboardCapability() { XGrabKeyboard(display, window, True, GrabModeAsync, GrabModeAsync, CurrentTime); } void XDevice::disableKeyboardCapability() { XUngrabKeyboard(display, CurrentTime); } void XDevice::disableMouseCapability() { XUngrabPointer(display, CurrentTime); } void XDevice::setSyncBehaviour(bool b) { syncOnSwaps = b; }