bsnes/ruby/video/sdl.cpp

136 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() { term(); }
Display* display = nullptr;
SDL_Surface* screen = nullptr;
SDL_Surface* buffer = nullptr;
unsigned iwidth = 0;
unsigned iheight = 0;
struct {
uintptr_t handle = 0;
unsigned width = 0;
unsigned height = 0;
} settings;
auto cap(const string& name) -> bool {
if(name == Video::Handle) return true;
return false;
}
auto get(const string& name) -> any {
if(name == Video::Handle) return settings.handle;
return {};
}
auto set(const string& name, const any& value) -> bool {
if(name == Video::Handle && value.is<uintptr_t>()) {
settings.handle = value.get<uintptr_t>();
return true;
}
return false;
}
auto resize(unsigned width, unsigned height) -> void {
if(iwidth >= width && iheight >= height) return;
iwidth = max(width, iwidth);
iheight = max(height, iheight);
if(buffer) SDL_FreeSurface(buffer);
buffer = SDL_CreateRGBSurface(
SDL_SWSURFACE, iwidth, iheight, 32,
0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000
);
}
auto lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) -> bool {
if(width != settings.width || height != settings.height) {
resize(settings.width = width, settings.height = height);
}
if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer);
pitch = buffer->pitch;
return data = (uint32_t*)buffer->pixels;
}
auto unlock() -> void {
if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer);
}
auto clear() -> void {
if(SDL_MUSTLOCK(buffer)) SDL_LockSurface(buffer);
for(unsigned y = 0; y < iheight; y++) {
uint32_t* data = (uint32_t*)buffer->pixels + y * (buffer->pitch >> 2);
for(unsigned x = 0; x < iwidth; x++) *data++ = 0xff000000;
}
if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer);
refresh();
}
auto refresh() -> void {
//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(unsigned y = 0; y < settings.height; y++) {
uint32_t* data = (uint32_t*)buffer->pixels + y * (buffer->pitch >> 2);
for(unsigned x = 0; x < settings.width; x++) *data++ |= 0xff000000;
}
if(SDL_MUSTLOCK(buffer)) SDL_UnlockSurface(buffer);
XWindowAttributes attributes;
XGetWindowAttributes(display, settings.handle, &attributes);
SDL_Rect src, dest;
src.x = 0;
src.y = 0;
src.w = settings.width;
src.h = settings.height;
dest.x = 0;
dest.y = 0;
dest.w = attributes.width;
dest.h = attributes.height;
SDL_SoftStretch(buffer, &src, screen, &dest);
SDL_UpdateRect(screen, dest.x, dest.y, dest.w, dest.h);
}
auto init() -> bool {
display = XOpenDisplay(0);
//todo: this causes a segfault inside SDL_SetVideoMode on FreeBSD (works under Linux)
char env[512];
sprintf(env, "SDL_WINDOWID=%ld", (long)settings.handle);
putenv(env);
SDL_InitSubSystem(SDL_INIT_VIDEO);
screen = SDL_SetVideoMode(2560, 1600, 32, SDL_HWSURFACE);
XUndefineCursor(display, settings.handle);
buffer = 0;
iwidth = 0;
iheight = 0;
resize(settings.width = 256, settings.height = 256);
return true;
}
auto term() -> void {
XCloseDisplay(display);
SDL_FreeSurface(buffer);
SDL_QuitSubSystem(SDL_INIT_VIDEO);
}
};