main.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. /*
  2. * Copyright (c) 2020, the SerenityOS developers.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "ChessWidget.h"
  7. #include <LibCore/ConfigFile.h>
  8. #include <LibCore/DirIterator.h>
  9. #include <LibGUI/ActionGroup.h>
  10. #include <LibGUI/Application.h>
  11. #include <LibGUI/Clipboard.h>
  12. #include <LibGUI/FilePicker.h>
  13. #include <LibGUI/Icon.h>
  14. #include <LibGUI/Menu.h>
  15. #include <LibGUI/Menubar.h>
  16. #include <LibGUI/MessageBox.h>
  17. #include <LibGUI/Window.h>
  18. #include <unistd.h>
  19. int main(int argc, char** argv)
  20. {
  21. auto app = GUI::Application::construct(argc, argv);
  22. auto app_icon = GUI::Icon::default_icon("app-chess");
  23. auto window = GUI::Window::construct();
  24. auto& widget = window->set_main_widget<ChessWidget>();
  25. RefPtr<Core::ConfigFile> config = Core::ConfigFile::open_for_app("Chess", Core::ConfigFile::AllowWriting::Yes);
  26. if (pledge("stdio rpath wpath cpath recvfd sendfd thread proc exec", nullptr) < 0) {
  27. perror("pledge");
  28. return 1;
  29. }
  30. if (unveil("/res", "r") < 0) {
  31. perror("unveil");
  32. return 1;
  33. }
  34. if (unveil(config->filename().characters(), "crw") < 0) {
  35. perror("unveil");
  36. return 1;
  37. }
  38. if (unveil("/bin/ChessEngine", "x") < 0) {
  39. perror("unveil");
  40. return 1;
  41. }
  42. if (unveil("/etc/passwd", "r") < 0) {
  43. perror("unveil");
  44. return 1;
  45. }
  46. if (unveil(Core::StandardPaths::home_directory().characters(), "wcbr") < 0) {
  47. perror("unveil");
  48. return 1;
  49. }
  50. if (unveil(nullptr, nullptr) < 0) {
  51. perror("unveil");
  52. return 1;
  53. }
  54. auto size = config->read_num_entry("Display", "size", 512);
  55. window->set_title("Chess");
  56. window->set_base_size({ 4, 4 });
  57. window->set_size_increment({ 8, 8 });
  58. window->resize(size - 4, size - 4);
  59. window->set_icon(app_icon.bitmap_for_size(16));
  60. widget.set_piece_set(config->read_entry("Style", "PieceSet", "stelar7"));
  61. widget.set_board_theme(config->read_entry("Style", "BoardTheme", "Beige"));
  62. widget.set_coordinates(config->read_bool_entry("Style", "Coordinates", true));
  63. widget.set_show_available_moves(config->read_bool_entry("Style", "ShowAvailableMoves", true));
  64. auto& game_menu = window->add_menu("&Game");
  65. game_menu.add_action(GUI::Action::create("&Resign", { Mod_None, Key_F3 }, [&](auto&) {
  66. widget.resign();
  67. }));
  68. game_menu.add_action(GUI::Action::create("&Flip Board", { Mod_Ctrl, Key_F }, [&](auto&) {
  69. widget.flip_board();
  70. }));
  71. game_menu.add_separator();
  72. game_menu.add_action(GUI::Action::create("&Import PGN...", { Mod_Ctrl, Key_O }, [&](auto&) {
  73. Optional<String> import_path = GUI::FilePicker::get_open_filepath(window);
  74. if (!import_path.has_value())
  75. return;
  76. if (!widget.import_pgn(import_path.value())) {
  77. GUI::MessageBox::show(window, "Unable to import game.\n", "Error", GUI::MessageBox::Type::Error);
  78. return;
  79. }
  80. dbgln("Imported PGN file from {}", import_path.value());
  81. }));
  82. game_menu.add_action(GUI::Action::create("&Export PGN...", { Mod_Ctrl, Key_S }, [&](auto&) {
  83. Optional<String> export_path = GUI::FilePicker::get_save_filepath(window, "Untitled", "pgn");
  84. if (!export_path.has_value())
  85. return;
  86. if (!widget.export_pgn(export_path.value())) {
  87. GUI::MessageBox::show(window, "Unable to export game.\n", "Error", GUI::MessageBox::Type::Error);
  88. return;
  89. }
  90. dbgln("Exported PGN file to {}", export_path.value());
  91. }));
  92. game_menu.add_action(GUI::Action::create("&Copy FEN", { Mod_Ctrl, Key_C }, [&](auto&) {
  93. GUI::Clipboard::the().set_data(widget.get_fen().bytes());
  94. GUI::MessageBox::show(window, "Board state copied to clipboard as FEN.", "Copy FEN", GUI::MessageBox::Type::Information);
  95. }));
  96. game_menu.add_separator();
  97. game_menu.add_action(GUI::Action::create("&New Game", { Mod_None, Key_F2 }, [&](auto&) {
  98. if (widget.board().game_result() == Chess::Board::Result::NotFinished) {
  99. if (widget.resign() < 0)
  100. return;
  101. }
  102. widget.reset();
  103. }));
  104. game_menu.add_separator();
  105. game_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) {
  106. GUI::Application::the()->quit();
  107. }));
  108. auto& style_menu = window->add_menu("&Style");
  109. GUI::ActionGroup piece_set_action_group;
  110. piece_set_action_group.set_exclusive(true);
  111. auto& piece_set_menu = style_menu.add_submenu("&Piece Set");
  112. piece_set_menu.set_icon(app_icon.bitmap_for_size(16));
  113. Core::DirIterator di("/res/icons/chess/sets/", Core::DirIterator::SkipParentAndBaseDir);
  114. while (di.has_next()) {
  115. auto set = di.next_path();
  116. auto action = GUI::Action::create_checkable(set, [&](auto& action) {
  117. widget.set_piece_set(action.text());
  118. widget.update();
  119. config->write_entry("Style", "PieceSet", action.text());
  120. config->sync();
  121. });
  122. piece_set_action_group.add_action(*action);
  123. if (widget.piece_set() == set)
  124. action->set_checked(true);
  125. piece_set_menu.add_action(*action);
  126. }
  127. GUI::ActionGroup board_theme_action_group;
  128. board_theme_action_group.set_exclusive(true);
  129. auto& board_theme_menu = style_menu.add_submenu("Board Theme");
  130. board_theme_menu.set_icon(Gfx::Bitmap::try_load_from_file("/res/icons/chess/mini-board.png"));
  131. for (auto& theme : Vector({ "Beige", "Green", "Blue" })) {
  132. auto action = GUI::Action::create_checkable(theme, [&](auto& action) {
  133. widget.set_board_theme(action.text());
  134. widget.update();
  135. config->write_entry("Style", "BoardTheme", action.text());
  136. config->sync();
  137. });
  138. board_theme_action_group.add_action(*action);
  139. if (widget.board_theme().name == theme)
  140. action->set_checked(true);
  141. board_theme_menu.add_action(*action);
  142. }
  143. auto coordinates_action = GUI::Action::create_checkable("Coordinates", [&](auto& action) {
  144. widget.set_coordinates(action.is_checked());
  145. widget.update();
  146. config->write_bool_entry("Style", "Coordinates", action.is_checked());
  147. config->sync();
  148. });
  149. coordinates_action->set_checked(widget.coordinates());
  150. style_menu.add_action(coordinates_action);
  151. auto show_available_moves_action = GUI::Action::create_checkable("Show Available Moves", [&](auto& action) {
  152. widget.set_show_available_moves(action.is_checked());
  153. widget.update();
  154. config->write_bool_entry("Style", "ShowAvailableMoves", action.is_checked());
  155. config->sync();
  156. });
  157. show_available_moves_action->set_checked(widget.show_available_moves());
  158. style_menu.add_action(show_available_moves_action);
  159. auto& engine_menu = window->add_menu("&Engine");
  160. GUI::ActionGroup engines_action_group;
  161. engines_action_group.set_exclusive(true);
  162. auto& engine_submenu = engine_menu.add_submenu("&Engine");
  163. for (auto& engine : Vector({ "Human", "ChessEngine" })) {
  164. auto action = GUI::Action::create_checkable(engine, [&](auto& action) {
  165. if (action.text() == "Human") {
  166. widget.set_engine(nullptr);
  167. } else {
  168. widget.set_engine(Engine::construct(action.text()));
  169. widget.input_engine_move();
  170. }
  171. });
  172. engines_action_group.add_action(*action);
  173. if (engine == String("Human"))
  174. action->set_checked(true);
  175. engine_submenu.add_action(*action);
  176. }
  177. auto& help_menu = window->add_menu("&Help");
  178. help_menu.add_action(GUI::CommonActions::make_about_action("Chess", app_icon, window));
  179. window->show();
  180. widget.reset();
  181. return app->exec();
  182. }