//XShm driver for Xorg //Note that on composited displays, the alpha bits will allow translucency underneath the active window //As this is not a feature of ruby, this driver must always set the alpha bits on clear() and refresh() //Linear interpolation is only applied horizontally for performance reasons, although Nearest is still much faster #include #include struct VideoXShm : Video { VideoXShm() { initialize(); } ~VideoXShm() { terminate(); } auto ready() -> bool { return _ready; } auto context() -> uintptr { return _context; } auto smooth() -> bool { return _smooth; } auto setContext(uintptr context) -> bool { if(_context == context) return true; _context = context; return initialize(); } auto setSmooth(bool smooth) -> bool { _smooth = smooth; return true; } auto clear() -> void { if(!ready()) return; auto dp = _inputBuffer; uint length = _inputWidth * _inputHeight; while(length--) *dp++ = 255u << 24; output(); } auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool { if(!ready()) return false; if(!_inputBuffer || _inputWidth != width || _inputHeight != height) { if(_inputBuffer) delete[] _inputBuffer; _inputWidth = width; _inputHeight = height; _inputBuffer = new uint32_t[width * height + 16]; //+16 is padding for linear interpolation } data = _inputBuffer; pitch = _inputWidth * sizeof(uint32_t); return true; } auto unlock() -> void { if(!ready()) return; } auto output() -> void { if(!ready()) return; size(); float xratio = (float)_inputWidth / (float)_outputWidth; float yratio = (float)_inputHeight / (float)_outputHeight; #pragma omp parallel for for(uint y = 0; y < _outputHeight; y++) { float ystep = y * yratio; float xstep = 0; uint32_t* sp = _inputBuffer + (uint)ystep * _inputWidth; uint32_t* dp = _outputBuffer + y * _outputWidth; if(!_smooth) { for(uint x = 0; x < _outputWidth; x++) { *dp++ = 255u << 24 | sp[(uint)xstep]; xstep += xratio; } } else { for(uint x = 0; x < _outputWidth; x++) { *dp++ = 255u << 24 | interpolate(xstep - (uint)xstep, sp[(uint)xstep], sp[(uint)xstep + 1]); xstep += xratio; } } } GC gc = XCreateGC(_display, _window, 0, 0); XShmPutImage(_display, _window, gc, _image, 0, 0, 0, 0, _outputWidth, _outputHeight, False); XFreeGC(_display, gc); XFlush(_display); } private: auto initialize() -> bool { terminate(); if(!_context) return false; _display = XOpenDisplay(0); _screen = DefaultScreen(_display); XWindowAttributes getAttributes; XGetWindowAttributes(_display, (Window)_context, &getAttributes); _depth = getAttributes.depth; _visual = getAttributes.visual; //driver only supports 32-bit pixels //note that even on 15-bit and 16-bit displays, the window visual's depth should be 32 if(_depth < 24 || _depth > 32) { free(); return false; } XSetWindowAttributes setAttributes = {0}; setAttributes.border_pixel = 0; _window = XCreateWindow(_display, (Window)_context, 0, 0, 256, 256, 0, getAttributes.depth, InputOutput, getAttributes.visual, CWBorderPixel, &setAttributes ); XSetWindowBackground(_display, _window, 0); XMapWindow(_display, _window); XFlush(_display); while(XPending(_display)) { XEvent event; XNextEvent(_display, &event); } if(!size()) return false; return _ready = true; } auto terminate() -> void { free(); if(_display) { XCloseDisplay(_display); _display = nullptr; } } auto size() -> bool { XWindowAttributes windowAttributes; XGetWindowAttributes(_display, (Window)_context, &windowAttributes); if(_outputBuffer && _outputWidth == windowAttributes.width && _outputHeight == windowAttributes.height) return true; _outputWidth = windowAttributes.width; _outputHeight = windowAttributes.height; XResizeWindow(_display, _window, _outputWidth, _outputHeight); free(); _shmInfo.shmid = shmget(IPC_PRIVATE, _outputWidth * _outputHeight * sizeof(uint32_t), IPC_CREAT | 0777); if(_shmInfo.shmid < 0) return false; _shmInfo.shmaddr = (char*)shmat(_shmInfo.shmid, 0, 0); _shmInfo.readOnly = False; XShmAttach(_display, &_shmInfo); _outputBuffer = (uint32_t*)_shmInfo.shmaddr; _image = XShmCreateImage(_display, _visual, _depth, ZPixmap, _shmInfo.shmaddr, &_shmInfo, _outputWidth, _outputHeight); return true; } auto free() -> void { if(_outputBuffer) { _outputBuffer = nullptr; XShmDetach(_display, &_shmInfo); XDestroyImage(_image); shmdt(_shmInfo.shmaddr); shmctl(_shmInfo.shmid, IPC_RMID, 0); } } alwaysinline auto interpolate(float mu, uint32_t a, uint32_t b) -> uint32_t { uint8_t ar = a >> 16, ag = a >> 8, ab = a >> 0; uint8_t br = b >> 16, bg = b >> 8, bb = b >> 0; uint8_t cr = ar * (1.0 - mu) + br * mu; uint8_t cg = ag * (1.0 - mu) + bg * mu; uint8_t cb = ab * (1.0 - mu) + bb * mu; return cr << 16 | cg << 8 | cb << 0; } bool _ready = false; uintptr _context = 0; bool _smooth = true; uint32_t* _inputBuffer = nullptr; uint _inputWidth = 0; uint _inputHeight = 0; Display* _display = nullptr; int _screen = 0; int _depth = 0; Visual* _visual = nullptr; Window _window = 0; XShmSegmentInfo _shmInfo; XImage* _image = nullptr; uint32_t* _outputBuffer = nullptr; uint _outputWidth = 0; uint _outputHeight = 0; };