diff options
author | Timur Sultanov <SultanovTS@yandex.ru> | 2022-01-19 18:20:42 +0300 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-02-03 00:47:22 +0100 |
commit | c7bd47c87c002a7ee9e4484bf21781e0dbc05614 (patch) | |
tree | 70f8b90a2ca05f8b71e2d0a01cd1d6fa74cf4888 /Userland | |
parent | 5a31697bcf44920e50b8f98c7bf1469a959a0900 (diff) | |
download | serenity-c7bd47c87c002a7ee9e4484bf21781e0dbc05614.zip |
Base+WindowsServer+keymap: Store multiple keymaps in a config
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Applications/KeyboardSettings/CMakeLists.txt | 2 | ||||
-rw-r--r-- | Userland/Applications/KeyboardSettings/Keyboard.gml | 55 | ||||
-rw-r--r-- | Userland/Applications/KeyboardSettings/KeyboardSettingsWidget.cpp | 205 | ||||
-rw-r--r-- | Userland/Applications/KeyboardSettings/KeyboardSettingsWidget.h | 17 | ||||
-rw-r--r-- | Userland/Applications/KeyboardSettings/KeymapDialog.gml | 33 | ||||
-rw-r--r-- | Userland/Applications/KeyboardSettings/main.cpp | 8 | ||||
-rw-r--r-- | Userland/Services/KeyboardPreferenceLoader/main.cpp | 6 | ||||
-rw-r--r-- | Userland/Services/WindowServer/KeymapSwitcher.cpp | 41 | ||||
-rw-r--r-- | Userland/Services/WindowServer/KeymapSwitcher.h | 9 | ||||
-rw-r--r-- | Userland/Services/WindowServer/main.cpp | 1 | ||||
-rw-r--r-- | Userland/Utilities/keymap.cpp | 94 |
11 files changed, 388 insertions, 83 deletions
diff --git a/Userland/Applications/KeyboardSettings/CMakeLists.txt b/Userland/Applications/KeyboardSettings/CMakeLists.txt index 8a7ccbc475..83bed80c33 100644 --- a/Userland/Applications/KeyboardSettings/CMakeLists.txt +++ b/Userland/Applications/KeyboardSettings/CMakeLists.txt @@ -5,11 +5,13 @@ serenity_component( ) compile_gml(Keyboard.gml KeyboardWidgetGML.h keyboard_widget_gml) +compile_gml(KeymapDialog.gml KeymapDialogGML.h keymap_dialog_gml) set(SOURCES main.cpp KeyboardSettingsWidget.cpp KeyboardWidgetGML.h + KeymapDialogGML.h ) serenity_app(KeyboardSettings ICON app-keyboard-settings) diff --git a/Userland/Applications/KeyboardSettings/Keyboard.gml b/Userland/Applications/KeyboardSettings/Keyboard.gml index ad1f7d65a7..261ce8513b 100644 --- a/Userland/Applications/KeyboardSettings/Keyboard.gml +++ b/Userland/Applications/KeyboardSettings/Keyboard.gml @@ -8,7 +8,7 @@ @GUI::GroupBox { title: "Mapping" - fixed_height: 200 + fixed_height: 150 layout: @GUI::HorizontalBoxLayout { margins: [16, 8, 8] @@ -25,8 +25,6 @@ fixed_height: 32 icon: "/res/icons/32x32/app-keyboard-mapper.png" } - - @GUI::Widget } @GUI::Widget { @@ -34,23 +32,59 @@ spacing: 2 } + @GUI::ListView { + name: "selected_keymaps" + } + @GUI::Widget { + fixed_height: 24 + layout: @GUI::HorizontalBoxLayout { - spacing: 16 + spacing: 4 } - @GUI::Label { - text: "Character mapping file:" - fixed_width: 130 - text_alignment: "CenterLeft" + @GUI::Button { + name: "add_keymap_button" + text: "Add keymap" } - @GUI::ComboBox { - name: "character_map_file_combo" + @GUI::Button { + name: "remove_keymap_button" + text: "Remove keymap" + enabled: false } } + } + } + + @GUI::GroupBox { + title: "Test input" + + layout: @GUI::HorizontalBoxLayout { + margins: [16, 8, 8] + spacing: 16 + } + + @GUI::Widget { + fixed_width: 32 + layout: @GUI::VerticalBoxLayout { + } + + @GUI::Label { + fixed_width: 32 + fixed_height: 32 + icon: "/res/icons/32x32/app-keyboard-settings.png" + } + } + + @GUI::Widget { + layout: @GUI::VerticalBoxLayout { + spacing: 2 + } @GUI::Widget { + fixed_height: 24 + layout: @GUI::HorizontalBoxLayout { spacing: 16 } @@ -68,7 +102,6 @@ } @GUI::TextEditor { - fixed_height: 100 name: "test_typing_area" } } diff --git a/Userland/Applications/KeyboardSettings/KeyboardSettingsWidget.cpp b/Userland/Applications/KeyboardSettings/KeyboardSettingsWidget.cpp index bc19174744..e3d95390f6 100644 --- a/Userland/Applications/KeyboardSettings/KeyboardSettingsWidget.cpp +++ b/Userland/Applications/KeyboardSettings/KeyboardSettingsWidget.cpp @@ -9,16 +9,126 @@ #include <AK/JsonObject.h> #include <AK/QuickSort.h> #include <Applications/KeyboardSettings/KeyboardWidgetGML.h> +#include <Applications/KeyboardSettings/KeymapDialogGML.h> #include <LibConfig/Client.h> #include <LibCore/DirIterator.h> #include <LibCore/File.h> #include <LibGUI/Application.h> +#include <LibGUI/ComboBox.h> +#include <LibGUI/Dialog.h> #include <LibGUI/ItemListModel.h> #include <LibGUI/Label.h> #include <LibGUI/MessageBox.h> +#include <LibGUI/Model.h> +#include <LibGUI/Widget.h> +#include <LibGUI/Window.h> #include <LibKeyboard/CharacterMap.h> #include <spawn.h> +class KeymapSelectionDialog final : public GUI::Dialog { + C_OBJECT(KeymapSelectionDialog) +public: + virtual ~KeymapSelectionDialog() override {}; + + static String select_keymap(Window* parent_window, Vector<String> const& selected_keymaps) + { + auto dialog = KeymapSelectionDialog::construct(parent_window, selected_keymaps); + dialog->set_title("Add a keymap"); + + if (dialog->exec() == GUI::Dialog::ExecOK) { + return dialog->selected_keymap(); + } + + return String::empty(); + } + + String selected_keymap() { return m_selected_keymap; } + +private: + KeymapSelectionDialog(Window* parent_window, Vector<String> const& selected_keymaps) + : Dialog(parent_window) + { + auto& widget = set_main_widget<GUI::Widget>(); + if (!widget.load_from_gml(keymap_dialog_gml)) + VERIFY_NOT_REACHED(); + + set_resizable(false); + resize(190, 54); + + set_icon(parent_window->icon()); + + Core::DirIterator iterator("/res/keymaps/", Core::DirIterator::Flags::SkipDots); + if (iterator.has_error()) { + GUI::MessageBox::show(nullptr, String::formatted("Error on reading mapping file list: {}", iterator.error_string()), "Keyboard settings", GUI::MessageBox::Type::Error); + GUI::Application::the()->quit(-1); + } + + while (iterator.has_next()) { + auto name = iterator.next_path(); + auto basename = name.replace(".json", ""); + if (!selected_keymaps.find(basename).is_end()) + continue; + m_character_map_files.append(basename); + } + quick_sort(m_character_map_files); + + m_selected_keymap = m_character_map_files.first(); + + m_keymaps_combobox = *widget.find_descendant_of_type_named<GUI::ComboBox>("keymaps_combobox"); + m_keymaps_combobox->set_only_allow_values_from_model(true); + m_keymaps_combobox->set_model(*GUI::ItemListModel<String>::create(m_character_map_files)); + m_keymaps_combobox->set_selected_index(0); + + m_keymaps_combobox->on_change = [&](auto& keymap, auto) { + m_selected_keymap = keymap; + }; + + auto& ok_button = *widget.find_descendant_of_type_named<GUI::Button>("ok_button"); + ok_button.on_click = [this](auto) { + done(Dialog::ExecOK); + }; + + auto& cancel_button = *widget.find_descendant_of_type_named<GUI::Button>("cancel_button"); + cancel_button.on_click = [this](auto) { + done(Dialog::ExecCancel); + }; + } + + RefPtr<GUI::ComboBox> m_keymaps_combobox; + Vector<String> m_character_map_files; + String m_selected_keymap; +}; + +class KeymapModel final : public GUI::Model { +public: + KeymapModel() {}; + + int row_count(GUI::ModelIndex const&) const override { return m_data.size(); } + int column_count(GUI::ModelIndex const&) const override { return 1; } + + GUI::Variant data(GUI::ModelIndex const& index, GUI::ModelRole) const override + { + return m_data.at(index.row()); + } + + void remove_at(size_t index) + { + m_data.remove(index); + invalidate(); + } + + void add_keymap(String const& keymap) + { + m_data.append(keymap); + invalidate(); + } + + Vector<String> const& keymaps() const { return m_data; } + +private: + Vector<String> m_data; +}; + KeyboardSettingsWidget::KeyboardSettingsWidget() { load_from_gml(keyboard_widget_gml); @@ -33,37 +143,51 @@ KeyboardSettingsWidget::KeyboardSettingsWidget() m_current_applied_keymap = keymap_object.get("keymap").to_string(); dbgln("KeyboardSettings thinks the current keymap is: {}", m_current_applied_keymap); - Core::DirIterator iterator("/res/keymaps/", Core::DirIterator::Flags::SkipDots); - if (iterator.has_error()) { - GUI::MessageBox::show(nullptr, String::formatted("Error on reading mapping file list: {}", iterator.error_string()), "Keyboard settings", GUI::MessageBox::Type::Error); - GUI::Application::the()->quit(-1); - } + auto mapper_config(Core::ConfigFile::open("/etc/Keyboard.ini")); + auto keymaps = mapper_config->read_entry("Mapping", "Keymaps", ""); - while (iterator.has_next()) { - auto name = iterator.next_path(); - m_character_map_files.append(name.replace(".json", "")); - } - quick_sort(m_character_map_files); + auto keymaps_vector = keymaps.split(','); - size_t initial_keymap_index = SIZE_MAX; - for (size_t i = 0; i < m_character_map_files.size(); ++i) { - if (m_character_map_files[i].equals_ignoring_case(m_current_applied_keymap)) - initial_keymap_index = i; + m_selected_keymaps_listview = find_descendant_of_type_named<GUI::ListView>("selected_keymaps"); + m_selected_keymaps_listview->horizontal_scrollbar().set_visible(false); + m_selected_keymaps_listview->set_model(adopt_ref(*new KeymapModel())); + auto& keymaps_list_model = static_cast<KeymapModel&>(*m_selected_keymaps_listview->model()); + + for (auto& keymap : keymaps_vector) { + m_initial_keymap_list.append(keymap); + keymaps_list_model.add_keymap(keymap); } - VERIFY(initial_keymap_index < m_character_map_files.size()); - - m_character_map_file_combo = find_descendant_of_type_named<GUI::ComboBox>("character_map_file_combo"); - m_character_map_file_combo->set_only_allow_values_from_model(true); - m_character_map_file_combo->set_model(*GUI::ItemListModel<String>::create(m_character_map_files)); - m_character_map_file_combo->set_selected_index(initial_keymap_index); - // This is a bit of a hack. We set the keymap to the selected one, so that it applies in the testing box below. - // But, we also keep track of the current "applied" keymap, and then revert to that when we exit. - // Ideally, we'd only use the selected keymap for the testing box without making a global system change. - m_character_map_file_combo->on_change = [this](auto& keymap, auto) { - set_keymap(keymap); + + m_add_keymap_button = find_descendant_of_type_named<GUI::Button>("add_keymap_button"); + + m_add_keymap_button->on_click = [&](auto) { + auto keymap = KeymapSelectionDialog::select_keymap(window(), keymaps_list_model.keymaps()); + keymaps_list_model.add_keymap(keymap); + }; + + m_remove_keymap_button = find_descendant_of_type_named<GUI::Button>("remove_keymap_button"); + + m_remove_keymap_button->on_click = [&](auto) { + auto& selection = m_selected_keymaps_listview->selection(); + for (auto& index : selection.indices()) { + keymaps_list_model.remove_at(index.row()); + } + }; + + m_selected_keymaps_listview->on_selection_change = [&]() { + auto& selection = m_selected_keymaps_listview->selection(); + m_remove_keymap_button->set_enabled(!selection.is_empty() && keymaps_list_model.keymaps().size() > 1); + }; + + m_test_typing_area = *find_descendant_of_type_named<GUI::TextEditor>("test_typing_area"); + m_test_typing_area->on_focusin = [&]() { + set_keymaps(keymaps_list_model.keymaps()); + }; + + m_test_typing_area->on_focusout = [&]() { + set_keymaps(m_initial_keymap_list); }; - m_test_typing_area = find_descendant_of_type_named<GUI::TextEditor>("test_typing_area"); m_clear_test_typing_area_button = find_descendant_of_type_named<GUI::Button>("button_clear_test_typing_area"); m_clear_test_typing_area_button->on_click = [this](auto) { m_test_typing_area->clear(); @@ -76,25 +200,36 @@ KeyboardSettingsWidget::KeyboardSettingsWidget() KeyboardSettingsWidget::~KeyboardSettingsWidget() { - set_keymap(m_current_applied_keymap); + set_keymaps(m_initial_keymap_list); +} + +void KeyboardSettingsWidget::window_activated(bool is_active_window) +{ + if (is_active_window && m_test_typing_area->is_focused()) { + auto& keymaps_list_model = static_cast<KeymapModel&>(*m_selected_keymaps_listview->model()); + set_keymaps(keymaps_list_model.keymaps()); + } else { + set_keymaps(m_initial_keymap_list); + } } void KeyboardSettingsWidget::apply_settings() { - String character_map_file = m_character_map_file_combo->text(); - if (character_map_file.is_empty()) { - GUI::MessageBox::show(window(), "Please select character mapping file.", "Keyboard settings", GUI::MessageBox::Type::Error); - return; + auto& m_keymaps_list_model = static_cast<KeymapModel&>(*m_selected_keymaps_listview->model()); + set_keymaps(m_keymaps_list_model.keymaps()); + m_initial_keymap_list.clear(); + for (auto& keymap : m_keymaps_list_model.keymaps()) { + m_initial_keymap_list.append(keymap); } - m_current_applied_keymap = character_map_file; - set_keymap(character_map_file); Config::write_bool("KeyboardSettings", "StartupEnable", "NumLock", m_num_lock_checkbox->is_checked()); } -void KeyboardSettingsWidget::set_keymap(String const& keymap_filename) +void KeyboardSettingsWidget::set_keymaps(Vector<String> const& keymaps) { pid_t child_pid; - const char* argv[] = { "/bin/keymap", keymap_filename.characters(), nullptr }; + + auto keymaps_string = String::join(',', keymaps); + const char* argv[] = { "/bin/keymap", "-s", keymaps_string.characters(), nullptr }; if ((errno = posix_spawn(&child_pid, "/bin/keymap", nullptr, nullptr, const_cast<char**>(argv), environ))) { perror("posix_spawn"); exit(1); diff --git a/Userland/Applications/KeyboardSettings/KeyboardSettingsWidget.h b/Userland/Applications/KeyboardSettings/KeyboardSettingsWidget.h index 9084027354..cadc87f1c2 100644 --- a/Userland/Applications/KeyboardSettings/KeyboardSettingsWidget.h +++ b/Userland/Applications/KeyboardSettings/KeyboardSettingsWidget.h @@ -6,11 +6,13 @@ #pragma once +#include <AK/Vector.h> #include <LibGUI/Button.h> #include <LibGUI/CheckBox.h> -#include <LibGUI/ComboBox.h> +#include <LibGUI/ListView.h> #include <LibGUI/SettingsWindow.h> #include <LibGUI/TextEditor.h> +#include <LibGUI/WindowManagerServerConnection.h> class KeyboardSettingsWidget final : public GUI::SettingsWindow::Tab { C_OBJECT(KeyboardSettingsWidget) @@ -19,16 +21,21 @@ public: virtual void apply_settings() override; + void window_activated(bool is_active_window); + private: KeyboardSettingsWidget(); - void set_keymap(String const& keymap_filename); + void set_keymaps(Vector<String> const& keymaps); + + Vector<String> m_initial_keymap_list; String m_current_applied_keymap; - Vector<String> m_character_map_files; - RefPtr<GUI::ComboBox> m_character_map_file_combo; + RefPtr<GUI::ListView> m_selected_keymaps_listview; + RefPtr<GUI::CheckBox> m_num_lock_checkbox; + RefPtr<GUI::Button> m_add_keymap_button; + RefPtr<GUI::Button> m_remove_keymap_button; RefPtr<GUI::TextEditor> m_test_typing_area; RefPtr<GUI::Button> m_clear_test_typing_area_button; - RefPtr<GUI::CheckBox> m_num_lock_checkbox; }; diff --git a/Userland/Applications/KeyboardSettings/KeymapDialog.gml b/Userland/Applications/KeyboardSettings/KeymapDialog.gml new file mode 100644 index 0000000000..71e1efbe62 --- /dev/null +++ b/Userland/Applications/KeyboardSettings/KeymapDialog.gml @@ -0,0 +1,33 @@ +@GUI::Widget { + fill_with_background_color: true + + layout: @GUI::VerticalBoxLayout { + margins: [4] + } + + @GUI::ComboBox { + name: "keymaps_combobox" + } + + @GUI::Widget { + fixed_height: 24 + + layout: @GUI::HorizontalBoxLayout { + } + + @GUI::Widget { + } + + @GUI::Button { + name: "ok_button" + text: "OK" + fixed_width: 75 + } + + @GUI::Button { + name: "cancel_button" + text: "Cancel" + fixed_width: 75 + } + } +} diff --git a/Userland/Applications/KeyboardSettings/main.cpp b/Userland/Applications/KeyboardSettings/main.cpp index e88765304f..046312eeeb 100644 --- a/Userland/Applications/KeyboardSettings/main.cpp +++ b/Userland/Applications/KeyboardSettings/main.cpp @@ -9,7 +9,6 @@ #include <LibCore/System.h> #include <LibGUI/Application.h> #include <LibGUI/SettingsWindow.h> -#include <LibGUI/WindowServerConnection.h> #include <LibMain/Main.h> // Including this after to avoid LibIPC errors @@ -25,13 +24,18 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) TRY(Core::System::unveil("/res", "r")); TRY(Core::System::unveil("/bin/keymap", "x")); TRY(Core::System::unveil("/proc/keymap", "r")); + TRY(Core::System::unveil("/etc/Keyboard.ini", "r")); TRY(Core::System::unveil(nullptr, nullptr)); auto app_icon = GUI::Icon::default_icon("app-keyboard-settings"); auto window = TRY(GUI::SettingsWindow::create("Keyboard Settings")); window->set_icon(app_icon.bitmap_for_size(16)); - (void)TRY(window->add_tab<KeyboardSettingsWidget>("Keyboard")); + auto keyboard_settings_widget = TRY(window->add_tab<KeyboardSettingsWidget>("Keyboard")); + + window->on_active_window_change = [&](bool is_active_window) { + keyboard_settings_widget->window_activated(is_active_window); + }; window->show(); return app->exec(); diff --git a/Userland/Services/KeyboardPreferenceLoader/main.cpp b/Userland/Services/KeyboardPreferenceLoader/main.cpp index 63bc5b10e0..671dfddfc4 100644 --- a/Userland/Services/KeyboardPreferenceLoader/main.cpp +++ b/Userland/Services/KeyboardPreferenceLoader/main.cpp @@ -24,10 +24,12 @@ ErrorOr<int> serenity_main(Main::Arguments) TRY(Core::System::unveil("/dev/keyboard0", "r")); TRY(Core::System::unveil(nullptr, nullptr)); auto mapper_config(Core::ConfigFile::open("/etc/Keyboard.ini")); - auto keymap = mapper_config->read_entry("Mapping", "Keymap", ""); + auto keymaps = mapper_config->read_entry("Mapping", "Keymaps", ""); + + auto keymaps_vector = keymaps.split(','); pid_t child_pid; - const char* argv[] = { "/bin/keymap", keymap.characters(), nullptr }; + const char* argv[] = { "/bin/keymap", "-m", keymaps_vector.first().characters(), nullptr }; if ((errno = posix_spawn(&child_pid, "/bin/keymap", nullptr, nullptr, const_cast<char**>(argv), environ))) { perror("posix_spawn"); exit(1); diff --git a/Userland/Services/WindowServer/KeymapSwitcher.cpp b/Userland/Services/WindowServer/KeymapSwitcher.cpp index f35543d8f7..3de9a8c373 100644 --- a/Userland/Services/WindowServer/KeymapSwitcher.cpp +++ b/Userland/Services/WindowServer/KeymapSwitcher.cpp @@ -5,6 +5,7 @@ */ #include <AK/JsonObject.h> +#include <LibCore/ConfigFile.h> #include <LibCore/File.h> #include <WindowServer/KeymapSwitcher.h> #include <spawn.h> @@ -14,6 +15,15 @@ namespace WindowServer { KeymapSwitcher::KeymapSwitcher() { + m_file_watcher = MUST(Core::FileWatcher::create()); + + m_file_watcher->on_change = [this](auto&) { + refresh(); + }; + + MUST(m_file_watcher->add_watch(m_keyboard_config, Core::FileWatcherEvent::Type::ContentModified)); + + refresh(); } KeymapSwitcher::~KeymapSwitcher() @@ -24,15 +34,34 @@ void KeymapSwitcher::refresh() { m_keymaps.clear(); - //TODO: load keymaps from file - m_keymaps.append("en-us"); - m_keymaps.append("ru"); + auto mapper_config(Core::ConfigFile::open(m_keyboard_config)); + auto keymaps = mapper_config->read_entry("Mapping", "Keymaps", ""); + + auto keymaps_vector = keymaps.split(','); + + for (auto& keymap : keymaps_vector) { + m_keymaps.append(keymap); + } + + if (m_keymaps.is_empty()) { + dbgln("Empty list of keymaps - adding default (en-us)"); + m_keymaps.append("en-us"); + } + + auto current_keymap = get_current_keymap(); + + // Refresh might indicate that some external program has changed the keymap, + // so better notify our clients that we may have a new keymap + if (on_keymap_change) + on_keymap_change(current_keymap); + + if (m_keymaps.find(current_keymap).is_end()) { + setkeymap(m_keymaps.first()); + } } 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 @@ -77,7 +106,7 @@ String KeymapSwitcher::get_current_keymap() const void KeymapSwitcher::setkeymap(const AK::String& keymap) { pid_t child_pid; - const char* argv[] = { "/bin/keymap", keymap.characters(), nullptr }; + const char* argv[] = { "/bin/keymap", "-m", keymap.characters(), nullptr }; if ((errno = posix_spawn(&child_pid, "/bin/keymap", nullptr, nullptr, const_cast<char**>(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 index 04dfb09b61..d87d0b7d20 100644 --- a/Userland/Services/WindowServer/KeymapSwitcher.h +++ b/Userland/Services/WindowServer/KeymapSwitcher.h @@ -9,6 +9,7 @@ #include <AK/String.h> #include <AK/Vector.h> #include <AK/WeakPtr.h> +#include <LibCore/FileWatcher.h> #include <LibCore/Object.h> #include <LibKeyboard/CharacterMap.h> #include <WindowServer/WMClientConnection.h> @@ -20,8 +21,6 @@ class KeymapSwitcher final : public Core::Object { public: virtual ~KeymapSwitcher() override; - void refresh(); - void next_keymap(); Function<void(String const& keymap)> on_keymap_change; @@ -29,11 +28,17 @@ public: String get_current_keymap() const; private: + void refresh(); + KeymapSwitcher(); Vector<AK::String> m_keymaps; void setkeymap(AK::String const&); + + RefPtr<Core::FileWatcher> m_file_watcher; + + const char* m_keyboard_config = "/etc/Keyboard.ini"; }; } diff --git a/Userland/Services/WindowServer/main.cpp b/Userland/Services/WindowServer/main.cpp index 9697fda608..bb26ef29ad 100644 --- a/Userland/Services/WindowServer/main.cpp +++ b/Userland/Services/WindowServer/main.cpp @@ -25,6 +25,7 @@ ErrorOr<int> serenity_main(Main::Arguments) TRY(Core::System::unveil("/res", "r")); TRY(Core::System::unveil("/tmp", "cw")); TRY(Core::System::unveil("/etc/WindowServer.ini", "rwc")); + TRY(Core::System::unveil("/etc/Keyboard.ini", "r")); TRY(Core::System::unveil("/dev", "rw")); TRY(Core::System::unveil("/bin/keymap", "x")); TRY(Core::System::unveil("/proc/keymap", "r")); diff --git a/Userland/Utilities/keymap.cpp b/Userland/Utilities/keymap.cpp index d92977684a..0818798109 100644 --- a/Userland/Utilities/keymap.cpp +++ b/Userland/Utilities/keymap.cpp @@ -4,6 +4,8 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include <AK/String.h> +#include <AK/Vector.h> #include <LibCore/ArgsParser.h> #include <LibCore/ConfigFile.h> #include <LibCore/System.h> @@ -11,44 +13,96 @@ #include <LibMain/Main.h> #include <stdio.h> +int set_keymap(String const& keymap); + +int set_keymap(String const& keymap) +{ + auto character_map = Keyboard::CharacterMap::load_from_file(keymap); + if (character_map.is_error()) { + warnln("Cannot read keymap {}", keymap); + warnln("Hint: Must be a keymap name (e.g. 'en-us')"); + return 1; + } + + int rc = character_map.value().set_system_map(); + if (rc != 0) { + perror("setkeymap"); + } + + return rc; +} + ErrorOr<int> serenity_main(Main::Arguments arguments) { TRY(Core::System::pledge("stdio setkeymap getkeymap rpath wpath cpath")); TRY(Core::System::unveil("/res/keymaps", "r")); TRY(Core::System::unveil("/etc/Keyboard.ini", "rwc")); - const char* path = nullptr; + String mapping; + String mappings; Core::ArgsParser args_parser; - args_parser.add_positional_argument(path, "The mapping file to be used", "file", Core::ArgsParser::Required::No); + args_parser.add_option(mapping, "The mapping to be used", "set-keymap", 'm', "keymap"); + args_parser.add_option(mappings, "Comma separated list of enabled mappings", "set-keymaps", 's', "keymaps"); args_parser.parse(arguments); - if (path && path[0] == '/') - TRY(Core::System::unveil(path, "r")); - TRY(Core::System::unveil(nullptr, nullptr)); - if (!path) { + if (mapping.is_empty() && mappings.is_empty()) { auto keymap = TRY(Keyboard::CharacterMap::fetch_system_map()); outln("{}", keymap.character_map_name()); return 0; } - auto character_map = Keyboard::CharacterMap::load_from_file(path); - if (character_map.is_error()) { - warnln("Cannot read keymap {}", path); - warnln("Hint: Must be either a keymap name (e.g. 'en-us') or a full path."); - return 1; - } + auto mapper_config(Core::ConfigFile::open("/etc/Keyboard.ini", Core::ConfigFile::AllowWriting::Yes)); - int rc = character_map.value().set_system_map(); - if (rc != 0) { - perror("setkeymap"); - return rc; + int rc = 0; + + if (!mappings.is_empty()) { + auto mappings_vector = mappings.split(','); + + if (mappings_vector.is_empty()) { + warnln("Keymaps list should not be empty"); + return 1; + } + + // Verify that all specified keymaps are loadable + for (auto& keymap_name : mappings_vector) { + auto keymap = Keyboard::CharacterMap::load_from_file(keymap_name); + if (keymap.is_error()) { + warnln("Cannot load keymap {}: {}({})", keymap_name, keymap.error().string_literal(), keymap.error().code()); + return keymap.error(); + } + } + + auto keymaps = String::join(',', mappings_vector); + mapper_config->write_entry("Mapping", "Keymaps", keymaps); + mapper_config->sync(); + rc = set_keymap(mappings_vector.first()); + if (rc != 0) { + return rc; + } } - auto mapper_config(Core::ConfigFile::open("/etc/Keyboard.ini", Core::ConfigFile::AllowWriting::Yes)); - mapper_config->write_entry("Mapping", "Keymap", path); - mapper_config->sync(); + auto keymaps = mapper_config->read_entry("Mapping", "Keymaps"); + auto keymaps_vector = keymaps.split(','); - return rc; + if (!mapping.is_empty()) { + if (keymaps_vector.is_empty()) { + warnln("No keymaps configured - writing default configurations (en-us)"); + mapper_config->write_entry("Mapping", "Keymaps", "en-us"); + mapper_config->sync(); + keymaps_vector.append("en-us"); + } + + if (!keymaps_vector.find(mapping).is_end()) { + rc = set_keymap(mapping); + if (rc != 0) { + return rc; + } + } else { + warnln("Keymap '{}' is not in list of configured keymaps ({})", mapping, keymaps); + } + } + + return 0; } |