Rewrite host GPU abstraction

- Don't have to repeat the same thing for 4 renderers.
 - Add native Metal renderer.
This commit is contained in:
Stenzek
2023-08-13 13:42:02 +10:00
parent bfa792ddbf
commit e3d9ba4c99
249 changed files with 28851 additions and 32222 deletions

View File

@@ -67,14 +67,12 @@ if(USE_WAYLAND)
)
endif()
if(USE_DRMKMS AND USE_EVDEV)
message(STATUS "Building VTY/DRM/KMS/EVDev NoGUI Platform.")
target_compile_definitions(duckstation-nogui PRIVATE "NOGUI_PLATFORM_VTY=1" "WITH_DRMKMS=1")
target_sources(duckstation-nogui PRIVATE
vty_key_names.h
vty_nogui_platform.cpp
vty_nogui_platform.h
if(APPLE)
message(STATUS "Building Cocoa NoGUI Platform.")
target_sources(duckstation-nogui PRIVATE
cocoa_key_names.h
cocoa_nogui_platform.mm
cocoa_nogui_platform.h
)
target_include_directories(duckstation-nogui PRIVATE ${LIBEVDEV_INCLUDE_DIRS})
target_link_libraries(duckstation-nogui PRIVATE ${LIBEVDEV_LIBRARIES})
endif()

View File

@@ -0,0 +1,133 @@
#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

View File

@@ -1,20 +1,43 @@
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
// 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 "nogui_platform.h"
#include <atomic>
#include <deque>
#include <functional>
#include <libevdev/libevdev.h>
#include <mutex>
#include <vector>
class VTYNoGUIPlatform : public NoGUIPlatform
#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:
VTYNoGUIPlatform();
~VTYNoGUIPlatform();
CocoaNoGUIPlatform();
~CocoaNoGUIPlatform();
bool Initialize();
@@ -24,6 +47,7 @@ public:
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;
@@ -43,21 +67,8 @@ public:
bool CopyTextToClipboard(const std::string_view& text) override;
private:
void OpenEVDevFDs();
void CloseEVDevFDs();
void PollEvDevKeyboards();
void SetImGuiKeyMap();
NSWindow* m_window = nil;
float m_window_scale = 1.0f;
struct EvDevKeyboard
{
struct libevdev* obj;
int fd;
};
std::vector<EvDevKeyboard> m_evdev_keyboards;
std::deque<std::function<void()>> m_callback_queue;
std::mutex m_callback_queue_mutex;
std::atomic_bool m_message_loop_running{false};
std::atomic_bool m_fullscreen{false};
};

View File

@@ -0,0 +1,305 @@
// 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 "core/host_settings.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)viewDidEndLiveResize:(NSEvent *)event {
[super viewDidEndLiveResize:event];
}
- (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;
}

View File

@@ -3,9 +3,6 @@
<Import Project="..\..\dep\msvc\vsprops\Configurations.props" />
<ItemGroup>
<ClCompile Include="nogui_host.cpp" />
<ClCompile Include="vty_nogui_platform.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="wayland_nogui_platform.cpp">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClCompile>
@@ -18,12 +15,6 @@
<ClInclude Include="nogui_host.h" />
<ClInclude Include="nogui_platform.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="vty_key_names.h">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="vty_nogui_platform.h">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="wayland_nogui_platform.h">
<ExcludedFromBuild>true</ExcludedFromBuild>
</ClInclude>

View File

@@ -3,7 +3,6 @@
<ItemGroup>
<ClCompile Include="nogui_host.cpp" />
<ClCompile Include="win32_nogui_platform.cpp" />
<ClCompile Include="vty_nogui_platform.cpp" />
<ClCompile Include="wayland_nogui_platform.cpp" />
<ClCompile Include="x11_nogui_platform.cpp" />
</ItemGroup>
@@ -11,8 +10,6 @@
<ClInclude Include="resource.h" />
<ClInclude Include="nogui_host.h" />
<ClInclude Include="win32_nogui_platform.h" />
<ClInclude Include="vty_nogui_platform.h" />
<ClInclude Include="vty_key_names.h" />
<ClInclude Include="nogui_platform.h" />
<ClInclude Include="wayland_nogui_platform.h" />
<ClInclude Include="x11_nogui_platform.h" />

View File

