From 836f76da898ee514022d7b2c84a88f6dcc1a4c0b Mon Sep 17 00:00:00 2001 From: Stenzek Date: Wed, 24 Oct 2018 15:50:54 +1000 Subject: [PATCH] DolphinNoGUI: Runtime selection of platform --- Source/Core/DolphinNoGUI/CMakeLists.txt | 6 + Source/Core/DolphinNoGUI/MainNoGUI.cpp | 342 +++--------------- Source/Core/DolphinNoGUI/Platform.cpp | 53 +++ Source/Core/DolphinNoGUI/Platform.h | 48 +++ Source/Core/DolphinNoGUI/PlatformHeadless.cpp | 50 +++ Source/Core/DolphinNoGUI/PlatformX11.cpp | 270 ++++++++++++++ 6 files changed, 474 insertions(+), 295 deletions(-) create mode 100644 Source/Core/DolphinNoGUI/Platform.cpp create mode 100644 Source/Core/DolphinNoGUI/Platform.h create mode 100644 Source/Core/DolphinNoGUI/PlatformHeadless.cpp create mode 100644 Source/Core/DolphinNoGUI/PlatformX11.cpp diff --git a/Source/Core/DolphinNoGUI/CMakeLists.txt b/Source/Core/DolphinNoGUI/CMakeLists.txt index 34890d294e..b14152d194 100644 --- a/Source/Core/DolphinNoGUI/CMakeLists.txt +++ b/Source/Core/DolphinNoGUI/CMakeLists.txt @@ -3,9 +3,15 @@ if(NOT((ENABLE_X11 AND X11_FOUND) OR ENABLE_HEADLESS)) endif() add_executable(dolphin-nogui + Platform.cpp + PlatformHeadless.cpp MainNoGUI.cpp ) +if(ENABLE_X11 AND X11_FOUND) + target_sources(dolphin-nogui PRIVATE PlatformX11.cpp) +endif() + set_target_properties(dolphin-nogui PROPERTIES OUTPUT_NAME dolphin-emu-nogui) target_link_libraries(dolphin-nogui diff --git a/Source/Core/DolphinNoGUI/MainNoGUI.cpp b/Source/Core/DolphinNoGUI/MainNoGUI.cpp index 697e4cbe6d..c5ec03430f 100644 --- a/Source/Core/DolphinNoGUI/MainNoGUI.cpp +++ b/Source/Core/DolphinNoGUI/MainNoGUI.cpp @@ -2,30 +2,23 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include "DolphinNoGUI/Platform.h" + #include #include #include #include #include #include -#include +#ifndef _WIN32 #include - -#include "Common/CommonTypes.h" -#include "Common/Event.h" -#include "Common/Flag.h" -#include "Common/Logging/LogManager.h" -#include "Common/MsgHandler.h" +#endif #include "Core/Analytics.h" #include "Core/Boot/Boot.h" #include "Core/BootManager.h" -#include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/Host.h" -#include "Core/IOS/IOS.h" -#include "Core/IOS/STM/STM.h" -#include "Core/State.h" #include "UICommon/CommandLineParse.h" #ifdef USE_DISCORD_PRESENCE @@ -36,49 +29,22 @@ #include "VideoCommon/RenderBase.h" #include "VideoCommon/VideoBackendBase.h" -static bool rendererHasFocus = true; -static bool rendererIsFullscreen = false; -static Common::Flag s_running{true}; -static Common::Flag s_shutdown_requested{false}; -static Common::Flag s_tried_graceful_shutdown{false}; +static std::unique_ptr s_platform; static void signal_handler(int) { const char message[] = "A signal was received. A second signal will force Dolphin to stop.\n"; +#ifdef _WIN32 + puts(message); +#else if (write(STDERR_FILENO, message, sizeof(message)) < 0) { } - s_shutdown_requested.Set(); +#endif + + s_platform->RequestShutdown(); } -namespace ProcessorInterface -{ -void PowerButton_Tap(); -} - -class Platform -{ -public: - virtual void Init() {} - virtual void SetTitle(const std::string& title) {} - virtual void MainLoop() - { - while (s_running.IsSet()) - { - Core::HostDispatchJobs(); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - } - virtual void Shutdown() {} - virtual ~Platform() {} - - virtual WindowSystemType GetWindowSystem() const { return WindowSystemType::Headless; } - virtual void* GetDisplayHandle() const { return nullptr; } - virtual void* GetWindowHandle() const { return nullptr; } -}; - -static Platform* platform; - void Host_NotifyMapLoaded() { } @@ -86,18 +52,16 @@ void Host_RefreshDSPDebuggerWindow() { } -static Common::Event updateMainFrameEvent; +static Common::Event s_update_main_frame_event; void Host_Message(HostMessageID id) { if (id == HostMessageID::WMUserStop) - s_running.Clear(); - if (id == HostMessageID::WMUserJobDispatch || id == HostMessageID::WMUserStop) - updateMainFrameEvent.Set(); + s_platform->Stop(); } void Host_UpdateTitle(const std::string& title) { - platform->SetTitle(title); + s_platform->SetTitle(title); } void Host_UpdateDisasmDialog() @@ -106,7 +70,7 @@ void Host_UpdateDisasmDialog() void Host_UpdateMainFrame() { - updateMainFrameEvent.Set(); + s_update_main_frame_event.Set(); } void Host_RequestRenderWindowSize(int width, int height) @@ -120,12 +84,12 @@ bool Host_UINeedsControllerState() bool Host_RendererHasFocus() { - return rendererHasFocus; + return s_platform->IsWindowFocused(); } bool Host_RendererIsFullscreen() { - return rendererIsFullscreen; + return s_platform->IsWindowFullscreen(); } void Host_YieldToUI() @@ -136,232 +100,35 @@ void Host_UpdateProgressDialog(const char* caption, int position, int total) { } +static std::unique_ptr GetPlatform(const optparse::Values& options) +{ + std::string platform_name = static_cast(options.get("platform")); + #if HAVE_X11 -#include -#include -#include -#include "UICommon/X11Utils.h" -#ifndef HOST_NAME_MAX -#include -#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX + if (platform_name == "x11" || platform_name.empty()) + return Platform::CreateX11Platform(); #endif -class PlatformX11 : public Platform -{ - Display* dpy; - Window win; - Cursor blankCursor = None; -#if defined(HAVE_XRANDR) && HAVE_XRANDR - X11Utils::XRRConfiguration* XRRConfig; -#endif + if (platform_name == "headless" || platform_name.empty()) + return Platform::CreateHeadlessPlatform(); - void Init() override - { - XInitThreads(); - dpy = XOpenDisplay(nullptr); - if (!dpy) - { - PanicAlert("No X11 display found"); - exit(1); - } - - win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), SConfig::GetInstance().iRenderWindowXPos, - SConfig::GetInstance().iRenderWindowYPos, - SConfig::GetInstance().iRenderWindowWidth, - SConfig::GetInstance().iRenderWindowHeight, 0, 0, BlackPixel(dpy, 0)); - XSelectInput(dpy, win, StructureNotifyMask | KeyPressMask | FocusChangeMask); - Atom wmProtocols[1]; - wmProtocols[0] = XInternAtom(dpy, "WM_DELETE_WINDOW", True); - XSetWMProtocols(dpy, win, wmProtocols, 1); - pid_t pid = getpid(); - XChangeProperty(dpy, win, XInternAtom(dpy, "_NET_WM_PID", False), XA_CARDINAL, 32, - PropModeReplace, reinterpret_cast(&pid), 1); - char host_name[HOST_NAME_MAX] = ""; - if (!gethostname(host_name, sizeof(host_name))) - { - XTextProperty wmClientMachine = {reinterpret_cast(host_name), XA_STRING, 8, - strlen(host_name)}; - XSetWMClientMachine(dpy, win, &wmClientMachine); - } - XMapRaised(dpy, win); - XFlush(dpy); - - if (SConfig::GetInstance().bDisableScreenSaver) - X11Utils::InhibitScreensaver(win, true); - -#if defined(HAVE_XRANDR) && HAVE_XRANDR - XRRConfig = new X11Utils::XRRConfiguration(dpy, win); -#endif - - if (SConfig::GetInstance().bHideCursor) - { - // make a blank cursor - Pixmap Blank; - XColor DummyColor; - char ZeroData[1] = {0}; - Blank = XCreateBitmapFromData(dpy, win, ZeroData, 1, 1); - blankCursor = XCreatePixmapCursor(dpy, Blank, Blank, &DummyColor, &DummyColor, 0, 0); - XFreePixmap(dpy, Blank); - XDefineCursor(dpy, win, blankCursor); - } - } - - void SetTitle(const std::string& string) override { XStoreName(dpy, win, string.c_str()); } - void MainLoop() override - { - bool fullscreen = SConfig::GetInstance().bFullscreen; - if (fullscreen) - { - rendererIsFullscreen = X11Utils::ToggleFullscreen(dpy, win); -#if defined(HAVE_XRANDR) && HAVE_XRANDR - XRRConfig->ToggleDisplayMode(True); -#endif - } - - // The actual loop - while (s_running.IsSet()) - { - if (s_shutdown_requested.TestAndClear()) - { - const auto ios = IOS::HLE::GetIOS(); - const auto stm = ios ? ios->GetDeviceByName("/dev/stm/eventhook") : nullptr; - if (!s_tried_graceful_shutdown.IsSet() && stm && - std::static_pointer_cast(stm)->HasHookInstalled()) - { - ProcessorInterface::PowerButton_Tap(); - s_tried_graceful_shutdown.Set(); - } - else - { - s_running.Clear(); - } - } - - XEvent event; - KeySym key; - for (int num_events = XPending(dpy); num_events > 0; num_events--) - { - XNextEvent(dpy, &event); - switch (event.type) - { - case KeyPress: - key = XLookupKeysym((XKeyEvent*)&event, 0); - if (key == XK_Escape) - { - s_shutdown_requested.Set(); - } - else if (key == XK_F10) - { - if (Core::GetState() == Core::State::Running) - { - if (SConfig::GetInstance().bHideCursor) - XUndefineCursor(dpy, win); - Core::SetState(Core::State::Paused); - } - else - { - if (SConfig::GetInstance().bHideCursor) - XDefineCursor(dpy, win, blankCursor); - Core::SetState(Core::State::Running); - } - } - else if ((key == XK_Return) && (event.xkey.state & Mod1Mask)) - { - fullscreen = !fullscreen; - X11Utils::ToggleFullscreen(dpy, win); -#if defined(HAVE_XRANDR) && HAVE_XRANDR - XRRConfig->ToggleDisplayMode(fullscreen); -#endif - } - else if (key >= XK_F1 && key <= XK_F8) - { - int slot_number = key - XK_F1 + 1; - if (event.xkey.state & ShiftMask) - State::Save(slot_number); - else - State::Load(slot_number); - } - else if (key == XK_F9) - Core::SaveScreenShot(); - else if (key == XK_F11) - State::LoadLastSaved(); - else if (key == XK_F12) - { - if (event.xkey.state & ShiftMask) - State::UndoLoadState(); - else - State::UndoSaveState(); - } - break; - case FocusIn: - rendererHasFocus = true; - if (SConfig::GetInstance().bHideCursor && Core::GetState() != Core::State::Paused) - XDefineCursor(dpy, win, blankCursor); - break; - case FocusOut: - rendererHasFocus = false; - if (SConfig::GetInstance().bHideCursor) - XUndefineCursor(dpy, win); - break; - case ClientMessage: - if ((unsigned long)event.xclient.data.l[0] == XInternAtom(dpy, "WM_DELETE_WINDOW", False)) - s_shutdown_requested.Set(); - break; - case ConfigureNotify: - { - if (g_renderer) - g_renderer->ResizeSurface(); - } - break; - } - } - if (!fullscreen) - { - Window winDummy; - unsigned int borderDummy, depthDummy; - XGetGeometry(dpy, win, &winDummy, &SConfig::GetInstance().iRenderWindowXPos, - &SConfig::GetInstance().iRenderWindowYPos, - (unsigned int*)&SConfig::GetInstance().iRenderWindowWidth, - (unsigned int*)&SConfig::GetInstance().iRenderWindowHeight, &borderDummy, - &depthDummy); - rendererIsFullscreen = false; - } - Core::HostDispatchJobs(); - usleep(100000); - } - } - - void Shutdown() override - { -#if defined(HAVE_XRANDR) && HAVE_XRANDR - delete XRRConfig; -#endif - - if (SConfig::GetInstance().bHideCursor) - XFreeCursor(dpy, blankCursor); - - XCloseDisplay(dpy); - } - - WindowSystemType GetWindowSystem() const override { return WindowSystemType::X11; } - void* GetDisplayHandle() const override { return static_cast(dpy); } - void* GetWindowHandle() const override { return reinterpret_cast(win); } -}; -#endif - -static Platform* GetPlatform() -{ -#if defined(USE_HEADLESS) - return new Platform(); -#elif HAVE_X11 - return new PlatformX11(); -#endif return nullptr; } int main(int argc, char* argv[]) { auto parser = CommandLineParse::CreateParser(CommandLineParse::ParserOptions::OmitGUIOptions); + parser->add_option("-p", "--platform") + .action("store") + .help("Window platform to use [%choices]") + .choices({ + "headless" +#if HAVE_X11 + , + "x11" +#endif + }); + optparse::Values& options = CommandLineParse::ParseArguments(parser.get(), argc, argv); std::vector args = parser->args(); @@ -398,25 +165,22 @@ int main(int argc, char* argv[]) std::string user_directory; if (options.is_set("user")) - { user_directory = static_cast(options.get("user")); - } - - platform = GetPlatform(); - if (!platform) - { - fprintf(stderr, "No platform found\n"); - return 1; - } UICommon::SetUserDirectory(user_directory); UICommon::Init(); + s_platform = GetPlatform(options); + if (!s_platform || !s_platform->Init()) + { + fprintf(stderr, "No platform found, or failed to initialize.\n"); + return 1; + } + Core::SetOnStateChangedCallback([](Core::State state) { if (state == Core::State::Uninitialized) - s_running.Clear(); + s_platform->Stop(); }); - platform->Init(); // Shut down cleanly on SIGINT and SIGTERM struct sigaction sa; @@ -428,10 +192,7 @@ int main(int argc, char* argv[]) DolphinAnalytics::Instance()->ReportDolphinStart("nogui"); - WindowSystemInfo wsi(platform->GetWindowSystem(), platform->GetDisplayHandle(), - platform->GetWindowHandle()); - - if (!BootManager::BootCore(std::move(boot), wsi)) + if (!BootManager::BootCore(std::move(boot), s_platform->GetWindowSystemInfo())) { fprintf(stderr, "Could not boot the specified file\n"); return 1; @@ -441,21 +202,12 @@ int main(int argc, char* argv[]) Discord::UpdateDiscordPresence(); #endif - while (!Core::IsRunning() && s_running.IsSet()) - { - Core::HostDispatchJobs(); - updateMainFrameEvent.Wait(); - } - - if (s_running.IsSet()) - platform->MainLoop(); + s_platform->MainLoop(); Core::Stop(); Core::Shutdown(); - platform->Shutdown(); + s_platform.reset(); UICommon::Shutdown(); - delete platform; - return 0; } diff --git a/Source/Core/DolphinNoGUI/Platform.cpp b/Source/Core/DolphinNoGUI/Platform.cpp new file mode 100644 index 0000000000..69b184bfe9 --- /dev/null +++ b/Source/Core/DolphinNoGUI/Platform.cpp @@ -0,0 +1,53 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinNoGUI/Platform.h" +#include "Core/IOS/IOS.h" +#include "Core/IOS/STM/STM.h" +#include "Core/State.h" + +namespace ProcessorInterface +{ +void PowerButton_Tap(); +} + +Platform::~Platform() = default; + +bool Platform::Init() +{ + return true; +} + +void Platform::SetTitle(const std::string& title) +{ +} + +void Platform::UpdateRunningFlag() +{ + if (m_shutdown_requested.TestAndClear()) + { + const auto ios = IOS::HLE::GetIOS(); + const auto stm = ios ? ios->GetDeviceByName("/dev/stm/eventhook") : nullptr; + if (!m_tried_graceful_shutdown.IsSet() && stm && + std::static_pointer_cast(stm)->HasHookInstalled()) + { + ProcessorInterface::PowerButton_Tap(); + m_tried_graceful_shutdown.Set(); + } + else + { + m_running.Clear(); + } + } +} + +void Platform::Stop() +{ + m_running.Clear(); +} + +void Platform::RequestShutdown() +{ + m_shutdown_requested.Set(); +} diff --git a/Source/Core/DolphinNoGUI/Platform.h b/Source/Core/DolphinNoGUI/Platform.h new file mode 100644 index 0000000000..19edbb1168 --- /dev/null +++ b/Source/Core/DolphinNoGUI/Platform.h @@ -0,0 +1,48 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +#include "Common/Flag.h" +#include "Common/WindowSystemInfo.h" + +class Platform +{ +public: + virtual ~Platform(); + + bool IsRunning() const { return m_running.IsSet(); } + bool IsWindowFocused() const { return m_window_focus; } + bool IsWindowFullscreen() const { return m_window_fullscreen; } + + virtual bool Init(); + virtual void SetTitle(const std::string& title); + virtual void MainLoop() = 0; + + virtual WindowSystemInfo GetWindowSystemInfo() const = 0; + + // Requests a graceful shutdown, from SIGINT/SIGTERM. + void RequestShutdown(); + + // Request an immediate shutdown. + void Stop(); + + static std::unique_ptr CreateHeadlessPlatform(); +#ifdef HAVE_X11 + static std::unique_ptr CreateX11Platform(); +#endif + +protected: + void UpdateRunningFlag(); + + Common::Flag m_running{true}; + Common::Flag m_shutdown_requested{false}; + Common::Flag m_tried_graceful_shutdown{false}; + + bool m_window_focus = true; + bool m_window_fullscreen = false; +}; diff --git a/Source/Core/DolphinNoGUI/PlatformHeadless.cpp b/Source/Core/DolphinNoGUI/PlatformHeadless.cpp new file mode 100644 index 0000000000..b848492152 --- /dev/null +++ b/Source/Core/DolphinNoGUI/PlatformHeadless.cpp @@ -0,0 +1,50 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include "Core/Core.h" +#include "DolphinNoGUI/Platform.h" + +namespace +{ +class PlatformHeadless : public Platform +{ +public: + void SetTitle(const std::string& title) override; + void MainLoop() override; + + WindowSystemInfo GetWindowSystemInfo() const override; +}; + +void PlatformHeadless::SetTitle(const std::string& title) +{ + std::fprintf(stdout, "%s\n", title.c_str()); +} + +void PlatformHeadless::MainLoop() +{ + while (m_running.IsSet()) + { + UpdateRunningFlag(); + Core::HostDispatchJobs(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } +} + +WindowSystemInfo PlatformHeadless::GetWindowSystemInfo() const +{ + WindowSystemInfo wsi; + wsi.type = WindowSystemType::Headless; + wsi.display_connection = nullptr; + wsi.render_surface = nullptr; + return wsi; +} + +} // namespace + +std::unique_ptr Platform::CreateHeadlessPlatform() +{ + return std::make_unique(); +} diff --git a/Source/Core/DolphinNoGUI/PlatformX11.cpp b/Source/Core/DolphinNoGUI/PlatformX11.cpp new file mode 100644 index 0000000000..31a8a2e28d --- /dev/null +++ b/Source/Core/DolphinNoGUI/PlatformX11.cpp @@ -0,0 +1,270 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include "DolphinNoGUI/Platform.h" + +#include "Common/MsgHandler.h" +#include "Core/ConfigManager.h" +#include "Core/Core.h" +#include "Core/State.h" + +#include +#include + +#include +#include +#include +#include "UICommon/X11Utils.h" +#include "VideoCommon/RenderBase.h" + +#ifndef HOST_NAME_MAX +#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX +#endif + +namespace +{ +class PlatformX11 : public Platform +{ +public: + ~PlatformX11() override; + + bool Init() override; + void SetTitle(const std::string& string) override; + void MainLoop() override; + + WindowSystemInfo GetWindowSystemInfo() const; + +private: + void CloseDisplay(); + void UpdateWindowPosition(); + void ProcessEvents(); + + Display* m_display = nullptr; + Window m_window = {}; + Cursor m_blank_cursor = None; +#if defined(HAVE_XRANDR) && HAVE_XRANDR + X11Utils::XRRConfiguration* m_xrr_config = nullptr; +#endif +}; + +PlatformX11::~PlatformX11() +{ +#if defined(HAVE_XRANDR) && HAVE_XRANDR + delete m_xrr_config; +#endif + + if (m_display) + { + if (SConfig::GetInstance().bHideCursor) + XFreeCursor(m_display, m_blank_cursor); + + XCloseDisplay(m_display); + } +} + +bool PlatformX11::Init() +{ + XInitThreads(); + m_display = XOpenDisplay(nullptr); + if (!m_display) + { + PanicAlert("No X11 display found"); + return false; + } + + m_window = XCreateSimpleWindow( + m_display, DefaultRootWindow(m_display), SConfig::GetInstance().iRenderWindowXPos, + SConfig::GetInstance().iRenderWindowYPos, SConfig::GetInstance().iRenderWindowWidth, + SConfig::GetInstance().iRenderWindowHeight, 0, 0, BlackPixel(m_display, 0)); + XSelectInput(m_display, m_window, StructureNotifyMask | KeyPressMask | FocusChangeMask); + Atom wmProtocols[1]; + wmProtocols[0] = XInternAtom(m_display, "WM_DELETE_WINDOW", True); + XSetWMProtocols(m_display, m_window, wmProtocols, 1); + pid_t pid = getpid(); + XChangeProperty(m_display, m_window, XInternAtom(m_display, "_NET_WM_PID", False), XA_CARDINAL, + 32, PropModeReplace, reinterpret_cast(&pid), 1); + char host_name[HOST_NAME_MAX] = ""; + if (!gethostname(host_name, sizeof(host_name))) + { + XTextProperty wmClientMachine = {reinterpret_cast(host_name), XA_STRING, 8, + strlen(host_name)}; + XSetWMClientMachine(m_display, m_window, &wmClientMachine); + } + XMapRaised(m_display, m_window); + XFlush(m_display); + XSync(m_display, True); + ProcessEvents(); + + if (SConfig::GetInstance().bDisableScreenSaver) + X11Utils::InhibitScreensaver(m_window, true); + +#if defined(HAVE_XRANDR) && HAVE_XRANDR + m_xrr_config = new X11Utils::XRRConfiguration(m_display, m_window); +#endif + + if (SConfig::GetInstance().bHideCursor) + { + // make a blank cursor + Pixmap Blank; + XColor DummyColor; + char ZeroData[1] = {0}; + Blank = XCreateBitmapFromData(m_display, m_window, ZeroData, 1, 1); + m_blank_cursor = XCreatePixmapCursor(m_display, Blank, Blank, &DummyColor, &DummyColor, 0, 0); + XFreePixmap(m_display, Blank); + XDefineCursor(m_display, m_window, m_blank_cursor); + } + + // Enter fullscreen if enabled. + if (SConfig::GetInstance().bFullscreen) + { + m_window_fullscreen = X11Utils::ToggleFullscreen(m_display, m_window); +#if defined(HAVE_XRANDR) && HAVE_XRANDR + m_xrr_config->ToggleDisplayMode(True); +#endif + ProcessEvents(); + } + + UpdateWindowPosition(); + return true; +} + +void PlatformX11::SetTitle(const std::string& string) +{ + XStoreName(m_display, m_window, string.c_str()); +} + +void PlatformX11::MainLoop() +{ + while (IsRunning()) + { + UpdateRunningFlag(); + Core::HostDispatchJobs(); + ProcessEvents(); + UpdateWindowPosition(); + + // TODO: Is this sleep appropriate? + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +} + +WindowSystemInfo PlatformX11::GetWindowSystemInfo() const +{ + WindowSystemInfo wsi; + wsi.type = WindowSystemType::X11; + wsi.display_connection = static_cast(m_display); + wsi.render_surface = reinterpret_cast(m_window); + return wsi; +} + +void PlatformX11::UpdateWindowPosition() +{ + if (m_window_fullscreen) + return; + + Window winDummy; + unsigned int borderDummy, depthDummy; + XGetGeometry(m_display, m_window, &winDummy, &SConfig::GetInstance().iRenderWindowXPos, + &SConfig::GetInstance().iRenderWindowYPos, + reinterpret_cast(&SConfig::GetInstance().iRenderWindowWidth), + reinterpret_cast(&SConfig::GetInstance().iRenderWindowHeight), + &borderDummy, &depthDummy); +} + +void PlatformX11::ProcessEvents() +{ + XEvent event; + KeySym key; + for (int num_events = XPending(m_display); num_events > 0; num_events--) + { + XNextEvent(m_display, &event); + switch (event.type) + { + case KeyPress: + key = XLookupKeysym((XKeyEvent*)&event, 0); + if (key == XK_Escape) + { + RequestShutdown(); + } + else if (key == XK_F10) + { + if (Core::GetState() == Core::State::Running) + { + if (SConfig::GetInstance().bHideCursor) + XUndefineCursor(m_display, m_window); + Core::SetState(Core::State::Paused); + } + else + { + if (SConfig::GetInstance().bHideCursor) + XDefineCursor(m_display, m_window, m_blank_cursor); + Core::SetState(Core::State::Running); + } + } + else if ((key == XK_Return) && (event.xkey.state & Mod1Mask)) + { + m_window_fullscreen = !m_window_fullscreen; + X11Utils::ToggleFullscreen(m_display, m_window); +#if defined(HAVE_XRANDR) && HAVE_XRANDR + m_xrr_config->ToggleDisplayMode(m_window_fullscreen); +#endif + UpdateWindowPosition(); + } + else if (key >= XK_F1 && key <= XK_F8) + { + int slot_number = key - XK_F1 + 1; + if (event.xkey.state & ShiftMask) + State::Save(slot_number); + else + State::Load(slot_number); + } + else if (key == XK_F9) + Core::SaveScreenShot(); + else if (key == XK_F11) + State::LoadLastSaved(); + else if (key == XK_F12) + { + if (event.xkey.state & ShiftMask) + State::UndoLoadState(); + else + State::UndoSaveState(); + } + break; + case FocusIn: + { + m_window_focus = true; + if (SConfig::GetInstance().bHideCursor && Core::GetState() != Core::State::Paused) + XDefineCursor(m_display, m_window, m_blank_cursor); + } + break; + case FocusOut: + { + m_window_focus = false; + if (SConfig::GetInstance().bHideCursor) + XUndefineCursor(m_display, m_window); + } + break; + case ClientMessage: + { + if ((unsigned long)event.xclient.data.l[0] == + XInternAtom(m_display, "WM_DELETE_WINDOW", False)) + Stop(); + } + break; + case ConfigureNotify: + { + if (g_renderer) + g_renderer->ResizeSurface(); + } + break; + } + } +} +} // namespace + +std::unique_ptr Platform::CreateX11Platform() +{ + return std::make_unique(); +}