frontend: initial sdl overlay implementation
[pcsx_rearmed.git] / frontend / plat_sdl.c
1 /*
2  * (C) GraÅžvydas "notaz" Ignotas, 2011,2012
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 <SDL.h>
13
14 #include "libpicofe/input.h"
15 #include "libpicofe/in_sdl.h"
16 #include "libpicofe/menu.h"
17 #include "plugin_lib.h"
18 #include "main.h"
19 #include "menu.h"
20 #include "plat.h"
21 #include "revision.h"
22
23 static const struct in_default_bind in_sdl_defbinds[] = {
24   { SDLK_UP,     IN_BINDTYPE_PLAYER12, DKEY_UP },
25   { SDLK_DOWN,   IN_BINDTYPE_PLAYER12, DKEY_DOWN },
26   { SDLK_LEFT,   IN_BINDTYPE_PLAYER12, DKEY_LEFT },
27   { SDLK_RIGHT,  IN_BINDTYPE_PLAYER12, DKEY_RIGHT },
28   { SDLK_d,      IN_BINDTYPE_PLAYER12, DKEY_TRIANGLE },
29   { SDLK_z,      IN_BINDTYPE_PLAYER12, DKEY_CROSS },
30   { SDLK_x,      IN_BINDTYPE_PLAYER12, DKEY_CIRCLE },
31   { SDLK_s,      IN_BINDTYPE_PLAYER12, DKEY_SQUARE },
32   { SDLK_v,      IN_BINDTYPE_PLAYER12, DKEY_START },
33   { SDLK_c,      IN_BINDTYPE_PLAYER12, DKEY_SELECT },
34   { SDLK_w,      IN_BINDTYPE_PLAYER12, DKEY_L1 },
35   { SDLK_r,      IN_BINDTYPE_PLAYER12, DKEY_R1 },
36   { SDLK_e,      IN_BINDTYPE_PLAYER12, DKEY_L2 },
37   { SDLK_t,      IN_BINDTYPE_PLAYER12, DKEY_R2 },
38   { SDLK_ESCAPE, IN_BINDTYPE_EMU, SACTION_ENTER_MENU },
39   { SDLK_F1,     IN_BINDTYPE_EMU, SACTION_SAVE_STATE },
40   { SDLK_F2,     IN_BINDTYPE_EMU, SACTION_LOAD_STATE },
41   { SDLK_F3,     IN_BINDTYPE_EMU, SACTION_PREV_SSLOT },
42   { SDLK_F4,     IN_BINDTYPE_EMU, SACTION_NEXT_SSLOT },
43   { SDLK_F5,     IN_BINDTYPE_EMU, SACTION_TOGGLE_FSKIP },
44   { SDLK_F6,     IN_BINDTYPE_EMU, SACTION_SCREENSHOT },
45   { SDLK_F7,     IN_BINDTYPE_EMU, SACTION_TOGGLE_FPS },
46   { SDLK_F8,     IN_BINDTYPE_EMU, SACTION_SWITCH_DISPMODE },
47   { SDLK_F11,    IN_BINDTYPE_EMU, SACTION_TOGGLE_FULLSCREEN },
48   { SDLK_BACKSPACE, IN_BINDTYPE_EMU, SACTION_FAST_FORWARD },
49   { 0, 0, 0 }
50 };
51
52 // XXX: maybe determine this instead..
53 #define WM_DECORATION_H 32
54
55 static SDL_Surface *screen;
56 static SDL_Overlay *overlay;
57 static int window_w, window_h;
58 static int fs_w, fs_h;
59 static int psx_w, psx_h;
60 static void *menubg_img;
61 static int in_menu, pending_resize, old_fullscreen;
62
63 static int change_video_mode(int w, int h)
64 {
65   psx_w = w;
66   psx_h = h;
67
68   if (overlay != NULL) {
69     SDL_FreeYUVOverlay(overlay);
70     overlay = NULL;
71   }
72
73   if (g_use_overlay && !in_menu) {
74     Uint32 flags = SDL_RESIZABLE;
75     int win_w = window_w;
76     int win_h = window_h;
77
78     if (g_fullscreen) {
79       flags |= SDL_FULLSCREEN;
80       win_w = fs_w;
81       win_h = fs_h;
82     }
83
84     screen = SDL_SetVideoMode(win_w, win_h, 0, flags);
85     if (screen == NULL) {
86       fprintf(stderr, "SDL_SetVideoMode failed: %s\n", SDL_GetError());
87       return -1;
88     }
89
90     overlay = SDL_CreateYUVOverlay(w, h, SDL_UYVY_OVERLAY, screen);
91     if (overlay != NULL) {
92       /*printf("overlay: fmt %x, planes: %d, pitch: %d, hw: %d\n",
93         overlay->format, overlay->planes, *overlay->pitches,
94         overlay->hw_overlay);*/
95
96       if ((long)overlay->pixels[0] & 3)
97         fprintf(stderr, "warning: overlay pointer is unaligned\n");
98       if (!overlay->hw_overlay)
99         fprintf(stderr, "warning: video overlay is not hardware accelerated,"
100                         " you may want to disable it.\n");
101     }
102     else {
103       fprintf(stderr, "warning: could not create overlay.\n");
104     }
105   }
106
107   if (overlay == NULL) {
108     screen = SDL_SetVideoMode(w, h, 16, 0);
109     if (screen == NULL) {
110       fprintf(stderr, "SDL_SetVideoMode failed: %s\n", SDL_GetError());
111       return -1;
112     }
113
114     if (!in_menu) {
115       window_w = screen->w;
116       window_h = screen->h;
117     }
118   }
119
120   old_fullscreen = g_fullscreen;
121   return 0;
122 }
123
124 static void event_handler(void *event_)
125 {
126   SDL_Event *event = event_;
127
128   if (event->type == SDL_VIDEORESIZE) {
129     //printf("%dx%d\n", event->resize.w, event->resize.h);
130     if (overlay != NULL && !g_fullscreen && !old_fullscreen) {
131       window_w = event->resize.w;
132       window_h = event->resize.h;
133       pending_resize = 1;
134     }
135   }
136 }
137
138 void plat_init(void)
139 {
140   const SDL_VideoInfo *info;
141   int ret, h;
142
143   ret = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE);
144   if (ret != 0) {
145     fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
146     exit(1);
147   }
148
149   info = SDL_GetVideoInfo();
150   if (info != NULL) {
151     fs_w = info->current_w;
152     fs_h = info->current_h;
153   }
154
155   in_menu = 1;
156   g_menuscreen_w = 640;
157   if (fs_w != 0 && g_menuscreen_w > fs_w)
158     g_menuscreen_w = fs_w;
159   g_menuscreen_h = 480;
160   if (fs_h != 0) {
161     h = fs_h;
162     if (info && info->wm_available && h > WM_DECORATION_H)
163       h -= WM_DECORATION_H;
164     if (g_menuscreen_h > h)
165       g_menuscreen_h = h;
166   }
167
168   ret = change_video_mode(g_menuscreen_w, g_menuscreen_h);
169   if (ret != 0) {
170     ret = change_video_mode(0, 0);
171     if (ret != 0)
172       goto fail;
173
174     if (screen->w < 320 || screen->h < 240) {
175       fprintf(stderr, "resolution %dx%d is too small, sorry.\n",
176               screen->w, screen->h);
177       goto fail;
178     }
179   }
180   g_menuscreen_w = window_w = screen->w;
181   g_menuscreen_h = window_h = screen->h;
182
183   SDL_WM_SetCaption("PCSX-ReARMed " REV, NULL);
184
185   menubg_img = malloc(640 * 512 * 2);
186   if (menubg_img == NULL)
187     goto fail;
188
189   in_sdl_init(in_sdl_defbinds, event_handler);
190   in_probe();
191   pl_rearmed_cbs.only_16bpp = 1;
192   return;
193
194 fail:
195   SDL_Quit();
196   exit(1);
197 }
198
199 void plat_finish(void)
200 {
201   free(menubg_img);
202   menubg_img = NULL;
203   SDL_Quit();
204 }
205
206 void plat_gvideo_open(int is_pal)
207 {
208 }
209
210 void *plat_gvideo_set_mode(int *w, int *h, int *bpp)
211 {
212   change_video_mode(*w, *h);
213   return screen->pixels;
214 }
215
216 static void test_convert(void *d, const void *s, int pixels)
217 {
218   unsigned int *dst = d;
219   const unsigned short *src = s;
220   int r0, g0, b0, r1, g1, b1;
221   int y0, y1, u, v;
222
223   for (; pixels > 0; src += 2, dst++, pixels -= 2) {
224     r0 =  src[0] >> 11;
225     g0 = (src[0] >> 6) & 0x1f;
226     b0 =  src[0] & 0x1f;
227     r1 =  src[1] >> 11;
228     g1 = (src[1] >> 6) & 0x1f;
229     b1 =  src[1] & 0x1f;
230     y0 = (int)((0.299f * r0) + (0.587f * g0) + (0.114f * b0));
231     y1 = (int)((0.299f * r1) + (0.587f * g1) + (0.114f * b1));
232     //u = (int)(((-0.169f * r0) + (-0.331f * g0) + ( 0.499f * b0)) * 8) + 128;
233     //v = (int)((( 0.499f * r0) + (-0.418f * g0) + (-0.0813f * b0)) * 8) + 128;
234     u = (int)(8 * 0.565f * (b0 - y0)) + 128;
235     v = (int)(8 * 0.713f * (r0 - y0)) + 128;
236     // valid Y range seems to be 16..235
237     y0 = 16 + 219 * y0 / 31;
238     y1 = 16 + 219 * y1 / 31;
239
240     if (y0 < 0 || y0 > 255 || y1 < 0 || y1 > 255
241         || u < 0 || u > 255 || v < 0 || v > 255)
242     {
243       printf("oor: %d, %d, %d, %d\n", y0, y1, u, v);
244     }
245     *dst = (y1 << 24) | (v << 16) | (y0 << 8) | u;
246   }
247 }
248
249 /* XXX: missing SDL_LockSurface() */
250 void *plat_gvideo_flip(void)
251 {
252   if (!in_menu && overlay != NULL) {
253     SDL_Rect dstrect = { 0, 0, screen->w, screen->h };
254     SDL_LockYUVOverlay(overlay);
255     test_convert(overlay->pixels[0], screen->pixels, overlay->w * overlay->h);
256     SDL_UnlockYUVOverlay(overlay);
257     SDL_DisplayYUVOverlay(overlay, &dstrect);
258   }
259   else
260     SDL_Flip(screen);
261
262   if (pending_resize || g_fullscreen != old_fullscreen) {
263     // must be done here so that correct buffer is returned
264     change_video_mode(psx_w, psx_h);
265     pending_resize = 0;
266   }
267
268   return screen->pixels;
269 }
270
271 void plat_gvideo_close(void)
272 {
273 }
274
275 void plat_video_menu_enter(int is_rom_loaded)
276 {
277   in_menu = 1;
278
279   /* surface will be lost, must adjust pl_vout_buf for menu bg */
280   // FIXME?
281   memcpy(menubg_img, screen->pixels, psx_w * psx_h * 2);
282   pl_vout_buf = menubg_img;
283
284   change_video_mode(g_menuscreen_w, g_menuscreen_h);
285 }
286
287 void plat_video_menu_begin(void)
288 {
289   SDL_LockSurface(screen);
290   g_menuscreen_ptr = screen->pixels;
291 }
292
293 void plat_video_menu_end(void)
294 {
295   SDL_UnlockSurface(screen);
296   SDL_Flip(screen);
297   g_menuscreen_ptr = NULL;
298 }
299
300 void plat_video_menu_leave(void)
301 {
302   in_menu = 0;
303 }
304
305 /* unused stuff */
306 void *plat_prepare_screenshot(int *w, int *h, int *bpp)
307 {
308   return 0;
309 }
310
311 void plat_trigger_vibrate(int is_strong)
312 {
313 }
314
315 void plat_minimize(void)
316 {
317 }
318
319 // vim:shiftwidth=2:expandtab