/* ** eventthread.h ** ** This file is part of mkxp. ** ** Copyright (C) 2013 - 2021 Amaryllis Kulla ** ** 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 . */ #ifndef EVENTTHREAD_H #define EVENTTHREAD_H #ifndef MKXPZ_RETRO # include # include # include # include # include #endif // MKXPZ_RETRO #include #include #include "config.h" #include "etc-internal.h" #include "sdl-util.h" #ifndef MKXPZ_RETRO # include "keybindings.h" #endif // MKXPZ_RETRO #ifdef MKXPZ_RETRO # include #else typedef struct MKXPZ_ALCDEVICE ALCdevice; #endif // MKXPZ_RETRO struct SDL_Window; union SDL_Event; #define MAX_FINGERS 4 class EventThread { #ifndef MKXPZ_RETRO 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); 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; #endif // MKXPZ_RETRO }; #ifndef MKXPZ_RETRO /* Used to asynchronously inform the RGSS thread * about certain value changes */ template struct UnidirMessage { #ifndef MKXPZ_RETRO 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; #endif // MKXPZ_RETRO }; 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; }; #endif // MKXPZ_RETRO 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; #ifndef MKXPZ_RETRO UnidirMessage windowSizeMsg; UnidirMessage drawableSizeMsg; UnidirMessage bindingUpdateMsg; SyncPoint syncPoint; #endif // MKXPZ_RETRO const char *argv0; SDL_Window *window; ALCdevice *alcDev; #ifndef MKXPZ_RETRO SDL_GLContext glContext; #endif // MKXPZ_RETRO 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, #ifdef MKXPZ_RETRO const Config& newconf #else const Config& newconf, SDL_GLContext ctx #endif // MKXPZ_RETRO ) : ethread(ethread), argv0(argv0), window(window), alcDev(alcDev), sizeResoRatio(1, 1), refreshRate(refreshRate), scale(scalingFactor), #ifdef MKXPZ_RETRO config(newconf) #else config(newconf), glContext(ctx) #endif // MKXPZ_RETRO { rqResetFinish.set(); } }; #endif // EVENTTHREAD_H