1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2012 Sam Lantinga
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19    Sam Lantinga
20    slouken@libsdl.org
21*/
22#include "SDL_config.h"
23
24/* Qtopia based framebuffer implementation */
25
26#include <unistd.h>
27
28#include <qapplication.h>
29#include <qpe/qpeapplication.h>
30
31#include "SDL_timer.h"
32
33#include "SDL_QWin.h"
34
35extern "C" {
36
37#include "../SDL_sysvideo.h"
38#include "../../events/SDL_events_c.h"
39#include "SDL_sysevents_c.h"
40#include "SDL_sysmouse_c.h"
41#include "SDL_syswm_c.h"
42#include "SDL_lowvideo.h"
43
44  //#define QTOPIA_DEBUG
45#define QT_HIDDEN_SIZE	32	/* starting hidden window size */
46
47  /* Name of the environment variable used to invert the screen rotation or not:
48     Possible values:
49     !=0 : Screen is 270� rotated
50     0: Screen is 90� rotated*/
51#define SDL_QT_ROTATION_ENV_NAME "SDL_QT_INVERT_ROTATION"
52
53  /* Initialization/Query functions */
54  static int QT_VideoInit(_THIS, SDL_PixelFormat *vformat);
55  static SDL_Rect **QT_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags);
56  static SDL_Surface *QT_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
57  static void QT_UpdateMouse(_THIS);
58  static int QT_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors);
59  static void QT_VideoQuit(_THIS);
60
61  /* Hardware surface functions */
62  static int QT_AllocHWSurface(_THIS, SDL_Surface *surface);
63  static int QT_LockHWSurface(_THIS, SDL_Surface *surface);
64  static void QT_UnlockHWSurface(_THIS, SDL_Surface *surface);
65  static void QT_FreeHWSurface(_THIS, SDL_Surface *surface);
66
67  static int QT_ToggleFullScreen(_THIS, int fullscreen);
68
69  static int QT_IconifyWindow(_THIS);
70  static SDL_GrabMode QT_GrabInput(_THIS, SDL_GrabMode mode);
71
72  /* FB driver bootstrap functions */
73
74  static int QT_Available(void)
75  {
76    return(1);
77  }
78
79  static void QT_DeleteDevice(SDL_VideoDevice *device)
80  {
81    SDL_free(device->hidden);
82    SDL_free(device);
83  }
84
85  static SDL_VideoDevice *QT_CreateDevice(int devindex)
86  {
87    SDL_VideoDevice *device;
88
89    /* Initialize all variables that we clean on shutdown */
90    device = (SDL_VideoDevice *)SDL_malloc(sizeof(SDL_VideoDevice));
91    if ( device ) {
92      SDL_memset(device, 0, (sizeof *device));
93      device->hidden = (struct SDL_PrivateVideoData *)
94	SDL_malloc((sizeof *device->hidden));
95    }
96    if ( (device == NULL) || (device->hidden == NULL) ) {
97      SDL_OutOfMemory();
98      if ( device ) {
99	SDL_free(device);
100      }
101      return(0);
102    }
103    SDL_memset(device->hidden, 0, (sizeof *device->hidden));
104
105    /* Set the function pointers */
106    device->VideoInit = QT_VideoInit;
107    device->ListModes = QT_ListModes;
108    device->SetVideoMode = QT_SetVideoMode;
109    device->UpdateMouse = QT_UpdateMouse;
110    device->SetColors = QT_SetColors;
111    device->UpdateRects = NULL;
112    device->VideoQuit = QT_VideoQuit;
113    device->AllocHWSurface = QT_AllocHWSurface;
114    device->CheckHWBlit = NULL;
115    device->FillHWRect = NULL;
116    device->SetHWColorKey = NULL;
117    device->SetHWAlpha = NULL;
118    device->LockHWSurface = QT_LockHWSurface;
119    device->UnlockHWSurface = QT_UnlockHWSurface;
120    device->FlipHWSurface = NULL;
121    device->FreeHWSurface = QT_FreeHWSurface;
122    device->SetIcon = NULL;
123    device->SetCaption = QT_SetWMCaption;
124    device->IconifyWindow = QT_IconifyWindow;
125    device->GrabInput = QT_GrabInput;
126    device->GetWMInfo = NULL;
127    device->FreeWMCursor = QT_FreeWMCursor;
128    device->CreateWMCursor = QT_CreateWMCursor;
129    device->ShowWMCursor = QT_ShowWMCursor;
130    device->WarpWMCursor = QT_WarpWMCursor;
131    device->InitOSKeymap = QT_InitOSKeymap;
132    device->PumpEvents = QT_PumpEvents;
133
134    device->free = QT_DeleteDevice;
135    device->ToggleFullScreen = QT_ToggleFullScreen;
136
137    /* Set the driver flags */
138    device->handles_any_size = 0;
139
140    return device;
141  }
142
143  VideoBootStrap Qtopia_bootstrap = {
144    "qtopia", "Qtopia / QPE graphics",
145    QT_Available, QT_CreateDevice
146  };
147
148  /* Function to sort the display_list */
149  static int CompareModes(const void *A, const void *B)
150  {
151#if 0
152    const display_mode *a = (display_mode *)A;
153    const display_mode *b = (display_mode *)B;
154
155    if ( a->space == b->space ) {
156      return((b->virtual_width*b->virtual_height)-
157	     (a->virtual_width*a->virtual_height));
158    } else {
159      return(ColorSpaceToBitsPerPixel(b->space)-
160	     ColorSpaceToBitsPerPixel(a->space));
161    }
162#endif
163    return 0;
164  }
165
166  /* Yes, this isn't the fastest it could be, but it works nicely */
167  static int QT_AddMode(_THIS, int index, unsigned int w, unsigned int h)
168  {
169    SDL_Rect *mode;
170    int i;
171    int next_mode;
172
173    /* Check to see if we already have this mode */
174    if ( SDL_nummodes[index] > 0 ) {
175      for ( i=SDL_nummodes[index]-1; i >= 0; --i ) {
176	mode = SDL_modelist[index][i];
177	if ( (mode->w == w) && (mode->h == h) ) {
178	  return(0);
179	}
180      }
181    }
182
183    /* Set up the new video mode rectangle */
184    mode = (SDL_Rect *)SDL_malloc(sizeof *mode);
185    if ( mode == NULL ) {
186      SDL_OutOfMemory();
187      return(-1);
188    }
189    mode->x = 0;
190    mode->y = 0;
191    mode->w = w;
192    mode->h = h;
193#ifdef QTOPIA_DEBUG
194    fprintf(stderr, "Adding mode %dx%d at %d bytes per pixel\n", w, h, index+1);
195#endif
196
197    /* Allocate the new list of modes, and fill in the new mode */
198    next_mode = SDL_nummodes[index];
199    SDL_modelist[index] = (SDL_Rect **)
200      SDL_realloc(SDL_modelist[index], (1+next_mode+1)*sizeof(SDL_Rect *));
201    if ( SDL_modelist[index] == NULL ) {
202      SDL_OutOfMemory();
203      SDL_nummodes[index] = 0;
204      SDL_free(mode);
205      return(-1);
206    }
207    SDL_modelist[index][next_mode] = mode;
208    SDL_modelist[index][next_mode+1] = NULL;
209    SDL_nummodes[index]++;
210
211    return(0);
212  }
213
214  int QT_VideoInit(_THIS, SDL_PixelFormat *vformat)
215  {
216    /* Initialize the QPE Application  */
217     /* Determine the screen depth */
218    vformat->BitsPerPixel = QPixmap::defaultDepth();
219
220    // For now we hardcode the current depth because anything else
221    // might as well be emulated by SDL rather than by Qtopia.
222
223    QSize desktop_size = qApp->desktop()->size();
224    QT_AddMode(_this, ((vformat->BitsPerPixel+7)/8)-1,
225	       desktop_size.width(), desktop_size.height());
226    QT_AddMode(_this, ((vformat->BitsPerPixel+7)/8)-1,
227	       desktop_size.height(), desktop_size.width());
228
229    /* Determine the current screen size */
230    _this->info.current_w = desktop_size.width();
231    _this->info.current_h = desktop_size.height();
232
233    /* Create the window / widget */
234    SDL_Win = new SDL_QWin(QSize(QT_HIDDEN_SIZE, QT_HIDDEN_SIZE));
235    ((QPEApplication*)qApp)->showMainWidget(SDL_Win);
236    /* Fill in some window manager capabilities */
237    _this->info.wm_available = 0;
238
239    /* We're done! */
240    return(0);
241  }
242
243  /* We support any dimension at our bit-depth */
244  SDL_Rect **QT_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags)
245  {
246    SDL_Rect **modes;
247
248    modes = ((SDL_Rect **)0);
249    if ( (flags & SDL_FULLSCREEN) == SDL_FULLSCREEN ) {
250      modes = SDL_modelist[((format->BitsPerPixel+7)/8)-1];
251    } else {
252      if ( format->BitsPerPixel ==
253	   _this->screen->format->BitsPerPixel ) {
254	modes = ((SDL_Rect **)-1);
255      }
256    }
257    return(modes);
258  }
259
260  /* Various screen update functions available */
261  static void QT_NormalUpdate(_THIS, int numrects, SDL_Rect *rects);
262
263
264  static int QT_SetFullScreen(_THIS, SDL_Surface *screen, int fullscreen)
265  {
266    return -1;
267  }
268
269  static int QT_ToggleFullScreen(_THIS, int fullscreen)
270  {
271    return -1;
272  }
273
274  /* FIXME: check return values and cleanup here */
275  SDL_Surface *QT_SetVideoMode(_THIS, SDL_Surface *current,
276			       int width, int height, int bpp, Uint32 flags)
277  {
278
279    QImage *qimage;
280    QSize desktop_size = qApp->desktop()->size();
281
282
283    current->flags = 0; //SDL_FULLSCREEN; // We always run fullscreen.
284
285    if(width <= desktop_size.width()
286	      && height <= desktop_size.height()) {
287      current->w = desktop_size.width();
288      current->h = desktop_size.height();
289    } else if(width <= desktop_size.height() && height <= desktop_size.width()) {
290      // Landscape mode
291      char * envString = SDL_getenv(SDL_QT_ROTATION_ENV_NAME);
292      int envValue = envString ? atoi(envString) : 0;
293      screenRotation = envValue ? SDL_QT_ROTATION_270 : SDL_QT_ROTATION_90;
294      current->h = desktop_size.width();
295      current->w = desktop_size.height();
296    } else {
297      SDL_SetError("Unsupported resolution, %dx%d\n", width, height);
298    }
299    if ( flags & SDL_OPENGL ) {
300      SDL_SetError("OpenGL not supported");
301      return(NULL);
302    }
303    /* Create the QImage framebuffer */
304    qimage = new QImage(current->w, current->h, bpp);
305    if (qimage->isNull()) {
306      SDL_SetError("Couldn't create screen bitmap");
307      delete qimage;
308      return(NULL);
309    }
310    current->pitch = qimage->bytesPerLine();
311    current->pixels = (void *)qimage->bits();
312    SDL_Win->setImage(qimage);
313    _this->UpdateRects = QT_NormalUpdate;
314    SDL_Win->setFullscreen(true);
315    /* We're done */
316    return(current);
317  }
318
319  /* Update the current mouse state and position */
320  void QT_UpdateMouse(_THIS)
321  {
322    QPoint point(-1, -1);
323    if ( SDL_Win->isActiveWindow() ) {
324      point = SDL_Win->mousePos();
325    }
326
327    if ( (point.x() >= 0) && (point.x() < SDL_VideoSurface->w) &&
328	 (point.y() >= 0) && (point.y() < SDL_VideoSurface->h) ) {
329      SDL_PrivateAppActive(1, SDL_APPMOUSEFOCUS);
330      SDL_PrivateMouseMotion(0, 0,
331			     (Sint16)point.x(), (Sint16)point.y());
332    } else {
333      SDL_PrivateAppActive(0, SDL_APPMOUSEFOCUS);
334    }
335  }
336
337  /* We don't actually allow hardware surfaces other than the main one */
338  static int QT_AllocHWSurface(_THIS, SDL_Surface *surface)
339  {
340    return(-1);
341  }
342  static void QT_FreeHWSurface(_THIS, SDL_Surface *surface)
343  {
344    return;
345  }
346  static int QT_LockHWSurface(_THIS, SDL_Surface *surface)
347  {
348    return(0);
349  }
350  static void QT_UnlockHWSurface(_THIS, SDL_Surface *surface)
351  {
352    return;
353  }
354
355  static void QT_NormalUpdate(_THIS, int numrects, SDL_Rect *rects)
356  {
357    if(SDL_Win->lockScreen()) {
358      for(int i=0; i<numrects; ++i ) {
359	QRect rect(rects[i].x, rects[i].y,
360		   rects[i].w, rects[i].h);
361	SDL_Win->repaintRect(rect);
362      }
363      SDL_Win->unlockScreen();
364    }
365  }
366  /* Is the system palette settable? */
367  int QT_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
368  {
369    return -1;
370  }
371
372  void QT_VideoQuit(_THIS)
373  {
374    // This is dumb, but if I free this, the app doesn't exit correctly.
375    // Of course, this will leak memory if init video is done more than once.
376    // Sucks but such is life.
377
378    //    -- David Hedbor
379    //    delete SDL_Win;
380    //    SDL_Win = 0;
381    _this->screen->pixels = NULL;
382    QT_GrabInput(_this, SDL_GRAB_OFF);
383  }
384
385  static int QT_IconifyWindow(_THIS) {
386    SDL_Win->hide();
387
388    return true;
389  }
390
391  static SDL_GrabMode QT_GrabInput(_THIS, SDL_GrabMode mode) {
392    if(mode == SDL_GRAB_OFF) {
393      QPEApplication::grabKeyboard();
394      qApp->processEvents();
395      QPEApplication::ungrabKeyboard();
396    } else {
397      QPEApplication::grabKeyboard();
398    }
399    qApp->processEvents();
400    return mode;
401  }
402
403}; /* Extern C */
404