Merge branch 'blender2.7'
[blender.git] / intern / ghost / intern / GHOST_SystemSDL.cpp
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file \ingroup GHOST
18  */
19
20 #include <assert.h>
21
22 #include "GHOST_ContextSDL.h"
23 #include "GHOST_SystemSDL.h"
24 #include "GHOST_WindowSDL.h"
25
26 #include "GHOST_WindowManager.h"
27
28 #include "GHOST_EventCursor.h"
29 #include "GHOST_EventKey.h"
30 #include "GHOST_EventButton.h"
31 #include "GHOST_EventWheel.h"
32
33 GHOST_SystemSDL::GHOST_SystemSDL()
34     :
35       GHOST_System()
36 {
37         if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) {
38                 printf("Error initializing SDL:  %s\n", SDL_GetError());
39         }
40
41         /* SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); */
42         /* SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); */
43         SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
44         SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
45         SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
46         SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
47         SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
48         SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
49 }
50
51 GHOST_SystemSDL::~GHOST_SystemSDL()
52 {
53         SDL_Quit();
54 }
55
56 GHOST_IWindow *
57 GHOST_SystemSDL::createWindow(const STR_String& title,
58                               GHOST_TInt32 left,
59                               GHOST_TInt32 top,
60                               GHOST_TUns32 width,
61                               GHOST_TUns32 height,
62                               GHOST_TWindowState state,
63                               GHOST_TDrawingContextType type,
64                               GHOST_GLSettings glSettings,
65                               const bool exclusive,
66                               const GHOST_TEmbedderWindowID parentWindow
67                               )
68 {
69         GHOST_WindowSDL *window = NULL;
70
71         window = new GHOST_WindowSDL(this, title,
72                                      left, top, width, height,
73                                      state, parentWindow, type,
74                                      ((glSettings.flags & GHOST_glStereoVisual) != 0), exclusive,
75                                      glSettings.numOfAASamples);
76
77         if (window) {
78                 if (GHOST_kWindowStateFullScreen == state) {
79                         SDL_Window *sdl_win = window->getSDLWindow();
80                         SDL_DisplayMode mode;
81
82                         static_cast<GHOST_DisplayManagerSDL *> (m_displayManager)->getCurrentDisplayModeSDL(mode);
83
84                         SDL_SetWindowDisplayMode(sdl_win, &mode);
85                         SDL_ShowWindow(sdl_win);
86                         SDL_SetWindowFullscreen(sdl_win, SDL_TRUE);
87                 }
88
89                 if (window->getValid()) {
90                         m_windowManager->addWindow(window);
91                         pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
92                 }
93                 else {
94                         delete window;
95                         window = NULL;
96                 }
97         }
98         return window;
99 }
100
101 GHOST_TSuccess
102 GHOST_SystemSDL::init() {
103         GHOST_TSuccess success = GHOST_System::init();
104
105         if (success) {
106                 m_displayManager = new GHOST_DisplayManagerSDL(this);
107
108                 if (m_displayManager) {
109                         return GHOST_kSuccess;
110                 }
111         }
112
113         return GHOST_kFailure;
114 }
115
116 /**
117  * Returns the dimensions of the main display on this system.
118  * \return The dimension of the main display.
119  */
120 void
121 GHOST_SystemSDL::getAllDisplayDimensions(GHOST_TUns32& width,
122                                          GHOST_TUns32& height) const
123 {
124         SDL_DisplayMode mode;
125         SDL_GetDesktopDisplayMode(0, &mode); /* note, always 0 display */
126         width = mode.w;
127         height = mode.h;
128 }
129
130 void
131 GHOST_SystemSDL::getMainDisplayDimensions(GHOST_TUns32& width,
132                                           GHOST_TUns32& height) const
133 {
134         SDL_DisplayMode mode;
135         SDL_GetCurrentDisplayMode(0, &mode); /* note, always 0 display */
136         width = mode.w;
137         height = mode.h;
138 }
139
140 GHOST_TUns8
141 GHOST_SystemSDL::getNumDisplays() const
142 {
143         return SDL_GetNumVideoDisplays();
144 }
145
146 GHOST_IContext *
147 GHOST_SystemSDL::createOffscreenContext()
148 {
149         GHOST_Context *context = new GHOST_ContextSDL(
150                 0,
151                 0,
152                 NULL,
153                 0, // profile bit
154                 3, 3,
155                 GHOST_OPENGL_SDL_CONTEXT_FLAGS,
156                 GHOST_OPENGL_SDL_RESET_NOTIFICATION_STRATEGY);
157
158         if (context->initializeDrawingContext())
159                 return context;
160         else
161                 delete context;
162
163         return NULL;
164 }
165
166 GHOST_TSuccess
167 GHOST_SystemSDL::disposeContext(GHOST_IContext *context)
168 {
169         delete context;
170
171         return GHOST_kSuccess;
172 }
173
174 GHOST_TSuccess
175 GHOST_SystemSDL::getModifierKeys(GHOST_ModifierKeys& keys) const
176 {
177         SDL_Keymod mod = SDL_GetModState();
178
179         keys.set(GHOST_kModifierKeyLeftShift,    (mod & KMOD_LSHIFT) != 0);
180         keys.set(GHOST_kModifierKeyRightShift,   (mod & KMOD_RSHIFT) != 0);
181         keys.set(GHOST_kModifierKeyLeftControl,  (mod & KMOD_LCTRL) != 0);
182         keys.set(GHOST_kModifierKeyRightControl, (mod & KMOD_RCTRL) != 0);
183         keys.set(GHOST_kModifierKeyLeftAlt,      (mod & KMOD_LALT) != 0);
184         keys.set(GHOST_kModifierKeyRightAlt,     (mod & KMOD_RALT) != 0);
185         keys.set(GHOST_kModifierKeyOS,           (mod & (KMOD_LGUI | KMOD_RGUI)) != 0);
186
187         return GHOST_kSuccess;
188 }
189
190 #define GXMAP(k, x, y) case x: k = y; break
191
192 static GHOST_TKey
193 convertSDLKey(SDL_Scancode key)
194 {
195         GHOST_TKey type;
196
197         if ((key >= SDL_SCANCODE_A) && (key <= SDL_SCANCODE_Z)) {
198                 type = GHOST_TKey(key - SDL_SCANCODE_A + int(GHOST_kKeyA));
199         }
200         else if ((key >= SDL_SCANCODE_1) && (key <= SDL_SCANCODE_0)) {
201                 type = (key == SDL_SCANCODE_0) ? GHOST_kKey0 : GHOST_TKey(key - SDL_SCANCODE_1 + int(GHOST_kKey1));
202         }
203         else if ((key >= SDL_SCANCODE_F1) && (key <= SDL_SCANCODE_F12)) {
204                 type = GHOST_TKey(key - SDL_SCANCODE_F1 + int(GHOST_kKeyF1));
205         }
206         else if ((key >= SDL_SCANCODE_F13) && (key <= SDL_SCANCODE_F24)) {
207                 type = GHOST_TKey(key - SDL_SCANCODE_F13 + int(GHOST_kKeyF13));
208         }
209         else {
210                 switch (key) {
211                         /* TODO SDL_SCANCODE_NONUSBACKSLASH */
212
213                         GXMAP(type, SDL_SCANCODE_BACKSPACE,      GHOST_kKeyBackSpace);
214                         GXMAP(type, SDL_SCANCODE_TAB,            GHOST_kKeyTab);
215                         GXMAP(type, SDL_SCANCODE_RETURN,         GHOST_kKeyEnter);
216                         GXMAP(type, SDL_SCANCODE_ESCAPE,         GHOST_kKeyEsc);
217                         GXMAP(type, SDL_SCANCODE_SPACE,          GHOST_kKeySpace);
218
219                         GXMAP(type, SDL_SCANCODE_SEMICOLON,      GHOST_kKeySemicolon);
220                         GXMAP(type, SDL_SCANCODE_PERIOD,         GHOST_kKeyPeriod);
221                         GXMAP(type, SDL_SCANCODE_COMMA,          GHOST_kKeyComma);
222                         GXMAP(type, SDL_SCANCODE_APOSTROPHE,     GHOST_kKeyQuote);
223                         GXMAP(type, SDL_SCANCODE_GRAVE,          GHOST_kKeyAccentGrave);
224                         GXMAP(type, SDL_SCANCODE_MINUS,          GHOST_kKeyMinus);
225                         GXMAP(type, SDL_SCANCODE_EQUALS,         GHOST_kKeyEqual);
226
227                         GXMAP(type, SDL_SCANCODE_SLASH,          GHOST_kKeySlash);
228                         GXMAP(type, SDL_SCANCODE_BACKSLASH,      GHOST_kKeyBackslash);
229                         GXMAP(type, SDL_SCANCODE_KP_EQUALS,      GHOST_kKeyEqual);
230                         GXMAP(type, SDL_SCANCODE_LEFTBRACKET,    GHOST_kKeyLeftBracket);
231                         GXMAP(type, SDL_SCANCODE_RIGHTBRACKET,   GHOST_kKeyRightBracket);
232                         GXMAP(type, SDL_SCANCODE_PAUSE,          GHOST_kKeyPause);
233
234                         GXMAP(type, SDL_SCANCODE_LSHIFT,         GHOST_kKeyLeftShift);
235                         GXMAP(type, SDL_SCANCODE_RSHIFT,         GHOST_kKeyRightShift);
236                         GXMAP(type, SDL_SCANCODE_LCTRL,          GHOST_kKeyLeftControl);
237                         GXMAP(type, SDL_SCANCODE_RCTRL,          GHOST_kKeyRightControl);
238                         GXMAP(type, SDL_SCANCODE_LALT,           GHOST_kKeyLeftAlt);
239                         GXMAP(type, SDL_SCANCODE_RALT,           GHOST_kKeyRightAlt);
240                         GXMAP(type, SDL_SCANCODE_LGUI,           GHOST_kKeyOS);
241                         GXMAP(type, SDL_SCANCODE_RGUI,           GHOST_kKeyOS);
242
243                         GXMAP(type, SDL_SCANCODE_INSERT,         GHOST_kKeyInsert);
244                         GXMAP(type, SDL_SCANCODE_DELETE,         GHOST_kKeyDelete);
245                         GXMAP(type, SDL_SCANCODE_HOME,           GHOST_kKeyHome);
246                         GXMAP(type, SDL_SCANCODE_END,            GHOST_kKeyEnd);
247                         GXMAP(type, SDL_SCANCODE_PAGEUP,         GHOST_kKeyUpPage);
248                         GXMAP(type, SDL_SCANCODE_PAGEDOWN,       GHOST_kKeyDownPage);
249
250                         GXMAP(type, SDL_SCANCODE_LEFT,           GHOST_kKeyLeftArrow);
251                         GXMAP(type, SDL_SCANCODE_RIGHT,          GHOST_kKeyRightArrow);
252                         GXMAP(type, SDL_SCANCODE_UP,             GHOST_kKeyUpArrow);
253                         GXMAP(type, SDL_SCANCODE_DOWN,           GHOST_kKeyDownArrow);
254
255                         GXMAP(type, SDL_SCANCODE_CAPSLOCK,       GHOST_kKeyCapsLock);
256                         GXMAP(type, SDL_SCANCODE_SCROLLLOCK,     GHOST_kKeyScrollLock);
257                         GXMAP(type, SDL_SCANCODE_NUMLOCKCLEAR,   GHOST_kKeyNumLock);
258                         GXMAP(type, SDL_SCANCODE_PRINTSCREEN,    GHOST_kKeyPrintScreen);
259
260                         /* keypad events */
261
262                         /* note, sdl defines a bunch of kp defines I never saw before like
263                          * SDL_SCANCODE_KP_PERCENT, SDL_SCANCODE_KP_XOR - campbell */
264                         GXMAP(type, SDL_SCANCODE_KP_0,           GHOST_kKeyNumpad0);
265                         GXMAP(type, SDL_SCANCODE_KP_1,           GHOST_kKeyNumpad1);
266                         GXMAP(type, SDL_SCANCODE_KP_2,           GHOST_kKeyNumpad2);
267                         GXMAP(type, SDL_SCANCODE_KP_3,           GHOST_kKeyNumpad3);
268                         GXMAP(type, SDL_SCANCODE_KP_4,           GHOST_kKeyNumpad4);
269                         GXMAP(type, SDL_SCANCODE_KP_5,           GHOST_kKeyNumpad5);
270                         GXMAP(type, SDL_SCANCODE_KP_6,           GHOST_kKeyNumpad6);
271                         GXMAP(type, SDL_SCANCODE_KP_7,           GHOST_kKeyNumpad7);
272                         GXMAP(type, SDL_SCANCODE_KP_8,           GHOST_kKeyNumpad8);
273                         GXMAP(type, SDL_SCANCODE_KP_9,           GHOST_kKeyNumpad9);
274                         GXMAP(type, SDL_SCANCODE_KP_PERIOD,      GHOST_kKeyNumpadPeriod);
275
276                         GXMAP(type, SDL_SCANCODE_KP_ENTER,       GHOST_kKeyNumpadEnter);
277                         GXMAP(type, SDL_SCANCODE_KP_PLUS,        GHOST_kKeyNumpadPlus);
278                         GXMAP(type, SDL_SCANCODE_KP_MINUS,       GHOST_kKeyNumpadMinus);
279                         GXMAP(type, SDL_SCANCODE_KP_MULTIPLY,    GHOST_kKeyNumpadAsterisk);
280                         GXMAP(type, SDL_SCANCODE_KP_DIVIDE,      GHOST_kKeyNumpadSlash);
281
282                         /* Media keys in some keyboards and laptops with XFree86/Xorg */
283                         GXMAP(type, SDL_SCANCODE_AUDIOPLAY,      GHOST_kKeyMediaPlay);
284                         GXMAP(type, SDL_SCANCODE_AUDIOSTOP,      GHOST_kKeyMediaStop);
285                         GXMAP(type, SDL_SCANCODE_AUDIOPREV,      GHOST_kKeyMediaFirst);
286                         // GXMAP(type,XF86XK_AudioRewind,       GHOST_kKeyMediaFirst);
287                         GXMAP(type, SDL_SCANCODE_AUDIONEXT,      GHOST_kKeyMediaLast);
288
289                         default:
290                                 printf("Unknown\n");
291                                 type = GHOST_kKeyUnknown;
292                                 break;
293                 }
294         }
295
296         return type;
297 }
298 #undef GXMAP
299
300 /**
301  * Events don't always have valid windows,
302  * but GHOST needs a window _always_. fallback to the GL window.
303  */
304 static SDL_Window *SDL_GetWindowFromID_fallback(Uint32 id)
305 {
306         SDL_Window *sdl_win = SDL_GetWindowFromID(id);
307         if (sdl_win == NULL) {
308                 sdl_win = SDL_GL_GetCurrentWindow();
309         }
310         return sdl_win;
311 }
312
313 void
314 GHOST_SystemSDL::processEvent(SDL_Event *sdl_event)
315 {
316         GHOST_Event *g_event = NULL;
317
318         switch (sdl_event->type) {
319                 case SDL_WINDOWEVENT:
320                 {
321                         SDL_WindowEvent &sdl_sub_evt = sdl_event->window;
322                         GHOST_WindowSDL *window = findGhostWindow(SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID));
323                         //assert(window != NULL); // can be NULL on close window.
324
325                         switch (sdl_sub_evt.event) {
326                                 case SDL_WINDOWEVENT_EXPOSED:
327                                         g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window);
328                                         break;
329                                 case SDL_WINDOWEVENT_RESIZED:
330                                         g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window);
331                                         break;
332                                 case SDL_WINDOWEVENT_MOVED:
333                                         g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, window);
334                                         break;
335                                 case SDL_WINDOWEVENT_FOCUS_GAINED:
336                                         g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window);
337                                         break;
338                                 case SDL_WINDOWEVENT_FOCUS_LOST:
339                                         g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window);
340                                         break;
341                                 case SDL_WINDOWEVENT_CLOSE:
342                                         g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window);
343                                         break;
344                         }
345
346                         break;
347                 }
348                 case SDL_QUIT:
349                         g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL);
350                         break;
351
352                 case SDL_MOUSEMOTION:
353                 {
354                         SDL_MouseMotionEvent &sdl_sub_evt = sdl_event->motion;
355                         SDL_Window *sdl_win = SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID);
356                         GHOST_WindowSDL *window = findGhostWindow(sdl_win);
357                         assert(window != NULL);
358
359                         int x_win, y_win;
360                         SDL_GetWindowPosition(sdl_win, &x_win, &y_win);
361
362                         GHOST_TInt32 x_root = sdl_sub_evt.x + x_win;
363                         GHOST_TInt32 y_root = sdl_sub_evt.y + y_win;
364
365 #if 0
366                         if (window->getCursorGrabMode() != GHOST_kGrabDisable && window->getCursorGrabMode() != GHOST_kGrabNormal)
367                         {
368                                 GHOST_TInt32 x_new = x_root;
369                                 GHOST_TInt32 y_new = y_root;
370                                 GHOST_TInt32 x_accum, y_accum;
371                                 GHOST_Rect bounds;
372
373                                 /* fallback to window bounds */
374                                 if (window->getCursorGrabBounds(bounds) == GHOST_kFailure)
375                                         window->getClientBounds(bounds);
376
377                                 /* could also clamp to screen bounds
378                                  * wrap with a window outside the view will fail atm  */
379                                 bounds.wrapPoint(x_new, y_new, 8); /* offset of one incase blender is at screen bounds */
380                                 window->getCursorGrabAccum(x_accum, y_accum);
381
382                                 // cant use setCursorPosition because the mouse may have no focus!
383                                 if (x_new != x_root || y_new != y_root) {
384                                         if (1) {  //xme.time > m_last_warp) {
385                                                 /* when wrapping we don't need to add an event because the
386                                                  * setCursorPosition call will cause a new event after */
387                                                 SDL_WarpMouseInWindow(sdl_win, x_new - x_win, y_new - y_win); /* wrap */
388                                                 window->setCursorGrabAccum(x_accum + (x_root - x_new), y_accum + (y_root - y_new));
389                                                 // m_last_warp= lastEventTime(xme.time);
390                                         }
391                                         else {
392                                                 // setCursorPosition(x_new, y_new); /* wrap but don't accumulate */
393                                                 SDL_WarpMouseInWindow(sdl_win, x_new - x_win, y_new - y_win);
394                                         }
395
396                                         g_event = new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, x_new, y_new);
397                                 }
398                                 else {
399                                         g_event = new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, x_root + x_accum, y_root + y_accum);
400                                 }
401                         }
402                         else
403 #endif
404                         {
405                                 g_event = new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, x_root, y_root);
406                         }
407                         break;
408                 }
409                 case SDL_MOUSEBUTTONUP:
410                 case SDL_MOUSEBUTTONDOWN:
411                 {
412                         SDL_MouseButtonEvent &sdl_sub_evt = sdl_event->button;
413                         GHOST_TButtonMask gbmask = GHOST_kButtonMaskLeft;
414                         GHOST_TEventType type = (sdl_sub_evt.state == SDL_PRESSED) ? GHOST_kEventButtonDown : GHOST_kEventButtonUp;
415
416                         GHOST_WindowSDL *window = findGhostWindow(SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID));
417                         assert(window != NULL);
418
419                         /* process rest of normal mouse buttons */
420                         if (sdl_sub_evt.button == SDL_BUTTON_LEFT)
421                                 gbmask = GHOST_kButtonMaskLeft;
422                         else if (sdl_sub_evt.button == SDL_BUTTON_MIDDLE)
423                                 gbmask = GHOST_kButtonMaskMiddle;
424                         else if (sdl_sub_evt.button == SDL_BUTTON_RIGHT)
425                                 gbmask = GHOST_kButtonMaskRight;
426                         /* these buttons are untested! */
427                         else if (sdl_sub_evt.button == SDL_BUTTON_X1)
428                                 gbmask = GHOST_kButtonMaskButton4;
429                         else if (sdl_sub_evt.button == SDL_BUTTON_X2)
430                                 gbmask = GHOST_kButtonMaskButton5;
431                         else
432                                 break;
433
434                         g_event = new GHOST_EventButton(getMilliSeconds(), type, window, gbmask);
435                         break;
436                 }
437                 case SDL_MOUSEWHEEL:
438                 {
439                         SDL_MouseWheelEvent &sdl_sub_evt = sdl_event->wheel;
440                         GHOST_WindowSDL *window = findGhostWindow(SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID));
441                         assert(window != NULL);
442                         g_event = new GHOST_EventWheel(getMilliSeconds(), window, sdl_sub_evt.y);
443                         break;
444                 }
445                 case SDL_KEYDOWN:
446                 case SDL_KEYUP:
447                 {
448                         SDL_KeyboardEvent &sdl_sub_evt = sdl_event->key;
449                         SDL_Keycode sym = sdl_sub_evt.keysym.sym;
450                         GHOST_TEventType type = (sdl_sub_evt.state == SDL_PRESSED) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp;
451
452                         GHOST_WindowSDL *window = findGhostWindow(SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID));
453                         assert(window != NULL);
454
455                         GHOST_TKey gkey = convertSDLKey(sdl_sub_evt.keysym.scancode);
456                         /* note, the sdl_sub_evt.keysym.sym is truncated, for unicode support ghost has to be modified */
457                         /* printf("%d\n", sym); */
458                         if (sym > 127) {
459                                 switch (sym) {
460                                         case SDLK_KP_DIVIDE: sym = '/'; break;
461                                         case SDLK_KP_MULTIPLY: sym = '*'; break;
462                                         case SDLK_KP_MINUS: sym = '-'; break;
463                                         case SDLK_KP_PLUS: sym = '+'; break;
464                                         case SDLK_KP_1: sym = '1'; break;
465                                         case SDLK_KP_2: sym = '2'; break;
466                                         case SDLK_KP_3: sym = '3'; break;
467                                         case SDLK_KP_4: sym = '4'; break;
468                                         case SDLK_KP_5: sym = '5'; break;
469                                         case SDLK_KP_6: sym = '6'; break;
470                                         case SDLK_KP_7: sym = '7'; break;
471                                         case SDLK_KP_8: sym = '8'; break;
472                                         case SDLK_KP_9: sym = '9'; break;
473                                         case SDLK_KP_0: sym = '0'; break;
474                                         case SDLK_KP_PERIOD: sym = '.'; break;
475                                         default: sym = 0; break;
476                                 }
477                         }
478                         else {
479                                 if (sdl_sub_evt.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) {
480                                         /* lame US keyboard assumptions */
481                                         if (sym >= 'a' && sym <= ('a' + 32)) {
482                                                 sym -= 32;
483                                         }
484                                         else {
485                                                 switch (sym) {
486                                                         case '`': sym = '~'; break;
487                                                         case '1': sym = '!'; break;
488                                                         case '2': sym = '@'; break;
489                                                         case '3': sym = '#'; break;
490                                                         case '4': sym = '$'; break;
491                                                         case '5': sym = '%'; break;
492                                                         case '6': sym = '^'; break;
493                                                         case '7': sym = '&'; break;
494                                                         case '8': sym = '*'; break;
495                                                         case '9': sym = '('; break;
496                                                         case '0': sym = ')'; break;
497                                                         case '-': sym = '_'; break;
498                                                         case '=': sym = '+'; break;
499                                                         case '[': sym = '{'; break;
500                                                         case ']': sym = '}'; break;
501                                                         case '\\': sym = '|'; break;
502                                                         case ';': sym = ':'; break;
503                                                         case '\'': sym = '"'; break;
504                                                         case ',': sym = '<'; break;
505                                                         case '.': sym = '>'; break;
506                                                         case '/': sym = '?'; break;
507                                                         default:            break;
508                                                 }
509                                         }
510                                 }
511                         }
512
513                         g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, sym, NULL);
514                         break;
515                 }
516         }
517
518         if (g_event) {
519                 pushEvent(g_event);
520         }
521 }
522
523 GHOST_TSuccess
524 GHOST_SystemSDL::getCursorPosition(GHOST_TInt32& x,
525                                    GHOST_TInt32& y) const
526 {
527         int x_win, y_win;
528         SDL_Window *win = SDL_GetMouseFocus();
529         SDL_GetWindowPosition(win, &x_win, &y_win);
530
531         int xi, yi;
532         SDL_GetMouseState(&xi, &yi);
533         x = xi + x_win;
534         y = yi + x_win;
535
536         return GHOST_kSuccess;
537 }
538
539 GHOST_TSuccess
540 GHOST_SystemSDL::setCursorPosition(GHOST_TInt32 x,
541                                    GHOST_TInt32 y)
542 {
543         int x_win, y_win;
544         SDL_Window *win = SDL_GetMouseFocus();
545         SDL_GetWindowPosition(win, &x_win, &y_win);
546
547         SDL_WarpMouseInWindow(win, x - x_win, y - y_win);
548         return GHOST_kSuccess;
549 }
550
551 bool
552 GHOST_SystemSDL::generateWindowExposeEvents()
553 {
554         std::vector<GHOST_WindowSDL *>::iterator w_start = m_dirty_windows.begin();
555         std::vector<GHOST_WindowSDL *>::const_iterator w_end = m_dirty_windows.end();
556         bool anyProcessed = false;
557
558         for (; w_start != w_end; ++w_start) {
559                 GHOST_Event *g_event = new
560                                        GHOST_Event(
561                                            getMilliSeconds(),
562                                            GHOST_kEventWindowUpdate,
563                                            *w_start
564                                            );
565
566                 (*w_start)->validate();
567
568                 if (g_event) {
569                         //printf("Expose events pushed\n");
570                         pushEvent(g_event);
571                         anyProcessed = true;
572                 }
573         }
574
575         m_dirty_windows.clear();
576         return anyProcessed;
577 }
578
579
580 bool
581 GHOST_SystemSDL::processEvents(bool waitForEvent)
582 {
583         // Get all the current events -- translate them into
584         // ghost events and call base class pushEvent() method.
585
586         bool anyProcessed = false;
587
588         do {
589                 GHOST_TimerManager *timerMgr = getTimerManager();
590
591                 if (waitForEvent && m_dirty_windows.empty() && !SDL_HasEvents(SDL_FIRSTEVENT, SDL_LASTEVENT)) {
592                         GHOST_TUns64 next = timerMgr->nextFireTime();
593
594                         if (next == GHOST_kFireTimeNever) {
595                                 SDL_WaitEventTimeout(NULL, -1);
596                                 //SleepTillEvent(m_display, -1);
597                         }
598                         else {
599                                 GHOST_TInt64 maxSleep = next - getMilliSeconds();
600
601                                 if (maxSleep >= 0) {
602                                         SDL_WaitEventTimeout(NULL, next - getMilliSeconds());
603                                         // SleepTillEvent(m_display, next - getMilliSeconds()); // X11
604                                 }
605                         }
606                 }
607
608                 if (timerMgr->fireTimers(getMilliSeconds())) {
609                         anyProcessed = true;
610                 }
611
612                 SDL_Event sdl_event;
613                 while (SDL_PollEvent(&sdl_event)) {
614                         processEvent(&sdl_event);
615                         anyProcessed = true;
616                 }
617
618                 if (generateWindowExposeEvents()) {
619                         anyProcessed = true;
620                 }
621         } while (waitForEvent && !anyProcessed);
622
623         return anyProcessed;
624 }
625
626
627 GHOST_WindowSDL *
628 GHOST_SystemSDL::findGhostWindow(SDL_Window *sdl_win)
629 {
630         if (sdl_win == NULL) return NULL;
631
632         // It is not entirely safe to do this as the backptr may point
633         // to a window that has recently been removed.
634         // We should always check the window manager's list of windows
635         // and only process events on these windows.
636
637         std::vector<GHOST_IWindow *> & win_vec = m_windowManager->getWindows();
638
639         std::vector<GHOST_IWindow *>::iterator win_it = win_vec.begin();
640         std::vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
641
642         for (; win_it != win_end; ++win_it) {
643                 GHOST_WindowSDL *window = static_cast<GHOST_WindowSDL *>(*win_it);
644                 if (window->getSDLWindow() == sdl_win) {
645                         return window;
646                 }
647         }
648         return NULL;
649 }
650
651
652 void
653 GHOST_SystemSDL::addDirtyWindow(GHOST_WindowSDL *bad_wind)
654 {
655         GHOST_ASSERT((bad_wind != NULL), "addDirtyWindow() NULL ptr trapped (window)");
656
657         m_dirty_windows.push_back(bad_wind);
658 }
659
660 bool
661 GHOST_SystemSDL::supportsNativeDialogs(void)
662 {
663         return false;
664 }
665
666 GHOST_TSuccess GHOST_SystemSDL::getButtons(GHOST_Buttons& buttons) const
667 {
668         Uint8 state = SDL_GetMouseState(NULL, NULL);
669         buttons.set(GHOST_kButtonMaskLeft,   (state & SDL_BUTTON_LMASK) != 0);
670         buttons.set(GHOST_kButtonMaskMiddle, (state & SDL_BUTTON_MMASK) != 0);
671         buttons.set(GHOST_kButtonMaskRight,  (state & SDL_BUTTON_RMASK) != 0);
672
673         return GHOST_kSuccess;
674 }
675
676 GHOST_TUns8 *
677 GHOST_SystemSDL::getClipboard(bool selection) const
678 {
679         return (GHOST_TUns8 *)SDL_GetClipboardText();
680 }
681
682 void
683 GHOST_SystemSDL::putClipboard(GHOST_TInt8 *buffer, bool selection) const
684 {
685         SDL_SetClipboardText(buffer);
686 }
687
688 GHOST_TUns64
689 GHOST_SystemSDL::getMilliSeconds()
690 {
691         return GHOST_TUns64(SDL_GetTicks()); /* note, 32 -> 64bits */
692 }