mirror of
https://github.com/mkxp-z/mkxp-z.git
synced 2025-08-26 16:53:45 +02:00

This removes the need to have xxd installed and provides a portable way to specify the name of the output array (xxd has an `-n` option for this, but it isn't present in older versions of xxd), which helps reduce the possibility of symbol conflicts in libretro builds and also prevents portability issues since the name of xxd's output array depends on the relative path to the input file, which can break if Meson changes the structure of the build directory or if the user sets the build directory to a different location. Also, this custom executable declares the array as const so that it goes into the read-only data section of the binary instead of the data section.
949 lines
28 KiB
C++
949 lines
28 KiB
C++
/*
|
|
** eventthread.cpp
|
|
**
|
|
** This file is part of mkxp.
|
|
**
|
|
** Copyright (C) 2013 - 2021 Amaryllis Kulla <ancurio@mapleshrine.eu>
|
|
**
|
|
** mkxp is free software: you can redistribute it and/or modify
|
|
** it under the terms of the GNU General Public License as published by
|
|
** the Free Software Foundation, either version 2 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** mkxp is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with mkxp. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "eventthread.h"
|
|
|
|
#include <SDL_events.h>
|
|
#include <SDL_messagebox.h>
|
|
#include <SDL_timer.h>
|
|
#include <SDL_thread.h>
|
|
#include <SDL_touch.h>
|
|
#include <SDL_rect.h>
|
|
|
|
#include <al.h>
|
|
#include <alc.h>
|
|
#include <alext.h>
|
|
#include <cmath>
|
|
|
|
#include "sharedstate.h"
|
|
#include "graphics.h"
|
|
|
|
#ifndef MKXPZ_BUILD_XCODE
|
|
#include "settingsmenu.h"
|
|
#include "gamecontrollerdb.txt.xxd"
|
|
#else
|
|
#include "system/system.h"
|
|
#include "filesystem/filesystem.h"
|
|
#include "TouchBar.h"
|
|
#endif
|
|
|
|
#include "al-util.h"
|
|
#include "debugwriter.h"
|
|
|
|
#ifndef __APPLE__
|
|
#include "util/string-util.h"
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
typedef void (ALC_APIENTRY *LPALCDEVICEPAUSESOFT) (ALCdevice *device);
|
|
typedef void (ALC_APIENTRY *LPALCDEVICERESUMESOFT) (ALCdevice *device);
|
|
|
|
#define AL_DEVICE_PAUSE_FUN \
|
|
AL_FUN(DevicePause, LPALCDEVICEPAUSESOFT) \
|
|
AL_FUN(DeviceResume, LPALCDEVICERESUMESOFT)
|
|
|
|
struct ALCFunctions
|
|
{
|
|
#define AL_FUN(name, type) type name;
|
|
AL_DEVICE_PAUSE_FUN
|
|
#undef AL_FUN
|
|
} static alc;
|
|
|
|
static void
|
|
initALCFunctions(ALCdevice *alcDev)
|
|
{
|
|
if (!strstr(alcGetString(alcDev, ALC_EXTENSIONS), "ALC_SOFT_pause_device"))
|
|
return;
|
|
|
|
Debug() << "ALC_SOFT_pause_device present";
|
|
|
|
#define AL_FUN(name, type) alc. name = (type) alcGetProcAddress(alcDev, "alc" #name "SOFT");
|
|
AL_DEVICE_PAUSE_FUN;
|
|
#undef AL_FUN
|
|
}
|
|
|
|
#define HAVE_ALC_DEVICE_PAUSE alc.DevicePause
|
|
|
|
uint8_t EventThread::keyStates[];
|
|
EventThread::ControllerState EventThread::controllerState;
|
|
EventThread::MouseState EventThread::mouseState;
|
|
EventThread::TouchState EventThread::touchState;
|
|
SDL_atomic_t EventThread::verticalScrollDistance;
|
|
|
|
/* User event codes */
|
|
enum
|
|
{
|
|
REQUEST_SETFULLSCREEN = 0,
|
|
REQUEST_WINRESIZE,
|
|
REQUEST_WINREPOSITION,
|
|
REQUEST_WINRENAME,
|
|
REQUEST_WINCENTER,
|
|
REQUEST_MESSAGEBOX,
|
|
REQUEST_SETCURSORVISIBLE,
|
|
|
|
REQUEST_TEXTMODE,
|
|
|
|
REQUEST_SETTINGS,
|
|
|
|
UPDATE_FPS,
|
|
UPDATE_SCREEN_RECT,
|
|
|
|
EVENT_COUNT
|
|
};
|
|
|
|
static uint32_t usrIdStart;
|
|
|
|
bool EventThread::allocUserEvents()
|
|
{
|
|
usrIdStart = SDL_RegisterEvents(EVENT_COUNT);
|
|
|
|
if (usrIdStart == (uint32_t) -1)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
EventThread::EventThread()
|
|
: ctrl(0),
|
|
fullscreen(false),
|
|
showCursor(false)
|
|
{
|
|
textInputLock = SDL_CreateMutex();
|
|
}
|
|
|
|
EventThread::~EventThread()
|
|
{
|
|
SDL_DestroyMutex(textInputLock);
|
|
}
|
|
|
|
SDL_TimerID hideCursorTimerID = 0;
|
|
Uint32 cursorTimerCallback(Uint32 interval, void* param)
|
|
{
|
|
EventThread *ethread = static_cast<EventThread*>(param);
|
|
hideCursorTimerID = 0;
|
|
ethread->requestShowCursor(ethread->getShowCursor());
|
|
return 0;
|
|
}
|
|
void EventThread::cursorTimer()
|
|
{
|
|
SDL_RemoveTimer(hideCursorTimerID);
|
|
hideCursorTimerID = SDL_AddTimer(500, cursorTimerCallback, this);
|
|
}
|
|
|
|
void EventThread::process(RGSSThreadData &rtData)
|
|
{
|
|
SDL_Event event;
|
|
SDL_Window *win = rtData.window;
|
|
UnidirMessage<Vec2i> &windowSizeMsg = rtData.windowSizeMsg;
|
|
UnidirMessage<Vec2i> &drawableSizeMsg = rtData.drawableSizeMsg;
|
|
|
|
initALCFunctions(rtData.alcDev);
|
|
|
|
// XXX this function breaks input focus on OSX
|
|
#ifndef __APPLE__
|
|
SDL_SetEventFilter(eventFilter, &rtData);
|
|
#endif
|
|
|
|
fullscreen = rtData.config.fullscreen;
|
|
int toggleFSMod = rtData.config.anyAltToggleFS ? KMOD_ALT : KMOD_LALT;
|
|
|
|
bool displayingFPS = rtData.config.displayFPS;
|
|
|
|
if (displayingFPS || rtData.config.printFPS)
|
|
fps.sendUpdates.set();
|
|
|
|
bool cursorInWindow = false;
|
|
/* Will be updated eventually */
|
|
SDL_Rect gameScreen = { 0, 0, 0, 0 };
|
|
|
|
/* SDL doesn't send an initial FOCUS_GAINED event */
|
|
bool windowFocused = true;
|
|
|
|
bool terminate = false;
|
|
|
|
#ifdef MKXPZ_BUILD_XCODE
|
|
SDL_GameControllerAddMappingsFromFile(mkxp_fs::getPathForAsset("gamecontrollerdb", "txt").c_str());
|
|
#else
|
|
SDL_GameControllerAddMappingsFromRW(
|
|
SDL_RWFromConstMem(mkxp_assets_gamecontrollerdb_txt, sizeof mkxp_assets_gamecontrollerdb_txt),
|
|
1);
|
|
#endif
|
|
|
|
SDL_JoystickUpdate();
|
|
if (SDL_NumJoysticks() > 0 && SDL_IsGameController(0)) {
|
|
ctrl = SDL_GameControllerOpen(0);
|
|
}
|
|
|
|
char buffer[128];
|
|
|
|
char pendingTitle[128];
|
|
bool havePendingTitle = false;
|
|
|
|
bool resetting = false;
|
|
|
|
int winW, winH;
|
|
int i, rc;
|
|
|
|
SDL_DisplayMode dm = {0};
|
|
|
|
SDL_GetWindowSize(win, &winW, &winH);
|
|
|
|
// Just in case it's started when the window is opened
|
|
// for some dumb reason
|
|
SDL_StopTextInput();
|
|
|
|
textInputBuffer.clear();
|
|
#ifndef MKXPZ_BUILD_XCODE
|
|
SettingsMenu *sMenu = 0;
|
|
#else
|
|
// Will always be 0
|
|
void *sMenu = 0;
|
|
#endif
|
|
|
|
while (true)
|
|
{
|
|
if (!SDL_WaitEvent(&event))
|
|
{
|
|
Debug() << "EventThread: Event error";
|
|
break;
|
|
}
|
|
#ifndef MKXPZ_BUILD_XCODE
|
|
if (sMenu && sMenu->onEvent(event))
|
|
{
|
|
if (sMenu->destroyReq())
|
|
{
|
|
delete sMenu;
|
|
sMenu = 0;
|
|
|
|
updateCursorState(cursorInWindow && windowFocused, gameScreen);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
/* Preselect and discard unwanted events here */
|
|
switch (event.type)
|
|
{
|
|
case SDL_MOUSEBUTTONDOWN :
|
|
case SDL_MOUSEBUTTONUP :
|
|
case SDL_MOUSEMOTION :
|
|
if (event.button.which == SDL_TOUCH_MOUSEID)
|
|
continue;
|
|
break;
|
|
|
|
case SDL_FINGERDOWN :
|
|
case SDL_FINGERUP :
|
|
case SDL_FINGERMOTION :
|
|
if (event.tfinger.fingerId >= MAX_FINGERS)
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
/* Now process the rest */
|
|
switch (event.type)
|
|
{
|
|
case SDL_WINDOWEVENT :
|
|
switch (event.window.event)
|
|
{
|
|
case SDL_WINDOWEVENT_SIZE_CHANGED :
|
|
winW = event.window.data1;
|
|
winH = event.window.data2;
|
|
|
|
int drwW, drwH;
|
|
SDL_GL_GetDrawableSize(win, &drwW, &drwH);
|
|
|
|
windowSizeMsg.post(Vec2i(winW, winH));
|
|
drawableSizeMsg.post(Vec2i(drwW, drwH));
|
|
resetInputStates();
|
|
break;
|
|
|
|
case SDL_WINDOWEVENT_ENTER :
|
|
cursorInWindow = true;
|
|
mouseState.inWindow = true;
|
|
updateCursorState(cursorInWindow && windowFocused && !sMenu, gameScreen);
|
|
|
|
break;
|
|
|
|
case SDL_WINDOWEVENT_LEAVE :
|
|
cursorInWindow = false;
|
|
mouseState.inWindow = false;
|
|
updateCursorState(cursorInWindow && windowFocused && !sMenu, gameScreen);
|
|
|
|
break;
|
|
|
|
case SDL_WINDOWEVENT_CLOSE :
|
|
terminate = true;
|
|
|
|
break;
|
|
|
|
case SDL_WINDOWEVENT_FOCUS_GAINED :
|
|
windowFocused = true;
|
|
updateCursorState(cursorInWindow && windowFocused && !sMenu, gameScreen);
|
|
|
|
break;
|
|
|
|
case SDL_WINDOWEVENT_FOCUS_LOST :
|
|
windowFocused = false;
|
|
updateCursorState(cursorInWindow && windowFocused && !sMenu, gameScreen);
|
|
resetInputStates();
|
|
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case SDL_TEXTINPUT :
|
|
lockText(true);
|
|
if (textInputBuffer.size() < 512) {
|
|
textInputBuffer += event.text.text;
|
|
}
|
|
lockText(false);
|
|
break;
|
|
|
|
case SDL_QUIT :
|
|
terminate = true;
|
|
Debug() << "EventThread termination requested";
|
|
|
|
break;
|
|
|
|
case SDL_KEYDOWN :
|
|
if (event.key.keysym.scancode == SDL_SCANCODE_RETURN &&
|
|
(event.key.keysym.mod & toggleFSMod))
|
|
{
|
|
setFullscreen(win, !fullscreen);
|
|
if (!fullscreen && havePendingTitle)
|
|
{
|
|
SDL_SetWindowTitle(win, pendingTitle);
|
|
pendingTitle[0] = '\0';
|
|
havePendingTitle = false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (event.key.keysym.scancode == SDL_SCANCODE_F1 && rtData.config.enableSettings)
|
|
{
|
|
// Do not open settings menu until initializing shared state.
|
|
// Opening before initializing shared state will crash (segmentation fault).
|
|
if (!shState)
|
|
{
|
|
break;
|
|
}
|
|
|
|
#ifndef MKXPZ_BUILD_XCODE
|
|
if (!sMenu)
|
|
{
|
|
sMenu = new SettingsMenu(rtData);
|
|
updateCursorState(false, gameScreen);
|
|
}
|
|
|
|
sMenu->raise();
|
|
#else
|
|
openSettingsWindow();
|
|
#endif
|
|
}
|
|
|
|
if (event.key.keysym.scancode == SDL_SCANCODE_F2)
|
|
{
|
|
if (!displayingFPS)
|
|
{
|
|
|
|
fps.sendUpdates.set();
|
|
displayingFPS = true;
|
|
}
|
|
else
|
|
{
|
|
displayingFPS = false;
|
|
|
|
if (!rtData.config.printFPS)
|
|
fps.sendUpdates.clear();
|
|
|
|
if (fullscreen)
|
|
{
|
|
/* Prevent fullscreen flicker */
|
|
strncpy(pendingTitle, rtData.config.windowTitle.c_str(),
|
|
sizeof(pendingTitle));
|
|
havePendingTitle = true;
|
|
|
|
break;
|
|
}
|
|
|
|
SDL_SetWindowTitle(win, rtData.config.windowTitle.c_str());
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (event.key.keysym.scancode == SDL_SCANCODE_F12)
|
|
{
|
|
if (!rtData.config.enableReset)
|
|
break;
|
|
|
|
if (resetting)
|
|
break;
|
|
|
|
resetting = true;
|
|
rtData.rqResetFinish.clear();
|
|
rtData.rqReset.set();
|
|
break;
|
|
}
|
|
|
|
keyStates[event.key.keysym.scancode] = true;
|
|
break;
|
|
|
|
case SDL_KEYUP :
|
|
if (event.key.keysym.scancode == SDL_SCANCODE_F12)
|
|
{
|
|
if (!rtData.config.enableReset)
|
|
break;
|
|
|
|
resetting = false;
|
|
rtData.rqResetFinish.set();
|
|
break;
|
|
}
|
|
|
|
keyStates[event.key.keysym.scancode] = false;
|
|
break;
|
|
|
|
case SDL_CONTROLLERBUTTONDOWN:
|
|
controllerState.buttons[event.cbutton.button] = true;
|
|
break;
|
|
|
|
case SDL_CONTROLLERBUTTONUP:
|
|
controllerState.buttons[event.cbutton.button] = false;
|
|
break;
|
|
|
|
case SDL_CONTROLLERAXISMOTION:
|
|
controllerState.axes[event.caxis.axis] = event.caxis.value;
|
|
break;
|
|
|
|
case SDL_CONTROLLERDEVICEADDED:
|
|
if (event.cdevice.which > 0)
|
|
break;
|
|
|
|
ctrl = SDL_GameControllerOpen(0);
|
|
break;
|
|
|
|
case SDL_CONTROLLERDEVICEREMOVED:
|
|
resetInputStates();
|
|
ctrl = 0;
|
|
break;
|
|
|
|
case SDL_MOUSEBUTTONDOWN :
|
|
mouseState.buttons[event.button.button] = true;
|
|
break;
|
|
|
|
case SDL_MOUSEBUTTONUP :
|
|
mouseState.buttons[event.button.button] = false;
|
|
break;
|
|
|
|
case SDL_MOUSEMOTION :
|
|
mouseState.x = event.motion.x;
|
|
mouseState.y = event.motion.y;
|
|
cursorTimer();
|
|
updateCursorState(cursorInWindow, gameScreen);
|
|
break;
|
|
|
|
case SDL_MOUSEWHEEL :
|
|
/* Only consider vertical scrolling for now */
|
|
SDL_AtomicAdd(&verticalScrollDistance, event.wheel.y);
|
|
|
|
case SDL_FINGERDOWN :
|
|
i = event.tfinger.fingerId;
|
|
touchState.fingers[i].down = true;
|
|
|
|
case SDL_FINGERMOTION :
|
|
i = event.tfinger.fingerId;
|
|
touchState.fingers[i].x = event.tfinger.x * winW;
|
|
touchState.fingers[i].y = event.tfinger.y * winH;
|
|
break;
|
|
|
|
case SDL_FINGERUP :
|
|
i = event.tfinger.fingerId;
|
|
memset(&touchState.fingers[i], 0, sizeof(touchState.fingers[0]));
|
|
break;
|
|
|
|
default :
|
|
/* Handle user events */
|
|
switch(event.type - usrIdStart)
|
|
{
|
|
case REQUEST_SETFULLSCREEN :
|
|
setFullscreen(win, static_cast<bool>(event.user.code));
|
|
break;
|
|
|
|
case REQUEST_WINRESIZE :
|
|
SDL_SetWindowSize(win, event.window.data1, event.window.data2);
|
|
rtData.rqWindowAdjust.clear();
|
|
break;
|
|
|
|
case REQUEST_WINREPOSITION :
|
|
SDL_SetWindowPosition(win, event.window.data1, event.window.data2);
|
|
rtData.rqWindowAdjust.clear();
|
|
break;
|
|
|
|
case REQUEST_WINCENTER :
|
|
rc = SDL_GetDesktopDisplayMode(SDL_GetWindowDisplayIndex(win), &dm);
|
|
if (!rc)
|
|
SDL_SetWindowPosition(win,
|
|
(dm.w / 2) - (winW / 2),
|
|
(dm.h / 2) - (winH / 2));
|
|
rtData.rqWindowAdjust.clear();
|
|
break;
|
|
|
|
case REQUEST_WINRENAME :
|
|
rtData.config.windowTitle = (const char*)event.user.data1;
|
|
SDL_SetWindowTitle(win, rtData.config.windowTitle.c_str());
|
|
break;
|
|
|
|
case REQUEST_TEXTMODE :
|
|
if (event.user.code)
|
|
{
|
|
SDL_StartTextInput();
|
|
lockText(true);
|
|
textInputBuffer.clear();
|
|
lockText(false);
|
|
}
|
|
else
|
|
{
|
|
SDL_StopTextInput();
|
|
lockText(true);
|
|
textInputBuffer.clear();
|
|
lockText(false);
|
|
}
|
|
break;
|
|
|
|
case REQUEST_MESSAGEBOX :
|
|
{
|
|
#ifndef __APPLE__
|
|
// Try to format the message with additional newlines
|
|
std::string message = copyWithNewlines((const char*) event.user.data1,
|
|
70);
|
|
SDL_ShowSimpleMessageBox(event.user.code,
|
|
rtData.config.windowTitle.c_str(),
|
|
message.c_str(), win);
|
|
#else
|
|
SDL_ShowSimpleMessageBox(event.user.code,
|
|
rtData.config.windowTitle.c_str(),
|
|
(const char*)event.user.data1, win);
|
|
#endif
|
|
free(event.user.data1);
|
|
msgBoxDone.set();
|
|
break;
|
|
}
|
|
case REQUEST_SETCURSORVISIBLE :
|
|
showCursor = event.user.code;
|
|
updateCursorState(cursorInWindow, gameScreen);
|
|
break;
|
|
|
|
case REQUEST_SETTINGS :
|
|
#ifndef MKXPZ_BUILD_XCODE
|
|
if (!sMenu)
|
|
{
|
|
sMenu = new SettingsMenu(rtData);
|
|
updateCursorState(false, gameScreen);
|
|
}
|
|
|
|
sMenu->raise();
|
|
#else
|
|
openSettingsWindow();
|
|
#endif
|
|
break;
|
|
|
|
case UPDATE_FPS :
|
|
if (rtData.config.printFPS)
|
|
Debug() << "FPS:" << event.user.code;
|
|
|
|
if (!fps.sendUpdates)
|
|
break;
|
|
|
|
snprintf(buffer, sizeof(buffer), "%s - %d FPS",
|
|
rtData.config.windowTitle.c_str(), event.user.code);
|
|
|
|
/* Updating the window title in fullscreen
|
|
* mode seems to cause flickering */
|
|
if (fullscreen)
|
|
{
|
|
strncpy(pendingTitle, buffer, sizeof(pendingTitle));
|
|
havePendingTitle = true;
|
|
|
|
break;
|
|
}
|
|
|
|
SDL_SetWindowTitle(win, buffer);
|
|
break;
|
|
|
|
case UPDATE_SCREEN_RECT :
|
|
gameScreen.x = event.user.windowID;
|
|
gameScreen.y = event.user.code;
|
|
gameScreen.w = reinterpret_cast<intptr_t>(event.user.data1);
|
|
gameScreen.h = reinterpret_cast<intptr_t>(event.user.data2);
|
|
updateCursorState(cursorInWindow, gameScreen);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (terminate)
|
|
break;
|
|
}
|
|
|
|
/* Just in case */
|
|
rtData.syncPoint.resumeThreads();
|
|
|
|
if (SDL_GameControllerGetAttached(ctrl))
|
|
SDL_GameControllerClose(ctrl);
|
|
|
|
#ifndef MKXPZ_BUILD_XCODE
|
|
delete sMenu;
|
|
#endif
|
|
}
|
|
|
|
int EventThread::eventFilter(void *data, SDL_Event *event)
|
|
{
|
|
RGSSThreadData &rtData = *static_cast<RGSSThreadData*>(data);
|
|
|
|
switch (event->type)
|
|
{
|
|
case SDL_APP_WILLENTERBACKGROUND :
|
|
Debug() << "SDL_APP_WILLENTERBACKGROUND";
|
|
|
|
if (HAVE_ALC_DEVICE_PAUSE)
|
|
alc.DevicePause(rtData.alcDev);
|
|
|
|
rtData.syncPoint.haltThreads();
|
|
|
|
return 0;
|
|
|
|
case SDL_APP_DIDENTERBACKGROUND :
|
|
Debug() << "SDL_APP_DIDENTERBACKGROUND";
|
|
return 0;
|
|
|
|
case SDL_APP_WILLENTERFOREGROUND :
|
|
Debug() << "SDL_APP_WILLENTERFOREGROUND";
|
|
return 0;
|
|
|
|
case SDL_APP_DIDENTERFOREGROUND :
|
|
Debug() << "SDL_APP_DIDENTERFOREGROUND";
|
|
|
|
if (HAVE_ALC_DEVICE_PAUSE)
|
|
alc.DeviceResume(rtData.alcDev);
|
|
|
|
rtData.syncPoint.resumeThreads();
|
|
|
|
return 0;
|
|
|
|
case SDL_APP_TERMINATING :
|
|
Debug() << "SDL_APP_TERMINATING";
|
|
return 0;
|
|
|
|
case SDL_APP_LOWMEMORY :
|
|
Debug() << "SDL_APP_LOWMEMORY";
|
|
return 0;
|
|
|
|
// case SDL_RENDER_TARGETS_RESET :
|
|
// Debug() << "****** SDL_RENDER_TARGETS_RESET";
|
|
// return 0;
|
|
|
|
// case SDL_RENDER_DEVICE_RESET :
|
|
// Debug() << "****** SDL_RENDER_DEVICE_RESET";
|
|
// return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void EventThread::cleanup()
|
|
{
|
|
SDL_Event event;
|
|
|
|
while (SDL_PollEvent(&event))
|
|
if ((event.type - usrIdStart) == REQUEST_MESSAGEBOX)
|
|
free(event.user.data1);
|
|
}
|
|
|
|
void EventThread::resetInputStates()
|
|
{
|
|
memset(&keyStates, 0, sizeof(keyStates));
|
|
memset(&controllerState, 0, sizeof(controllerState));
|
|
memset(&mouseState.buttons, 0, sizeof(mouseState.buttons));
|
|
memset(&touchState, 0, sizeof(touchState));
|
|
}
|
|
|
|
void EventThread::setFullscreen(SDL_Window *win, bool mode)
|
|
{
|
|
SDL_SetWindowFullscreen
|
|
(win, mode ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
|
fullscreen = mode;
|
|
}
|
|
|
|
void EventThread::updateCursorState(bool inWindow,
|
|
const SDL_Rect &screen)
|
|
{
|
|
SDL_Point pos = { mouseState.x, mouseState.y };
|
|
bool inScreen = inWindow && SDL_PointInRect(&pos, &screen);
|
|
|
|
if (inScreen)
|
|
SDL_ShowCursor(showCursor || hideCursorTimerID ? SDL_TRUE : SDL_FALSE);
|
|
else
|
|
SDL_ShowCursor(SDL_TRUE);
|
|
}
|
|
|
|
void EventThread::requestTerminate()
|
|
{
|
|
SDL_Event event;
|
|
event.type = SDL_QUIT;
|
|
SDL_PushEvent(&event);
|
|
}
|
|
|
|
void EventThread::requestFullscreenMode(bool mode)
|
|
{
|
|
if (mode == fullscreen)
|
|
return;
|
|
|
|
SDL_Event event;
|
|
event.type = usrIdStart + REQUEST_SETFULLSCREEN;
|
|
event.user.code = static_cast<Sint32>(mode);
|
|
SDL_PushEvent(&event);
|
|
}
|
|
|
|
void EventThread::requestWindowResize(int width, int height)
|
|
{
|
|
shState->rtData().rqWindowAdjust.set();
|
|
SDL_Event event;
|
|
event.type = usrIdStart + REQUEST_WINRESIZE;
|
|
event.window.data1 = width;
|
|
event.window.data2 = height;
|
|
SDL_PushEvent(&event);
|
|
}
|
|
|
|
void EventThread::requestWindowReposition(int x, int y)
|
|
{
|
|
shState->rtData().rqWindowAdjust.set();
|
|
SDL_Event event;
|
|
event.type = usrIdStart + REQUEST_WINREPOSITION;
|
|
event.window.data1 = x;
|
|
event.window.data2 = y;
|
|
SDL_PushEvent(&event);
|
|
}
|
|
|
|
void EventThread::requestWindowCenter()
|
|
{
|
|
shState->rtData().rqWindowAdjust.set();
|
|
SDL_Event event;
|
|
event.type = usrIdStart + REQUEST_WINCENTER;
|
|
SDL_PushEvent(&event);
|
|
}
|
|
|
|
void EventThread::requestWindowRename(const char *title)
|
|
{
|
|
SDL_Event event;
|
|
event.type = usrIdStart + REQUEST_WINRENAME;
|
|
event.user.data1 = (void*)title;
|
|
SDL_PushEvent(&event);
|
|
}
|
|
|
|
void EventThread::requestShowCursor(bool mode)
|
|
{
|
|
SDL_Event event;
|
|
event.type = usrIdStart + REQUEST_SETCURSORVISIBLE;
|
|
event.user.code = mode;
|
|
SDL_PushEvent(&event);
|
|
}
|
|
|
|
void EventThread::requestTextInputMode(bool mode)
|
|
{
|
|
SDL_Event event;
|
|
event.type = usrIdStart + REQUEST_TEXTMODE;
|
|
event.user.code = mode;
|
|
SDL_PushEvent(&event);
|
|
}
|
|
|
|
void EventThread::requestSettingsMenu()
|
|
{
|
|
SDL_Event event;
|
|
event.type = usrIdStart + REQUEST_SETTINGS;
|
|
SDL_PushEvent(&event);
|
|
}
|
|
|
|
void EventThread::showMessageBox(const char *body, int flags)
|
|
{
|
|
msgBoxDone.clear();
|
|
|
|
// mkxp has already been asked to quit.
|
|
// Don't break things if the window wants to close
|
|
if (shState->rtData().rqTerm)
|
|
return;
|
|
|
|
SDL_Event event;
|
|
event.user.code = flags;
|
|
event.user.data1 = strdup(body);
|
|
event.type = usrIdStart + REQUEST_MESSAGEBOX;
|
|
SDL_PushEvent(&event);
|
|
|
|
/* Keep repainting screen while box is open */
|
|
try{
|
|
shState->graphics().repaintWait(msgBoxDone);
|
|
}catch(...){}
|
|
/* Prevent endless loops */
|
|
resetInputStates();
|
|
}
|
|
|
|
bool EventThread::getFullscreen() const
|
|
{
|
|
return fullscreen;
|
|
}
|
|
|
|
bool EventThread::getShowCursor() const
|
|
{
|
|
return showCursor;
|
|
}
|
|
|
|
bool EventThread::getControllerConnected() const
|
|
{
|
|
return ctrl != 0;
|
|
}
|
|
|
|
SDL_GameController *EventThread::controller() const
|
|
{
|
|
return ctrl;
|
|
}
|
|
|
|
void EventThread::notifyFrame()
|
|
{
|
|
#ifdef MKXPZ_BUILD_XCODE
|
|
uint32_t frames = round(shState->graphics().averageFrameRate());
|
|
updateTouchBarFPSDisplay(frames);
|
|
#endif
|
|
if (!fps.sendUpdates)
|
|
return;
|
|
|
|
SDL_Event event;
|
|
#ifdef MKXPZ_BUILD_XCODE
|
|
event.user.code = frames;
|
|
#else
|
|
event.user.code = round(shState->graphics().averageFrameRate());
|
|
#endif
|
|
event.user.type = usrIdStart + UPDATE_FPS;
|
|
SDL_PushEvent(&event);
|
|
}
|
|
|
|
void EventThread::notifyGameScreenChange(const SDL_Rect &screen)
|
|
{
|
|
/* We have to get a bit hacky here to fit the rectangle
|
|
* data into the user event struct */
|
|
SDL_Event event;
|
|
event.type = usrIdStart + UPDATE_SCREEN_RECT;
|
|
event.user.windowID = screen.x;
|
|
event.user.code = screen.y;
|
|
event.user.data1 = reinterpret_cast<void*>(screen.w);
|
|
event.user.data2 = reinterpret_cast<void*>(screen.h);
|
|
SDL_PushEvent(&event);
|
|
}
|
|
|
|
void EventThread::lockText(bool lock)
|
|
{
|
|
lock ? SDL_LockMutex(textInputLock) : SDL_UnlockMutex(textInputLock);
|
|
}
|
|
|
|
void SyncPoint::haltThreads()
|
|
{
|
|
if (mainSync.locked)
|
|
return;
|
|
|
|
/* Lock the reply sync first to avoid races */
|
|
reply.lock();
|
|
|
|
/* Lock main sync and sleep until RGSS thread
|
|
* reports back */
|
|
mainSync.lock();
|
|
reply.waitForUnlock();
|
|
|
|
/* Now that the RGSS thread is asleep, we can
|
|
* safely put the other threads to sleep as well
|
|
* without causing deadlocks */
|
|
secondSync.lock();
|
|
}
|
|
|
|
void SyncPoint::resumeThreads()
|
|
{
|
|
if (!mainSync.locked)
|
|
return;
|
|
|
|
mainSync.unlock(false);
|
|
secondSync.unlock(true);
|
|
}
|
|
|
|
bool SyncPoint::mainSyncLocked()
|
|
{
|
|
return mainSync.locked;
|
|
}
|
|
|
|
void SyncPoint::waitMainSync()
|
|
{
|
|
reply.unlock(false);
|
|
mainSync.waitForUnlock();
|
|
}
|
|
|
|
void SyncPoint::passSecondarySync()
|
|
{
|
|
if (!secondSync.locked)
|
|
return;
|
|
|
|
secondSync.waitForUnlock();
|
|
}
|
|
|
|
SyncPoint::Util::Util()
|
|
{
|
|
mut = SDL_CreateMutex();
|
|
cond = SDL_CreateCond();
|
|
}
|
|
|
|
SyncPoint::Util::~Util()
|
|
{
|
|
SDL_DestroyCond(cond);
|
|
SDL_DestroyMutex(mut);
|
|
}
|
|
|
|
void SyncPoint::Util::lock()
|
|
{
|
|
locked.set();
|
|
}
|
|
|
|
void SyncPoint::Util::unlock(bool multi)
|
|
{
|
|
locked.clear();
|
|
|
|
if (multi)
|
|
SDL_CondBroadcast(cond);
|
|
else
|
|
SDL_CondSignal(cond);
|
|
}
|
|
|
|
void SyncPoint::Util::waitForUnlock()
|
|
{
|
|
SDL_LockMutex(mut);
|
|
|
|
while (locked)
|
|
SDL_CondWait(cond, mut);
|
|
|
|
SDL_UnlockMutex(mut);
|
|
}
|