NoGUI: Purge unused code
This commit is contained in:
parent
4e0c417add
commit
d2d6e397e6
|
@ -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
|
||||
|
|
|
@ -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()
|
|
@ -1,133 +0,0 @@
|
|||
#pragma once
|
||||
#include "common/types.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include <Carbon/Carbon.h>
|
||||
|
||||
namespace CocoaKeyNames {
|
||||
static const std::map<int, const char*> 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<unsigned short> 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
|
|
@ -1,74 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AppKit/AppKit.h>
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
#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 <atomic>
|
||||
|
||||
@interface CocoaNoGUIView : NSView<NSWindowDelegate>
|
||||
- (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<WindowInfo> GetPlatformWindowInfo() override;
|
||||
void SetPlatformWindowTitle(std::string title) override;
|
||||
|
||||
std::optional<u32> ConvertHostKeyboardStringToCode(const std::string_view& str) override;
|
||||
std::optional<std::string> ConvertHostKeyboardCodeToString(u32 code) override;
|
||||
|
||||
void RunMessageLoop() override;
|
||||
void ExecuteInMessageLoop(std::function<void()> 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};
|
||||
};
|
|
@ -1,301 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// 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<s32>(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<s32>(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<s32>(event.keyCode), true);
|
||||
}
|
||||
|
||||
- (void)keyUp:(NSEvent *)event {
|
||||
[super keyUp:event];
|
||||
NoGUIHost::ProcessPlatformKeyEvent(static_cast<s32>(event.keyCode), false);
|
||||
}
|
||||
|
||||
- (void)windowDidEndLiveResize:(NSNotification *)notif
|
||||
{
|
||||
const NSSize size = [self convertSizeToBacking:self.frame.size];
|
||||
NoGUIHost::ProcessPlatformWindowResize(static_cast<s32>(size.width), static_cast<s32>(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<CGFloat>(window_width), static_cast<CGFloat>(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<CGFloat>(window_x), static_cast<CGFloat>(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<s32>(frame_origin.x), static_cast<s32>(frame_origin.y),
|
||||
static_cast<s32>(content_size.width), static_cast<s32>(content_size.height));
|
||||
}
|
||||
|
||||
[m_window close];
|
||||
[m_window release];
|
||||
m_window = nil;
|
||||
}
|
||||
|
||||
std::optional<WindowInfo> 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<u32>(size.width);
|
||||
wi.surface_height = static_cast<u32>(size.height);
|
||||
wi.surface_scale = m_window_scale;
|
||||
wi.type = WindowInfo::Type::MacOS;
|
||||
wi.window_handle = static_cast<void*>(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<u32> CocoaNoGUIPlatform::ConvertHostKeyboardStringToCode(const std::string_view& str)
|
||||
{
|
||||
std::optional<unsigned short> converted(CocoaKeyNames::GetKeyCodeForName(str));
|
||||
return converted.has_value() ? std::optional<u32>(static_cast<u32>(converted.value())) : std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> CocoaNoGUIPlatform::ConvertHostKeyboardCodeToString(u32 code)
|
||||
{
|
||||
const char* converted = CocoaKeyNames::GetKeyName(static_cast<unsigned short>(code));
|
||||
return converted ? std::optional<std::string>(converted) : std::nullopt;
|
||||
}
|
||||
|
||||
void CocoaNoGUIPlatform::RunMessageLoop()
|
||||
{
|
||||
[NSApp run];
|
||||
}
|
||||
|
||||
void CocoaNoGUIPlatform::ExecuteInMessageLoop(std::function<void()> 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<CGFloat>(new_window_width), static_cast<CGFloat>(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> NoGUIPlatform::CreateCocoaPlatform()
|
||||
{
|
||||
std::unique_ptr<CocoaNoGUIPlatform> ret(new CocoaNoGUIPlatform());
|
||||
if (!ret->Initialize())
|
||||
return {};
|
||||
|
||||
return ret;
|
||||
}
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 111 KiB |
|
@ -1,17 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<asmv3:application>
|
||||
<asmv3:windowsSettings>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
|
@ -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
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\dep\msvc\vsprops\Configurations.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="nogui_host.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="sdl_nogui_platform.cpp" />
|
||||
<ClCompile Include="wayland_nogui_platform.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="win32_nogui_platform.cpp" />
|
||||
<ClCompile Include="x11_nogui_platform.cpp">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="nogui_host.h" />
|
||||
<ClInclude Include="nogui_platform.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="sdl_nogui_platform.h" />
|
||||
<ClInclude Include="wayland_nogui_platform.h">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
<ClInclude Include="win32_nogui_platform.h" />
|
||||
<ClInclude Include="x11_nogui_platform.h">
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Manifest Include="duckstation-nogui.manifest" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="duckstation-nogui.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="duckstation-nogui.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\common\common.vcxproj">
|
||||
<Project>{ee054e08-3799-4a59-a422-18259c105ffd}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\core\core.vcxproj">
|
||||
<Project>{868b98c8-65a1-494b-8346-250a73a48c0a}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\util\util.vcxproj">
|
||||
<Project>{57f6206d-f264-4b07-baf8-11b9bbe1f455}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{0A172B2E-DC67-49FC-A4C1-975F93C586C4}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\dep\msvc\vsprops\Win32Application.props" />
|
||||
<Import Project="..\core\core.props" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<ForcedIncludeFiles>pch.h</ForcedIncludeFiles>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="..\..\dep\msvc\vsprops\Targets.props" />
|
||||
</Project>
|
|
@ -1,30 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="nogui_host.cpp" />
|
||||
<ClCompile Include="win32_nogui_platform.cpp" />
|
||||
<ClCompile Include="wayland_nogui_platform.cpp" />
|
||||
<ClCompile Include="x11_nogui_platform.cpp" />
|
||||
<ClCompile Include="pch.cpp" />
|
||||
<ClCompile Include="sdl_nogui_platform.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="nogui_host.h" />
|
||||
<ClInclude Include="win32_nogui_platform.h" />
|
||||
<ClInclude Include="nogui_platform.h" />
|
||||
<ClInclude Include="wayland_nogui_platform.h" />
|
||||
<ClInclude Include="x11_nogui_platform.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="sdl_nogui_platform.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Manifest Include="duckstation-nogui.manifest" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="duckstation-nogui.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="duckstation-nogui.rc" />
|
||||
</ItemGroup>
|
||||
</Project>
|
File diff suppressed because it is too large
Load Diff
|
@ -1,36 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
#include "common/types.h"
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
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
|
|
@ -1,70 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
||||
// 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 <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
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<WindowInfo> GetPlatformWindowInfo() = 0;
|
||||
virtual void SetPlatformWindowTitle(std::string title) = 0;
|
||||
|
||||
virtual std::optional<u32> ConvertHostKeyboardStringToCode(const std::string_view& str) = 0;
|
||||
virtual std::optional<std::string> ConvertHostKeyboardCodeToString(u32 code) = 0;
|
||||
|
||||
virtual void RunMessageLoop() = 0;
|
||||
virtual void ExecuteInMessageLoop(std::function<void()> 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<NoGUIPlatform> CreateWin32Platform();
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
static std::unique_ptr<NoGUIPlatform> CreateCocoaPlatform();
|
||||
#endif
|
||||
#ifdef ENABLE_SDL2
|
||||
static std::unique_ptr<NoGUIPlatform> CreateSDLPlatform();
|
||||
#endif
|
||||
#ifdef NOGUI_PLATFORM_WAYLAND
|
||||
static std::unique_ptr<NoGUIPlatform> CreateWaylandPlatform();
|
||||
#endif
|
||||
#ifdef NOGUI_PLATFORM_X11
|
||||
static std::unique_ptr<NoGUIPlatform> CreateX11Platform();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
static constexpr s32 DEFAULT_WINDOW_WIDTH = 1280;
|
||||
static constexpr s32 DEFAULT_WINDOW_HEIGHT = 720;
|
||||
};
|
||||
|
||||
extern std::unique_ptr<NoGUIPlatform> g_nogui_window;
|
|
@ -1,4 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#include "pch.h"
|
|
@ -1,6 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/pch.h"
|
|
@ -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
|
|
@ -1,274 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// 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 <array>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
namespace SDLKeyNames {
|
||||
|
||||
static const std::map<int, const char*> 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<DWORD> 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
|
|
@ -1,449 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// 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 <SDL.h>
|
||||
#include <SDL_syswm.h>
|
||||
|
||||
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<u32>(-1) || m_wakeup_event_id == static_cast<u32>(-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<int>(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<WindowInfo> 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<float>(std::max(window_px_width, 1)) / static_cast<float>(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<u32>(window_px_width);
|
||||
wi.surface_height = static_cast<u32>(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<int>(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<u32> SDLNoGUIPlatform::ConvertHostKeyboardStringToCode(const std::string_view& str)
|
||||
{
|
||||
std::optional<DWORD> converted(SDLKeyNames::GetKeyCodeForName(str));
|
||||
return converted.has_value() ? std::optional<u32>(static_cast<u32>(converted.value())) : std::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> SDLNoGUIPlatform::ConvertHostKeyboardCodeToString(u32 code)
|
||||
{
|
||||
const char* converted = SDLKeyNames::GetKeyName(code);
|
||||
return converted ? std::optional<std::string>(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<void()> func)
|
||||
{
|
||||
std::function<void()>* pfunc = new std::function<void()>(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<s32>(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<float>(ev->motion.x);
|
||||
const float y = static_cast<float>(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<void()>* pfunc = reinterpret_cast<std::function<void()>*>(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<SDLInputSource*>(InputManager::GetInputSourceInterface(InputSourceType::SDL));
|
||||
if (is) [[likely]]
|
||||
is->ProcessSDLEvent(&event_copy);
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<NoGUIPlatform> NoGUIPlatform::CreateSDLPlatform()
|
||||
{
|
||||
std::unique_ptr<SDLNoGUIPlatform> ret(new SDLNoGUIPlatform());
|
||||
if (!ret->Initialize())
|
||||
return {};
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "common/windows_headers.h"
|
||||
|
||||
#include "nogui_platform.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
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<WindowInfo> GetPlatformWindowInfo() override;
|
||||
void SetPlatformWindowTitle(std::string title) override;
|
||||
|
||||
std::optional<u32> ConvertHostKeyboardStringToCode(const std::string_view& str) override;
|
||||
std::optional<std::string> ConvertHostKeyboardCodeToString(u32 code) override;
|
||||
|
||||
void RunMessageLoop() override;
|
||||
void ExecuteInMessageLoop(std::function<void()> 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};
|
||||
};
|
|
@ -1,483 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
||||
// 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 <linux/input-event-codes.h>
|
||||
#include <sys/mman.h>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
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<u32>(window_width);
|
||||
m_window_info.surface_height = static_cast<u32>(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<WindowInfo> 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<u32> 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<std::string> WaylandNoGUIPlatform::ConvertHostKeyboardCodeToString(u32 code)
|
||||
{
|
||||
std::unique_lock lock(m_key_map_mutex);
|
||||
const auto it = m_key_map.find(static_cast<s32>(code));
|
||||
return (it != m_key_map.end()) ? std::optional<std::string>(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<WaylandNoGUIPlatform*>(data);
|
||||
if (std::strcmp(interface, wl_compositor_interface.name) == 0)
|
||||
{
|
||||
platform->m_compositor =
|
||||
static_cast<wl_compositor*>(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<xdg_wm_base*>(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<zxdg_decoration_manager_v1*>(
|
||||
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_seat*>(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<WaylandNoGUIPlatform*>(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<WaylandNoGUIPlatform*>(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<WaylandNoGUIPlatform*>(data);
|
||||
char* keymap_string = static_cast<char*>(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<s32>(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<xkb_keycode_t>(key + 8);
|
||||
const bool pressed = (state == WL_KEYBOARD_KEY_STATE_PRESSED);
|
||||
NoGUIHost::ProcessPlatformKeyEvent(static_cast<s32>(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<WaylandNoGUIPlatform*>(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<float>(wl_fixed_to_double(x));
|
||||
const float pos_y = static_cast<float>(wl_fixed_to_double(y));
|
||||
NoGUIHost::ProcessPlatformMouseMoveEvent(static_cast<int>(pos_x), static_cast<int>(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<float>(wl_fixed_to_double(value)), -1.0f, 1.0f) : 0.0f;
|
||||
const float y = (axis == 0) ? std::clamp(static_cast<float>(-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<void()> 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<void()> 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<int>(url.size()), url.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WaylandNoGUIPlatform::CopyTextToClipboard(const std::string_view& text)
|
||||
{
|
||||
Log_ErrorPrintf("WaylandNoGUIPlatform::CopyTextToClipboard() not implemented: %.*s", static_cast<int>(text.size()), text.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<NoGUIPlatform> NoGUIPlatform::CreateWaylandPlatform()
|
||||
{
|
||||
std::unique_ptr<WaylandNoGUIPlatform> ret = std::unique_ptr<WaylandNoGUIPlatform>(new WaylandNoGUIPlatform());
|
||||
if (!ret->Initialize())
|
||||
return {};
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "nogui_platform.h"
|
||||
|
||||
#include "wayland-xdg-decoration-client-protocol.h"
|
||||
#include "wayland-xdg-shell-client-protocol.h"
|
||||
#include <wayland-client-protocol.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
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<WindowInfo> GetPlatformWindowInfo() override;
|
||||
void SetPlatformWindowTitle(std::string title) override;
|
||||
|
||||
std::optional<u32> ConvertHostKeyboardStringToCode(const std::string_view& str) override;
|
||||
std::optional<std::string> ConvertHostKeyboardCodeToString(u32 code) override;
|
||||
|
||||
void RunMessageLoop() override;
|
||||
void ExecuteInMessageLoop(std::function<void()> 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<s32, std::string> m_key_map;
|
||||
std::mutex m_key_map_mutex;
|
||||
|
||||
std::deque<std::function<void()>> m_callback_queue;
|
||||
std::mutex m_callback_queue_mutex;
|
||||
};
|
|
@ -1,169 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// 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 <array>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
namespace Win32KeyNames {
|
||||
static const std::map<int, const char*> 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<DWORD> 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
|
|
@ -1,449 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// 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 <Dbt.h>
|
||||
#include <shellapi.h>
|
||||
#include <tchar.h>
|
||||
|
||||
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<decltype(get_dpi_for_window)>(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<float>(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<WindowInfo> Win32NoGUIPlatform::GetPlatformWindowInfo()
|
||||
{
|
||||
if (!m_hwnd)
|
||||
return std::nullopt;
|
||||
|
||||
RECT rc = {};
|
||||
GetClientRect(m_hwnd, &rc);
|
||||
|
||||
WindowInfo wi;
|
||||
wi.surface_width = static_cast<u32>(rc.right - rc.left);
|
||||
wi.surface_height = static_cast<u32>(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<u32> Win32NoGUIPlatform::ConvertHostKeyboardStringToCode(const std::string_view& str)
|
||||
{
|
||||
std::optional<DWORD> converted(Win32KeyNames::GetKeyCodeForName(str));
|
||||
return converted.has_value() ? std::optional<u32>(static_cast<u32>(converted.value())) : std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string> Win32NoGUIPlatform::ConvertHostKeyboardCodeToString(u32 code)
|
||||
{
|
||||
const char* converted = Win32KeyNames::GetKeyName(code);
|
||||
return converted ? std::optional<std::string>(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<void()> func)
|
||||
{
|
||||
std::function<void()>* pfunc = new std::function<void()>(std::move(func));
|
||||
if (m_hwnd)
|
||||
PostMessageW(m_hwnd, WM_FUNC, 0, reinterpret_cast<LPARAM>(pfunc));
|
||||
else
|
||||
PostThreadMessageW(m_window_thread_id, WM_FUNC, 0, reinterpret_cast<LPARAM>(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<int>(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<LPWSTR>(GlobalLock(hText));
|
||||
MultiByteToWideChar(CP_UTF8, 0, text.data(), static_cast<int>(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<Win32NoGUIPlatform*>(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<s32>(wParam), pressed);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_CHAR:
|
||||
{
|
||||
if (ImGuiManager::WantsTextInput())
|
||||
{
|
||||
const WCHAR utf16[1] = {static_cast<WCHAR>(wParam)};
|
||||
char utf8[8] = {};
|
||||
const int utf8_len = WideCharToMultiByte(CP_UTF8, 0, utf16, static_cast<int>(std::size(utf16)), utf8,
|
||||
static_cast<int>(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<float>(static_cast<s16>(LOWORD(lParam)));
|
||||
const float y = static_cast<float>(static_cast<s16>(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<DWORD>(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<float>(static_cast<s16>(HIWORD(wParam))) / static_cast<float>(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<void()>* pfunc = reinterpret_cast<std::function<void()>*>(lParam);
|
||||
if (pfunc)
|
||||
{
|
||||
(*pfunc)();
|
||||
delete pfunc;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_WAKEUP:
|
||||
break;
|
||||
|
||||
default:
|
||||
return DefWindowProcW(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::unique_ptr<NoGUIPlatform> NoGUIPlatform::CreateWin32Platform()
|
||||
{
|
||||
std::unique_ptr<Win32NoGUIPlatform> ret(new Win32NoGUIPlatform());
|
||||
if (!ret->Initialize())
|
||||
return {};
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#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<WindowInfo> GetPlatformWindowInfo() override;
|
||||
void SetPlatformWindowTitle(std::string title) override;
|
||||
|
||||
std::optional<u32> ConvertHostKeyboardStringToCode(const std::string_view& str) override;
|
||||
std::optional<std::string> ConvertHostKeyboardCodeToString(u32 code) override;
|
||||
|
||||
void RunMessageLoop() override;
|
||||
void ExecuteInMessageLoop(std::function<void()> 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;
|
||||
};
|
|
@ -1,356 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// 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<u32>(window_width);
|
||||
m_window_info.surface_height = static_cast<u32>(window_height);
|
||||
m_window_info.surface_scale = 1.0f;
|
||||
m_window_info.type = WindowInfo::Type::X11;
|
||||
m_window_info.window_handle = reinterpret_cast<void*>(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<WindowInfo> 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>(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<s32>(upper_keysym), keyname);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<u32> 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<std::string> X11NoGUIPlatform::ConvertHostKeyboardCodeToString(u32 code)
|
||||
{
|
||||
const auto it = m_key_map.find(static_cast<s32>(code));
|
||||
return (it != m_key_map.end()) ? std::optional<std::string>(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<s32>(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<float>(event.xmotion.x),
|
||||
static_cast<float>(event.xmotion.y));
|
||||
}
|
||||
break;
|
||||
|
||||
case ConfigureNotify:
|
||||
{
|
||||
const s32 width = std::max<s32>(static_cast<s32>(event.xconfigure.width), 1);
|
||||
const s32 height = std::max<s32>(static_cast<s32>(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<Atom>(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<void()> 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<void()> 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<int>(url.size()), url.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool X11NoGUIPlatform::CopyTextToClipboard(const std::string_view& text)
|
||||
{
|
||||
Log_ErrorPrintf("X11NoGUIPlatform::CopyTextToClipboard() not implemented: %.*s", static_cast<int>(text.size()),
|
||||
text.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<NoGUIPlatform> NoGUIPlatform::CreateX11Platform()
|
||||
{
|
||||
std::unique_ptr<X11NoGUIPlatform> ret = std::unique_ptr<X11NoGUIPlatform>(new X11NoGUIPlatform());
|
||||
if (!ret->Initialize())
|
||||
return {};
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
||||
// 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 <atomic>
|
||||
#include <deque>
|
||||
#include <linux/input-event-codes.h>
|
||||
#include <mutex>
|
||||
#include <sys/mman.h>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
#include <unordered_map>
|
||||
|
||||
// Include X stuff *last*.
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/keysym.h>
|
||||
#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<WindowInfo> GetPlatformWindowInfo() override;
|
||||
void SetPlatformWindowTitle(std::string title) override;
|
||||
|
||||
std::optional<u32> ConvertHostKeyboardStringToCode(const std::string_view& str) override;
|
||||
std::optional<std::string> ConvertHostKeyboardCodeToString(u32 code) override;
|
||||
|
||||
void RunMessageLoop() override;
|
||||
void ExecuteInMessageLoop(std::function<void()> 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<s32, std::string> m_key_map;
|
||||
|
||||
std::deque<std::function<void()>> 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;
|
||||
};
|
|
@ -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"
|
Loading…
Reference in New Issue