#include "vty_host_interface.h" #include "common/log.h" #include "common/string_util.h" #include "evdev_key_names.h" #include "frontend-common/ini_settings_interface.h" #include "imgui.h" #include #include #include #include Log_SetChannel(VTYHostInterface); VTYHostInterface::VTYHostInterface() = default; VTYHostInterface::~VTYHostInterface() { CloseEVDevFDs(); } std::unique_ptr VTYHostInterface::Create() { return std::make_unique(); } bool VTYHostInterface::Initialize() { if (!NoGUIHostInterface::Initialize()) return false; OpenEVDevFDs(); signal(SIGTERM, SIGTERMHandler); signal(SIGINT, SIGTERMHandler); signal(SIGQUIT, SIGTERMHandler); return true; } void VTYHostInterface::Shutdown() { CloseEVDevFDs(); NoGUIHostInterface::Shutdown(); } bool VTYHostInterface::IsFullscreen() const { return true; } bool VTYHostInterface::SetFullscreen(bool enabled) { return enabled; } void VTYHostInterface::FixIncompatibleSettings(bool display_osd_messages) { NoGUIHostInterface::FixIncompatibleSettings(display_osd_messages); // Some things we definitely don't want. g_settings.confim_power_off = false; } bool VTYHostInterface::CreatePlatformWindow(bool fullscreen) { SetImGuiKeyMap(); return true; } void VTYHostInterface::DestroyPlatformWindow() { // nothing to destroy, it's all in the context } std::optional VTYHostInterface::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 = m_settings_interface->GetStringValue("GPU", "FullscreenMode", ""); if (!fullscreen_mode.empty()) { if (!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()); } } return wi; } void VTYHostInterface::PollAndUpdate() { PollEvDevKeyboards(); NoGUIHostInterface::PollAndUpdate(); } void VTYHostInterface::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 VTYHostInterface::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 VTYHostInterface::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 if (ev.value == 2) continue; const bool pressed = (ev.value == 1); const HostKeyCode code = static_cast(ev.code); if (static_cast(code) < countof(ImGuiIO::KeysDown)) ImGui::GetIO().KeysDown[code] = pressed; HandleHostKeyEvent(code, pressed); } } } void VTYHostInterface::SetImGuiKeyMap() { ImGuiIO& io = ImGui::GetIO(); io.KeyMap[ImGuiKey_Tab] = KEY_TAB; io.KeyMap[ImGuiKey_LeftArrow] = KEY_LEFT; io.KeyMap[ImGuiKey_RightArrow] = KEY_RIGHT; io.KeyMap[ImGuiKey_UpArrow] = KEY_UP; io.KeyMap[ImGuiKey_DownArrow] = KEY_DOWN; io.KeyMap[ImGuiKey_PageUp] = KEY_PAGEUP; io.KeyMap[ImGuiKey_PageDown] = KEY_PAGEDOWN; io.KeyMap[ImGuiKey_Home] = KEY_HOME; io.KeyMap[ImGuiKey_End] = KEY_END; io.KeyMap[ImGuiKey_Insert] = KEY_INSERT; io.KeyMap[ImGuiKey_Delete] = KEY_DELETE; io.KeyMap[ImGuiKey_Backspace] = KEY_BACKSPACE; io.KeyMap[ImGuiKey_Space] = KEY_SPACE; io.KeyMap[ImGuiKey_Enter] = KEY_ENTER; io.KeyMap[ImGuiKey_Escape] = KEY_ESC; io.KeyMap[ImGuiKey_KeyPadEnter] = KEY_KPENTER; io.KeyMap[ImGuiKey_A] = KEY_A; io.KeyMap[ImGuiKey_C] = KEY_C; io.KeyMap[ImGuiKey_V] = KEY_V; io.KeyMap[ImGuiKey_X] = KEY_X; io.KeyMap[ImGuiKey_Y] = KEY_Y; io.KeyMap[ImGuiKey_Z] = KEY_Z; } std::optional VTYHostInterface::GetHostKeyCode(const std::string_view key_code) const { std::optional kc = EvDevKeyNames::GetKeyCodeForName(key_code); if (!kc.has_value()) return std::nullopt; return static_cast(kc.value()); } void VTYHostInterface::SIGTERMHandler(int sig) { Log_InfoPrintf("Recieved SIGTERM"); static_cast(g_host_interface)->m_quit_request = true; signal(sig, SIG_DFL); }