VVVVVV/desktop_version/src/KeyPoll.cpp

631 lines
17 KiB
C++

#define KEY_DEFINITION
#include "KeyPoll.h"
#include <string.h>
#include "Alloc.h"
#include "ButtonGlyphs.h"
#include "Constants.h"
#include "Exit.h"
#include "Game.h"
#include "GlitchrunnerMode.h"
#include "Graphics.h"
#include "GraphicsUtil.h"
#include "Localization.h"
#include "LocalizationStorage.h"
#include "Music.h"
#include "Screen.h"
#include "UTF8.h"
#include "UtilityClass.h"
#include "Vlogging.h"
bool SaveScreenshot(void);
int inline KeyPoll::getThreshold(void)
{
switch (sensitivity)
{
case 0:
return 28000;
case 1:
return 16000;
case 2:
return 8000;
case 3:
return 4000;
case 4:
return 2000;
}
return 8000;
}
KeyPoll::KeyPoll(void)
{
xVel = 0;
yVel = 0;
// 0..5
sensitivity = 2;
keybuffer="";
leftbutton=0; rightbutton=0; middlebutton=0;
mousex = 0;
mousey = 0;
resetWindow = 0;
pressedbackspace=false;
linealreadyemptykludge = false;
isActive = true;
}
void KeyPoll::enabletextentry(void)
{
keybuffer="";
SDL_StartTextInput();
}
void KeyPoll::disabletextentry(void)
{
SDL_StopTextInput();
}
bool KeyPoll::textentry(void)
{
return SDL_IsTextInputActive() == SDL_TRUE;
}
void KeyPoll::toggleFullscreen(void)
{
gameScreen.toggleFullScreen();
keymap.clear(); /* we lost the input due to a new window. */
if (GlitchrunnerMode_less_than_or_equal(Glitchrunner2_2))
{
game.press_left = false;
game.press_right = false;
game.press_action = true;
game.press_map = false;
}
}
static int changemousestate(
int timeout,
const bool show,
const bool hide
) {
int prev;
int new_;
if (timeout > 0)
{
return --timeout;
}
/* If we want to both show and hide at the same time, prioritize showing */
if (show)
{
new_ = SDL_ENABLE;
}
else if (hide)
{
new_ = SDL_DISABLE;
}
else
{
return timeout;
}
prev = SDL_ShowCursor(SDL_QUERY);
if (prev == new_)
{
return timeout;
}
SDL_ShowCursor(new_);
switch (new_)
{
case SDL_DISABLE:
timeout = 0;
break;
case SDL_ENABLE:
timeout = 30;
break;
}
return timeout;
}
/* Also used in Input.cpp. */
void recomputetextboxes(void);
bool cycle_language(bool should_recompute_textboxes)
{
extern KeyPoll key;
if (game.gamestate == TITLEMODE
&& game.currentmenuname == Menu::translator_options_cutscenetest)
{
/* Unfortunately, despite how it may appear to be working, the options
* are actually language-specific, and the order could be totally
* different between languages too. So we can't cycle in this menu. */
music.playef(Sound_CRY);
return should_recompute_textboxes;
}
if (game.translator_cutscene_test)
{
/* Refuse cycling here for similar reasons, even if it seems like it's
* working. The text boxes are based off of the language XML and
* could be completely different between languages. */
music.playef(Sound_CRY);
return should_recompute_textboxes;
}
int i = loc::languagelist_curlang;
if (key.keymap[SDLK_LSHIFT])
{
/* Backwards */
i--;
}
else
{
/* Forwards */
i++;
}
if (!loc::languagelist.empty())
{
i = POS_MOD(i, (int) loc::languagelist.size());
loc::languagelist_curlang = i;
loc::lang = loc::languagelist[i].code;
loc::loadtext(false);
graphics.grphx.init_translations();
should_recompute_textboxes = true;
}
if (game.gamestate == TITLEMODE)
{
int temp = game.menucountdown;
game.createmenu(game.currentmenuname, true);
game.menucountdown = temp;
if (game.currentmenuname == Menu::language)
{
game.currentmenuoption = i;
}
}
return should_recompute_textboxes;
}
void KeyPoll::Poll(void)
{
static int raw_mousex = 0;
static int raw_mousey = 0;
static int mousetoggletimeout = 0;
bool showmouse = false;
bool hidemouse = false;
bool altpressed = false;
bool fullscreenkeybind = false;
SDL_GameController *controller = NULL;
SDL_Event evt;
bool should_recompute_textboxes = false;
bool active_input_device_changed = false;
bool keyboard_was_active = BUTTONGLYPHS_keyboard_is_active();
while (SDL_PollEvent(&evt))
{
switch (evt.type)
{
/* Keyboard Input */
case SDL_KEYDOWN:
{
keymap[evt.key.keysym.sym] = true;
if (evt.key.keysym.sym == SDLK_BACKSPACE)
{
pressedbackspace = true;
}
#ifdef __APPLE__ /* OSX prefers the command keys over the alt keys. -flibit */
altpressed = keymap[SDLK_LGUI] || keymap[SDLK_RGUI];
#else
altpressed = keymap[SDLK_LALT] || keymap[SDLK_RALT];
#endif
bool returnpressed = evt.key.keysym.sym == SDLK_RETURN;
bool fpressed = evt.key.keysym.sym == SDLK_f;
bool f11pressed = evt.key.keysym.sym == SDLK_F11;
if ((altpressed && (returnpressed || fpressed)) || f11pressed)
{
fullscreenkeybind = true;
}
if (loc::show_translator_menu && evt.key.keysym.sym == SDLK_F8 && !evt.key.repeat)
{
if (keymap[SDLK_LCTRL])
{
/* Debug keybind to cycle language. */
should_recompute_textboxes = cycle_language(should_recompute_textboxes);
}
else
{
/* Reload language files */
loc::loadtext(false);
graphics.grphx.init_translations();
music.playef(Sound_COIN);
}
}
if (evt.key.keysym.sym == SDLK_F6 && !evt.key.repeat)
{
const bool success = SaveScreenshot();
game.old_screenshot_border_timer = 255;
game.screenshot_border_timer = 255;
game.screenshot_saved_success = success;
}
BUTTONGLYPHS_keyboard_set_active(true);
if (textentry())
{
if (evt.key.keysym.sym == SDLK_BACKSPACE && !keybuffer.empty())
{
keybuffer.erase(UTF8_backspace(keybuffer.c_str(), keybuffer.length()));
if (keybuffer.empty())
{
linealreadyemptykludge = true;
}
}
else if ( evt.key.keysym.sym == SDLK_v &&
keymap[SDLK_LCTRL] )
{
char* text = SDL_GetClipboardText();
if (text != NULL)
{
keybuffer += text;
VVV_free(text);
}
}
else if ( evt.key.keysym.sym == SDLK_x &&
keymap[SDLK_LCTRL] )
{
if (SDL_SetClipboardText(keybuffer.c_str()) == 0)
{
keybuffer = "";
}
}
}
break;
}
case SDL_KEYUP:
keymap[evt.key.keysym.sym] = false;
if (evt.key.keysym.sym == SDLK_BACKSPACE)
{
pressedbackspace = false;
}
break;
case SDL_TEXTINPUT:
if (!altpressed)
{
keybuffer += evt.text.text;
}
break;
/* Mouse Input */
case SDL_MOUSEMOTION:
raw_mousex = evt.motion.x;
raw_mousey = evt.motion.y;
break;
case SDL_MOUSEBUTTONDOWN:
switch (evt.button.button)
{
case SDL_BUTTON_LEFT:
raw_mousex = evt.button.x;
raw_mousey = evt.button.y;
leftbutton = 1;
break;
case SDL_BUTTON_RIGHT:
raw_mousex = evt.button.x;
raw_mousey = evt.button.y;
rightbutton = 1;
break;
case SDL_BUTTON_MIDDLE:
raw_mousex = evt.button.x;
raw_mousey = evt.button.y;
middlebutton = 1;
break;
}
break;
case SDL_MOUSEBUTTONUP:
switch (evt.button.button)
{
case SDL_BUTTON_LEFT:
raw_mousex = evt.button.x;
raw_mousey = evt.button.y;
leftbutton=0;
break;
case SDL_BUTTON_RIGHT:
raw_mousex = evt.button.x;
raw_mousey = evt.button.y;
rightbutton=0;
break;
case SDL_BUTTON_MIDDLE:
raw_mousex = evt.button.x;
raw_mousey = evt.button.y;
middlebutton=0;
break;
}
break;
/* Controller Input */
case SDL_CONTROLLERBUTTONDOWN:
buttonmap[(SDL_GameControllerButton) evt.cbutton.button] = true;
BUTTONGLYPHS_keyboard_set_active(false);
controller = controllers[evt.cbutton.which];
BUTTONGLYPHS_update_layout(controller);
break;
case SDL_CONTROLLERBUTTONUP:
buttonmap[(SDL_GameControllerButton) evt.cbutton.button] = false;
break;
case SDL_CONTROLLERAXISMOTION:
{
const int threshold = getThreshold();
switch (evt.caxis.axis)
{
case SDL_CONTROLLER_AXIS_LEFTX:
if ( evt.caxis.value > -threshold &&
evt.caxis.value < threshold )
{
xVel = 0;
}
else
{
xVel = (evt.caxis.value > 0) ? 1 : -1;
}
break;
case SDL_CONTROLLER_AXIS_LEFTY:
if ( evt.caxis.value > -threshold &&
evt.caxis.value < threshold )
{
yVel = 0;
}
else
{
yVel = (evt.caxis.value > 0) ? 1 : -1;
}
break;
}
BUTTONGLYPHS_keyboard_set_active(false);
controller = controllers[evt.caxis.which];
BUTTONGLYPHS_update_layout(controller);
break;
}
case SDL_CONTROLLERDEVICEADDED:
{
controller = SDL_GameControllerOpen(evt.cdevice.which);
vlog_info(
"Opened SDL_GameController ID #%i, %s",
evt.cdevice.which,
SDL_GameControllerName(controller)
);
controllers[SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller))] = controller;
BUTTONGLYPHS_keyboard_set_active(false);
BUTTONGLYPHS_update_layout(controller);
break;
}
case SDL_CONTROLLERDEVICEREMOVED:
{
controller = controllers[evt.cdevice.which];
controllers.erase(evt.cdevice.which);
vlog_info("Closing %s", SDL_GameControllerName(controller));
SDL_GameControllerClose(controller);
if (controllers.empty())
{
BUTTONGLYPHS_keyboard_set_active(true);
}
break;
}
/* Window Events */
case SDL_WINDOWEVENT:
switch (evt.window.event)
{
/* Window Resize */
case SDL_WINDOWEVENT_RESIZED:
if (SDL_GetWindowFlags(
SDL_GetWindowFromID(evt.window.windowID)
) & SDL_WINDOW_INPUT_FOCUS)
{
resetWindow = true;
}
break;
/* Window Focus */
case SDL_WINDOWEVENT_FOCUS_GAINED:
if (!game.disablepause)
{
isActive = true;
if ((!game.disableaudiopause || !game.disabletemporaryaudiopause) && music.currentsong != -1)
{
music.resume();
music.resumeef();
}
}
if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "x11") == 0)
{
if (wasFullscreen)
{
gameScreen.isWindowed = false;
SDL_SetWindowFullscreen(
SDL_GetWindowFromID(evt.window.windowID),
SDL_WINDOW_FULLSCREEN_DESKTOP
);
}
}
SDL_DisableScreenSaver();
gameScreen.recacheTextures();
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
if (!game.disablepause)
{
isActive = false;
if (!game.disableaudiopause || !game.disabletemporaryaudiopause)
{
music.pause();
music.pauseef();
}
}
if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "x11") == 0)
{
wasFullscreen = !gameScreen.isWindowed;
gameScreen.isWindowed = true;
SDL_SetWindowFullscreen(
SDL_GetWindowFromID(evt.window.windowID),
0
);
}
SDL_EnableScreenSaver();
break;
/* Mouse Focus */
case SDL_WINDOWEVENT_ENTER:
SDL_DisableScreenSaver();
break;
case SDL_WINDOWEVENT_LEAVE:
SDL_EnableScreenSaver();
break;
}
break;
/* Quit Event */
case SDL_QUIT:
VVV_exit(0);
break;
}
switch (evt.type)
{
case SDL_KEYDOWN:
if (evt.key.repeat == 0)
{
hidemouse = true;
}
break;
case SDL_TEXTINPUT:
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERAXISMOTION:
hidemouse = true;
break;
case SDL_MOUSEMOTION:
case SDL_MOUSEBUTTONDOWN:
showmouse = true;
break;
}
}
mousetoggletimeout = changemousestate(
mousetoggletimeout,
showmouse,
hidemouse
);
if (fullscreenkeybind)
{
toggleFullscreen();
}
if (gameScreen.scalingMode == SCALING_STRETCH)
{
/* In this mode specifically, we have to fix the mouse coordinates */
int actualscreenwidth;
int actualscreenheight;
gameScreen.GetScreenSize(&actualscreenwidth, &actualscreenheight);
mousex = raw_mousex * SCREEN_WIDTH_PIXELS / actualscreenwidth;
mousey = raw_mousey * SCREEN_HEIGHT_PIXELS / actualscreenheight;
}
else
{
mousex = raw_mousex;
mousey = raw_mousey;
}
active_input_device_changed = keyboard_was_active != BUTTONGLYPHS_keyboard_is_active();
should_recompute_textboxes |= active_input_device_changed;
if (should_recompute_textboxes)
{
recomputetextboxes();
}
}
bool KeyPoll::isDown(SDL_Keycode key)
{
return keymap[key];
}
bool KeyPoll::isDown(std::vector<SDL_GameControllerButton> buttons)
{
for (size_t i = 0; i < buttons.size(); i += 1)
{
if (buttonmap[buttons[i]])
{
return true;
}
}
return false;
}
bool KeyPoll::isDown(SDL_GameControllerButton button)
{
return buttonmap[button];
}
bool KeyPoll::controllerButtonDown(void)
{
for (
SDL_GameControllerButton button = SDL_CONTROLLER_BUTTON_A;
button < SDL_CONTROLLER_BUTTON_DPAD_UP;
button = (SDL_GameControllerButton) (button + 1)
) {
if (isDown(button))
{
return true;
}
}
return false;
}
bool KeyPoll::controllerWantsLeft(bool includeVert)
{
return ( buttonmap[SDL_CONTROLLER_BUTTON_DPAD_LEFT] ||
xVel < 0 ||
( includeVert &&
( buttonmap[SDL_CONTROLLER_BUTTON_DPAD_UP] ||
yVel < 0 ) ) );
}
bool KeyPoll::controllerWantsRight(bool includeVert)
{
return ( buttonmap[SDL_CONTROLLER_BUTTON_DPAD_RIGHT] ||
xVel > 0 ||
( includeVert &&
( buttonmap[SDL_CONTROLLER_BUTTON_DPAD_DOWN] ||
yVel > 0 ) ) );
}
bool KeyPoll::controllerWantsUp(void)
{
return buttonmap[SDL_CONTROLLER_BUTTON_DPAD_UP] || yVel < 0;
}
bool KeyPoll::controllerWantsDown(void)
{
return buttonmap[SDL_CONTROLLER_BUTTON_DPAD_DOWN] || yVel > 0;
}