// 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 #include #include #include #include // Abstract base for the DGA devices. class DgaDevice : public Device { public: DgaDevice( Exemplar ); DgaDevice( uint width, uint height, uint depth, int pages ); ~DgaDevice(); const char *getName() const { return "DgaDevice"; } uint allocateColour( uint , uint , uint ); void processPendingEvents(); protected: bool initialize(); protected: char *base; int scanSize; int bankSize; Display *display; Window root; int ram; int col; Visual *visual; Colormap colourMap; }; DgaDevice::DgaDevice( Exemplar e ) : Device( e, (MouseCapability) ) { } #define MINMAJOR 0 #define MINMINOR 0 // Large chunks of this constructor were adopted from the dga.c // demonstration distributed by XFree86. DgaDevice::DgaDevice(uint width, uint height, uint min_depth, int pages) : Device(width, height, min_depth) { col = 0; if (isBad()) return; if ((display = XOpenDisplay(NULL)) == 0) { cout << "Unable to open display.\n" << endl; active = false; setBad(); return; } int MajorVersion, MinorVersion; int EventBase, ErrorBase; if (geteuid() != 0) { debug() << "Failed to create XDgaDevice - must be suid root" << endlog; setBad(); return; } if ( !XF86DGAQueryVersion(display, &MajorVersion, &MinorVersion) || !XF86DGAQueryExtension(display, &EventBase, &ErrorBase) || (MajorVersion < MINMAJOR) || (MajorVersion == MINMAJOR && MinorVersion < MINMINOR)) { debug() << "Failed to create XDgaDevice" << endlog; setBad(); setuid(getuid()); return; } visual = DefaultVisual(display, 0); depth = DefaultDepth(display, 0); // Always get a private colormap. colourMap = XCreateColormap(display, DefaultRootWindow(display), visual, AllocNone); XSetWindowAttributes attrib; attrib.colormap = colourMap; // not used yet. attrib.event_mask = (KeyPressMask | PointerMotionMask); root = XCreateWindow(display, DefaultRootWindow(display), 0, 0, WidthOfScreen(ScreenOfDisplay(display, 0)), HeightOfScreen(ScreenOfDisplay(display, 0)), 0, CopyFromParent, InputOutput, CopyFromParent, CWColormap|CWEventMask, &attrib); XMapWindow(display, root); XRaiseWindow(display, root); XGrabKeyboard(display, root, True, GrabModeAsync, GrabModeAsync, CurrentTime); XGrabPointer(display, root, True, PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); XF86DGAGetVideo(display, DefaultScreen(display), &base, &scanSize, &bankSize, &ram); debug() << "ram:"< 16) ? 4 : ((depth > 8) ? 2 : 1); scanSize *= pixelSize; if ((HeightOfScreen(ScreenOfDisplay(display,0)) * scanSize * pages) > bankSize) { debug() << "Bank size too small for " << pages << " on-card images." << endlog; setBad(); setuid(getuid()); return; } XF86DGADirectVideo(display, DefaultScreen(display), XF86DGADirectGraphics| XF86DGADirectMouse| XF86DGADirectKeyb); setuid(getuid()); memset(base, 0, bankSize); } DgaDevice::~DgaDevice() { if (isActive()) { XF86DGADirectVideo(display, DefaultScreen(display), 0); } } bool DgaDevice::initialize() { return true; } uint DgaDevice::allocateColour( uint red, uint green, uint blue ) { XColor xcol; xcol.red = red; xcol.green = green; xcol.blue = blue; xcol.flags = DoRed | DoGreen | DoBlue; // xcol.pixel = col++; if (!XAllocColor(display, colourMap, &xcol)) printf("failed to alloc colour\n"); return xcol.pixel; } void DgaDevice::processPendingEvents() { char buf[21]; XEvent event; XMotionEvent *mevent = (XMotionEvent *) &event; 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); break; case MotionNotify: mouseXRel += mevent->x_root; mouseYRel += mevent->y_root; /* cout << "Mouse Motion: " << mevent->x_root << "," << mevent->y_root << endl; */ break; } } } // A Dga device that uses on-card memory for the backbuffer. Works // out somewhat slower than X-SHM because the clearBuffer operation is // much more expensive. If there was an 'Accelerated DGA' that let // you use Xlib calls to clear off-screen memory, the performance // would benefit on video cards which support such fills. Apparently // the pageflipping routine waits for vsync, which would also // contribute to slowing things down. class FlipDgaDevice : public DgaDevice { public: FlipDgaDevice( Exemplar ); FlipDgaDevice( uint width, uint height, uint depth ); ~FlipDgaDevice(); const char *getName() const { return "FlipDgaDevice"; } void swapBuffers(); protected: Device *clone( uint width, uint height, uint depth ); int estimateSpeed() const { return 9; } // Lower than XShm protected: int top[2]; char *fb[2]; int current; }; static FlipDgaDevice advertiseFlip( Device::exemplar ); FlipDgaDevice::FlipDgaDevice( Exemplar e ) : DgaDevice( e ) { registerChildClass( this ); } Device * FlipDgaDevice::clone( uint width, uint height, uint depth ) { return new FlipDgaDevice( width, height, depth ); } FlipDgaDevice::FlipDgaDevice( uint width, uint height, uint depth ) : DgaDevice( width, height, depth, 2 ), current(0) { if (isBad()) return; int centering = ((HeightOfScreen(ScreenOfDisplay(display,0)) - height) / 2 * scanSize + (WidthOfScreen(ScreenOfDisplay(display,0)) - width) / 2 * pixelSize); fb[0] = base + centering; fb[1] = fb[0] + HeightOfScreen(ScreenOfDisplay(display,0)) * scanSize; top[0] = 0; top[1] = HeightOfScreen(ScreenOfDisplay(display,0)); rowSize = scanSize; frameBuf = (uchar *)fb[0]; } FlipDgaDevice::~FlipDgaDevice() { } void FlipDgaDevice::swapBuffers() { XF86DGASetViewPort(display, DefaultScreen(display), 0, top[current]); current ^= 1; frameBuf = (uchar *)fb[current]; } // A Dga Device which uses an in-core backbuffer, and memcpy to blit the // new frame to the memory-mapped video ram. class BlitDgaDevice : public DgaDevice { public: BlitDgaDevice( Exemplar ); BlitDgaDevice( uint width, uint height, uint depth ); ~BlitDgaDevice(); const char *getName() const { return "BlitDgaDevice"; } void swapBuffers(); protected: Device *clone( uint width, uint height, uint depth ); int estimateSpeed() const { return 11; } // Higher than XShm? protected: uchar *dest; }; static BlitDgaDevice advertiseBlit( Device::exemplar ); BlitDgaDevice::BlitDgaDevice( Exemplar e ) : DgaDevice( e ) { registerChildClass( this ); } Device * BlitDgaDevice::clone( uint width, uint height, uint depth ) { return new BlitDgaDevice( width, height, depth ); } BlitDgaDevice::BlitDgaDevice( uint width, uint height, uint depth ) : DgaDevice( width, height, depth, 1 ) { if (isBad()) return; int centering = ((HeightOfScreen(ScreenOfDisplay(display,0)) - height) / 2 * scanSize + (WidthOfScreen(ScreenOfDisplay(display,0)) - width) / 2 * pixelSize); dest = (uchar *)base + centering; rowSize = width * pixelSize; frameBuf = new uchar[height * rowSize]; memset(frameBuf, 0, height * rowSize); } BlitDgaDevice::~BlitDgaDevice() { } void BlitDgaDevice::swapBuffers() { uint xn = min(xmin, oldxmin) * pixelSize; uint xx = max(xmax, oldxmax) * pixelSize; uint yn = min(ymin, oldymin); uint yx = max(ymax, oldymax); if (xn < xx && yn < yx) { xn &= ~0x03; if (xx & 0x03) { xx &= ~0x03; xx += 0x04; } uchar *to = dest + xn + (yn * scanSize); uchar *from = frameBuf + xn + (yn * rowSize); uint wid = xx - xn; int i = yx - yn; #if 0 // Memcpy turns out faster on Linux - I haven't investigated why. wid = wid >> 2; asm("cld"); do { asm ("rep\n\t" "movsl" : /* no output registers */ : "c" (wid), "S" (from), "D" (to) : "%ecx", "%edi", "%esi" ); to += scanSize; from += rowSize; } while (--i); #else do { memcpy(to, from, wid); to += scanSize; from += rowSize; } while (--i); #endif } }