main.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. /*
  2. * Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include "CharacterMapFileListModel.h"
  27. #include <AK/QuickSort.h>
  28. #include <AK/JsonObject.h>
  29. #include <LibCore/ArgsParser.h>
  30. #include <LibCore/DirIterator.h>
  31. #include <LibCore/File.h>
  32. #include <LibGUI/AboutDialog.h>
  33. #include <LibGUI/Action.h>
  34. #include <LibGUI/Application.h>
  35. #include <LibGUI/BoxLayout.h>
  36. #include <LibGUI/Button.h>
  37. #include <LibGUI/ComboBox.h>
  38. #include <LibGUI/Label.h>
  39. #include <LibGUI/Menu.h>
  40. #include <LibGUI/MenuBar.h>
  41. #include <LibGUI/MessageBox.h>
  42. #include <LibGUI/WindowServerConnection.h>
  43. #include <LibKeyboard/CharacterMap.h>
  44. #include <spawn.h>
  45. int main(int argc, char** argv)
  46. {
  47. if (pledge("stdio rpath accept cpath wpath shared_buffer unix fattr proc exec", nullptr) < 0) {
  48. perror("pledge");
  49. return 1;
  50. }
  51. // If there is no command line parameter go for GUI.
  52. auto app = GUI::Application::construct(argc, argv);
  53. if (pledge("stdio rpath accept shared_buffer proc exec", nullptr) < 0) {
  54. perror("pledge");
  55. return 1;
  56. }
  57. if (unveil("/res", "r") < 0 ) {
  58. perror("unveil");
  59. return 1;
  60. }
  61. if (unveil("/bin/keymap", "x") < 0 ) {
  62. perror("unveil");
  63. return 1;
  64. }
  65. if (unveil("/proc/keymap", "r") < 0) {
  66. perror("unveil");
  67. return 1;
  68. }
  69. if (unveil(nullptr, nullptr)) {
  70. perror("unveil");
  71. return 1;
  72. }
  73. auto app_icon = GUI::Icon::default_icon("app-keyboard-settings");
  74. auto proc_keymap = Core::File::construct("/proc/keymap");
  75. if (!proc_keymap->open(Core::IODevice::OpenMode::ReadOnly))
  76. ASSERT_NOT_REACHED();
  77. auto json = JsonValue::from_string(proc_keymap->read_all());
  78. ASSERT(json.has_value());
  79. JsonObject keymap_object = json.value().as_object();
  80. ASSERT(keymap_object.has("keymap"));
  81. String current_keymap = keymap_object.get("keymap").to_string();
  82. dbg() << "KeyboardSettings thinks the current keymap is: " << current_keymap;
  83. Vector<String> character_map_files;
  84. Core::DirIterator iterator("/res/keymaps/", Core::DirIterator::Flags::SkipDots);
  85. if (iterator.has_error()) {
  86. GUI::MessageBox::show(nullptr, String::format("Error on reading mapping file list: %d", iterator.error_string()), "Keyboard settings", GUI::MessageBox::Type::Error);
  87. return -1;
  88. }
  89. while (iterator.has_next()) {
  90. auto name = iterator.next_path();
  91. name.replace(".json", "");
  92. character_map_files.append(name);
  93. }
  94. quick_sort(character_map_files);
  95. size_t initial_keymap_index = SIZE_MAX;
  96. for (size_t i = 0; i < character_map_files.size(); ++i) {
  97. if (character_map_files[i].equals_ignoring_case(current_keymap))
  98. initial_keymap_index = i;
  99. }
  100. ASSERT(initial_keymap_index < character_map_files.size());
  101. auto window = GUI::Window::construct();
  102. window->set_title("Keyboard settings");
  103. window->resize(300, 70);
  104. window->set_icon(app_icon.bitmap_for_size(16));
  105. auto& root_widget = window->set_main_widget<GUI::Widget>();
  106. root_widget.set_layout<GUI::VerticalBoxLayout>();
  107. root_widget.set_fill_with_background_color(true);
  108. root_widget.layout()->set_spacing(0);
  109. root_widget.layout()->set_margins({ 4, 4, 4, 4 });
  110. auto& character_map_file_selection_container = root_widget.add<GUI::Widget>();
  111. character_map_file_selection_container.set_layout<GUI::HorizontalBoxLayout>();
  112. character_map_file_selection_container.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed);
  113. character_map_file_selection_container.set_preferred_size(0, 22);
  114. auto& character_map_file_label = character_map_file_selection_container.add<GUI::Label>();
  115. character_map_file_label.set_text_alignment(Gfx::TextAlignment::CenterLeft);
  116. character_map_file_label.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fill);
  117. character_map_file_label.set_preferred_size({ 130, 0 });
  118. character_map_file_label.set_text("Character Mapping File:");
  119. auto& character_map_file_combo = character_map_file_selection_container.add<GUI::ComboBox>();
  120. character_map_file_combo.set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed);
  121. character_map_file_combo.set_preferred_size(0, 22);
  122. character_map_file_combo.set_only_allow_values_from_model(true);
  123. character_map_file_combo.set_model(*CharacterMapFileListModel::create(character_map_files));
  124. character_map_file_combo.set_selected_index(initial_keymap_index);
  125. root_widget.layout()->add_spacer();
  126. auto apply_settings = [&](bool quit) {
  127. String character_map_file = character_map_file_combo.text();
  128. if (character_map_file.is_empty()) {
  129. GUI::MessageBox::show(window, "Please select character mapping file.", "Keyboard settings", GUI::MessageBox::Type::Error);
  130. return;
  131. }
  132. pid_t child_pid;
  133. const char* argv[] = { "/bin/keymap", character_map_file.characters(), nullptr };
  134. if ((errno = posix_spawn(&child_pid, "/bin/keymap", nullptr, nullptr, const_cast<char**>(argv), environ))) {
  135. perror("posix_spawn");
  136. exit(1);
  137. }
  138. if (quit)
  139. app->quit();
  140. };
  141. auto& bottom_widget = root_widget.add<GUI::Widget>();
  142. bottom_widget.set_layout<GUI::HorizontalBoxLayout>();
  143. bottom_widget.layout()->add_spacer();
  144. bottom_widget.set_size_policy(Orientation::Vertical, GUI::SizePolicy::Fixed);
  145. bottom_widget.set_preferred_size(1, 22);
  146. auto& apply_button = bottom_widget.add<GUI::Button>();
  147. apply_button.set_text("Apply");
  148. apply_button.set_size_policy(Orientation::Horizontal, GUI::SizePolicy::Fixed);
  149. apply_button.set_preferred_size(60, 22);
  150. apply_button.on_click = [&](auto) {
  151. apply_settings(false);
  152. };
  153. auto& ok_button = bottom_widget.add<GUI::Button>();
  154. ok_button.set_text("OK");
  155. ok_button.set_size_policy(Orientation::Horizontal, GUI::SizePolicy::Fixed);
  156. ok_button.set_preferred_size(60, 22);
  157. ok_button.on_click = [&](auto) {
  158. apply_settings(true);
  159. };
  160. auto& cancel_button = bottom_widget.add<GUI::Button>();
  161. cancel_button.set_text("Cancel");
  162. cancel_button.set_size_policy(Orientation::Horizontal, GUI::SizePolicy::Fixed);
  163. cancel_button.set_preferred_size(60, 22);
  164. cancel_button.on_click = [&](auto) {
  165. app->quit();
  166. };
  167. auto quit_action = GUI::CommonActions::make_quit_action(
  168. [&](auto&) {
  169. app->quit();
  170. });
  171. auto about_action = GUI::Action::create("About",
  172. [&](auto&) {
  173. GUI::AboutDialog::show("Keyboard settings", app_icon.bitmap_for_size(32), window);
  174. });
  175. auto menubar = GUI::MenuBar::construct();
  176. auto& app_menu = menubar->add_menu("Keyboard settings");
  177. app_menu.add_action(quit_action);
  178. auto& help_menu = menubar->add_menu("Help");
  179. help_menu.add_action(about_action);
  180. app->set_menubar(move(menubar));
  181. window->show();
  182. return app->exec();
  183. }