#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static constexpr ui::Color TASKBAR_COLOR = ui::Color::from_rgb(83, 83, 83); static OwnedPtr launcher_client; void sigchld_handler(int) { wait(nullptr); } void sighup_handler(int) { // Reload the taskbar by exec-ing the executable, resetting everything. StringView args[] = { "/usr/bin/taskbar" }; os::Process::exec(args[0], { args, 1 }); } Result create_widget_group_for_app(ui::HorizontalLayout& layout, StringView path, StringView icon) { auto* button = TRY(make(ui::Rect { 0, 0, 50, 50 })); layout.add_widget(*button); auto* container = TRY( make(ui::Rect { 0, 0, 50, 50 }, ui::VerticalAlignment::Center, ui::HorizontalAlignment::Center)); button->set_widget(*container); button->set_action([=] { os::Launcher::LaunchDetachedRequest request; SET_IPC_STRING(request.command, path.chars()); launcher_client->send_async(request); }); auto image = TRY(ui::ImageWidget::load(icon)); container->set_widget(*image); image.leak(); return {}; } struct ApplicationFile { String name; String command; String icon; }; Vector s_app_files; // Pretty much copied from init.cpp. static Result load_application_file(const os::Path& path) { os::println("[taskbar] reading app file: %s", path.name().chars()); auto file = TRY(os::File::open(path, os::File::ReadOnly)); ApplicationFile app_file; while (true) { auto line = TRY(file->read_line()); if (line.is_empty()) break; line.trim("\n"); if (line.is_empty()) continue; auto parts = TRY(line.split_once('=')); if (parts.size() < 2 || parts[0].is_empty() || parts[1].is_empty()) { os::println("[taskbar] file contains invalid line, aborting: '%s'", line.chars()); return {}; } if (parts[0].view() == "Name") { app_file.name = move(parts[1]); continue; } if (parts[0].view() == "Description") { // We let users specify this in the config file, but taskbar doesn't actually use it. continue; } if (parts[0].view() == "Command") { app_file.command = move(parts[1]); continue; } if (parts[0].view() == "Icon") { app_file.icon = move(parts[1]); continue; } os::println("[taskbar] skipping unknown entry name %s", parts[0].chars()); } if (app_file.icon.is_empty()) { os::println("[taskbar] app file is missing 'Icon' entry, aborting!"); return {}; } if (app_file.command.is_empty()) { os::println("[taskbar] app file is missing 'Command' entry, aborting!"); return {}; } os::println("[taskbar] loaded app %s into memory", app_file.name.chars()); TRY(s_app_files.try_append(move(app_file))); return {}; } static Result load_app_files_from_path(StringView path) { os::println("[taskbar] loading app files from %s", path.chars()); auto dir = TRY(os::Directory::open(path)); auto services = TRY(dir->list_names(os::Directory::Filter::ParentAndBase)); sort(services.begin(), services.end(), String::compare); for (const auto& entry : services) TRY(load_application_file({ dir->fd(), entry.view() })); return {}; } Result luna_main(int, char**) { ui::App app; TRY(app.init("/tmp/wsys.sock")); TRY(os::EventLoop::the().register_signal_handler(SIGCHLD, sigchld_handler)); TRY(os::EventLoop::the().register_signal_handler(SIGHUP, sighup_handler)); launcher_client = TRY(os::IPC::Client::connect("/tmp/launch.sock", false)); ui::Rect screen = app.screen_rect(); ui::Rect bar = ui::Rect { ui::Point { 0, screen.height - 50 }, screen.width, 50 }; auto window = TRY(ui::Window::create(bar, ui::WindowType::System)); app.set_main_window(window); window->set_background(TASKBAR_COLOR); window->set_special_attributes(ui::UNFOCUSEABLE); ui::HorizontalLayout layout(ui::Margins { 0, 0, 0, 0 }, ui::AdjustHeight::Yes, ui::AdjustWidth::No); window->set_main_widget(layout); load_app_files_from_path("/usr/share/applications/"); auto home = TRY(os::FileSystem::home_directory()); StringBuilder sb; TRY(sb.add(home.view())); TRY(sb.add("/.applications/"_sv)); auto local_app_file_dir = TRY(sb.string()); load_app_files_from_path(local_app_file_dir.view()); for (const auto& app_file : s_app_files) { create_widget_group_for_app(layout, app_file.command.view(), app_file.icon.view()); } return app.run(); }