@@ -17,7 +17,7 @@
#include "core/settings.h"
#include "core/system.h"
#include "util/host_display.h"
#include "util/gpu_device.h"
#include "util/imgui_manager.h"
#include "util/ini_settings_interface.h"
#include "util/input_manager.h"
@@ -63,6 +63,9 @@ std::unique_ptr<NoGUIPlatform> g_nogui_window;
// Local function declarations
//////////////////////////////////////////////////////////////////////////
namespace NoGUIHost {
/// Starts the virtual machine.
static void StartSystem(SystemBootParameters params);
static bool ParseCommandLineParametersAndInitializeConfig(int argc, char* argv[],
std::optional<SystemBootParameters>& autoboot);
static void PrintCommandLineVersion();
@@ -88,8 +91,6 @@ static void UpdateWindowTitle(const std::string& game_title);
static void CancelAsyncOp();
static void StartAsyncOp(std::function<void(ProgressCallback*)> callback);
static void AsyncOpThreadEntryPoint(std::function<void(ProgressCallback*)> callback);
static bool AcquireHostDisplay(RenderAPI api);
static void ReleaseHostDisplay();
} // namespace NoGUIHost
//////////////////////////////////////////////////////////////////////////
@@ -102,7 +103,7 @@ static bool s_save_state_on_shutdown = false;
static bool s_was_paused_by_focus_loss = false;
static Threading::Thread s_cpu_thread;
static Threading::KernelSemaphore s_host_display_created_or_destroyed;
static Threading::KernelSemaphore s_platform_window_updated;
static std::atomic_bool s_running{false};
static std::mutex s_cpu_thread_events_mutex;
static std::condition_variable s_cpu_thread_event_done;
@@ -159,7 +160,7 @@ void NoGUIHost::SetAppRoot()
void NoGUIHost::SetResourcesDirectory()
{
#ifndef __APPLE__
#ifndef __APPLE__NOT_USED // Not using bundles yet.
// On Windows/Linux, these are in the binary directory.
EmuFolders::Resources = Path::Combine(EmuFolders::AppRoot, "resources");
#else
@@ -423,8 +424,7 @@ void NoGUIHost::StartSystem(SystemBootParameters params)
void NoGUIHost::ProcessPlatformWindowResize(s32 width, s32 height, float scale)
{
Host::RunOnCPUThread([width, height, scale]() {
// TODO: Scale
g_host_display->ResizeWindow(width, height);
g_gpu_device->ResizeWindow(width, height, scale);
ImGuiManager::WindowResized();
System::HostDisplayResized();
});
@@ -432,8 +432,8 @@ void NoGUIHost::ProcessPlatformWindowResize(s32 width, s32 height, float scale)
void NoGUIHost::ProcessPlatformMouseMoveEvent(float x, float y)
{
if (g_host_display)
g_host_display->SetMousePosition(static_cast<s32>(x), static_cast<s32>(y));
if (g_gpu_device)
g_gpu_device->SetMousePosition(static_cast<s32>(x), static_cast<s32>(y));
InputManager::UpdatePointerAbsolutePosition(0, x, y);
ImGuiManager::UpdateMousePosition(x, y);
@@ -616,8 +616,8 @@ void NoGUIHost::CPUThreadEntryPoint()
// input source setup must happen on emu thread
CommonHost::Initialize();
// start the GS thread up and get it going
if (AcquireHostDisplay(Settings::GetRenderAPIForRenderer(g_settings.gpu_renderer)))
// start the fullscreen UI and get it going
if (Host::CreateGPUDevice(Settings::GetRenderAPIForRenderer(g_settings.gpu_renderer)) && FullscreenUI::Initialize())
{
// kick a game list refresh if we're not in batch mode
if (!InBatchMode())
@@ -637,7 +637,8 @@ void NoGUIHost::CPUThreadEntryPoint()
if (System::IsValid())
System::ShutdownSystem(false);
ReleaseHostDisplay();
Host::ReleaseGPUDevice();
Host::ReleaseRenderWindow();
CommonHost::Shutdown();
g_nogui_window->QuitMessageLoop();
@@ -655,58 +656,35 @@ void NoGUIHost::CPUThreadMainLoop()
Host::PumpMessagesOnCPUThread();
Host::RenderDisplay(false);
if (!g_host_display->IsVsyncEnabled())
g_host_display->ThrottlePresentation();
if (!g_gpu_device->IsVsyncEnabled())
g_gpu_device->ThrottlePresentation();
}
}
bool NoGUIHost::AcquireHostDisplay(RenderAPI api)
std::optional<WindowInfo> Host::AcquireRenderWindow(bool recreate_window)
{
Assert(!g_host_display);
std::optional<WindowInfo> wi;
g_nogui_window->ExecuteInMessageLoop([api]() {
if (g_nogui_window->CreatePlatformWindow(GetWindowTitle(System::GetGameTitle())))
g_nogui_window->ExecuteInMessageLoop([&wi, recreate_window]() {
bool res = g_nogui_window->HasPlatformWindow();
if (!res || recreate_window)
{
const std::optional<WindowInfo> wi(g_nogui_window->GetPlatformWindowInfo());
if (wi.has_value())
{
g_host_display = Host::CreateDisplayForAPI(api);
if (g_host_display && !g_host_display->CreateDevice(wi.value(), System::ShouldUseVSync()))
g_host_display.reset();
}
if (g_host_display)
g_host_display->DoneCurrent();
else
if (res)
g_nogui_window->DestroyPlatformWindow();
}
s_host_display_created_or_destroyed.Post();
res = g_nogui_window->CreatePlatformWindow(NoGUIHost::GetWindowTitle(System::GetGameTitle()));
}
if (res)
wi = g_nogui_window->GetPlatformWindowInfo();
s_platform_window_updated.Post();
});
s_host_display_created_or_destroyed.Wait();
s_platform_window_updated.Wait();
if (!g_host_display)
if (!wi.has_value())
{
g_nogui_window->ReportError("Error", "Failed to create host display.");
return false;
}
if (!g_host_display->MakeCurrent() || !g_host_display->SetupDevice() || !ImGuiManager::Initialize() ||
!CommonHost::CreateHostDisplayResources())
{
ImGuiManager::Shutdown();
CommonHost::ReleaseHostDisplayResources();
g_host_display.reset();
g_nogui_window->DestroyPlatformWindow();
return false;
}
if (!FullscreenUI::Initialize())
{
g_nogui_window->ReportError("Error", "Failed to initialize fullscreen UI");
ReleaseHostDisplay();
return false;
g_nogui_window->ReportError("Error", "Failed to create render window.");
return std::nullopt;
}
// reload input sources, since it might use the window handle
@@ -714,44 +692,18 @@ bool NoGUIHost::AcquireHostDisplay(RenderAPI api)
auto lock = Host::GetSettingsLock();
InputManager::ReloadSources(*Host::GetSettingsInterface(), lock);
}
return true;
return wi;
}
bool Host::AcquireHostDisplay(RenderAPI api)
void Host::ReleaseRenderWindow()
{
if (g_host_display && g_host_display->GetRenderAPI() == api)
{
// current is fine
return true;
}
// otherwise we need to switch
NoGUIHost::ReleaseHostDisplay();
return NoGUIHost::AcquireHostDisplay(api);
}
void NoGUIHost::ReleaseHostDisplay()
{
if (!g_host_display)
return;
// close input sources, since it might use the window handle
InputManager::CloseSources();
CommonHost::ReleaseHostDisplayResources();
FullscreenUI::Shutdown();
ImGuiManager::Shutdown();
g_host_display.reset();
// Need to block here, otherwise the recreation message associates with the old window.
g_nogui_window->ExecuteInMessageLoop([]() {
g_nogui_window->DestroyPlatformWindow();
s_host_display_created_or_destroyed.Post();
s_platform_window_updated.Post();
});
s_host_display_created_or_destroyed.Wait();
}
void Host::ReleaseHostDisplay()
{
// we keep the fsui going, so no need to do anything here
s_platform_window_updated.Wait();
}
void Host::OnSystemStarting()
@@ -786,38 +738,10 @@ void Host::OnSystemDestroyed()
Log_VerbosePrintf("Host::OnSystemDestroyed()");
}
void Host::InvalidateDisplay()
void Host::BeginPresentFrame()
{
RenderDisplay(false);
}
void Host::RenderDisplay(bool skip_present)
{
// acquire for IO.MousePos.
std::atomic_thread_fence(std::memory_order_acquire);
if (!skip_present)
{
FullscreenUI::Render();
ImGuiManager::RenderTextOverlays();
ImGuiManager::RenderOSDMessages();
}
// Debug windows are always rendered, otherwise mouse input breaks on skip.
ImGuiManager::RenderOverlayWindows();
ImGuiManager::RenderDebugWindows();
g_host_display->Render(skip_present);
ImGuiManager::NewFrame();
}
// void Host::ResizeHostDisplay(u32 new_window_width, u32 new_window_height, float new_window_scale)
// {
// s_host_display->ResizeRenderWindow(new_window_width, new_window_height, new_window_scale);
// ImGuiManager::WindowResized();
// }
void Host::RequestResizeHostDisplay(s32 width, s32 height)
{
g_nogui_window->RequestRenderWindowSize(width, height);
@@ -879,7 +803,7 @@ std::unique_ptr<NoGUIPlatform> NoGUIHost::CreatePlatform()
#if defined(_WIN32)
ret = NoGUIPlatform::CreateWin32Platform();
#elif defined(__APPLE__)
// nothing yet
ret = NoGUIPlatform::CreateCocoaPlatform();
#else
// linux
const char* platform = std::getenv("DUCKSTATION_NOGUI_PLATFORM");
@@ -891,10 +815,6 @@ std::unique_ptr<NoGUIPlatform> NoGUIHost::CreatePlatform()
if (!ret && (!platform || StringUtil::Strcasecmp(platform, "x11") == 0) && std::getenv("DISPLAY"))
ret = NoGUIPlatform::CreateX11Platform();
#endif
#ifdef NOGUI_PLATFORM_VTY
if (!ret && (!platform || StringUtil::Strcasecmp(platform, "vty") == 0))
ret = NoGUIPlatform::CreateVTYPlatform();
#endif
#endif
return ret;

View File

@@ -3,7 +3,7 @@
#pragma once
#include "common/types.h"
#include "core/system.h"
#include <functional>
#include <string>
@@ -12,9 +12,6 @@ namespace NoGUIHost {
bool InBatchMode();
void SetBatchMode(bool enabled);
/// Starts the virtual machine.
void StartSystem(SystemBootParameters params);
/// Returns the application name and version, optionally including debug/devel config indicator.
std::string GetAppNameAndVersion();
@@ -36,4 +33,4 @@ 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
} // namespace NoGUIHost

View File

@@ -3,8 +3,10 @@
#pragma once
#include "util/gpu_device.h"
#include "common/types.h"
#include "util/host_display.h"
#include <functional>
#include <memory>
#include <optional>
@@ -24,6 +26,7 @@ public:
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;
@@ -46,20 +49,19 @@ public:
#ifdef _WIN32
static std::unique_ptr<NoGUIPlatform> CreateWin32Platform();
#endif
#ifdef __APPLE__
static std::unique_ptr<NoGUIPlatform> CreateCocoaPlatform();
#endif
#ifdef NOGUI_PLATFORM_WAYLAND
static std::unique_ptr<NoGUIPlatform> CreateWaylandPlatform();
#endif
#ifdef NOGUI_PLATFORM_X11
static std::unique_ptr<NoGUIPlatform> CreateX11Platform();
#endif
#ifdef NOGUI_PLATFORM_VTY
static std::unique_ptr<NoGUIPlatform> CreateVTYPlatform();
#endif
protected:
static constexpr s32 DEFAULT_WINDOW_WIDTH = 1280;
static constexpr s32 DEFAULT_WINDOW_HEIGHT = 720;
};
extern std::unique_ptr<NoGUIPlatform> g_nogui_window;
extern std::unique_ptr<NoGUIPlatform> g_nogui_window;

View File

@@ -1,281 +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/string.h"
#include "common/types.h"
#include <array>
#include <cstring>
#include <linux/input-event-codes.h>
#include <map>
#include <optional>
#include <string_view>
namespace VTYKeyNames {
static const std::map<int, const char*> s_evdev_key_names = {{KEY_ESC, "Escape"},
{KEY_1, "1"},
{KEY_2, "2"},
{KEY_3, "3"},
{KEY_4, "4"},
{KEY_5, "5"},
{KEY_6, "6"},
{KEY_7, "7"},
{KEY_8, "8"},
{KEY_9, "9"},
{KEY_0, "0"},
{KEY_MINUS, "Minus"},
{KEY_EQUAL, "Equal"},
{KEY_BACKSPACE, "Backspace"},
{KEY_TAB, "Tab"},
{KEY_Q, "Q"},
{KEY_W, "W"},
{KEY_E, "E"},
{KEY_R, "R"},
{KEY_T, "T"},
{KEY_Y, "Y"},
{KEY_U, "U"},
{KEY_I, "I"},
{KEY_O, "O"},
{KEY_P, "P"},
{KEY_LEFTBRACE, "Leftbrace"},
{KEY_RIGHTBRACE, "Rightbrace"},
{KEY_ENTER, "Return"},
{KEY_LEFTCTRL, "Leftctrl"},
{KEY_A, "A"},
{KEY_S, "S"},
{KEY_D, "D"},
{KEY_F, "F"},
{KEY_G, "G"},
{KEY_H, "H"},
{KEY_J, "J"},
{KEY_K, "K"},
{KEY_L, "L"},
{KEY_SEMICOLON, "Semicolon"},
{KEY_APOSTROPHE, "Apostrophe"},
{KEY_GRAVE, "Grave"},
{KEY_LEFTSHIFT, "Leftshift"},
{KEY_BACKSLASH, "Backslash"},
{KEY_Z, "Z"},
{KEY_X, "X"},
{KEY_C, "C"},
{KEY_V, "V"},
{KEY_B, "B"},
{KEY_N, "N"},
{KEY_M, "M"},
{KEY_COMMA, "Comma"},
{KEY_DOT, "Dot"},
{KEY_SLASH, "Slash"},
{KEY_RIGHTSHIFT, "Rightshift"},
{KEY_KPASTERISK, "Kpasterisk"},
{KEY_LEFTALT, "Leftalt"},
{KEY_SPACE, "Space"},
{KEY_CAPSLOCK, "Capslock"},
{KEY_F1, "F1"},
{KEY_F2, "F2"},
{KEY_F3, "F3"},
{KEY_F4, "F4"},
{KEY_F5, "F5"},
{KEY_F6, "F6"},
{KEY_F7, "F7"},
{KEY_F8, "F8"},
{KEY_F9, "F9"},
{KEY_F10, "F10"},
{KEY_NUMLOCK, "Numlock"},
{KEY_SCROLLLOCK, "Scrolllock"},
{KEY_KP7, "Kp7"},
{KEY_KP8, "Kp8"},
{KEY_KP9, "Kp9"},
{KEY_KPMINUS, "Kpminus"},
{KEY_KP4, "Kp4"},
{KEY_KP5, "Kp5"},
{KEY_KP6, "Kp6"},
{KEY_KPPLUS, "Kpplus"},
{KEY_KP1, "Kp1"},
{KEY_KP2, "Kp2"},
{KEY_KP3, "Kp3"},
{KEY_KP0, "Kp0"},
{KEY_KPDOT, "Kpdot"},
{KEY_ZENKAKUHANKAKU, "Zenkakuhankaku"},
{KEY_102ND, "102nd"},
{KEY_F11, "F11"},
{KEY_F12, "F12"},
{KEY_RO, "Ro"},
{KEY_KATAKANA, "Katakana"},
{KEY_HIRAGANA, "Hiragana"},
{KEY_HENKAN, "Henkan"},
{KEY_KATAKANAHIRAGANA, "Katakanahiragana"},
{KEY_MUHENKAN, "Muhenkan"},
{KEY_KPJPCOMMA, "Kpjpcomma"},
{KEY_KPENTER, "Kpenter"},
{KEY_RIGHTCTRL, "Rightctrl"},
{KEY_KPSLASH, "Kpslash"},
{KEY_SYSRQ, "Sysrq"},
{KEY_RIGHTALT, "RightAlt"},
{KEY_LINEFEED, "Linefeed"},
{KEY_HOME, "Home"},
{KEY_UP, "Up"},
{KEY_PAGEUP, "PageUp"},
{KEY_LEFT, "Left"},
{KEY_RIGHT, "Right"},
{KEY_END, "End"},
{KEY_DOWN, "Down"},
{KEY_PAGEDOWN, "PageDown"},
{KEY_INSERT, "Insert"},
{KEY_DELETE, "Delete"},
{KEY_MACRO, "Macro"},
{KEY_MUTE, "Mute"},
{KEY_VOLUMEDOWN, "VolumeDown"},
{KEY_VOLUMEUP, "VolumeUp"},
{KEY_POWER, "Power"},
{KEY_KPEQUAL, "Kpequal"},
{KEY_KPPLUSMINUS, "Kpplusminus"},
{KEY_PAUSE, "Pause"},
{KEY_SCALE, "Scale"},
{KEY_KPCOMMA, "Kpcomma"},
{KEY_HANGEUL, "Hangeul"},
{KEY_HANGUEL, "Hanguel"},
{KEY_HANJA, "Hanja"},
{KEY_YEN, "Yen"},
{KEY_LEFTMETA, "Leftmeta"},
{KEY_RIGHTMETA, "Rightmeta"},
{KEY_COMPOSE, "Compose"},
{KEY_STOP, "Stop"},
{KEY_AGAIN, "Again"},
{KEY_PROPS, "Props"},
{KEY_UNDO, "Undo"},
{KEY_FRONT, "Front"},
{KEY_COPY, "Copy"},
{KEY_OPEN, "Open"},
{KEY_PASTE, "Paste"},
{KEY_FIND, "Find"},
{KEY_CUT, "Cut"},
{KEY_HELP, "Help"},
{KEY_MENU, "Menu"},
{KEY_CALC, "Calc"},
{KEY_SETUP, "Setup"},
{KEY_SLEEP, "Sleep"},
{KEY_WAKEUP, "Wakeup"},
{KEY_FILE, "File"},
{KEY_SENDFILE, "Sendfile"},
{KEY_DELETEFILE, "Deletefile"},
{KEY_XFER, "Xfer"},
{KEY_PROG1, "Prog1"},
{KEY_PROG2, "Prog2"},
{KEY_WWW, "Www"},
{KEY_MSDOS, "Msdos"},
{KEY_COFFEE, "Coffee"},
{KEY_SCREENLOCK, "Screenlock"},
{KEY_ROTATE_DISPLAY, "Rotate_display"},
{KEY_DIRECTION, "Direction"},
{KEY_CYCLEWINDOWS, "Cyclewindows"},
{KEY_MAIL, "Mail"},
{KEY_BOOKMARKS, "Bookmarks"},
{KEY_COMPUTER, "Computer"},
{KEY_BACK, "Back"},
{KEY_FORWARD, "Forward"},
{KEY_CLOSECD, "Closecd"},
{KEY_EJECTCD, "Ejectcd"},
{KEY_EJECTCLOSECD, "Ejectclosecd"},
{KEY_NEXTSONG, "Nextsong"},
{KEY_PLAYPAUSE, "Playpause"},
{KEY_PREVIOUSSONG, "Previoussong"},
{KEY_STOPCD, "Stopcd"},
{KEY_RECORD, "Record"},
{KEY_REWIND, "Rewind"},
{KEY_PHONE, "Phone"},
{KEY_ISO, "Iso"},
{KEY_CONFIG, "Config"},
{KEY_HOMEPAGE, "Homepage"},
{KEY_REFRESH, "Refresh"},
{KEY_EXIT, "Exit"},
{KEY_MOVE, "Move"},
{KEY_EDIT, "Edit"},
{KEY_SCROLLUP, "Scrollup"},
{KEY_SCROLLDOWN, "Scrolldown"},
{KEY_KPLEFTPAREN, "Kpleftparen"},
{KEY_KPRIGHTPAREN, "Kprightparen"},
{KEY_NEW, "New"},
{KEY_REDO, "Redo"},
{KEY_F13, "F13"},
{KEY_F14, "F14"},
{KEY_F15, "F15"},
{KEY_F16, "F16"},
{KEY_F17, "F17"},
{KEY_F18, "F18"},
{KEY_F19, "F19"},
{KEY_F20, "F20"},
{KEY_F21, "F21"},
{KEY_F22, "F22"},
{KEY_F23, "F23"},
{KEY_F24, "F24"},
{KEY_PLAYCD, "Playcd"},
{KEY_PAUSECD, "Pausecd"},
{KEY_PROG3, "Prog3"},
{KEY_PROG4, "Prog4"},
{KEY_DASHBOARD, "Dashboard"},
{KEY_SUSPEND, "Suspend"},
{KEY_CLOSE, "Close"},
{KEY_PLAY, "Play"},
{KEY_FASTFORWARD, "Fastforward"},
{KEY_BASSBOOST, "Bassboost"},
{KEY_PRINT, "Print"},
{KEY_HP, "Hp"},
{KEY_CAMERA, "Camera"},
{KEY_SOUND, "Sound"},
{KEY_QUESTION, "Question"},
{KEY_EMAIL, "Email"},
{KEY_CHAT, "Chat"},
{KEY_SEARCH, "Search"},
{KEY_CONNECT, "Connect"},
{KEY_FINANCE, "Finance"},
{KEY_SPORT, "Sport"},
{KEY_SHOP, "Shop"},
{KEY_ALTERASE, "Alterase"},
{KEY_CANCEL, "Cancel"},
{KEY_BRIGHTNESSDOWN, "Brightnessdown"},
{KEY_BRIGHTNESSUP, "Brightnessup"},
{KEY_MEDIA, "Media"},
{KEY_SWITCHVIDEOMODE, "Switchvideomode"},
{KEY_KBDILLUMTOGGLE, "Kbdillumtoggle"},
{KEY_KBDILLUMDOWN, "Kbdillumdown"},
{KEY_KBDILLUMUP, "Kbdillumup"},
{KEY_SEND, "Send"},
{KEY_REPLY, "Reply"},
{KEY_FORWARDMAIL, "Forwardmail"},
{KEY_SAVE, "Save"},
{KEY_DOCUMENTS, "Documents"},
{KEY_BATTERY, "Battery"},
{KEY_BLUETOOTH, "Bluetooth"},
{KEY_WLAN, "Wlan"},
{KEY_UWB, "Uwb"},
{KEY_UNKNOWN, "Unknown"},
{KEY_VIDEO_NEXT, "Video_next"},
{KEY_VIDEO_PREV, "Video_prev"},
{KEY_BRIGHTNESS_CYCLE, "Brightness_cycle"},
{KEY_BRIGHTNESS_AUTO, "Brightness_auto"},
{KEY_BRIGHTNESS_ZERO, "Brightness_zero"},
{KEY_DISPLAY_OFF, "Display_off"},
{KEY_WWAN, "Wwan"},
{KEY_WIMAX, "Wimax"},
{KEY_RFKILL, "Rfkill"},
{KEY_MICMUTE, "Micmute"}};
static inline const char* GetKeyName(int key)
{
const auto it = s_evdev_key_names.find(key);
return it == s_evdev_key_names.end() ? nullptr : it->second;
}
static inline std::optional<int> GetKeyCodeForName(const std::string_view key_name)
{
for (const auto& it : s_evdev_key_names)
{
if (key_name == it.second)
return it.first;
}
return std::nullopt;
}
} // namespace VTYKeyNames

View File

@@ -1,248 +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 "vty_nogui_platform.h"
#include "common/log.h"
#include "common/string_util.h"
#include "common/threading.h"
#include "core/host.h"
#include "core/host_settings.h"
#include "nogui_host.h"
#include "resource.h"
#include "vty_key_names.h"
#include <fcntl.h>
#include <linux/input-event-codes.h>
#include <thread>
#include <unistd.h>
Log_SetChannel(VTYNoGUIPlatform);
#ifdef WITH_DRMKMS
#include "common/drm_display.h"
#endif
VTYNoGUIPlatform::VTYNoGUIPlatform()
{
m_message_loop_running.store(true, std::memory_order_release);
}
VTYNoGUIPlatform::~VTYNoGUIPlatform()
{
CloseEVDevFDs();
}
std::unique_ptr<NoGUIPlatform> NoGUIPlatform::CreateVTYPlatform()
{
std::unique_ptr<VTYNoGUIPlatform> platform(std::make_unique<VTYNoGUIPlatform>());
if (!platform->Initialize())
platform.reset();
return platform;
}
bool VTYNoGUIPlatform::Initialize()
{
OpenEVDevFDs();
return true;
}
void VTYNoGUIPlatform::ReportError(const std::string_view& title, const std::string_view& message)
{
// not implemented
}
bool VTYNoGUIPlatform::ConfirmMessage(const std::string_view& title, const std::string_view& message)
{
// not implemented
return true;
}
void VTYNoGUIPlatform::SetDefaultConfig(SettingsInterface& si)
{
// noop
}
bool VTYNoGUIPlatform::CreatePlatformWindow(std::string title)
{
return true;
}
void VTYNoGUIPlatform::DestroyPlatformWindow()
{
// noop
}
std::optional<WindowInfo> VTYNoGUIPlatform::GetPlatformWindowInfo()
{
WindowInfo wi;
wi.type = WindowInfo::Type::Display;
wi.surface_width = 0;
wi.surface_height = 0;
wi.surface_refresh_rate = 0.0f;
wi.surface_format = WindowInfo::SurfaceFormat::Auto;
const std::string fullscreen_mode = Host::GetStringSettingValue("GPU", "FullscreenMode", "");
if (!fullscreen_mode.empty())
{
if (!HostDisplay::ParseFullscreenMode(fullscreen_mode, &wi.surface_width, &wi.surface_height,
&wi.surface_refresh_rate))
{
Log_ErrorPrintf("Failed to parse fullscreen mode '%s'", fullscreen_mode.c_str());
}
}
#ifdef WITH_DRMKMS
// set to current mode
if (wi.surface_width == 0)
{
if (!DRMDisplay::GetCurrentMode(&wi.surface_width, &wi.surface_height, &wi.surface_refresh_rate))
Log_ErrorPrintf("Failed to get current mode, will use default.");
}
#endif
// This isn't great, but it's an approximation at least..
if (wi.surface_width > 0)
wi.surface_scale = std::max(0.1f, static_cast<float>(wi.surface_width) / 1280.0f);
return wi;
}
void VTYNoGUIPlatform::SetFullscreen(bool enabled)
{
// already fullscreen :-)
}
bool VTYNoGUIPlatform::RequestRenderWindowSize(s32 new_window_width, s32 new_window_height)
{
return false;
}
bool VTYNoGUIPlatform::OpenURL(const std::string_view& url)
{
Log_ErrorPrintf("VTYNoGUIPlatform::OpenURL() not implemented: %.*s", static_cast<int>(url.size()), url.data());
return false;
}
bool VTYNoGUIPlatform::CopyTextToClipboard(const std::string_view& text)
{
Log_ErrorPrintf("VTYNoGUIPlatform::CopyTextToClipboard() not implemented: %.*s", static_cast<int>(text.size()),
text.data());
return false;
}
void VTYNoGUIPlatform::SetPlatformWindowTitle(std::string title)
{
Log_InfoPrintf("Window Title: %s", title.c_str());
}
void VTYNoGUIPlatform::RunMessageLoop()
{
while (m_message_loop_running.load(std::memory_order_acquire))
{
PollEvDevKeyboards();
{
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 VTYNoGUIPlatform::ExecuteInMessageLoop(std::function<void()> func)
{
std::unique_lock lock(m_callback_queue_mutex);
m_callback_queue.push_back(std::move(func));
}
void VTYNoGUIPlatform::QuitMessageLoop()
{
m_message_loop_running.store(false, std::memory_order_release);
}
void VTYNoGUIPlatform::OpenEVDevFDs()
{
for (int i = 0; i < 1000; i++)
{
TinyString path;
path.Format("/dev/input/event%d", i);
int fd = open(path, O_RDONLY | O_NONBLOCK);
if (fd < 0)
break;
struct libevdev* obj;
if (libevdev_new_from_fd(fd, &obj) != 0)
{
Log_ErrorPrintf("libevdev_new_from_fd(%s) failed", path.GetCharArray());
close(fd);
continue;
}
Log_DevPrintf("Input path: %s", path.GetCharArray());
Log_DevPrintf("Input device name: \"%s\"", libevdev_get_name(obj));
Log_DevPrintf("Input device ID: bus %#x vendor %#x product %#x", libevdev_get_id_bustype(obj),
libevdev_get_id_vendor(obj), libevdev_get_id_product(obj));
if (!libevdev_has_event_code(obj, EV_KEY, KEY_SPACE))
{
Log_DevPrintf("This device does not look like a keyboard");
libevdev_free(obj);
close(fd);
continue;
}
const int grab_res = libevdev_grab(obj, LIBEVDEV_GRAB);
if (grab_res != 0)
Log_WarningPrintf("Failed to grab '%s' (%s): %d", libevdev_get_name(obj), path.GetCharArray(), grab_res);
m_evdev_keyboards.push_back({obj, fd});
}
}
void VTYNoGUIPlatform::CloseEVDevFDs()
{
for (const EvDevKeyboard& kb : m_evdev_keyboards)
{
libevdev_grab(kb.obj, LIBEVDEV_UNGRAB);
libevdev_free(kb.obj);
close(kb.fd);
}
m_evdev_keyboards.clear();
}
void VTYNoGUIPlatform::PollEvDevKeyboards()
{
for (const EvDevKeyboard& kb : m_evdev_keyboards)
{
struct input_event ev;
while (libevdev_next_event(kb.obj, LIBEVDEV_READ_FLAG_NORMAL, &ev) == 0)
{
// auto-repeat
// TODO: forward char to imgui
if (ev.value == 2)
continue;
const bool pressed = (ev.value == 1);
NoGUIHost::ProcessPlatformKeyEvent(static_cast<s32>(ev.code), pressed);
}
}
}
std::optional<u32> VTYNoGUIPlatform::ConvertHostKeyboardStringToCode(const std::string_view& str)
{
std::optional<int> converted(VTYKeyNames::GetKeyCodeForName(str));
return converted.has_value() ? std::optional<u32>(static_cast<u32>(converted.value())) : std::nullopt;
}
std::optional<std::string> VTYNoGUIPlatform::ConvertHostKeyboardCodeToString(u32 code)
{
const char* keyname = VTYKeyNames::GetKeyName(static_cast<int>(code));
return keyname ? std::optional<std::string>(std::string(keyname)) : std::nullopt;
}

View File

@@ -150,6 +150,11 @@ bool WaylandNoGUIPlatform::CreatePlatformWindow(std::string title)
return true;
}
bool WaylandNoGUIPlatform::HasPlatformWindow() const
{
return (m_surface != nullptr);
}
void WaylandNoGUIPlatform::DestroyPlatformWindow()
{
m_window_info = {};

View File

@@ -29,6 +29,7 @@ public:
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;

View File

@@ -134,6 +134,11 @@ bool Win32NoGUIPlatform::CreatePlatformWindow(std::string title)
return true;
}
bool Win32NoGUIPlatform::HasPlatformWindow() const
{
return (m_hwnd != NULL);
}
void Win32NoGUIPlatform::DestroyPlatformWindow()
{
if (!m_hwnd)

View File

@@ -23,6 +23,7 @@ public:
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;

View File

@@ -99,6 +99,11 @@ bool X11NoGUIPlatform::CreatePlatformWindow(std::string title)
return true;
}
bool X11NoGUIPlatform::HasPlatformWindow() const
{
return m_window != 0;
}
void X11NoGUIPlatform::DestroyPlatformWindow()
{
m_window_info = {};

View File

@@ -48,6 +48,7 @@ public:
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;