|
- //
- // Lol Engine
- //
- // Copyright © 2010—2016 Sam Hocevar <sam@hocevar.net>
- //
- // Lol Engine is free software. It comes without any warranty, to
- // the extent permitted by applicable law. You can redistribute it
- // and/or modify it under the terms of the Do What the Fuck You Want
- // to Public License, Version 2, as published by the WTFPL Task Force.
- // See http://www.wtfpl.net/ for more details.
- //
-
- #include <lol/engine-internal.h>
-
- #if LOL_USE_SDL || LOL_USE_OLD_SDL
- # if HAVE_SDL2_SDL_H
- # include <SDL2/SDL.h>
- # elif HAVE_SDL_SDL_H
- # include <SDL/SDL.h>
- # else
- # include <SDL.h>
- # endif
- #endif
-
- #include "sdlinput.h"
-
- #include "input/input_internal.h"
-
- /* We force joystick polling because no events are received when
- * there is no SDL display (eg. on the Raspberry Pi). */
- #define SDL_FORCE_POLL_JOYSTICK 1
-
- #if EMSCRIPTEN
- # define MOUSE_SPEED_MOD 10.f
- #else
- # define MOUSE_SPEED_MOD 100.f
- #endif
-
- namespace lol
- {
-
- #if LOL_USE_OLD_SDL
- /* Quick and dirty for now... This is deprecated anyway. */
- static int sdl12_to_scancode(int ch, int sc)
- {
- if (ch >= 'a' && ch <= 'z')
- ch = ch - 'a' + 'A';
-
- # define _SC(id, str, name) if (ch == str[0]) return id;
- # include "input/keys.h"
-
- return 0;
- }
- #else
- //-------------------------------------------------------------------------
- # define _SC(id, str, name) static const uint16_t SDLOL_##name = id;
- # include "input/keys.h"
- //-------------------------------------------------------------------------
- static bool ScanCodeIsValid(int sc)
- {
- switch (sc)
- {
- # define _SC(id, str, name) \
- case id: return true;
- # include "input/keys.h"
- default: return false;
- }
- return false;
- }
- //-------------------------------------------------------------------------
- static String ScanCodeToText(int sc)
- {
- switch (sc)
- {
- # define _SC(id, str, name) \
- case id: return String(str);
- # include "input/keys.h"
- default:
- msg::error("ScanCodeToText unknown scancode %0d\n", sc);
- }
- return String();
- }
- //-------------------------------------------------------------------------
- static String ScanCodeToName(int sc)
- {
- switch (sc)
- {
- # define _SC(id, str, name) \
- case id: return String(#name);
- # include "input/keys.h"
- default:
- msg::error("ScanCodeToText unknown scancode %0d\n", sc);
- }
- return String();
- }
- #endif
-
- /*
- * SDL Input implementation class
- */
-
- class SdlInputData
- {
- friend class SdlInput;
-
- private:
- void Tick(float seconds);
-
- static ivec2 GetMousePos();
- static void SetMousePos(ivec2 position);
-
- SdlInputData(int app_w, int app_h, int screen_w, int screen_h)
- : m_mouse(nullptr),
- m_keyboard(nullptr),
- m_prevmouse(ivec2::zero),
- m_app(vec2((float)app_w, (float)app_h)),
- m_screen(vec2((float)screen_w, (float)screen_h)),
- m_mousecapture(false),
- m_tick_in_draw_thread(false)
- { }
-
- #if LOL_USE_SDL || LOL_USE_OLD_SDL
- array<SDL_Joystick *, InputDeviceInternal *> m_joysticks;
- InputDeviceInternal *m_mouse;
- InputDeviceInternal *m_keyboard;
- #endif // LOL_USE_SDL
-
- ivec2 m_prevmouse;
- vec2 m_app;
- vec2 m_screen;
-
- bool m_mousecapture;
- bool m_tick_in_draw_thread;
- };
-
- /*
- * Public SdlInput class
- */
-
- SdlInput::SdlInput(int app_w, int app_h, int screen_w, int screen_h)
- : m_data(new SdlInputData(app_w, app_h, screen_w, screen_h))
- {
- #if _WIN32
- m_data->m_tick_in_draw_thread = true;
- #endif
-
- #if LOL_USE_OLD_SDL
- /* Enable Unicode translation of keyboard events */
- SDL_EnableUNICODE(1);
- #endif
-
- #if LOL_USE_SDL || LOL_USE_OLD_SDL
- SDL_Init(SDL_INIT_TIMER | SDL_INIT_JOYSTICK);
- #endif
-
- m_data->m_keyboard = InputDeviceInternal::CreateStandardKeyboard();
- m_data->m_mouse = InputDeviceInternal::CreateStandardMouse();
-
- #if LOL_USE_SDL || LOL_USE_OLD_SDL
- # if !EMSCRIPTEN
- # if SDL_FORCE_POLL_JOYSTICK
- SDL_JoystickEventState(SDL_QUERY);
- # else
- SDL_JoystickEventState(SDL_ENABLE);
- # endif //SDL_FORCE_POLL_JOYSTICK
-
- /* Register all the joysticks we can find, and let the input
- * system decide what it wants to track. */
- for (int i = 0; i < SDL_NumJoysticks(); i++)
- {
- SDL_Joystick *sdlstick = SDL_JoystickOpen(i);
-
- /* Blacklist some devices:
- * - HDAPS, it's not a real joystick.
- * - X360 controllers, Xinput handles them better since
- * it won't think there is only one trigger axis. */
- # if LOL_USE_SDL
- char const *name = SDL_JoystickName(sdlstick);
- # elif LOL_USE_OLD_SDL
- char const *name = SDL_JoystickName(i);
- # endif
- if (strstr(name, "HDAPS")
- # if LOL_USE_XINPUT
- || strstr(name, "XBOX 360 For Windows")
- # endif //LOL_USE_XINPUT
- || false)
- {
- SDL_JoystickClose(sdlstick);
- continue;
- }
-
- //String::format("Joystick%d", i + 1).C()
- InputDeviceInternal* stick = new InputDeviceInternal(g_name_joystick(i + 1));
- for (int j = 0; j < SDL_JoystickNumAxes(sdlstick); ++j)
- stick->AddAxis(String::format("Axis%d", j + 1).C());
- for (int j = 0; j < SDL_JoystickNumButtons(sdlstick); ++j)
- stick->AddKey(String::format("Button%d", j + 1).C());
-
- m_data->m_joysticks.push(sdlstick, stick);
- }
- # endif //EMSCRIPTEN
- #endif
-
- m_gamegroup = GAMEGROUP_INPUT;
- }
-
- SdlInput::~SdlInput()
- {
- #if (LOL_USE_SDL || LOL_USE_OLD_SDL) && !EMSCRIPTEN
- /* Unregister all the joysticks we added */
- while (m_data->m_joysticks.count())
- {
- SDL_JoystickClose(m_data->m_joysticks[0].m1);
- delete m_data->m_joysticks[0].m2;
- m_data->m_joysticks.remove(0);
- }
- #endif
- delete m_data;
- }
-
- void SdlInput::TickGame(float seconds)
- {
- Entity::TickGame(seconds);
-
- if (!m_data->m_tick_in_draw_thread)
- m_data->Tick(seconds);
- }
-
- void SdlInput::TickDraw(float seconds, Scene &scene)
- {
- Entity::TickDraw(seconds, scene);
-
- if (m_data->m_tick_in_draw_thread)
- m_data->Tick(seconds);
- }
-
- void SdlInputData::Tick(float seconds)
- {
- #if LOL_USE_SDL || LOL_USE_OLD_SDL
- /* Pump all joystick events because no event is coming to us. */
- # if SDL_FORCE_POLL_JOYSTICK && !EMSCRIPTEN
- SDL_JoystickUpdate();
- for (int j = 0; j < m_joysticks.count(); j++)
- {
- for (int i = 0; i < SDL_JoystickNumButtons(m_joysticks[j].m1); i++)
- m_joysticks[j].m2->SetKey(i, SDL_JoystickGetButton(m_joysticks[j].m1, i) != 0);
- for (int i = 0; i < SDL_JoystickNumAxes(m_joysticks[j].m1); i++)
- m_joysticks[j].m2->SetAxis(i, (float)SDL_JoystickGetAxis(m_joysticks[j].m1, i) / 32768.f);
- }
- # endif
-
- m_mouse->SetAxis(4, 0);
-
- # if !LOL_USE_OLD_SDL
- if (m_keyboard->IsTextInputActive())
- SDL_StartTextInput();
- else
- SDL_StopTextInput();
- # endif
-
- /* Handle keyboard and WM events */
- SDL_Event event;
- while (SDL_PollEvent(&event))
- {
- switch (event.type)
- {
- case SDL_QUIT:
- Ticker::Shutdown();
- break;
-
- case SDL_KEYDOWN:
- case SDL_KEYUP:
- # if LOL_USE_OLD_SDL
- switch (int sc = sdl12_to_scancode(event.key.keysym.sym,
- event.key.keysym.scancode))
- # else
- switch (int sc = event.key.keysym.scancode)
- # endif
- {
- //Lock management
- case SDLOL_CapsLock:
- case SDLOL_ScrollLock:
- case SDLOL_NumLockClear:
- # if defined SDLOL_CapsLock && defined SDLOL_ScrollLock && defined SDLOL_NumLockClear
- //Update status on key down only
- if (event.type == SDL_KEYDOWN)
- {
- int sc2 = sc;
- switch (sc)
- {
- case SDLOL_CapsLock:
- sc2 = SDLOL_CapsLockStatus;
- break;
- case SDLOL_ScrollLock:
- sc2 = SDLOL_ScrollLockStatus;
- break;
- case SDLOL_NumLockClear:
- sc2 = SDLOL_NumLockClearStatus;
- break;
- }
- m_keyboard->SetKey(sc2, !m_keyboard->GetKey(sc2));
- /* DEBUG STUFF
- msg::info("Repeat: 0x%02x : %s/%s/%s/%i\n",
- (int)m_keyboard, ScanCodeToText(sc2).C(), ScanCodeToName(sc2).C(),
- m_keyboard->GetKey(sc2) ? "up" : "down", event.key.repeat);
- */
- }
- # endif
- default:
- # if LOL_USE_OLD_SDL
- m_keyboard->SetKey(sc ? sc : event.key.keysym.scancode,
- event.type == SDL_KEYDOWN);
- # else
- if (ScanCodeIsValid(sc))
- {
- //Set key updates the corresponding key
- m_keyboard->SetKey(sc, event.type == SDL_KEYDOWN);
-
- /* DEBUG STUFF
- msg::info("Repeat: 0x%02x : %s/%s/%s/%i\n",
- (int)m_keyboard, ScanCodeToText(sc).C(), ScanCodeToName(sc).C(),
- event.type == SDL_KEYDOWN ? "up" : "down", event.key.repeat);
- */
- }
- /* DEBUG STUFF
- else
- msg::error("unknown keypress (sym 0x%02x, scancode %0d)\n",
- event.key.keysym.sym, event.key.keysym.scancode);
- */
- # endif
- }
- break;
-
- # if !LOL_USE_OLD_SDL
- //case SDL_TEXTEDITING: //TODO: handle that ?
- case SDL_TEXTINPUT:
- m_keyboard->AddText(event.text.text);
- break;
-
- # endif
-
- # if LOL_USE_OLD_SDL
- case SDL_MOUSEBUTTONDOWN:
- case SDL_MOUSEBUTTONUP:
- if (event.button.button != SDL_BUTTON_WHEELUP && event.button.button != SDL_BUTTON_WHEELDOWN)
- m_mouse->SetKey(event.button.button - 1, event.type == SDL_MOUSEBUTTONDOWN);
- else
- m_mouse->SetAxis(4, (event.button.button != SDL_BUTTON_WHEELUP) ? (1) : (-1));
- break;
- # else
- case SDL_MOUSEBUTTONDOWN:
- case SDL_MOUSEBUTTONUP:
- //event.button.which
- m_mouse->SetKey(event.button.button - 1, event.type == SDL_MOUSEBUTTONDOWN);
- break;
- case SDL_MOUSEWHEEL:
- m_mouse->SetAxis(4, (float)event.button.y);
- break;
- case SDL_WINDOWEVENT:
- {
- switch (event.window.event)
- {
- case SDL_WINDOWEVENT_ENTER:
- case SDL_WINDOWEVENT_FOCUS_GAINED:
- m_mouse->SetKey(3, true);
- break;
- case SDL_WINDOWEVENT_LEAVE:
- case SDL_WINDOWEVENT_FOCUS_LOST:
- m_mouse->SetKey(3, false);
- break;
- }
- break;
- }
- # endif
-
- # if !SDL_FORCE_POLL_JOYSTICK
- case SDL_JOYAXISMOTION:
- m_joysticks[event.jaxis.which].m2->SetAxis(event.jaxis.axis, (float)event.jaxis.value / 32768.f);
- break;
-
- case SDL_JOYBUTTONUP:
- case SDL_JOYBUTTONDOWN:
- m_joysticks[event.jbutton.which].m2->SetKey(event.jbutton.button, event.jbutton.state);
- break;
- # endif
- }
- }
-
- /* Handle mouse input */
- ivec2 mouse = SdlInputData::GetMousePos();
- if (InputDeviceInternal::GetMouseCapture() != m_mousecapture)
- {
- m_mousecapture = InputDeviceInternal::GetMouseCapture();
- # if LOL_USE_SDL
- SDL_SetRelativeMouseMode(m_mousecapture ? SDL_TRUE : SDL_FALSE);
- # elif LOL_USE_OLD_SDL
- SDL_WM_GrabInput(m_mousecapture ? SDL_GRAB_ON : SDL_GRAB_OFF);
- # endif
- mouse = (ivec2)m_app / 2;
- SdlInputData::SetMousePos(mouse);
- //SDL_ShowCursor(m_mousecapture ? SDL_DISABLE : SDL_ENABLE);
- }
-
- if (mouse.x >= 0 && mouse.x < m_app.x && mouse.y >= 0 && mouse.y < m_app.y)
- {
- //We need the max if we want coherent mouse speed between axis
- float max_screen_size = lol::max(m_screen.x, m_screen.y);
- vec2 vmouse = vec2(mouse);
- vec2 vprevmouse = vec2(m_prevmouse);
- m_mouse->SetCursor(0, vmouse / m_app, mouse);
- // Note: 100.0f is an arbitrary value that makes it feel about the same than an xbox controller joystick
- m_mouse->SetAxis(0, (mouse.x - vprevmouse.x) * MOUSE_SPEED_MOD / max_screen_size);
- // Y Axis is also negated to match the usual joystick Y axis (negatives values are for the upper direction)
- m_mouse->SetAxis(1,-(mouse.y - vprevmouse.y) * MOUSE_SPEED_MOD / max_screen_size);
- //Pixel movement
- m_mouse->SetAxis(2, (mouse.x - vprevmouse.x));
- m_mouse->SetAxis(3,-(mouse.y - vprevmouse.y));
- }
-
- //Mouse is focused, Validate the InScreen Key
- //Hardcoded 3, not very nice.
- # if !EMSCRIPTEN && LOL_USE_OLD_SDL
- m_mouse->SetKey(3, !!(SDL_GetAppState() & SDL_APPMOUSEFOCUS));
- # else
- //Handled in PollEvent
- # endif
-
- if (m_mousecapture)
- {
- mouse = ivec2(m_app * .5f);
- SdlInputData::SetMousePos(mouse);
- }
-
- m_prevmouse = mouse;
-
- #else
- UNUSED(seconds);
- #endif //LOL_USE_SDL
- }
-
- // NOTE: these two functions are pointless now and could be inlined directly
- ivec2 SdlInputData::GetMousePos()
- {
- ivec2 ret(-1, -1);
-
- #if LOL_USE_SDL || LOL_USE_OLD_SDL
- # if !EMSCRIPTEN && LOL_USE_OLD_SDL
- if (SDL_GetAppState() & SDL_APPMOUSEFOCUS)
- # endif
- {
- SDL_GetMouseState(&ret.x, &ret.y);
- ret.y = Video::GetSize().y - 1 - ret.y;
- }
- #endif
- return ret;
- }
-
- void SdlInputData::SetMousePos(ivec2 position)
- {
- #if LOL_USE_SDL
- // FIXME: how do I warped mouse?
- #elif LOL_USE_OLD_SDL
- SDL_WarpMouse((uint16_t)position.x, (uint16_t)position.y);
- #else
- UNUSED(position);
- #endif
- }
-
- } /* namespace lol */
|