reducing size of gfx state, cleanup
[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 void processVideoEvent(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;
219                 }
220         }
221
222         // Forward video event to the active scaler, if any.
223         if (scaler)
224                 scaler->filter(event);
225 }
226
227 // This is here for completeness, but palette mode is mostly useless (slow).
228 void S9xSetPalette ()
229 {
230         if (Settings.SixteenBit) return;
231         
232         SDL_Color colors[256];
233         int brightness = IPPU.MaxBrightness *138;
234         for (int i = 0; i < 256; i++)
235         {
236                 colors[i].r = ((PPU.CGDATA[i] >> 0) & 0x1F) * brightness;
237                 colors[i].g = ((PPU.CGDATA[i] >> 5) & 0x1F) * brightness;
238                 colors[i].b = ((PPU.CGDATA[i] >> 10) & 0x1F) * brightness;
239         }
240         
241         SDL_SetColors(screen, colors, 0, 256);
242 }
243
244 /** Called before rendering a frame.
245         This function must ensure GFX.Screen points to something, but we did that
246         while initializing video output.
247         @return TRUE if we should render the frame.
248  */
249 bool8_32 S9xInitUpdate ()
250 {
251         scaler->prepare();
252
253         return TRUE;
254 }
255
256 /** Called once a complete SNES screen has been rendered into the GFX.Screen
257         memory buffer.
258
259         Now is your chance to copy the SNES rendered screen to the
260         host computer's screen memory. The problem is that you have to cope with
261         different sized SNES rendered screens. Width is always 256, unless you're
262         supporting SNES hi-res. screen modes (Settings.SupportHiRes is TRUE), in
263         which case it can be 256 or 512. The height parameter can be either 224 or
264         239 if you're only supporting SNES lo-res. screen modes, or 224, 239, 448 or
265         478 if hi-res. SNES screen modes are being supported.
266  */
267 // TODO Above.
268 bool8_32 S9xDeinitUpdate (int width, int height, bool8_32 sixteenBit)
269 {
270         scaler->finish();
271
272 #if CONF_EXIT_BUTTON
273         if (ExitBtnRequiresDraw()) {
274                 scaler->pause();
275                 ExitBtnDraw(screen);
276                 scaler->resume();
277         }
278 #endif
279
280         return TRUE;
281 }
282