Serenity Operating System
at portability 218 lines 8.5 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 "TaskbarWindow.h" 28#include "TaskbarButton.h" 29#include <AK/SharedBuffer.h> 30#include <LibCore/ConfigFile.h> 31#include <LibGUI/BoxLayout.h> 32#include <LibGUI/Button.h> 33#include <LibGUI/Desktop.h> 34#include <LibGUI/Frame.h> 35#include <LibGUI/Window.h> 36#include <stdio.h> 37 38//#define EVENT_DEBUG 39 40TaskbarWindow::TaskbarWindow() 41{ 42 set_window_type(GUI::WindowType::Taskbar); 43 set_title("Taskbar"); 44 45 on_screen_rect_change(GUI::Desktop::the().rect()); 46 47 GUI::Desktop::the().on_rect_change = [this](const Gfx::Rect& rect) { on_screen_rect_change(rect); }; 48 49 auto widget = GUI::Frame::construct(); 50 widget->set_fill_with_background_color(true); 51 widget->set_layout(make<GUI::HorizontalBoxLayout>()); 52 widget->layout()->set_margins({ 3, 2, 3, 2 }); 53 widget->layout()->set_spacing(3); 54 widget->set_frame_thickness(1); 55 widget->set_frame_shape(Gfx::FrameShape::Panel); 56 widget->set_frame_shadow(Gfx::FrameShadow::Raised); 57 set_main_widget(widget); 58 59 WindowList::the().aid_create_button = [this](auto& identifier) { 60 return create_button(identifier); 61 }; 62 63 create_quick_launch_bar(); 64} 65 66TaskbarWindow::~TaskbarWindow() 67{ 68} 69 70void TaskbarWindow::create_quick_launch_bar() 71{ 72 auto quick_launch_bar = main_widget()->add<GUI::Frame>(); 73 quick_launch_bar->set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fixed); 74 quick_launch_bar->set_layout(make<GUI::HorizontalBoxLayout>()); 75 quick_launch_bar->layout()->set_spacing(3); 76 quick_launch_bar->layout()->set_margins({ 3, 0, 3, 0 }); 77 quick_launch_bar->set_frame_thickness(1); 78 quick_launch_bar->set_frame_shape(Gfx::FrameShape::Container); 79 quick_launch_bar->set_frame_shadow(Gfx::FrameShadow::Raised); 80 81 int total_width = 6; 82 bool first = true; 83 84 auto config = Core::ConfigFile::get_for_app("Taskbar"); 85 constexpr const char* quick_launch = "QuickLaunch"; 86 87 // FIXME: Core::ConfigFile does not keep the order of the entries. 88 for (auto& name : config->keys(quick_launch)) { 89 auto af_name = config->read_entry(quick_launch, name); 90 ASSERT(!af_name.is_null()); 91 auto af_path = String::format("/res/apps/%s", af_name.characters()); 92 auto af = Core::ConfigFile::open(af_path); 93 auto app_executable = af->read_entry("App", "Executable"); 94 auto app_icon_path = af->read_entry("Icons", "16x16"); 95 96 auto button = quick_launch_bar->add<GUI::Button>(); 97 button->set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fixed); 98 button->set_preferred_size(22, 22); 99 button->set_button_style(Gfx::ButtonStyle::CoolBar); 100 101 button->set_icon(Gfx::Bitmap::load_from_file(app_icon_path)); 102 // FIXME: the tooltip ends up outside the screen rect. 103 button->set_tooltip(name); 104 button->on_click = [app_executable](auto&) { 105 pid_t pid = fork(); 106 if (pid < 0) { 107 perror("fork"); 108 } else if (pid == 0) { 109 execl(app_executable.characters(), app_executable.characters(), nullptr); 110 perror("execl"); 111 ASSERT_NOT_REACHED(); 112 } 113 }; 114 115 if (!first) 116 total_width += 3; 117 first = false; 118 total_width += 22; 119 } 120 121 quick_launch_bar->set_preferred_size(total_width, 22); 122} 123 124void TaskbarWindow::on_screen_rect_change(const Gfx::Rect& rect) 125{ 126 Gfx::Rect new_rect { rect.x(), rect.bottom() - taskbar_height() + 1, rect.width(), taskbar_height() }; 127 set_rect(new_rect); 128} 129 130NonnullRefPtr<GUI::Button> TaskbarWindow::create_button(const WindowIdentifier& identifier) 131{ 132 auto button = main_widget()->add<TaskbarButton>(identifier); 133 button->set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fixed); 134 button->set_preferred_size(140, 22); 135 button->set_checkable(true); 136 button->set_text_alignment(Gfx::TextAlignment::CenterLeft); 137 return button; 138} 139 140static bool should_include_window(GUI::WindowType window_type) 141{ 142 return window_type == GUI::WindowType::Normal; 143} 144 145void TaskbarWindow::wm_event(GUI::WMEvent& event) 146{ 147 WindowIdentifier identifier { event.client_id(), event.window_id() }; 148 switch (event.type()) { 149 case GUI::Event::WM_WindowRemoved: { 150#ifdef EVENT_DEBUG 151 auto& removed_event = static_cast<GUI::WMWindowRemovedEvent&>(event); 152 dbgprintf("WM_WindowRemoved: client_id=%d, window_id=%d\n", 153 removed_event.client_id(), 154 removed_event.window_id()); 155#endif 156 WindowList::the().remove_window(identifier); 157 update(); 158 break; 159 } 160 case GUI::Event::WM_WindowRectChanged: { 161#ifdef EVENT_DEBUG 162 auto& changed_event = static_cast<GUI::WMWindowRectChangedEvent&>(event); 163 dbgprintf("WM_WindowRectChanged: client_id=%d, window_id=%d, rect=%s\n", 164 changed_event.client_id(), 165 changed_event.window_id(), 166 changed_event.rect().to_string().characters()); 167#endif 168 break; 169 } 170 171 case GUI::Event::WM_WindowIconBitmapChanged: { 172 auto& changed_event = static_cast<GUI::WMWindowIconBitmapChangedEvent&>(event); 173#ifdef EVENT_DEBUG 174 dbgprintf("WM_WindowIconBitmapChanged: client_id=%d, window_id=%d, icon_buffer_id=%d\n", 175 changed_event.client_id(), 176 changed_event.window_id(), 177 changed_event.icon_buffer_id()); 178#endif 179 if (auto* window = WindowList::the().window(identifier)) { 180 auto buffer = SharedBuffer::create_from_shared_buffer_id(changed_event.icon_buffer_id()); 181 ASSERT(buffer); 182 window->button()->set_icon(Gfx::Bitmap::create_with_shared_buffer(Gfx::BitmapFormat::RGBA32, *buffer, changed_event.icon_size())); 183 } 184 break; 185 } 186 187 case GUI::Event::WM_WindowStateChanged: { 188 auto& changed_event = static_cast<GUI::WMWindowStateChangedEvent&>(event); 189#ifdef EVENT_DEBUG 190 dbgprintf("WM_WindowStateChanged: client_id=%d, window_id=%d, title=%s, rect=%s, is_active=%u, is_minimized=%u\n", 191 changed_event.client_id(), 192 changed_event.window_id(), 193 changed_event.title().characters(), 194 changed_event.rect().to_string().characters(), 195 changed_event.is_active(), 196 changed_event.is_minimized()); 197#endif 198 if (!should_include_window(changed_event.window_type())) 199 break; 200 auto& window = WindowList::the().ensure_window(identifier); 201 window.set_title(changed_event.title()); 202 window.set_rect(changed_event.rect()); 203 window.set_active(changed_event.is_active()); 204 window.set_minimized(changed_event.is_minimized()); 205 if (window.is_minimized()) { 206 window.button()->set_foreground_color(Color::DarkGray); 207 window.button()->set_text(String::format("[%s]", changed_event.title().characters())); 208 } else { 209 window.button()->set_foreground_color(Color::Black); 210 window.button()->set_text(changed_event.title()); 211 } 212 window.button()->set_checked(changed_event.is_active()); 213 break; 214 } 215 default: 216 break; 217 } 218}