more properly handling new haa semantics
[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.Pitch = scaler->getDrawBufferPitch();
148         GFX.ZPitch = GFX.Pitch / 2;
149         // gfx & tile.cpp depend on the zbuffer pitch being always half of the color buffer pitch.
150         // Which is a pity, since the color buffer might be much larger.
151
152         GFX.Screen = scaler->getDrawBuffer();
153         GFX.SubScreen = (uint8 *) malloc(GFX.Pitch * IMAGE_HEIGHT);
154         GFX.ZBuffer =  (uint8 *) malloc(GFX.ZPitch * IMAGE_HEIGHT);
155         GFX.SubZBuffer = (uint8 *) malloc(GFX.ZPitch * IMAGE_HEIGHT);
156
157         GFX.Delta = (GFX.SubScreen - GFX.Screen) >> 1;
158         GFX.DepthDelta = GFX.SubZBuffer - GFX.ZBuffer;
159         GFX.PPL = GFX.Pitch / (screen->format->BitsPerPixel / 8);
160
161         scaler->getRenderedGUIArea(GUI.RenderX, GUI.RenderY, GUI.RenderW, GUI.RenderH);
162         scaler->getRatio(GUI.ScaleX, GUI.ScaleY);
163
164         printf("Video: %dx%d (%dx%d output), %hu bits per pixel, %s, %s\n",
165                 gameWidth, gameHeight,
166                 screen->w, screen->h, screen->format->BitsPerPixel,
167                 Config.fullscreen ? "fullscreen" : "windowed",
168                 scaler->getName());
169 }
170
171 static void drawOnscreenControls()
172 {
173         if (Config.touchscreenInput) {
174                 S9xInputScreenChanged();
175         }
176
177         if (Config.touchscreenShow) {
178                 scaler->pause();
179                 SDL_FillRect(screen, NULL, 0);
180                 S9xInputScreenDraw(Settings.SixteenBit ? 2 : 1,
181                                                         screen->pixels, screen->pitch);
182                 SDL_Flip(screen);
183                 scaler->resume();
184         }
185 }
186
187 void S9xInitDisplay(int argc, char ** argv)
188 {       
189         if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) 
190                 DIE("SDL_InitSubSystem(VIDEO): %s", SDL_GetError());
191
192         setupVideoSurface();
193         drawOnscreenControls();
194 }
195
196 void S9xDeinitDisplay()
197 {
198         freeVideoSurface();     
199         SDL_QuitSubSystem(SDL_INIT_VIDEO);
200 }
201
202 void S9xVideoToggleFullscreen()
203 {
204         freeVideoSurface();
205         Config.fullscreen = !Config.fullscreen;
206         setupVideoSurface();
207         drawOnscreenControls();
208 }
209
210 bool videoEventFilter(const SDL_Event& event)
211 {
212         // If we're in power save mode, and this is a defocus event, quit.
213         if (Config.saver) {
214                 if (event.type == SDL_ACTIVEEVENT &&
215                    (event.active.state & SDL_APPINPUTFOCUS) &&
216                    !event.active.gain) {
217                         S9xDoAction(kActionQuit);
218                         return true;
219                 }
220         }
221
222         // Forward video event to the active scaler, if any.
223         if (scaler)
224                 return scaler->filter(event);
225         else
226                 return false;
227 }
228
229 // This is here for completeness, but palette mode is mostly useless (slow).
230 void S9xSetPalette ()
231 {
232         if (Settings.SixteenBit) return;
233         
234         SDL_Color colors[256];
235         int brightness = IPPU.MaxBrightness *138;
236         for (int i = 0; i < 256; i++)
237         {
238                 colors[i].r = ((PPU.CGDATA[i] >> 0) & 0x1F) * brightness;
239                 colors[i].g = ((PPU.CGDATA[i] >> 5) & 0x1F) * brightness;
240                 colors[i].b = ((PPU.CGDATA[i] >> 10) & 0x1F) * brightness;
241         }
242         
243         SDL_SetColors(screen, colors, 0, 256);
244 }
245
246 /** Called before rendering a frame.
247         This function must ensure GFX.Screen points to something, but we did that
248         while initializing video output.
249         @return TRUE if we should render the frame.
250  */
251 bool8_32 S9xInitUpdate ()
252 {
253         scaler->prepare();
254
255         return TRUE;
256 }
257
258 /** Called once a complete SNES screen has been rendered into the GFX.Screen
259         memory buffer.
260
261         Now is your chance to copy the SNES rendered screen to the
262         host computer's screen memory. The problem is that you have to cope with
263         different sized SNES rendered screens. Width is always 256, unless you're
264         supporting SNES hi-res. screen modes (Settings.SupportHiRes is TRUE), in
265         which case it can be 256 or 512. The height parameter can be either 224 or
266         239 if you're only supporting SNES lo-res. screen modes, or 224, 239, 448 or
267         478 if hi-res. SNES screen modes are being supported.
268  */
269 // TODO Above.
270 bool8_32 S9xDeinitUpdate (int width, int height, bool8_32 sixteenBit)
271 {
272         scaler->finish();
273
274 #if CONF_EXIT_BUTTON
275         if (ExitBtnRequiresDraw()) {
276                 scaler->pause();
277                 ExitBtnDraw(screen);
278                 scaler->resume();
279         }
280 #endif
281
282         return TRUE;
283 }
284