fix incorrect assumption, minor refactoring
[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         int fullscreen_blit = 0;
255         int i, Bpp, x, y, w, h;
256         char *src, *dst;
257
258         trace("%d, %p", nrects, rects);
259
260         fullscreen_blit =
261                 nrects == 1 && rects->x == 0 && rects->y == 0
262                     && (rects->w == screen->w || rects->w == 0)
263                     && (rects->h == screen->h || rects->h == 0);
264
265         if (screen->flags & SDL_DOUBLEBUF) {
266                 if (fullscreen_blit && !pdata->app_uses_flip)
267                         screen->pixels = osdl_video_flip(pdata);
268                 return;
269         }
270
271         src = screen->pixels;
272         dst = pdata->front_buffer;
273         if (src == dst)
274                 return;
275
276         if (fullscreen_blit) {
277                 memcpy(dst, src, screen->pitch * screen->h);
278                 return;
279         }
280
281         for (i = 0, Bpp = screen->format->BytesPerPixel; i < nrects; i++) {
282                 /* this supposedly has no clipping, but we'll do it anyway */
283                 x = rects[i].x, y = rects[i].y, w = rects[i].w, h = rects[i].h;
284                 if (x < 0)
285                         w += x, x = 0;
286                 else if (x + w > screen->w)
287                         w = screen->w - x;
288                 if (w <= 0)
289                         continue;
290
291                 if (y < 0)
292                         h += y, y = 0;
293                 else if (y + h > screen->h)
294                         h = screen->h - y;
295
296                 for (; h > 0; y++, h--)
297                         memcpy(dst + y * screen->pitch + x * Bpp,
298                                src + y * screen->pitch + x * Bpp,
299                                w * Bpp);
300         }
301 }
302
303 static void omap_InitOSKeymap(SDL_VideoDevice *this)
304 {
305         trace();
306 }
307
308 static int key_event_cb(void *cb_arg, int sdl_kc, int sdl_sc, int is_pressed)
309 {
310         SDL_keysym keysym = { 0, };
311         int shift = 0;
312         SDLMod mod;
313         int ret;
314
315         keysym.sym = sdl_kc;
316         keysym.scancode = sdl_sc;
317
318         /* 0xff if pandora's Fn, so we exclude it too.. */
319         if (is_pressed && sdl_kc < 0xff && SDL_TranslateUNICODE) {
320                 mod = SDL_GetModState();
321                 if (!(mod & KMOD_CTRL) && (!!(mod & KMOD_SHIFT) ^ !!(mod & KMOD_CAPS)))
322                         shift = 1;
323
324                 /* prefer X mapping, if that doesn't work use hardcoded one */
325                 ret = xenv_keycode_to_keysym(sdl_sc, shift);
326                 if (ret >= 0) {
327                         keysym.unicode = ret;
328                         if ((mod & KMOD_CTRL)
329                             && 0x60 <= keysym.unicode && keysym.unicode <= 0x7f)
330                         {
331                                 keysym.unicode -= 0x60;
332                         }
333                         /* hmh.. */
334                         if ((keysym.unicode & 0xff00) == 0xff00)
335                                 keysym.unicode &= ~0xff00;
336                 }
337                 else {
338                         keysym.unicode = sdl_kc;
339                         if ((mod & KMOD_CTRL) && 0x60 <= sdl_kc && sdl_kc <= 0x7f)
340                         {
341                                 keysym.unicode = sdl_kc - 0x60;
342                         }
343                         else if (shift && 'a' <= sdl_kc && sdl_kc <= 'z')
344                         {
345                                 keysym.unicode = sdl_kc - 'a' + 'A';
346                         }
347                 }
348         }
349
350         SDL_PrivateKeyboard(is_pressed, &keysym);
351 }
352
353 /* clamp x to min..max-1 */
354 #define clamp(x, min, max) \
355         if (x < (min)) x = min; \
356         if (x >= (max)) x = max
357
358 static void translate_mouse(SDL_VideoDevice *this, int *x, int *y)
359 {
360         struct SDL_PrivateVideoData *pdata = this->hidden;
361
362         if (!pdata->cfg_no_ts_translate && pdata->layer_w != 0 && pdata->layer_h != 0) {
363                 *x = pdata->border_l + ((*x - pdata->layer_x) * pdata->ts_xmul >> 16);
364                 *y = pdata->border_t + ((*y - pdata->layer_y) * pdata->ts_ymul >> 16);
365                 clamp(*x, 0, this->screen->w);
366                 clamp(*y, 0, this->screen->h);
367         }
368 }
369
370 static int ts_event_cb(void *cb_arg, int x, int y, unsigned int pressure)
371 {
372         static int was_pressed;
373         SDL_VideoDevice *this = cb_arg;
374         struct SDL_PrivateVideoData *pdata = this->hidden;
375
376         translate_mouse(this, &x, &y);
377
378         pressure = !!pressure;
379         if (pressure != was_pressed) {
380                 SDL_PrivateMouseButton(pressure ? SDL_PRESSED : SDL_RELEASED, 1, x, y);
381                 was_pressed = pressure;
382         }
383         else
384                 SDL_PrivateMouseMotion(0, 0, x, y);
385 }
386
387 static int xmouseb_event_cb(void *cb_arg, int x, int y, int button, int is_pressed)
388 {
389         SDL_VideoDevice *this = cb_arg;
390         struct SDL_PrivateVideoData *pdata = this->hidden;
391
392         translate_mouse(this, &x, &y);
393         SDL_PrivateMouseButton(is_pressed ? SDL_PRESSED : SDL_RELEASED, button, x, y);
394 }
395
396 static int xmousem_event_cb(void *cb_arg, int x, int y)
397 {
398         SDL_VideoDevice *this = cb_arg;
399         struct SDL_PrivateVideoData *pdata = this->hidden;
400
401         translate_mouse(this, &x, &y);
402         SDL_PrivateMouseMotion(0, 0, x, y);
403 }
404
405 static int xkey_cb(void *cb_arg, int kc, int is_pressed)
406 {
407         SDL_VideoDevice *this = cb_arg;
408         struct SDL_PrivateVideoData *pdata = this->hidden;
409         int ret;
410
411         if (kc == XF86XK_MenuKB && is_pressed) {
412                 ret = osdl_video_pause(pdata, 1);
413                 if (ret == 0) {
414                         xenv_minimize();
415                         osdl_video_pause(pdata, 0);
416                         omapsdl_input_get_events(0, NULL, NULL, NULL);
417                 }
418         }
419 }
420
421 static void omap_PumpEvents(SDL_VideoDevice *this) 
422 {
423         struct SDL_PrivateVideoData *pdata = this->hidden;
424         int read_tslib = 1;
425
426         trace();
427
428         if (pdata->xenv_up) {
429                 if (!pdata->cfg_ts_force_tslib) {
430                         xenv_update(xkey_cb, xmouseb_event_cb, xmousem_event_cb, this);
431                         if (pdata->xenv_mouse)
432                                 read_tslib = 0;
433                 }
434                 else {
435                         /* just flush X event queue */
436                         xenv_update(NULL, NULL, NULL, NULL);
437                 }
438         }
439
440         omapsdl_input_get_events(0, key_event_cb,
441                 read_tslib ? ts_event_cb : NULL, this);
442 }
443
444 static SDL_VideoDevice *omap_create(int devindex)
445 {
446         SDL_VideoDevice *this;
447
448         this = calloc(1, sizeof(*this) + sizeof(*this->hidden));
449         if (this == NULL) {
450                 SDL_OutOfMemory();
451                 return 0;
452         }
453         this->hidden = (void *)(this + 1);
454         this->VideoInit = omap_VideoInit;
455         this->ListModes = omap_ListModes;
456         this->SetVideoMode = omap_SetVideoMode;
457         this->LockHWSurface = omap_LockHWSurface;
458         this->UnlockHWSurface = omap_UnlockHWSurface;
459         this->FlipHWSurface = omap_FlipHWSurface;
460         this->AllocHWSurface = omap_AllocHWSurface;
461         this->FreeHWSurface = omap_FreeHWSurface;
462         this->SetColors = omap_SetColors;
463         this->UpdateRects = omap_UpdateRects;
464         this->VideoQuit = omap_VideoQuit;
465         this->InitOSKeymap = omap_InitOSKeymap;
466         this->PumpEvents = omap_PumpEvents;
467         this->free = omap_free;
468
469         return this;
470 }
471
472 VideoBootStrap omapdss_bootstrap = {
473         "omapdss", "OMAP DSS2 Framebuffer Driver",
474         omap_available, omap_create
475 };
476