use offscreen blitting when SDL_DOUBLEBUF is not set
[sdl_omap.git] / src / video / omapdss / sdlif.c
1 /*
2  * (C) GraÅžvydas "notaz" Ignotas, 2010-2012
3  *
4  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
5  * See the COPYING file in the top-level directory.
6  */
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <X11/XF86keysym.h>
11
12 #include "../SDL_sysvideo.h"
13 #include "../SDL_pixels_c.h"
14 #include "../../events/SDL_events_c.h"
15
16 #include "linux/xenv.h"
17 #include "osdl.h"
18
19
20 static int omap_available(void) 
21 {
22         trace();
23         return 1;
24 }
25
26 static void omap_free(SDL_VideoDevice *device)
27 {
28         trace();
29         free(device);
30 }
31
32 static int omap_VideoInit(SDL_VideoDevice *this, SDL_PixelFormat *vformat)
33 {
34         const char *tmp;
35         int w, h, ret;
36
37         trace();
38
39         // default to 16bpp
40         vformat->BitsPerPixel = 16;
41
42         omapsdl_input_init();
43         omapsdl_config(this->hidden);
44
45         tmp = getenv("SDL_OMAP_DEFAULT_MODE");
46         if (tmp != NULL && sscanf(tmp, "%dx%d", &w, &h) == 2) {
47                 this->info.current_w = w;
48                 this->info.current_h = h;
49         }
50         else if (osdl_video_detect_screen(this->hidden) == 0) {
51                 this->info.current_w = this->hidden->phys_w;
52                 this->info.current_h = this->hidden->phys_h;
53         }
54
55         this->handles_any_size = 1;
56         this->info.hw_available = 1;
57
58         return 0;
59 }
60
61 static void omap_VideoQuit(SDL_VideoDevice *this)
62 {
63         trace();
64
65         osdl_video_finish(this->hidden);
66         this->screen->pixels = NULL;
67         omapsdl_input_finish();
68 }
69
70 static SDL_Rect **omap_ListModes(SDL_VideoDevice *this, SDL_PixelFormat *format, Uint32 flags)
71 {
72         static SDL_Rect omap_mode_max = {
73                 /* with handles_any_size, should accept anything up to this
74                  * XXX: possibly set this dynamically based on free vram? */
75                 0, 0, 1600, 1200
76         };
77         /* broken API needs this stupidity */
78         static SDL_Rect *omap_modes[] = {
79                 &omap_mode_max,
80                 NULL
81         };
82
83         trace();
84
85         if (format->BitsPerPixel <= 8)
86                 // not (yet?) supported
87                 return NULL;
88
89         return omap_modes;
90 }
91
92 static SDL_Surface *omap_SetVideoMode(SDL_VideoDevice *this, SDL_Surface *current, int width,
93                                         int height, int bpp, Uint32 flags)
94 {
95         struct SDL_PrivateVideoData *pdata = this->hidden;
96         SDL_PixelFormat *format;
97         Uint32 unhandled_flags;
98         int doublebuf;
99         void *fbmem;
100
101         trace("%d, %d, %d, %08x", width, height, bpp, flags);
102
103         omapsdl_config_from_env(pdata);
104
105         switch (bpp) {
106         case 16:
107                 format = SDL_ReallocFormat(current, 16, 0xf800, 0x07e0, 0x001f, 0);
108                 break;
109         case 24:
110                 format = SDL_ReallocFormat(current, 24, 0xff0000, 0xff00, 0xff, 0);
111                 break;
112         case 32:
113                 format = SDL_ReallocFormat(current, 32, 0xff0000, 0xff00, 0xff, 0);
114                 break;
115         default:
116                 err("SetVideoMode: bpp %d not supported", bpp);
117                 return NULL;
118         }
119         if (format == NULL)
120                 return NULL;
121
122         if (!(flags & SDL_DOUBLEBUF) && pdata->cfg_force_doublebuf) {
123                 log("forcing SDL_DOUBLEBUF");
124                 flags |= SDL_DOUBLEBUF;
125         }
126
127         if (pdata->border_l | pdata->border_r | pdata->border_t | pdata->border_b) {
128                 if (pdata->border_l + pdata->border_r >= width
129                     || pdata->border_t + pdata->border_b >= height)
130                 {
131                         err("specified border too large, ignoring");
132                         pdata->border_l = pdata->border_r = pdata->border_t = pdata->border_b = 0;
133                 }
134         }
135
136         /* always use doublebuf, when SDL_DOUBLEBUF is not set,
137          * we'll have to blit manually on UpdateRects() */
138         doublebuf = 1;
139
140         fbmem = osdl_video_set_mode(pdata,
141                 pdata->border_l, pdata->border_r, pdata->border_t, pdata->border_b,
142                 width, height, bpp, &doublebuf, this->wm_title);
143         if (fbmem == NULL) {
144                 err("failing on mode %dx%d@%d, doublebuf %s, border %d,%d,%d,%d",
145                     width, height, bpp, (flags & SDL_DOUBLEBUF) ? "on" : "off",
146                     pdata->border_l, pdata->border_r, pdata->border_t, pdata->border_b);
147                 return NULL;
148         }
149         pdata->front_buffer = osdl_video_get_active_buffer(pdata);
150         if (pdata->front_buffer == NULL) {
151                 err("osdl_video_get_active_buffer failed\n");
152                 return NULL;
153         }
154
155         if (!doublebuf) {
156                 if (flags & SDL_DOUBLEBUF) {
157                         log("doublebuffering could not be set\n");
158                         flags &= ~SDL_DOUBLEBUF;
159                 }
160                 /* XXX: could just malloc a back buffer here instead */
161                 pdata->cfg_force_directbuf = 1;
162         }
163
164         if (!(flags & SDL_DOUBLEBUF) && pdata->cfg_force_directbuf)
165                 fbmem = pdata->front_buffer;
166
167         flags |= SDL_FULLSCREEN | SDL_HWSURFACE;
168         unhandled_flags = flags & ~(SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF);
169         if (unhandled_flags != 0) {
170                 log("dropping unhandled flags: %08x", unhandled_flags);
171                 flags &= ~unhandled_flags;
172         }
173
174         current->flags = flags;
175         current->w = width;
176         current->h = height;
177         current->pitch = SDL_CalculatePitch(current);
178         current->pixels = fbmem;
179         pdata->app_uses_flip = 0;
180
181         if (pdata->layer_w != 0 && pdata->layer_h != 0) {
182                 int v_width  = width  - (pdata->border_l + pdata->border_r);
183                 int v_height = height - (pdata->border_t + pdata->border_b);
184                 pdata->ts_xmul = (v_width  << 16) / pdata->layer_w;
185                 pdata->ts_ymul = (v_height << 16) / pdata->layer_h;
186         }
187
188         return current;
189 }
190
191 static int omap_LockHWSurface(SDL_VideoDevice *this, SDL_Surface *surface)
192 {
193         trace("%p", surface);
194
195         return 0;
196 }
197
198 static void omap_UnlockHWSurface(SDL_VideoDevice *this, SDL_Surface *surface)
199 {
200         trace("%p", surface);
201 }
202
203 static int omap_FlipHWSurface(SDL_VideoDevice *this, SDL_Surface *surface)
204 {
205         struct SDL_PrivateVideoData *pdata = this->hidden;
206         static int warned;
207
208         trace("%p", surface);
209
210         if (surface != this->screen) {
211                 if (!warned) {
212                         err("flip surface %p which is not screen %p?\n",
213                                 surface, this->screen);
214                         warned = 1;
215                 }
216                 return;
217         }
218
219         if (surface->flags & SDL_DOUBLEBUF)
220                 surface->pixels = osdl_video_flip(pdata);
221         else {
222                 if (surface->pixels != pdata->front_buffer)
223                         memcpy(surface->pixels, pdata->front_buffer,
224                                 surface->pitch * surface->h);
225         }
226
227         pdata->app_uses_flip = 1;
228
229         return 0;
230 }
231
232 /* we can't do hw surfaces (besides screen one) yet */
233 static int omap_AllocHWSurface(SDL_VideoDevice *this, SDL_Surface *surface)
234 {
235         trace("%p", surface);
236         return -1;
237 }
238
239 static void omap_FreeHWSurface(SDL_VideoDevice *this, SDL_Surface *surface)
240 {
241         trace("%p", surface);
242 }
243
244 static int omap_SetColors(SDL_VideoDevice *this, int firstcolor, int ncolors, SDL_Color *colors)
245 {
246         trace("%d, %d, %p", firstcolor, ncolors, colors);
247         return 0;
248 }
249
250 static void omap_UpdateRects(SDL_VideoDevice *this, int nrects, SDL_Rect *rects)
251 {
252         struct SDL_PrivateVideoData *pdata = this->hidden;
253         SDL_Surface *screen = this->screen;
254         Uint16 *src, *dst;
255         int i, x, y, w, h;
256
257         trace("%d, %p", nrects, rects);
258
259         if (screen->flags & SDL_DOUBLEBUF) {
260                 if (nrects == 1 && rects->x == 0 && rects->y == 0
261                     && (rects->w == screen->w || rects->w == 0)
262                     && (rects->h == screen->h || rects->h == 0)
263                     && !pdata->app_uses_flip)
264                 {
265                         screen->pixels = osdl_video_flip(pdata);
266                 }
267                 return;
268         }
269
270         src = screen->pixels;
271         dst = pdata->front_buffer;
272         if (src == dst)
273                 return;
274
275         for (i = 0; i < nrects; i++) {
276                 /* this supposedly has no clipping, but we'll do it anyway */
277                 x = rects[i].x, y = rects[i].y, w = rects[i].w, h = rects[i].h;
278                 if (x < 0)
279                         w += x, x = 0;
280                 else if (x + w > screen->w)
281                         w = screen->w - x;
282                 if (w <= 0)
283                         continue;
284
285                 if (y < 0)
286                         h += y, y = 0;
287                 else if (y + h > screen->h)
288                         h = screen->h - y;
289
290                 for (; h > 0; y++, h--)
291                         memcpy(dst + y * screen->pitch / 2 + x,
292                                src + y * screen->pitch / 2 + x,
293                                w * 2);
294         }
295 }
296
297 static void omap_InitOSKeymap(SDL_VideoDevice *this)
298 {
299         trace();
300 }
301
302 static int key_event_cb(void *cb_arg, int sdl_kc, int sdl_sc, int is_pressed)
303 {
304         SDL_keysym keysym = { 0, };
305         int shift = 0;
306         SDLMod mod;
307         int ret;
308
309         keysym.sym = sdl_kc;
310         keysym.scancode = sdl_sc;
311
312         /* 0xff if pandora's Fn, so we exclude it too.. */
313         if (is_pressed && sdl_kc < 0xff && SDL_TranslateUNICODE) {
314                 mod = SDL_GetModState();
315                 if (!(mod & KMOD_CTRL) && (!!(mod & KMOD_SHIFT) ^ !!(mod & KMOD_CAPS)))
316                         shift = 1;
317
318                 /* prefer X mapping, if that doesn't work use hardcoded one */
319                 ret = xenv_keycode_to_keysym(sdl_sc, shift);
320                 if (ret >= 0) {
321                         keysym.unicode = ret;
322                         if ((mod & KMOD_CTRL)
323                             && 0x60 <= keysym.unicode && keysym.unicode <= 0x7f)
324                         {
325                                 keysym.unicode -= 0x60;
326                         }
327                         /* hmh.. */
328                         if ((keysym.unicode & 0xff00) == 0xff00)
329                                 keysym.unicode &= ~0xff00;
330                 }
331                 else {
332                         keysym.unicode = sdl_kc;
333                         if ((mod & KMOD_CTRL) && 0x60 <= sdl_kc && sdl_kc <= 0x7f)
334                         {
335                                 keysym.unicode = sdl_kc - 0x60;
336                         }
337                         else if (shift && 'a' <= sdl_kc && sdl_kc <= 'z')
338                         {
339                                 keysym.unicode = sdl_kc - 'a' + 'A';
340                         }
341                 }
342         }
343
344         SDL_PrivateKeyboard(is_pressed, &keysym);
345 }
346
347 /* clamp x to min..max-1 */
348 #define clamp(x, min, max) \
349         if (x < (min)) x = min; \
350         if (x >= (max)) x = max
351
352 static void translate_mouse(SDL_VideoDevice *this, int *x, int *y)
353 {
354         struct SDL_PrivateVideoData *pdata = this->hidden;
355
356         if (!pdata->cfg_no_ts_translate && pdata->layer_w != 0 && pdata->layer_h != 0) {
357                 *x = pdata->border_l + ((*x - pdata->layer_x) * pdata->ts_xmul >> 16);
358                 *y = pdata->border_t + ((*y - pdata->layer_y) * pdata->ts_ymul >> 16);
359                 clamp(*x, 0, this->screen->w);
360                 clamp(*y, 0, this->screen->h);
361         }
362 }
363
364 static int ts_event_cb(void *cb_arg, int x, int y, unsigned int pressure)
365 {
366         static int was_pressed;
367         SDL_VideoDevice *this = cb_arg;
368         struct SDL_PrivateVideoData *pdata = this->hidden;
369
370         translate_mouse(this, &x, &y);
371
372         pressure = !!pressure;
373         if (pressure != was_pressed) {
374                 SDL_PrivateMouseButton(pressure ? SDL_PRESSED : SDL_RELEASED, 1, x, y);
375                 was_pressed = pressure;
376         }
377         else
378                 SDL_PrivateMouseMotion(0, 0, x, y);
379 }
380
381 static int xmouseb_event_cb(void *cb_arg, int x, int y, int button, int is_pressed)
382 {
383         SDL_VideoDevice *this = cb_arg;
384         struct SDL_PrivateVideoData *pdata = this->hidden;
385
386         translate_mouse(this, &x, &y);
387         SDL_PrivateMouseButton(is_pressed ? SDL_PRESSED : SDL_RELEASED, button, x, y);
388 }
389
390 static int xmousem_event_cb(void *cb_arg, int x, int y)
391 {
392         SDL_VideoDevice *this = cb_arg;
393         struct SDL_PrivateVideoData *pdata = this->hidden;
394
395         translate_mouse(this, &x, &y);
396         SDL_PrivateMouseMotion(0, 0, x, y);
397 }
398
399 static int xkey_cb(void *cb_arg, int kc, int is_pressed)
400 {
401         SDL_VideoDevice *this = cb_arg;
402         struct SDL_PrivateVideoData *pdata = this->hidden;
403         int ret;
404
405         if (kc == XF86XK_MenuKB && is_pressed) {
406                 ret = osdl_video_pause(pdata, 1);
407                 if (ret == 0) {
408                         xenv_minimize();
409                         osdl_video_pause(pdata, 0);
410                         omapsdl_input_get_events(0, NULL, NULL, NULL);
411                 }
412         }
413 }
414
415 static void omap_PumpEvents(SDL_VideoDevice *this) 
416 {
417         struct SDL_PrivateVideoData *pdata = this->hidden;
418         int read_tslib = 1;
419
420         trace();
421
422         if (pdata->xenv_up) {
423                 if (!pdata->cfg_ts_force_tslib) {
424                         xenv_update(xkey_cb, xmouseb_event_cb, xmousem_event_cb, this);
425                         if (pdata->xenv_mouse)
426                                 read_tslib = 0;
427                 }
428                 else {
429                         /* just flush X event queue */
430                         xenv_update(NULL, NULL, NULL, NULL);
431                 }
432         }
433
434         omapsdl_input_get_events(0, key_event_cb,
435                 read_tslib ? ts_event_cb : NULL, this);
436 }
437
438 static SDL_VideoDevice *omap_create(int devindex)
439 {
440         SDL_VideoDevice *this;
441
442         this = calloc(1, sizeof(*this) + sizeof(*this->hidden));
443         if (this == NULL) {
444                 SDL_OutOfMemory();
445                 return 0;
446         }
447         this->hidden = (void *)(this + 1);
448         this->VideoInit = omap_VideoInit;
449         this->ListModes = omap_ListModes;
450         this->SetVideoMode = omap_SetVideoMode;
451         this->LockHWSurface = omap_LockHWSurface;
452         this->UnlockHWSurface = omap_UnlockHWSurface;
453         this->FlipHWSurface = omap_FlipHWSurface;
454         this->AllocHWSurface = omap_AllocHWSurface;
455         this->FreeHWSurface = omap_FreeHWSurface;
456         this->SetColors = omap_SetColors;
457         this->UpdateRects = omap_UpdateRects;
458         this->VideoQuit = omap_VideoQuit;
459         this->InitOSKeymap = omap_InitOSKeymap;
460         this->PumpEvents = omap_PumpEvents;
461         this->free = omap_free;
462
463         return this;
464 }
465
466 VideoBootStrap omapdss_bootstrap = {
467         "omapdss", "OMAP DSS2 Framebuffer Driver",
468         omap_available, omap_create
469 };
470