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