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 &&
177 w % 16 == 0 /* asm assumes w div by 16 */;
180 Scaler* instantiate(SDL_Surface* screen, int w, int h) const
182 return new ARMScaler(screen, w, h);
186 static const Factory factory;
188 const char * getName() const
190 return "software ARM 2x scaling";
193 uint8* getDrawBuffer() const
198 unsigned int getDrawBufferPitch() const
203 void getRenderedGUIArea(unsigned short & x, unsigned short & y,
204 unsigned short & w, unsigned short & h) const
206 x = m_area.x; y = m_area.y; w = m_area.w; h = m_area.h;
218 uint16 * src = reinterpret_cast<uint16*>(m_surface);
219 uint16 * dst = reinterpret_cast<uint16*>(
220 ((uint8*) m_screen->pixels)
222 + (m_area.y * m_screen->pitch));
223 const int src_pitch = m_w;
224 const int dst_pitch = m_screen->pitch / m_Bpp;
227 for (y = 0; y < m_h*2; y++) {
230 "mov r0, %0; mov r1, %1; mov r2, %2;"
231 "stmdb r13!,{r4,r5,r6,r7,r8,r9,r10,r11,r12,r14};"
232 "1: ldmia r1!,{r3,r4,r5,r6,r7,r8,r9,r10};"
233 "mov r14,r5,lsr #16;"
234 "mov r12,r5,lsl #16;"
235 "orr r14,r14,r14,lsl #16;"
236 "orr r12,r12,r12,lsr #16;"
237 "mov r11,r4,lsr #16;"
239 "orr r11,r11,r11,lsl #16;"
240 "orr r5,r5,r5,lsr #16;"
243 "orr r4,r4,r4,lsl #16;"
244 "orr r3,r3,r3,lsr #16;"
245 "stmia r0!,{r3,r4,r5,r11,r12,r14};"
248 "orr r3,r3,r3,lsr #16;"
249 "orr r4,r4,r4,lsl #16;"
252 "orr r5,r5,r5,lsr #16;"
253 "orr r6,r6,r6,lsl #16;"
256 "orr r7,r7,r7,lsr #16;"
257 "orr r8,r8,r8,lsl #16;"
258 "mov r12,r10,lsr #16;"
259 "mov r11,r10,lsl #16;"
260 "orr r12,r12,r12,lsl #16;"
261 "orr r11,r11,r11,lsr #16;"
262 "mov r10,r9,lsr #16;"
264 "orr r10,r10,r10,lsl #16;"
265 "orr r9,r9,r9,lsr #16;"
266 "stmia r0!,{r3,r4,r5,r6,r7,r8,r9,r10,r11,r12};"
269 "ldmia r13!,{r4,r5,r6,r7,r8,r9,r10,r11,r12,r14};"
271 : "r" (dst), "r" (src), "r" (m_w)
272 : "r0", "r1", "r2", "r3"
275 if (y&1) src += src_pitch;
278 SDL_UpdateRects(m_screen, 1, &m_area);
281 const ARMScaler::Factory ARMScaler::factory;
284 class SWScaler : public Scaler
286 SDL_Surface * m_screen;
289 const int m_w, m_h, m_Bpp;
291 SWScaler(SDL_Surface* screen, int w, int h)
292 : m_screen(screen), m_w(w), m_h(h),
293 m_Bpp(m_screen->format->BitsPerPixel / 8)
295 centerRectangle(m_area, GUI.Width, GUI.Height, w * 2, h * 2);
296 m_surface = reinterpret_cast<uint8*>(malloc(w * h * m_Bpp));
304 class Factory : public ScalerFactory
306 const char * getName() const
311 bool canEnable(int bpp, int w, int h) const
313 return w * 2 < GUI.Width && h * 2 < GUI.Height;
316 Scaler* instantiate(SDL_Surface* screen, int w, int h) const
318 return new SWScaler(screen, w, h);
322 static const Factory factory;
324 const char * getName() const
326 return "software 2x scaling";
329 uint8* getDrawBuffer() const
334 unsigned int getDrawBufferPitch() const
339 void getRenderedGUIArea(unsigned short & x, unsigned short & y,
340 unsigned short & w, unsigned short & h) const
342 x = m_area.x; y = m_area.y; w = m_area.w; h = m_area.h;
354 uint16 * src = reinterpret_cast<uint16*>(m_surface);
355 uint16 * dst = reinterpret_cast<uint16*>(
356 ((uint8*) m_screen->pixels)
358 + (m_area.y * m_screen->pitch));
359 const int src_pitch = m_w;
360 const int dst_pitch = m_screen->pitch / m_Bpp;
363 for (y = 0; y < m_h*2; y++) {
364 for (x = 0; x < m_w*2; x+=2) {
366 dst[x + 1] = src[x/2];
369 if (y&1) src += src_pitch;
372 SDL_UpdateRects(m_screen, 1, &m_area);
375 const SWScaler::Factory SWScaler::factory;
378 class XSPScaler : public Scaler
380 SDL_Surface* m_screen;
382 SDL_Rect m_real_area;
384 static void setDoubling(bool enable)
386 SDL_SysWMinfo wminfo;
387 SDL_VERSION(&wminfo.version);
388 if ( SDL_GetWMInfo(&wminfo) ) {
389 Display *dpy = wminfo.info.x11.display;
390 XSPSetPixelDoubling(dpy, 0, enable ? 1 : 0);
395 XSPScaler(SDL_Surface* screen, int w, int h)
398 centerRectangle(m_area, GUI.Width, GUI.Height,
402 m_real_area.x = m_area.x;
403 m_real_area.y = m_area.y;
404 m_real_area.w = m_area.w / 2;
405 m_real_area.h = m_area.h / 2;
413 class Factory : public ScalerFactory
415 const char * getName() const
420 bool canEnable(int bpp, int w, int h) const
422 return w * 2 < GUI.Width && h * 2 < GUI.Height;
425 Scaler* instantiate(SDL_Surface* screen, int w, int h) const
427 return new XSPScaler(screen, w, h);
431 static const Factory factory;
433 const char * getName() const
435 return "XSP pixel doubling";
438 uint8* getDrawBuffer() const
440 const int Bpp = screen->format->BitsPerPixel / 8;
441 const int pitch = screen->pitch;
442 return ((uint8*) screen->pixels)
444 + (m_area.y * pitch);
447 unsigned int getDrawBufferPitch() const
449 return screen->pitch;
452 void getRenderedGUIArea(unsigned short & x, unsigned short & y,
453 unsigned short & w, unsigned short & h) const
455 x = m_area.x; y = m_area.y; w = m_area.w; h = m_area.h;
467 SDL_UpdateRects(m_screen, 1, &m_real_area);
470 const XSPScaler::Factory XSPScaler::factory;
473 static const ScalerFactory* scalers[] = {
481 &DummyScaler::factory
484 static const ScalerFactory* searchForScaler(int bpp, int w, int h)
486 const int n = sizeof(scalers) / sizeof(ScalerFactory*);
489 if (Config.scaler && strcasecmp(Config.scaler, "help") == 0 ) {
491 printf("Scalers list:\n");
492 for (i = 0; i < n; i++) {
493 printf(" %s\n", scalers[i]->getName());
495 DIE("End of scalers list");
496 } else if (Config.scaler && strcasecmp(Config.scaler, "auto") != 0 ) {
497 // We prefer a specific scaler
498 for (i = 0; i < n; i++) {
499 if (strcasecmp(scalers[i]->getName(), Config.scaler) == 0) {
500 if (!scalers[i]->canEnable(bpp, w, h)) {
501 DIE("Cannot use selected scaler");
506 DIE("Selected scaler '%s' does not exist", Config.scaler);
509 for (i = 0; i < n; i++) {
510 if (scalers[i]->canEnable(bpp, w, h)) {
514 DIE("Can't use any scaler");
518 static void calculateScreenSize()
520 SDL_SysWMinfo wminfo;
521 SDL_VERSION(&wminfo.version);
523 if ( SDL_GetWMInfo(&wminfo) ) {
524 Display *dpy = wminfo.info.x11.display;
527 XWindowAttributes xwa;
529 if (Config.fullscreen) {
530 w = wminfo.info.x11.fswindow;
532 gotScreenSize = true;
534 w = wminfo.info.x11.wmwindow;
536 gotWindowSize = true;
539 XGetWindowAttributes(dpy, w, &xwa);
543 size->h = xwa.height;
547 void S9xSetTitle(const char *title)
550 SDL_VERSION(&info.version);
551 if ( SDL_GetWMInfo(&info) ) {
552 Display *dpy = info.info.x11.display;
555 win = info.info.x11.fswindow;
556 if (win) XStoreName(dpy, win, title);
557 win = info.info.x11.wmwindow;
558 if (win) XStoreName(dpy, win, title);
563 static void freeVideoSurface()
565 screen = 0; // There's no need to free the screen surface.
568 free(GFX.SubScreen); GFX.SubScreen = 0;
569 free(GFX.ZBuffer); GFX.ZBuffer = 0;
570 free(GFX.SubZBuffer); GFX.SubZBuffer = 0;
572 free(scaler); scaler = 0;
575 static void setupVideoSurface()
577 // Real surface area.
578 const unsigned gameWidth = IMAGE_WIDTH;
579 const unsigned gameHeight = IMAGE_HEIGHT;
587 if ((Config.fullscreen && !gotScreenSize) ||
588 (!Config.fullscreen && !gotWindowSize)) {
589 // Do a first try, in order to get window/screen size
590 screen = SDL_SetVideoMode(gameWidth, gameHeight, 16,
591 SDL_SWSURFACE | SDL_RESIZABLE |
592 (Config.fullscreen ? SDL_FULLSCREEN : 0));
593 if (!screen) DIE("SDL_SetVideoMode: %s", SDL_GetError());
594 calculateScreenSize();
596 if (Config.fullscreen) {
597 GUI.Width = screenSize.w;
598 GUI.Height = screenSize.h;
600 GUI.Width = windowSize.w;
601 GUI.Height = windowSize.h;
604 GUI.Width = gameWidth;
605 GUI.Height = gameHeight;
609 if (gameHeight > GUI.Height || gameWidth > GUI.Width)
610 DIE("Video is larger than window size!");
612 const ScalerFactory* sFactory =
613 searchForScaler(Settings.SixteenBit ? 16 : 8, gameWidth, gameHeight);
615 screen = SDL_SetVideoMode(GUI.Width, GUI.Height,
616 Settings.SixteenBit ? 16 : 8,
618 (Config.fullscreen ? SDL_FULLSCREEN : 0));
620 DIE("SDL_SetVideoMode: %s", SDL_GetError());
622 SDL_ShowCursor(SDL_DISABLE);
624 scaler = sFactory->instantiate(screen, gameWidth, gameHeight);
626 // We get pitch surface values from SDL
627 GFX.RealPitch = GFX.Pitch = scaler->getDrawBufferPitch();
628 GFX.ZPitch = GFX.Pitch / 2; // gfx & tile.cpp depend on this, unfortunately.
629 GFX.PixSize = screen->format->BitsPerPixel / 8;
631 GFX.Screen = scaler->getDrawBuffer();
632 GFX.SubScreen = (uint8 *) malloc(GFX.Pitch * IMAGE_HEIGHT);
633 GFX.ZBuffer = (uint8 *) malloc(GFX.ZPitch * IMAGE_HEIGHT);
634 GFX.SubZBuffer = (uint8 *) malloc(GFX.ZPitch * IMAGE_HEIGHT);
636 GFX.Delta = (GFX.SubScreen - GFX.Screen) >> 1;
637 GFX.PPL = GFX.Pitch >> 1;
638 GFX.PPLx2 = GFX.Pitch;
640 scaler->getRenderedGUIArea(GUI.RenderX, GUI.RenderY, GUI.RenderW, GUI.RenderH);
641 GUI.Scale = scaler->getRatio();
643 printf("Video: %dx%d (%dx%d output), %hu bits per pixel, %s, %s\n",
644 gameWidth, gameHeight,
645 screen->w, screen->h, screen->format->BitsPerPixel,
646 Config.fullscreen ? "fullscreen" : "windowed",
650 static void drawOnscreenControls()
652 if (Config.touchscreenInput) {
653 S9xInputScreenChanged();
654 if (Config.touchscreenShow) {
655 S9xInputScreenDraw(Settings.SixteenBit ? 2 : 1,
656 screen->pixels, screen->pitch);
662 void S9xInitDisplay(int argc, const char ** argv)
664 if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
665 DIE("SDL_InitSubSystem(VIDEO): %s", SDL_GetError());
668 drawOnscreenControls();
671 void S9xDeinitDisplay()
674 SDL_QuitSubSystem(SDL_INIT_VIDEO);
677 void S9xVideoToggleFullscreen()
679 Config.fullscreen = !Config.fullscreen;
682 drawOnscreenControls();
685 void S9xVideoOutputFocus(bool hasFocus)
689 setDoubling(hasFocus);
694 // This is here for completeness, but palette mode is useless on N8x0
695 void S9xSetPalette ()
697 if (Settings.SixteenBit) return;
699 SDL_Color colors[256];
700 int brightness = IPPU.MaxBrightness *138;
701 for (int i = 0; i < 256; i++)
703 colors[i].r = ((PPU.CGDATA[i] >> 0) & 0x1F) * brightness;
704 colors[i].g = ((PPU.CGDATA[i] >> 5) & 0x1F) * brightness;
705 colors[i].b = ((PPU.CGDATA[i] >> 10) & 0x1F) * brightness;
708 SDL_SetColors(screen, colors, 0, 256);
711 bool8_32 S9xInitUpdate ()
718 bool8_32 S9xDeinitUpdate (int width, int height, bool8_32 sixteenBit)