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