Add command line argument to switch gpu backend. Add `--gpu-backend` option to override the gpu backend selected by Blender. Values for this option that will be available in releases for now are: * opengl: Force blender to select OpenGL backend. During development and depending on compile options additional values can exist: * metal: Force Blender to select Metal backend. When this option isn't provided the internal logic for GPU backend selection will be used. Note that this is at the time of writing the same as always selecting the opengl backend. Reviewed By: fclem, brecht, MichaelPW Differential Revision: https://developer.blender.org/D16297
768 lines
24 KiB
C++
768 lines
24 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup GHOST
|
|
*/
|
|
|
|
#include <cassert>
|
|
#include <stdexcept>
|
|
|
|
#include "GHOST_ContextSDL.h"
|
|
#include "GHOST_SystemSDL.h"
|
|
#include "GHOST_WindowSDL.h"
|
|
|
|
#include "GHOST_WindowManager.h"
|
|
|
|
#include "GHOST_EventButton.h"
|
|
#include "GHOST_EventCursor.h"
|
|
#include "GHOST_EventKey.h"
|
|
#include "GHOST_EventWheel.h"
|
|
|
|
GHOST_SystemSDL::GHOST_SystemSDL() : GHOST_System()
|
|
{
|
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) {
|
|
throw std::runtime_error("Error initializing SDL: " + std::string(SDL_GetError()));
|
|
}
|
|
|
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
|
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
|
|
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
|
|
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
|
|
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
|
|
}
|
|
|
|
GHOST_SystemSDL::~GHOST_SystemSDL()
|
|
{
|
|
SDL_Quit();
|
|
}
|
|
|
|
GHOST_IWindow *GHOST_SystemSDL::createWindow(const char *title,
|
|
int32_t left,
|
|
int32_t top,
|
|
uint32_t width,
|
|
uint32_t height,
|
|
GHOST_TWindowState state,
|
|
GHOST_GLSettings glSettings,
|
|
const bool exclusive,
|
|
const bool /* is_dialog */,
|
|
const GHOST_IWindow *parentWindow)
|
|
{
|
|
GHOST_WindowSDL *window = nullptr;
|
|
|
|
window = new GHOST_WindowSDL(this,
|
|
title,
|
|
left,
|
|
top,
|
|
width,
|
|
height,
|
|
state,
|
|
glSettings.context_type,
|
|
((glSettings.flags & GHOST_glStereoVisual) != 0),
|
|
exclusive,
|
|
parentWindow);
|
|
|
|
if (window) {
|
|
if (GHOST_kWindowStateFullScreen == state) {
|
|
SDL_Window *sdl_win = window->getSDLWindow();
|
|
SDL_DisplayMode mode;
|
|
|
|
static_cast<GHOST_DisplayManagerSDL *>(m_displayManager)->getCurrentDisplayModeSDL(mode);
|
|
|
|
SDL_SetWindowDisplayMode(sdl_win, &mode);
|
|
SDL_ShowWindow(sdl_win);
|
|
SDL_SetWindowFullscreen(sdl_win, SDL_TRUE);
|
|
}
|
|
|
|
if (window->getValid()) {
|
|
m_windowManager->addWindow(window);
|
|
pushEvent(new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window));
|
|
}
|
|
else {
|
|
delete window;
|
|
window = nullptr;
|
|
}
|
|
}
|
|
return window;
|
|
}
|
|
|
|
GHOST_TSuccess GHOST_SystemSDL::init()
|
|
{
|
|
GHOST_TSuccess success = GHOST_System::init();
|
|
|
|
if (success) {
|
|
m_displayManager = new GHOST_DisplayManagerSDL(this);
|
|
|
|
if (m_displayManager) {
|
|
return GHOST_kSuccess;
|
|
}
|
|
}
|
|
|
|
return GHOST_kFailure;
|
|
}
|
|
|
|
/**
|
|
* Returns the dimensions of the main display on this system.
|
|
* \return The dimension of the main display.
|
|
*/
|
|
void GHOST_SystemSDL::getAllDisplayDimensions(uint32_t &width, uint32_t &height) const
|
|
{
|
|
SDL_DisplayMode mode;
|
|
SDL_GetDesktopDisplayMode(0, &mode); /* NOTE: always 0 display. */
|
|
width = mode.w;
|
|
height = mode.h;
|
|
}
|
|
|
|
void GHOST_SystemSDL::getMainDisplayDimensions(uint32_t &width, uint32_t &height) const
|
|
{
|
|
SDL_DisplayMode mode;
|
|
SDL_GetCurrentDisplayMode(0, &mode); /* NOTE: always 0 display. */
|
|
width = mode.w;
|
|
height = mode.h;
|
|
}
|
|
|
|
uint8_t GHOST_SystemSDL::getNumDisplays() const
|
|
{
|
|
return SDL_GetNumVideoDisplays();
|
|
}
|
|
|
|
GHOST_IContext *GHOST_SystemSDL::createOffscreenContext(GHOST_GLSettings /*glSettings*/)
|
|
{
|
|
GHOST_Context *context = new GHOST_ContextSDL(false,
|
|
nullptr,
|
|
0, /* Profile bit. */
|
|
3,
|
|
3,
|
|
GHOST_OPENGL_SDL_CONTEXT_FLAGS,
|
|
GHOST_OPENGL_SDL_RESET_NOTIFICATION_STRATEGY);
|
|
|
|
if (context->initializeDrawingContext()) {
|
|
return context;
|
|
}
|
|
delete context;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
GHOST_TSuccess GHOST_SystemSDL::disposeContext(GHOST_IContext *context)
|
|
{
|
|
delete context;
|
|
|
|
return GHOST_kSuccess;
|
|
}
|
|
|
|
GHOST_TSuccess GHOST_SystemSDL::getModifierKeys(GHOST_ModifierKeys &keys) const
|
|
{
|
|
SDL_Keymod mod = SDL_GetModState();
|
|
|
|
keys.set(GHOST_kModifierKeyLeftShift, (mod & KMOD_LSHIFT) != 0);
|
|
keys.set(GHOST_kModifierKeyRightShift, (mod & KMOD_RSHIFT) != 0);
|
|
keys.set(GHOST_kModifierKeyLeftControl, (mod & KMOD_LCTRL) != 0);
|
|
keys.set(GHOST_kModifierKeyRightControl, (mod & KMOD_RCTRL) != 0);
|
|
keys.set(GHOST_kModifierKeyLeftAlt, (mod & KMOD_LALT) != 0);
|
|
keys.set(GHOST_kModifierKeyRightAlt, (mod & KMOD_RALT) != 0);
|
|
keys.set(GHOST_kModifierKeyLeftOS, (mod & KMOD_LGUI) != 0);
|
|
keys.set(GHOST_kModifierKeyRightOS, (mod & KMOD_RGUI) != 0);
|
|
|
|
return GHOST_kSuccess;
|
|
}
|
|
|
|
#define GXMAP(k, x, y) \
|
|
case x: \
|
|
k = y; \
|
|
break
|
|
|
|
static GHOST_TKey convertSDLKey(SDL_Scancode key)
|
|
{
|
|
GHOST_TKey type;
|
|
|
|
if ((key >= SDL_SCANCODE_A) && (key <= SDL_SCANCODE_Z)) {
|
|
type = GHOST_TKey(key - SDL_SCANCODE_A + int(GHOST_kKeyA));
|
|
}
|
|
else if ((key >= SDL_SCANCODE_1) && (key <= SDL_SCANCODE_0)) {
|
|
type = (key == SDL_SCANCODE_0) ? GHOST_kKey0 :
|
|
GHOST_TKey(key - SDL_SCANCODE_1 + int(GHOST_kKey1));
|
|
}
|
|
else if ((key >= SDL_SCANCODE_F1) && (key <= SDL_SCANCODE_F12)) {
|
|
type = GHOST_TKey(key - SDL_SCANCODE_F1 + int(GHOST_kKeyF1));
|
|
}
|
|
else if ((key >= SDL_SCANCODE_F13) && (key <= SDL_SCANCODE_F24)) {
|
|
type = GHOST_TKey(key - SDL_SCANCODE_F13 + int(GHOST_kKeyF13));
|
|
}
|
|
else {
|
|
switch (key) {
|
|
/* TODO SDL_SCANCODE_NONUSBACKSLASH */
|
|
|
|
GXMAP(type, SDL_SCANCODE_BACKSPACE, GHOST_kKeyBackSpace);
|
|
GXMAP(type, SDL_SCANCODE_TAB, GHOST_kKeyTab);
|
|
GXMAP(type, SDL_SCANCODE_RETURN, GHOST_kKeyEnter);
|
|
GXMAP(type, SDL_SCANCODE_ESCAPE, GHOST_kKeyEsc);
|
|
GXMAP(type, SDL_SCANCODE_SPACE, GHOST_kKeySpace);
|
|
|
|
GXMAP(type, SDL_SCANCODE_SEMICOLON, GHOST_kKeySemicolon);
|
|
GXMAP(type, SDL_SCANCODE_PERIOD, GHOST_kKeyPeriod);
|
|
GXMAP(type, SDL_SCANCODE_COMMA, GHOST_kKeyComma);
|
|
GXMAP(type, SDL_SCANCODE_APOSTROPHE, GHOST_kKeyQuote);
|
|
GXMAP(type, SDL_SCANCODE_GRAVE, GHOST_kKeyAccentGrave);
|
|
GXMAP(type, SDL_SCANCODE_MINUS, GHOST_kKeyMinus);
|
|
GXMAP(type, SDL_SCANCODE_EQUALS, GHOST_kKeyEqual);
|
|
|
|
GXMAP(type, SDL_SCANCODE_SLASH, GHOST_kKeySlash);
|
|
GXMAP(type, SDL_SCANCODE_BACKSLASH, GHOST_kKeyBackslash);
|
|
GXMAP(type, SDL_SCANCODE_KP_EQUALS, GHOST_kKeyEqual);
|
|
GXMAP(type, SDL_SCANCODE_LEFTBRACKET, GHOST_kKeyLeftBracket);
|
|
GXMAP(type, SDL_SCANCODE_RIGHTBRACKET, GHOST_kKeyRightBracket);
|
|
GXMAP(type, SDL_SCANCODE_PAUSE, GHOST_kKeyPause);
|
|
|
|
GXMAP(type, SDL_SCANCODE_LSHIFT, GHOST_kKeyLeftShift);
|
|
GXMAP(type, SDL_SCANCODE_RSHIFT, GHOST_kKeyRightShift);
|
|
GXMAP(type, SDL_SCANCODE_LCTRL, GHOST_kKeyLeftControl);
|
|
GXMAP(type, SDL_SCANCODE_RCTRL, GHOST_kKeyRightControl);
|
|
GXMAP(type, SDL_SCANCODE_LALT, GHOST_kKeyLeftAlt);
|
|
GXMAP(type, SDL_SCANCODE_RALT, GHOST_kKeyRightAlt);
|
|
GXMAP(type, SDL_SCANCODE_LGUI, GHOST_kKeyLeftOS);
|
|
GXMAP(type, SDL_SCANCODE_RGUI, GHOST_kKeyRightOS);
|
|
GXMAP(type, SDL_SCANCODE_APPLICATION, GHOST_kKeyApp);
|
|
|
|
GXMAP(type, SDL_SCANCODE_INSERT, GHOST_kKeyInsert);
|
|
GXMAP(type, SDL_SCANCODE_DELETE, GHOST_kKeyDelete);
|
|
GXMAP(type, SDL_SCANCODE_HOME, GHOST_kKeyHome);
|
|
GXMAP(type, SDL_SCANCODE_END, GHOST_kKeyEnd);
|
|
GXMAP(type, SDL_SCANCODE_PAGEUP, GHOST_kKeyUpPage);
|
|
GXMAP(type, SDL_SCANCODE_PAGEDOWN, GHOST_kKeyDownPage);
|
|
|
|
GXMAP(type, SDL_SCANCODE_LEFT, GHOST_kKeyLeftArrow);
|
|
GXMAP(type, SDL_SCANCODE_RIGHT, GHOST_kKeyRightArrow);
|
|
GXMAP(type, SDL_SCANCODE_UP, GHOST_kKeyUpArrow);
|
|
GXMAP(type, SDL_SCANCODE_DOWN, GHOST_kKeyDownArrow);
|
|
|
|
GXMAP(type, SDL_SCANCODE_CAPSLOCK, GHOST_kKeyCapsLock);
|
|
GXMAP(type, SDL_SCANCODE_SCROLLLOCK, GHOST_kKeyScrollLock);
|
|
GXMAP(type, SDL_SCANCODE_NUMLOCKCLEAR, GHOST_kKeyNumLock);
|
|
GXMAP(type, SDL_SCANCODE_PRINTSCREEN, GHOST_kKeyPrintScreen);
|
|
|
|
/* keypad events */
|
|
|
|
/* NOTE: SDL defines a bunch of key-pad identifiers that aren't supported by GHOST,
|
|
* such as #SDL_SCANCODE_KP_PERCENT, #SDL_SCANCODE_KP_XOR. */
|
|
GXMAP(type, SDL_SCANCODE_KP_0, GHOST_kKeyNumpad0);
|
|
GXMAP(type, SDL_SCANCODE_KP_1, GHOST_kKeyNumpad1);
|
|
GXMAP(type, SDL_SCANCODE_KP_2, GHOST_kKeyNumpad2);
|
|
GXMAP(type, SDL_SCANCODE_KP_3, GHOST_kKeyNumpad3);
|
|
GXMAP(type, SDL_SCANCODE_KP_4, GHOST_kKeyNumpad4);
|
|
GXMAP(type, SDL_SCANCODE_KP_5, GHOST_kKeyNumpad5);
|
|
GXMAP(type, SDL_SCANCODE_KP_6, GHOST_kKeyNumpad6);
|
|
GXMAP(type, SDL_SCANCODE_KP_7, GHOST_kKeyNumpad7);
|
|
GXMAP(type, SDL_SCANCODE_KP_8, GHOST_kKeyNumpad8);
|
|
GXMAP(type, SDL_SCANCODE_KP_9, GHOST_kKeyNumpad9);
|
|
GXMAP(type, SDL_SCANCODE_KP_PERIOD, GHOST_kKeyNumpadPeriod);
|
|
|
|
GXMAP(type, SDL_SCANCODE_KP_ENTER, GHOST_kKeyNumpadEnter);
|
|
GXMAP(type, SDL_SCANCODE_KP_PLUS, GHOST_kKeyNumpadPlus);
|
|
GXMAP(type, SDL_SCANCODE_KP_MINUS, GHOST_kKeyNumpadMinus);
|
|
GXMAP(type, SDL_SCANCODE_KP_MULTIPLY, GHOST_kKeyNumpadAsterisk);
|
|
GXMAP(type, SDL_SCANCODE_KP_DIVIDE, GHOST_kKeyNumpadSlash);
|
|
|
|
/* Media keys in some keyboards and laptops with XFree86/Xorg */
|
|
GXMAP(type, SDL_SCANCODE_AUDIOPLAY, GHOST_kKeyMediaPlay);
|
|
GXMAP(type, SDL_SCANCODE_AUDIOSTOP, GHOST_kKeyMediaStop);
|
|
GXMAP(type, SDL_SCANCODE_AUDIOPREV, GHOST_kKeyMediaFirst);
|
|
// GXMAP(type, XF86XK_AudioRewind, GHOST_kKeyMediaFirst);
|
|
GXMAP(type, SDL_SCANCODE_AUDIONEXT, GHOST_kKeyMediaLast);
|
|
|
|
default:
|
|
printf("Unknown\n");
|
|
type = GHOST_kKeyUnknown;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return type;
|
|
}
|
|
#undef GXMAP
|
|
|
|
static char convert_keyboard_event_to_ascii(const SDL_KeyboardEvent &sdl_sub_evt)
|
|
{
|
|
SDL_Keycode sym = sdl_sub_evt.keysym.sym;
|
|
if (sym > 127) {
|
|
switch (sym) {
|
|
case SDLK_KP_DIVIDE:
|
|
sym = '/';
|
|
break;
|
|
case SDLK_KP_MULTIPLY:
|
|
sym = '*';
|
|
break;
|
|
case SDLK_KP_MINUS:
|
|
sym = '-';
|
|
break;
|
|
case SDLK_KP_PLUS:
|
|
sym = '+';
|
|
break;
|
|
case SDLK_KP_1:
|
|
sym = '1';
|
|
break;
|
|
case SDLK_KP_2:
|
|
sym = '2';
|
|
break;
|
|
case SDLK_KP_3:
|
|
sym = '3';
|
|
break;
|
|
case SDLK_KP_4:
|
|
sym = '4';
|
|
break;
|
|
case SDLK_KP_5:
|
|
sym = '5';
|
|
break;
|
|
case SDLK_KP_6:
|
|
sym = '6';
|
|
break;
|
|
case SDLK_KP_7:
|
|
sym = '7';
|
|
break;
|
|
case SDLK_KP_8:
|
|
sym = '8';
|
|
break;
|
|
case SDLK_KP_9:
|
|
sym = '9';
|
|
break;
|
|
case SDLK_KP_0:
|
|
sym = '0';
|
|
break;
|
|
case SDLK_KP_PERIOD:
|
|
sym = '.';
|
|
break;
|
|
default:
|
|
sym = 0;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (sdl_sub_evt.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) {
|
|
/* Weak US keyboard assumptions. */
|
|
if (sym >= 'a' && sym <= ('a' + 32)) {
|
|
sym -= 32;
|
|
}
|
|
else {
|
|
switch (sym) {
|
|
case '`':
|
|
sym = '~';
|
|
break;
|
|
case '1':
|
|
sym = '!';
|
|
break;
|
|
case '2':
|
|
sym = '@';
|
|
break;
|
|
case '3':
|
|
sym = '#';
|
|
break;
|
|
case '4':
|
|
sym = '$';
|
|
break;
|
|
case '5':
|
|
sym = '%';
|
|
break;
|
|
case '6':
|
|
sym = '^';
|
|
break;
|
|
case '7':
|
|
sym = '&';
|
|
break;
|
|
case '8':
|
|
sym = '*';
|
|
break;
|
|
case '9':
|
|
sym = '(';
|
|
break;
|
|
case '0':
|
|
sym = ')';
|
|
break;
|
|
case '-':
|
|
sym = '_';
|
|
break;
|
|
case '=':
|
|
sym = '+';
|
|
break;
|
|
case '[':
|
|
sym = '{';
|
|
break;
|
|
case ']':
|
|
sym = '}';
|
|
break;
|
|
case '\\':
|
|
sym = '|';
|
|
break;
|
|
case ';':
|
|
sym = ':';
|
|
break;
|
|
case '\'':
|
|
sym = '"';
|
|
break;
|
|
case ',':
|
|
sym = '<';
|
|
break;
|
|
case '.':
|
|
sym = '>';
|
|
break;
|
|
case '/':
|
|
sym = '?';
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return char(sym);
|
|
}
|
|
|
|
/**
|
|
* Events don't always have valid windows,
|
|
* but GHOST needs a window _always_. fallback to the GL window.
|
|
*/
|
|
static SDL_Window *SDL_GetWindowFromID_fallback(Uint32 id)
|
|
{
|
|
SDL_Window *sdl_win = SDL_GetWindowFromID(id);
|
|
if (sdl_win == nullptr) {
|
|
sdl_win = SDL_GL_GetCurrentWindow();
|
|
}
|
|
return sdl_win;
|
|
}
|
|
|
|
void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event)
|
|
{
|
|
GHOST_Event *g_event = nullptr;
|
|
|
|
switch (sdl_event->type) {
|
|
case SDL_WINDOWEVENT: {
|
|
SDL_WindowEvent &sdl_sub_evt = sdl_event->window;
|
|
GHOST_WindowSDL *window = findGhostWindow(
|
|
SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID));
|
|
/* Can be nullptr on close window. */
|
|
#if 0
|
|
assert(window != nullptr);
|
|
#endif
|
|
|
|
switch (sdl_sub_evt.event) {
|
|
case SDL_WINDOWEVENT_EXPOSED:
|
|
g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, window);
|
|
break;
|
|
case SDL_WINDOWEVENT_RESIZED:
|
|
g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window);
|
|
break;
|
|
case SDL_WINDOWEVENT_MOVED:
|
|
g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowMove, window);
|
|
break;
|
|
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
|
g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowActivate, window);
|
|
break;
|
|
case SDL_WINDOWEVENT_FOCUS_LOST:
|
|
g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowDeactivate, window);
|
|
break;
|
|
case SDL_WINDOWEVENT_CLOSE:
|
|
g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowClose, window);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SDL_QUIT: {
|
|
GHOST_IWindow *window = m_windowManager->getActiveWindow();
|
|
g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventQuitRequest, window);
|
|
break;
|
|
}
|
|
|
|
case SDL_MOUSEMOTION: {
|
|
SDL_MouseMotionEvent &sdl_sub_evt = sdl_event->motion;
|
|
SDL_Window *sdl_win = SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID);
|
|
GHOST_WindowSDL *window = findGhostWindow(sdl_win);
|
|
assert(window != nullptr);
|
|
|
|
int x_win, y_win;
|
|
SDL_GetWindowPosition(sdl_win, &x_win, &y_win);
|
|
|
|
int32_t x_root = sdl_sub_evt.x + x_win;
|
|
int32_t y_root = sdl_sub_evt.y + y_win;
|
|
|
|
#if 0
|
|
if (window->getCursorGrabMode() != GHOST_kGrabDisable &&
|
|
window->getCursorGrabMode() != GHOST_kGrabNormal) {
|
|
int32_t x_new = x_root;
|
|
int32_t y_new = y_root;
|
|
int32_t x_accum, y_accum;
|
|
GHOST_Rect bounds;
|
|
|
|
/* fallback to window bounds */
|
|
if (window->getCursorGrabBounds(bounds) == GHOST_kFailure)
|
|
window->getClientBounds(bounds);
|
|
|
|
/* Could also clamp to screen bounds wrap with a window outside the view will
|
|
* fail at the moment. Use offset of 8 in case the window is at screen bounds. */
|
|
bounds.wrapPoint(x_new, y_new, 8, window->getCursorGrabAxis());
|
|
window->getCursorGrabAccum(x_accum, y_accum);
|
|
|
|
/* Can't use #setCursorPosition because the mouse may have no focus! */
|
|
if (x_new != x_root || y_new != y_root) {
|
|
if (1 /* `xme.time > m_last_warp` */ ) {
|
|
/* when wrapping we don't need to add an event because the
|
|
* #setCursorPosition call will cause a new event after */
|
|
SDL_WarpMouseInWindow(sdl_win, x_new - x_win, y_new - y_win); /* wrap */
|
|
window->setCursorGrabAccum(x_accum + (x_root - x_new), y_accum + (y_root - y_new));
|
|
// m_last_warp = lastEventTime(xme.time);
|
|
}
|
|
else {
|
|
// setCursorPosition(x_new, y_new); /* wrap but don't accumulate */
|
|
SDL_WarpMouseInWindow(sdl_win, x_new - x_win, y_new - y_win);
|
|
}
|
|
|
|
g_event = new GHOST_EventCursor(getMilliSeconds(),
|
|
GHOST_kEventCursorMove,
|
|
window,
|
|
x_new,
|
|
y_new,
|
|
GHOST_TABLET_DATA_NONE);
|
|
}
|
|
else {
|
|
g_event = new GHOST_EventCursor(getMilliSeconds(),
|
|
GHOST_kEventCursorMove,
|
|
window,
|
|
x_root + x_accum,
|
|
y_root + y_accum,
|
|
GHOST_TABLET_DATA_NONE);
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
g_event = new GHOST_EventCursor(getMilliSeconds(),
|
|
GHOST_kEventCursorMove,
|
|
window,
|
|
x_root,
|
|
y_root,
|
|
GHOST_TABLET_DATA_NONE);
|
|
}
|
|
break;
|
|
}
|
|
case SDL_MOUSEBUTTONUP:
|
|
case SDL_MOUSEBUTTONDOWN: {
|
|
SDL_MouseButtonEvent &sdl_sub_evt = sdl_event->button;
|
|
GHOST_TButton gbmask = GHOST_kButtonMaskLeft;
|
|
GHOST_TEventType type = (sdl_sub_evt.state == SDL_PRESSED) ? GHOST_kEventButtonDown :
|
|
GHOST_kEventButtonUp;
|
|
|
|
GHOST_WindowSDL *window = findGhostWindow(
|
|
SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID));
|
|
assert(window != nullptr);
|
|
|
|
/* process rest of normal mouse buttons */
|
|
if (sdl_sub_evt.button == SDL_BUTTON_LEFT) {
|
|
gbmask = GHOST_kButtonMaskLeft;
|
|
}
|
|
else if (sdl_sub_evt.button == SDL_BUTTON_MIDDLE) {
|
|
gbmask = GHOST_kButtonMaskMiddle;
|
|
}
|
|
else if (sdl_sub_evt.button == SDL_BUTTON_RIGHT) {
|
|
gbmask = GHOST_kButtonMaskRight;
|
|
/* these buttons are untested! */
|
|
}
|
|
else if (sdl_sub_evt.button == SDL_BUTTON_X1) {
|
|
gbmask = GHOST_kButtonMaskButton4;
|
|
}
|
|
else if (sdl_sub_evt.button == SDL_BUTTON_X2) {
|
|
gbmask = GHOST_kButtonMaskButton5;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
|
|
g_event = new GHOST_EventButton(
|
|
getMilliSeconds(), type, window, gbmask, GHOST_TABLET_DATA_NONE);
|
|
break;
|
|
}
|
|
case SDL_MOUSEWHEEL: {
|
|
SDL_MouseWheelEvent &sdl_sub_evt = sdl_event->wheel;
|
|
GHOST_WindowSDL *window = findGhostWindow(
|
|
SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID));
|
|
assert(window != nullptr);
|
|
g_event = new GHOST_EventWheel(getMilliSeconds(), window, sdl_sub_evt.y);
|
|
break;
|
|
}
|
|
case SDL_KEYDOWN:
|
|
case SDL_KEYUP: {
|
|
SDL_KeyboardEvent &sdl_sub_evt = sdl_event->key;
|
|
GHOST_TEventType type = (sdl_sub_evt.state == SDL_PRESSED) ? GHOST_kEventKeyDown :
|
|
GHOST_kEventKeyUp;
|
|
const bool is_repeat = sdl_sub_evt.repeat != 0;
|
|
|
|
GHOST_WindowSDL *window = findGhostWindow(
|
|
SDL_GetWindowFromID_fallback(sdl_sub_evt.windowID));
|
|
assert(window != nullptr);
|
|
|
|
GHOST_TKey gkey = convertSDLKey(sdl_sub_evt.keysym.scancode);
|
|
/* NOTE: the `sdl_sub_evt.keysym.sym` is truncated,
|
|
* for unicode support ghost has to be modified. */
|
|
|
|
/* TODO(@campbellbarton): support full unicode, SDL supports this but it needs to be
|
|
* explicitly enabled via #SDL_StartTextInput which GHOST would have to wrap. */
|
|
char utf8_buf[sizeof(GHOST_TEventKeyData::utf8_buf)] = {'\0'};
|
|
if (type == GHOST_kEventKeyDown) {
|
|
utf8_buf[0] = convert_keyboard_event_to_ascii(sdl_sub_evt);
|
|
}
|
|
|
|
g_event = new GHOST_EventKey(getMilliSeconds(), type, window, gkey, is_repeat, utf8_buf);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (g_event) {
|
|
pushEvent(g_event);
|
|
}
|
|
}
|
|
|
|
GHOST_TSuccess GHOST_SystemSDL::getCursorPosition(int32_t &x, int32_t &y) const
|
|
{
|
|
int x_win, y_win;
|
|
SDL_Window *win = SDL_GetMouseFocus();
|
|
SDL_GetWindowPosition(win, &x_win, &y_win);
|
|
|
|
int xi, yi;
|
|
SDL_GetMouseState(&xi, &yi);
|
|
x = xi + x_win;
|
|
y = yi + x_win;
|
|
|
|
return GHOST_kSuccess;
|
|
}
|
|
|
|
GHOST_TSuccess GHOST_SystemSDL::setCursorPosition(int32_t x, int32_t y)
|
|
{
|
|
int x_win, y_win;
|
|
SDL_Window *win = SDL_GetMouseFocus();
|
|
SDL_GetWindowPosition(win, &x_win, &y_win);
|
|
|
|
SDL_WarpMouseInWindow(win, x - x_win, y - y_win);
|
|
return GHOST_kSuccess;
|
|
}
|
|
|
|
bool GHOST_SystemSDL::generateWindowExposeEvents()
|
|
{
|
|
std::vector<GHOST_WindowSDL *>::iterator w_start = m_dirty_windows.begin();
|
|
std::vector<GHOST_WindowSDL *>::const_iterator w_end = m_dirty_windows.end();
|
|
bool anyProcessed = false;
|
|
|
|
for (; w_start != w_end; ++w_start) {
|
|
GHOST_Event *g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowUpdate, *w_start);
|
|
|
|
(*w_start)->validate();
|
|
|
|
if (g_event) {
|
|
// printf("Expose events pushed\n");
|
|
pushEvent(g_event);
|
|
anyProcessed = true;
|
|
}
|
|
}
|
|
|
|
m_dirty_windows.clear();
|
|
return anyProcessed;
|
|
}
|
|
|
|
bool GHOST_SystemSDL::processEvents(bool waitForEvent)
|
|
{
|
|
/* Get all the current events - translate them into
|
|
* ghost events and call base class #pushEvent() method. */
|
|
|
|
bool anyProcessed = false;
|
|
|
|
do {
|
|
GHOST_TimerManager *timerMgr = getTimerManager();
|
|
|
|
if (waitForEvent && m_dirty_windows.empty() && !SDL_HasEvents(SDL_FIRSTEVENT, SDL_LASTEVENT)) {
|
|
uint64_t next = timerMgr->nextFireTime();
|
|
|
|
if (next == GHOST_kFireTimeNever) {
|
|
SDL_WaitEventTimeout(nullptr, -1);
|
|
// SleepTillEvent(m_display, -1);
|
|
}
|
|
else {
|
|
int64_t maxSleep = next - getMilliSeconds();
|
|
|
|
if (maxSleep >= 0) {
|
|
SDL_WaitEventTimeout(nullptr, next - getMilliSeconds());
|
|
// SleepTillEvent(m_display, next - getMilliSeconds()); /* X11. */
|
|
}
|
|
}
|
|
}
|
|
|
|
if (timerMgr->fireTimers(getMilliSeconds())) {
|
|
anyProcessed = true;
|
|
}
|
|
|
|
SDL_Event sdl_event;
|
|
while (SDL_PollEvent(&sdl_event)) {
|
|
processEvent(&sdl_event);
|
|
anyProcessed = true;
|
|
}
|
|
|
|
if (generateWindowExposeEvents()) {
|
|
anyProcessed = true;
|
|
}
|
|
} while (waitForEvent && !anyProcessed);
|
|
|
|
return anyProcessed;
|
|
}
|
|
|
|
GHOST_WindowSDL *GHOST_SystemSDL::findGhostWindow(SDL_Window *sdl_win)
|
|
{
|
|
if (sdl_win == nullptr) {
|
|
return nullptr;
|
|
}
|
|
/* It is not entirely safe to do this as the backptr may point
|
|
* to a window that has recently been removed.
|
|
* We should always check the window manager's list of windows
|
|
* and only process events on these windows. */
|
|
|
|
const std::vector<GHOST_IWindow *> &win_vec = m_windowManager->getWindows();
|
|
|
|
std::vector<GHOST_IWindow *>::const_iterator win_it = win_vec.begin();
|
|
std::vector<GHOST_IWindow *>::const_iterator win_end = win_vec.end();
|
|
|
|
for (; win_it != win_end; ++win_it) {
|
|
GHOST_WindowSDL *window = static_cast<GHOST_WindowSDL *>(*win_it);
|
|
if (window->getSDLWindow() == sdl_win) {
|
|
return window;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void GHOST_SystemSDL::addDirtyWindow(GHOST_WindowSDL *bad_wind)
|
|
{
|
|
GHOST_ASSERT((bad_wind != nullptr), "addDirtyWindow() nullptr ptr trapped (window)");
|
|
|
|
m_dirty_windows.push_back(bad_wind);
|
|
}
|
|
|
|
GHOST_TSuccess GHOST_SystemSDL::getButtons(GHOST_Buttons &buttons) const
|
|
{
|
|
Uint8 state = SDL_GetMouseState(nullptr, nullptr);
|
|
buttons.set(GHOST_kButtonMaskLeft, (state & SDL_BUTTON_LMASK) != 0);
|
|
buttons.set(GHOST_kButtonMaskMiddle, (state & SDL_BUTTON_MMASK) != 0);
|
|
buttons.set(GHOST_kButtonMaskRight, (state & SDL_BUTTON_RMASK) != 0);
|
|
|
|
return GHOST_kSuccess;
|
|
}
|
|
|
|
char *GHOST_SystemSDL::getClipboard(bool /*selection*/) const
|
|
{
|
|
return (char *)SDL_GetClipboardText();
|
|
}
|
|
|
|
void GHOST_SystemSDL::putClipboard(const char *buffer, bool /*selection*/) const
|
|
{
|
|
SDL_SetClipboardText(buffer);
|
|
}
|
|
|
|
uint64_t GHOST_SystemSDL::getMilliSeconds()
|
|
{
|
|
return uint64_t(SDL_GetTicks()); /* NOTE: 32 -> 64bits. */
|
|
}
|