Blender V5.0
GHOST_SystemSDL.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cassert>
10#include <stdexcept>
11
12#include "GHOST_ContextSDL.hh"
13#include "GHOST_SystemSDL.hh"
14#include "GHOST_WindowSDL.hh"
15
17
18#include "GHOST_EventButton.hh"
19#include "GHOST_EventCursor.hh"
20#include "GHOST_EventKey.hh"
21#include "GHOST_EventWheel.hh"
22
24{
25 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) {
26 throw std::runtime_error(SDL_GetError());
27 }
28
29 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
30 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
31 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
32 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
33 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
34}
35
37{
38 SDL_Quit();
39}
40
41GHOST_IWindow *GHOST_SystemSDL::createWindow(const char *title,
44 uint32_t width,
45 uint32_t height,
47 GHOST_GPUSettings gpu_settings,
48 const bool exclusive,
49 const bool /*is_dialog*/,
50 const GHOST_IWindow *parent_window)
51{
52 GHOST_WindowSDL *window = nullptr;
53
54 const GHOST_ContextParams context_params = GHOST_CONTEXT_PARAMS_FROM_GPU_SETTINGS(gpu_settings);
55
56 window = new GHOST_WindowSDL(this,
57 title,
58 left,
59 top,
60 width,
61 height,
62 state,
63 gpu_settings.context_type,
64 context_params,
65 exclusive,
66 parent_window);
67
68 if (window) {
70 SDL_Window *sdl_win = window->getSDLWindow();
71 SDL_DisplayMode mode;
72
73 memset(&mode, 0, sizeof(mode));
74
75 SDL_SetWindowDisplayMode(sdl_win, &mode);
76 SDL_ShowWindow(sdl_win);
77 SDL_SetWindowFullscreen(sdl_win, SDL_TRUE);
78 }
79
80 if (window->getValid()) {
81 window_manager_->addWindow(window);
82 pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
83 }
84 else {
85 delete window;
86 window = nullptr;
87 }
88 }
89 return window;
90}
91
93{
95
96 if (success) {
97 return GHOST_kSuccess;
98 }
99
100 return GHOST_kFailure;
101}
102
107void GHOST_SystemSDL::getAllDisplayDimensions(uint32_t &width, uint32_t &height) const
108{
109 SDL_DisplayMode mode;
110 const int display_index = 0; /* NOTE: always 0 display. */
111 if (SDL_GetDesktopDisplayMode(display_index, &mode) < 0) {
112 return;
113 }
114 width = mode.w;
115 height = mode.h;
116}
117
118void GHOST_SystemSDL::getMainDisplayDimensions(uint32_t &width, uint32_t &height) const
119{
120 SDL_DisplayMode mode;
121 const int display_index = 0; /* NOTE: always 0 display. */
122 if (SDL_GetCurrentDisplayMode(display_index, &mode) < 0) {
123 return;
124 }
125 width = mode.w;
126 height = mode.h;
127}
128
130{
131 return SDL_GetNumVideoDisplays();
132}
133
135{
136 const GHOST_ContextParams context_params_offscreen =
138
139 switch (gpu_settings.context_type) {
140#ifdef WITH_OPENGL_BACKEND
141 case GHOST_kDrawingContextTypeOpenGL: {
142 for (int minor = 6; minor >= 3; --minor) {
143 GHOST_Context *context = new GHOST_ContextSDL(
144 context_params_offscreen,
145 nullptr,
146 0, /* Profile bit. */
147 4,
148 minor,
151
152 if (context->initializeDrawingContext()) {
153 return context;
154 }
155 delete context;
156 }
157 return nullptr;
158 }
159#endif
160
161 default:
162 /* Unsupported backend. */
163 return nullptr;
164 }
165}
166
168{
169 delete context;
170
171 return GHOST_kSuccess;
172}
173
175{
176 SDL_Keymod mod = SDL_GetModState();
177
178 keys.set(GHOST_kModifierKeyLeftShift, (mod & KMOD_LSHIFT) != 0);
179 keys.set(GHOST_kModifierKeyRightShift, (mod & KMOD_RSHIFT) != 0);
180 keys.set(GHOST_kModifierKeyLeftControl, (mod & KMOD_LCTRL) != 0);
181 keys.set(GHOST_kModifierKeyRightControl, (mod & KMOD_RCTRL) != 0);
182 keys.set(GHOST_kModifierKeyLeftAlt, (mod & KMOD_LALT) != 0);
183 keys.set(GHOST_kModifierKeyRightAlt, (mod & KMOD_RALT) != 0);
184 keys.set(GHOST_kModifierKeyLeftOS, (mod & KMOD_LGUI) != 0);
185 keys.set(GHOST_kModifierKeyRightOS, (mod & KMOD_RGUI) != 0);
186
187 return GHOST_kSuccess;
188}
189
190#define GXMAP(k, x, y) \
191 case x: \
192 k = y; \
193 break
194
195static GHOST_TKey convertSDLKey(SDL_Scancode key)
196{
197 GHOST_TKey type;
198
199 if ((key >= SDL_SCANCODE_A) && (key <= SDL_SCANCODE_Z)) {
200 type = GHOST_TKey(key - SDL_SCANCODE_A + int(GHOST_kKeyA));
201 }
202 else if ((key >= SDL_SCANCODE_1) && (key <= SDL_SCANCODE_0)) {
203 type = (key == SDL_SCANCODE_0) ? GHOST_kKey0 :
204 GHOST_TKey(key - SDL_SCANCODE_1 + int(GHOST_kKey1));
205 }
206 else if ((key >= SDL_SCANCODE_F1) && (key <= SDL_SCANCODE_F12)) {
207 type = GHOST_TKey(key - SDL_SCANCODE_F1 + int(GHOST_kKeyF1));
208 }
209 else if ((key >= SDL_SCANCODE_F13) && (key <= SDL_SCANCODE_F24)) {
210 type = GHOST_TKey(key - SDL_SCANCODE_F13 + int(GHOST_kKeyF13));
211 }
212 else {
213 switch (key) {
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_kKeyLeftOS);
242 GXMAP(type, SDL_SCANCODE_RGUI, GHOST_kKeyRightOS);
243 GXMAP(type, SDL_SCANCODE_APPLICATION, GHOST_kKeyApp);
244
245 GXMAP(type, SDL_SCANCODE_INSERT, GHOST_kKeyInsert);
246 GXMAP(type, SDL_SCANCODE_DELETE, GHOST_kKeyDelete);
247 GXMAP(type, SDL_SCANCODE_HOME, GHOST_kKeyHome);
248 GXMAP(type, SDL_SCANCODE_END, GHOST_kKeyEnd);
249 GXMAP(type, SDL_SCANCODE_PAGEUP, GHOST_kKeyUpPage);
250 GXMAP(type, SDL_SCANCODE_PAGEDOWN, GHOST_kKeyDownPage);
251
252 GXMAP(type, SDL_SCANCODE_LEFT, GHOST_kKeyLeftArrow);
253 GXMAP(type, SDL_SCANCODE_RIGHT, GHOST_kKeyRightArrow);
254 GXMAP(type, SDL_SCANCODE_UP, GHOST_kKeyUpArrow);
255 GXMAP(type, SDL_SCANCODE_DOWN, GHOST_kKeyDownArrow);
256
257 GXMAP(type, SDL_SCANCODE_CAPSLOCK, GHOST_kKeyCapsLock);
258 GXMAP(type, SDL_SCANCODE_SCROLLLOCK, GHOST_kKeyScrollLock);
259 GXMAP(type, SDL_SCANCODE_NUMLOCKCLEAR, GHOST_kKeyNumLock);
260 GXMAP(type, SDL_SCANCODE_PRINTSCREEN, GHOST_kKeyPrintScreen);
261
262 /* keypad events */
263
264 /* NOTE: SDL defines a bunch of key-pad identifiers that aren't supported by GHOST,
265 * such as #SDL_SCANCODE_KP_PERCENT, #SDL_SCANCODE_KP_XOR. */
266 GXMAP(type, SDL_SCANCODE_KP_0, GHOST_kKeyNumpad0);
267 GXMAP(type, SDL_SCANCODE_KP_1, GHOST_kKeyNumpad1);
268 GXMAP(type, SDL_SCANCODE_KP_2, GHOST_kKeyNumpad2);
269 GXMAP(type, SDL_SCANCODE_KP_3, GHOST_kKeyNumpad3);
270 GXMAP(type, SDL_SCANCODE_KP_4, GHOST_kKeyNumpad4);
271 GXMAP(type, SDL_SCANCODE_KP_5, GHOST_kKeyNumpad5);
272 GXMAP(type, SDL_SCANCODE_KP_6, GHOST_kKeyNumpad6);
273 GXMAP(type, SDL_SCANCODE_KP_7, GHOST_kKeyNumpad7);
274 GXMAP(type, SDL_SCANCODE_KP_8, GHOST_kKeyNumpad8);
275 GXMAP(type, SDL_SCANCODE_KP_9, GHOST_kKeyNumpad9);
276 GXMAP(type, SDL_SCANCODE_KP_PERIOD, GHOST_kKeyNumpadPeriod);
277
278 GXMAP(type, SDL_SCANCODE_KP_ENTER, GHOST_kKeyNumpadEnter);
279 GXMAP(type, SDL_SCANCODE_KP_PLUS, GHOST_kKeyNumpadPlus);
280 GXMAP(type, SDL_SCANCODE_KP_MINUS, GHOST_kKeyNumpadMinus);
281 GXMAP(type, SDL_SCANCODE_KP_MULTIPLY, GHOST_kKeyNumpadAsterisk);
282 GXMAP(type, SDL_SCANCODE_KP_DIVIDE, GHOST_kKeyNumpadSlash);
283
284 /* Media keys in some keyboards and laptops with XFree86/XORG. */
285 GXMAP(type, SDL_SCANCODE_AUDIOPLAY, GHOST_kKeyMediaPlay);
286 GXMAP(type, SDL_SCANCODE_AUDIOSTOP, GHOST_kKeyMediaStop);
287 GXMAP(type, SDL_SCANCODE_AUDIOPREV, GHOST_kKeyMediaFirst);
288 // GXMAP(type, XF86XK_AudioRewind, GHOST_kKeyMediaFirst);
289 GXMAP(type, SDL_SCANCODE_AUDIONEXT, GHOST_kKeyMediaLast);
290
291 /* International Keys. */
292
293 /* This key has multiple purposes,
294 * however the only GHOST key that uses the scan-code is GrLess. */
295 GXMAP(type, SDL_SCANCODE_NONUSBACKSLASH, GHOST_kKeyGrLess);
296
297 default:
298 printf("Unknown\n");
299 type = GHOST_kKeyUnknown;
300 break;
301 }
302 }
303
304 return type;
305}
306#undef GXMAP
307
308static char convert_keyboard_event_to_ascii(const SDL_KeyboardEvent &sdl_sub_evt)
309{
310 SDL_Keycode sym = sdl_sub_evt.keysym.sym;
311 if (sym > 127) {
312 switch (sym) {
313 case SDLK_KP_DIVIDE:
314 sym = '/';
315 break;
316 case SDLK_KP_MULTIPLY:
317 sym = '*';
318 break;
319 case SDLK_KP_MINUS:
320 sym = '-';
321 break;
322 case SDLK_KP_PLUS:
323 sym = '+';
324 break;
325 case SDLK_KP_1:
326 sym = '1';
327 break;
328 case SDLK_KP_2:
329 sym = '2';
330 break;
331 case SDLK_KP_3:
332 sym = '3';
333 break;
334 case SDLK_KP_4:
335 sym = '4';
336 break;
337 case SDLK_KP_5:
338 sym = '5';
339 break;
340 case SDLK_KP_6:
341 sym = '6';
342 break;
343 case SDLK_KP_7:
344 sym = '7';
345 break;
346 case SDLK_KP_8:
347 sym = '8';
348 break;
349 case SDLK_KP_9:
350 sym = '9';
351 break;
352 case SDLK_KP_0:
353 sym = '0';
354 break;
355 case SDLK_KP_PERIOD:
356 sym = '.';
357 break;
358 default:
359 sym = 0;
360 break;
361 }
362 }
363 else {
364 if (sdl_sub_evt.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) {
365 /* Weak US keyboard assumptions. */
366 if (sym >= 'a' && sym <= ('a' + 32)) {
367 sym -= 32;
368 }
369 else {
370 switch (sym) {
371 case '`':
372 sym = '~';
373 break;
374 case '1':
375 sym = '!';
376 break;
377 case '2':
378 sym = '@';
379 break;
380 case '3':
381 sym = '#';
382 break;
383 case '4':
384 sym = '$';
385 break;
386 case '5':
387 sym = '%';
388 break;
389 case '6':
390 sym = '^';
391 break;
392 case '7':
393 sym = '&';
394 break;
395 case '8':
396 sym = '*';
397 break;
398 case '9':
399 sym = '(';
400 break;
401 case '0':
402 sym = ')';
403 break;
404 case '-':
405 sym = '_';
406 break;
407 case '=':
408 sym = '+';
409 break;
410 case '[':
411 sym = '{';
412 break;
413 case ']':
414 sym = '}';
415 break;
416 case '\\':
417 sym = '|';
418 break;
419 case ';':
420 sym = ':';
421 break;
422 case '\'':
423 sym = '"';
424 break;
425 case ',':
426 sym = '<';
427 break;
428 case '.':
429 sym = '>';
430 break;
431 case '/':
432 sym = '?';
433 break;
434 default:
435 break;
436 }
437 }
438 }
439 }
440 return char(sym);
441}
442
447static SDL_Window *SDL_GetWindowFromID_fallback(Uint32 id)
448{
449 SDL_Window *sdl_win = SDL_GetWindowFromID(id);
450 if (sdl_win == nullptr) {
451 sdl_win = SDL_GL_GetCurrentWindow();
452 }
453 return sdl_win;
454}
455
456void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event)
457{
458 GHOST_Event *g_event = nullptr;
459
460 switch (sdl_event->type) {
461 case SDL_WINDOWEVENT: {
462 const SDL_WindowEvent &sdl_sub_evt = sdl_event->window;
463 const uint64_t event_ms = sdl_sub_evt.timestamp;
464 GHOST_WindowSDL *window = findGhostWindow(
465 SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID));
466 /* Can be nullptr on close window. */
467#if 0
468 assert(window != nullptr);
469#endif
470
471 switch (sdl_sub_evt.event) {
472 case SDL_WINDOWEVENT_EXPOSED:
473 g_event = new GHOST_Event(event_ms, GHOST_kEventWindowUpdate, window);
474 break;
475 case SDL_WINDOWEVENT_RESIZED:
476 g_event = new GHOST_Event(event_ms, GHOST_kEventWindowSize, window);
477 break;
478 case SDL_WINDOWEVENT_MOVED:
479 g_event = new GHOST_Event(event_ms, GHOST_kEventWindowMove, window);
480 break;
481 case SDL_WINDOWEVENT_FOCUS_GAINED:
482 g_event = new GHOST_Event(event_ms, GHOST_kEventWindowActivate, window);
483 break;
484 case SDL_WINDOWEVENT_FOCUS_LOST:
485 g_event = new GHOST_Event(event_ms, GHOST_kEventWindowDeactivate, window);
486 break;
487 case SDL_WINDOWEVENT_CLOSE:
488 g_event = new GHOST_Event(event_ms, GHOST_kEventWindowClose, window);
489 break;
490 }
491
492 break;
493 }
494
495 case SDL_QUIT: {
496 const SDL_QuitEvent &sdl_sub_evt = sdl_event->quit;
497 const uint64_t event_ms = sdl_sub_evt.timestamp;
498 GHOST_IWindow *window = window_manager_->getActiveWindow();
499 g_event = new GHOST_Event(event_ms, GHOST_kEventQuitRequest, window);
500 break;
501 }
502
503 case SDL_MOUSEMOTION: {
504 const SDL_MouseMotionEvent &sdl_sub_evt = sdl_event->motion;
505 const uint64_t event_ms = sdl_sub_evt.timestamp;
506 SDL_Window *sdl_win = SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID);
507 GHOST_WindowSDL *window = findGhostWindow(sdl_win);
508 assert(window != nullptr);
509
510 int x_win, y_win;
511 SDL_GetWindowPosition(sdl_win, &x_win, &y_win);
512
513 int32_t x_root = sdl_sub_evt.x + x_win;
514 int32_t y_root = sdl_sub_evt.y + y_win;
515
516#if 0
517 if (window->getCursorGrabMode() != GHOST_kGrabDisable &&
519 {
520 int32_t x_new = x_root;
521 int32_t y_new = y_root;
522 int32_t x_accum, y_accum;
523 GHOST_Rect bounds;
524
525 /* fallback to window bounds */
526 if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) {
527 window->getClientBounds(bounds);
528 }
529
530 /* Could also clamp to screen bounds wrap with a window outside the view will
531 * fail at the moment. Use offset of 8 in case the window is at screen bounds. */
532 bounds.wrapPoint(x_new, y_new, 8, window->getCursorGrabAxis());
533 window->getCursorGrabAccum(x_accum, y_accum);
534
535 /* Can't use #setCursorPosition because the mouse may have no focus! */
536 if (x_new != x_root || y_new != y_root) {
537 if (1 /* `xme.time > last_warp_` */) {
538 /* when wrapping we don't need to add an event because the
539 * #setCursorPosition call will cause a new event after */
540 SDL_WarpMouseInWindow(sdl_win, x_new - x_win, y_new - y_win); /* wrap */
541 window->setCursorGrabAccum(x_accum + (x_root - x_new), y_accum + (y_root - y_new));
542 // last_warp_ = lastEventTime(xme.time);
543 }
544 else {
545 // setCursorPosition(x_new, y_new); /* wrap but don't accumulate */
546 SDL_WarpMouseInWindow(sdl_win, x_new - x_win, y_new - y_win);
547 }
548
549 g_event = new GHOST_EventCursor(
550 event_ms, GHOST_kEventCursorMove, window, x_new, y_new, GHOST_TABLET_DATA_NONE);
551 }
552 else {
553 g_event = new GHOST_EventCursor(event_ms,
555 window,
556 x_root + x_accum,
557 y_root + y_accum,
559 }
560 }
561 else
562#endif
563 {
564 g_event = new GHOST_EventCursor(
565 event_ms, GHOST_kEventCursorMove, window, x_root, y_root, GHOST_TABLET_DATA_NONE);
566 }
567 break;
568 }
569 case SDL_MOUSEBUTTONUP:
570 case SDL_MOUSEBUTTONDOWN: {
571 const SDL_MouseButtonEvent &sdl_sub_evt = sdl_event->button;
572 const uint64_t event_ms = sdl_sub_evt.timestamp;
574 GHOST_TEventType type = (sdl_sub_evt.state == SDL_PRESSED) ? GHOST_kEventButtonDown :
576
577 GHOST_WindowSDL *window = findGhostWindow(
578 SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID));
579 assert(window != nullptr);
580
581 /* process rest of normal mouse buttons */
582 if (sdl_sub_evt.button == SDL_BUTTON_LEFT) {
583 gbmask = GHOST_kButtonMaskLeft;
584 }
585 else if (sdl_sub_evt.button == SDL_BUTTON_MIDDLE) {
587 }
588 else if (sdl_sub_evt.button == SDL_BUTTON_RIGHT) {
589 gbmask = GHOST_kButtonMaskRight;
590 /* these buttons are untested! */
591 }
592 else if (sdl_sub_evt.button == SDL_BUTTON_X1) {
594 }
595 else if (sdl_sub_evt.button == SDL_BUTTON_X2) {
597 }
598 else {
599 break;
600 }
601
602 g_event = new GHOST_EventButton(event_ms, type, window, gbmask, GHOST_TABLET_DATA_NONE);
603 break;
604 }
605 case SDL_MOUSEWHEEL: {
606 const SDL_MouseWheelEvent &sdl_sub_evt = sdl_event->wheel;
607 const uint64_t event_ms = sdl_sub_evt.timestamp;
608 GHOST_WindowSDL *window = findGhostWindow(
609 SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID));
610 assert(window != nullptr);
611 if (sdl_sub_evt.x != 0) {
612 g_event = new GHOST_EventWheel(
613 event_ms, window, GHOST_kEventWheelAxisHorizontal, sdl_sub_evt.x);
614 }
615 else if (sdl_sub_evt.y != 0) {
616 g_event = new GHOST_EventWheel(
617 event_ms, window, GHOST_kEventWheelAxisVertical, sdl_sub_evt.y);
618 }
619 break;
620 }
621 case SDL_KEYDOWN:
622 case SDL_KEYUP: {
623 const SDL_KeyboardEvent &sdl_sub_evt = sdl_event->key;
624 const uint64_t event_ms = sdl_sub_evt.timestamp;
625 GHOST_TEventType type = (sdl_sub_evt.state == SDL_PRESSED) ? GHOST_kEventKeyDown :
627 const bool is_repeat = sdl_sub_evt.repeat != 0;
628
629 GHOST_WindowSDL *window = findGhostWindow(
630 SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID));
631 assert(window != nullptr);
632
633 GHOST_TKey gkey = convertSDLKey(sdl_sub_evt.keysym.scancode);
634 /* NOTE: the `sdl_sub_evt.keysym.sym` is truncated,
635 * for unicode support ghost has to be modified. */
636
637 /* TODO(@ideasman42): support full unicode, SDL supports this but it needs to be
638 * explicitly enabled via #SDL_StartTextInput which GHOST would have to wrap. */
639 char utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'};
640 if (type == GHOST_kEventKeyDown) {
641 utf8_buf[0] = convert_keyboard_event_to_ascii(sdl_sub_evt);
642 }
643
644 g_event = new GHOST_EventKey(event_ms, type, window, gkey, is_repeat, utf8_buf);
645 break;
646 }
647 }
648
649 if (g_event) {
650 pushEvent(g_event);
651 }
652}
653
655{
656 int x_win, y_win;
657 SDL_Window *win = SDL_GetMouseFocus();
658 SDL_GetWindowPosition(win, &x_win, &y_win);
659
660 int xi, yi;
661 SDL_GetMouseState(&xi, &yi);
662 x = xi + x_win;
663 y = yi + x_win;
664
665 return GHOST_kSuccess;
666}
667
669{
670 int x_win, y_win;
671 SDL_Window *win = SDL_GetMouseFocus();
672 SDL_GetWindowPosition(win, &x_win, &y_win);
673
674 SDL_WarpMouseInWindow(win, x - x_win, y - y_win);
675 return GHOST_kSuccess;
676}
677
678bool GHOST_SystemSDL::generateWindowExposeEvents()
679{
680 std::vector<GHOST_WindowSDL *>::iterator w_start = dirty_windows_.begin();
681 std::vector<GHOST_WindowSDL *>::const_iterator w_end = dirty_windows_.end();
682 bool anyProcessed = false;
683
684 for (; w_start != w_end; ++w_start) {
685 /* The caller doesn't have a time-stamp. */
686 const uint64_t event_ms = getMilliSeconds();
687 GHOST_Event *g_event = new GHOST_Event(event_ms, GHOST_kEventWindowUpdate, *w_start);
688
689 (*w_start)->validate();
690
691 if (g_event) {
692 // printf("Expose events pushed\n");
693 pushEvent(g_event);
694 anyProcessed = true;
695 }
696 }
697
698 dirty_windows_.clear();
699 return anyProcessed;
700}
701
702bool GHOST_SystemSDL::processEvents(bool waitForEvent)
703{
704 /* Get all the current events - translate them into
705 * ghost events and call base class #pushEvent() method. */
706
707 bool anyProcessed = false;
708
709 do {
711
712 if (waitForEvent && dirty_windows_.empty() && !SDL_HasEvents(SDL_FIRSTEVENT, SDL_LASTEVENT)) {
713 uint64_t next = timerMgr->nextFireTime();
714
715 if (next == GHOST_kFireTimeNever) {
716 SDL_WaitEventTimeout(nullptr, -1);
717 // SleepTillEvent(display_, -1);
718 }
719 else {
720 int64_t maxSleep = next - getMilliSeconds();
721
722 if (maxSleep >= 0) {
723 SDL_WaitEventTimeout(nullptr, next - getMilliSeconds());
724 // SleepTillEvent(display_, next - getMilliSeconds()); /* X11. */
725 }
726 }
727 }
728
729 if (timerMgr->fireTimers(getMilliSeconds())) {
730 anyProcessed = true;
731 }
732
733 SDL_Event sdl_event;
734 while (SDL_PollEvent(&sdl_event)) {
735 processEvent(&sdl_event);
736 anyProcessed = true;
737 }
738
739 if (generateWindowExposeEvents()) {
740 anyProcessed = true;
741 }
742 } while (waitForEvent && !anyProcessed);
743
744 return anyProcessed;
745}
746
747GHOST_WindowSDL *GHOST_SystemSDL::findGhostWindow(SDL_Window *sdl_win)
748{
749 if (sdl_win == nullptr) {
750 return nullptr;
751 }
752 /* It is not entirely safe to do this as the back-pointer may point
753 * to a window that has recently been removed.
754 * We should always check the window manager's list of windows
755 * and only process events on these windows. */
756
757 const std::vector<GHOST_IWindow *> &win_vec = window_manager_->getWindows();
758
759 std::vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
760 std::vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
761
762 for (; win_it != win_end; ++win_it) {
763 GHOST_WindowSDL *window = static_cast<GHOST_WindowSDL *>(*win_it);
764 if (window->getSDLWindow() == sdl_win) {
765 return window;
766 }
767 }
768 return nullptr;
769}
770
772{
773 GHOST_ASSERT((bad_wind != nullptr), "addDirtyWindow() nullptr ptr trapped (window)");
774
775 dirty_windows_.push_back(bad_wind);
776}
777
779{
780 Uint8 state = SDL_GetMouseState(nullptr, nullptr);
781 buttons.set(GHOST_kButtonMaskLeft, (state & SDL_BUTTON_LMASK) != 0);
782 buttons.set(GHOST_kButtonMaskMiddle, (state & SDL_BUTTON_MMASK) != 0);
783 buttons.set(GHOST_kButtonMaskRight, (state & SDL_BUTTON_RMASK) != 0);
784
785 return GHOST_kSuccess;
786}
787
789{
792 /* NOTE: order the following flags as they they're declared in the source. */
793 ~(
794 /* This SDL back-end has not yet implemented primary clipboard. */
796 /* This SDL back-end has not yet implemented image copy/paste. */
798 /* This SDL back-end has not yet implemented color sampling the desktop. */
800 /* No support yet for IME input methods. */
802 /* No support for window decoration styles. */
804 /* No support for precisely placing windows on multiple monitors. */
806 /* No support for a Hyper modifier key. */
808 /* No support yet for RGBA mouse cursors. */
810 /* No support yet for dynamic cursor generation. */
812}
813
814char *GHOST_SystemSDL::getClipboard(bool /*selection*/) const
815{
816 return (char *)SDL_GetClipboardText();
817}
818
819void GHOST_SystemSDL::putClipboard(const char *buffer, bool /*selection*/) const
820{
821 SDL_SetClipboardText(buffer);
822}
823
825{
826 return SDL_GetTicks64();
827}
#define GHOST_OPENGL_SDL_RESET_NOTIFICATION_STRATEGY
#define GHOST_OPENGL_SDL_CONTEXT_FLAGS
#define GHOST_ASSERT(x, info)
static char convert_keyboard_event_to_ascii(const SDL_KeyboardEvent &sdl_sub_evt)
static GHOST_TKey convertSDLKey(SDL_Scancode key)
#define GXMAP(k, x, y)
static SDL_Window * SDL_GetWindowFromID_fallback(Uint32 id)
#define pushEvent
@ GHOST_kEventWheelAxisVertical
@ GHOST_kEventWheelAxisHorizontal
GHOST_TWindowState
@ GHOST_kWindowStateFullScreen
GHOST_TEventType
@ GHOST_kEventWindowClose
@ GHOST_kEventWindowMove
@ GHOST_kEventWindowSize
@ GHOST_kEventCursorMove
@ GHOST_kEventButtonUp
@ GHOST_kEventWindowActivate
@ GHOST_kEventWindowUpdate
@ GHOST_kEventWindowDeactivate
@ GHOST_kEventButtonDown
@ GHOST_kEventKeyDown
@ GHOST_kEventKeyUp
@ GHOST_kEventQuitRequest
static const GHOST_TabletData GHOST_TABLET_DATA_NONE
GHOST_TCapabilityFlag
@ GHOST_kCapabilityClipboardPrimary
@ GHOST_kCapabilityKeyboardHyperKey
@ GHOST_kCapabilityCursorRGBA
@ GHOST_kCapabilityInputIME
@ GHOST_kCapabilityCursorGenerator
@ GHOST_kCapabilityMultiMonitorPlacement
@ GHOST_kCapabilityClipboardImage
@ GHOST_kCapabilityWindowDecorationStyles
@ GHOST_kCapabilityDesktopSample
#define GHOST_CONTEXT_PARAMS_FROM_GPU_SETTINGS(gpu_settings)
#define GHOST_CAPABILITY_FLAG_ALL
GHOST_TKey
@ GHOST_kKeyLeftOS
@ GHOST_kKeyInsert
@ GHOST_kKeySemicolon
@ GHOST_kKeyMediaPlay
@ GHOST_kKeyQuote
@ GHOST_kKeyNumpad3
@ GHOST_kKeyAccentGrave
@ GHOST_kKeyNumpad1
@ GHOST_kKeyLeftAlt
@ GHOST_kKeyRightShift
@ GHOST_kKeyNumLock
@ GHOST_kKeyEnter
@ GHOST_kKeyNumpadSlash
@ GHOST_kKeyRightArrow
@ GHOST_kKeyF13
@ GHOST_kKeyNumpad4
@ GHOST_kKeyPause
@ GHOST_kKeyCapsLock
@ GHOST_kKeyApp
@ GHOST_kKeyMinus
@ GHOST_kKeyMediaStop
@ GHOST_kKeyBackSpace
@ GHOST_kKey0
@ GHOST_kKeyDownPage
@ GHOST_kKeyGrLess
@ GHOST_kKeyDownArrow
@ GHOST_kKeyRightOS
@ GHOST_kKeyNumpadPeriod
@ GHOST_kKeyF1
@ GHOST_kKeyNumpadAsterisk
@ GHOST_kKeyPrintScreen
@ GHOST_kKeyLeftControl
@ GHOST_kKeyLeftBracket
@ GHOST_kKey1
@ GHOST_kKeyTab
@ GHOST_kKeyComma
@ GHOST_kKeyRightBracket
@ GHOST_kKeyBackslash
@ GHOST_kKeyNumpad2
@ GHOST_kKeyRightAlt
@ GHOST_kKeyPeriod
@ GHOST_kKeyNumpadPlus
@ GHOST_kKeyUpPage
@ GHOST_kKeyNumpad5
@ GHOST_kKeyLeftArrow
@ GHOST_kKeyEqual
@ GHOST_kKeyHome
@ GHOST_kKeyNumpad6
@ GHOST_kKeyNumpad8
@ GHOST_kKeyNumpad9
@ GHOST_kKeyEnd
@ GHOST_kKeyUpArrow
@ GHOST_kKeyDelete
@ GHOST_kKeyNumpad0
@ GHOST_kKeyA
@ GHOST_kKeyMediaFirst
@ GHOST_kKeyNumpad7
@ GHOST_kKeyRightControl
@ GHOST_kKeyEsc
@ GHOST_kKeyUnknown
@ GHOST_kKeyScrollLock
@ GHOST_kKeySlash
@ GHOST_kKeyNumpadEnter
@ GHOST_kKeyNumpadMinus
@ GHOST_kKeyLeftShift
@ GHOST_kKeyMediaLast
@ GHOST_kKeySpace
@ GHOST_kModifierKeyRightControl
@ GHOST_kModifierKeyLeftControl
@ GHOST_kModifierKeyRightAlt
@ GHOST_kModifierKeyRightShift
@ GHOST_kModifierKeyLeftAlt
@ GHOST_kModifierKeyLeftShift
@ GHOST_kModifierKeyLeftOS
@ GHOST_kModifierKeyRightOS
GHOST_TSuccess
Definition GHOST_Types.h:57
@ GHOST_kFailure
Definition GHOST_Types.h:57
@ GHOST_kSuccess
Definition GHOST_Types.h:57
@ GHOST_kFireTimeNever
@ GHOST_kGrabDisable
@ GHOST_kGrabNormal
#define GHOST_CONTEXT_PARAMS_FROM_GPU_SETTINGS_OFFSCREEN(gpu_settings)
GHOST_TButton
@ GHOST_kButtonMaskRight
@ GHOST_kButtonMaskButton4
@ GHOST_kButtonMaskLeft
@ GHOST_kButtonMaskButton5
@ GHOST_kButtonMaskMiddle
long long int int64_t
unsigned long long int uint64_t
static btDbvtVolume bounds(btDbvtNode **leaves, int count)
Definition btDbvt.cpp:299
virtual GHOST_TSuccess init()=0
GHOST_TSuccess getCursorPosition(int32_t &x, int32_t &y) const override
GHOST_TSuccess setCursorPosition(int32_t x, int32_t y) override
char * getClipboard(bool selection) const override
void getMainDisplayDimensions(uint32_t &width, uint32_t &height) const override
GHOST_IContext * createOffscreenContext(GHOST_GPUSettings gpu_settings) override
GHOST_TCapabilityFlag getCapabilities() const override
void addDirtyWindow(GHOST_WindowSDL *bad_wind)
void getAllDisplayDimensions(uint32_t &width, uint32_t &height) const override
uint8_t getNumDisplays() const override
GHOST_TSuccess disposeContext(GHOST_IContext *context) override
void putClipboard(const char *buffer, bool selection) const override
GHOST_TSuccess getButtons(GHOST_Buttons &buttons) const override
GHOST_TSuccess getModifierKeys(GHOST_ModifierKeys &keys) const override
uint64_t getMilliSeconds() const override
bool processEvents(bool waitForEvent) override
GHOST_WindowManager * window_manager_
GHOST_TSuccess init() override
GHOST_TimerManager * getTimerManager() const
bool fireTimers(uint64_t time)
SDL_Window * getSDLWindow()
void getClientBounds(GHOST_Rect &bounds) const override
bool getValid() const override
GHOST_TSuccess getCursorGrabBounds(GHOST_Rect &bounds) const override
void setCursorGrabAccum(int32_t x, int32_t y)
GHOST_TAxisFlag getCursorGrabAxis() const
GHOST_TGrabCursorMode getCursorGrabMode() const
void getCursorGrabAccum(int32_t &x, int32_t &y) const
uint top
#define assert(assertion)
#define printf(...)
VecBase< float, D > constexpr mod(VecOp< float, D >, VecOp< float, D >) RET
static ulong * next
static ulong state[N]
static int left
void set(GHOST_TButton mask, bool down)
GHOST_TDrawingContextType context_type
void set(GHOST_TModifierKey mask, bool down)