new build system; package can be built in i386
[drnoksnes] / platform / sdlv.cpp
1 #include <stdio.h>
2
3 #include <X11/Xlib.h>
4 #include <X11/Xutil.h>
5 #include <SDL.h>
6 #include <SDL_syswm.h>
7
8 #if CONF_XSP
9 #       include <X11/extensions/Xsp.h>
10 #endif
11
12 #include "snes9x.h"
13 #include "platform.h"
14 #include "display.h"
15 #include "gfx.h"
16 #include "ppu.h"
17
18 #define DIE(format, ...) do { \
19                 fprintf(stderr, "Died at %s:%d: ", __FILE__, __LINE__ ); \
20                 fprintf(stderr, format "\n", ## __VA_ARGS__); \
21                 abort(); \
22         } while (0);
23
24 struct gui GUI;
25
26 static SDL_Surface *screen;
27
28 static SDL_Rect windowSize, screenSize;
29 static bool gotWindowSize, gotScreenSize;
30
31 /** Inside the surface, where are we drawing */
32 static SDL_Rect renderArea;
33
34 #if CONF_XSP
35 static void setDoubling(bool enable)
36 {
37         SDL_SysWMinfo wminfo;
38         SDL_VERSION(&wminfo.version);
39         if ( SDL_GetWMInfo(&wminfo) ) {
40                 Display *dpy = wminfo.info.x11.display;
41                 XSPSetPixelDoubling(dpy, 0, enable ? 1 : 0);
42                 XFlush(dpy);
43         }
44 }
45 #endif
46
47 static void centerRectangle(SDL_Rect& result, int areaW, int areaH, int w, int h)
48 {
49         result.x = areaW / 2 - w / 2;
50         result.w = w;
51         result.y = areaH / 2 - h / 2;
52         result.h = h;
53 }
54
55 static void calculateScreenSize()
56 {
57         SDL_SysWMinfo wminfo;
58         SDL_VERSION(&wminfo.version);
59
60         if ( SDL_GetWMInfo(&wminfo) ) {
61                 Display *dpy = wminfo.info.x11.display;
62                 Window w;
63                 SDL_Rect* size;
64                 XWindowAttributes xwa;
65
66                 if (Config.fullscreen) {
67                         w =  wminfo.info.x11.fswindow;
68                         size = &screenSize;
69                         gotScreenSize = true;
70                 } else {
71                         w =  wminfo.info.x11.wmwindow;
72                         size = &windowSize;
73                         gotWindowSize = true;
74                 }
75
76                 XGetWindowAttributes(dpy, w, &xwa);
77                 size->x = xwa.x;
78                 size->y = xwa.y;
79                 size->w = xwa.width;
80                 size->h = xwa.height;
81         }
82 }
83
84 void S9xSetTitle(const char *title)
85 {
86         SDL_SysWMinfo info;
87         SDL_VERSION(&info.version);
88         if ( SDL_GetWMInfo(&info) ) {
89                 Display *dpy = info.info.x11.display;
90                 Window win;
91                 if (dpy) {
92                         win = info.info.x11.fswindow;
93                         if (win) XStoreName(dpy, win, title);
94                         win = info.info.x11.wmwindow;
95                         if (win) XStoreName(dpy, win, title);
96                 }
97         }
98 }
99
100 static void freeVideoSurface()
101 {
102         screen = 0; // There's no need to free the screen surface.
103         GFX.Screen = 0;
104         
105         free(GFX.SubScreen); GFX.SubScreen = 0;
106         free(GFX.ZBuffer); GFX.ZBuffer = 0;
107         free(GFX.SubZBuffer); GFX.SubZBuffer = 0;
108 }
109
110 static void setupVideoSurface()
111 {
112         // Real surface area.
113         const unsigned gameWidth = IMAGE_WIDTH;
114         const unsigned gameHeight = IMAGE_HEIGHT;
115
116 #ifdef MAEMO
117         if ((Config.fullscreen && !gotScreenSize) ||
118                 (!Config.fullscreen && !gotWindowSize)) {
119                 // Do a first try, in order to get window/screen size
120                 screen = SDL_SetVideoMode(gameWidth, gameHeight, 16,
121                         SDL_SWSURFACE | SDL_RESIZABLE |
122                         (Config.fullscreen ? SDL_FULLSCREEN : 0));
123                 if (!screen) DIE("SDL_SetVideoMode: %s", SDL_GetError());
124                 calculateScreenSize();
125         }
126         if (Config.fullscreen) {
127                 GUI.Width = screenSize.w;
128                 GUI.Height = screenSize.h;
129         } else {
130                 GUI.Width = windowSize.w;
131                 GUI.Height = windowSize.h;
132         }
133 #else
134         GUI.Width = gameWidth;
135         GUI.Height = gameHeight;
136 #endif
137 #if CONF_XSP
138         // So, can we enable Xsp?
139         if (gameWidth * 2 < GUI.Width && gameHeight * 2 < GUI.Height) {
140                 Config.xsp = true;
141         } else  {
142                 Config.xsp = false;
143                 setDoubling(false); // Before switching video modes; avoids flicker.
144         }
145 #else
146         Config.xsp = false;
147 #endif
148
149         // Safeguard
150         if (gameHeight > GUI.Height || gameWidth > GUI.Width)
151                 DIE("Video is larger than window size!");
152
153         screen = SDL_SetVideoMode(GUI.Width, GUI.Height,
154                                                                 Settings.SixteenBit ? 16 : 8,
155                                                                 SDL_SWSURFACE |
156                                                                 (Config.fullscreen ? SDL_FULLSCREEN : 0));
157         if (!screen)
158                 DIE("SDL_SetVideoMode: %s", SDL_GetError());
159         
160         SDL_ShowCursor(SDL_DISABLE);
161
162         // We get pitch surface values from SDL
163         GFX.RealPitch = GFX.Pitch = screen->pitch;
164         GFX.ZPitch = GFX.Pitch / 2; // gfx & tile.cpp depend on this, unfortunately.
165         GFX.PixSize = screen->format->BitsPerPixel / 8;
166
167         // Ok, calculate renderArea
168 #if CONF_XSP
169         if (Config.xsp) {
170                 setDoubling(true);
171                 centerRectangle(renderArea, GUI.Width, GUI.Height,
172                         gameWidth * 2, gameHeight * 2);
173         } else {
174                 centerRectangle(renderArea, GUI.Width, GUI.Height,
175                         gameWidth, gameHeight);
176         }
177 #else
178         centerRectangle(renderArea, GUI.Width, GUI.Height, gameWidth, gameHeight);
179 #endif
180         
181         GFX.Screen = ((uint8*) screen->pixels)
182                 + (renderArea.x * GFX.PixSize)
183                 + (renderArea.y * GFX.Pitch);
184         GFX.SubScreen = (uint8 *) malloc(GFX.Pitch * IMAGE_HEIGHT);
185         GFX.ZBuffer =  (uint8 *) malloc(GFX.ZPitch * IMAGE_HEIGHT);
186         GFX.SubZBuffer = (uint8 *) malloc(GFX.ZPitch * IMAGE_HEIGHT);
187
188         GFX.Delta = (GFX.SubScreen - GFX.Screen) >> 1;
189         GFX.PPL = GFX.Pitch >> 1;
190         GFX.PPLx2 = GFX.Pitch;
191
192         GUI.RenderX = renderArea.x;
193         GUI.RenderY = renderArea.y;
194         GUI.RenderW = renderArea.w;
195         GUI.RenderH = renderArea.h;
196
197 #if CONF_XSP
198         if (Config.xsp) {
199                 // Do not update 2x the area.
200                 renderArea.w /= 2;
201                 renderArea.h /= 2;
202         }
203 #endif
204
205         printf("Video: %dx%d (%dx%d output), %hu bits per pixel, %s %s\n",
206                 gameWidth, gameHeight,
207                 screen->w, screen->h, screen->format->BitsPerPixel,
208                 Config.fullscreen ? "fullscreen" : "windowed",
209                 Config.xsp ? "with pixel doubling" : "");
210 }
211
212 void S9xInitDisplay(int argc, const char ** argv)
213 {       
214         if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) 
215                 DIE("SDL_InitSubSystem(VIDEO): %s", SDL_GetError());
216
217         setupVideoSurface();
218 }
219
220 void S9xDeinitDisplay()
221 {
222         freeVideoSurface();     
223         SDL_QuitSubSystem(SDL_INIT_VIDEO);
224 }
225
226 void S9xVideoToggleFullscreen()
227 {
228         Config.fullscreen = !Config.fullscreen;
229         freeVideoSurface();
230         setupVideoSurface();
231 }
232
233 void S9xVideoOutputFocus(bool hasFocus)
234 {
235 #if CONF_XSP
236         if (Config.xsp) {
237                 setDoubling(hasFocus);
238         } 
239 #endif
240 }
241
242 // This is here for completeness, but palette mode is useless on N8x0
243 void S9xSetPalette ()
244 {
245         if (Settings.SixteenBit) return;
246         
247         SDL_Color colors[256];
248         int brightness = IPPU.MaxBrightness *138;
249         for (int i = 0; i < 256; i++)
250         {
251                 colors[i].r = ((PPU.CGDATA[i] >> 0) & 0x1F) * brightness;
252                 colors[i].g = ((PPU.CGDATA[i] >> 5) & 0x1F) * brightness;
253                 colors[i].b = ((PPU.CGDATA[i] >> 10) & 0x1F) * brightness;
254         }
255         
256         SDL_SetColors(screen, colors, 0, 256);
257 }
258
259 bool8_32 S9xInitUpdate ()
260 {
261         if(SDL_MUSTLOCK(screen)) 
262         {
263                 if(SDL_LockSurface(screen) < 0) {
264                         DIE("Failed to lock SDL surface: %s", SDL_GetError());
265                 }
266         }
267
268         return TRUE;
269 }
270
271 bool8_32 S9xDeinitUpdate (int width, int height, bool8_32 sixteenBit)
272 {
273         if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);
274
275         SDL_UpdateRects(screen, 1, &renderArea);
276
277         return TRUE;
278 }
279