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