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