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