mirror of https://github.com/bsnes-emu/bsnes.git
133 lines
3.6 KiB
C++
133 lines
3.6 KiB
C++
#include <sys/ipc.h>
|
|
#include <sys/shm.h>
|
|
#include <X11/extensions/Xv.h>
|
|
#include <X11/extensions/Xvlib.h>
|
|
#include <X11/extensions/XShm.h>
|
|
#include <SDL/SDL.h>
|
|
|
|
struct VideoSDL : Video {
|
|
VideoSDL() { initialize(); }
|
|
~VideoSDL() { terminate(); }
|
|
|
|
auto ready() -> bool { return _ready; }
|
|
|
|
auto context() -> uintptr { return _context; }
|
|
|
|
auto setContext(uintptr context) -> bool {
|
|
if(_context == context) return true;
|
|
_context = context;
|
|
return initialize();
|
|
}
|
|
|
|
auto clear() -> void {
|
|
if(!ready()) return;
|
|
if(SDL_MUSTLOCK(_buffer)) SDL_LockSurface(_buffer);
|
|
for(uint y : range(_bufferHeight)) {
|
|
uint32_t* data = (uint32_t*)_buffer->pixels + y * (_buffer->pitch >> 2);
|
|
for(uint x : range(_bufferWidth)) *data++ = 0xff000000;
|
|
}
|
|
if(SDL_MUSTLOCK(_buffer)) SDL_UnlockSurface(_buffer);
|
|
output();
|
|
}
|
|
|
|
auto lock(uint32_t*& data, uint& pitch, uint width, uint height) -> bool {
|
|
if(!ready()) return false;
|
|
if(width != _width || height != _height) resize(_width = width, _height = height);
|
|
if(SDL_MUSTLOCK(_buffer)) SDL_LockSurface(_buffer);
|
|
pitch = _buffer->pitch;
|
|
return data = (uint32_t*)_buffer->pixels;
|
|
}
|
|
|
|
auto unlock() -> void {
|
|
if(!ready()) return;
|
|
if(SDL_MUSTLOCK(_buffer)) SDL_UnlockSurface(_buffer);
|
|
}
|
|
|
|
auto output() -> void {
|
|
if(!ready()) return;
|
|
|
|
//ruby input is X8R8G8B8, top 8-bits are ignored.
|
|
//as SDL forces us to use a 32-bit buffer, we must set alpha to 255 (full opacity)
|
|
//to prevent blending against the window beneath when X window visual is 32-bits.
|
|
if(SDL_MUSTLOCK(_buffer)) SDL_LockSurface(_buffer);
|
|
for(uint y : range(_height)) {
|
|
uint32_t* data = (uint32_t*)_buffer->pixels + y * (_buffer->pitch >> 2);
|
|
for(uint x : range(_width)) *data++ |= 0xff000000;
|
|
}
|
|
if(SDL_MUSTLOCK(_buffer)) SDL_UnlockSurface(_buffer);
|
|
|
|
XWindowAttributes attributes;
|
|
XGetWindowAttributes(_display, _context, &attributes);
|
|
|
|
SDL_Rect source;
|
|
SDL_Rect target;
|
|
|
|
source.x = 0;
|
|
source.y = 0;
|
|
source.w = _width;
|
|
source.h = _height;
|
|
|
|
target.x = 0;
|
|
target.y = 0;
|
|
target.w = attributes.width;
|
|
target.h = attributes.height;
|
|
|
|
SDL_SoftStretch(_buffer, &source, _screen, &target);
|
|
SDL_UpdateRect(_screen, target.x, target.y, target.w, target.h);
|
|
}
|
|
|
|
private:
|
|
auto initialize() -> bool {
|
|
terminate();
|
|
if(!_context) return false;
|
|
|
|
_display = XOpenDisplay(0);
|
|
|
|
char env[512];
|
|
sprintf(env, "SDL_WINDOWID=%ld", (long)_context);
|
|
putenv(env);
|
|
|
|
SDL_InitSubSystem(SDL_INIT_VIDEO);
|
|
_screen = SDL_SetVideoMode(2560, 1600, 32, SDL_HWSURFACE);
|
|
XUndefineCursor(_display, _context);
|
|
|
|
_buffer = nullptr;
|
|
_bufferWidth = 0;
|
|
_bufferHeight = 0;
|
|
resize(_width = 256, _height = 256);
|
|
|
|
return _ready = true;
|
|
}
|
|
|
|
auto terminate() -> void {
|
|
_ready = false;
|
|
if(_buffer) SDL_FreeSurface(_buffer), _buffer = nullptr;
|
|
if(_screen) SDL_QuitSubSystem(SDL_INIT_VIDEO), _screen = nullptr;
|
|
if(_display) XCloseDisplay(_display), _display = nullptr;
|
|
}
|
|
|
|
auto resize(uint width, uint height) -> void {
|
|
if(_bufferWidth >= width && _bufferHeight >= height) return;
|
|
|
|
_bufferWidth = max(width, _bufferWidth);
|
|
_bufferHeight = max(height, _bufferHeight);
|
|
|
|
if(_buffer) SDL_FreeSurface(_buffer);
|
|
_buffer = SDL_CreateRGBSurface(
|
|
SDL_SWSURFACE, _bufferWidth, _bufferHeight, 32,
|
|
0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000
|
|
);
|
|
}
|
|
|
|
bool _ready = false;
|
|
uintptr _context = 0;
|
|
|
|
Display* _display = nullptr;
|
|
SDL_Surface* _screen = nullptr;
|
|
SDL_Surface* _buffer = nullptr;
|
|
uint _bufferWidth = 0;
|
|
uint _bufferHeight = 0;
|
|
uint _width = 0;
|
|
uint _height = 0;
|
|
};
|