Serenity Operating System
1/*
2 * Copyright (c) 2022, Joe Petrus <joe@petrus.io>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include "WordGame.h"
8#include <AK/URL.h>
9#include <Games/MasterWord/MasterWordGML.h>
10#include <LibConfig/Client.h>
11#include <LibCore/System.h>
12#include <LibDesktop/Launcher.h>
13#include <LibGUI/Action.h>
14#include <LibGUI/ActionGroup.h>
15#include <LibGUI/Application.h>
16#include <LibGUI/Icon.h>
17#include <LibGUI/InputBox.h>
18#include <LibGUI/Menubar.h>
19#include <LibGUI/MessageBox.h>
20#include <LibGUI/Statusbar.h>
21#include <LibGUI/Window.h>
22#include <LibMain/Main.h>
23
24ErrorOr<int> serenity_main(Main::Arguments arguments)
25{
26 TRY(Core::System::pledge("stdio rpath recvfd sendfd unix"));
27
28 auto app = TRY(GUI::Application::try_create(arguments));
29
30 Config::pledge_domain("MasterWord");
31
32 TRY(Desktop::Launcher::add_allowed_handler_with_only_specific_urls("/bin/Help", { URL::create_with_file_scheme("/usr/share/man/man6/MasterWord.md") }));
33 TRY(Desktop::Launcher::seal_allowlist());
34
35 TRY(Core::System::pledge("stdio rpath recvfd sendfd"));
36
37 TRY(Core::System::unveil("/tmp/session/%sid/portal/launch", "rw"));
38 TRY(Core::System::unveil("/res", "r"));
39 TRY(Core::System::unveil(nullptr, nullptr));
40
41 auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-masterword"sv));
42
43 auto window = TRY(GUI::Window::try_create());
44
45 window->set_double_buffering_enabled(false);
46 window->set_title("MasterWord");
47 window->set_resizable(false);
48
49 auto main_widget = TRY(window->set_main_widget<GUI::Widget>());
50 TRY(main_widget->load_from_gml(master_word_gml));
51 auto& game = *main_widget->find_descendant_of_type_named<MasterWord::WordGame>("word_game");
52 auto& statusbar = *main_widget->find_descendant_of_type_named<GUI::Statusbar>("statusbar");
53
54 auto use_system_theme = Config::read_bool("MasterWord"sv, ""sv, "use_system_theme"sv, false);
55 game.set_use_system_theme(use_system_theme);
56
57 auto shortest_word = game.shortest_word();
58 auto longest_word = game.longest_word();
59
60 window->resize(game.game_size());
61 window->set_focused_widget(&game);
62
63 auto game_menu = TRY(window->try_add_menu("&Game"));
64
65 TRY(game_menu->try_add_action(GUI::Action::create("&New Game", { Mod_None, Key_F2 }, [&](auto&) {
66 game.reset();
67 })));
68
69 TRY(game_menu->try_add_separator());
70 TRY(game_menu->try_add_action(GUI::CommonActions::make_quit_action([](auto&) {
71 GUI::Application::the()->quit();
72 })));
73
74 auto settings_menu = TRY(window->try_add_menu("&Settings"));
75
76 TRY(settings_menu->try_add_action(GUI::Action::create("Set &Word Length", [&](auto&) {
77 auto word_length = Config::read_i32("MasterWord"sv, ""sv, "word_length"sv, 5);
78 auto word_length_string = DeprecatedString::number(word_length);
79 if (GUI::InputBox::show(window, word_length_string, "Word length:"sv, "MasterWord"sv, GUI::InputType::NonemptyText) == GUI::InputBox::ExecResult::OK) {
80 auto maybe_word_length = word_length_string.template to_uint();
81 if (!maybe_word_length.has_value() || maybe_word_length.value() < shortest_word || maybe_word_length.value() > longest_word) {
82 GUI::MessageBox::show(window, DeprecatedString::formatted("Please enter a number between {} and {}.", shortest_word, longest_word), "MasterWord"sv);
83 return;
84 }
85
86 word_length = maybe_word_length.value();
87 Config::write_i32("MasterWord"sv, ""sv, "word_length"sv, word_length);
88 game.set_word_length(word_length);
89 window->resize(game.game_size());
90 }
91 })));
92 TRY(settings_menu->try_add_action(GUI::Action::create("Set &Number Of Guesses", [&](auto&) {
93 auto max_guesses = Config::read_i32("MasterWord"sv, ""sv, "max_guesses"sv, 5);
94 auto max_guesses_string = DeprecatedString::number(max_guesses);
95 if (GUI::InputBox::show(window, max_guesses_string, "Maximum number of guesses:"sv, "MasterWord"sv, GUI::InputType::NonemptyText) == GUI::InputBox::ExecResult::OK) {
96 auto maybe_max_guesses = max_guesses_string.template to_uint();
97 if (!maybe_max_guesses.has_value() || maybe_max_guesses.value() < 1 || maybe_max_guesses.value() > 20) {
98 GUI::MessageBox::show(window, "Please enter a number between 1 and 20."sv, "MasterWord"sv);
99 return;
100 }
101
102 max_guesses = maybe_max_guesses.value();
103 Config::write_i32("MasterWord"sv, ""sv, "max_guesses"sv, max_guesses);
104 game.set_max_guesses(max_guesses);
105 window->resize(game.game_size());
106 }
107 })));
108
109 auto toggle_check_guesses = GUI::Action::create_checkable("Check &Guesses in dictionary", [&](auto& action) {
110 auto checked = action.is_checked();
111 game.set_check_guesses_in_dictionary(checked);
112 Config::write_bool("MasterWord"sv, ""sv, "check_guesses_in_dictionary"sv, checked);
113 });
114 toggle_check_guesses->set_checked(game.is_checking_guesses());
115 TRY(settings_menu->try_add_action(toggle_check_guesses));
116
117 auto theme_menu = TRY(window->try_add_menu("&Theme"));
118 auto system_theme_action = GUI::Action::create("&System", [&](auto&) {
119 game.set_use_system_theme(true);
120 Config::write_bool("MasterWord"sv, ""sv, "use_system_theme"sv, true);
121 });
122 system_theme_action->set_checkable(true);
123 system_theme_action->set_checked(use_system_theme);
124 TRY(theme_menu->try_add_action(system_theme_action));
125
126 auto wordle_theme_action = GUI::Action::create("&Wordle", [&](auto&) {
127 game.set_use_system_theme(false);
128 Config::write_bool("MasterWord"sv, ""sv, "use_system_theme"sv, false);
129 });
130 wordle_theme_action->set_checkable(true);
131 wordle_theme_action->set_checked(!use_system_theme);
132 TRY(theme_menu->try_add_action(wordle_theme_action));
133
134 GUI::ActionGroup theme_actions;
135 theme_actions.set_exclusive(true);
136 theme_actions.set_unchecking_allowed(false);
137 theme_actions.add_action(system_theme_action);
138 theme_actions.add_action(wordle_theme_action);
139
140 auto help_menu = TRY(window->try_add_menu("&Help"));
141 TRY(help_menu->try_add_action(GUI::CommonActions::make_command_palette_action(window)));
142 TRY(help_menu->try_add_action(GUI::CommonActions::make_help_action([](auto&) {
143 Desktop::Launcher::open(URL::create_with_file_scheme("/usr/share/man/man6/MasterWord.md"), "/bin/Help");
144 })));
145 TRY(help_menu->try_add_action(GUI::CommonActions::make_about_action("MasterWord", app_icon, window)));
146
147 game.on_message = [&](auto message) {
148 if (!message.has_value())
149 statusbar.set_text("");
150 else
151 statusbar.set_text(*message);
152 };
153
154 window->show();
155
156 window->set_icon(app_icon.bitmap_for_size(16));
157
158 return app->exec();
159}