diff --git a/Userland/Services/WindowServer/CMakeLists.txt b/Userland/Services/WindowServer/CMakeLists.txt index 154beb9ad8..f412145ea4 100644 --- a/Userland/Services/WindowServer/CMakeLists.txt +++ b/Userland/Services/WindowServer/CMakeLists.txt @@ -36,6 +36,7 @@ set(SOURCES WindowManagerServerEndpoint.h WindowManagerClientEndpoint.h WMClientConnection.cpp + KeymapSwitcher.cpp ) serenity_bin(WindowServer) diff --git a/Userland/Services/WindowServer/KeymapSwitcher.cpp b/Userland/Services/WindowServer/KeymapSwitcher.cpp new file mode 100644 index 0000000000..e34fdb3e0c --- /dev/null +++ b/Userland/Services/WindowServer/KeymapSwitcher.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021, Timur Sultanov + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include + +namespace WindowServer { + +static KeymapSwitcher* s_the; + +KeymapSwitcher& KeymapSwitcher::the() +{ + VERIFY(s_the); + return *s_the; +} + +KeymapSwitcher::KeymapSwitcher() +{ + s_the = this; +} + +KeymapSwitcher::~KeymapSwitcher() +{ +} + +void KeymapSwitcher::refresh() +{ + m_keymaps.clear(); + + //TODO: load keymaps from file + m_keymaps.append("en-us"); + m_keymaps.append("ru"); +} + +void KeymapSwitcher::next_keymap() +{ + refresh(); + + if (m_keymaps.is_empty()) { + dbgln("No keymaps loaded - leaving system keymap unchanged"); + return; // TODO: figure out what to do when there is no keymap configured + } + + auto current_keymap_name = get_current_keymap(); + + dbgln("Current system keymap: {}", current_keymap_name); + + auto it = m_keymaps.find_if([&](const auto& enumerator) { + return enumerator == current_keymap_name; + }); + + if (it.is_end()) { + auto first_keymap = m_keymaps.first(); + dbgln("Cannot find current keymap in the keymap list - setting first available ({})", first_keymap); + setkeymap(first_keymap); + } else { + it++; + + if (it.is_end()) { + it = m_keymaps.begin(); + } + + dbgln("Setting system keymap to: {}", *it); + setkeymap(*it); + } +} + +String KeymapSwitcher::get_current_keymap() const +{ + auto proc_keymap = Core::File::construct("/proc/keymap"); + if (!proc_keymap->open(Core::OpenMode::ReadOnly)) + VERIFY_NOT_REACHED(); + + auto json = JsonValue::from_string(proc_keymap->read_all()).release_value_but_fixme_should_propagate_errors(); + auto const& keymap_object = json.as_object(); + VERIFY(keymap_object.has("keymap")); + auto keymap = keymap_object.get("keymap").to_string(); + dbgln("Current keymap is: {}", keymap); + + return keymap; +} + +void KeymapSwitcher::setkeymap(const AK::String& keymap) +{ + pid_t child_pid; + const char* argv[] = { "/bin/keymap", keymap.characters(), nullptr }; + if ((errno = posix_spawn(&child_pid, "/bin/keymap", nullptr, nullptr, const_cast(argv), environ))) { + perror("posix_spawn"); + dbgln("Failed to call /bin/keymap, error: {} ({})", errno, strerror(errno)); + } +} + +} diff --git a/Userland/Services/WindowServer/KeymapSwitcher.h b/Userland/Services/WindowServer/KeymapSwitcher.h new file mode 100644 index 0000000000..c9f6a0b77c --- /dev/null +++ b/Userland/Services/WindowServer/KeymapSwitcher.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021, Timur Sultanov + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace WindowServer { + +class KeymapSwitcher final : public Core::Object { + C_OBJECT(KeymapSwitcher) +public: + static KeymapSwitcher& the(); + + virtual ~KeymapSwitcher() override; + + void refresh(); + + void next_keymap(); + +private: + KeymapSwitcher(); + + Vector m_keymaps; + + void setkeymap(AK::String const&); + String get_current_keymap() const; +}; + +} diff --git a/Userland/Services/WindowServer/WindowManager.cpp b/Userland/Services/WindowServer/WindowManager.cpp index 0739d86c9e..b9b8db61fc 100644 --- a/Userland/Services/WindowServer/WindowManager.cpp +++ b/Userland/Services/WindowServer/WindowManager.cpp @@ -36,6 +36,7 @@ WindowManager& WindowManager::the() WindowManager::WindowManager(Gfx::PaletteImpl const& palette) : m_switcher(WindowSwitcher::construct()) + , m_keymap_switcher(KeymapSwitcher::construct()) , m_palette(palette) { s_the = this; @@ -1557,6 +1558,11 @@ void WindowManager::process_key_event(KeyEvent& event) return; } + if (event.type() == Event::KeyDown && (event.modifiers() == (Mod_Alt | Mod_Shift) && (event.key() == Key_Shift || event.key() == Key_Alt))) { + m_keymap_switcher->next_keymap(); + return; + } + if (event.type() == Event::KeyDown && (event.modifiers() == (Mod_Ctrl | Mod_Alt) || event.modifiers() == (Mod_Ctrl | Mod_Shift | Mod_Alt)) && (window_stack_columns() > 1 || window_stack_rows() > 1)) { auto& current_stack = current_window_stack(); auto row = current_stack.row(); diff --git a/Userland/Services/WindowServer/WindowManager.h b/Userland/Services/WindowServer/WindowManager.h index 4b2eb47d3c..d7f8214222 100644 --- a/Userland/Services/WindowServer/WindowManager.h +++ b/Userland/Services/WindowServer/WindowManager.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -433,6 +434,7 @@ private: u8 m_keyboard_modifiers { 0 }; NonnullRefPtr m_switcher; + NonnullRefPtr m_keymap_switcher; WeakPtr