2 * $Id: GHOST_SystemSDL.cpp 38349 2011-07-13 00:49:22Z gsrb3d $
3 * ***** BEGIN GPL LICENSE BLOCK *****
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * Contributor(s): Campbell Barton
21 * ***** END GPL LICENSE BLOCK *****
24 /** \file ghost/intern/GHOST_SystemSDL.cpp
30 #include "GHOST_SystemSDL.h"
32 #include "GHOST_WindowManager.h"
34 #include "GHOST_EventCursor.h"
35 #include "GHOST_EventKey.h"
36 #include "GHOST_EventButton.h"
37 #include "GHOST_EventWheel.h"
39 GHOST_SystemSDL::GHOST_SystemSDL()
43 if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) != 0) {
44 printf ("Error initializing SDL: %s\n", SDL_GetError());
47 /* SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); */
48 /* SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); */
49 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
50 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
51 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
52 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
53 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
54 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
57 GHOST_SystemSDL::~GHOST_SystemSDL()
63 GHOST_SystemSDL::createWindow(const STR_String& title,
68 GHOST_TWindowState state,
69 GHOST_TDrawingContextType type,
71 const GHOST_TUns16 numOfAASamples,
72 const GHOST_TEmbedderWindowID parentWindow
75 GHOST_WindowSDL *window= NULL;
77 window= new GHOST_WindowSDL (this, title, left, top, width, height, state, parentWindow, type, stereoVisual, 1);
80 if (window->getValid()) {
81 m_windowManager->addWindow(window);
82 pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
93 GHOST_SystemSDL::init() {
94 GHOST_TSuccess success = GHOST_System::init();
97 m_displayManager = new GHOST_DisplayManagerSDL(this);
99 if (m_displayManager) {
100 return GHOST_kSuccess;
104 return GHOST_kFailure;
108 GHOST_SystemSDL::getMainDisplayDimensions(GHOST_TUns32& width,
109 GHOST_TUns32& height) const
111 SDL_DisplayMode mode;
112 SDL_GetCurrentDisplayMode(0, &mode); /* note, always 0 display */
118 GHOST_SystemSDL::getNumDisplays() const
120 return SDL_GetNumVideoDisplays();
124 GHOST_SystemSDL::getModifierKeys(GHOST_ModifierKeys& keys) const
126 SDL_Keymod mod= SDL_GetModState();
128 keys.set(GHOST_kModifierKeyLeftShift, (mod & KMOD_LSHIFT) != 0);
129 keys.set(GHOST_kModifierKeyRightShift, (mod & KMOD_RSHIFT) != 0);
130 keys.set(GHOST_kModifierKeyLeftControl, (mod & KMOD_LCTRL) != 0);
131 keys.set(GHOST_kModifierKeyRightControl, (mod & KMOD_RCTRL) != 0);
132 keys.set(GHOST_kModifierKeyLeftAlt, (mod & KMOD_LALT) != 0);
133 keys.set(GHOST_kModifierKeyRightAlt, (mod & KMOD_RALT) != 0);
134 keys.set(GHOST_kModifierKeyOS, (mod & (KMOD_LGUI|KMOD_RGUI)) != 0);
136 return GHOST_kSuccess;
139 #define GXMAP(k,x,y) case x: k= y; break;
142 convertSDLKey(SDL_Scancode key)
146 if ((key >= SDL_SCANCODE_A) && (key <= SDL_SCANCODE_Z)) {
147 type= GHOST_TKey( key - SDL_SCANCODE_A + int(GHOST_kKeyA));
148 } else if ((key >= SDL_SCANCODE_1) && (key <= SDL_SCANCODE_0)) {
149 type= (key == SDL_SCANCODE_0) ? GHOST_kKey0 : GHOST_TKey(key - SDL_SCANCODE_1 + int(GHOST_kKey1));
150 } else if ((key >= SDL_SCANCODE_F1) && (key <= SDL_SCANCODE_F12)) {
151 type= GHOST_TKey(key - SDL_SCANCODE_F1 + int(GHOST_kKeyF1));
152 } else if ((key >= SDL_SCANCODE_F13) && (key <= SDL_SCANCODE_F24)) {
153 type= GHOST_TKey(key - SDL_SCANCODE_F13 + int(GHOST_kKeyF13));
156 /* TODO SDL_SCANCODE_NONUSBACKSLASH */
158 GXMAP(type,SDL_SCANCODE_BACKSPACE, GHOST_kKeyBackSpace);
159 GXMAP(type,SDL_SCANCODE_TAB, GHOST_kKeyTab);
160 GXMAP(type,SDL_SCANCODE_RETURN, GHOST_kKeyEnter);
161 GXMAP(type,SDL_SCANCODE_ESCAPE, GHOST_kKeyEsc);
162 GXMAP(type,SDL_SCANCODE_SPACE, GHOST_kKeySpace);
164 GXMAP(type,SDL_SCANCODE_SEMICOLON, GHOST_kKeySemicolon);
165 GXMAP(type,SDL_SCANCODE_PERIOD, GHOST_kKeyPeriod);
166 GXMAP(type,SDL_SCANCODE_COMMA, GHOST_kKeyComma);
167 GXMAP(type,SDL_SCANCODE_APOSTROPHE, GHOST_kKeyQuote);
168 GXMAP(type,SDL_SCANCODE_GRAVE, GHOST_kKeyAccentGrave);
169 GXMAP(type,SDL_SCANCODE_MINUS, GHOST_kKeyMinus);
170 GXMAP(type,SDL_SCANCODE_EQUALS, GHOST_kKeyEqual);
172 GXMAP(type,SDL_SCANCODE_SLASH, GHOST_kKeySlash);
173 GXMAP(type,SDL_SCANCODE_BACKSLASH, GHOST_kKeyBackslash);
174 GXMAP(type,SDL_SCANCODE_KP_EQUALS, GHOST_kKeyEqual);
175 GXMAP(type,SDL_SCANCODE_LEFTBRACKET, GHOST_kKeyLeftBracket);
176 GXMAP(type,SDL_SCANCODE_RIGHTBRACKET, GHOST_kKeyRightBracket);
177 GXMAP(type,SDL_SCANCODE_PAUSE, GHOST_kKeyPause);
179 GXMAP(type,SDL_SCANCODE_LSHIFT, GHOST_kKeyLeftShift);
180 GXMAP(type,SDL_SCANCODE_RSHIFT, GHOST_kKeyRightShift);
181 GXMAP(type,SDL_SCANCODE_LCTRL, GHOST_kKeyLeftControl);
182 GXMAP(type,SDL_SCANCODE_RCTRL, GHOST_kKeyRightControl);
183 GXMAP(type,SDL_SCANCODE_LALT, GHOST_kKeyLeftAlt);
184 GXMAP(type,SDL_SCANCODE_RALT, GHOST_kKeyRightAlt);
185 GXMAP(type,SDL_SCANCODE_LGUI, GHOST_kKeyOS);
186 GXMAP(type,SDL_SCANCODE_RGUI, GHOST_kKeyOS);
188 GXMAP(type,SDL_SCANCODE_INSERT, GHOST_kKeyInsert);
189 GXMAP(type,SDL_SCANCODE_DELETE, GHOST_kKeyDelete);
190 GXMAP(type,SDL_SCANCODE_HOME, GHOST_kKeyHome);
191 GXMAP(type,SDL_SCANCODE_END, GHOST_kKeyEnd);
192 GXMAP(type,SDL_SCANCODE_PAGEUP, GHOST_kKeyUpPage);
193 GXMAP(type,SDL_SCANCODE_PAGEDOWN, GHOST_kKeyDownPage);
195 GXMAP(type,SDL_SCANCODE_LEFT, GHOST_kKeyLeftArrow);
196 GXMAP(type,SDL_SCANCODE_RIGHT, GHOST_kKeyRightArrow);
197 GXMAP(type,SDL_SCANCODE_UP, GHOST_kKeyUpArrow);
198 GXMAP(type,SDL_SCANCODE_DOWN, GHOST_kKeyDownArrow);
200 GXMAP(type,SDL_SCANCODE_CAPSLOCK, GHOST_kKeyCapsLock);
201 GXMAP(type,SDL_SCANCODE_SCROLLLOCK, GHOST_kKeyScrollLock);
202 GXMAP(type,SDL_SCANCODE_NUMLOCKCLEAR, GHOST_kKeyNumLock);
203 GXMAP(type,SDL_SCANCODE_PRINTSCREEN, GHOST_kKeyPrintScreen);
207 /* note, sdl defines a bunch of kp defines I never saw before like
208 * SDL_SCANCODE_KP_PERCENT, SDL_SCANCODE_KP_XOR - campbell */
209 GXMAP(type,SDL_SCANCODE_KP_0, GHOST_kKeyNumpad0);
210 GXMAP(type,SDL_SCANCODE_KP_1, GHOST_kKeyNumpad1);
211 GXMAP(type,SDL_SCANCODE_KP_2, GHOST_kKeyNumpad2);
212 GXMAP(type,SDL_SCANCODE_KP_3, GHOST_kKeyNumpad3);
213 GXMAP(type,SDL_SCANCODE_KP_4, GHOST_kKeyNumpad4);
214 GXMAP(type,SDL_SCANCODE_KP_5, GHOST_kKeyNumpad5);
215 GXMAP(type,SDL_SCANCODE_KP_6, GHOST_kKeyNumpad6);
216 GXMAP(type,SDL_SCANCODE_KP_7, GHOST_kKeyNumpad7);
217 GXMAP(type,SDL_SCANCODE_KP_8, GHOST_kKeyNumpad8);
218 GXMAP(type,SDL_SCANCODE_KP_9, GHOST_kKeyNumpad9);
219 GXMAP(type,SDL_SCANCODE_KP_PERIOD, GHOST_kKeyNumpadPeriod);
221 GXMAP(type,SDL_SCANCODE_KP_ENTER, GHOST_kKeyNumpadEnter);
222 GXMAP(type,SDL_SCANCODE_KP_PLUS, GHOST_kKeyNumpadPlus);
223 GXMAP(type,SDL_SCANCODE_KP_MINUS, GHOST_kKeyNumpadMinus);
224 GXMAP(type,SDL_SCANCODE_KP_MULTIPLY, GHOST_kKeyNumpadAsterisk);
225 GXMAP(type,SDL_SCANCODE_KP_DIVIDE, GHOST_kKeyNumpadSlash);
227 /* Media keys in some keyboards and laptops with XFree86/Xorg */
228 GXMAP(type,SDL_SCANCODE_AUDIOPLAY, GHOST_kKeyMediaPlay);
229 GXMAP(type,SDL_SCANCODE_AUDIOSTOP, GHOST_kKeyMediaStop);
230 GXMAP(type,SDL_SCANCODE_AUDIOPREV, GHOST_kKeyMediaFirst);
231 // GXMAP(type,XF86XK_AudioRewind, GHOST_kKeyMediaFirst);
232 GXMAP(type,SDL_SCANCODE_AUDIONEXT, GHOST_kKeyMediaLast);
236 type= GHOST_kKeyUnknown;
247 GHOST_SystemSDL::processEvent(SDL_Event *sdl_event)
249 GHOST_Event * g_event= NULL;
251 switch(sdl_event->type) {
252 case SDL_WINDOWEVENT:
254 SDL_WindowEvent &sdl_sub_evt= sdl_event->window;
255 GHOST_WindowSDL *window= findGhostWindow(SDL_GetWindowFromID(sdl_sub_evt.windowID));
256 //assert(window != NULL); // can be NULL on close window.
258 switch (sdl_sub_evt.event) {
259 case SDL_WINDOWEVENT_EXPOSED:
260 g_event= new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window);
262 case SDL_WINDOWEVENT_RESIZED:
263 g_event= new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window);
265 case SDL_WINDOWEVENT_MOVED:
266 g_event= new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, window);
268 case SDL_WINDOWEVENT_FOCUS_GAINED:
269 g_event= new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window);
271 case SDL_WINDOWEVENT_FOCUS_LOST:
272 g_event= new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window);
274 case SDL_WINDOWEVENT_CLOSE:
275 g_event= new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window);
281 g_event= new GHOST_Event(getMilliSeconds(), GHOST_kEventQuit, NULL);
284 case SDL_MOUSEMOTION:
286 SDL_MouseMotionEvent &sdl_sub_evt= sdl_event->motion;
287 SDL_Window *sdl_win= SDL_GetWindowFromID(sdl_sub_evt.windowID);
288 GHOST_WindowSDL *window= findGhostWindow(sdl_win);
289 assert(window != NULL);
292 SDL_GetWindowPosition(sdl_win, &x_win, &y_win);
294 GHOST_TInt32 x_root= sdl_sub_evt.x + x_win;
295 GHOST_TInt32 y_root= sdl_sub_evt.y + y_win;
298 if(window->getCursorGrabMode() != GHOST_kGrabDisable && window->getCursorGrabMode() != GHOST_kGrabNormal)
300 GHOST_TInt32 x_new= x_root;
301 GHOST_TInt32 y_new= y_root;
302 GHOST_TInt32 x_accum, y_accum;
305 /* fallback to window bounds */
306 if(window->getCursorGrabBounds(bounds)==GHOST_kFailure)
307 window->getClientBounds(bounds);
309 /* could also clamp to screen bounds
310 * wrap with a window outside the view will fail atm */
311 bounds.wrapPoint(x_new, y_new, 8); /* offset of one incase blender is at screen bounds */
312 window->getCursorGrabAccum(x_accum, y_accum);
314 // cant use setCursorPosition because the mouse may have no focus!
315 if(x_new != x_root || y_new != y_root) {
316 if (1 ) { //xme.time > m_last_warp) {
317 /* when wrapping we don't need to add an event because the
318 * setCursorPosition call will cause a new event after */
319 SDL_WarpMouseInWindow(sdl_win, x_new - x_win, y_new - y_win); /* wrap */
320 window->setCursorGrabAccum(x_accum + (x_root - x_new), y_accum + (y_root - y_new));
321 // m_last_warp= lastEventTime(xme.time);
323 // setCursorPosition(x_new, y_new); /* wrap but don't accumulate */
324 SDL_WarpMouseInWindow(sdl_win, x_new - x_win, y_new - y_win);
327 g_event = new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, x_new, y_new);
330 g_event = new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, x_root + x_accum, y_root + y_accum);
336 g_event= new GHOST_EventCursor(getMilliSeconds(), GHOST_kEventCursorMove, window, x_root, y_root);
340 case SDL_MOUSEBUTTONUP:
341 case SDL_MOUSEBUTTONDOWN:
343 SDL_MouseButtonEvent &sdl_sub_evt= sdl_event->button;
344 GHOST_TButtonMask gbmask= GHOST_kButtonMaskLeft;
345 GHOST_TEventType type= (sdl_sub_evt.state==SDL_PRESSED) ? GHOST_kEventButtonDown : GHOST_kEventButtonUp;
347 GHOST_WindowSDL *window= findGhostWindow(SDL_GetWindowFromID(sdl_sub_evt.windowID));
348 assert(window != NULL);
350 /* process rest of normal mouse buttons */
351 if(sdl_sub_evt.button == SDL_BUTTON_LEFT)
352 gbmask= GHOST_kButtonMaskLeft;
353 else if(sdl_sub_evt.button == SDL_BUTTON_MIDDLE)
354 gbmask= GHOST_kButtonMaskMiddle;
355 else if(sdl_sub_evt.button == SDL_BUTTON_RIGHT)
356 gbmask= GHOST_kButtonMaskRight;
357 /* these buttons are untested! */
358 else if(sdl_sub_evt.button == SDL_BUTTON_X1)
359 gbmask= GHOST_kButtonMaskButton4;
360 else if(sdl_sub_evt.button == SDL_BUTTON_X2)
361 gbmask= GHOST_kButtonMaskButton5;
365 g_event= new GHOST_EventButton(getMilliSeconds(), type, window, gbmask);
370 SDL_MouseWheelEvent &sdl_sub_evt= sdl_event->wheel;
371 GHOST_WindowSDL *window= findGhostWindow(SDL_GetWindowFromID(sdl_sub_evt.windowID));
372 assert(window != NULL);
373 g_event= new GHOST_EventWheel(getMilliSeconds(), window, sdl_sub_evt.y);
379 SDL_KeyboardEvent &sdl_sub_evt= sdl_event->key;
380 SDL_Keycode sym= sdl_sub_evt.keysym.sym;
381 GHOST_TEventType type= (sdl_sub_evt.state == SDL_PRESSED) ? GHOST_kEventKeyDown : GHOST_kEventKeyUp;
383 GHOST_WindowSDL *window= findGhostWindow(SDL_GetWindowFromID(sdl_sub_evt.windowID));
384 assert(window != NULL);
386 GHOST_TKey gkey= convertSDLKey(sdl_sub_evt.keysym.scancode);
387 /* note, the sdl_sub_evt.keysym.sym is truncated, for unicode support ghost has to be modified */
392 if(sdl_sub_evt.keysym.mod & (KMOD_LSHIFT|KMOD_RSHIFT)) {
393 /* lame US keyboard assumptions */
394 if(sym >= 'a' && sym <= ('a' + 32)) {
399 case '`': sym= '~'; break;
400 case '1': sym= '!'; break;
401 case '2': sym= '@'; break;
402 case '3': sym= '#'; break;
403 case '4': sym= '$'; break;
404 case '5': sym= '%'; break;
405 case '6': sym= '^'; break;
406 case '7': sym= '&'; break;
407 case '8': sym= '*'; break;
408 case '9': sym= '('; break;
409 case '0': sym= ')'; break;
410 case '-': sym= '_'; break;
411 case '=': sym= '+'; break;
412 case '[': sym= '{'; break;
413 case ']': sym= '}'; break;
414 case '\\': sym= '|'; break;
415 case ';': sym= ':'; break;
416 case '\'': sym= '"'; break;
417 case ',': sym= '<'; break;
418 case '.': sym= '>'; break;
419 case '/': sym= '?'; break;
426 g_event= new GHOST_EventKey(getMilliSeconds(), type, window, gkey, sym);
437 GHOST_SystemSDL::getCursorPosition(GHOST_TInt32& x,
438 GHOST_TInt32& y) const
441 SDL_Window *win= SDL_GetMouseFocus();
442 SDL_GetWindowPosition(win, &x_win, &y_win);
445 SDL_GetMouseState(&xi, &yi);
449 return GHOST_kSuccess;
453 GHOST_SystemSDL::setCursorPosition(GHOST_TInt32 x,
457 SDL_Window *win= SDL_GetMouseFocus();
458 SDL_GetWindowPosition(win, &x_win, &y_win);
460 SDL_WarpMouseInWindow(win, x - x_win, y - y_win);
461 return GHOST_kSuccess;
465 GHOST_SystemSDL::generateWindowExposeEvents()
467 vector<GHOST_WindowSDL *>::iterator w_start= m_dirty_windows.begin();
468 vector<GHOST_WindowSDL *>::const_iterator w_end= m_dirty_windows.end();
469 bool anyProcessed= false;
471 for (;w_start != w_end; ++w_start) {
472 GHOST_Event * g_event= new
475 GHOST_kEventWindowUpdate,
479 (*w_start)->validate();
482 printf("Expose events pushed\n");
488 m_dirty_windows.clear();
494 GHOST_SystemSDL::processEvents(bool waitForEvent)
496 // Get all the current events -- translate them into
497 // ghost events and call base class pushEvent() method.
499 bool anyProcessed= false;
502 GHOST_TimerManager* timerMgr= getTimerManager();
504 if (waitForEvent && m_dirty_windows.empty() && !SDL_HasEvents(SDL_FIRSTEVENT, SDL_LASTEVENT)) {
505 GHOST_TUns64 next= timerMgr->nextFireTime();
507 if (next==GHOST_kFireTimeNever) {
508 SDL_WaitEventTimeout(NULL, -1);
509 //SleepTillEvent(m_display, -1);
511 GHOST_TInt64 maxSleep= next - getMilliSeconds();
514 SDL_WaitEventTimeout(NULL, next - getMilliSeconds());
515 // SleepTillEvent(m_display, next - getMilliSeconds()); // X11
520 if (timerMgr->fireTimers(getMilliSeconds())) {
525 while (SDL_PollEvent(&sdl_event)) {
526 processEvent(&sdl_event);
530 if (generateWindowExposeEvents()) {
533 } while (waitForEvent && !anyProcessed);
540 GHOST_SystemSDL::findGhostWindow(SDL_Window *sdl_win)
542 if (sdl_win == NULL) return NULL;
544 // It is not entirely safe to do this as the backptr may point
545 // to a window that has recently been removed.
546 // We should always check the window manager's list of windows
547 // and only process events on these windows.
549 vector<GHOST_IWindow *> & win_vec= m_windowManager->getWindows();
551 vector<GHOST_IWindow *>::iterator win_it= win_vec.begin();
552 vector<GHOST_IWindow *>::const_iterator win_end= win_vec.end();
554 for (; win_it != win_end; ++win_it) {
555 GHOST_WindowSDL * window= static_cast<GHOST_WindowSDL *>(*win_it);
556 if (window->getSDLWindow() == sdl_win) {
565 GHOST_SystemSDL::addDirtyWindow(GHOST_WindowSDL *bad_wind)
567 GHOST_ASSERT((bad_wind != NULL), "addDirtyWindow() NULL ptr trapped (window)");
569 m_dirty_windows.push_back(bad_wind);
573 GHOST_TSuccess GHOST_SystemSDL::getButtons(GHOST_Buttons& buttons) const
575 Uint8 state= SDL_GetMouseState(NULL, NULL);
576 buttons.set(GHOST_kButtonMaskLeft, (state & SDL_BUTTON_LMASK) != 0);
577 buttons.set(GHOST_kButtonMaskMiddle, (state & SDL_BUTTON_MMASK) != 0);
578 buttons.set(GHOST_kButtonMaskRight, (state & SDL_BUTTON_RMASK) != 0);
580 return GHOST_kSuccess;
584 GHOST_SystemSDL::getClipboard(bool selection) const
586 return (GHOST_TUns8 *)SDL_GetClipboardText();
590 GHOST_SystemSDL::putClipboard(GHOST_TInt8 *buffer, bool selection) const
592 SDL_SetClipboardText(buffer);
596 GHOST_SystemSDL::getMilliSeconds()
598 return GHOST_TUns64(SDL_GetTicks()); /* note, 32 -> 64bits */