Serenity Operating System
at portability 262 lines 9.2 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 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 27#include <AK/Optional.h> 28#include <AK/String.h> 29#include <AK/StringBuilder.h> 30#include <AK/Vector.h> 31#include <LibCore/File.h> 32#include <LibGUI/Application.h> 33#include <LibGUI/BoxLayout.h> 34#include <LibGUI/Button.h> 35#include <LibGUI/Desktop.h> 36#include <LibGUI/Label.h> 37#include <LibGUI/MessageBox.h> 38#include <LibGUI/StackWidget.h> 39#include <LibGUI/Window.h> 40#include <LibGfx/Font.h> 41#include <LibGfx/Bitmap.h> 42#include <stdio.h> 43#include <unistd.h> 44 45#include "BackgroundWidget.h" 46#include "TextWidget.h" 47#include "UnuncheckableButton.h" 48 49struct ContentPage { 50 String menu_name; 51 String title; 52 String icon = String::empty(); 53 Vector<String> content; 54}; 55 56Optional<Vector<ContentPage>> parse_welcome_file(const String& path) 57{ 58 const auto error = Optional<Vector<ContentPage>>(); 59 auto file = Core::File::construct(path); 60 61 if (!file->open(Core::IODevice::ReadOnly)) 62 return error; 63 64 Vector<ContentPage> pages; 65 StringBuilder current_output_line; 66 bool started = false; 67 ContentPage current; 68 while (true) { 69 auto buffer = file->read_line(4096); 70 if (buffer.is_null()) { 71 if (file->error()) { 72 file->close(); 73 return error; 74 } 75 76 break; 77 } 78 79 auto line = String((char*)buffer.data()); 80 if (line.length() > 1) 81 line = line.substring(0, line.length() - 1); // remove newline 82 switch (line[0]) { 83 case '*': 84 dbg() << "menu_item line:\t" << line; 85 if (started) 86 pages.append(current); 87 else 88 started = true; 89 90 current = {}; 91 current.menu_name = line.substring(2, line.length() - 2); 92 break; 93 case '$': 94 dbg() << "icon line: \t" << line; 95 current.icon = line.substring(2, line.length() - 2); 96 break; 97 case '>': 98 dbg() << "title line:\t" << line; 99 current.title = line.substring(2, line.length() - 2); 100 break; 101 case '\n': 102 dbg() << "newline"; 103 104 if (!current_output_line.to_string().is_empty()) 105 current.content.append(current_output_line.to_string()); 106 current_output_line.clear(); 107 break; 108 case '#': 109 dbg() << "comment line:\t" << line; 110 break; 111 default: 112 dbg() << "content line:\t" << line; 113 if (current_output_line.length() != 0) 114 current_output_line.append(' '); 115 current_output_line.append(line); 116 break; 117 } 118 } 119 120 if (started) { 121 current.content.append(current_output_line.to_string()); 122 pages.append(current); 123 } 124 125 file->close(); 126 return pages; 127} 128 129int main(int argc, char** argv) 130{ 131 if (pledge("stdio shared_buffer rpath unix cpath fattr", nullptr) < 0) { 132 perror("pledge"); 133 return 1; 134 } 135 136 GUI::Application app(argc, argv); 137 138 if (pledge("stdio shared_buffer rpath", nullptr) < 0) { 139 perror("pledge"); 140 return 1; 141 } 142 143 if (unveil("/res", "r") < 0) { 144 perror("unveil"); 145 return 1; 146 } 147 148 unveil(nullptr, nullptr); 149 150 Optional<Vector<ContentPage>> _pages = parse_welcome_file("/res/welcome.txt"); 151 if (!_pages.has_value()) { 152 GUI::MessageBox::show("Could not open Welcome file.", "Welcome", GUI::MessageBox::Type::Error, GUI::MessageBox::InputType::OK, nullptr); 153 return 1; 154 } 155 auto pages = _pages.value(); 156 157 auto window = GUI::Window::construct(); 158 window->set_title("Welcome"); 159 Gfx::Rect window_rect { 0, 0, 640, 360 }; 160 window_rect.center_within(GUI::Desktop::the().rect()); 161 window->set_resizable(true); 162 window->set_rect(window_rect); 163 164 auto background = BackgroundWidget::construct(); 165 window->set_main_widget(background); 166 background->set_fill_with_background_color(false); 167 background->set_layout(make<GUI::VerticalBoxLayout>()); 168 background->layout()->set_margins({ 16, 8, 16, 8 }); 169 background->layout()->set_spacing(8); 170 background->set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fill); 171 172 // 173 // header 174 // 175 176 auto header = background->add<GUI::Label>(); 177 header->set_font(Gfx::Font::load_from_file("/res/fonts/PebbletonBold11.font")); 178 header->set_text("Welcome to SerenityOS!"); 179 header->set_text_alignment(Gfx::TextAlignment::CenterLeft); 180 header->set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); 181 header->set_preferred_size(0, 30); 182 183 // 184 // main section 185 // 186 187 auto main_section = background->add<GUI::Widget>(); 188 main_section->set_layout(make<GUI::HorizontalBoxLayout>()); 189 main_section->layout()->set_margins({ 0, 0, 0, 0 }); 190 main_section->layout()->set_spacing(8); 191 main_section->set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fill); 192 193 auto menu = main_section->add<GUI::Widget>(); 194 menu->set_layout(make<GUI::VerticalBoxLayout>()); 195 menu->layout()->set_margins({ 0, 0, 0, 0 }); 196 menu->layout()->set_spacing(4); 197 menu->set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fill); 198 menu->set_preferred_size(100, 0); 199 200 auto stack = main_section->add<GUI::StackWidget>(); 201 stack->set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fill); 202 203 bool first = true; 204 for (auto& page : pages) { 205 auto content = stack->add<GUI::Widget>(); 206 content->set_layout(make<GUI::VerticalBoxLayout>()); 207 content->layout()->set_margins({ 0, 0, 0, 0 }); 208 content->layout()->set_spacing(8); 209 content->set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fill); 210 211 auto title_box = content->add<GUI::Widget>(); 212 title_box->set_layout(make<GUI::HorizontalBoxLayout>()); 213 title_box->layout()->set_spacing(4); 214 title_box->set_preferred_size(0, 16); 215 title_box->set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); 216 217 if (!page.icon.is_empty()) { 218 auto icon = title_box->add<GUI::Label>(); 219 icon->set_icon(Gfx::Bitmap::load_from_file(page.icon)); 220 icon->set_preferred_size(16, 16); 221 icon->set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fixed); 222 } 223 224 auto content_title = title_box->add<GUI::Label>(); 225 content_title->set_font(Gfx::Font::default_bold_font()); 226 content_title->set_text(page.title); 227 content_title->set_text_alignment(Gfx::TextAlignment::CenterLeft); 228 content_title->set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); 229 content_title->set_preferred_size(0, 10); 230 231 for (auto& paragraph : page.content) { 232 auto content_text = content->add<TextWidget>(); 233 content_text->set_font(Gfx::Font::default_font()); 234 content_text->set_text(paragraph); 235 content_text->set_text_alignment(Gfx::TextAlignment::TopLeft); 236 content_text->set_line_height(12); 237 content_text->wrap_and_set_height(); 238 } 239 240 auto menu_option = menu->add<UnuncheckableButton>(); 241 menu_option->set_font(Gfx::Font::default_font()); 242 menu_option->set_text(page.menu_name); 243 menu_option->set_text_alignment(Gfx::TextAlignment::CenterLeft); 244 menu_option->set_size_policy(GUI::SizePolicy::Fill, GUI::SizePolicy::Fixed); 245 menu_option->set_preferred_size(0, 20); 246 menu_option->set_checkable(true); 247 menu_option->set_exclusive(true); 248 249 if (first) 250 menu_option->set_checked(true); 251 252 menu_option->on_click = [content = content.ptr(), &stack](auto&) { 253 stack->set_active_widget(content); 254 content->invalidate_layout(); 255 }; 256 257 first = false; 258 } 259 260 window->show(); 261 return app.exec(); 262}