afe01879ac5527c3c8de9c86dbe09b72c6ae8e9c
[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 #include "snes9x.h"
9 #include "platform.h"
10 #include "display.h"
11 #include "gfx.h"
12 #include "ppu.h"
13 #include "sdlv.h"
14
15 #define DIE(format, ...) do { \
16                 fprintf(stderr, "Died at %s:%d: ", __FILE__, __LINE__ ); \
17                 fprintf(stderr, format "\n", ## __VA_ARGS__); \
18                 abort(); \
19         } while (0);
20
21 struct gui GUI;
22
23 SDL_Surface* screen;
24
25 static SDL_Rect windowSize, screenSize;
26 static bool gotWindowSize, gotScreenSize;
27
28 /** The current scaler object */
29 Scaler* scaler;
30
31 /** Use the current window size to calculate screen size.
32         Useful on "single window" platforms, like Hildon.
33  */
34 static void calculateScreenSize()
35 {
36         SDL_SysWMinfo wminfo;
37         SDL_VERSION(&wminfo.version);
38
39         if ( SDL_GetWMInfo(&wminfo) ) {
40                 Display *dpy = wminfo.info.x11.display;
41                 Window w;
42                 SDL_Rect* size;
43                 XWindowAttributes xwa;
44
45                 if (Config.fullscreen) {
46                         w =  wminfo.info.x11.fswindow;
47                         size = &screenSize;
48                         gotScreenSize = true;
49                 } else {
50                         w =  wminfo.info.x11.wmwindow;
51                         size = &windowSize;
52                         gotWindowSize = true;
53                 }
54
55                 XGetWindowAttributes(dpy, w, &xwa);
56                 size->x = xwa.x;
57                 size->y = xwa.y;
58                 size->w = xwa.width;
59                 size->h = xwa.height;
60         }
61 }
62
63 /** Sets the main window title */
64 void S9xSetTitle(const char *title)
65 {
66         // This is a Maemo specific hack, but works on most platforms.
67         SDL_SysWMinfo info;
68         SDL_VERSION(&info.version);
69         if ( SDL_GetWMInfo(&info) ) {
70                 Display *dpy = info.info.x11.display;
71                 Window win;
72                 if (dpy) {
73                         win = info.info.x11.fswindow;
74                         if (win) XStoreName(dpy, win, title);
75                         win = info.info.x11.wmwindow;
76                         if (win) XStoreName(dpy, win, title);
77                 }
78         }
79 }
80
81 static void freeVideoSurface()
82 {
83         screen = 0; // There's no need to free the screen surface.
84         GFX.Screen = 0;
85
86         free(GFX.SubScreen); GFX.SubScreen = 0;
87         free(GFX.ZBuffer); GFX.ZBuffer = 0;
88         free(GFX.SubZBuffer); GFX.SubZBuffer = 0;
89
90         delete scaler; scaler = 0;
91 }
92
93 static void setupVideoSurface()
94 {
95         // Real surface area.
96         const unsigned gameWidth = IMAGE_WIDTH;
97         const unsigned gameHeight = IMAGE_HEIGHT;
98
99 #ifdef MAEMO
100         // Under Maemo we know that the window manager will automatically
101         // resize our windows to fullscreen.
102         // Thus we can use that to detect the screen size.
103         // Of course, this causes flicker, so we try to avoid it when
104         // changing between modes.
105         if ((Config.fullscreen && !gotScreenSize) ||
106                 (!Config.fullscreen && !gotWindowSize)) {
107                 screen = SDL_SetVideoMode(gameWidth, gameHeight, 16,
108                         SDL_SWSURFACE | SDL_RESIZABLE |
109                         (Config.fullscreen ? SDL_FULLSCREEN : 0));
110                 if (!screen) DIE("SDL_SetVideoMode: %s", SDL_GetError());
111                 calculateScreenSize();
112         }
113         if (Config.fullscreen) {
114                 GUI.Width = screenSize.w;
115                 GUI.Height = screenSize.h;
116         } else {
117                 GUI.Width = windowSize.w;
118                 GUI.Height = windowSize.h;
119         }
120 #else
121         GUI.Width = gameWidth;
122         GUI.Height = gameHeight;
123 #endif
124 #if CONF_EXIT_BUTTON
125         ExitBtnReset();
126 #endif
127
128         // Safeguard
129         if (gameHeight > GUI.Height || gameWidth > GUI.Width)
130                 DIE("Video is larger than window size!");
131
132         const ScalerFactory* sFactory =
133                 searchForScaler(Settings.SixteenBit ? 16 : 8, gameWidth, gameHeight);
134
135         screen = SDL_SetVideoMode(GUI.Width, GUI.Height,
136                                                                 Settings.SixteenBit ? 16 : 8,
137                                                                 SDL_SWSURFACE |
138                                                                 (Config.fullscreen ? SDL_FULLSCREEN : 0));
139         if (!screen)
140                 DIE("SDL_SetVideoMode: %s", SDL_GetError());
141         
142         SDL_ShowCursor(SDL_DISABLE);
143
144         scaler = sFactory->instantiate(screen, gameWidth, gameHeight);
145
146         // Each scaler may have its own pitch
147         GFX.RealPitch = GFX.Pitch = scaler->getDrawBufferPitch();
148         GFX.ZPitch = GFX.Pitch / 2; // gfx & tile.cpp depend on this, unfortunately.
149         GFX.PixSize = screen->format->BitsPerPixel / 8;
150         
151         GFX.Screen = scaler->getDrawBuffer();
152         GFX.SubScreen = (uint8 *) malloc(GFX.Pitch * IMAGE_HEIGHT);
153         GFX.ZBuffer =  (uint8 *) malloc(GFX.ZPitch * IMAGE_HEIGHT);
154         GFX.SubZBuffer = (uint8 *) malloc(GFX.ZPitch * IMAGE_HEIGHT);
155
156         GFX.Delta = (GFX.SubScreen - GFX.Screen) >> 1;
157         GFX.PPL = GFX.Pitch >> 1;
158         GFX.PPLx2 = GFX.Pitch;
159
160         scaler->getRenderedGUIArea(GUI.RenderX, GUI.RenderY, GUI.RenderW, GUI.RenderH);
161         scaler->getRatio(GUI.ScaleX, GUI.ScaleY);
162
163         printf("Video: %dx%d (%dx%d output), %hu bits per pixel, %s, %s\n",
164                 gameWidth, gameHeight,
165                 screen->w, screen->h, screen->format->BitsPerPixel,
166                 Config.fullscreen ? "fullscreen" : "windowed",
167                 scaler->getName());
168 }
169
170 static void drawOnscreenControls()
171 {
172         if (Config.touchscreenInput) {
173                 S9xInputScreenChanged();
174         }
175
176         if (Config.touchscreenShow) {
177                 scaler->pause();
178                 SDL_FillRect(screen, NULL, 0);
179                 S9xInputScreenDraw(Settings.SixteenBit ? 2 : 1,
180                                                         screen->pixels, screen->pitch);
181                 SDL_Flip(screen);
182                 scaler->resume();
183         }
184 }
185
186 void S9xInitDisplay(int argc, char ** argv)
187 {       
188         if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) 
189                 DIE("SDL_InitSubSystem(VIDEO): %s", SDL_GetError());
190
191         setupVideoSurface();
192         drawOnscreenControls();
193 }
194
195 void S9xDeinitDisplay()
196 {
197         freeVideoSurface();     
198         SDL_QuitSubSystem(SDL_INIT_VIDEO);
199 }
200
201 void S9xVideoToggleFullscreen()
202 {
203         freeVideoSurface();
204         Config.fullscreen = !Config.fullscreen;
205         setupVideoSurface();
206         drawOnscreenControls();
207 }
208
209 void processVideoEvent(const SDL_Event& event)
210 {
211         // If we're in power save mode, and this is a defocus event, quit.
212         if (Config.saver) {
213                 if (event.type == SDL_ACTIVEEVENT &&
214                    (event.active.state & SDL_APPINPUTFOCUS) &&
215                    !event.active.gain) {
216                         S9xDoAction(kActionQuit);
217                         return;
218                 }
219         }
220
221         // Forward video event to the active scaler, if any.
222         if (scaler)
223                 scaler->filter(event);
224 }
225
226 // This is here for completeness, but palette mode is mostly useless (slow).
227 void S9xSetPalette ()
228 {
229         if (Settings.SixteenBit) return;
230         
231         SDL_Color colors[256];
232         int brightness = IPPU.MaxBrightness *138;
233         for (int i = 0; i < 256; i++)
234         {
235                 colors[i].r = ((PPU.CGDATA[i] >> 0) & 0x1F) * brightness;
236                 colors[i].g = ((PPU.CGDATA[i] >> 5) & 0x1F) * brightness;
237                 colors[i].b = ((PPU.CGDATA[i] >> 10) & 0x1F) * brightness;
238         }
239         
240         SDL_SetColors(screen, colors, 0, 256);
241 }
242
243 /** Called before rendering a frame.
244         This function must ensure GFX.Screen points to something, but we did that
245         while initializing video output.
246         @return TRUE if we should render the frame.
247  */
248 bool8_32 S9xInitUpdate ()
249 {
250         scaler->prepare();
251
252         return TRUE;
253 }
254
255 /** Called once a complete SNES screen has been rendered into the GFX.Screen
256         memory buffer.
257
258         Now is your chance to copy the SNES rendered screen to the
259         host computer's screen memory. The problem is that you have to cope with
260         different sized SNES rendered screens. Width is always 256, unless you're
261         supporting SNES hi-res. screen modes (Settings.SupportHiRes is TRUE), in
262         which case it can be 256 or 512. The height parameter can be either 224 or
263         239 if you're only supporting SNES lo-res. screen modes, or 224, 239, 448 or
264         478 if hi-res. SNES screen modes are being supported.
265  */
266 // TODO Above.
267 bool8_32 S9xDeinitUpdate (int width, int height, bool8_32 sixteenBit)
268 {
269         scaler->finish();
270
271 #if CONF_EXIT_BUTTON
272         if (ExitBtnRequiresDraw()) {
273                 scaler->pause();
274                 ExitBtnDraw(screen);
275                 scaler->resume();
276         }
277 #endif
278
279         return TRUE;
280 }
281