diff --git a/duckstation.sln b/duckstation.sln index 7f3aa45a6..5e701f97a 100644 --- a/duckstation.sln +++ b/duckstation.sln @@ -42,8 +42,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "updater", "src\updater\upda EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vixl", "dep\vixl\vixl.vcxproj", "{8906836E-F06E-46E8-B11A-74E5E8C7B8FB}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "duckstation-nogui", "src\duckstation-nogui\duckstation-nogui.vcxproj", "{0A172B2E-DC67-49FC-A4C1-975F93C586C4}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rcheevos", "dep\rcheevos\rcheevos.vcxproj", "{4BA0A6D4-3AE1-42B2-9347-096FD023FF64}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "duckstation-regtest", "src\duckstation-regtest\duckstation-regtest.vcxproj", "{3029310E-4211-4C87-801A-72E130A648EF}" @@ -588,24 +586,6 @@ Global {8906836E-F06E-46E8-B11A-74E5E8C7B8FB}.ReleaseLTCG-Clang|ARM64.ActiveCfg = ReleaseLTCG-Clang|ARM64 {8906836E-F06E-46E8-B11A-74E5E8C7B8FB}.ReleaseLTCG-Clang|ARM64.Build.0 = ReleaseLTCG-Clang|ARM64 {8906836E-F06E-46E8-B11A-74E5E8C7B8FB}.ReleaseLTCG-Clang|x64.ActiveCfg = ReleaseLTCG-Clang|x64 - {0A172B2E-DC67-49FC-A4C1-975F93C586C4}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {0A172B2E-DC67-49FC-A4C1-975F93C586C4}.Debug|x64.ActiveCfg = Debug|x64 - {0A172B2E-DC67-49FC-A4C1-975F93C586C4}.Debug-Clang|ARM64.ActiveCfg = Debug-Clang|ARM64 - {0A172B2E-DC67-49FC-A4C1-975F93C586C4}.Debug-Clang|x64.ActiveCfg = Debug-Clang|x64 - {0A172B2E-DC67-49FC-A4C1-975F93C586C4}.DebugFast|ARM64.ActiveCfg = DebugFast|ARM64 - {0A172B2E-DC67-49FC-A4C1-975F93C586C4}.DebugFast|x64.ActiveCfg = DebugFast|x64 - {0A172B2E-DC67-49FC-A4C1-975F93C586C4}.DebugFast-Clang|ARM64.ActiveCfg = DebugFast-Clang|ARM64 - {0A172B2E-DC67-49FC-A4C1-975F93C586C4}.DebugFast-Clang|ARM64.Build.0 = DebugFast-Clang|ARM64 - {0A172B2E-DC67-49FC-A4C1-975F93C586C4}.DebugFast-Clang|x64.ActiveCfg = DebugFast-Clang|x64 - {0A172B2E-DC67-49FC-A4C1-975F93C586C4}.Release|ARM64.ActiveCfg = Release|ARM64 - {0A172B2E-DC67-49FC-A4C1-975F93C586C4}.Release|x64.ActiveCfg = Release|x64 - {0A172B2E-DC67-49FC-A4C1-975F93C586C4}.Release-Clang|ARM64.ActiveCfg = Release-Clang|ARM64 - {0A172B2E-DC67-49FC-A4C1-975F93C586C4}.Release-Clang|ARM64.Build.0 = Release-Clang|ARM64 - {0A172B2E-DC67-49FC-A4C1-975F93C586C4}.Release-Clang|x64.ActiveCfg = Release-Clang|x64 - {0A172B2E-DC67-49FC-A4C1-975F93C586C4}.ReleaseLTCG|ARM64.ActiveCfg = ReleaseLTCG|ARM64 - {0A172B2E-DC67-49FC-A4C1-975F93C586C4}.ReleaseLTCG|x64.ActiveCfg = ReleaseLTCG|x64 - {0A172B2E-DC67-49FC-A4C1-975F93C586C4}.ReleaseLTCG-Clang|ARM64.ActiveCfg = ReleaseLTCG-Clang|ARM64 - {0A172B2E-DC67-49FC-A4C1-975F93C586C4}.ReleaseLTCG-Clang|x64.ActiveCfg = ReleaseLTCG-Clang|x64 {4BA0A6D4-3AE1-42B2-9347-096FD023FF64}.Debug|ARM64.ActiveCfg = Debug|ARM64 {4BA0A6D4-3AE1-42B2-9347-096FD023FF64}.Debug|ARM64.Build.0 = Debug|ARM64 {4BA0A6D4-3AE1-42B2-9347-096FD023FF64}.Debug|x64.ActiveCfg = Debug|x64 diff --git a/src/duckstation-nogui/CMakeLists.txt b/src/duckstation-nogui/CMakeLists.txt deleted file mode 100644 index d5feb7387..000000000 --- a/src/duckstation-nogui/CMakeLists.txt +++ /dev/null @@ -1,97 +0,0 @@ -add_executable(duckstation-nogui - nogui_host.cpp - nogui_host.h - nogui_platform.h -) - -target_precompile_headers(duckstation-nogui PRIVATE "pch.h") -target_link_libraries(duckstation-nogui PRIVATE core util common imgui scmversion) - -add_core_resources(duckstation-nogui) - -if(WIN32) - message(STATUS "Building Win32 NoGUI Platform.") - target_sources(duckstation-nogui PRIVATE - duckstation-nogui.manifest - resource.h - win32_nogui_platform.cpp - win32_nogui_platform.h - ) - - # We want a Windows subsystem application not console. - set_target_properties(duckstation-nogui PROPERTIES - WIN32_EXECUTABLE TRUE - DEBUG_POSTFIX "-debug") -endif() - - -if(APPLE) - message(STATUS "Building Cocoa NoGUI Platform.") - set(COCOA_SOURCES PRIVATE - cocoa_key_names.h - cocoa_nogui_platform.mm - cocoa_nogui_platform.h - ) - target_sources(duckstation-nogui PRIVATE ${COCOA_SOURCES}) - find_library(QUARTZCORE_LIBRARY QuartzCore) - find_library(COCOA_LIBRARY Cocoa) - target_link_libraries(duckstation-nogui PRIVATE ${QUARTZCORE_LIBRARY} ${COCOA_LIBRARY}) - set_source_files_properties(${COCOA_SOURCES} PROPERTIES SKIP_PRECOMPILE_HEADERS TRUE) -endif() - -if(ENABLE_X11) - message(STATUS "Building X11 NoGUI Platform.") - target_compile_definitions(duckstation-nogui PRIVATE "NOGUI_PLATFORM_X11=1") - target_sources(duckstation-nogui PRIVATE - x11_nogui_platform.cpp - x11_nogui_platform.h - ) - target_include_directories(duckstation-nogui PRIVATE "${X11_INCLUDE_DIR}" "${X11_Xrandr_INCLUDE_PATH}") - target_link_libraries(duckstation-nogui PRIVATE "${X11_LIBRARIES}" "${X11_Xrandr_LIB}") -endif() - -if(ENABLE_WAYLAND) - message(STATUS "Building Wayland NoGUI Platform.") - find_package(ECM REQUIRED NO_MODULE) - list(APPEND CMAKE_MODULE_PATH "${ECM_MODULE_PATH}") - find_package(Wayland REQUIRED Client) - find_package(WaylandScanner REQUIRED) - find_package(WaylandProtocols 1.15 REQUIRED) - find_package(X11 REQUIRED) - if (NOT X11_xkbcommon_FOUND) - message(FATAL_ERROR "XKBCommon is required.") - endif() - - target_compile_definitions(duckstation-nogui PRIVATE "NOGUI_PLATFORM_WAYLAND=1") - target_sources(duckstation-nogui PRIVATE - wayland_nogui_platform.cpp - wayland_nogui_platform.h - ) - - # Required protocols. - ecm_add_wayland_client_protocol(WAYLAND_PROTOCOL_SRCS - PROTOCOL "${WaylandProtocols_DATADIR}/stable/xdg-shell/xdg-shell.xml" - BASENAME xdg-shell) - ecm_add_wayland_client_protocol(WAYLAND_PROTOCOL_SRCS - PROTOCOL "${WaylandProtocols_DATADIR}/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml" - BASENAME xdg-decoration) - target_sources(duckstation-nogui PRIVATE ${WAYLAND_PROTOCOL_SRCS}) - target_include_directories(duckstation-nogui PUBLIC "${CMAKE_CURRENT_BINARY_DIR}") - - # Disable PCH for protocol files, because they're C, and our PCH above is C++. - set_source_files_properties(${WAYLAND_PROTOCOL_SRCS} PROPERTIES SKIP_PRECOMPILE_HEADERS TRUE) - - target_link_libraries(duckstation-nogui PRIVATE - Wayland::Client - X11::xkbcommon - ) -endif() - -if(ENABLE_SDL2) - message(STATUS "Building SDL NoGUI Platform.") - target_sources(duckstation-nogui PRIVATE - sdl_nogui_platform.cpp - sdl_nogui_platform.h - ) - target_link_libraries(duckstation-nogui PUBLIC SDL2::SDL2) -endif() diff --git a/src/duckstation-nogui/cocoa_key_names.h b/src/duckstation-nogui/cocoa_key_names.h deleted file mode 100644 index 5e5e44828..000000000 --- a/src/duckstation-nogui/cocoa_key_names.h +++ /dev/null @@ -1,133 +0,0 @@ -#pragma once -#include "common/types.h" - -#include -#include -#include -#include -#include - -#include - -namespace CocoaKeyNames { -static const std::map s_cocoa_key_names = { - {kVK_Return, "Return"}, - {kVK_Escape, "Escape"}, - {kVK_Delete, "Backspace"}, - {kVK_Tab, "Tab"}, - {kVK_Space, "Space"}, - {kVK_ANSI_Quote, "Quote"}, - {kVK_ANSI_Comma, "Comma"}, - {kVK_ANSI_Minus, "Minus"}, - {kVK_ANSI_Period, "Period"}, - {kVK_ANSI_Slash, "Slash"}, - {kVK_ANSI_0, "0"}, - {kVK_ANSI_1, "1"}, - {kVK_ANSI_2, "2"}, - {kVK_ANSI_3, "3"}, - {kVK_ANSI_4, "4"}, - {kVK_ANSI_5, "5"}, - {kVK_ANSI_6, "6"}, - {kVK_ANSI_7, "7"}, - {kVK_ANSI_8, "8"}, - {kVK_ANSI_9, "9"}, - {kVK_ANSI_Semicolon, "Semcolon"}, - {kVK_ANSI_Equal, "Equal"}, - {kVK_ANSI_LeftBracket, "BracketLeft"}, - {kVK_ANSI_Backslash, "Backslash"}, - {kVK_ANSI_RightBracket, "BracketRight"}, - {kVK_ANSI_Grave, "Grave"}, - {kVK_ANSI_A, "A"}, - {kVK_ANSI_B, "B"}, - {kVK_ANSI_C, "C"}, - {kVK_ANSI_D, "D"}, - {kVK_ANSI_E, "E"}, - {kVK_ANSI_F, "F"}, - {kVK_ANSI_G, "G"}, - {kVK_ANSI_H, "H"}, - {kVK_ANSI_I, "I"}, - {kVK_ANSI_J, "J"}, - {kVK_ANSI_K, "K"}, - {kVK_ANSI_L, "L"}, - {kVK_ANSI_M, "M"}, - {kVK_ANSI_N, "N"}, - {kVK_ANSI_O, "O"}, - {kVK_ANSI_P, "P"}, - {kVK_ANSI_Q, "Q"}, - {kVK_ANSI_R, "R"}, - {kVK_ANSI_S, "S"}, - {kVK_ANSI_T, "T"}, - {kVK_ANSI_U, "U"}, - {kVK_ANSI_V, "V"}, - {kVK_ANSI_W, "W"}, - {kVK_ANSI_X, "X"}, - {kVK_ANSI_Y, "Y"}, - {kVK_ANSI_Z, "Z"}, - {kVK_CapsLock, "CapsLock"}, - {kVK_F1, "F1"}, - {kVK_F2, "F2"}, - {kVK_F3, "F3"}, - {kVK_F4, "F4"}, - {kVK_F5, "F5"}, - {kVK_F6, "F6"}, - {kVK_F7, "F7"}, - {kVK_F8, "F8"}, - {kVK_F9, "F9"}, - {kVK_F10, "F10"}, - {kVK_F11, "F11"}, - {kVK_F12, "F12"}, - {kVK_Home, "Home"}, - {kVK_PageUp, "PageUp"}, - {kVK_End, "End"}, - {kVK_PageDown, "PageDown"}, - {kVK_RightArrow, "Right"}, - {kVK_LeftArrow, "Left"}, - {kVK_DownArrow, "Down"}, - {kVK_UpArrow, "Up"}, - {kVK_ANSI_KeypadDivide, "KeypadDivide"}, - {kVK_ANSI_KeypadMultiply, "KeypadMultiply"}, - {kVK_ANSI_KeypadMinus, "KeypadMinus"}, - {kVK_ANSI_KeypadPlus, "KeypadPlus"}, - {kVK_ANSI_KeypadEnter, "KeypadReturn"}, - {kVK_ANSI_Keypad1, "Keypad1"}, - {kVK_ANSI_Keypad2, "Keypad2"}, - {kVK_ANSI_Keypad3, "Keypad3"}, - {kVK_ANSI_Keypad4, "Keypad4"}, - {kVK_ANSI_Keypad5, "Keypad5"}, - {kVK_ANSI_Keypad6, "Keypad6"}, - {kVK_ANSI_Keypad7, "Keypad7"}, - {kVK_ANSI_Keypad8, "Keypad8"}, - {kVK_ANSI_Keypad9, "Keypad9"}, - {kVK_ANSI_Keypad0, "Keypad0"}, - {kVK_ANSI_KeypadDecimal, "KeypadPeriod"}, - {kVK_F13, "F13"}, - {kVK_F14, "F14"}, - {kVK_F15, "F15"}, - {kVK_F16, "F16"}, - {kVK_F17, "F17"}, - {kVK_F18, "F18"}, - {kVK_F19, "F19"}, - {kVK_F20, "F20"}, - {kVK_Help, "Help"}, - {kVK_Option, "Alt"}, - {kVK_Command, "Super"}, - {kVK_Function, "Control"}, -}; - -static const char* GetKeyName(unsigned short key) -{ - const auto it = s_cocoa_key_names.find(key); - return it == s_cocoa_key_names.end() ? nullptr : it->second; -} - -static std::optional GetKeyCodeForName(const std::string_view& key_name) -{ - for (const auto& it : s_cocoa_key_names) - { - if (key_name == it.second) - return it.first; - } - - return std::nullopt; -} -} // namespace CocoaKeyNames diff --git a/src/duckstation-nogui/cocoa_nogui_platform.h b/src/duckstation-nogui/cocoa_nogui_platform.h deleted file mode 100644 index 5b74978d4..000000000 --- a/src/duckstation-nogui/cocoa_nogui_platform.h +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#pragma once - -#include -#include - -#ifndef __OBJC__ -#error This file needs to be compiled with Objective C++. -#endif - -#if __has_feature(objc_arc) -#error ARC should not be enabled. -#endif - -#include "nogui_platform.h" - -#include - -@interface CocoaNoGUIView : NSView -- (BOOL)acceptsFirstResponder; -- (BOOL)canBecomeKeyView; -- (void)mouseDown:(NSEvent *)event; -- (void)rightMouseDown:(NSEvent *)event; -- (void)otherMouseDown:(NSEvent *)event; -- (void)mouseUp:(NSEvent *)event; -- (void)rightMouseUp:(NSEvent *)event; -- (void)otherMouseUp:(NSEvent *)event; -- (void)mouseMoved:(NSEvent *)event; -- (void)keyDown:(NSEvent *)event; -- (void)keyUp:(NSEvent *)event; -- (void)windowDidEndLiveResize:(NSNotification *)notif; -@end - -class CocoaNoGUIPlatform : public NoGUIPlatform -{ -public: - CocoaNoGUIPlatform(); - ~CocoaNoGUIPlatform(); - - bool Initialize(); - - void ReportError(const std::string_view& title, const std::string_view& message) override; - bool ConfirmMessage(const std::string_view& title, const std::string_view& message) override; - - void SetDefaultConfig(SettingsInterface& si) override; - - bool CreatePlatformWindow(std::string title) override; - bool HasPlatformWindow() const override; - void DestroyPlatformWindow() override; - std::optional GetPlatformWindowInfo() override; - void SetPlatformWindowTitle(std::string title) override; - - std::optional ConvertHostKeyboardStringToCode(const std::string_view& str) override; - std::optional ConvertHostKeyboardCodeToString(u32 code) override; - - void RunMessageLoop() override; - void ExecuteInMessageLoop(std::function func) override; - void QuitMessageLoop() override; - - void SetFullscreen(bool enabled) override; - - bool RequestRenderWindowSize(s32 new_window_width, s32 new_window_height) override; - - bool OpenURL(const std::string_view& url) override; - bool CopyTextToClipboard(const std::string_view& text) override; - -private: - NSWindow* m_window = nil; - float m_window_scale = 1.0f; - - std::atomic_bool m_fullscreen{false}; -}; diff --git a/src/duckstation-nogui/cocoa_nogui_platform.mm b/src/duckstation-nogui/cocoa_nogui_platform.mm deleted file mode 100644 index 61e81f582..000000000 --- a/src/duckstation-nogui/cocoa_nogui_platform.mm +++ /dev/null @@ -1,301 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#include "cocoa_nogui_platform.h" -#include "cocoa_key_names.h" -#include "nogui_host.h" - -#include "core/host.h" - -#include "util/cocoa_tools.h" -#include "util/imgui_manager.h" - -#include "common/log.h" -#include "common/scoped_guard.h" -#include "common/string_util.h" -#include "common/threading.h" - -Log_SetChannel(CocoaNoGUIPlatform); - -constexpr NSWindowStyleMask WINDOWED_STYLE = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable; - -@implementation CocoaNoGUIView - -- (BOOL)acceptsFirstResponder { - return YES; -} -- (BOOL)canBecomeKeyView { - return YES; -} -- (void)mouseDown:(NSEvent *)event { - NoGUIHost::ProcessPlatformMouseButtonEvent(0, true); -} -- (void)rightMouseDown:(NSEvent *)event { - NoGUIHost::ProcessPlatformMouseButtonEvent(1, true); -} -- (void)otherMouseDown:(NSEvent *)event { - NoGUIHost::ProcessPlatformMouseButtonEvent(static_cast(event.buttonNumber), true); -} - -- (void)mouseUp:(NSEvent *)event { - NoGUIHost::ProcessPlatformMouseButtonEvent(0, false); -} -- (void)rightMouseUp:(NSEvent *)event { - NoGUIHost::ProcessPlatformMouseButtonEvent(1, false); -} -- (void)otherMouseUp:(NSEvent *)event { - NoGUIHost::ProcessPlatformMouseButtonEvent(static_cast(event.buttonNumber), false); -} - -- (void)mouseMoved:(NSEvent *)event { - // Flip for lower-left origin. - const NSView* contentView = self; - const NSPoint pt = [contentView convertPointToBacking:[event locationInWindow]]; - const NSSize size = [contentView convertSizeToBacking:contentView.frame.size]; - const float local_x = pt.x; - const float local_y = size.height - pt.y; - NoGUIHost::ProcessPlatformMouseMoveEvent(local_x, local_y); -} - -- (void)keyDown:(NSEvent *)event { - [super keyDown:event]; - if (ImGuiManager::WantsTextInput() && event.characters && event.characters.length > 0) - { - ImGuiManager::AddTextInput([event.characters UTF8String]); - } - - if (!event.isARepeat) - NoGUIHost::ProcessPlatformKeyEvent(static_cast(event.keyCode), true); -} - -- (void)keyUp:(NSEvent *)event { - [super keyUp:event]; - NoGUIHost::ProcessPlatformKeyEvent(static_cast(event.keyCode), false); -} - -- (void)windowDidEndLiveResize:(NSNotification *)notif -{ - const NSSize size = [self convertSizeToBacking:self.frame.size]; - NoGUIHost::ProcessPlatformWindowResize(static_cast(size.width), static_cast(size.height), 1.0f); -} -@end - -CocoaNoGUIPlatform::CocoaNoGUIPlatform() = default; - -CocoaNoGUIPlatform::~CocoaNoGUIPlatform() -{ - if (m_window) - { - [m_window release]; - m_window = nil; - } -} - -bool CocoaNoGUIPlatform::Initialize() -{ - [NSApplication sharedApplication]; - - // Needed for keyboard in put. - const ProcessSerialNumber psn = {0, kCurrentProcess}; - TransformProcessType(&psn, kProcessTransformToForegroundApplication); - return true; -} - -void CocoaNoGUIPlatform::ReportError(const std::string_view& title, const std::string_view& message) -{ - if (![NSThread isMainThread]) - { - dispatch_sync(dispatch_get_main_queue(), [this, &title, &message]() { ReportError(title, message); }); - return; - } - - @autoreleasepool { - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - [alert setMessageText: CocoaTools::StringViewToNSString(title)]; - [alert setInformativeText: CocoaTools::StringViewToNSString(message)]; - [alert runModal]; - } -} - -bool CocoaNoGUIPlatform::ConfirmMessage(const std::string_view& title, const std::string_view& message) -{ - if (![NSThread isMainThread]) - { - bool result = false; - dispatch_sync(dispatch_get_main_queue(), [this, &title, &message, &result]() { result = ConfirmMessage(title, message); }); - return result; - } - - @autoreleasepool { - NSAlert *alert = [[[NSAlert alloc] init] autorelease]; - [alert setMessageText: CocoaTools::StringViewToNSString(title)]; - [alert setInformativeText: CocoaTools::StringViewToNSString(message)]; - [alert addButtonWithTitle:@"Yes"]; - [alert addButtonWithTitle:@"No"]; - return ([alert runModal] == 0); - } -} - -void CocoaNoGUIPlatform::SetDefaultConfig(SettingsInterface& si) -{ - // noop -} - -bool CocoaNoGUIPlatform::CreatePlatformWindow(std::string title) -{ - @autoreleasepool { - s32 window_x, window_y, window_width, window_height; - const bool has_window_geom = NoGUIHost::GetSavedPlatformWindowGeometry(&window_x, &window_y, &window_width, &window_height); - if (!has_window_geom) - { - window_width = DEFAULT_WINDOW_WIDTH; - window_height = DEFAULT_WINDOW_HEIGHT; - } - - m_window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0.0f, 0.0f, static_cast(window_width), static_cast(window_height)) - styleMask:WINDOWED_STYLE - backing:NSBackingStoreBuffered defer:YES]; - - CocoaNoGUIView* view = [[[CocoaNoGUIView alloc] init] autorelease]; - [m_window setDelegate:view]; - [m_window setContentView:view]; - - if (!has_window_geom) - [m_window center]; - else - [m_window setFrameOrigin:NSMakePoint(static_cast(window_x), static_cast(window_y))]; - - [m_window setTitle: [NSString stringWithUTF8String:title.c_str()]]; - [m_window setAcceptsMouseMovedEvents:YES]; - [m_window setReleasedWhenClosed:NO]; - [m_window setIsVisible:TRUE]; - [m_window makeKeyAndOrderFront:nil]; - } - - if (m_fullscreen.load(std::memory_order_acquire)) - SetFullscreen(true); - - return true; -} - -bool CocoaNoGUIPlatform::HasPlatformWindow() const -{ - return (m_window != NULL); -} - -void CocoaNoGUIPlatform::DestroyPlatformWindow() -{ - if (m_window == nil) - return; - - const CGPoint frame_origin = m_window.frame.origin; - const CGSize content_size = m_window.contentView.frame.size; - - if (!m_fullscreen.load(std::memory_order_acquire)) - { - NoGUIHost::SavePlatformWindowGeometry(static_cast(frame_origin.x), static_cast(frame_origin.y), - static_cast(content_size.width), static_cast(content_size.height)); - } - - [m_window close]; - [m_window release]; - m_window = nil; -} - -std::optional CocoaNoGUIPlatform::GetPlatformWindowInfo() -{ - if (m_window == nil) - return std::nullopt; - - NSView* contentView = [m_window contentView]; - const NSSize size = [contentView convertSizeToBacking:contentView.frame.size]; - - WindowInfo wi; - wi.surface_width = static_cast(size.width); - wi.surface_height = static_cast(size.height); - wi.surface_scale = m_window_scale; - wi.type = WindowInfo::Type::MacOS; - wi.window_handle = static_cast(m_window.contentView); - return wi; -} - -void CocoaNoGUIPlatform::SetPlatformWindowTitle(std::string title) -{ - dispatch_async(dispatch_get_main_queue(), [this, title = std::move(title)]() { - if (!m_window) - return; - - @autoreleasepool { - [m_window setTitle: [NSString stringWithUTF8String:title.c_str()]]; - } - }); -} - -std::optional CocoaNoGUIPlatform::ConvertHostKeyboardStringToCode(const std::string_view& str) -{ - std::optional converted(CocoaKeyNames::GetKeyCodeForName(str)); - return converted.has_value() ? std::optional(static_cast(converted.value())) : std::nullopt; -} - -std::optional CocoaNoGUIPlatform::ConvertHostKeyboardCodeToString(u32 code) -{ - const char* converted = CocoaKeyNames::GetKeyName(static_cast(code)); - return converted ? std::optional(converted) : std::nullopt; -} - -void CocoaNoGUIPlatform::RunMessageLoop() -{ - [NSApp run]; -} - -void CocoaNoGUIPlatform::ExecuteInMessageLoop(std::function func) -{ - dispatch_async(dispatch_get_main_queue(), [func = std::move(func)]() { - func(); - }); -} - -void CocoaNoGUIPlatform::QuitMessageLoop() -{ - [NSApp stop:nil]; -} - -void CocoaNoGUIPlatform::SetFullscreen(bool enabled) -{ - Log_ErrorPrint("SetFullscreen() not implemented."); -} - -bool CocoaNoGUIPlatform::RequestRenderWindowSize(s32 new_window_width, s32 new_window_height) -{ - dispatch_async(dispatch_get_main_queue(), [this, new_window_width, new_window_height]() { - if (!m_window) - return; - - @autoreleasepool { - [m_window setContentSize:NSMakeSize(static_cast(new_window_width), static_cast(new_window_height))]; - } - }); - - return true; -} - -bool CocoaNoGUIPlatform::OpenURL(const std::string_view& url) -{ - Log_ErrorPrint("OpenURL() not implemented."); - return false; -} - -bool CocoaNoGUIPlatform::CopyTextToClipboard(const std::string_view& text) -{ - Log_ErrorPrint("CopyTextToClipboard() not implemented."); - return false; -} - -std::unique_ptr NoGUIPlatform::CreateCocoaPlatform() -{ - std::unique_ptr ret(new CocoaNoGUIPlatform()); - if (!ret->Initialize()) - return {}; - - return ret; -} diff --git a/src/duckstation-nogui/duckstation-nogui.aps b/src/duckstation-nogui/duckstation-nogui.aps deleted file mode 100644 index 147191914..000000000 Binary files a/src/duckstation-nogui/duckstation-nogui.aps and /dev/null differ diff --git a/src/duckstation-nogui/duckstation-nogui.ico b/src/duckstation-nogui/duckstation-nogui.ico deleted file mode 100644 index f95b67991..000000000 Binary files a/src/duckstation-nogui/duckstation-nogui.ico and /dev/null differ diff --git a/src/duckstation-nogui/duckstation-nogui.manifest b/src/duckstation-nogui/duckstation-nogui.manifest deleted file mode 100644 index 6bbce6d9b..000000000 --- a/src/duckstation-nogui/duckstation-nogui.manifest +++ /dev/null @@ -1,17 +0,0 @@ - - - - - PerMonitorV2 - true - - - - - - - - - - - \ No newline at end of file diff --git a/src/duckstation-nogui/duckstation-nogui.rc b/src/duckstation-nogui/duckstation-nogui.rc deleted file mode 100644 index 913553f50..000000000 --- a/src/duckstation-nogui/duckstation-nogui.rc +++ /dev/null @@ -1,110 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (Australia) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,0,1 - PRODUCTVERSION 1,0,0,1 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x1L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "0c0904b0" - BEGIN - VALUE "CompanyName", "https://github.com/stenzek/duckstation" - VALUE "FileDescription", "DuckStation PS1 Emulator" - VALUE "FileVersion", "1.0.0.1" - VALUE "InternalName", "duckstation-nogui.exe" - VALUE "LegalCopyright", "Copyright (C) 2020 Stenzek and collaborators" - VALUE "OriginalFilename", "duckstation-nogui.exe" - VALUE "ProductName", "DuckStation NoGUI Frontend" - VALUE "ProductVersion", "1.0.0.1" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0xc09, 1200 - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_ICON1 ICON "duckstation-nogui.ico" - -#endif // English (Australia) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - diff --git a/src/duckstation-nogui/duckstation-nogui.vcxproj b/src/duckstation-nogui/duckstation-nogui.vcxproj deleted file mode 100644 index fbbb40c64..000000000 --- a/src/duckstation-nogui/duckstation-nogui.vcxproj +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - Create - - - - true - - - - true - - - - - - - - - - true - - - - true - - - - - - - - - - - - - - {ee054e08-3799-4a59-a422-18259c105ffd} - - - {868b98c8-65a1-494b-8346-250a73a48c0a} - - - {57f6206d-f264-4b07-baf8-11b9bbe1f455} - - - - {0A172B2E-DC67-49FC-A4C1-975F93C586C4} - - - - - - Use - pch.h - pch.h - - - - \ No newline at end of file diff --git a/src/duckstation-nogui/duckstation-nogui.vcxproj.filters b/src/duckstation-nogui/duckstation-nogui.vcxproj.filters deleted file mode 100644 index 5cdac5f86..000000000 --- a/src/duckstation-nogui/duckstation-nogui.vcxproj.filters +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/duckstation-nogui/nogui_host.cpp b/src/duckstation-nogui/nogui_host.cpp deleted file mode 100644 index 7e3c6ab1a..000000000 --- a/src/duckstation-nogui/nogui_host.cpp +++ /dev/null @@ -1,1471 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#include "nogui_host.h" -#include "nogui_platform.h" - -#include "scmversion/scmversion.h" - -#include "core/achievements.h" -#include "core/controller.h" -#include "core/fullscreen_ui.h" -#include "core/game_list.h" -#include "core/gpu.h" -#include "core/host.h" -#include "core/imgui_overlays.h" -#include "core/settings.h" -#include "core/system.h" - -#include "util/gpu_device.h" -#include "util/imgui_fullscreen.h" -#include "util/imgui_manager.h" -#include "util/ini_settings_interface.h" -#include "util/input_manager.h" -#include "util/platform_misc.h" - -#include "imgui.h" -#include "imgui_internal.h" -#include "imgui_stdlib.h" - -#include "common/assert.h" -#include "common/byte_stream.h" -#include "common/crash_handler.h" -#include "common/error.h" -#include "common/file_system.h" -#include "common/log.h" -#include "common/path.h" -#include "common/string_util.h" -#include "common/threading.h" - -#include -#include -#include -#include -#include - -Log_SetChannel(NoGUIHost); - -#ifdef _WIN32 -#include "common/windows_headers.h" -#include -#include -#endif - -static constexpr u32 SETTINGS_VERSION = 3; -static constexpr auto CPU_THREAD_POLL_INTERVAL = - std::chrono::milliseconds(8); // how often we'll poll controllers when paused - -std::unique_ptr g_nogui_window; - -////////////////////////////////////////////////////////////////////////// -// Local function declarations -////////////////////////////////////////////////////////////////////////// -namespace NoGUIHost { - -namespace { -class AsyncOpProgressCallback final : public BaseProgressCallback -{ -public: - AsyncOpProgressCallback(std::string name); - ~AsyncOpProgressCallback() override; - - ALWAYS_INLINE const std::string& GetName() const { return m_name; } - - void PushState() override; - void PopState() override; - - void SetCancellable(bool cancellable) override; - void SetTitle(const char* title) override; - void SetStatusText(const char* text) override; - void SetProgressRange(u32 range) override; - void SetProgressValue(u32 value) override; - - void DisplayError(const char* message) override; - void DisplayWarning(const char* message) override; - void DisplayInformation(const char* message) override; - void DisplayDebugMessage(const char* message) override; - - void ModalError(const char* message) override; - bool ModalConfirmation(const char* message) override; - void ModalInformation(const char* message) override; - - void SetCancelled(); - -private: - void Redraw(bool force); - - std::string m_name; - int m_last_progress_percent = -1; -}; -} // namespace - -/// Starts the virtual machine. -static void StartSystem(SystemBootParameters params); - -static bool ParseCommandLineParametersAndInitializeConfig(int argc, char* argv[], - std::optional& autoboot); -static void PrintCommandLineVersion(); -static void PrintCommandLineHelp(const char* progname); -static bool InitializeConfig(std::string settings_filename); -static void InitializeEarlyConsole(); -static void HookSignals(); -static bool ShouldUsePortableMode(); -static void SetAppRoot(); -static void SetResourcesDirectory(); -static void SetDataDirectory(); -static bool SetCriticalFolders(); -static void SetDefaultSettings(SettingsInterface& si, bool system, bool controller); -static std::string GetResourcePath(std::string_view name, bool allow_override); -static void StartCPUThread(); -static void StopCPUThread(); -static void ProcessCPUThreadEvents(bool block); -static void ProcessCPUThreadPlatformMessages(); -static void CPUThreadEntryPoint(); -static void CPUThreadMainLoop(); -static std::unique_ptr CreatePlatform(); -static std::string GetWindowTitle(const std::string& game_title); -static void UpdateWindowTitle(const std::string& game_title); -static void CancelAsyncOp(); -static void StartAsyncOp(std::function callback); -static void AsyncOpThreadEntryPoint(std::function callback); - -////////////////////////////////////////////////////////////////////////// -// Local variable declarations -////////////////////////////////////////////////////////////////////////// -static std::unique_ptr s_base_settings_interface; -static bool s_batch_mode = false; -static bool s_is_fullscreen = false; -static bool s_was_paused_by_focus_loss = false; - -static Threading::Thread s_cpu_thread; -static Threading::KernelSemaphore s_platform_window_updated; -static std::atomic_bool s_running{false}; -static std::mutex s_cpu_thread_events_mutex; -static std::condition_variable s_cpu_thread_event_done; -static std::condition_variable s_cpu_thread_event_posted; -static std::deque, bool>> s_cpu_thread_events; -static u32 s_blocking_cpu_events_pending = 0; // TODO: Token system would work better here. - -static std::mutex s_async_op_mutex; -static std::thread s_async_op_thread; -static AsyncOpProgressCallback* s_async_op_progress = nullptr; -} // namespace NoGUIHost - -////////////////////////////////////////////////////////////////////////// -// Initialization/Shutdown -////////////////////////////////////////////////////////////////////////// - -bool NoGUIHost::SetCriticalFolders() -{ - SetAppRoot(); - SetResourcesDirectory(); - SetDataDirectory(); - - // logging of directories in case something goes wrong super early - Log_DevPrintf("AppRoot Directory: %s", EmuFolders::AppRoot.c_str()); - Log_DevPrintf("DataRoot Directory: %s", EmuFolders::DataRoot.c_str()); - Log_DevPrintf("Resources Directory: %s", EmuFolders::Resources.c_str()); - - // Write crash dumps to the data directory, since that'll be accessible for certain. - CrashHandler::SetWriteDirectory(EmuFolders::DataRoot); - - // the resources directory should exist, bail out if not - if (!FileSystem::DirectoryExists(EmuFolders::Resources.c_str())) - { - g_nogui_window->ReportError("Error", "Resources directory is missing, your installation is incomplete."); - return false; - } - - return true; -} - -bool NoGUIHost::ShouldUsePortableMode() -{ - // Check whether portable.ini exists in the program directory. - return (FileSystem::FileExists(Path::Combine(EmuFolders::AppRoot, "portable.txt").c_str()) || - FileSystem::FileExists(Path::Combine(EmuFolders::AppRoot, "settings.ini").c_str())); -} - -void NoGUIHost::SetAppRoot() -{ - const std::string program_path = FileSystem::GetProgramPath(); - Log_InfoPrintf("Program Path: %s", program_path.c_str()); - - EmuFolders::AppRoot = Path::Canonicalize(Path::GetDirectory(program_path)); -} - -void NoGUIHost::SetResourcesDirectory() -{ -#ifndef __APPLE__NOT_USED // Not using bundles yet. - // On Windows/Linux, these are in the binary directory. - EmuFolders::Resources = Path::Combine(EmuFolders::AppRoot, "resources"); -#else - // On macOS, this is in the bundle resources directory. - EmuFolders::Resources = Path::Canonicalize(Path::Combine(EmuFolders::AppRoot, "../Resources")); -#endif -} - -void NoGUIHost::SetDataDirectory() -{ - // Already set, e.g. by -portable. - if (!EmuFolders::DataRoot.empty()) - return; - - if (ShouldUsePortableMode()) - { - EmuFolders::DataRoot = EmuFolders::AppRoot; - return; - } - -#if defined(_WIN32) - // On Windows, use My Documents\DuckStation. - PWSTR documents_directory; - if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &documents_directory))) - { - if (std::wcslen(documents_directory) > 0) - EmuFolders::DataRoot = Path::Combine(StringUtil::WideStringToUTF8String(documents_directory), "DuckStation"); - CoTaskMemFree(documents_directory); - } -#elif defined(__linux__) || defined(__FreeBSD__) - // Use $XDG_CONFIG_HOME/duckstation if it exists. - const char* xdg_config_home = getenv("XDG_CONFIG_HOME"); - if (xdg_config_home && Path::IsAbsolute(xdg_config_home)) - { - EmuFolders::DataRoot = Path::RealPath(Path::Combine(xdg_config_home, "duckstation")); - } - else - { - // Use ~/.local/share/duckstation otherwise. - const char* home_dir = getenv("HOME"); - if (home_dir) - { - // ~/.local/share should exist, but just in case it doesn't and this is a fresh profile.. - const std::string local_dir(Path::Combine(home_dir, ".local")); - const std::string share_dir(Path::Combine(local_dir, "share")); - FileSystem::EnsureDirectoryExists(local_dir.c_str(), false); - FileSystem::EnsureDirectoryExists(share_dir.c_str(), false); - EmuFolders::DataRoot = Path::RealPath(Path::Combine(share_dir, "duckstation")); - } - } -#elif defined(__APPLE__) - static constexpr char MAC_DATA_DIR[] = "Library/Application Support/DuckStation"; - const char* home_dir = getenv("HOME"); - if (home_dir) - EmuFolders::DataRoot = Path::RealPath(Path::Combine(home_dir, MAC_DATA_DIR)); -#endif - - // make sure it exists - if (!EmuFolders::DataRoot.empty() && !FileSystem::DirectoryExists(EmuFolders::DataRoot.c_str())) - { - // we're in trouble if we fail to create this directory... but try to hobble on with portable - if (!FileSystem::EnsureDirectoryExists(EmuFolders::DataRoot.c_str(), false)) - EmuFolders::DataRoot.clear(); - } - - // couldn't determine the data directory? fallback to portable. - if (EmuFolders::DataRoot.empty()) - EmuFolders::DataRoot = EmuFolders::AppRoot; -} - -bool NoGUIHost::InitializeConfig(std::string settings_filename) -{ - if (!SetCriticalFolders()) - return false; - - if (settings_filename.empty()) - settings_filename = Path::Combine(EmuFolders::DataRoot, "settings.ini"); - - Log_InfoPrintf("Loading config from %s.", settings_filename.c_str()); - s_base_settings_interface = std::make_unique(std::move(settings_filename)); - Host::Internal::SetBaseSettingsLayer(s_base_settings_interface.get()); - - u32 settings_version; - if (!s_base_settings_interface->Load() || - !s_base_settings_interface->GetUIntValue("Main", "SettingsVersion", &settings_version) || - settings_version != SETTINGS_VERSION) - { - if (s_base_settings_interface->ContainsValue("Main", "SettingsVersion")) - { - // NOTE: No point translating this, because there's no config loaded, so no language loaded. - Host::ReportErrorAsync("Error", fmt::format("Settings version {} does not match expected version {}, resetting.", - settings_version, SETTINGS_VERSION)); - } - - s_base_settings_interface->SetUIntValue("Main", "SettingsVersion", SETTINGS_VERSION); - SetDefaultSettings(*s_base_settings_interface, true, true); - s_base_settings_interface->Save(); - } - - EmuFolders::LoadConfig(*s_base_settings_interface.get()); - EmuFolders::EnsureFoldersExist(); - - // We need to create the console window early, otherwise it appears behind the main window. - if (!Log::IsConsoleOutputEnabled() && - s_base_settings_interface->GetBoolValue("Logging", "LogToConsole", Settings::DEFAULT_LOG_TO_CONSOLE)) - { - Log::SetConsoleOutputParams(true, s_base_settings_interface->GetBoolValue("Logging", "LogTimestamps", true)); - } - - return true; -} - -void NoGUIHost::SetDefaultSettings(SettingsInterface& si, bool system, bool controller) -{ - if (system) - { - System::SetDefaultSettings(si); - EmuFolders::SetDefaults(); - EmuFolders::Save(si); - } - - if (controller) - { - InputManager::SetDefaultSourceConfig(si); - Settings::SetDefaultControllerConfig(si); - Settings::SetDefaultHotkeyConfig(si); - } - - g_nogui_window->SetDefaultConfig(si); -} - -void Host::ReportFatalError(const std::string_view& title, const std::string_view& message) -{ - Log_ErrorPrintf("ReportFatalError: %.*s", static_cast(message.size()), message.data()); - abort(); -} - -void Host::ReportErrorAsync(const std::string_view& title, const std::string_view& message) -{ - if (!title.empty() && !message.empty()) - { - Log_ErrorPrintf("ReportErrorAsync: %.*s: %.*s", static_cast(title.size()), title.data(), - static_cast(message.size()), message.data()); - } - else if (!message.empty()) - { - Log_ErrorPrintf("ReportErrorAsync: %.*s", static_cast(message.size()), message.data()); - } - - g_nogui_window->ReportError(title, message); -} - -bool Host::ConfirmMessage(const std::string_view& title, const std::string_view& message) -{ - if (!title.empty() && !message.empty()) - { - Log_ErrorPrintf("ConfirmMessage: %.*s: %.*s", static_cast(title.size()), title.data(), - static_cast(message.size()), message.data()); - } - else if (!message.empty()) - { - Log_ErrorPrintf("ConfirmMessage: %.*s", static_cast(message.size()), message.data()); - } - - return g_nogui_window->ConfirmMessage(title, message); -} - -void Host::ReportDebuggerMessage(const std::string_view& message) -{ - Log_ErrorPrintf("ReportDebuggerMessage: %.*s", static_cast(message.size()), message.data()); -} - -std::span> Host::GetAvailableLanguageList() -{ - return {}; -} - -bool Host::ChangeLanguage(const char* new_language) -{ - return false; -} - -void Host::AddFixedInputBindings(SettingsInterface& si) -{ -} - -void Host::OnInputDeviceConnected(const std::string_view& identifier, const std::string_view& device_name) -{ - Host::AddKeyedOSDMessage(fmt::format("InputDeviceConnected-{}", identifier), - fmt::format("Input device {0} ({1}) connected.", device_name, identifier), 10.0f); -} - -void Host::OnInputDeviceDisconnected(const std::string_view& identifier) -{ - Host::AddKeyedOSDMessage(fmt::format("InputDeviceConnected-{}", identifier), - fmt::format("Input device {} disconnected.", identifier), 10.0f); -} - -s32 Host::Internal::GetTranslatedStringImpl(const std::string_view& context, const std::string_view& msg, char* tbuf, - size_t tbuf_space) -{ - if (msg.size() > tbuf_space) - return -1; - else if (msg.empty()) - return 0; - - std::memcpy(tbuf, msg.data(), msg.size()); - return static_cast(msg.size()); -} - -ALWAYS_INLINE std::string NoGUIHost::GetResourcePath(std::string_view filename, bool allow_override) -{ - return allow_override ? EmuFolders::GetOverridableResourcePath(filename) : - Path::Combine(EmuFolders::Resources, filename); -} - -bool Host::ResourceFileExists(std::string_view filename, bool allow_override) -{ - const std::string path = NoGUIHost::GetResourcePath(filename, allow_override); - return FileSystem::FileExists(path.c_str()); -} - -std::optional> Host::ReadResourceFile(std::string_view filename, bool allow_override) -{ - const std::string path = NoGUIHost::GetResourcePath(filename, allow_override); - std::optional> ret(FileSystem::ReadBinaryFile(path.c_str())); - if (!ret.has_value()) - Log_ErrorPrintf("Failed to read resource file '%s'", filename); - return ret; -} - -std::optional Host::ReadResourceFileToString(std::string_view filename, bool allow_override) -{ - const std::string path = NoGUIHost::GetResourcePath(filename, allow_override); - std::optional ret(FileSystem::ReadFileToString(path.c_str())); - if (!ret.has_value()) - Log_ErrorPrintf("Failed to read resource file to string '%s'", filename); - return ret; -} - -std::optional Host::GetResourceFileTimestamp(std::string_view filename, bool allow_override) -{ - const std::string path = NoGUIHost::GetResourcePath(filename, allow_override); - FILESYSTEM_STAT_DATA sd; - if (!FileSystem::StatFile(path.c_str(), &sd)) - { - Log_ErrorPrintf("Failed to stat resource file '%s'", filename); - return std::nullopt; - } - - return sd.ModificationTime; -} - -void Host::LoadSettings(SettingsInterface& si, std::unique_lock& lock) -{ -} - -void Host::CheckForSettingsChanges(const Settings& old_settings) -{ -} - -void Host::CommitBaseSettingChanges() -{ - NoGUIHost::SaveSettings(); -} - -void NoGUIHost::SaveSettings() -{ - auto lock = Host::GetSettingsLock(); - if (!s_base_settings_interface->Save()) - Log_ErrorPrintf("Failed to save settings."); -} - -bool NoGUIHost::InBatchMode() -{ - return s_batch_mode; -} - -void NoGUIHost::SetBatchMode(bool enabled) -{ - s_batch_mode = enabled; - if (enabled) - GameList::Refresh(false, true); -} - -void NoGUIHost::StartSystem(SystemBootParameters params) -{ - Host::RunOnCPUThread([params = std::move(params)]() { System::BootSystem(std::move(params)); }); -} - -void NoGUIHost::ProcessPlatformWindowResize(s32 width, s32 height, float scale) -{ - Host::RunOnCPUThread([width, height, scale]() { - g_gpu_device->ResizeWindow(width, height, scale); - ImGuiManager::WindowResized(); - System::HostDisplayResized(); - }); -} - -void NoGUIHost::ProcessPlatformMouseMoveEvent(float x, float y) -{ - InputManager::UpdatePointerAbsolutePosition(0, x, y); - ImGuiManager::UpdateMousePosition(x, y); -} - -void NoGUIHost::ProcessPlatformMouseButtonEvent(s32 button, bool pressed) -{ - Host::RunOnCPUThread([button, pressed]() { - InputManager::InvokeEvents(InputManager::MakePointerButtonKey(0, button), pressed ? 1.0f : 0.0f, - GenericInputBinding::Unknown); - }); -} - -void NoGUIHost::ProcessPlatformMouseWheelEvent(float x, float y) -{ - if (x != 0.0f) - InputManager::UpdatePointerRelativeDelta(0, InputPointerAxis::WheelX, x); - if (y != 0.0f) - InputManager::UpdatePointerRelativeDelta(0, InputPointerAxis::WheelY, y); -} - -void NoGUIHost::ProcessPlatformKeyEvent(s32 key, bool pressed) -{ - Host::RunOnCPUThread([key, pressed]() { - InputManager::InvokeEvents(InputManager::MakeHostKeyboardKey(key), pressed ? 1.0f : 0.0f, - GenericInputBinding::Unknown); - }); -} - -void NoGUIHost::ProcessPlatformTextEvent(const char* text) -{ - if (!ImGuiManager::WantsTextInput()) - return; - - Host::RunOnCPUThread([text = std::string(text)]() { ImGuiManager::AddTextInput(std::move(text)); }); -} - -void NoGUIHost::PlatformWindowFocusGained() -{ - Host::RunOnCPUThread([]() { - if (!System::IsValid() || !s_was_paused_by_focus_loss) - return; - - System::PauseSystem(false); - s_was_paused_by_focus_loss = false; - }); -} - -void NoGUIHost::PlatformWindowFocusLost() -{ - Host::RunOnCPUThread([]() { - if (!System::IsRunning() || !g_settings.pause_on_focus_loss) - return; - - s_was_paused_by_focus_loss = true; - System::PauseSystem(true); - }); -} - -void NoGUIHost::PlatformDevicesChanged() -{ - Host::RunOnCPUThread([]() { InputManager::ReloadDevices(); }); -} - -bool NoGUIHost::GetSavedPlatformWindowGeometry(s32* x, s32* y, s32* width, s32* height) -{ - auto lock = Host::GetSettingsLock(); - - bool result = s_base_settings_interface->GetIntValue("NoGUI", "WindowX", x); - result = result && s_base_settings_interface->GetIntValue("NoGUI", "WindowY", y); - result = result && s_base_settings_interface->GetIntValue("NoGUI", "WindowWidth", width); - result = result && s_base_settings_interface->GetIntValue("NoGUI", "WindowHeight", height); - return result; -} - -void NoGUIHost::SavePlatformWindowGeometry(s32 x, s32 y, s32 width, s32 height) -{ - if (s_is_fullscreen) - return; - - auto lock = Host::GetSettingsLock(); - s_base_settings_interface->SetIntValue("NoGUI", "WindowX", x); - s_base_settings_interface->SetIntValue("NoGUI", "WindowY", y); - s_base_settings_interface->SetIntValue("NoGUI", "WindowWidth", width); - s_base_settings_interface->SetIntValue("NoGUI", "WindowHeight", height); - s_base_settings_interface->Save(); -} - -std::string NoGUIHost::GetAppNameAndVersion() -{ - return fmt::format("DuckStation {}", g_scm_tag_str); -} - -std::string NoGUIHost::GetAppConfigSuffix() -{ -#if defined(_DEBUGFAST) - return " [DebugFast]"; -#elif defined(_DEBUG) - return " [Debug]"; -#else - return std::string(); -#endif -} - -void NoGUIHost::StartCPUThread() -{ - s_running.store(true, std::memory_order_release); - s_cpu_thread.Start(CPUThreadEntryPoint); -} - -void NoGUIHost::StopCPUThread() -{ - if (!s_cpu_thread.Joinable()) - return; - - { - std::unique_lock lock(s_cpu_thread_events_mutex); - s_running.store(false, std::memory_order_release); - s_cpu_thread_event_posted.notify_one(); - } - s_cpu_thread.Join(); -} - -void NoGUIHost::ProcessCPUThreadPlatformMessages() -{ - // This is lame. On Win32, we need to pump messages, even though *we* don't have any windows - // on the CPU thread, because SDL creates a hidden window for raw input for some game controllers. - // If we don't do this, we don't get any controller events. -#ifdef _WIN32 - MSG msg; - while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) - { - TranslateMessage(&msg); - DispatchMessageW(&msg); - } -#endif -} - -void NoGUIHost::ProcessCPUThreadEvents(bool block) -{ - std::unique_lock lock(s_cpu_thread_events_mutex); - - for (;;) - { - if (s_cpu_thread_events.empty()) - { - if (!block || !s_running.load(std::memory_order_acquire)) - return; - - // we still need to keep polling the controllers when we're paused - do - { - ProcessCPUThreadPlatformMessages(); - InputManager::PollSources(); - } while (!s_cpu_thread_event_posted.wait_for(lock, CPU_THREAD_POLL_INTERVAL, - []() { return !s_cpu_thread_events.empty(); })); - } - - // return after processing all events if we had one - block = false; - - auto event = std::move(s_cpu_thread_events.front()); - s_cpu_thread_events.pop_front(); - lock.unlock(); - event.first(); - lock.lock(); - - if (event.second) - { - s_blocking_cpu_events_pending--; - s_cpu_thread_event_done.notify_one(); - } - } -} - -void NoGUIHost::CPUThreadEntryPoint() -{ - Threading::SetNameOfCurrentThread("CPU Thread"); - - // input source setup must happen on emu thread - if (!System::Internal::ProcessStartup()) - { - g_nogui_window->QuitMessageLoop(); - return; - } - - // start the fullscreen UI and get it going - if (Host::CreateGPUDevice(Settings::GetRenderAPIForRenderer(g_settings.gpu_renderer)) && FullscreenUI::Initialize()) - { - // kick a game list refresh if we're not in batch mode - if (!InBatchMode()) - Host::RefreshGameListAsync(false); - - CPUThreadMainLoop(); - - Host::CancelGameListRefresh(); - } - else - { - g_nogui_window->ReportError("Error", "Failed to open host display."); - } - - // finish any events off (e.g. shutdown system with save) - ProcessCPUThreadEvents(false); - - if (System::IsValid()) - System::ShutdownSystem(false); - Host::ReleaseGPUDevice(); - Host::ReleaseRenderWindow(); - - System::Internal::ProcessShutdown(); - g_nogui_window->QuitMessageLoop(); -} - -void NoGUIHost::CPUThreadMainLoop() -{ - while (s_running.load(std::memory_order_acquire)) - { - if (System::IsRunning()) - { - System::Execute(); - continue; - } - - Host::PumpMessagesOnCPUThread(); - System::Internal::IdlePollUpdate(); - System::PresentDisplay(false); - if (!g_gpu_device->IsVSyncActive()) - g_gpu_device->ThrottlePresentation(); - } -} - -std::optional Host::AcquireRenderWindow(bool recreate_window) -{ - std::optional wi; - - g_nogui_window->ExecuteInMessageLoop([&wi, recreate_window]() { - bool res = g_nogui_window->HasPlatformWindow(); - if (!res || recreate_window) - { - if (res) - g_nogui_window->DestroyPlatformWindow(); - - res = g_nogui_window->CreatePlatformWindow(NoGUIHost::GetWindowTitle(System::GetGameTitle())); - } - if (res) - wi = g_nogui_window->GetPlatformWindowInfo(); - NoGUIHost::s_platform_window_updated.Post(); - }); - - NoGUIHost::s_platform_window_updated.Wait(); - - if (!wi.has_value()) - { - g_nogui_window->ReportError("Error", "Failed to create render window."); - return std::nullopt; - } - - // reload input sources, since it might use the window handle - { - auto lock = Host::GetSettingsLock(); - InputManager::ReloadSources(*Host::GetSettingsInterface(), lock); - } - - return wi; -} - -void Host::ReleaseRenderWindow() -{ - // Need to block here, otherwise the recreation message associates with the old window. - g_nogui_window->ExecuteInMessageLoop([]() { - g_nogui_window->DestroyPlatformWindow(); - NoGUIHost::s_platform_window_updated.Post(); - }); - NoGUIHost::s_platform_window_updated.Wait(); -} - -void Host::OnSystemStarting() -{ - NoGUIHost::s_was_paused_by_focus_loss = false; -} - -void Host::OnSystemStarted() -{ -} - -void Host::OnSystemPaused() -{ -} - -void Host::OnSystemResumed() -{ -} - -void Host::OnSystemDestroyed() -{ -} - -void Host::OnIdleStateChanged() -{ -} - -void Host::BeginPresentFrame() -{ -} - -void Host::RequestResizeHostDisplay(s32 width, s32 height) -{ - g_nogui_window->RequestRenderWindowSize(width, height); -} - -void Host::OpenURL(const std::string_view& url) -{ - g_nogui_window->OpenURL(url); -} - -bool Host::CopyTextToClipboard(const std::string_view& text) -{ - return g_nogui_window->CopyTextToClipboard(text); -} - -void Host::OnPerformanceCountersUpdated() -{ - // noop -} - -void Host::OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name) -{ - Log_VerbosePrintf("Host::OnGameChanged(\"%s\", \"%s\", \"%s\")", disc_path.c_str(), game_serial.c_str(), - game_name.c_str()); - NoGUIHost::UpdateWindowTitle(game_name); -} - -void Host::OnAchievementsLoginRequested(Achievements::LoginRequestReason reason) -{ - // noop -} - -void Host::OnAchievementsLoginSuccess(const char* username, u32 points, u32 sc_points, u32 unread_messages) -{ - // noop -} - -void Host::OnAchievementsRefreshed() -{ - // noop -} - -void Host::OnAchievementsHardcoreModeChanged(bool enabled) -{ - // noop -} - -void Host::OnCoverDownloaderOpenRequested() -{ - // noop -} - -void Host::SetMouseMode(bool relative, bool hide_cursor) -{ - // noop -} - -void Host::PumpMessagesOnCPUThread() -{ - NoGUIHost::ProcessCPUThreadPlatformMessages(); - NoGUIHost::ProcessCPUThreadEvents(false); -} - -std::unique_ptr NoGUIHost::CreatePlatform() -{ - std::unique_ptr ret; - - const char* platform = std::getenv("DUCKSTATION_NOGUI_PLATFORM"); -#ifdef ENABLE_SDL2 - if (platform && StringUtil::Strcasecmp(platform, "sdl") == 0) - ret = NoGUIPlatform::CreateSDLPlatform(); -#endif - -#if defined(_WIN32) - if (!ret) - ret = NoGUIPlatform::CreateWin32Platform(); -#elif defined(__APPLE__) - if (!ret) - ret = NoGUIPlatform::CreateCocoaPlatform(); -#else - // linux -#ifdef NOGUI_PLATFORM_WAYLAND - if (!ret && (!platform || StringUtil::Strcasecmp(platform, "wayland") == 0) && std::getenv("WAYLAND_DISPLAY")) - ret = NoGUIPlatform::CreateWaylandPlatform(); -#endif -#ifdef NOGUI_PLATFORM_X11 - if (!ret && (!platform || StringUtil::Strcasecmp(platform, "x11") == 0) && std::getenv("DISPLAY")) - ret = NoGUIPlatform::CreateX11Platform(); -#endif -#endif - - return ret; -} - -std::string NoGUIHost::GetWindowTitle(const std::string& game_title) -{ - std::string suffix(GetAppConfigSuffix()); - std::string window_title; - if (System::IsShutdown() || game_title.empty()) - window_title = GetAppNameAndVersion() + suffix; - else - window_title = game_title; - - return window_title; -} - -void NoGUIHost::UpdateWindowTitle(const std::string& game_title) -{ - g_nogui_window->SetPlatformWindowTitle(GetWindowTitle(game_title)); -} - -void Host::RunOnCPUThread(std::function function, bool block /* = false */) -{ - std::unique_lock lock(NoGUIHost::s_cpu_thread_events_mutex); - NoGUIHost::s_cpu_thread_events.emplace_back(std::move(function), block); - NoGUIHost::s_cpu_thread_event_posted.notify_one(); - if (block) - NoGUIHost::s_cpu_thread_event_done.wait(lock, []() { return NoGUIHost::s_blocking_cpu_events_pending == 0; }); -} - -void NoGUIHost::StartAsyncOp(std::function callback) -{ - CancelAsyncOp(); - s_async_op_thread = std::thread(AsyncOpThreadEntryPoint, std::move(callback)); -} - -void NoGUIHost::CancelAsyncOp() -{ - std::unique_lock lock(s_async_op_mutex); - if (!s_async_op_thread.joinable()) - return; - - if (s_async_op_progress) - s_async_op_progress->SetCancelled(); - - lock.unlock(); - s_async_op_thread.join(); -} - -void NoGUIHost::AsyncOpThreadEntryPoint(std::function callback) -{ - Threading::SetNameOfCurrentThread("Async Op"); - - AsyncOpProgressCallback fs_callback("async_op"); - std::unique_lock lock(s_async_op_mutex); - s_async_op_progress = &fs_callback; - - lock.unlock(); - callback(&fs_callback); - lock.lock(); - - s_async_op_progress = nullptr; -} - -void Host::RefreshGameListAsync(bool invalidate_cache) -{ - NoGUIHost::StartAsyncOp( - [invalidate_cache](ProgressCallback* progress) { GameList::Refresh(invalidate_cache, false, progress); }); -} - -void Host::CancelGameListRefresh() -{ - NoGUIHost::CancelAsyncOp(); -} - -bool Host::IsFullscreen() -{ - return NoGUIHost::s_is_fullscreen; -} - -void Host::SetFullscreen(bool enabled) -{ - if (NoGUIHost::s_is_fullscreen == enabled) - return; - - NoGUIHost::s_is_fullscreen = enabled; - g_nogui_window->SetFullscreen(enabled); -} - -std::optional Host::GetTopLevelWindowInfo() -{ - return g_nogui_window->GetPlatformWindowInfo(); -} - -void Host::RequestExit(bool allow_confirm) -{ - if (System::IsValid()) - { - Host::RunOnCPUThread([]() { System::ShutdownSystem(g_settings.save_state_on_exit); }); - } - - // clear the running flag, this'll break out of the main CPU loop once the VM is shutdown. - NoGUIHost::s_running.store(false, std::memory_order_release); -} - -void Host::RequestSystemShutdown(bool allow_confirm, bool save_state) -{ - // TODO: Confirm - if (System::IsValid()) - { - Host::RunOnCPUThread([save_state]() { System::ShutdownSystem(save_state); }); - } -} - -std::optional InputManager::ConvertHostKeyboardStringToCode(const std::string_view& str) -{ - return g_nogui_window->ConvertHostKeyboardStringToCode(str); -} - -std::optional InputManager::ConvertHostKeyboardCodeToString(u32 code) -{ - return g_nogui_window->ConvertHostKeyboardCodeToString(code); -} - -const char* InputManager::ConvertHostKeyboardCodeToIcon(u32 code) -{ - return nullptr; -} - -BEGIN_HOTKEY_LIST(g_host_hotkeys) -END_HOTKEY_LIST() - -static void SignalHandler(int signal) -{ - // First try the normal (graceful) shutdown/exit. - static bool graceful_shutdown_attempted = false; - if (!graceful_shutdown_attempted) - { - std::fprintf(stderr, "Received CTRL+C, attempting graceful shutdown. Press CTRL+C again to force.\n"); - graceful_shutdown_attempted = true; - Host::RequestExit(false); - return; - } - - std::signal(signal, SIG_DFL); - - // MacOS is missing std::quick_exit() despite it being C++11... -#ifndef __APPLE__ - std::quick_exit(1); -#else - _Exit(1); -#endif -} - -void NoGUIHost::HookSignals() -{ - std::signal(SIGINT, SignalHandler); - std::signal(SIGTERM, SignalHandler); -} - -void NoGUIHost::InitializeEarlyConsole() -{ - const bool was_console_enabled = Log::IsConsoleOutputEnabled(); - if (!was_console_enabled) - Log::SetConsoleOutputParams(true); -} - -void NoGUIHost::PrintCommandLineVersion() -{ - InitializeEarlyConsole(); - - std::fprintf(stderr, "DuckStation Version %s (%s)\n", g_scm_tag_str, g_scm_branch_str); - std::fprintf(stderr, "https://github.com/stenzek/duckstation\n"); - std::fprintf(stderr, "\n"); -} - -void NoGUIHost::PrintCommandLineHelp(const char* progname) -{ - InitializeEarlyConsole(); - - PrintCommandLineVersion(); - std::fprintf(stderr, "Usage: %s [parameters] [--] [boot filename]\n", progname); - std::fprintf(stderr, "\n"); - std::fprintf(stderr, " -help: Displays this information and exits.\n"); - std::fprintf(stderr, " -version: Displays version information and exits.\n"); - std::fprintf(stderr, " -batch: Enables batch mode (exits after powering off).\n"); - std::fprintf(stderr, " -fastboot: Force fast boot for provided filename.\n"); - std::fprintf(stderr, " -slowboot: Force slow boot for provided filename.\n"); - std::fprintf(stderr, " -bios: Boot into the BIOS shell.\n"); - std::fprintf(stderr, " -resume: Load resume save state. If a boot filename is provided,\n" - " that game's resume state will be loaded, otherwise the most\n" - " recent resume save state will be loaded.\n"); - std::fprintf(stderr, " -state : Loads specified save state by index. If a boot\n" - " filename is provided, a per-game state will be loaded, otherwise\n" - " a global state will be loaded.\n"); - std::fprintf(stderr, " -statefile : Loads state from the specified filename.\n" - " No boot filename is required with this option.\n"); - std::fprintf(stderr, " -exe : Boot the specified exe instead of loading from disc.\n"); - std::fprintf(stderr, " -fullscreen: Enters fullscreen mode immediately after starting.\n"); - std::fprintf(stderr, " -nofullscreen: Prevents fullscreen mode from triggering if enabled.\n"); - std::fprintf(stderr, " -portable: Forces \"portable mode\", data in same directory.\n"); - std::fprintf(stderr, " -settings : Loads a custom settings configuration from the\n" - " specified filename. Default settings applied if file not found.\n"); - std::fprintf(stderr, " -earlyconsole: Creates console as early as possible, for logging.\n"); - std::fprintf(stderr, " --: Signals that no more arguments will follow and the remaining\n" - " parameters make up the filename. Use when the filename contains\n" - " spaces or starts with a dash.\n"); - std::fprintf(stderr, "\n"); -} - -std::optional& AutoBoot(std::optional& autoboot) -{ - if (!autoboot) - autoboot.emplace(); - - return autoboot; -} - -bool NoGUIHost::ParseCommandLineParametersAndInitializeConfig(int argc, char* argv[], - std::optional& autoboot) -{ - std::optional state_index; - std::string settings_filename; - bool starting_bios = false; - - bool no_more_args = false; - - for (int i = 1; i < argc; i++) - { - if (!no_more_args) - { -#define CHECK_ARG(str) (std::strcmp(argv[i], (str)) == 0) -#define CHECK_ARG_PARAM(str) (std::strcmp(argv[i], (str)) == 0 && ((i + 1) < argc)) - - if (CHECK_ARG("-help")) - { - PrintCommandLineHelp(argv[0]); - return false; - } - else if (CHECK_ARG("-version")) - { - PrintCommandLineVersion(); - return false; - } - else if (CHECK_ARG("-batch")) - { - Log_InfoPrintf("Command Line: Using batch mode."); - s_batch_mode = true; - continue; - } - else if (CHECK_ARG("-bios")) - { - Log_InfoPrintf("Command Line: Starting BIOS."); - AutoBoot(autoboot); - starting_bios = true; - continue; - } - else if (CHECK_ARG("-fastboot")) - { - Log_InfoPrintf("Command Line: Forcing fast boot."); - AutoBoot(autoboot)->override_fast_boot = true; - continue; - } - else if (CHECK_ARG("-slowboot")) - { - Log_InfoPrintf("Command Line: Forcing slow boot."); - AutoBoot(autoboot)->override_fast_boot = false; - continue; - } - else if (CHECK_ARG("-resume")) - { - state_index = -1; - Log_InfoPrintf("Command Line: Loading resume state."); - continue; - } - else if (CHECK_ARG_PARAM("-state")) - { - state_index = StringUtil::FromChars(argv[++i]); - if (!state_index.has_value()) - { - Log_ErrorPrintf("Invalid state index"); - return false; - } - - Log_InfoPrintf("Command Line: Loading state index: %d", state_index.value()); - continue; - } - else if (CHECK_ARG_PARAM("-statefile")) - { - AutoBoot(autoboot)->save_state = argv[++i]; - Log_InfoPrintf("Command Line: Loading state file: '%s'", autoboot->save_state.c_str()); - continue; - } - else if (CHECK_ARG_PARAM("-exe")) - { - AutoBoot(autoboot)->override_exe = argv[++i]; - Log_InfoPrintf("Command Line: Overriding EXE file: '%s'", autoboot->override_exe.c_str()); - continue; - } - else if (CHECK_ARG("-fullscreen")) - { - Log_InfoPrintf("Command Line: Using fullscreen."); - AutoBoot(autoboot)->override_fullscreen = true; - // s_start_fullscreen_ui_fullscreen = true; - continue; - } - else if (CHECK_ARG("-nofullscreen")) - { - Log_InfoPrintf("Command Line: Not using fullscreen."); - AutoBoot(autoboot)->override_fullscreen = false; - continue; - } - else if (CHECK_ARG("-portable")) - { - Log_InfoPrintf("Command Line: Using portable mode."); - EmuFolders::DataRoot = EmuFolders::AppRoot; - continue; - } - else if (CHECK_ARG_PARAM("-settings")) - { - settings_filename = argv[++i]; - Log_InfoPrintf("Command Line: Overriding settings filename: %s", settings_filename.c_str()); - continue; - } - else if (CHECK_ARG("-earlyconsole")) - { - InitializeEarlyConsole(); - continue; - } - else if (CHECK_ARG("--")) - { - no_more_args = true; - continue; - } - else if (argv[i][0] == '-') - { - g_nogui_window->ReportError("Error", fmt::format("Unknown parameter: {}", argv[i])); - return false; - } - -#undef CHECK_ARG -#undef CHECK_ARG_PARAM - } - - if (autoboot && !autoboot->filename.empty()) - autoboot->filename += ' '; - AutoBoot(autoboot)->filename += argv[i]; - } - - // To do anything useful, we need the config initialized. - if (!NoGUIHost::InitializeConfig(std::move(settings_filename))) - { - // NOTE: No point translating this, because no config means the language won't be loaded anyway. - g_nogui_window->ReportError("Error", "Failed to initialize config."); - return EXIT_FAILURE; - } - - // Check the file we're starting actually exists. - - if (autoboot && !autoboot->filename.empty() && !FileSystem::FileExists(autoboot->filename.c_str())) - { - g_nogui_window->ReportError("Error", fmt::format("File '{}' does not exist.", autoboot->filename)); - return false; - } - - if (state_index.has_value()) - { - AutoBoot(autoboot); - - if (autoboot->filename.empty()) - { - // loading global state, -1 means resume the last game - if (state_index.value() < 0) - autoboot->save_state = System::GetMostRecentResumeSaveStatePath(); - else - autoboot->save_state = System::GetGlobalSaveStateFileName(state_index.value()); - } - else - { - // loading game state - const std::string game_serial(GameDatabase::GetSerialForPath(autoboot->filename.c_str())); - autoboot->save_state = System::GetGameSaveStateFileName(game_serial, state_index.value()); - } - - if (autoboot->save_state.empty() || !FileSystem::FileExists(autoboot->save_state.c_str())) - { - g_nogui_window->ReportError("Error", "The specified save state does not exist."); - return false; - } - } - - // check autoboot parameters, if we set something like fullscreen without a bios - // or disc, we don't want to actually start. - if (autoboot && autoboot->filename.empty() && autoboot->save_state.empty() && !starting_bios) - autoboot.reset(); - - return true; -} - -NoGUIHost::AsyncOpProgressCallback::AsyncOpProgressCallback(std::string name) - : BaseProgressCallback(), m_name(std::move(name)) -{ - ImGuiFullscreen::OpenBackgroundProgressDialog(m_name.c_str(), "", 0, 100, 0); -} - -NoGUIHost::AsyncOpProgressCallback::~AsyncOpProgressCallback() -{ - ImGuiFullscreen::CloseBackgroundProgressDialog(m_name.c_str()); -} - -void NoGUIHost::AsyncOpProgressCallback::PushState() -{ - BaseProgressCallback::PushState(); -} - -void NoGUIHost::AsyncOpProgressCallback::PopState() -{ - BaseProgressCallback::PopState(); - Redraw(true); -} - -void NoGUIHost::AsyncOpProgressCallback::SetCancellable(bool cancellable) -{ - BaseProgressCallback::SetCancellable(cancellable); - Redraw(true); -} - -void NoGUIHost::AsyncOpProgressCallback::SetTitle(const char* title) -{ - // todo? -} - -void NoGUIHost::AsyncOpProgressCallback::SetStatusText(const char* text) -{ - BaseProgressCallback::SetStatusText(text); - Redraw(true); -} - -void NoGUIHost::AsyncOpProgressCallback::SetProgressRange(u32 range) -{ - u32 last_range = m_progress_range; - - BaseProgressCallback::SetProgressRange(range); - - if (m_progress_range != last_range) - Redraw(false); -} - -void NoGUIHost::AsyncOpProgressCallback::SetProgressValue(u32 value) -{ - u32 lastValue = m_progress_value; - - BaseProgressCallback::SetProgressValue(value); - - if (m_progress_value != lastValue) - Redraw(false); -} - -void NoGUIHost::AsyncOpProgressCallback::Redraw(bool force) -{ - const int percent = - static_cast((static_cast(m_progress_value) / static_cast(m_progress_range)) * 100.0f); - if (percent == m_last_progress_percent && !force) - return; - - m_last_progress_percent = percent; - ImGuiFullscreen::UpdateBackgroundProgressDialog(m_name.c_str(), m_status_text, 0, 100, percent); -} - -void NoGUIHost::AsyncOpProgressCallback::DisplayError(const char* message) -{ - Log_ErrorPrint(message); - Host::ReportErrorAsync("Error", message); -} - -void NoGUIHost::AsyncOpProgressCallback::DisplayWarning(const char* message) -{ - Log_WarningPrint(message); -} - -void NoGUIHost::AsyncOpProgressCallback::DisplayInformation(const char* message) -{ - Log_InfoPrint(message); -} - -void NoGUIHost::AsyncOpProgressCallback::DisplayDebugMessage(const char* message) -{ - Log_DebugPrint(message); -} - -void NoGUIHost::AsyncOpProgressCallback::ModalError(const char* message) -{ - Log_ErrorPrint(message); - Host::ReportErrorAsync("Error", message); -} - -bool NoGUIHost::AsyncOpProgressCallback::ModalConfirmation(const char* message) -{ - return false; -} - -void NoGUIHost::AsyncOpProgressCallback::ModalInformation(const char* message) -{ - Log_InfoPrint(message); -} - -void NoGUIHost::AsyncOpProgressCallback::SetCancelled() -{ - if (m_cancellable) - m_cancelled = true; -} - -int main(int argc, char* argv[]) -{ - CrashHandler::Install(); - - g_nogui_window = NoGUIHost::CreatePlatform(); - if (!g_nogui_window) - return EXIT_FAILURE; - - std::optional autoboot; - if (!NoGUIHost::ParseCommandLineParametersAndInitializeConfig(argc, argv, autoboot)) - return EXIT_FAILURE; - - // the rest of initialization happens on the CPU thread. - NoGUIHost::HookSignals(); - NoGUIHost::StartCPUThread(); - - if (autoboot) - NoGUIHost::StartSystem(std::move(autoboot.value())); - - g_nogui_window->RunMessageLoop(); - - NoGUIHost::CancelAsyncOp(); - NoGUIHost::StopCPUThread(); - - // Ensure log is flushed. - Log::SetFileOutputParams(false, nullptr); - - NoGUIHost::s_base_settings_interface.reset(); - g_nogui_window.reset(); - return EXIT_SUCCESS; -} - -#ifdef _WIN32 - -int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd) -{ - std::vector argc_strings; - argc_strings.reserve(1); - - // CommandLineToArgvW() only adds the program path if the command line is empty?! - argc_strings.push_back(FileSystem::GetProgramPath()); - - if (std::wcslen(lpCmdLine) > 0) - { - int argc; - LPWSTR* argv_wide = CommandLineToArgvW(lpCmdLine, &argc); - if (argv_wide) - { - for (int i = 0; i < argc; i++) - argc_strings.push_back(StringUtil::WideStringToUTF8String(argv_wide[i])); - - LocalFree(argv_wide); - } - } - - std::vector argc_pointers; - argc_pointers.reserve(argc_strings.size()); - for (std::string& arg : argc_strings) - argc_pointers.push_back(arg.data()); - - return main(static_cast(argc_pointers.size()), argc_pointers.data()); -} - -#endif diff --git a/src/duckstation-nogui/nogui_host.h b/src/duckstation-nogui/nogui_host.h deleted file mode 100644 index 7087bded4..000000000 --- a/src/duckstation-nogui/nogui_host.h +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#pragma once -#include "common/types.h" - -#include -#include - -namespace NoGUIHost { -/// Sets batch mode (exit after game shutdown). -bool InBatchMode(); -void SetBatchMode(bool enabled); - -/// Returns the application name and version, optionally including debug/devel config indicator. -std::string GetAppNameAndVersion(); - -/// Returns the debug/devel config indicator. -std::string GetAppConfigSuffix(); - -/// Thread-safe settings access. -void SaveSettings(); - -/// Called on the UI thread in response to various events. -void ProcessPlatformWindowResize(s32 width, s32 height, float scale); -void ProcessPlatformMouseMoveEvent(float x, float y); -void ProcessPlatformMouseButtonEvent(s32 button, bool pressed); -void ProcessPlatformMouseWheelEvent(float x, float y); -void ProcessPlatformKeyEvent(s32 key, bool pressed); -void ProcessPlatformTextEvent(const char* text); -void PlatformWindowFocusGained(); -void PlatformWindowFocusLost(); -void PlatformDevicesChanged(); -bool GetSavedPlatformWindowGeometry(s32* x, s32* y, s32* width, s32* height); -void SavePlatformWindowGeometry(s32 x, s32 y, s32 width, s32 height); -} // namespace NoGUIHost diff --git a/src/duckstation-nogui/nogui_platform.h b/src/duckstation-nogui/nogui_platform.h deleted file mode 100644 index 452e545e5..000000000 --- a/src/duckstation-nogui/nogui_platform.h +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#pragma once - -#include "util/gpu_device.h" - -#include "common/types.h" - -#include -#include -#include -#include -#include - -class SettingsInterface; - -class NoGUIPlatform -{ -public: - virtual ~NoGUIPlatform() = default; - - virtual void ReportError(const std::string_view& title, const std::string_view& message) = 0; - virtual bool ConfirmMessage(const std::string_view& title, const std::string_view& message) = 0; - - virtual void SetDefaultConfig(SettingsInterface& si) = 0; - - virtual bool CreatePlatformWindow(std::string title) = 0; - virtual bool HasPlatformWindow() const = 0; - virtual void DestroyPlatformWindow() = 0; - - virtual std::optional GetPlatformWindowInfo() = 0; - virtual void SetPlatformWindowTitle(std::string title) = 0; - - virtual std::optional ConvertHostKeyboardStringToCode(const std::string_view& str) = 0; - virtual std::optional ConvertHostKeyboardCodeToString(u32 code) = 0; - - virtual void RunMessageLoop() = 0; - virtual void ExecuteInMessageLoop(std::function func) = 0; - virtual void QuitMessageLoop() = 0; - - virtual void SetFullscreen(bool enabled) = 0; - - virtual bool RequestRenderWindowSize(s32 new_window_width, s32 new_window_height) = 0; - - virtual bool OpenURL(const std::string_view& url) = 0; - virtual bool CopyTextToClipboard(const std::string_view& text) = 0; - -#ifdef _WIN32 - static std::unique_ptr CreateWin32Platform(); -#endif -#ifdef __APPLE__ - static std::unique_ptr CreateCocoaPlatform(); -#endif -#ifdef ENABLE_SDL2 - static std::unique_ptr CreateSDLPlatform(); -#endif -#ifdef NOGUI_PLATFORM_WAYLAND - static std::unique_ptr CreateWaylandPlatform(); -#endif -#ifdef NOGUI_PLATFORM_X11 - static std::unique_ptr CreateX11Platform(); -#endif - -protected: - static constexpr s32 DEFAULT_WINDOW_WIDTH = 1280; - static constexpr s32 DEFAULT_WINDOW_HEIGHT = 720; -}; - -extern std::unique_ptr g_nogui_window; diff --git a/src/duckstation-nogui/pch.cpp b/src/duckstation-nogui/pch.cpp deleted file mode 100644 index ef4256aee..000000000 --- a/src/duckstation-nogui/pch.cpp +++ /dev/null @@ -1,4 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#include "pch.h" diff --git a/src/duckstation-nogui/pch.h b/src/duckstation-nogui/pch.h deleted file mode 100644 index 3267189f4..000000000 --- a/src/duckstation-nogui/pch.h +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#pragma once - -#include "core/pch.h" diff --git a/src/duckstation-nogui/resource.h b/src/duckstation-nogui/resource.h deleted file mode 100644 index 966189314..000000000 --- a/src/duckstation-nogui/resource.h +++ /dev/null @@ -1,16 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by duckstation-sdl.rc -// -#define IDI_ICON1 102 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 103 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/src/duckstation-nogui/sdl_key_names.h b/src/duckstation-nogui/sdl_key_names.h deleted file mode 100644 index efc587ea7..000000000 --- a/src/duckstation-nogui/sdl_key_names.h +++ /dev/null @@ -1,274 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#pragma once - -#include "common/types.h" -#include "common/windows_headers.h" - -#include -#include -#include -#include -#include - -namespace SDLKeyNames { - -static const std::map s_sdl_key_names = {{SDLK_RETURN, "Return"}, - {SDLK_ESCAPE, "Escape"}, - {SDLK_BACKSPACE, "Backspace"}, - {SDLK_TAB, "Tab"}, - {SDLK_SPACE, "Space"}, - {SDLK_EXCLAIM, "Exclam"}, - {SDLK_QUOTEDBL, "QuoteDbl"}, - {SDLK_HASH, "Hash"}, - {SDLK_PERCENT, "Percent"}, - {SDLK_DOLLAR, "Dollar"}, - {SDLK_AMPERSAND, "Ampersand"}, - {SDLK_QUOTE, "Apostrophe"}, - {SDLK_LEFTPAREN, "ParenLeft"}, - {SDLK_RIGHTPAREN, "ParenRight"}, - {SDLK_ASTERISK, "Asterisk"}, - {SDLK_PLUS, "PLus"}, - {SDLK_COMMA, "Comma"}, - {SDLK_MINUS, "Minus"}, - {SDLK_PERIOD, "Period"}, - {SDLK_SLASH, "Slash"}, - {SDLK_0, "0"}, - {SDLK_1, "1"}, - {SDLK_2, "2"}, - {SDLK_3, "3"}, - {SDLK_4, "4"}, - {SDLK_5, "5"}, - {SDLK_6, "6"}, - {SDLK_7, "7"}, - {SDLK_8, "8"}, - {SDLK_9, "9"}, - {SDLK_COLON, "Colon"}, - {SDLK_SEMICOLON, "Semcolon"}, - {SDLK_LESS, "Less"}, - {SDLK_EQUALS, "Equal"}, - {SDLK_GREATER, "Greater"}, - {SDLK_QUESTION, "Question"}, - {SDLK_AT, "AT"}, - {SDLK_LEFTBRACKET, "BracketLeft"}, - {SDLK_BACKSLASH, "Backslash"}, - {SDLK_RIGHTBRACKET, "BracketRight"}, - {SDLK_CARET, "Caret"}, - {SDLK_UNDERSCORE, "Underscore"}, - {SDLK_BACKQUOTE, "Backquote"}, - {SDLK_a, "A"}, - {SDLK_b, "B"}, - {SDLK_c, "C"}, - {SDLK_d, "D"}, - {SDLK_e, "E"}, - {SDLK_f, "F"}, - {SDLK_g, "G"}, - {SDLK_h, "H"}, - {SDLK_i, "I"}, - {SDLK_j, "J"}, - {SDLK_k, "K"}, - {SDLK_l, "L"}, - {SDLK_m, "M"}, - {SDLK_n, "N"}, - {SDLK_o, "O"}, - {SDLK_p, "P"}, - {SDLK_q, "Q"}, - {SDLK_r, "R"}, - {SDLK_s, "S"}, - {SDLK_t, "T"}, - {SDLK_u, "U"}, - {SDLK_v, "V"}, - {SDLK_w, "W"}, - {SDLK_x, "X"}, - {SDLK_y, "Y"}, - {SDLK_z, "Z"}, - {SDLK_CAPSLOCK, "CapsLock"}, - {SDLK_F1, "F1"}, - {SDLK_F2, "F2"}, - {SDLK_F3, "F3"}, - {SDLK_F4, "F4"}, - {SDLK_F5, "F5"}, - {SDLK_F6, "F6"}, - {SDLK_F7, "F7"}, - {SDLK_F8, "F8"}, - {SDLK_F9, "F9"}, - {SDLK_F10, "F10"}, - {SDLK_F11, "F11"}, - {SDLK_F12, "F12"}, - {SDLK_PRINTSCREEN, "Print"}, - {SDLK_SCROLLLOCK, "ScrollLock"}, - {SDLK_PAUSE, "Pause"}, - {SDLK_INSERT, "Insert"}, - {SDLK_HOME, "Home"}, - {SDLK_PAGEUP, "PageUp"}, - {SDLK_DELETE, "Delete"}, - {SDLK_END, "End"}, - {SDLK_PAGEDOWN, "PageDown"}, - {SDLK_RIGHT, "Right"}, - {SDLK_LEFT, "Left"}, - {SDLK_DOWN, "Down"}, - {SDLK_UP, "Up"}, - {SDLK_NUMLOCKCLEAR, "NumLock"}, - {SDLK_KP_DIVIDE, "Keypad+Divide"}, - {SDLK_KP_MULTIPLY, "Keypad+Multiply"}, - {SDLK_KP_MINUS, "Keypad+Minus"}, - {SDLK_KP_PLUS, "Keypad+Plus"}, - {SDLK_KP_ENTER, "Keypad+Return"}, - {SDLK_KP_1, "Keypad+1"}, - {SDLK_KP_2, "Keypad+2"}, - {SDLK_KP_3, "Keypad+3"}, - {SDLK_KP_4, "Keypad+4"}, - {SDLK_KP_5, "Keypad+5"}, - {SDLK_KP_6, "Keypad+6"}, - {SDLK_KP_7, "Keypad+7"}, - {SDLK_KP_8, "Keypad+8"}, - {SDLK_KP_9, "Keypad+9"}, - {SDLK_KP_0, "Keypad+0"}, - {SDLK_KP_PERIOD, "Keypad+Period"}, - {SDLK_APPLICATION, "Application"}, - {SDLK_POWER, "Power"}, - {SDLK_KP_EQUALS, "Keypad+Equal"}, - {SDLK_F13, "F13"}, - {SDLK_F14, "F14"}, - {SDLK_F15, "F15"}, - {SDLK_F16, "F16"}, - {SDLK_F17, "F17"}, - {SDLK_F18, "F18"}, - {SDLK_F19, "F19"}, - {SDLK_F20, "F20"}, - {SDLK_F21, "F21"}, - {SDLK_F22, "F22"}, - {SDLK_F23, "F23"}, - {SDLK_F24, "F24"}, - {SDLK_EXECUTE, "Execute"}, - {SDLK_HELP, "Help"}, - {SDLK_MENU, "Menu"}, - {SDLK_SELECT, "Select"}, - {SDLK_STOP, "Stop"}, - {SDLK_AGAIN, "Again"}, - {SDLK_UNDO, "Undo"}, - {SDLK_CUT, "Cut"}, - {SDLK_COPY, "Copy"}, - {SDLK_PASTE, "Paste"}, - {SDLK_FIND, "Find"}, - {SDLK_MUTE, "Mute"}, - {SDLK_VOLUMEUP, "VolumeUp"}, - {SDLK_VOLUMEDOWN, "VolumeDown"}, - {SDLK_KP_COMMA, "Keypad+Comma"}, - {SDLK_KP_EQUALSAS400, "Keypad+EqualAS400"}, - {SDLK_ALTERASE, "AltErase"}, - {SDLK_SYSREQ, "SysReq"}, - {SDLK_CANCEL, "Cancel"}, - {SDLK_CLEAR, "Clear"}, - {SDLK_PRIOR, "Prior"}, - {SDLK_RETURN2, "Return2"}, - {SDLK_SEPARATOR, "Separator"}, - {SDLK_OUT, "Out"}, - {SDLK_OPER, "Oper"}, - {SDLK_CLEARAGAIN, "ClearAgain"}, - {SDLK_CRSEL, "CrSel"}, - {SDLK_EXSEL, "ExSel"}, - {SDLK_KP_00, "Keypad+00"}, - {SDLK_KP_000, "Keypad+000"}, - {SDLK_THOUSANDSSEPARATOR, "ThousandsSeparator"}, - {SDLK_DECIMALSEPARATOR, "DecimalSeparator"}, - {SDLK_CURRENCYUNIT, "CurrencyUnit"}, - {SDLK_CURRENCYSUBUNIT, "CurrencySubunit"}, - {SDLK_KP_LEFTPAREN, "Keypad+ParenLeft"}, - {SDLK_KP_RIGHTPAREN, "Keypad+ParenRight"}, - {SDLK_KP_LEFTBRACE, "Keypad+LeftBrace"}, - {SDLK_KP_RIGHTBRACE, "Keypad+RightBrace"}, - {SDLK_KP_TAB, "Keypad+Tab"}, - {SDLK_KP_BACKSPACE, "Keypad+Backspace"}, - {SDLK_KP_A, "Keypad+A"}, - {SDLK_KP_B, "Keypad+B"}, - {SDLK_KP_C, "Keypad+C"}, - {SDLK_KP_D, "Keypad+D"}, - {SDLK_KP_E, "Keypad+E"}, - {SDLK_KP_F, "Keypad+F"}, - {SDLK_KP_XOR, "Keypad+XOR"}, - {SDLK_KP_POWER, "Keypad+Power"}, - {SDLK_KP_PERCENT, "Keypad+Percent"}, - {SDLK_KP_LESS, "Keypad+Less"}, - {SDLK_KP_GREATER, "Keypad+Greater"}, - {SDLK_KP_AMPERSAND, "Keypad+Ampersand"}, - {SDLK_KP_DBLAMPERSAND, "Keypad+AmpersandDbl"}, - {SDLK_KP_VERTICALBAR, "Keypad+Bar"}, - {SDLK_KP_DBLVERTICALBAR, "Keypad+BarDbl"}, - {SDLK_KP_COLON, "Keypad+Colon"}, - {SDLK_KP_HASH, "Keypad+Hash"}, - {SDLK_KP_SPACE, "Keypad+Space"}, - {SDLK_KP_AT, "Keypad+At"}, - {SDLK_KP_EXCLAM, "Keypad+Exclam"}, - {SDLK_KP_MEMSTORE, "Keypad+MemStore"}, - {SDLK_KP_MEMRECALL, "Keypad+MemRecall"}, - {SDLK_KP_MEMCLEAR, "Keypad+MemClear"}, - {SDLK_KP_MEMADD, "Keypad+MemAdd"}, - {SDLK_KP_MEMSUBTRACT, "Keypad+MemSubtract"}, - {SDLK_KP_MEMMULTIPLY, "Keypad+MemMultiply"}, - {SDLK_KP_MEMDIVIDE, "Keypad+MemDivide"}, - {SDLK_KP_PLUSMINUS, "Keypad+PlusMinus"}, - {SDLK_KP_CLEAR, "Keypad+Clear"}, - {SDLK_KP_CLEARENTRY, "Keypad+ClearEntry"}, - {SDLK_KP_BINARY, "Keypad+Binary"}, - {SDLK_KP_OCTAL, "Keypad+Octal"}, - {SDLK_KP_DECIMAL, "Keypad+Decimal"}, - {SDLK_KP_HEXADECIMAL, "Keypad+Hexadecimal"}, - {SDLK_LCTRL, "LeftControl"}, - {SDLK_LSHIFT, "LeftShift"}, - {SDLK_LALT, "LeftAlt"}, - {SDLK_LGUI, "Super_L"}, - {SDLK_RCTRL, "RightCtrl"}, - {SDLK_RSHIFT, "RightShift"}, - {SDLK_RALT, "RightAlt"}, - {SDLK_RGUI, "RightSuper"}, - {SDLK_MODE, "Mode"}, - {SDLK_AUDIONEXT, "MediaNext"}, - {SDLK_AUDIOPREV, "MediaPrevious"}, - {SDLK_AUDIOSTOP, "MediaStop"}, - {SDLK_AUDIOPLAY, "MediaPlay"}, - {SDLK_AUDIOMUTE, "VolumeMute"}, - {SDLK_MEDIASELECT, "MediaSelect"}, - {SDLK_WWW, "WWW"}, - {SDLK_MAIL, "Mail"}, - {SDLK_CALCULATOR, "Calculator"}, - {SDLK_COMPUTER, "Computer"}, - {SDLK_AC_SEARCH, "Search"}, - {SDLK_AC_HOME, "Home"}, - {SDLK_AC_BACK, "Back"}, - {SDLK_AC_FORWARD, "Forward"}, - {SDLK_AC_STOP, "Stop"}, - {SDLK_AC_REFRESH, "Refresh"}, - {SDLK_AC_BOOKMARKS, "Bookmarks"}, - {SDLK_BRIGHTNESSDOWN, "BrightnessDown"}, - {SDLK_BRIGHTNESSUP, "BrightnessUp"}, - {SDLK_DISPLAYSWITCH, "DisplaySwitch"}, - {SDLK_KBDILLUMTOGGLE, "IllumToggle"}, - {SDLK_KBDILLUMDOWN, "IllumDown"}, - {SDLK_KBDILLUMUP, "IllumUp"}, - {SDLK_EJECT, "Eject"}, - {SDLK_SLEEP, "Sleep"}, - {SDLK_APP1, "App1"}, - {SDLK_APP2, "App2"}, - {SDLK_AUDIOREWIND, "MediaRewind"}, - {SDLK_AUDIOFASTFORWARD, "MediaFastForward"}}; - -static const char* GetKeyName(DWORD key) -{ - const auto it = s_sdl_key_names.find(key); - return it == s_sdl_key_names.end() ? nullptr : it->second; -} - -static std::optional GetKeyCodeForName(const std::string_view& key_name) -{ - for (const auto& it : s_sdl_key_names) - { - if (key_name == it.second) - return it.first; - } - - return std::nullopt; -} - -} // namespace SDLKeyNames diff --git a/src/duckstation-nogui/sdl_nogui_platform.cpp b/src/duckstation-nogui/sdl_nogui_platform.cpp deleted file mode 100644 index 3afb1e5b2..000000000 --- a/src/duckstation-nogui/sdl_nogui_platform.cpp +++ /dev/null @@ -1,449 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#include "sdl_nogui_platform.h" -#include "nogui_host.h" -#include "sdl_key_names.h" - -#include "core/host.h" - -#include "util/imgui_manager.h" -#include "util/sdl_input_source.h" - -#include "common/log.h" -#include "common/scoped_guard.h" -#include "common/string_util.h" -#include "common/threading.h" - -#include -#include - -Log_SetChannel(SDLNoGUIPlatform); - -static constexpr float DEFAULT_WINDOW_DPI = 96.0f; - -SDLNoGUIPlatform::SDLNoGUIPlatform() -{ - m_message_loop_running.store(true, std::memory_order_release); -} - -SDLNoGUIPlatform::~SDLNoGUIPlatform() -{ - SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_EVENTS); -} - -bool SDLNoGUIPlatform::Initialize() -{ - if (SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) - { - Log_ErrorFmt("SDL_InitSubSystem() failed: {}", SDL_GetError()); - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", - TinyString::from_format("SDL_InitSubSystem() failed: {}", SDL_GetError()), nullptr); - return false; - } - - m_func_event_id = SDL_RegisterEvents(1); - m_wakeup_event_id = SDL_RegisterEvents(1); - if (m_func_event_id == static_cast(-1) || m_wakeup_event_id == static_cast(-1)) - { - Log_ErrorFmt("SDL_RegisterEvents() failed: {}", SDL_GetError()); - return false; - } - - // prevent input source polling on main thread... - SDLInputSource::ALLOW_EVENT_POLLING = false; - - return true; -} - -void SDLNoGUIPlatform::ReportError(const std::string_view& title, const std::string_view& message) -{ - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, SmallString(title).c_str(), SmallString(message).c_str(), m_window); -} - -bool SDLNoGUIPlatform::ConfirmMessage(const std::string_view& title, const std::string_view& message) -{ - const SmallString title_copy(title); - const SmallString message_copy(message); - - static constexpr SDL_MessageBoxButtonData bd[2] = { - {SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 1, "Yes"}, - {SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 2, "No"}, - }; - const SDL_MessageBoxData md = {SDL_MESSAGEBOX_INFORMATION, - m_window, - title_copy.c_str(), - message_copy.c_str(), - static_cast(std::size(bd)), - bd, - nullptr}; - - int buttonid = -1; - SDL_ShowMessageBox(&md, &buttonid); - return (buttonid == 1); -} - -void SDLNoGUIPlatform::SetDefaultConfig(SettingsInterface& si) -{ - // noop -} - -bool SDLNoGUIPlatform::CreatePlatformWindow(std::string title) -{ - s32 window_x, window_y, window_width, window_height; - if (!NoGUIHost::GetSavedPlatformWindowGeometry(&window_x, &window_y, &window_width, &window_height)) - { - window_x = SDL_WINDOWPOS_UNDEFINED; - window_y = SDL_WINDOWPOS_UNDEFINED; - window_width = DEFAULT_WINDOW_WIDTH; - window_height = DEFAULT_WINDOW_HEIGHT; - } - - m_window = SDL_CreateWindow(title.c_str(), window_x, window_y, window_width, window_height, - SDL_WINDOW_RESIZABLE | SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS | - SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI); - if (!m_window) - { - Log_ErrorFmt("SDL_CreateWindow() failed: {}", SDL_GetError()); - ReportError("Error", TinyString::from_format("SDL_CreateWindow() failed: {}", SDL_GetError())); - return false; - } - - if (m_fullscreen.load(std::memory_order_acquire)) - SetFullscreen(true); - - return true; -} - -bool SDLNoGUIPlatform::HasPlatformWindow() const -{ - return (m_window != nullptr); -} - -void SDLNoGUIPlatform::DestroyPlatformWindow() -{ - if (!m_window) - return; - - if (!m_fullscreen.load(std::memory_order_acquire)) - { - int window_x = SDL_WINDOWPOS_UNDEFINED, window_y = SDL_WINDOWPOS_UNDEFINED; - int window_width = DEFAULT_WINDOW_WIDTH, window_height = DEFAULT_WINDOW_HEIGHT; - SDL_GetWindowPosition(m_window, &window_x, &window_y); - SDL_GetWindowSize(m_window, &window_width, &window_height); - NoGUIHost::SavePlatformWindowGeometry(window_x, window_y, window_width, window_height); - } - - SDL_DestroyWindow(m_window); - m_window = nullptr; -} - -std::optional SDLNoGUIPlatform::GetPlatformWindowInfo() -{ - if (!m_window) - return std::nullopt; - - SDL_SysWMinfo swi = {}; - SDL_VERSION(&swi.version); - - if (!SDL_GetWindowWMInfo(m_window, &swi)) - { - Log_ErrorFmt("SDL_GetWindowWMInfo() failed: {}", SDL_GetError()); - return std::nullopt; - } - - int window_width = 1, window_height = 1; - int window_px_width = 1, window_px_height = 1; - SDL_GetWindowSize(m_window, &window_width, &window_height); - SDL_GetWindowSizeInPixels(m_window, &window_px_width, &window_px_height); - m_window_scale = static_cast(std::max(window_px_width, 1)) / static_cast(std::max(window_width, 1)); - - if (const int display_index = SDL_GetWindowDisplayIndex(m_window); display_index >= 0) - { - float ddpi, hdpi, vdpi; - if (SDL_GetDisplayDPI(display_index, &ddpi, &hdpi, &vdpi) == 0) - m_window_scale = std::max(ddpi / DEFAULT_WINDOW_DPI, 0.5f); - } - - WindowInfo wi; - wi.surface_width = static_cast(window_px_width); - wi.surface_height = static_cast(window_px_height); - wi.surface_scale = m_window_scale; - - switch (swi.subsystem) - { -#ifdef SDL_VIDEO_DRIVER_WINDOWS - case SDL_SYSWM_WINDOWS: - wi.type = WindowInfo::Type::Win32; - wi.window_handle = swi.info.win.window; - break; -#endif - -#ifdef SDL_VIDEO_DRIVER_X11 - case SDL_SYSWM_X11: - wi.type = WindowInfo::Type::X11; - wi.display_connection = swi.info.x11.display; - wi.window_handle = swi.info.x11.window; - break; -#endif - -#ifdef SDL_VIDEO_DRIVER_WAYLAND - case SDL_SYSWM_WAYLAND: - wi.type = WindowInfo::Type::Wayland; - wi.display_connection = swi.info.wl.display; - wi.window_handle = swi.info.wl.surface; - break; -#endif - -#ifdef SDL_VIDEO_DRIVER_COCOA - case SDL_SYSWM_COCOA: - wi.type = WindowInfo::Type::MacOS; - wi.window_handle = swi.info.cocoa.window; - break; -#endif - - default: - Log_ErrorFmt("Unhandled WM subsystem {}", static_cast(swi.subsystem)); - return std::nullopt; - } - - return wi; -} - -void SDLNoGUIPlatform::SetPlatformWindowTitle(std::string title) -{ - if (!m_window) - return; - - SDL_SetWindowTitle(m_window, title.c_str()); -} - -std::optional SDLNoGUIPlatform::ConvertHostKeyboardStringToCode(const std::string_view& str) -{ - std::optional converted(SDLKeyNames::GetKeyCodeForName(str)); - return converted.has_value() ? std::optional(static_cast(converted.value())) : std::nullopt; - return std::nullopt; -} - -std::optional SDLNoGUIPlatform::ConvertHostKeyboardCodeToString(u32 code) -{ - const char* converted = SDLKeyNames::GetKeyName(code); - return converted ? std::optional(converted) : std::nullopt; -} - -void SDLNoGUIPlatform::RunMessageLoop() -{ - while (m_message_loop_running.load(std::memory_order_acquire)) - { - SDL_Event ev; - if (!SDL_WaitEvent(&ev)) - continue; - - ProcessEvent(&ev); - } -} - -void SDLNoGUIPlatform::ExecuteInMessageLoop(std::function func) -{ - std::function* pfunc = new std::function(std::move(func)); - - SDL_Event ev; - ev.user = {}; - ev.type = m_func_event_id; - ev.user.data1 = pfunc; - SDL_PushEvent(&ev); -} - -void SDLNoGUIPlatform::QuitMessageLoop() -{ - m_message_loop_running.store(false, std::memory_order_release); - - SDL_Event ev; - ev.user = {}; - ev.type = m_wakeup_event_id; - SDL_PushEvent(&ev); -} - -void SDLNoGUIPlatform::SetFullscreen(bool enabled) -{ - if (!m_window || m_fullscreen.load(std::memory_order_acquire) == enabled) - return; - - if (SDL_SetWindowFullscreen(m_window, enabled ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0) != 0) - { - Log_ErrorFmt("SDL_SetWindowFullscreen() failed: {}", SDL_GetError()); - return; - } - - m_fullscreen.store(enabled, std::memory_order_release); -} - -bool SDLNoGUIPlatform::RequestRenderWindowSize(s32 new_window_width, s32 new_window_height) -{ - if (!m_window || m_fullscreen.load(std::memory_order_acquire)) - return false; - - SDL_SetWindowSize(m_window, new_window_width, new_window_height); - return true; -} - -bool SDLNoGUIPlatform::OpenURL(const std::string_view& url) -{ - if (SDL_OpenURL(SmallString(url).c_str()) != 0) - { - Log_ErrorFmt("SDL_OpenURL() failed: {}", SDL_GetError()); - return false; - } - - return true; -} - -bool SDLNoGUIPlatform::CopyTextToClipboard(const std::string_view& text) -{ - if (SDL_SetClipboardText(SmallString(text).c_str()) != 0) - { - Log_ErrorFmt("SDL_SetClipboardText() failed: {}", SDL_GetError()); - return false; - } - - return true; -} - -void SDLNoGUIPlatform::ProcessEvent(const SDL_Event* ev) -{ - switch (ev->type) - { - case SDL_WINDOWEVENT: - { - switch (ev->window.event) - { - case SDL_WINDOWEVENT_SIZE_CHANGED: - { - int window_width = ev->window.data1, window_height = ev->window.data2; - SDL_GetWindowSizeInPixels(m_window, &window_width, &window_height); - NoGUIHost::ProcessPlatformWindowResize(window_width, window_height, m_window_scale); - } - break; - - case SDL_WINDOWEVENT_DISPLAY_CHANGED: - { - const int new_display = ev->window.data1; - float ddpi, hdpi, vdpi; - if (SDL_GetDisplayDPI(new_display, &ddpi, &hdpi, &vdpi) == 0) - { - if (const float new_scale = std::max(ddpi / DEFAULT_WINDOW_DPI, 0.5f); new_scale != m_window_scale) - { - m_window_scale = new_scale; - - int window_width = 1, window_height = 1; - SDL_GetWindowSizeInPixels(m_window, &window_width, &window_height); - NoGUIHost::ProcessPlatformWindowResize(window_width, window_height, m_window_scale); - } - } - } - break; - - case SDL_WINDOWEVENT_CLOSE: - { - Host::RunOnCPUThread([]() { Host::RequestExit(false); }); - } - break; - - case SDL_WINDOWEVENT_FOCUS_GAINED: - { - NoGUIHost::PlatformWindowFocusGained(); - } - break; - - case SDL_WINDOWEVENT_FOCUS_LOST: - { - NoGUIHost::PlatformWindowFocusLost(); - } - break; - - default: - break; - } - } - break; - - case SDL_KEYDOWN: - case SDL_KEYUP: - { - const bool pressed = (ev->type == SDL_KEYDOWN); - NoGUIHost::ProcessPlatformKeyEvent(static_cast(ev->key.keysym.sym), pressed); - } - break; - - case SDL_TEXTINPUT: - { - if (ImGuiManager::WantsTextInput()) - NoGUIHost::ProcessPlatformTextEvent(ev->text.text); - } - break; - - case SDL_MOUSEMOTION: - { - const float x = static_cast(ev->motion.x); - const float y = static_cast(ev->motion.y); - NoGUIHost::ProcessPlatformMouseMoveEvent(x, y); - } - break; - - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: - { - const bool pressed = (ev->type == SDL_MOUSEBUTTONDOWN); - if (ev->button.button > 0) - NoGUIHost::ProcessPlatformMouseButtonEvent(ev->button.button - 1, pressed); - } - break; - - case SDL_MOUSEWHEEL: - { - NoGUIHost::ProcessPlatformMouseWheelEvent(ev->wheel.preciseX, ev->wheel.preciseY); - } - break; - - case SDL_QUIT: - { - Host::RunOnCPUThread([]() { Host::RequestExit(false); }); - } - break; - - default: - { - if (ev->type == m_func_event_id) - { - std::function* pfunc = reinterpret_cast*>(ev->user.data1); - if (pfunc) - { - (*pfunc)(); - delete pfunc; - } - } - else if (ev->type == m_wakeup_event_id) - { - } - else if (SDLInputSource::IsHandledInputEvent(ev) && InputManager::GetInputSourceInterface(InputSourceType::SDL)) - { - Host::RunOnCPUThread([event_copy = *ev]() { - SDLInputSource* is = - static_cast(InputManager::GetInputSourceInterface(InputSourceType::SDL)); - if (is) [[likely]] - is->ProcessSDLEvent(&event_copy); - }); - } - } - break; - } -} - -std::unique_ptr NoGUIPlatform::CreateSDLPlatform() -{ - std::unique_ptr ret(new SDLNoGUIPlatform()); - if (!ret->Initialize()) - return {}; - - return ret; -} diff --git a/src/duckstation-nogui/sdl_nogui_platform.h b/src/duckstation-nogui/sdl_nogui_platform.h deleted file mode 100644 index ea8a1b83f..000000000 --- a/src/duckstation-nogui/sdl_nogui_platform.h +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#pragma once - -#include - -#include "common/windows_headers.h" - -#include "nogui_platform.h" - -#include - -class SDLNoGUIPlatform : public NoGUIPlatform -{ -public: - SDLNoGUIPlatform(); - ~SDLNoGUIPlatform(); - - bool Initialize(); - - void ReportError(const std::string_view& title, const std::string_view& message) override; - bool ConfirmMessage(const std::string_view& title, const std::string_view& message) override; - - void SetDefaultConfig(SettingsInterface& si) override; - - bool CreatePlatformWindow(std::string title) override; - bool HasPlatformWindow() const override; - void DestroyPlatformWindow() override; - std::optional GetPlatformWindowInfo() override; - void SetPlatformWindowTitle(std::string title) override; - - std::optional ConvertHostKeyboardStringToCode(const std::string_view& str) override; - std::optional ConvertHostKeyboardCodeToString(u32 code) override; - - void RunMessageLoop() override; - void ExecuteInMessageLoop(std::function func) override; - void QuitMessageLoop() override; - - void SetFullscreen(bool enabled) override; - - bool RequestRenderWindowSize(s32 new_window_width, s32 new_window_height) override; - - bool OpenURL(const std::string_view& url) override; - bool CopyTextToClipboard(const std::string_view& text) override; - -private: - void ProcessEvent(const SDL_Event* ev); - - SDL_Window* m_window = nullptr; - float m_window_scale = 1.0f; - u32 m_func_event_id = 0; - u32 m_wakeup_event_id = 0; - - std::atomic_bool m_message_loop_running{false}; - std::atomic_bool m_fullscreen{false}; -}; \ No newline at end of file diff --git a/src/duckstation-nogui/wayland_nogui_platform.cpp b/src/duckstation-nogui/wayland_nogui_platform.cpp deleted file mode 100644 index 3c1e26d05..000000000 --- a/src/duckstation-nogui/wayland_nogui_platform.cpp +++ /dev/null @@ -1,483 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#include "wayland_nogui_platform.h" -#include "common/assert.h" -#include "common/log.h" -#include "common/string_util.h" -#include "common/threading.h" -#include "core/host.h" -#include "nogui_host.h" -#include "nogui_platform.h" - -#include -#include -#include -#include - -Log_SetChannel(WaylandNoGUIPlatform); - -WaylandNoGUIPlatform::WaylandNoGUIPlatform() -{ - m_message_loop_running.store(true, std::memory_order_release); -} - -WaylandNoGUIPlatform::~WaylandNoGUIPlatform() -{ - if (m_xkb_state) - xkb_state_unref(m_xkb_state); - if (m_xkb_keymap) - xkb_keymap_unref(m_xkb_keymap); - if (m_wl_keyboard) - wl_keyboard_destroy(m_wl_keyboard); - if (m_wl_pointer) - wl_pointer_destroy(m_wl_pointer); - if (m_wl_seat) - wl_seat_destroy(m_wl_seat); - if (m_xkb_context) - xkb_context_unref(m_xkb_context); - if (m_registry) - wl_registry_destroy(m_registry); -} - -bool WaylandNoGUIPlatform::Initialize() -{ - m_xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - if (!m_xkb_context) - { - Panic("Failed to create XKB context"); - return false; - } - - m_display = wl_display_connect(nullptr); - if (!m_display) - { - Panic("Failed to connect to Wayland display."); - return false; - } - - static const wl_registry_listener registry_listener = {GlobalRegistryHandler, GlobalRegistryRemover}; - m_registry = wl_display_get_registry(m_display); - wl_registry_add_listener(m_registry, ®istry_listener, this); - - // Call back to registry listener to get compositor/shell. - wl_display_dispatch_pending(m_display); - wl_display_roundtrip(m_display); - - // We need a shell/compositor, or at least one we understand. - if (!m_compositor || !m_xdg_wm_base) - { - Panic("Missing Wayland shell/compositor\n"); - return false; - } - - static const xdg_wm_base_listener xdg_wm_base_listener = {XDGWMBasePing}; - xdg_wm_base_add_listener(m_xdg_wm_base, &xdg_wm_base_listener, this); - wl_display_dispatch_pending(m_display); - wl_display_roundtrip(m_display); - return true; -} - -void WaylandNoGUIPlatform::ReportError(const std::string_view& title, const std::string_view& message) -{ - // not implemented -} - -bool WaylandNoGUIPlatform::ConfirmMessage(const std::string_view& title, const std::string_view& message) -{ - // not implemented - return true; -} - -void WaylandNoGUIPlatform::SetDefaultConfig(SettingsInterface& si) {} - -bool WaylandNoGUIPlatform::CreatePlatformWindow(std::string title) -{ - s32 window_x, window_y, window_width, window_height; - bool has_window_pos = NoGUIHost::GetSavedPlatformWindowGeometry(&window_x, &window_y, &window_width, &window_height); - if (!has_window_pos) - { - window_x = 0; - window_y = 0; - window_width = DEFAULT_WINDOW_WIDTH; - window_height = DEFAULT_WINDOW_HEIGHT; - } - - // Create the compositor and shell surface. - if (!(m_surface = wl_compositor_create_surface(m_compositor)) || - !(m_xdg_surface = xdg_wm_base_get_xdg_surface(m_xdg_wm_base, m_surface)) || - !(m_xdg_toplevel = xdg_surface_get_toplevel(m_xdg_surface))) - { - Log_ErrorPrintf("Failed to create compositor/shell surfaces"); - return false; - } - - static const xdg_surface_listener shell_surface_listener = {XDGSurfaceConfigure}; - xdg_surface_add_listener(m_xdg_surface, &shell_surface_listener, this); - - static const xdg_toplevel_listener toplevel_listener = {TopLevelConfigure, TopLevelClose}; - xdg_toplevel_add_listener(m_xdg_toplevel, &toplevel_listener, this); - - // Create region in the surface to draw into. - m_region = wl_compositor_create_region(m_compositor); - wl_region_add(m_region, 0, 0, window_width, window_height); - wl_surface_set_opaque_region(m_surface, m_region); - wl_surface_commit(m_surface); - - // This doesn't seem to have any effect on kwin... - if (has_window_pos) - { - xdg_surface_set_window_geometry(m_xdg_surface, window_x, window_y, window_width, window_height); - } - - if (m_decoration_manager) - { - m_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(m_decoration_manager, m_xdg_toplevel); - if (m_toplevel_decoration) - zxdg_toplevel_decoration_v1_set_mode(m_toplevel_decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); - } - - m_window_info.surface_width = static_cast(window_width); - m_window_info.surface_height = static_cast(window_height); - m_window_info.surface_scale = 1.0f; - m_window_info.type = WindowInfo::Type::Wayland; - m_window_info.window_handle = m_surface; - m_window_info.display_connection = m_display; - - wl_display_dispatch_pending(m_display); - wl_display_roundtrip(m_display); - return true; -} - -bool WaylandNoGUIPlatform::HasPlatformWindow() const -{ - return (m_surface != nullptr); -} - -void WaylandNoGUIPlatform::DestroyPlatformWindow() -{ - m_window_info = {}; - - if (m_toplevel_decoration) - { - zxdg_toplevel_decoration_v1_destroy(m_toplevel_decoration); - m_toplevel_decoration = {}; - } - - if (m_xdg_toplevel) - { - xdg_toplevel_destroy(m_xdg_toplevel); - m_xdg_toplevel = {}; - } - - if (m_xdg_surface) - { - xdg_surface_destroy(m_xdg_surface); - m_xdg_surface = {}; - } - - if (m_surface) - { - wl_surface_destroy(m_surface); - m_surface = {}; - } - - wl_display_dispatch_pending(m_display); - wl_display_roundtrip(m_display); -} - -std::optional WaylandNoGUIPlatform::GetPlatformWindowInfo() -{ - if (m_window_info.type == WindowInfo::Type::Wayland) - return m_window_info; - else - return std::nullopt; -} - -void WaylandNoGUIPlatform::SetPlatformWindowTitle(std::string title) -{ - if (m_xdg_toplevel) - xdg_toplevel_set_title(m_xdg_toplevel, title.c_str()); -} - -std::optional WaylandNoGUIPlatform::ConvertHostKeyboardStringToCode(const std::string_view& str) -{ - std::unique_lock lock(m_key_map_mutex); - for (const auto& it : m_key_map) - { - if (StringUtil::Strncasecmp(it.second.c_str(), str.data(), str.length()) == 0) - return it.first; - } - - return std::nullopt; -} - -std::optional WaylandNoGUIPlatform::ConvertHostKeyboardCodeToString(u32 code) -{ - std::unique_lock lock(m_key_map_mutex); - const auto it = m_key_map.find(static_cast(code)); - return (it != m_key_map.end()) ? std::optional(it->second) : std::nullopt; -} - -void WaylandNoGUIPlatform::GlobalRegistryHandler(void* data, wl_registry* registry, uint32_t id, const char* interface, - uint32_t version) -{ - WaylandNoGUIPlatform* platform = static_cast(data); - if (std::strcmp(interface, wl_compositor_interface.name) == 0) - { - platform->m_compositor = - static_cast(wl_registry_bind(platform->m_registry, id, &wl_compositor_interface, 1)); - } - else if (std::strcmp(interface, xdg_wm_base_interface.name) == 0) - { - platform->m_xdg_wm_base = - static_cast(wl_registry_bind(platform->m_registry, id, &xdg_wm_base_interface, 1)); - } - else if (std::strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) - { - platform->m_decoration_manager = static_cast( - wl_registry_bind(platform->m_registry, id, &zxdg_decoration_manager_v1_interface, 1)); - } - else if (std::strcmp(interface, wl_seat_interface.name) == 0) - { - static const wl_seat_listener seat_listener = {&WaylandNoGUIPlatform::SeatCapabilities}; - platform->m_wl_seat = static_cast(wl_registry_bind(registry, id, &wl_seat_interface, 1)); - wl_seat_add_listener(platform->m_wl_seat, &seat_listener, platform); - } -} - -void WaylandNoGUIPlatform::GlobalRegistryRemover(void* data, wl_registry* registry, uint32_t id) {} - -void WaylandNoGUIPlatform::XDGWMBasePing(void* data, struct xdg_wm_base* xdg_wm_base, uint32_t serial) -{ - xdg_wm_base_pong(xdg_wm_base, serial); -} - -void WaylandNoGUIPlatform::XDGSurfaceConfigure(void* data, struct xdg_surface* xdg_surface, uint32_t serial) -{ - xdg_surface_ack_configure(xdg_surface, serial); -} - -void WaylandNoGUIPlatform::TopLevelConfigure(void* data, struct xdg_toplevel* xdg_toplevel, int32_t width, - int32_t height, struct wl_array* states) -{ - // If this is zero, it's asking us to set the size. - if (width == 0 || height == 0) - return; - - WaylandNoGUIPlatform* platform = static_cast(data); - platform->m_window_info.surface_width = width; - platform->m_window_info.surface_height = height; - - NoGUIHost::ProcessPlatformWindowResize(width, height, platform->m_window_info.surface_scale); -} - -void WaylandNoGUIPlatform::TopLevelClose(void* data, struct xdg_toplevel* xdg_toplevel) -{ - Host::RunOnCPUThread([]() { Host::RequestExit(false); }); -} - -void WaylandNoGUIPlatform::SeatCapabilities(void* data, wl_seat* seat, uint32_t capabilities) -{ - WaylandNoGUIPlatform* platform = static_cast(data); - if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) - { - static const wl_keyboard_listener keyboard_listener = { - &WaylandNoGUIPlatform::KeyboardKeymap, &WaylandNoGUIPlatform::KeyboardEnter, &WaylandNoGUIPlatform::KeyboardLeave, - &WaylandNoGUIPlatform::KeyboardKey, &WaylandNoGUIPlatform::KeyboardModifiers}; - platform->m_wl_keyboard = wl_seat_get_keyboard(seat); - wl_keyboard_add_listener(platform->m_wl_keyboard, &keyboard_listener, platform); - } - if (capabilities & WL_SEAT_CAPABILITY_POINTER) - { - static const wl_pointer_listener pointer_listener = { - &WaylandNoGUIPlatform::PointerEnter, &WaylandNoGUIPlatform::PointerLeave, &WaylandNoGUIPlatform::PointerMotion, - &WaylandNoGUIPlatform::PointerButton, &WaylandNoGUIPlatform::PointerAxis}; - platform->m_wl_pointer = wl_seat_get_pointer(seat); - wl_pointer_add_listener(platform->m_wl_pointer, &pointer_listener, platform); - } -} - -void WaylandNoGUIPlatform::KeyboardKeymap(void* data, wl_keyboard* keyboard, uint32_t format, int32_t fd, uint32_t size) -{ - WaylandNoGUIPlatform* platform = static_cast(data); - char* keymap_string = static_cast(mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0)); - if (platform->m_xkb_keymap) - xkb_keymap_unref(platform->m_xkb_keymap); - platform->m_xkb_keymap = xkb_keymap_new_from_string(platform->m_xkb_context, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, - XKB_KEYMAP_COMPILE_NO_FLAGS); - munmap(keymap_string, size); - close(fd); - - if (platform->m_xkb_state) - xkb_state_unref(platform->m_xkb_state); - platform->m_xkb_state = xkb_state_new(platform->m_xkb_keymap); - - platform->InitializeKeyMap(); -} - -void WaylandNoGUIPlatform::InitializeKeyMap() -{ - m_key_map.clear(); - Log_VerbosePrintf("Init keymap"); - - const xkb_keycode_t min_keycode = xkb_keymap_min_keycode(m_xkb_keymap); - const xkb_keycode_t max_keycode = xkb_keymap_max_keycode(m_xkb_keymap); - DebugAssert(max_keycode >= min_keycode); - - for (xkb_keycode_t keycode = min_keycode; keycode <= max_keycode; keycode++) - { - const xkb_layout_index_t num_layouts = xkb_keymap_num_layouts_for_key(m_xkb_keymap, keycode); - if (num_layouts == 0) - continue; - - // Take the first layout which we find a valid keysym for. - bool found_keysym = false; - for (xkb_layout_index_t layout = 0; layout < num_layouts && !found_keysym; layout++) - { - const xkb_level_index_t num_levels = xkb_keymap_num_levels_for_key(m_xkb_keymap, keycode, layout); - if (num_levels == 0) - continue; - - // Take the first level which we find a valid keysym for. - for (xkb_level_index_t level = 0; level < num_levels; level++) - { - const xkb_keysym_t* keysyms; - const int num_syms = xkb_keymap_key_get_syms_by_level(m_xkb_keymap, keycode, layout, level, &keysyms); - if (num_syms == 0) - continue; - - // Just take the first. Should only be one in most cases anyway. - const xkb_keysym_t keysym = xkb_keysym_to_upper(keysyms[0]); - - char keysym_name_buf[64]; - if (xkb_keysym_get_name(keysym, keysym_name_buf, sizeof(keysym_name_buf)) <= 0) - continue; - - m_key_map.emplace(static_cast(keycode), keysym_name_buf); - found_keysym = false; - break; - } - } - } -} - -void WaylandNoGUIPlatform::KeyboardEnter(void* data, wl_keyboard* keyboard, uint32_t serial, wl_surface* surface, - wl_array* keys) -{ -} - -void WaylandNoGUIPlatform::KeyboardLeave(void* data, wl_keyboard* keyboard, uint32_t serial, wl_surface* surface) {} - -void WaylandNoGUIPlatform::KeyboardKey(void* data, wl_keyboard* keyboard, uint32_t serial, uint32_t time, uint32_t key, - uint32_t state) -{ - const xkb_keycode_t keycode = static_cast(key + 8); - const bool pressed = (state == WL_KEYBOARD_KEY_STATE_PRESSED); - NoGUIHost::ProcessPlatformKeyEvent(static_cast(keycode), pressed); -} - -void WaylandNoGUIPlatform::KeyboardModifiers(void* data, wl_keyboard* keyboard, uint32_t serial, - uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, - uint32_t group) -{ - WaylandNoGUIPlatform* platform = static_cast(data); - xkb_state_update_mask(platform->m_xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); -} - -void WaylandNoGUIPlatform::PointerEnter(void* data, wl_pointer* pointer, uint32_t serial, wl_surface* surface, - wl_fixed_t surface_x, wl_fixed_t surface_y) -{ -} - -void WaylandNoGUIPlatform::PointerLeave(void* data, wl_pointer* pointer, uint32_t serial, wl_surface* surface) {} - -void WaylandNoGUIPlatform::PointerMotion(void* data, wl_pointer* pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y) -{ - const float pos_x = static_cast(wl_fixed_to_double(x)); - const float pos_y = static_cast(wl_fixed_to_double(y)); - NoGUIHost::ProcessPlatformMouseMoveEvent(static_cast(pos_x), static_cast(pos_y)); -} - -void WaylandNoGUIPlatform::PointerButton(void* data, wl_pointer* pointer, uint32_t serial, uint32_t time, - uint32_t button, uint32_t state) -{ - if (button < BTN_MOUSE || (button - BTN_MOUSE) >= 32) - return; - - const s32 button_index = (button - BTN_MOUSE); - const bool button_pressed = (state == WL_POINTER_BUTTON_STATE_PRESSED); - NoGUIHost::ProcessPlatformMouseButtonEvent(button_index, button_pressed); -} - -void WaylandNoGUIPlatform::PointerAxis(void* data, wl_pointer* pointer, uint32_t time, uint32_t axis, wl_fixed_t value) -{ - const float x = (axis == 1) ? std::clamp(static_cast(wl_fixed_to_double(value)), -1.0f, 1.0f) : 0.0f; - const float y = (axis == 0) ? std::clamp(static_cast(-wl_fixed_to_double(value)), -1.0f, 1.0f) : 0.0f; - NoGUIHost::ProcessPlatformMouseWheelEvent(x, y); -} - -void WaylandNoGUIPlatform::RunMessageLoop() -{ - while (m_message_loop_running.load(std::memory_order_acquire)) - { - wl_display_dispatch_pending(m_display); - - { - std::unique_lock lock(m_callback_queue_mutex); - while (!m_callback_queue.empty()) - { - std::function func = std::move(m_callback_queue.front()); - m_callback_queue.pop_front(); - lock.unlock(); - func(); - lock.lock(); - } - } - - // TODO: Make this suck less. - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } -} - -void WaylandNoGUIPlatform::ExecuteInMessageLoop(std::function func) -{ - std::unique_lock lock(m_callback_queue_mutex); - m_callback_queue.push_back(std::move(func)); -} - -void WaylandNoGUIPlatform::QuitMessageLoop() -{ - m_message_loop_running.store(false, std::memory_order_release); -} - -void WaylandNoGUIPlatform::SetFullscreen(bool enabled) -{ - // how the heck can we do this? -} - -bool WaylandNoGUIPlatform::RequestRenderWindowSize(s32 new_window_width, s32 new_window_height) -{ - return false; -} - -bool WaylandNoGUIPlatform::OpenURL(const std::string_view& url) -{ - Log_ErrorPrintf("WaylandNoGUIPlatform::OpenURL() not implemented: %.*s", static_cast(url.size()), url.data()); - return false; -} - -bool WaylandNoGUIPlatform::CopyTextToClipboard(const std::string_view& text) -{ - Log_ErrorPrintf("WaylandNoGUIPlatform::CopyTextToClipboard() not implemented: %.*s", static_cast(text.size()), text.data()); - return false; -} - -std::unique_ptr NoGUIPlatform::CreateWaylandPlatform() -{ - std::unique_ptr ret = std::unique_ptr(new WaylandNoGUIPlatform()); - if (!ret->Initialize()) - return {}; - - return ret; -} diff --git a/src/duckstation-nogui/wayland_nogui_platform.h b/src/duckstation-nogui/wayland_nogui_platform.h deleted file mode 100644 index 2423abee5..000000000 --- a/src/duckstation-nogui/wayland_nogui_platform.h +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#pragma once - -#include -#include -#include -#include - -#include "nogui_platform.h" - -#include "wayland-xdg-decoration-client-protocol.h" -#include "wayland-xdg-shell-client-protocol.h" -#include -#include - -class WaylandNoGUIPlatform : public NoGUIPlatform -{ -public: - WaylandNoGUIPlatform(); - ~WaylandNoGUIPlatform(); - - bool Initialize(); - - void ReportError(const std::string_view& title, const std::string_view& message) override; - bool ConfirmMessage(const std::string_view& title, const std::string_view& message) override; - - void SetDefaultConfig(SettingsInterface& si) override; - - bool CreatePlatformWindow(std::string title) override; - bool HasPlatformWindow() const override; - void DestroyPlatformWindow() override; - std::optional GetPlatformWindowInfo() override; - void SetPlatformWindowTitle(std::string title) override; - - std::optional ConvertHostKeyboardStringToCode(const std::string_view& str) override; - std::optional ConvertHostKeyboardCodeToString(u32 code) override; - - void RunMessageLoop() override; - void ExecuteInMessageLoop(std::function func) override; - void QuitMessageLoop() override; - - void SetFullscreen(bool enabled) override; - - bool RequestRenderWindowSize(s32 new_window_width, s32 new_window_height) override; - - bool OpenURL(const std::string_view& url) override; - bool CopyTextToClipboard(const std::string_view& text) override; - -private: - void InitializeKeyMap(); - - static void GlobalRegistryHandler(void* data, wl_registry* registry, uint32_t id, const char* interface, - uint32_t version); - static void GlobalRegistryRemover(void* data, wl_registry* registry, uint32_t id); - static void XDGWMBasePing(void* data, struct xdg_wm_base* xdg_wm_base, uint32_t serial); - static void XDGSurfaceConfigure(void* data, struct xdg_surface* xdg_surface, uint32_t serial); - static void TopLevelConfigure(void* data, struct xdg_toplevel* xdg_toplevel, int32_t width, int32_t height, - struct wl_array* states); - static void PointerEnter(void* data, wl_pointer* pointer, uint32_t serial, wl_surface* surface, wl_fixed_t surface_x, - wl_fixed_t surface_y); - static void PointerLeave(void* data, wl_pointer* pointer, uint32_t serial, wl_surface* surface); - static void PointerMotion(void* data, wl_pointer* pointer, uint32_t time, wl_fixed_t x, wl_fixed_t y); - static void PointerButton(void* data, wl_pointer* pointer, uint32_t serial, uint32_t time, uint32_t button, - uint32_t state); - static void PointerAxis(void* data, wl_pointer* pointer, uint32_t time, uint32_t axis, wl_fixed_t value); - static void KeyboardKeymap(void* data, wl_keyboard* keyboard, uint32_t format, int32_t fd, uint32_t size); - static void KeyboardEnter(void* data, wl_keyboard* keyboard, uint32_t serial, wl_surface* surface, wl_array* keys); - static void KeyboardLeave(void* data, wl_keyboard* keyboard, uint32_t serial, wl_surface* surface); - static void KeyboardKey(void* data, wl_keyboard* keyboard, uint32_t serial, uint32_t time, uint32_t key, - uint32_t state); - static void KeyboardModifiers(void* data, wl_keyboard* keyboard, uint32_t serial, uint32_t mods_depressed, - uint32_t mods_latched, uint32_t mods_locked, uint32_t group); - static void SeatCapabilities(void* data, wl_seat* seat, uint32_t capabilities); - static void TopLevelClose(void* data, struct xdg_toplevel* xdg_toplevel); - - std::atomic_bool m_message_loop_running{false}; - // std::atomic_bool m_fullscreen{false}; - - WindowInfo m_window_info = {}; - - wl_display* m_display = nullptr; - wl_registry* m_registry = nullptr; - wl_compositor* m_compositor = nullptr; - xdg_wm_base* m_xdg_wm_base = nullptr; - wl_surface* m_surface = nullptr; - wl_region* m_region = nullptr; - xdg_surface* m_xdg_surface = nullptr; - xdg_toplevel* m_xdg_toplevel = nullptr; - zxdg_decoration_manager_v1* m_decoration_manager = nullptr; - zxdg_toplevel_decoration_v1* m_toplevel_decoration = nullptr; - wl_seat* m_wl_seat = nullptr; - wl_keyboard* m_wl_keyboard = nullptr; - wl_pointer* m_wl_pointer = nullptr; - xkb_context* m_xkb_context = nullptr; - xkb_keymap* m_xkb_keymap = nullptr; - xkb_state* m_xkb_state = nullptr; - - std::unordered_map m_key_map; - std::mutex m_key_map_mutex; - - std::deque> m_callback_queue; - std::mutex m_callback_queue_mutex; -}; diff --git a/src/duckstation-nogui/win32_key_names.h b/src/duckstation-nogui/win32_key_names.h deleted file mode 100644 index ffe701158..000000000 --- a/src/duckstation-nogui/win32_key_names.h +++ /dev/null @@ -1,169 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#pragma once - -#include "common/types.h" -#include "common/windows_headers.h" - -#include -#include -#include -#include -#include - -namespace Win32KeyNames { -static const std::map s_win32_key_names = { - {VK_RETURN, "Return"}, - {VK_ESCAPE, "Escape"}, - {VK_BACK, "Backspace"}, - {VK_TAB, "Tab"}, - {VK_SPACE, "Space"}, - {0xDE, "Apostrophe"}, - {0xBC, "Comma"}, - {0xBD, "Minus"}, - {0xBE, "Period"}, - {0xBF, "Slash"}, - {'0', "0"}, - {'1', "1"}, - {'2', "2"}, - {'3', "3"}, - {'4', "4"}, - {'5', "5"}, - {'6', "6"}, - {'7', "7"}, - {'8', "8"}, - {'9', "9"}, - {0xBA, "Semcolon"}, - {0xBB, "Equal"}, - {0xDB, "BracketLeft"}, - {0xDC, "Backslash"}, - {0xDD, "BracketRight"}, - {0xC0, "QuoteLeft"}, - {'A', "A"}, - {'B', "B"}, - {'C', "C"}, - {'D', "D"}, - {'E', "E"}, - {'F', "F"}, - {'G', "G"}, - {'H', "H"}, - {'I', "I"}, - {'J', "J"}, - {'K', "K"}, - {'L', "L"}, - {'M', "M"}, - {'N', "N"}, - {'O', "O"}, - {'P', "P"}, - {'Q', "Q"}, - {'R', "R"}, - {'S', "S"}, - {'T', "T"}, - {'U', "U"}, - {'V', "V"}, - {'W', "W"}, - {'X', "X"}, - {'Y', "Y"}, - {'Z', "Z"}, - {VK_CAPITAL, "CapsLock"}, - {VK_F1, "F1"}, - {VK_F2, "F2"}, - {VK_F3, "F3"}, - {VK_F4, "F4"}, - {VK_F5, "F5"}, - {VK_F6, "F6"}, - {VK_F7, "F7"}, - {VK_F8, "F8"}, - {VK_F9, "F9"}, - {VK_F10, "F10"}, - {VK_F11, "F11"}, - {VK_F12, "F12"}, - {VK_PRINT, "Print"}, - {VK_SCROLL, "ScrollLock"}, - {VK_PAUSE, "Pause"}, - {VK_INSERT, "Insert"}, - {VK_HOME, "Home"}, - {VK_PRIOR, "PageUp"}, - {VK_DELETE, "Delete"}, - {VK_END, "End"}, - {VK_NEXT, "PageDown"}, - {VK_RIGHT, "Right"}, - {VK_LEFT, "Left"}, - {VK_DOWN, "Down"}, - {VK_UP, "Up"}, - {VK_NUMLOCK, "NumLock"}, - {VK_DIVIDE, "KeypadDivide"}, - {VK_MULTIPLY, "KeypadMultiply"}, - {VK_SUBTRACT, "KeypadMinus"}, - {VK_ADD, "KeypadPlus"}, - //{VK_KP_ENTER, "KeypadReturn"}, - {VK_NUMPAD1, "Keypad1"}, - {VK_NUMPAD2, "Keypad2"}, - {VK_NUMPAD3, "Keypad3"}, - {VK_NUMPAD4, "Keypad4"}, - {VK_NUMPAD5, "Keypad5"}, - {VK_NUMPAD6, "Keypad6"}, - {VK_NUMPAD7, "Keypad7"}, - {VK_NUMPAD8, "Keypad8"}, - {VK_NUMPAD9, "Keypad9"}, - {VK_NUMPAD0, "Keypad0"}, - {VK_SEPARATOR, "KeypadPeriod"}, - {VK_F13, "F13"}, - {VK_F14, "F14"}, - {VK_F15, "F15"}, - {VK_F16, "F16"}, - {VK_F17, "F17"}, - {VK_F18, "F18"}, - {VK_F19, "F19"}, - {VK_F20, "F20"}, - {VK_F21, "F21"}, - {VK_F22, "F22"}, - {VK_F23, "F23"}, - {VK_F24, "F24"}, - {VK_EXECUTE, "Execute"}, - {VK_HELP, "Help"}, - {VK_MENU, "Menu"}, - {VK_SELECT, "Select"}, - {VK_MEDIA_STOP, "Stop"}, - {VK_VOLUME_UP, "VolumeUp"}, - {VK_VOLUME_DOWN, "VolumeDown"}, - {VK_CANCEL, "Cancel"}, - {VK_CLEAR, "Clear"}, - {VK_PRIOR, "Prior"}, - {VK_SEPARATOR, "Separator"}, - {VK_CRSEL, "CrSel"}, - {VK_EXSEL, "ExSel"}, - {VK_LCONTROL, "LeftControl"}, - {VK_LSHIFT, "LeftShift"}, - {VK_LMENU, "LeftAlt"}, - {VK_LWIN, "Super_L"}, - {VK_RCONTROL, "RightCtrl"}, - {VK_RSHIFT, "RightShift"}, - {VK_RMENU, "RightAlt"}, - {VK_RWIN, "RightSuper"}, - {VK_MEDIA_NEXT_TRACK, "MediaNext"}, - {VK_MEDIA_PREV_TRACK, "MediaPrevious"}, - {VK_MEDIA_STOP, "MediaStop"}, - {VK_MEDIA_PLAY_PAUSE, "MediaPlay"}, - {VK_VOLUME_MUTE, "VolumeMute"}, - {VK_SLEEP, "Sleep"}, -}; - -static const char* GetKeyName(DWORD key) -{ - const auto it = s_win32_key_names.find(key); - return it == s_win32_key_names.end() ? nullptr : it->second; -} - -static std::optional GetKeyCodeForName(const std::string_view& key_name) -{ - for (const auto& it : s_win32_key_names) - { - if (key_name == it.second) - return it.first; - } - - return std::nullopt; -} -} // namespace Win32KeyNames \ No newline at end of file diff --git a/src/duckstation-nogui/win32_nogui_platform.cpp b/src/duckstation-nogui/win32_nogui_platform.cpp deleted file mode 100644 index b32da90ce..000000000 --- a/src/duckstation-nogui/win32_nogui_platform.cpp +++ /dev/null @@ -1,449 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#include "win32_nogui_platform.h" -#include "nogui_host.h" -#include "resource.h" -#include "win32_key_names.h" - -#include "core/host.h" - -#include "util/imgui_manager.h" - -#include "common/scoped_guard.h" -#include "common/string_util.h" -#include "common/threading.h" - -#include -#include -#include - -static constexpr LPCWSTR WINDOW_CLASS_NAME = L"DuckStationNoGUI"; -static constexpr DWORD WINDOWED_STYLE = WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_SIZEBOX; -static constexpr DWORD WINDOWED_EXSTYLE = WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE; -static constexpr DWORD FULLSCREEN_STYLE = WS_POPUP | WS_MINIMIZEBOX; - -static float GetWindowScale(HWND hwnd) -{ - static UINT(WINAPI * get_dpi_for_window)(HWND hwnd); - if (!get_dpi_for_window) - { - HMODULE mod = GetModuleHandleW(L"user32.dll"); - if (mod) - get_dpi_for_window = reinterpret_cast(GetProcAddress(mod, "GetDpiForWindow")); - } - if (!get_dpi_for_window) - return 1.0f; - - // less than 100% scaling seems unlikely. - const UINT dpi = hwnd ? get_dpi_for_window(hwnd) : 96; - return (dpi > 0) ? std::max(1.0f, static_cast(dpi) / 96.0f) : 1.0f; -} - -Win32NoGUIPlatform::Win32NoGUIPlatform() -{ - m_message_loop_running.store(true, std::memory_order_release); -} - -Win32NoGUIPlatform::~Win32NoGUIPlatform() -{ - UnregisterClassW(WINDOW_CLASS_NAME, GetModuleHandle(nullptr)); -} - -bool Win32NoGUIPlatform::Initialize() -{ - WNDCLASSEXW wc = {}; - wc.cbSize = sizeof(WNDCLASSEXW); - wc.style = 0; - wc.lpfnWndProc = WndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = GetModuleHandle(nullptr); - wc.hIcon = LoadIconA(wc.hInstance, (LPCSTR)IDI_ICON1); - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); - wc.lpszMenuName = NULL; - wc.lpszClassName = WINDOW_CLASS_NAME; - wc.hIconSm = LoadIconA(wc.hInstance, (LPCSTR)IDI_ICON1); - - if (!RegisterClassExW(&wc)) - { - MessageBoxW(nullptr, L"Window registration failed.", L"Error", MB_ICONERROR | MB_OK); - return false; - } - - m_window_thread_id = GetCurrentThreadId(); - return true; -} - -void Win32NoGUIPlatform::ReportError(const std::string_view& title, const std::string_view& message) -{ - const std::wstring title_copy(StringUtil::UTF8StringToWideString(title)); - const std::wstring message_copy(StringUtil::UTF8StringToWideString(message)); - - MessageBoxW(m_hwnd, message_copy.c_str(), title_copy.c_str(), MB_ICONERROR | MB_OK); -} - -bool Win32NoGUIPlatform::ConfirmMessage(const std::string_view& title, const std::string_view& message) -{ - const std::wstring title_copy(StringUtil::UTF8StringToWideString(title)); - const std::wstring message_copy(StringUtil::UTF8StringToWideString(message)); - - return (MessageBoxW(m_hwnd, message_copy.c_str(), title_copy.c_str(), MB_ICONQUESTION | MB_YESNO) == IDYES); -} - -void Win32NoGUIPlatform::SetDefaultConfig(SettingsInterface& si) -{ - // noop -} - -bool Win32NoGUIPlatform::CreatePlatformWindow(std::string title) -{ - s32 window_x, window_y, window_width, window_height; - if (!NoGUIHost::GetSavedPlatformWindowGeometry(&window_x, &window_y, &window_width, &window_height)) - { - window_x = CW_USEDEFAULT; - window_y = CW_USEDEFAULT; - window_width = DEFAULT_WINDOW_WIDTH; - window_height = DEFAULT_WINDOW_HEIGHT; - } - - HWND hwnd = CreateWindowExW(WS_EX_CLIENTEDGE, WINDOW_CLASS_NAME, StringUtil::UTF8StringToWideString(title).c_str(), - WINDOWED_STYLE, window_x, window_y, window_width, window_height, nullptr, nullptr, - GetModuleHandleW(nullptr), this); - if (!hwnd) - { - MessageBoxW(nullptr, L"CreateWindowEx failed.", L"Error", MB_ICONERROR | MB_OK); - return false; - } - - // deliberately not stored to m_hwnd yet, because otherwise the msg handlers will run - ShowWindow(hwnd, SW_SHOW); - UpdateWindow(hwnd); - m_hwnd = hwnd; - m_window_scale = GetWindowScale(m_hwnd); - m_last_mouse_buttons = 0; - - if (m_fullscreen.load(std::memory_order_acquire)) - SetFullscreen(true); - - // We use these notifications to detect when a controller is connected or disconnected. - DEV_BROADCAST_DEVICEINTERFACE_W filter = { - sizeof(DEV_BROADCAST_DEVICEINTERFACE_W), DBT_DEVTYP_DEVICEINTERFACE, 0, {}, {}}; - m_dev_notify_handle = - RegisterDeviceNotificationW(hwnd, &filter, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES); - - return true; -} - -bool Win32NoGUIPlatform::HasPlatformWindow() const -{ - return (m_hwnd != NULL); -} - -void Win32NoGUIPlatform::DestroyPlatformWindow() -{ - if (!m_hwnd) - return; - - if (m_dev_notify_handle) - { - UnregisterDeviceNotification(m_dev_notify_handle); - m_dev_notify_handle = NULL; - } - - RECT rc; - if (!m_fullscreen.load(std::memory_order_acquire) && GetWindowRect(m_hwnd, &rc)) - { - NoGUIHost::SavePlatformWindowGeometry(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top); - } - - DestroyWindow(m_hwnd); - m_hwnd = {}; -} - -std::optional Win32NoGUIPlatform::GetPlatformWindowInfo() -{ - if (!m_hwnd) - return std::nullopt; - - RECT rc = {}; - GetClientRect(m_hwnd, &rc); - - WindowInfo wi; - wi.surface_width = static_cast(rc.right - rc.left); - wi.surface_height = static_cast(rc.bottom - rc.top); - wi.surface_scale = m_window_scale; - wi.type = WindowInfo::Type::Win32; - wi.window_handle = m_hwnd; - return wi; -} - -void Win32NoGUIPlatform::SetPlatformWindowTitle(std::string title) -{ - if (!m_hwnd) - return; - - SetWindowTextW(m_hwnd, StringUtil::UTF8StringToWideString(title).c_str()); -} - -std::optional Win32NoGUIPlatform::ConvertHostKeyboardStringToCode(const std::string_view& str) -{ - std::optional converted(Win32KeyNames::GetKeyCodeForName(str)); - return converted.has_value() ? std::optional(static_cast(converted.value())) : std::nullopt; -} - -std::optional Win32NoGUIPlatform::ConvertHostKeyboardCodeToString(u32 code) -{ - const char* converted = Win32KeyNames::GetKeyName(code); - return converted ? std::optional(converted) : std::nullopt; -} - -void Win32NoGUIPlatform::RunMessageLoop() -{ - while (m_message_loop_running.load(std::memory_order_acquire)) - { - MSG msg; - if (GetMessageW(&msg, NULL, 0, 0)) - { - // handle self messages (when we don't have a window yet) - if (msg.hwnd == NULL && msg.message >= WM_FIRST && msg.message <= WM_LAST) - { - WndProc(NULL, msg.message, msg.wParam, msg.lParam); - } - else - { - TranslateMessage(&msg); - DispatchMessageW(&msg); - } - } - } -} - -void Win32NoGUIPlatform::ExecuteInMessageLoop(std::function func) -{ - std::function* pfunc = new std::function(std::move(func)); - if (m_hwnd) - PostMessageW(m_hwnd, WM_FUNC, 0, reinterpret_cast(pfunc)); - else - PostThreadMessageW(m_window_thread_id, WM_FUNC, 0, reinterpret_cast(pfunc)); -} - -void Win32NoGUIPlatform::QuitMessageLoop() -{ - m_message_loop_running.store(false, std::memory_order_release); - PostThreadMessageW(m_window_thread_id, WM_WAKEUP, 0, 0); -} - -void Win32NoGUIPlatform::SetFullscreen(bool enabled) -{ - if (!m_hwnd || m_fullscreen.load(std::memory_order_acquire) == enabled) - return; - - LONG style = GetWindowLong(m_hwnd, GWL_STYLE); - LONG exstyle = GetWindowLong(m_hwnd, GWL_EXSTYLE); - RECT rc; - - if (enabled) - { - HMONITOR monitor = MonitorFromWindow(m_hwnd, MONITOR_DEFAULTTONEAREST); - if (!monitor) - return; - - MONITORINFO mi = {sizeof(MONITORINFO), {}, {}, 0u}; - if (!GetMonitorInfo(monitor, &mi) || !GetWindowRect(m_hwnd, &m_windowed_rect)) - return; - - style = (style & ~WINDOWED_STYLE) | FULLSCREEN_STYLE; - exstyle = (style & ~WINDOWED_EXSTYLE); - rc = mi.rcMonitor; - } - else - { - style = (style & ~FULLSCREEN_STYLE) | WINDOWED_STYLE; - exstyle = exstyle | WINDOWED_EXSTYLE; - rc = m_windowed_rect; - } - - SetWindowLongPtrW(m_hwnd, GWL_STYLE, style); - SetWindowLongPtrW(m_hwnd, GWL_EXSTYLE, exstyle); - SetWindowPos(m_hwnd, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_SHOWWINDOW); - - m_fullscreen.store(enabled, std::memory_order_release); -} - -bool Win32NoGUIPlatform::RequestRenderWindowSize(s32 new_window_width, s32 new_window_height) -{ - RECT rc; - if (!m_hwnd || m_fullscreen.load(std::memory_order_acquire) || !GetWindowRect(m_hwnd, &rc)) - { - return false; - } - - return SetWindowPos(m_hwnd, NULL, rc.left, rc.top, new_window_width, new_window_height, SWP_SHOWWINDOW); -} - -bool Win32NoGUIPlatform::OpenURL(const std::string_view& url) -{ - return (ShellExecuteW(nullptr, L"open", StringUtil::UTF8StringToWideString(url).c_str(), nullptr, nullptr, - SW_SHOWNORMAL) != NULL); -} - -bool Win32NoGUIPlatform::CopyTextToClipboard(const std::string_view& text) -{ - const int wlen = MultiByteToWideChar(CP_UTF8, 0, text.data(), static_cast(text.length()), nullptr, 0); - if (wlen < 0) - return false; - - if (!OpenClipboard(m_hwnd)) - return false; - - ScopedGuard clipboard_cleanup([]() { CloseClipboard(); }); - EmptyClipboard(); - - const HANDLE hText = GlobalAlloc(GMEM_MOVEABLE, (wlen + 1) * sizeof(wchar_t)); - if (hText == NULL) - return false; - - LPWSTR mem = static_cast(GlobalLock(hText)); - MultiByteToWideChar(CP_UTF8, 0, text.data(), static_cast(text.length()), mem, wlen); - mem[wlen] = 0; - GlobalUnlock(hText); - - SetClipboardData(CF_UNICODETEXT, hText); - return true; -} - -LRESULT CALLBACK Win32NoGUIPlatform::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - Win32NoGUIPlatform* platform = static_cast(g_nogui_window.get()); - if (hwnd != platform->m_hwnd && msg != WM_FUNC) - return DefWindowProcW(hwnd, msg, wParam, lParam); - - switch (msg) - { - case WM_SIZE: - { - const u32 width = LOWORD(lParam); - const u32 height = HIWORD(lParam); - NoGUIHost::ProcessPlatformWindowResize(width, height, platform->m_window_scale); - } - break; - - case WM_KEYDOWN: - case WM_KEYUP: - { - const bool pressed = (msg == WM_KEYDOWN); - NoGUIHost::ProcessPlatformKeyEvent(static_cast(wParam), pressed); - } - break; - - case WM_CHAR: - { - if (ImGuiManager::WantsTextInput()) - { - const WCHAR utf16[1] = {static_cast(wParam)}; - char utf8[8] = {}; - const int utf8_len = WideCharToMultiByte(CP_UTF8, 0, utf16, static_cast(std::size(utf16)), utf8, - static_cast(sizeof(utf8) - 1), nullptr, nullptr); - if (utf8_len > 0) - { - utf8[utf8_len] = 0; - NoGUIHost::ProcessPlatformTextEvent(utf8); - } - } - } - break; - - case WM_MOUSEMOVE: - { - const float x = static_cast(static_cast(LOWORD(lParam))); - const float y = static_cast(static_cast(HIWORD(lParam))); - NoGUIHost::ProcessPlatformMouseMoveEvent(x, y); - } - break; - - case WM_LBUTTONDOWN: - case WM_LBUTTONUP: - case WM_MBUTTONDOWN: - case WM_MBUTTONUP: - case WM_RBUTTONDOWN: - case WM_RBUTTONUP: - case WM_XBUTTONDOWN: - case WM_XBUTTONUP: - { - const DWORD buttons = static_cast(wParam); - const DWORD changed = platform->m_last_mouse_buttons ^ buttons; - platform->m_last_mouse_buttons = buttons; - - static constexpr DWORD masks[] = {MK_LBUTTON, MK_RBUTTON, MK_MBUTTON, MK_XBUTTON1, MK_XBUTTON2}; - for (u32 i = 0; i < std::size(masks); i++) - { - if (changed & masks[i]) - NoGUIHost::ProcessPlatformMouseButtonEvent(i, (buttons & masks[i]) != 0); - } - } - break; - - case WM_MOUSEWHEEL: - case WM_MOUSEHWHEEL: - { - const float d = - std::clamp(static_cast(static_cast(HIWORD(wParam))) / static_cast(WHEEL_DELTA), -1.0f, 1.0f); - NoGUIHost::ProcessPlatformMouseWheelEvent((msg == WM_MOUSEHWHEEL) ? d : 0.0f, (msg == WM_MOUSEWHEEL) ? d : 0.0f); - } - break; - - case WM_ACTIVATEAPP: - { - if (wParam) - NoGUIHost::PlatformWindowFocusGained(); - else - NoGUIHost::PlatformWindowFocusLost(); - } - break; - - case WM_CLOSE: - case WM_QUIT: - { - Host::RunOnCPUThread([]() { Host::RequestExit(false); }); - return 0; - } - break; - - case WM_DEVICECHANGE: - { - if (wParam == DBT_DEVNODES_CHANGED) - NoGUIHost::PlatformDevicesChanged(); - } - break; - - case WM_FUNC: - { - std::function* pfunc = reinterpret_cast*>(lParam); - if (pfunc) - { - (*pfunc)(); - delete pfunc; - } - } - break; - - case WM_WAKEUP: - break; - - default: - return DefWindowProcW(hwnd, msg, wParam, lParam); - } - - return 0; -} - -std::unique_ptr NoGUIPlatform::CreateWin32Platform() -{ - std::unique_ptr ret(new Win32NoGUIPlatform()); - if (!ret->Initialize()) - return {}; - - return ret; -} diff --git a/src/duckstation-nogui/win32_nogui_platform.h b/src/duckstation-nogui/win32_nogui_platform.h deleted file mode 100644 index 7af55f920..000000000 --- a/src/duckstation-nogui/win32_nogui_platform.h +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#pragma once - -#include - -#include "common/windows_headers.h" - -#include "nogui_platform.h" - -class Win32NoGUIPlatform : public NoGUIPlatform -{ -public: - Win32NoGUIPlatform(); - ~Win32NoGUIPlatform(); - - bool Initialize(); - - void ReportError(const std::string_view& title, const std::string_view& message) override; - bool ConfirmMessage(const std::string_view& title, const std::string_view& message) override; - - void SetDefaultConfig(SettingsInterface& si) override; - - bool CreatePlatformWindow(std::string title) override; - bool HasPlatformWindow() const override; - void DestroyPlatformWindow() override; - std::optional GetPlatformWindowInfo() override; - void SetPlatformWindowTitle(std::string title) override; - - std::optional ConvertHostKeyboardStringToCode(const std::string_view& str) override; - std::optional ConvertHostKeyboardCodeToString(u32 code) override; - - void RunMessageLoop() override; - void ExecuteInMessageLoop(std::function func) override; - void QuitMessageLoop() override; - - void SetFullscreen(bool enabled) override; - - bool RequestRenderWindowSize(s32 new_window_width, s32 new_window_height) override; - - bool OpenURL(const std::string_view& url) override; - bool CopyTextToClipboard(const std::string_view& text) override; - -private: - enum : u32 - { - WM_FIRST = WM_USER + 1337, - WM_FUNC = WM_FIRST, - WM_WAKEUP, - WM_LAST = WM_WAKEUP - }; - - static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); - - HWND m_hwnd{}; - DWORD m_window_thread_id = 0; - RECT m_windowed_rect = {}; - float m_window_scale = 1.0f; - - std::atomic_bool m_message_loop_running{false}; - std::atomic_bool m_fullscreen{false}; - - DWORD m_last_mouse_buttons = 0; - - HDEVNOTIFY m_dev_notify_handle = NULL; -}; \ No newline at end of file diff --git a/src/duckstation-nogui/x11_nogui_platform.cpp b/src/duckstation-nogui/x11_nogui_platform.cpp deleted file mode 100644 index ab6e456ff..000000000 --- a/src/duckstation-nogui/x11_nogui_platform.cpp +++ /dev/null @@ -1,356 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#include "x11_nogui_platform.h" - -Log_SetChannel(X11NoGUIPlatform); - -X11NoGUIPlatform::X11NoGUIPlatform() -{ - m_message_loop_running.store(true, std::memory_order_release); -} - -X11NoGUIPlatform::~X11NoGUIPlatform() -{ - if (m_display) - { - // Segfaults somewhere in an unloaded module on Ubuntu 22.04 :S - // I really don't care enough about X to figure out why. The application is shutting down - // anyway, so a leak here isn't a big deal. - // XCloseDisplay(m_display); - } -} - -bool X11NoGUIPlatform::Initialize() -{ - const int res = XInitThreads(); - if (res == 0) - Log_WarningPrintf("XInitThreads() returned %d, things might not be stable.", res); - - m_display = XOpenDisplay(nullptr); - if (!m_display) - { - Log_ErrorPrint("Failed to connect to X11 display."); - return false; - } - - InitializeKeyMap(); - return true; -} - -void X11NoGUIPlatform::ReportError(const std::string_view& title, const std::string_view& message) -{ - // not implemented -} - -bool X11NoGUIPlatform::ConfirmMessage(const std::string_view& title, const std::string_view& message) -{ - // not implemented - return true; -} - -void X11NoGUIPlatform::SetDefaultConfig(SettingsInterface& si) {} - -bool X11NoGUIPlatform::CreatePlatformWindow(std::string title) -{ - s32 window_x, window_y, window_width, window_height; - bool has_window_pos = NoGUIHost::GetSavedPlatformWindowGeometry(&window_x, &window_y, &window_width, &window_height); - if (!has_window_pos) - { - window_x = 0; - window_y = 0; - window_width = DEFAULT_WINDOW_WIDTH; - window_height = DEFAULT_WINDOW_HEIGHT; - } - - XDisplayLocker locker(m_display); - { - m_window = XCreateSimpleWindow(m_display, DefaultRootWindow(m_display), window_x, window_y, window_width, - window_height, 0, 0, BlackPixel(m_display, 0)); - if (!m_window) - { - Log_ErrorPrint("Failed to create X window"); - return false; - } - - XSelectInput(m_display, m_window, - StructureNotifyMask | KeyPressMask | KeyReleaseMask | FocusChangeMask | PointerMotionMask | - ButtonPressMask | ButtonReleaseMask); - XStoreName(m_display, m_window, title.c_str()); - - // Enable close notifications. - Atom wmProtocols[1]; - wmProtocols[0] = XInternAtom(m_display, "WM_DELETE_WINDOW", True); - XSetWMProtocols(m_display, m_window, wmProtocols, 1); - - m_window_info.surface_width = static_cast(window_width); - m_window_info.surface_height = static_cast(window_height); - m_window_info.surface_scale = 1.0f; - m_window_info.type = WindowInfo::Type::X11; - m_window_info.window_handle = reinterpret_cast(m_window); - m_window_info.display_connection = m_display; - - XMapRaised(m_display, m_window); - XFlush(m_display); - XSync(m_display, True); - } - - ProcessXEvents(); - return true; -} - -bool X11NoGUIPlatform::HasPlatformWindow() const -{ - return m_window != 0; -} - -void X11NoGUIPlatform::DestroyPlatformWindow() -{ - m_window_info = {}; - - if (m_window) - { - XDisplayLocker locker(m_display); - SaveWindowGeometry(); - XUnmapWindow(m_display, m_window); - XDestroyWindow(m_display, m_window); - m_window = {}; - } -} - -std::optional X11NoGUIPlatform::GetPlatformWindowInfo() -{ - if (m_window_info.type == WindowInfo::Type::X11) - return m_window_info; - else - return std::nullopt; -} - -void X11NoGUIPlatform::SetPlatformWindowTitle(std::string title) -{ - ExecuteInMessageLoop([this, title = std::move(title)]() { - if (m_window) - { - XDisplayLocker locker(m_display); - XStoreName(m_display, m_window, title.c_str()); - } - }); -} - -void X11NoGUIPlatform::InitializeKeyMap() -{ - int min_keycode = 0, max_keycode = -1; - XDisplayKeycodes(m_display, &min_keycode, &max_keycode); - for (int keycode = 0; keycode <= max_keycode; keycode++) - { - KeySym keysym = NoSymbol; - for (int i = 0; i < 8 && keysym == NoSymbol; i++) - keysym = XKeycodeToKeysym(m_display, static_cast(keycode), i); - if (keysym == NoSymbol) - continue; - - KeySym upper_keysym; - XConvertCase(keysym, &keysym, &upper_keysym); - - // Would this fail? - const char* keyname = XKeysymToString(upper_keysym); - if (!keyname) - continue; - - m_key_map.emplace(static_cast(upper_keysym), keyname); - } -} - -std::optional X11NoGUIPlatform::ConvertHostKeyboardStringToCode(const std::string_view& str) -{ - for (const auto& it : m_key_map) - { - if (str == it.second) - return it.first; - } - - return std::nullopt; -} - -std::optional X11NoGUIPlatform::ConvertHostKeyboardCodeToString(u32 code) -{ - const auto it = m_key_map.find(static_cast(code)); - return (it != m_key_map.end()) ? std::optional(it->second) : std::nullopt; -} - -void X11NoGUIPlatform::ProcessXEvents() -{ - XDisplayLocker locker(m_display); - - for (int num_events = XPending(m_display); num_events > 0; num_events--) - { - XEvent event; - XNextEvent(m_display, &event); - switch (event.type) - { - case KeyPress: - case KeyRelease: - { - KeySym sym = XLookupKeysym(&event.xkey, 0); - if (sym != NoSymbol) - { - KeySym upper_sym = sym; - XConvertCase(sym, &sym, &upper_sym); - NoGUIHost::ProcessPlatformKeyEvent(static_cast(upper_sym), (event.type == KeyPress)); - } - } - break; - - case ButtonPress: - case ButtonRelease: - { - if (event.xbutton.button >= Button4 && event.xbutton.button <= Button5) - { - // Button 4/5 are mouse wheel events on X, apparently... - NoGUIHost::ProcessPlatformMouseWheelEvent(0.0f, (event.xbutton.button == Button4) ? 1.0f : -1.0f); - } - else if (event.xbutton.button >= Button1) - { - // Swap middle and right buttons. - const u32 xbutton = event.xbutton.button; - const u32 mapped_button = (xbutton == Button3) ? 1 : (xbutton == Button2 ? 2 : (xbutton - Button1)); - NoGUIHost::ProcessPlatformMouseButtonEvent(mapped_button, event.type == ButtonPress); - } - } - break; - - case MotionNotify: - { - NoGUIHost::ProcessPlatformMouseMoveEvent(static_cast(event.xmotion.x), - static_cast(event.xmotion.y)); - } - break; - - case ConfigureNotify: - { - const s32 width = std::max(static_cast(event.xconfigure.width), 1); - const s32 height = std::max(static_cast(event.xconfigure.height), 1); - NoGUIHost::ProcessPlatformWindowResize(width, height, m_window_info.surface_scale); - } - break; - - case FocusIn: - { - NoGUIHost::PlatformWindowFocusGained(); - } - break; - - case FocusOut: - { - NoGUIHost::PlatformWindowFocusGained(); - } - break; - - case ClientMessage: - { - if (static_cast(event.xclient.data.l[0]) == XInternAtom(m_display, "WM_DELETE_WINDOW", False)) - Host::RequestExit(false); - } - break; - - default: - break; - } - } -} - -void X11NoGUIPlatform::RunMessageLoop() -{ - while (m_message_loop_running.load(std::memory_order_acquire)) - { - ProcessXEvents(); - - { - std::unique_lock lock(m_callback_queue_mutex); - while (!m_callback_queue.empty()) - { - std::function func = std::move(m_callback_queue.front()); - m_callback_queue.pop_front(); - lock.unlock(); - func(); - lock.lock(); - } - } - - // TODO: Make this suck less. - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } -} - -void X11NoGUIPlatform::ExecuteInMessageLoop(std::function func) -{ - std::unique_lock lock(m_callback_queue_mutex); - m_callback_queue.push_back(std::move(func)); -} - -void X11NoGUIPlatform::QuitMessageLoop() -{ - m_message_loop_running.store(false, std::memory_order_release); -} - -void X11NoGUIPlatform::SetFullscreen(bool enabled) -{ - if (!m_window || m_fullscreen.load(std::memory_order_acquire) == enabled) - return; - - XDisplayLocker locker(m_display); - - XEvent event; - event.xclient.type = ClientMessage; - event.xclient.message_type = XInternAtom(m_display, "_NET_WM_STATE", False); - event.xclient.window = m_window; - event.xclient.format = 32; - event.xclient.data.l[0] = _NET_WM_STATE_TOGGLE; - event.xclient.data.l[1] = XInternAtom(m_display, "_NET_WM_STATE_FULLSCREEN", False); - if (!XSendEvent(m_display, DefaultRootWindow(m_display), False, SubstructureRedirectMask | SubstructureNotifyMask, - &event)) - { - Log_ErrorPrintf("Failed to switch to %s", enabled ? "Fullscreen" : "windowed"); - return; - } - - m_fullscreen.store(enabled, std::memory_order_release); -} - -void X11NoGUIPlatform::SaveWindowGeometry() -{ - int x = 0, y = 0; - unsigned int width = 0, height = 0; - unsigned int dummy_border, dummy_depth; - Window dummy_window; - XGetGeometry(m_display, m_window, &dummy_window, &x, &y, &width, &height, &dummy_border, &dummy_depth); - if (width > 0 && height > 0) - NoGUIHost::SavePlatformWindowGeometry(x, y, width, height); -} - -bool X11NoGUIPlatform::RequestRenderWindowSize(s32 new_window_width, s32 new_window_height) -{ - return false; -} - -bool X11NoGUIPlatform::OpenURL(const std::string_view& url) -{ - Log_ErrorPrintf("X11NoGUIPlatform::OpenURL() not implemented: %.*s", static_cast(url.size()), url.data()); - return false; -} - -bool X11NoGUIPlatform::CopyTextToClipboard(const std::string_view& text) -{ - Log_ErrorPrintf("X11NoGUIPlatform::CopyTextToClipboard() not implemented: %.*s", static_cast(text.size()), - text.data()); - return false; -} - -std::unique_ptr NoGUIPlatform::CreateX11Platform() -{ - std::unique_ptr ret = std::unique_ptr(new X11NoGUIPlatform()); - if (!ret->Initialize()) - return {}; - - return ret; -} diff --git a/src/duckstation-nogui/x11_nogui_platform.h b/src/duckstation-nogui/x11_nogui_platform.h deleted file mode 100644 index 1d050b47a..000000000 --- a/src/duckstation-nogui/x11_nogui_platform.h +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#pragma once - -#include "nogui_platform.h" - -// Why do we have all these here instead of in the source? -// Because X11 is a giant turd and #defines commonly used words. -#include "common/assert.h" -#include "common/log.h" -#include "common/string_util.h" -#include "common/threading.h" -#include "core/host.h" -#include "nogui_host.h" -#include "nogui_platform.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -// Include X stuff *last*. -#include -#include -#include -#include -#define _NET_WM_STATE_REMOVE 0 -#define _NET_WM_STATE_ADD 1 -#define _NET_WM_STATE_TOGGLE 2 - -class X11NoGUIPlatform : public NoGUIPlatform -{ -public: - X11NoGUIPlatform(); - ~X11NoGUIPlatform(); - - bool Initialize(); - - void ReportError(const std::string_view& title, const std::string_view& message) override; - bool ConfirmMessage(const std::string_view& title, const std::string_view& message) override; - - void SetDefaultConfig(SettingsInterface& si) override; - - bool CreatePlatformWindow(std::string title) override; - bool HasPlatformWindow() const override; - void DestroyPlatformWindow() override; - std::optional GetPlatformWindowInfo() override; - void SetPlatformWindowTitle(std::string title) override; - - std::optional ConvertHostKeyboardStringToCode(const std::string_view& str) override; - std::optional ConvertHostKeyboardCodeToString(u32 code) override; - - void RunMessageLoop() override; - void ExecuteInMessageLoop(std::function func) override; - void QuitMessageLoop() override; - - void SetFullscreen(bool enabled) override; - - bool RequestRenderWindowSize(s32 new_window_width, s32 new_window_height) override; - - bool OpenURL(const std::string_view& url) override; - bool CopyTextToClipboard(const std::string_view& text) override; - -private: - void InitializeKeyMap(); - void SaveWindowGeometry(); - void ProcessXEvents(); - - std::atomic_bool m_message_loop_running{false}; - std::atomic_bool m_fullscreen{false}; - - WindowInfo m_window_info = {}; - - Display* m_display = nullptr; - Window m_window = {}; - - std::unordered_map m_key_map; - - std::deque> m_callback_queue; - std::mutex m_callback_queue_mutex; -}; - -class XDisplayLocker -{ -public: - XDisplayLocker(Display* dpy) : m_display(dpy) { XLockDisplay(m_display); } - - ~XDisplayLocker() { XUnlockDisplay(m_display); } - -private: - Display* m_display; -}; diff --git a/src/scmversion/update_rc_version.bat b/src/scmversion/update_rc_version.bat index e83f989d9..65a1dd3c4 100644 --- a/src/scmversion/update_rc_version.bat +++ b/src/scmversion/update_rc_version.bat @@ -4,6 +4,3 @@ FOR /F "tokens=* USEBACKQ" %%g IN (`git describe --dirty`) do (SET "TAG=%%g") powershell -Command "(gc ..\duckstation-qt\duckstation-qt.rc) -replace '1,0,0,1', '"%TAG:~0,1%","%TAG:~2,1%","%TAG:~4,4%",0' | Out-File -encoding ASCII ..\duckstation-qt\duckstation-qt.rc" powershell -Command "(gc ..\duckstation-qt\duckstation-qt.rc) -replace '1.0.0.1', '"%TAG:~0,1%"."%TAG:~2,1%"."%TAG:~4,4%"' | Out-File -encoding ASCII ..\duckstation-qt\duckstation-qt.rc" - -powershell -Command "(gc ..\duckstation-nogui\duckstation-nogui.rc) -replace '1,0,0,1', '"%TAG:~0,1%","%TAG:~2,1%","%TAG:~4,4%",0' | Out-File -encoding ASCII ..\duckstation-nogui\duckstation-nogui.rc -powershell -Command "(gc ..\duckstation-nogui\duckstation-nogui.rc) -replace '1.0.0.1', '"%TAG:~0,1%"."%TAG:~2,1%"."%TAG:~4,4%"' | Out-File -encoding ASCII ..\duckstation-nogui\duckstation-nogui.rc" \ No newline at end of file