adding gconf schema for defaults, advanced setting
[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 class Scaler;
32 /** The current scaler object */
33 static Scaler* scaler;
34
35 static void centerRectangle(SDL_Rect& result, int areaW, int areaH, int w, int h)
36 {
37         result.x = areaW / 2 - w / 2;
38         result.w = w;
39         result.y = areaH / 2 - h / 2;
40         result.h = h;
41 }
42
43 class Scaler
44 {
45 public:
46         Scaler() { };
47         virtual ~Scaler() { };
48
49         virtual const char * getName() const = 0;
50
51         virtual uint8* getDrawBuffer() const = 0;
52         virtual unsigned int getDrawBufferPitch() const = 0;
53         virtual void getRenderedGUIArea(unsigned short & x, unsigned short & y,
54                                                                         unsigned short & w, unsigned short & h)
55                                                                         const = 0;
56         virtual int getRatio() const = 0;
57         virtual void prepare() = 0;
58         virtual void finish() = 0;
59 };
60
61 class ScalerFactory
62 {
63 public:
64         ScalerFactory() { };
65         virtual ~ScalerFactory() { };
66         virtual const char * getName() const = 0;
67         virtual bool canEnable(int w, int h) const = 0;
68         virtual Scaler* instantiate(SDL_Surface* screen, int w, int h) const = 0;
69 };
70
71 class DummyScaler : public Scaler
72 {
73         SDL_Surface * m_screen;
74         SDL_Rect m_area;
75
76         DummyScaler(SDL_Surface* screen, int w, int h)
77         : m_screen(screen)
78         {
79                 centerRectangle(m_area, GUI.Width, GUI.Height, w, h);
80         }
81 public:
82
83         ~DummyScaler()
84         {
85         };
86
87         class Factory : public ScalerFactory
88         {
89                 const char * getName() const
90                 {
91                         return "none";
92                 }
93
94                 bool canEnable(int w, int h) const
95                 {
96                         return true;
97                 }
98
99                 Scaler* instantiate(SDL_Surface* screen, int w, int h) const
100                 {
101                         return new DummyScaler(screen, w, h);
102                 }
103         };
104
105         static const Factory factory;
106
107         const char * getName() const
108         {
109                 return "no scaling";
110         }
111
112         uint8* getDrawBuffer() const
113         {
114                 const int Bpp = screen->format->BitsPerPixel / 8;
115                 const int pitch = screen->pitch;
116                 return ((uint8*) screen->pixels)
117                         + (m_area.x * Bpp)
118                         + (m_area.y * pitch);
119         };
120
121         unsigned int getDrawBufferPitch() const
122         {
123                 return screen->pitch;
124         };
125
126         void getRenderedGUIArea(unsigned short & x, unsigned short & y,
127                                                         unsigned short & w, unsigned short & h) const
128         {
129                 x = m_area.x; y = m_area.y; w = m_area.w; h = m_area.h;
130         };
131
132         int getRatio() const
133         {
134                 return 1;
135         };
136
137         void prepare() { };
138
139         void finish()
140         {
141                 SDL_UpdateRects(m_screen, 1, &m_area);
142         };
143 };
144 const DummyScaler::Factory DummyScaler::factory;
145
146 class SWScaler : public Scaler
147 {
148         SDL_Surface * m_screen;
149         SDL_Rect m_area;
150         uint8 * m_surface;
151         const int m_w, m_h, m_Bpp;
152
153         SWScaler(SDL_Surface* screen, int w, int h)
154         : m_screen(screen), m_w(w), m_h(h),
155          m_Bpp(m_screen->format->BitsPerPixel / 8)
156         {
157                 centerRectangle(m_area, GUI.Width, GUI.Height, w * 2, h * 2);
158                 m_surface = reinterpret_cast<uint8*>(malloc(w * h * m_Bpp));
159         }
160 public:
161         ~SWScaler()
162         {
163                 free(m_surface);
164         };
165
166         class Factory : public ScalerFactory
167         {
168                 const char * getName() const
169                 {
170                         return "2x";
171                 }
172
173                 bool canEnable(int w, int h) const
174                 {
175                         return w * 2 < GUI.Width && h * 2 < GUI.Height;
176                 }
177
178                 Scaler* instantiate(SDL_Surface* screen, int w, int h) const
179                 {
180                         return new SWScaler(screen, w, h);
181                 }
182         };
183
184         static const Factory factory;
185
186         const char * getName() const
187         {
188                 return "software 2x scaling";
189         }
190
191         uint8* getDrawBuffer() const
192         {
193                 return m_surface;
194         };
195
196         unsigned int getDrawBufferPitch() const
197         {
198                 return m_w * m_Bpp;
199         };
200
201         void getRenderedGUIArea(unsigned short & x, unsigned short & y,
202                                                         unsigned short & w, unsigned short & h) const
203         {
204                 x = m_area.x; y = m_area.y; w = m_area.w; h = m_area.h;
205         };
206
207         int getRatio() const
208         {
209                 return 2;
210         };
211
212         void prepare() { };
213
214         void finish()
215         {
216                 uint16 * src = reinterpret_cast<uint16*>(m_surface);
217                 uint16 * dst = reinterpret_cast<uint16*>(
218                         ((uint8*) m_screen->pixels)
219                         + (m_area.x * m_Bpp)
220                         + (m_area.y * m_screen->pitch));
221                 const int src_pitch = m_w;
222                 const int dst_pitch = m_screen->pitch / m_Bpp;
223                 int x, y;
224
225                 for (y = 0; y < m_h*2; y++) {
226                         for (x = 0; x < m_w*2; x+=2) {
227                                 dst[x] = src[x/2];
228                                 dst[x + 1] = src[x/2];
229                         }
230                         dst += dst_pitch;
231                         if (y&1) src += src_pitch;
232                 }
233
234                 SDL_UpdateRects(m_screen, 1, &m_area);
235         };
236 };
237 const SWScaler::Factory SWScaler::factory;
238
239 #if CONF_XSP
240 class XSPScaler : public Scaler
241 {
242         SDL_Surface* m_screen;
243         SDL_Rect m_area;
244         SDL_Rect m_real_area;
245
246         static void setDoubling(bool enable)
247         {
248                 SDL_SysWMinfo wminfo;
249                 SDL_VERSION(&wminfo.version);
250                 if ( SDL_GetWMInfo(&wminfo) ) {
251                         Display *dpy = wminfo.info.x11.display;
252                         XSPSetPixelDoubling(dpy, 0, enable ? 1 : 0);
253                         XFlush(dpy);
254                 }
255         }
256
257         XSPScaler(SDL_Surface* screen, int w, int h)
258         : m_screen(screen)
259         {
260                 centerRectangle(m_area, GUI.Width, GUI.Height,
261                         w * 2, h * 2);
262                 setDoubling(true);
263
264                 m_real_area.x = m_area.x;
265                 m_real_area.y = m_area.y;
266                 m_real_area.w = m_area.w / 2;
267                 m_real_area.h = m_area.h / 2;
268         };
269 public:
270         ~XSPScaler()
271         {
272                 setDoubling(false);
273         };
274
275         class Factory : public ScalerFactory
276         {
277                 const char * getName() const
278                 {
279                         return "xsp";
280                 }
281
282                 bool canEnable(int w, int h) const
283                 {
284                         return w * 2 < GUI.Width && h * 2 < GUI.Height;
285                 };
286
287                 Scaler* instantiate(SDL_Surface* screen, int w, int h) const
288                 {
289                         return new XSPScaler(screen, w, h);
290                 }
291         };
292
293         static const Factory factory;
294
295         const char * getName() const
296         {
297                 return "XSP pixel doubling";
298         }
299
300         uint8* getDrawBuffer() const
301         {
302                 const int Bpp = screen->format->BitsPerPixel / 8;
303                 const int pitch = screen->pitch;
304                 return ((uint8*) screen->pixels)
305                         + (m_area.x * Bpp)
306                         + (m_area.y * pitch);
307         };
308
309         unsigned int getDrawBufferPitch() const
310         {
311                 return screen->pitch;
312         };
313
314         void getRenderedGUIArea(unsigned short & x, unsigned short & y,
315                                                         unsigned short & w, unsigned short & h) const
316         {
317                 x = m_area.x; y = m_area.y; w = m_area.w; h = m_area.h;
318         };
319
320         int getRatio() const
321         {
322                 return 2;
323         };
324
325         void prepare() { };
326
327         void finish()
328         {
329                 SDL_UpdateRects(m_screen, 1, &m_real_area);
330         };
331 };
332 const XSPScaler::Factory XSPScaler::factory;
333 #endif
334
335 static const ScalerFactory* scalers[] = {
336 #if CONF_XSP
337         &XSPScaler::factory,
338 #endif
339         &SWScaler::factory,
340         &DummyScaler::factory
341 };
342
343 static const ScalerFactory* searchForScaler(int w, int h)
344 {
345         const int n = sizeof(scalers) / sizeof(ScalerFactory*);
346         int i;
347
348         if (Config.scaler && strcasecmp(Config.scaler, "help") == 0 ) {
349                 // List scalers
350                 printf("Scalers list:\n");
351                 for (i = 0; i < n; i++) {
352                         printf(" %s\n", scalers[i]->getName());
353                 }
354                 DIE("End of scalers list");
355         } else if (Config.scaler && strcasecmp(Config.scaler, "auto") != 0 ) {
356                 // We prefer a specific scaler
357                 for (i = 0; i < n; i++) {
358                         if (strcasecmp(scalers[i]->getName(), Config.scaler) == 0) {
359                                 if (!scalers[i]->canEnable(w, h)) {
360                                         DIE("Cannot use selected scaler");
361                                 }
362                                 return scalers[i];
363                         }
364                 }
365                 DIE("Select scaler does not exist");
366         } else {
367                 // Just try them all
368                 for (i = 0; i < n; i++) {
369                         if (scalers[i]->canEnable(w, h)) {
370                                 return scalers[i];
371                         }
372                 }
373                 DIE("Can't use any scaler");
374         }
375 }
376
377 static void calculateScreenSize()
378 {
379         SDL_SysWMinfo wminfo;
380         SDL_VERSION(&wminfo.version);
381
382         if ( SDL_GetWMInfo(&wminfo) ) {
383                 Display *dpy = wminfo.info.x11.display;
384                 Window w;
385                 SDL_Rect* size;
386                 XWindowAttributes xwa;
387
388                 if (Config.fullscreen) {
389                         w =  wminfo.info.x11.fswindow;
390                         size = &screenSize;
391                         gotScreenSize = true;
392                 } else {
393                         w =  wminfo.info.x11.wmwindow;
394                         size = &windowSize;
395                         gotWindowSize = true;
396                 }
397
398                 XGetWindowAttributes(dpy, w, &xwa);
399                 size->x = xwa.x;
400                 size->y = xwa.y;
401                 size->w = xwa.width;
402                 size->h = xwa.height;
403         }
404 }
405
406 void S9xSetTitle(const char *title)
407 {
408         SDL_SysWMinfo info;
409         SDL_VERSION(&info.version);
410         if ( SDL_GetWMInfo(&info) ) {
411                 Display *dpy = info.info.x11.display;
412                 Window win;
413                 if (dpy) {
414                         win = info.info.x11.fswindow;
415                         if (win) XStoreName(dpy, win, title);
416                         win = info.info.x11.wmwindow;
417                         if (win) XStoreName(dpy, win, title);
418                 }
419         }
420 }
421
422 static void freeVideoSurface()
423 {
424         screen = 0; // There's no need to free the screen surface.
425         GFX.Screen = 0;
426
427         free(GFX.SubScreen); GFX.SubScreen = 0;
428         free(GFX.ZBuffer); GFX.ZBuffer = 0;
429         free(GFX.SubZBuffer); GFX.SubZBuffer = 0;
430
431         free(scaler); scaler = 0;
432 }
433
434 static void setupVideoSurface()
435 {
436         // Real surface area.
437         const unsigned gameWidth = IMAGE_WIDTH;
438         const unsigned gameHeight = IMAGE_HEIGHT;
439
440         if (scaler) {
441                 delete scaler;
442                 scaler = 0;
443         }
444
445 #ifdef MAEMO
446         if ((Config.fullscreen && !gotScreenSize) ||
447                 (!Config.fullscreen && !gotWindowSize)) {
448                 // Do a first try, in order to get window/screen size
449                 screen = SDL_SetVideoMode(gameWidth, gameHeight, 16,
450                         SDL_SWSURFACE | SDL_RESIZABLE |
451                         (Config.fullscreen ? SDL_FULLSCREEN : 0));
452                 if (!screen) DIE("SDL_SetVideoMode: %s", SDL_GetError());
453                 calculateScreenSize();
454         }
455         if (Config.fullscreen) {
456                 GUI.Width = screenSize.w;
457                 GUI.Height = screenSize.h;
458         } else {
459                 GUI.Width = windowSize.w;
460                 GUI.Height = windowSize.h;
461         }
462 #else
463         GUI.Width = gameWidth;
464         GUI.Height = gameHeight;
465 #endif
466
467         // Safeguard
468         if (gameHeight > GUI.Height || gameWidth > GUI.Width)
469                 DIE("Video is larger than window size!");
470
471         const ScalerFactory* sFactory = searchForScaler(gameWidth, gameHeight);
472
473         screen = SDL_SetVideoMode(GUI.Width, GUI.Height,
474                                                                 Settings.SixteenBit ? 16 : 8,
475                                                                 SDL_SWSURFACE |
476                                                                 (Config.fullscreen ? SDL_FULLSCREEN : 0));
477         if (!screen)
478                 DIE("SDL_SetVideoMode: %s", SDL_GetError());
479         
480         SDL_ShowCursor(SDL_DISABLE);
481
482         scaler = sFactory->instantiate(screen, gameWidth, gameHeight);
483
484         // We get pitch surface values from SDL
485         GFX.RealPitch = GFX.Pitch = scaler->getDrawBufferPitch();
486         GFX.ZPitch = GFX.Pitch / 2; // gfx & tile.cpp depend on this, unfortunately.
487         GFX.PixSize = screen->format->BitsPerPixel / 8;
488         
489         GFX.Screen = scaler->getDrawBuffer();
490         GFX.SubScreen = (uint8 *) malloc(GFX.Pitch * IMAGE_HEIGHT);
491         GFX.ZBuffer =  (uint8 *) malloc(GFX.ZPitch * IMAGE_HEIGHT);
492         GFX.SubZBuffer = (uint8 *) malloc(GFX.ZPitch * IMAGE_HEIGHT);
493
494         GFX.Delta = (GFX.SubScreen - GFX.Screen) >> 1;
495         GFX.PPL = GFX.Pitch >> 1;
496         GFX.PPLx2 = GFX.Pitch;
497
498         scaler->getRenderedGUIArea(GUI.RenderX, GUI.RenderY, GUI.RenderW, GUI.RenderH);
499         GUI.Scale = scaler->getRatio();
500
501         printf("Video: %dx%d (%dx%d output), %hu bits per pixel, %s, %s\n",
502                 gameWidth, gameHeight,
503                 screen->w, screen->h, screen->format->BitsPerPixel,
504                 Config.fullscreen ? "fullscreen" : "windowed",
505                 scaler->getName());
506 }
507
508 static void drawOnscreenControls()
509 {
510         if (Config.touchscreenInput) {
511                 S9xInputScreenChanged();
512                 if (Config.touchscreenShow) {
513                         S9xInputScreenDraw(Settings.SixteenBit ? 2 : 1,
514                                                                 screen->pixels, screen->pitch);
515                         SDL_Flip(screen);
516                 }
517         }
518 }
519
520 void S9xInitDisplay(int argc, const char ** argv)
521 {       
522         if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) 
523                 DIE("SDL_InitSubSystem(VIDEO): %s", SDL_GetError());
524
525         setupVideoSurface();
526         drawOnscreenControls();
527 }
528
529 void S9xDeinitDisplay()
530 {
531         freeVideoSurface();     
532         SDL_QuitSubSystem(SDL_INIT_VIDEO);
533 }
534
535 void S9xVideoToggleFullscreen()
536 {
537         Config.fullscreen = !Config.fullscreen;
538         freeVideoSurface();
539         setupVideoSurface();
540         drawOnscreenControls();
541 }
542
543 void S9xVideoOutputFocus(bool hasFocus)
544 {
545 #if 0 // TODO
546         if (Config.xsp) {
547                 setDoubling(hasFocus);
548         } 
549 #endif
550 }
551
552 // This is here for completeness, but palette mode is useless on N8x0
553 void S9xSetPalette ()
554 {
555         if (Settings.SixteenBit) return;
556         
557         SDL_Color colors[256];
558         int brightness = IPPU.MaxBrightness *138;
559         for (int i = 0; i < 256; i++)
560         {
561                 colors[i].r = ((PPU.CGDATA[i] >> 0) & 0x1F) * brightness;
562                 colors[i].g = ((PPU.CGDATA[i] >> 5) & 0x1F) * brightness;
563                 colors[i].b = ((PPU.CGDATA[i] >> 10) & 0x1F) * brightness;
564         }
565         
566         SDL_SetColors(screen, colors, 0, 256);
567 }
568
569 bool8_32 S9xInitUpdate ()
570 {
571         scaler->prepare();
572
573         return TRUE;
574 }
575
576 bool8_32 S9xDeinitUpdate (int width, int height, bool8_32 sixteenBit)
577 {
578         scaler->finish();
579
580         return TRUE;
581 }
582