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