9 # include <X11/extensions/Xsp.h>
18 #define DIE(format, ...) do { \
19 fprintf(stderr, "Died at %s:%d: ", __FILE__, __LINE__ ); \
20 fprintf(stderr, format "\n", ## __VA_ARGS__); \
26 static SDL_Surface* screen;
28 static SDL_Rect windowSize, screenSize;
29 static bool gotWindowSize, gotScreenSize;
32 /** The current scaler object */
33 static Scaler* scaler;
35 static void centerRectangle(SDL_Rect& result, int areaW, int areaH, int w, int h)
37 result.x = areaW / 2 - w / 2;
39 result.y = areaH / 2 - h / 2;
47 virtual ~Scaler() { };
49 virtual const char * getName() const = 0;
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)
56 virtual int getRatio() const = 0;
57 virtual void prepare() = 0;
58 virtual void finish() = 0;
65 virtual ~ScalerFactory() { };
66 virtual const char * getName() const = 0;
67 virtual bool canEnable(int bpp, int w, int h) const = 0;
68 virtual Scaler* instantiate(SDL_Surface* screen, int w, int h) const = 0;
71 class DummyScaler : public Scaler
73 SDL_Surface * m_screen;
76 DummyScaler(SDL_Surface* screen, int w, int h)
79 centerRectangle(m_area, GUI.Width, GUI.Height, w, h);
87 class Factory : public ScalerFactory
89 const char * getName() const
94 bool canEnable(int bpp, int w, int h) const
99 Scaler* instantiate(SDL_Surface* screen, int w, int h) const
101 return new DummyScaler(screen, w, h);
105 static const Factory factory;
107 const char * getName() const
112 uint8* getDrawBuffer() const
114 const int Bpp = screen->format->BitsPerPixel / 8;
115 const int pitch = screen->pitch;
116 return ((uint8*) screen->pixels)
118 + (m_area.y * pitch);
121 unsigned int getDrawBufferPitch() const
123 return screen->pitch;
126 void getRenderedGUIArea(unsigned short & x, unsigned short & y,
127 unsigned short & w, unsigned short & h) const
129 x = m_area.x; y = m_area.y; w = m_area.w; h = m_area.h;
141 SDL_UpdateRects(m_screen, 1, &m_area);
144 const DummyScaler::Factory DummyScaler::factory;
147 class ARMScaler : public Scaler
149 SDL_Surface * m_screen;
152 const int m_w, m_h, m_Bpp;
154 ARMScaler(SDL_Surface* screen, int w, int h)
155 : m_screen(screen), m_w(w), m_h(h),
156 m_Bpp(m_screen->format->BitsPerPixel / 8)
158 centerRectangle(m_area, GUI.Width, GUI.Height, w * 2, h * 2);
159 m_surface = reinterpret_cast<uint8*>(malloc(w * h * m_Bpp));
167 class Factory : public ScalerFactory
169 const char * getName() const
174 bool canEnable(int bpp, int w, int h) const
176 return bpp == 16 && w * 2 < GUI.Width && h * 2 < GUI.Height;
179 Scaler* instantiate(SDL_Surface* screen, int w, int h) const
181 return new ARMScaler(screen, w, h);
185 static const Factory factory;
187 const char * getName() const
189 return "software ARM 2x scaling";
192 uint8* getDrawBuffer() const
197 unsigned int getDrawBufferPitch() const
202 void getRenderedGUIArea(unsigned short & x, unsigned short & y,
203 unsigned short & w, unsigned short & h) const
205 x = m_area.x; y = m_area.y; w = m_area.w; h = m_area.h;
217 uint16 * src = reinterpret_cast<uint16*>(m_surface);
218 uint16 * dst = reinterpret_cast<uint16*>(
219 ((uint8*) m_screen->pixels)
221 + (m_area.y * m_screen->pitch));
222 const int src_pitch = m_w;
223 const int dst_pitch = m_screen->pitch / m_Bpp;
226 for (y = 0; y < m_h*2; y++) {
229 "mov r0, %0; mov r1, %1; mov r2, %2;"
230 "stmdb r13!,{r4,r5,r6,r7,r8,r9,r10,r11,r12,r14};"
231 "1: ldmia r1!,{r3,r4,r5,r6,r7,r8,r9,r10};"
232 "mov r14,r5,lsr #16;"
233 "mov r12,r5,lsl #16;"
234 "orr r14,r14,r14,lsl #16;"
235 "orr r12,r12,r12,lsr #16;"
236 "mov r11,r4,lsr #16;"
238 "orr r11,r11,r11,lsl #16;"
239 "orr r5,r5,r5,lsr #16;"
242 "orr r4,r4,r4,lsl #16;"
243 "orr r3,r3,r3,lsr #16;"
244 "stmia r0!,{r3,r4,r5,r11,r12,r14};"
247 "orr r3,r3,r3,lsr #16;"
248 "orr r4,r4,r4,lsl #16;"
251 "orr r5,r5,r5,lsr #16;"
252 "orr r6,r6,r6,lsl #16;"
255 "orr r7,r7,r7,lsr #16;"
256 "orr r8,r8,r8,lsl #16;"
257 "mov r12,r10,lsr #16;"
258 "mov r11,r10,lsl #16;"
259 "orr r12,r12,r12,lsl #16;"
260 "orr r11,r11,r11,lsr #16;"
261 "mov r10,r9,lsr #16;"
263 "orr r10,r10,r10,lsl #16;"
264 "orr r9,r9,r9,lsr #16;"
265 "stmia r0!,{r3,r4,r5,r6,r7,r8,r9,r10,r11,r12};"
268 "ldmia r13!,{r4,r5,r6,r7,r8,r9,r10,r11,r12,r14};"
270 : "r" (dst), "r" (src), "r" (m_w)
271 : "r0", "r1", "r2", "r3"
274 if (y&1) src += src_pitch;
277 SDL_UpdateRects(m_screen, 1, &m_area);
280 const ARMScaler::Factory ARMScaler::factory;
283 class SWScaler : public Scaler
285 SDL_Surface * m_screen;
288 const int m_w, m_h, m_Bpp;
290 SWScaler(SDL_Surface* screen, int w, int h)
291 : m_screen(screen), m_w(w), m_h(h),
292 m_Bpp(m_screen->format->BitsPerPixel / 8)
294 centerRectangle(m_area, GUI.Width, GUI.Height, w * 2, h * 2);
295 m_surface = reinterpret_cast<uint8*>(malloc(w * h * m_Bpp));
303 class Factory : public ScalerFactory
305 const char * getName() const
310 bool canEnable(int bpp, int w, int h) const
312 return w * 2 < GUI.Width && h * 2 < GUI.Height;
315 Scaler* instantiate(SDL_Surface* screen, int w, int h) const
317 return new SWScaler(screen, w, h);
321 static const Factory factory;
323 const char * getName() const
325 return "software 2x scaling";
328 uint8* getDrawBuffer() const
333 unsigned int getDrawBufferPitch() const
338 void getRenderedGUIArea(unsigned short & x, unsigned short & y,
339 unsigned short & w, unsigned short & h) const
341 x = m_area.x; y = m_area.y; w = m_area.w; h = m_area.h;
353 uint16 * src = reinterpret_cast<uint16*>(m_surface);
354 uint16 * dst = reinterpret_cast<uint16*>(
355 ((uint8*) m_screen->pixels)
357 + (m_area.y * m_screen->pitch));
358 const int src_pitch = m_w;
359 const int dst_pitch = m_screen->pitch / m_Bpp;
362 for (y = 0; y < m_h*2; y++) {
363 for (x = 0; x < m_w*2; x+=2) {
365 dst[x + 1] = src[x/2];
368 if (y&1) src += src_pitch;
371 SDL_UpdateRects(m_screen, 1, &m_area);
374 const SWScaler::Factory SWScaler::factory;
377 class XSPScaler : public Scaler
379 SDL_Surface* m_screen;
381 SDL_Rect m_real_area;
383 static void setDoubling(bool enable)
385 SDL_SysWMinfo wminfo;
386 SDL_VERSION(&wminfo.version);
387 if ( SDL_GetWMInfo(&wminfo) ) {
388 Display *dpy = wminfo.info.x11.display;
389 XSPSetPixelDoubling(dpy, 0, enable ? 1 : 0);
394 XSPScaler(SDL_Surface* screen, int w, int h)
397 centerRectangle(m_area, GUI.Width, GUI.Height,
401 m_real_area.x = m_area.x;
402 m_real_area.y = m_area.y;
403 m_real_area.w = m_area.w / 2;
404 m_real_area.h = m_area.h / 2;
412 class Factory : public ScalerFactory
414 const char * getName() const
419 bool canEnable(int bpp, int w, int h) const
421 return w * 2 < GUI.Width && h * 2 < GUI.Height;
424 Scaler* instantiate(SDL_Surface* screen, int w, int h) const
426 return new XSPScaler(screen, w, h);
430 static const Factory factory;
432 const char * getName() const
434 return "XSP pixel doubling";
437 uint8* getDrawBuffer() const
439 const int Bpp = screen->format->BitsPerPixel / 8;
440 const int pitch = screen->pitch;
441 return ((uint8*) screen->pixels)
443 + (m_area.y * pitch);
446 unsigned int getDrawBufferPitch() const
448 return screen->pitch;
451 void getRenderedGUIArea(unsigned short & x, unsigned short & y,
452 unsigned short & w, unsigned short & h) const
454 x = m_area.x; y = m_area.y; w = m_area.w; h = m_area.h;
466 SDL_UpdateRects(m_screen, 1, &m_real_area);
469 const XSPScaler::Factory XSPScaler::factory;
472 static const ScalerFactory* scalers[] = {
480 &DummyScaler::factory
483 static const ScalerFactory* searchForScaler(int bpp, int w, int h)
485 const int n = sizeof(scalers) / sizeof(ScalerFactory*);
488 if (Config.scaler && strcasecmp(Config.scaler, "help") == 0 ) {
490 printf("Scalers list:\n");
491 for (i = 0; i < n; i++) {
492 printf(" %s\n", scalers[i]->getName());
494 DIE("End of scalers list");
495 } else if (Config.scaler && strcasecmp(Config.scaler, "auto") != 0 ) {
496 // We prefer a specific scaler
497 for (i = 0; i < n; i++) {
498 if (strcasecmp(scalers[i]->getName(), Config.scaler) == 0) {
499 if (!scalers[i]->canEnable(bpp, w, h)) {
500 DIE("Cannot use selected scaler");
505 DIE("Selected scaler '%s' does not exist", Config.scaler);
508 for (i = 0; i < n; i++) {
509 if (scalers[i]->canEnable(bpp, w, h)) {
513 DIE("Can't use any scaler");
517 static void calculateScreenSize()
519 SDL_SysWMinfo wminfo;
520 SDL_VERSION(&wminfo.version);
522 if ( SDL_GetWMInfo(&wminfo) ) {
523 Display *dpy = wminfo.info.x11.display;
526 XWindowAttributes xwa;
528 if (Config.fullscreen) {
529 w = wminfo.info.x11.fswindow;
531 gotScreenSize = true;
533 w = wminfo.info.x11.wmwindow;
535 gotWindowSize = true;
538 XGetWindowAttributes(dpy, w, &xwa);
542 size->h = xwa.height;
546 void S9xSetTitle(const char *title)
549 SDL_VERSION(&info.version);
550 if ( SDL_GetWMInfo(&info) ) {
551 Display *dpy = info.info.x11.display;
554 win = info.info.x11.fswindow;
555 if (win) XStoreName(dpy, win, title);
556 win = info.info.x11.wmwindow;
557 if (win) XStoreName(dpy, win, title);
562 static void freeVideoSurface()
564 screen = 0; // There's no need to free the screen surface.
567 free(GFX.SubScreen); GFX.SubScreen = 0;
568 free(GFX.ZBuffer); GFX.ZBuffer = 0;
569 free(GFX.SubZBuffer); GFX.SubZBuffer = 0;
571 free(scaler); scaler = 0;
574 static void setupVideoSurface()
576 // Real surface area.
577 const unsigned gameWidth = IMAGE_WIDTH;
578 const unsigned gameHeight = IMAGE_HEIGHT;
586 if ((Config.fullscreen && !gotScreenSize) ||
587 (!Config.fullscreen && !gotWindowSize)) {
588 // Do a first try, in order to get window/screen size
589 screen = SDL_SetVideoMode(gameWidth, gameHeight, 16,
590 SDL_SWSURFACE | SDL_RESIZABLE |
591 (Config.fullscreen ? SDL_FULLSCREEN : 0));
592 if (!screen) DIE("SDL_SetVideoMode: %s", SDL_GetError());
593 calculateScreenSize();
595 if (Config.fullscreen) {
596 GUI.Width = screenSize.w;
597 GUI.Height = screenSize.h;
599 GUI.Width = windowSize.w;
600 GUI.Height = windowSize.h;
603 GUI.Width = gameWidth;
604 GUI.Height = gameHeight;
608 if (gameHeight > GUI.Height || gameWidth > GUI.Width)
609 DIE("Video is larger than window size!");
611 const ScalerFactory* sFactory =
612 searchForScaler(Settings.SixteenBit ? 16 : 8, gameWidth, gameHeight);
614 screen = SDL_SetVideoMode(GUI.Width, GUI.Height,
615 Settings.SixteenBit ? 16 : 8,
617 (Config.fullscreen ? SDL_FULLSCREEN : 0));
619 DIE("SDL_SetVideoMode: %s", SDL_GetError());
621 SDL_ShowCursor(SDL_DISABLE);
623 scaler = sFactory->instantiate(screen, gameWidth, gameHeight);
625 // We get pitch surface values from SDL
626 GFX.RealPitch = GFX.Pitch = scaler->getDrawBufferPitch();
627 GFX.ZPitch = GFX.Pitch / 2; // gfx & tile.cpp depend on this, unfortunately.
628 GFX.PixSize = screen->format->BitsPerPixel / 8;
630 GFX.Screen = scaler->getDrawBuffer();
631 GFX.SubScreen = (uint8 *) malloc(GFX.Pitch * IMAGE_HEIGHT);
632 GFX.ZBuffer = (uint8 *) malloc(GFX.ZPitch * IMAGE_HEIGHT);
633 GFX.SubZBuffer = (uint8 *) malloc(GFX.ZPitch * IMAGE_HEIGHT);
635 GFX.Delta = (GFX.SubScreen - GFX.Screen) >> 1;
636 GFX.PPL = GFX.Pitch >> 1;
637 GFX.PPLx2 = GFX.Pitch;
639 scaler->getRenderedGUIArea(GUI.RenderX, GUI.RenderY, GUI.RenderW, GUI.RenderH);
640 GUI.Scale = scaler->getRatio();
642 printf("Video: %dx%d (%dx%d output), %hu bits per pixel, %s, %s\n",
643 gameWidth, gameHeight,
644 screen->w, screen->h, screen->format->BitsPerPixel,
645 Config.fullscreen ? "fullscreen" : "windowed",
649 static void drawOnscreenControls()
651 if (Config.touchscreenInput) {
652 S9xInputScreenChanged();
653 if (Config.touchscreenShow) {
654 S9xInputScreenDraw(Settings.SixteenBit ? 2 : 1,
655 screen->pixels, screen->pitch);
661 void S9xInitDisplay(int argc, const char ** argv)
663 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
664 DIE("SDL_InitSubSystem(VIDEO): %s", SDL_GetError());
667 drawOnscreenControls();
670 void S9xDeinitDisplay()
673 SDL_QuitSubSystem(SDL_INIT_VIDEO);
676 void S9xVideoToggleFullscreen()
678 Config.fullscreen = !Config.fullscreen;
681 drawOnscreenControls();
684 void S9xVideoOutputFocus(bool hasFocus)
688 setDoubling(hasFocus);
693 // This is here for completeness, but palette mode is useless on N8x0
694 void S9xSetPalette ()
696 if (Settings.SixteenBit) return;
698 SDL_Color colors[256];
699 int brightness = IPPU.MaxBrightness *138;
700 for (int i = 0; i < 256; i++)
702 colors[i].r = ((PPU.CGDATA[i] >> 0) & 0x1F) * brightness;
703 colors[i].g = ((PPU.CGDATA[i] >> 5) & 0x1F) * brightness;
704 colors[i].b = ((PPU.CGDATA[i] >> 10) & 0x1F) * brightness;
707 SDL_SetColors(screen, colors, 0, 256);
710 bool8_32 S9xInitUpdate ()
717 bool8_32 S9xDeinitUpdate (int width, int height, bool8_32 sixteenBit)