mkxp-z/src/eventthread.h
2025-03-20 00:32:23 -03:00

297 lines
5.8 KiB
C++

/*
** eventthread.h
**
** 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/>.
*/
#ifndef EVENTTHREAD_H
#define EVENTTHREAD_H
#include <SDL_scancode.h>
#include <SDL_mouse.h>
#include <SDL_mutex.h>
#include <SDL_atomic.h>
#include <SDL_gamecontroller.h>
#include <string>
#include <stdint.h>
#include "config.h"
#include "etc-internal.h"
#include "sdl-util.h"
#include "keybindings.h"
struct RGSSThreadData;
typedef struct MKXPZ_ALCDEVICE ALCdevice;
struct SDL_Window;
union SDL_Event;
#define MAX_FINGERS 4
class EventThread
{
public:
struct ControllerState {
int axes[SDL_CONTROLLER_AXIS_MAX];
bool buttons[SDL_CONTROLLER_BUTTON_MAX];
};
struct MouseState
{
int x, y;
bool inWindow;
bool buttons[32];
};
struct FingerState
{
bool down;
int x, y;
};
struct TouchState
{
FingerState fingers[MAX_FINGERS];
};
static uint8_t keyStates[SDL_NUM_SCANCODES];
static ControllerState controllerState;
static MouseState mouseState;
static TouchState touchState;
static SDL_atomic_t verticalScrollDistance;
std::string textInputBuffer;
void lockText(bool lock);
static bool allocUserEvents();
EventThread();
~EventThread();
void process(RGSSThreadData &rtData);
void cleanup();
/* Called from RGSS thread */
void requestFullscreenMode(bool mode);
void requestWindowResize(int width, int height);
void requestWindowReposition(int x, int y);
void requestWindowCenter();
void requestWindowRename(const char *title);
void requestShowCursor(bool mode);
void requestTextInputMode(bool mode);
void requestSettingsMenu();
void requestTerminate();
bool getFullscreen() const;
bool getShowCursor() const;
bool getControllerConnected() const;
SDL_GameController *controller() const;
void showMessageBox(const char *body, int flags = 0);
/* RGSS thread calls this once per frame */
void notifyFrame();
/* Called on game screen (size / offset) changes */
void notifyGameScreenChange(const SDL_Rect &screen);
const SourceDesc getLastInput();
void clearLastInput();
private:
static int eventFilter(void *, SDL_Event*);
void resetInputStates();
void setFullscreen(SDL_Window *, bool mode);
void updateCursorState(bool inWindow,
const SDL_Rect &screen);
void cursorTimer();
bool fullscreen;
bool showCursor;
SDL_GameController *ctrl;
AtomicFlag msgBoxDone;
SDL_mutex *textInputLock;
struct
{
AtomicFlag sendUpdates;
} fps;
};
/* Used to asynchronously inform the RGSS thread
* about certain value changes */
template<typename T>
struct UnidirMessage
{
UnidirMessage()
: mutex(SDL_CreateMutex()),
current(T())
{}
~UnidirMessage()
{
SDL_DestroyMutex(mutex);
}
/* Done from the sending side */
void post(const T &value)
{
SDL_LockMutex(mutex);
changed.set();
current = value;
SDL_UnlockMutex(mutex);
}
/* Done from the receiving side */
bool poll(T &out) const
{
if (!changed)
return false;
SDL_LockMutex(mutex);
out = current;
changed.clear();
SDL_UnlockMutex(mutex);
return true;
}
/* Done from either */
void get(T &out) const
{
SDL_LockMutex(mutex);
out = current;
SDL_UnlockMutex(mutex);
}
private:
SDL_mutex *mutex;
mutable AtomicFlag changed;
T current;
};
struct SyncPoint
{
/* Used by eventFilter to control sleep/wakeup */
void haltThreads();
void resumeThreads();
/* Used by RGSS thread */
bool mainSyncLocked();
void waitMainSync();
/* Used by secondary (audio) threads */
void passSecondarySync();
private:
struct Util
{
Util();
~Util();
void lock();
void unlock(bool multi);
void waitForUnlock();
AtomicFlag locked;
SDL_mutex *mut;
SDL_cond *cond;
};
Util mainSync;
Util reply;
Util secondSync;
};
struct RGSSThreadData
{
/* Main thread sets this to request RGSS thread to terminate */
AtomicFlag rqTerm;
/* In response, RGSS thread sets this to confirm
* that it received the request and isn't stuck */
AtomicFlag rqTermAck;
/* Set when F12 is pressed */
AtomicFlag rqReset;
/* Set when F12 is released */
AtomicFlag rqResetFinish;
// Set when window is being adjusted (resize, reposition)
AtomicFlag rqWindowAdjust;
EventThread *ethread;
UnidirMessage<Vec2i> windowSizeMsg;
UnidirMessage<Vec2i> drawableSizeMsg;
UnidirMessage<BDescVec> bindingUpdateMsg;
SyncPoint syncPoint;
const char *argv0;
SDL_Window *window;
ALCdevice *alcDev;
SDL_GLContext glContext;
Vec2 sizeResoRatio;
Vec2i screenOffset;
int scale;
const int refreshRate;
Config config;
std::string rgssErrorMsg;
RGSSThreadData(EventThread *ethread,
const char *argv0,
SDL_Window *window,
ALCdevice *alcDev,
int refreshRate,
int scalingFactor,
const Config& newconf,
SDL_GLContext ctx)
: ethread(ethread),
argv0(argv0),
window(window),
alcDev(alcDev),
sizeResoRatio(1, 1),
refreshRate(refreshRate),
scale(scalingFactor),
config(newconf),
glContext(ctx)
{
rqResetFinish.set();
}
};
#endif // EVENTTHREAD_H