File: sdl.cpp

package info (click to toggle)
higan 098-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 11,904 kB
  • ctags: 13,286
  • sloc: cpp: 108,285; ansic: 778; makefile: 32; sh: 18
file content (135 lines) | stat: -rw-r--r-- 3,697 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#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);
  }
};