09b17783b00036e1f49282a61a88b9c3fef07bbe
[pcsx_rearmed.git] / frontend / plat_sdl.c
1 /*
2  * (C) GraÅžvydas "notaz" Ignotas, 2011-2013
3  *
4  * This work is licensed under the terms of any of these licenses
5  * (at your option):
6  *  - GNU GPL, version 2 or later.
7  *  - GNU LGPL, version 2.1 or later.
8  * See the COPYING file in the top-level directory.
9  */
10
11 #include <stdio.h>
12 #include <assert.h>
13 #include <SDL.h>
14
15 #include "../libpcsxcore/plugins.h"
16 #include "libpicofe/input.h"
17 #include "libpicofe/in_sdl.h"
18 #include "libpicofe/menu.h"
19 #include "libpicofe/fonts.h"
20 #include "libpicofe/plat_sdl.h"
21 #include "libpicofe/plat.h"
22 #include "libpicofe/gl.h"
23 #include "cspace.h"
24 #include "plugin_lib.h"
25 #include "plugin.h"
26 #include "menu.h"
27 #include "main.h"
28 #include "plat.h"
29 #include "revision.h"
30
31 #include "libpicofe/plat_sdl.c"
32
33 static const struct in_default_bind in_sdl_defbinds[] = {
34   { SDLK_UP,     IN_BINDTYPE_PLAYER12, DKEY_UP },
35   { SDLK_DOWN,   IN_BINDTYPE_PLAYER12, DKEY_DOWN },
36   { SDLK_LEFT,   IN_BINDTYPE_PLAYER12, DKEY_LEFT },
37   { SDLK_RIGHT,  IN_BINDTYPE_PLAYER12, DKEY_RIGHT },
38   { SDLK_d,      IN_BINDTYPE_PLAYER12, DKEY_TRIANGLE },
39   { SDLK_z,      IN_BINDTYPE_PLAYER12, DKEY_CROSS },
40   { SDLK_x,      IN_BINDTYPE_PLAYER12, DKEY_CIRCLE },
41   { SDLK_s,      IN_BINDTYPE_PLAYER12, DKEY_SQUARE },
42   { SDLK_v,      IN_BINDTYPE_PLAYER12, DKEY_START },
43   { SDLK_c,      IN_BINDTYPE_PLAYER12, DKEY_SELECT },
44   { SDLK_w,      IN_BINDTYPE_PLAYER12, DKEY_L1 },
45   { SDLK_r,      IN_BINDTYPE_PLAYER12, DKEY_R1 },
46   { SDLK_e,      IN_BINDTYPE_PLAYER12, DKEY_L2 },
47   { SDLK_t,      IN_BINDTYPE_PLAYER12, DKEY_R2 },
48   { SDLK_ESCAPE, IN_BINDTYPE_EMU, SACTION_ENTER_MENU },
49   { SDLK_F1,     IN_BINDTYPE_EMU, SACTION_SAVE_STATE },
50   { SDLK_F2,     IN_BINDTYPE_EMU, SACTION_LOAD_STATE },
51   { SDLK_F3,     IN_BINDTYPE_EMU, SACTION_PREV_SSLOT },
52   { SDLK_F4,     IN_BINDTYPE_EMU, SACTION_NEXT_SSLOT },
53   { SDLK_F5,     IN_BINDTYPE_EMU, SACTION_TOGGLE_FSKIP },
54   { SDLK_F6,     IN_BINDTYPE_EMU, SACTION_SCREENSHOT },
55   { SDLK_F7,     IN_BINDTYPE_EMU, SACTION_TOGGLE_FPS },
56   { SDLK_F8,     IN_BINDTYPE_EMU, SACTION_SWITCH_DISPMODE },
57   { SDLK_F11,    IN_BINDTYPE_EMU, SACTION_TOGGLE_FULLSCREEN },
58   { SDLK_BACKSPACE, IN_BINDTYPE_EMU, SACTION_FAST_FORWARD },
59   { 0, 0, 0 }
60 };
61
62 const struct menu_keymap in_sdl_key_map[] =
63 {
64   { SDLK_UP,     PBTN_UP },
65   { SDLK_DOWN,   PBTN_DOWN },
66   { SDLK_LEFT,   PBTN_LEFT },
67   { SDLK_RIGHT,  PBTN_RIGHT },
68   { SDLK_RETURN, PBTN_MOK },
69   { SDLK_ESCAPE, PBTN_MBACK },
70   { SDLK_SEMICOLON,    PBTN_MA2 },
71   { SDLK_QUOTE,        PBTN_MA3 },
72   { SDLK_LEFTBRACKET,  PBTN_L },
73   { SDLK_RIGHTBRACKET, PBTN_R },
74 };
75
76 const struct menu_keymap in_sdl_joy_map[] =
77 {
78   { SDLK_UP,    PBTN_UP },
79   { SDLK_DOWN,  PBTN_DOWN },
80   { SDLK_LEFT,  PBTN_LEFT },
81   { SDLK_RIGHT, PBTN_RIGHT },
82   /* joystick */
83   { SDLK_WORLD_0, PBTN_MOK },
84   { SDLK_WORLD_1, PBTN_MBACK },
85   { SDLK_WORLD_2, PBTN_MA2 },
86   { SDLK_WORLD_3, PBTN_MA3 },
87 };
88
89 static const struct in_pdata in_sdl_platform_data = {
90   .defbinds  = in_sdl_defbinds,
91   .key_map   = in_sdl_key_map,
92   .kmap_size = sizeof(in_sdl_key_map) / sizeof(in_sdl_key_map[0]),
93   .joy_map   = in_sdl_joy_map,
94   .jmap_size = sizeof(in_sdl_joy_map) / sizeof(in_sdl_joy_map[0]),
95 };
96
97 static int psx_w = 256, psx_h = 240;
98 static void *shadow_fb, *menubg_img;
99 static int vout_fullscreen_old;
100 static int forced_clears;
101 static int forced_flips;
102 static int sdl12_compat;
103 static int resized;
104 static int in_menu;
105
106 static int gl_w_prev, gl_h_prev, gl_quirks_prev;
107 static float gl_vertices[] = {
108         -1.0f,  1.0f,  0.0f, // 0    0  1
109          1.0f,  1.0f,  0.0f, // 1  ^
110         -1.0f, -1.0f,  0.0f, // 2  | 2  3
111          1.0f, -1.0f,  0.0f, // 3  +-->
112 };
113
114 static void handle_window_resize(void);
115 static void handle_scaler_resize(int w, int h);
116 static void centered_clear(void);
117
118 static int plugin_owns_display(void)
119 {
120   // if true, a plugin is drawing and flipping
121   return (pl_rearmed_cbs.gpu_caps & GPU_CAP_OWNS_DISPLAY);
122 }
123
124 static void plugin_update(void)
125 {
126   // used by some plugins...
127   pl_rearmed_cbs.screen_w = plat_sdl_screen->w;
128   pl_rearmed_cbs.screen_h = plat_sdl_screen->h;
129   pl_rearmed_cbs.gles_display = gl_es_display;
130   pl_rearmed_cbs.gles_surface = gl_es_surface;
131   plugin_call_rearmed_cbs();
132 }
133
134 static void sdl_event_handler(void *event_)
135 {
136   SDL_Event *event = event_;
137
138   switch (event->type) {
139   case SDL_VIDEORESIZE:
140     if (window_w != (event->resize.w & ~3) || window_h != (event->resize.h & ~1)) {
141       window_w = event->resize.w & ~3;
142       window_h = event->resize.h & ~1;
143       resized = 1;
144       if (!in_menu && plat_sdl_gl_active && plugin_owns_display()) {
145         // the plugin flips by itself so resize has to be handled here
146         handle_window_resize();
147         if (GPU_open != NULL) {
148           int ret = GPU_open(&gpuDisp, "PCSX", NULL);
149           if (ret)
150             fprintf(stderr, "GPU_open: %d\n", ret);
151         }
152       }
153     }
154     return;
155   case SDL_ACTIVEEVENT:
156     // no need to redraw?
157     return;
158   default:
159     break;
160   }
161   plat_sdl_event_handler(event_);
162 }
163
164 static void quit_cb(void)
165 {
166   emu_core_ask_exit();
167 }
168
169 static void get_layer_pos(int *x, int *y, int *w, int *h)
170 {
171   // always fill entire SDL window
172   *x = *y = 0;
173   *w = pl_rearmed_cbs.screen_w;
174   *h = pl_rearmed_cbs.screen_h;
175 }
176
177 void plat_init(void)
178 {
179   static const char *hwfilters[] = { "linear", "nearest", NULL };
180   const SDL_version *ver;
181   int shadow_size;
182   int ret;
183
184   plat_sdl_quit_cb = quit_cb;
185
186   old_fullscreen = -1; // hack
187
188   ret = plat_sdl_init();
189   if (ret != 0)
190     exit(1);
191
192   ver = SDL_Linked_Version();
193   sdl12_compat = ver->patch >= 50;
194   printf("SDL %u.%u.%u compat=%d\n", ver->major, ver->minor, ver->patch, sdl12_compat);
195
196   in_menu = 1;
197   SDL_WM_SetCaption("PCSX-ReARMed " REV, NULL);
198
199   shadow_size = g_menuscreen_w * g_menuscreen_h * 2;
200   // alloc enough for double res. rendering
201   if (shadow_size < 1024 * 512 * 2)
202     shadow_size = 1024 * 512 * 2;
203
204   shadow_fb = malloc(shadow_size);
205   menubg_img = malloc(shadow_size);
206   if (shadow_fb == NULL || menubg_img == NULL) {
207     fprintf(stderr, "OOM\n");
208     exit(1);
209   }
210
211   in_sdl_init(&in_sdl_platform_data, sdl_event_handler);
212   in_probe();
213   pl_rearmed_cbs.only_16bpp = 1;
214   pl_rearmed_cbs.pl_get_layer_pos = get_layer_pos;
215
216   bgr_to_uyvy_init();
217
218   assert(plat_sdl_screen);
219   plugin_update();
220   if (plat_target.vout_method == vout_mode_gl)
221     gl_w_prev = plat_sdl_screen->w, gl_h_prev = plat_sdl_screen->h;
222   if (vout_mode_gl != -1)
223     plat_target.hwfilters = hwfilters;
224 }
225
226 void plat_finish(void)
227 {
228   free(shadow_fb);
229   shadow_fb = NULL;
230   free(menubg_img);
231   menubg_img = NULL;
232   plat_sdl_finish();
233 }
234
235 void plat_gvideo_open(int is_pal)
236 {
237 }
238
239 static void uyvy_to_rgb565(void *d, int pixels)
240 {
241   const unsigned int *src = (const void *)plat_sdl_overlay->pixels[0];
242   int x2 = plat_sdl_overlay->w >= psx_w * 2;
243   unsigned short *dst = d;
244   int v;
245
246   // no colors, for now
247   if (x2) {
248     for (; pixels > 0; src++, dst++, pixels--) {
249       v = (*src >> 8) & 0xff;
250       v = (v - 16) * 255 / 219 / 8;
251       *dst = (v << 11) | (v << 6) | v;
252     }
253   }
254   else {
255     for (; pixels > 0; src++, dst += 2, pixels -= 2) {
256       v = (*src >> 8) & 0xff;
257       v = (v - 16) * 255 / 219 / 8;
258       dst[0] = (v << 11) | (v << 6) | v;
259
260       v = (*src >> 24) & 0xff;
261       v = (v - 16) * 255 / 219 / 8;
262       dst[1] = (v << 11) | (v << 6) | v;
263     }
264   }
265 }
266
267 static void overlay_resize(int force)
268 {
269   int x2_mul = !in_menu && plat_target.vout_method > 1 ? 2 : 1; // lame
270   int w = in_menu ? g_menuscreen_w : psx_w;
271   int h = in_menu ? g_menuscreen_h : psx_h;
272
273   if (!force && plat_sdl_overlay && w * x2_mul == plat_sdl_overlay->w
274       && h == plat_sdl_overlay->h)
275     return;
276   if (plat_sdl_overlay)
277     SDL_FreeYUVOverlay(plat_sdl_overlay);
278   plat_sdl_overlay = SDL_CreateYUVOverlay(w * x2_mul, h, SDL_UYVY_OVERLAY,
279         plat_sdl_screen);
280   if (plat_sdl_overlay) {
281     //printf("overlay: %dx%d %08x hw=%d\n", plat_sdl_overlay->w, plat_sdl_overlay->h,
282     //    plat_sdl_overlay->format, plat_sdl_overlay->hw_overlay);
283     if (SDL_LockYUVOverlay(plat_sdl_overlay) == 0) {
284       plat_sdl_overlay_clear();
285       SDL_UnlockYUVOverlay(plat_sdl_overlay);
286     }
287   }
288   else {
289     fprintf(stderr, "overlay resize to %dx%d failed\n", w, h);
290     plat_target.vout_method = 0;
291   }
292   handle_scaler_resize(w, h);
293 }
294
295 static void overlay_blit(int doffs, const void *src_, int w, int h,
296                          int sstride, int bgr24)
297 {
298   const unsigned short *src = src_;
299   unsigned short *dst;
300   int dstride = plat_sdl_overlay->w;
301   int x2 = dstride >= 2 * w;
302
303   SDL_LockYUVOverlay(plat_sdl_overlay);
304   dst = (void *)plat_sdl_overlay->pixels[0];
305
306   dst += doffs;
307   if (bgr24) {
308     for (; h > 0; dst += dstride, src += sstride, h--)
309       bgr888_to_uyvy(dst, src, w, x2);
310   }
311   else {
312     for (; h > 0; dst += dstride, src += sstride, h--)
313       bgr555_to_uyvy(dst, src, w, x2);
314   }
315
316   SDL_UnlockYUVOverlay(plat_sdl_overlay);
317 }
318
319 static void overlay_hud_print(int x, int y, const char *str, int bpp)
320 {
321   int x2;
322   SDL_LockYUVOverlay(plat_sdl_overlay);
323   x2 = plat_sdl_overlay->w >= psx_w * 2;
324   if (x2)
325     x *= 2;
326   basic_text_out_uyvy_nf(plat_sdl_overlay->pixels[0], plat_sdl_overlay->w, x, y, str);
327   SDL_UnlockYUVOverlay(plat_sdl_overlay);
328 }
329
330 static void gl_finish_pl(void)
331 {
332   if (plugin_owns_display() && GPU_close != NULL)
333     GPU_close();
334   gl_destroy();
335 }
336
337 static void gl_resize(void)
338 {
339   int w = in_menu ? g_menuscreen_w : psx_w;
340   int h = in_menu ? g_menuscreen_h : psx_h;
341
342   gl_quirks &= ~(GL_QUIRK_SCALING_NEAREST | GL_QUIRK_VSYNC_ON);
343   if (plat_target.hwfilter) // inverted from plat_sdl_gl_scaling()
344     gl_quirks |= GL_QUIRK_SCALING_NEAREST;
345   if (g_opts & OPT_VSYNC)
346     gl_quirks |= GL_QUIRK_VSYNC_ON;
347
348   if (plugin_owns_display())
349     w = plat_sdl_screen->w, h = plat_sdl_screen->h;
350   if (plat_sdl_gl_active) {
351     if (w == gl_w_prev && h == gl_h_prev && gl_quirks == gl_quirks_prev)
352       return;
353     gl_finish_pl();
354   }
355   plat_sdl_gl_active = (gl_create(window, &gl_quirks, w, h) == 0);
356   if (plat_sdl_gl_active)
357     gl_w_prev = w, gl_h_prev = h, gl_quirks_prev = gl_quirks;
358   else {
359     fprintf(stderr, "warning: could not init GL.\n");
360     plat_target.vout_method = 0;
361   }
362   handle_scaler_resize(w, h);
363   plugin_update();
364   forced_flips = 0; // interferes with gl
365 }
366
367 static void overlay_or_gl_check_enable(void)
368 {
369   int ovl_on = plat_target.vout_method == vout_mode_overlay ||
370     plat_target.vout_method == vout_mode_overlay2x;
371   int gl_on = plat_target.vout_method == vout_mode_gl;
372   if (!gl_on && plat_sdl_gl_active) {
373     gl_finish_pl();
374     pl_rearmed_cbs.gles_display = gl_es_display;
375     pl_rearmed_cbs.gles_surface = gl_es_surface;
376     plat_sdl_gl_active = 0;
377   }
378   if (!ovl_on && plat_sdl_overlay) {
379     SDL_FreeYUVOverlay(plat_sdl_overlay);
380     plat_sdl_overlay = NULL;
381   }
382   if (ovl_on)
383     overlay_resize(0);
384   else if (gl_on)
385     gl_resize();
386 }
387
388 static void centered_clear(void)
389 {
390   int dstride = plat_sdl_screen->pitch / 2;
391   int w = plat_sdl_screen->w;
392   int h = plat_sdl_screen->h;
393   unsigned short *dst;
394
395   if (plat_sdl_gl_active) {
396     gl_clear();
397     return;
398   }
399
400   if (SDL_MUSTLOCK(plat_sdl_screen))
401     SDL_LockSurface(plat_sdl_screen);
402   dst = plat_sdl_screen->pixels;
403
404   for (; h > 0; dst += dstride, h--)
405     memset(dst, 0, w * 2);
406
407   if (SDL_MUSTLOCK(plat_sdl_screen))
408     SDL_UnlockSurface(plat_sdl_screen);
409
410   if (plat_sdl_overlay != NULL) {
411     // apply the parts not covered by the overlay
412     forced_flips = 3;
413   }
414 }
415
416 static int adj_src_dst(const SDL_Surface *sfc, int w, int pp, int *h,
417     unsigned short **dst, const unsigned short **src)
418 {
419   int line_w = w;
420   if (sfc->w > w)
421     *dst += (sfc->w - w) / 2;
422   else {
423     *src += (w - sfc->w) / 2;
424     line_w = sfc->w;
425   }
426   if (sfc->h > *h)
427     *dst += sfc->pitch * (sfc->h - *h) / 2 / 2;
428   else {
429     *src += pp * (*h - sfc->h) / 2;
430     *h = sfc->h;
431   }
432   return line_w;
433 }
434
435 static void centered_blit(int doffs, const void *src_, int w, int h,
436                           int sstride, int bgr24)
437 {
438   const unsigned short *src = src_;
439   unsigned short *dst;
440   int dstride;
441
442   if (SDL_MUSTLOCK(plat_sdl_screen))
443     SDL_LockSurface(plat_sdl_screen);
444   dst = plat_sdl_screen->pixels;
445   dstride = plat_sdl_screen->pitch / 2;
446   w = adj_src_dst(plat_sdl_screen, w, sstride, &h, &dst, &src);
447
448   if (bgr24) {
449     for (; h > 0; dst += dstride, src += sstride, h--)
450       bgr888_to_rgb565(dst, src, w * 3);
451   }
452   else {
453     for (; h > 0; dst += dstride, src += sstride, h--)
454       bgr555_to_rgb565(dst, src, w * 2);
455   }
456
457   if (SDL_MUSTLOCK(plat_sdl_screen))
458     SDL_UnlockSurface(plat_sdl_screen);
459 }
460
461 static void centered_blit_menu(void)
462 {
463   const unsigned short *src = g_menuscreen_ptr;
464   int w = g_menuscreen_w;
465   int h = g_menuscreen_h;
466   unsigned short *dst;
467   int dstride, len;
468
469   if (SDL_MUSTLOCK(plat_sdl_screen))
470     SDL_LockSurface(plat_sdl_screen);
471
472   dst = plat_sdl_screen->pixels;
473   dstride = plat_sdl_screen->pitch / 2;
474   len = adj_src_dst(plat_sdl_screen, w, g_menuscreen_pp, &h, &dst, &src);
475
476   for (; h > 0; dst += dstride, src += g_menuscreen_pp, h--)
477     memcpy(dst, src, len * 2);
478
479   if (SDL_MUSTLOCK(plat_sdl_screen))
480     SDL_UnlockSurface(plat_sdl_screen);
481 }
482
483 static void centered_hud_print(int x, int y, const char *str, int bpp)
484 {
485   int w_diff, h_diff;
486   if (SDL_MUSTLOCK(plat_sdl_screen))
487     SDL_LockSurface(plat_sdl_screen);
488   w_diff = plat_sdl_screen->w - psx_w;
489   h_diff = plat_sdl_screen->h - psx_h;
490   if (w_diff > 0) x += w_diff / 2;
491   if (h_diff > 0) y += h_diff / 2;
492   if (h_diff < 0) y += h_diff;
493   if (w_diff < 0 && x > 32) x += w_diff;
494   basic_text_out16_nf(plat_sdl_screen->pixels, plat_sdl_screen->pitch / 2, x, y, str);
495   if (SDL_MUSTLOCK(plat_sdl_screen))
496     SDL_UnlockSurface(plat_sdl_screen);
497 }
498
499 static void *setup_blit_callbacks(int w, int h)
500 {
501   pl_plat_clear = NULL;
502   pl_plat_blit = NULL;
503   pl_plat_hud_print = NULL;
504   if (plat_sdl_overlay != NULL) {
505     pl_plat_clear = plat_sdl_overlay_clear;
506     pl_plat_blit = overlay_blit;
507     pl_plat_hud_print = overlay_hud_print;
508   }
509   else if (plat_sdl_gl_active) {
510     return shadow_fb;
511   }
512   else {
513     pl_plat_clear = centered_clear;
514
515     if (!SDL_MUSTLOCK(plat_sdl_screen) && w == plat_sdl_screen->w &&
516         h == plat_sdl_screen->h)
517       return plat_sdl_screen->pixels;
518
519     pl_plat_blit = centered_blit;
520     pl_plat_hud_print = centered_hud_print;
521   }
522   return NULL;
523 }
524
525 // not using plat_sdl_change_video_mode() since we need
526 // different size overlay vs plat_sdl_screen layer
527 static void change_mode(int w, int h)
528 {
529   int set_w = w, set_h = h, had_overlay = 0, had_gl = 0;
530   if (plat_target.vout_fullscreen && (plat_target.vout_method != 0 || !sdl12_compat))
531     set_w = fs_w, set_h = fs_h;
532   if (plat_sdl_screen->w != set_w || plat_sdl_screen->h != set_h ||
533       plat_target.vout_fullscreen != vout_fullscreen_old)
534   {
535     Uint32 flags = plat_sdl_screen->flags;
536     if (plat_target.vout_fullscreen)
537       flags |= SDL_FULLSCREEN;
538     else {
539       flags &= ~SDL_FULLSCREEN;
540       if (plat_sdl_is_windowed())
541         flags |= SDL_RESIZABLE; // sdl12-compat 1.2.68 loses this flag
542     }
543     if (plat_sdl_overlay) {
544       SDL_FreeYUVOverlay(plat_sdl_overlay);
545       plat_sdl_overlay = NULL;
546       had_overlay = 1;
547     }
548     if (plat_sdl_gl_active) {
549       gl_finish_pl();
550       plat_sdl_gl_active = 0;
551       had_gl = 1;
552     }
553     SDL_PumpEvents();
554     plat_sdl_screen = SDL_SetVideoMode(set_w, set_h, 16, flags);
555     //printf("mode: %dx%d %x -> %dx%d\n", set_w, set_h, flags,
556     //  plat_sdl_screen->w, plat_sdl_screen->h);
557     assert(plat_sdl_screen);
558     if (vout_fullscreen_old && !plat_target.vout_fullscreen)
559       // why is this needed?? (on 1.2.68)
560       SDL_WM_GrabInput(SDL_GRAB_OFF);
561     if (vout_mode_gl != -1)
562       update_wm_display_window();
563     // overlay needs the latest plat_sdl_screen
564     if (had_overlay)
565       overlay_resize(1);
566     if (had_gl)
567       gl_resize();
568     centered_clear();
569     plugin_update();
570     vout_fullscreen_old = plat_target.vout_fullscreen;
571   }
572 }
573
574 static void handle_scaler_resize(int w, int h)
575 {
576   int ww = plat_sdl_screen->w;
577   int wh = plat_sdl_screen->h;
578   int layer_w_old = g_layer_w;
579   int layer_h_old = g_layer_h;
580   float w_mul, h_mul;
581   int x, y;
582   pl_update_layer_size(w, h, ww, wh);
583   if (layer_w_old != g_layer_w || layer_h_old != g_layer_h)
584     forced_clears = 3;
585
586   w_mul = 2.0f / ww;
587   h_mul = 2.0f / wh;
588   x = (ww - g_layer_w) / 2;
589   y = (wh - g_layer_h) / 2;
590   gl_vertices[3*0+0] = gl_vertices[3*2+0] = -1.0f + x * w_mul;
591   gl_vertices[3*1+0] = gl_vertices[3*3+0] = -1.0f + (x + g_layer_w) * w_mul;
592   gl_vertices[3*2+1] = gl_vertices[3*3+1] = -1.0f + y * h_mul;
593   gl_vertices[3*0+1] = gl_vertices[3*1+1] = -1.0f + (y + g_layer_h) * h_mul;
594 }
595
596 static void handle_window_resize(void)
597 {
598   // sdl12-compat: a hack to take advantage of sdl2 scaling
599   if (resized && (plat_target.vout_method != 0 || !sdl12_compat)) {
600     change_mode(window_w, window_h);
601     setup_blit_callbacks(psx_w, psx_h);
602     forced_clears = 3;
603   }
604   resized = 0;
605 }
606
607 void *plat_gvideo_set_mode(int *w, int *h, int *bpp)
608 {
609   psx_w = *w;
610   psx_h = *h;
611
612   if (plat_sdl_gl_active && plugin_owns_display())
613     return NULL;
614
615   if (plat_sdl_overlay != NULL)
616     overlay_resize(0);
617   else if (plat_sdl_gl_active) {
618     memset(shadow_fb, 0, (*w) * (*h) * 2);
619     gl_resize();
620   }
621   else if (plat_target.vout_method == 0) // && sdl12_compat
622     change_mode(*w, *h);
623
624   handle_scaler_resize(*w, *h); // override the value from pl_vout_set_mode()
625   return setup_blit_callbacks(*w, *h);
626 }
627
628 void *plat_gvideo_flip(void)
629 {
630   void *ret = NULL;
631   int do_flip = 0;
632   if (plat_sdl_overlay != NULL) {
633     SDL_Rect dstrect = {
634       (plat_sdl_screen->w - g_layer_w) / 2,
635       (plat_sdl_screen->h - g_layer_h) / 2,
636       g_layer_w, g_layer_h
637     };
638     SDL_DisplayYUVOverlay(plat_sdl_overlay, &dstrect);
639   }
640   else if (plat_sdl_gl_active) {
641     gl_flip_v(shadow_fb, psx_w, psx_h, g_scaler != SCALE_FULLSCREEN ? gl_vertices : NULL);
642     ret = shadow_fb;
643   }
644   else
645     do_flip |= 2;
646
647   if (forced_flips > 0) {
648     forced_flips--;
649     do_flip |= 1;
650   }
651   if (do_flip)
652     SDL_Flip(plat_sdl_screen);
653   handle_window_resize();
654   if (do_flip) {
655     if (forced_clears > 0) {
656       forced_clears--;
657       centered_clear();
658     }
659     if (!SDL_MUSTLOCK(plat_sdl_screen) && plat_sdl_screen->w == psx_w &&
660         plat_sdl_screen->h == psx_h && (do_flip & 2)) {
661       ret = plat_sdl_screen->pixels;
662     }
663   }
664   assert(ret || pl_plat_clear != NULL);
665   return ret;
666 }
667
668 void plat_gvideo_close(void)
669 {
670 }
671
672 void plat_video_menu_enter(int is_rom_loaded)
673 {
674   int d;
675
676   in_menu = 1;
677
678   /* surface will be lost, must adjust pl_vout_buf for menu bg */
679   if (plat_sdl_overlay != NULL)
680     uyvy_to_rgb565(menubg_img, psx_w * psx_h);
681   else if (plat_sdl_gl_active)
682     memcpy(menubg_img, shadow_fb, psx_w * psx_h * 2);
683   else {
684     unsigned short *dst = menubg_img;
685     const unsigned short *src;
686     int h;
687     if (SDL_MUSTLOCK(plat_sdl_screen))
688       SDL_LockSurface(plat_sdl_screen);
689     src = plat_sdl_screen->pixels;
690     src += (plat_sdl_screen->w - psx_w) / 2;
691     src += plat_sdl_screen->pitch * (plat_sdl_screen->h - psx_h) / 2 / 2;
692     for (h = psx_h; h > 0; dst += psx_w, src += plat_sdl_screen->pitch / 2, h--)
693       memcpy(dst, src, psx_w * 2);
694     if (SDL_MUSTLOCK(plat_sdl_screen))
695       SDL_UnlockSurface(plat_sdl_screen);
696   }
697   pl_vout_buf = menubg_img;
698
699   if (plat_target.vout_method == 0)
700     change_mode(g_menuscreen_w, g_menuscreen_h);
701   else
702     overlay_or_gl_check_enable();
703   centered_clear();
704
705   for (d = 0; d < IN_MAX_DEVS; d++)
706     in_set_config_int(d, IN_CFG_ANALOG_MAP_ULDR, 1);
707 }
708
709 void plat_video_menu_begin(void)
710 {
711   void *old_ovl = plat_sdl_overlay;
712   static int g_scaler_old;
713   int scaler_changed = g_scaler_old != g_scaler;
714   g_scaler_old = g_scaler;
715   if (plat_target.vout_fullscreen != vout_fullscreen_old ||
716       (plat_target.vout_fullscreen && scaler_changed)) {
717     change_mode(g_menuscreen_w, g_menuscreen_h);
718   }
719   overlay_or_gl_check_enable();
720   handle_scaler_resize(g_menuscreen_w, g_menuscreen_h);
721
722   if (old_ovl != plat_sdl_overlay || scaler_changed)
723     centered_clear();
724   g_menuscreen_ptr = shadow_fb;
725 }
726
727 void plat_video_menu_end(void)
728 {
729   int do_flip = 0;
730
731   if (plat_sdl_overlay != NULL) {
732     SDL_Rect dstrect = {
733       (plat_sdl_screen->w - g_layer_w) / 2,
734       (plat_sdl_screen->h - g_layer_h) / 2,
735       g_layer_w, g_layer_h
736     };
737
738     SDL_LockYUVOverlay(plat_sdl_overlay);
739     rgb565_to_uyvy(plat_sdl_overlay->pixels[0], shadow_fb,
740       g_menuscreen_w * g_menuscreen_h);
741     SDL_UnlockYUVOverlay(plat_sdl_overlay);
742
743     SDL_DisplayYUVOverlay(plat_sdl_overlay, &dstrect);
744   }
745   else if (plat_sdl_gl_active) {
746     gl_flip_v(g_menuscreen_ptr, g_menuscreen_w, g_menuscreen_h,
747         g_scaler != SCALE_FULLSCREEN ? gl_vertices : NULL);
748   }
749   else {
750     centered_blit_menu();
751     do_flip |= 2;
752   }
753
754   if (forced_flips > 0) {
755     forced_flips--;
756     do_flip |= 1;
757   }
758   if (do_flip)
759     SDL_Flip(plat_sdl_screen);
760
761   handle_window_resize();
762   g_menuscreen_ptr = NULL;
763 }
764
765 void plat_video_menu_leave(void)
766 {
767   int d;
768
769   in_menu = 0;
770   if (plat_sdl_overlay != NULL || plat_sdl_gl_active)
771     memset(shadow_fb, 0, g_menuscreen_w * g_menuscreen_h * 2);
772
773   if (plat_target.vout_fullscreen)
774     change_mode(fs_w, fs_h);
775   overlay_or_gl_check_enable();
776   centered_clear();
777   setup_blit_callbacks(psx_w, psx_h);
778
779   for (d = 0; d < IN_MAX_DEVS; d++)
780     in_set_config_int(d, IN_CFG_ANALOG_MAP_ULDR, 0);
781 }
782
783 void *plat_prepare_screenshot(int *w, int *h, int *bpp)
784 {
785   if (plat_sdl_screen && !SDL_MUSTLOCK(plat_sdl_screen) &&
786       plat_sdl_overlay == NULL && !plat_sdl_gl_active)
787   {
788     *w = plat_sdl_screen->pitch / 2;
789     *h = plat_sdl_screen->h;
790     *bpp = 16;
791     return plat_sdl_screen->pixels;
792   }
793   fprintf(stderr, "screenshot not implemented in current mode\n");
794   return NULL;
795 }
796
797 void plat_trigger_vibrate(int pad, int low, int high)
798 {
799 }
800
801 void plat_minimize(void)
802 {
803 }
804
805 // vim:shiftwidth=2:expandtab