Prechádzať zdrojové kódy

Taskbar: More bringup work. We now see a basic window list.

Andreas Kling 6 rokov pred
rodič
commit
96104b5524

+ 1 - 0
Applications/Taskbar/Makefile

@@ -1,6 +1,7 @@
 OBJS = \
     TaskbarWindow.o \
     TaskbarWidget.o \
+    WindowList.o \
     main.o
 
 APP = Taskbar

+ 4 - 3
Applications/Taskbar/TaskbarWidget.cpp

@@ -5,13 +5,14 @@
 #include <LibGUI/GPainter.h>
 #include <stdio.h>
 
-TaskbarWidget::TaskbarWidget(GWidget* parent)
+TaskbarWidget::TaskbarWidget(WindowList& window_list, GWidget* parent)
     : GFrame(parent)
+    , m_window_list(window_list)
 {
     set_fill_with_background_color(true);
     set_layout(make<GBoxLayout>(Orientation::Horizontal));
-    layout()->set_margins({ 0, 8, 0, 8 });
-    layout()->set_spacing(8);
+    layout()->set_margins({ 0, 3, 0, 3 });
+    layout()->set_spacing(3);
 
     set_frame_thickness(1);
     set_frame_shape(GFrame::Shape::Panel);

+ 4 - 1
Applications/Taskbar/TaskbarWidget.h

@@ -1,8 +1,10 @@
 #include <LibGUI/GFrame.h>
 
+class WindowList;
+
 class TaskbarWidget final : public GFrame {
 public:
-    TaskbarWidget(GWidget* parent = nullptr);
+    TaskbarWidget(WindowList&, GWidget* parent = nullptr);
     virtual ~TaskbarWidget() override;
 
     virtual const char* class_name() const override { return "TaskbarWidget"; }
@@ -10,4 +12,5 @@ public:
 private:
     virtual void paint_event(GPaintEvent&) override;
 
+    WindowList& m_window_list;
 };

+ 47 - 5
Applications/Taskbar/TaskbarWindow.cpp

@@ -3,6 +3,7 @@
 #include <LibGUI/GWindow.h>
 #include <LibGUI/GDesktop.h>
 #include <LibGUI/GEventLoop.h>
+#include <LibGUI/GButton.h>
 #include <WindowServer/WSAPITypes.h>
 #include <stdio.h>
 
@@ -16,7 +17,7 @@ TaskbarWindow::TaskbarWindow()
 
     GDesktop::the().on_rect_change = [this] (const Rect& rect) { on_screen_rect_change(rect); };
 
-    auto* widget = new TaskbarWidget;
+    auto* widget = new TaskbarWidget(m_window_list);
     set_main_widget(widget);
 }
 
@@ -32,10 +33,51 @@ void TaskbarWindow::on_screen_rect_change(const Rect& rect)
 
 void TaskbarWindow::wm_event(GWMEvent& event)
 {
-#if 0
+    WindowIdentifier identifier { event.client_id(), event.window_id() };
     switch (event.type()) {
-    case GEvent::WM_WindowAdded:
-        m_window_list.append({})
+    case GEvent::WM_WindowAdded: {
+        auto& added_event = static_cast<GWMWindowAddedEvent&>(event);
+        printf("WM_WindowAdded: client_id=%d, window_id=%d, title=%s, rect=%s\n",
+            added_event.client_id(),
+            added_event.window_id(),
+            added_event.title().characters(),
+            added_event.rect().to_string().characters()
+        );
+        auto& window = m_window_list.ensure_window(identifier);
+        window.set_title(added_event.title());
+        window.set_rect(added_event.rect());
+        window.set_button(new GButton(main_widget()));
+        window.button()->set_size_policy(SizePolicy::Fixed, SizePolicy::Fixed);
+        window.button()->set_preferred_size({ 100, 22 });
+        window.button()->set_caption(window.title());
+        update();
+        break;
+    }
+    case GEvent::WM_WindowRemoved: {
+        auto& removed_event = static_cast<GWMWindowRemovedEvent&>(event);
+        printf("WM_WindowRemoved: client_id=%d, window_id=%d\n",
+            removed_event.client_id(),
+            removed_event.window_id()
+        );
+        m_window_list.remove_window(identifier);
+        update();
+        break;
+    }
+    case GEvent::WM_WindowStateChanged: {
+        auto& changed_event = static_cast<GWMWindowStateChangedEvent&>(event);
+        printf("WM_WindowStateChanged: client_id=%d, window_id=%d, title=%s, rect=%s\n",
+            changed_event.client_id(),
+            changed_event.window_id(),
+            changed_event.title().characters(),
+            changed_event.rect().to_string().characters()
+        );
+        auto& window = m_window_list.ensure_window(identifier);
+        window.set_title(changed_event.title());
+        window.set_rect(changed_event.rect());
+        window.button()->set_caption(changed_event.title());
+        break;
+    }
+    default:
+        break;
     }
-#endif
 }

+ 4 - 1
Applications/Taskbar/TaskbarWindow.h

@@ -1,12 +1,13 @@
 #include <LibGUI/GWindow.h>
 #include <LibGUI/GWidget.h>
+#include "WindowList.h"
 
 class TaskbarWindow final : public GWindow {
 public:
     TaskbarWindow();
     virtual ~TaskbarWindow() override;
 
-    int taskbar_height() const { return 20; }
+    int taskbar_height() const { return 28; }
 
     virtual const char* class_name() const override { return "TaskbarWindow"; }
 
@@ -14,4 +15,6 @@ private:
     void on_screen_rect_change(const Rect&);
 
     virtual void wm_event(GWMEvent&) override;
+
+    WindowList m_window_list;
 };

+ 17 - 0
Applications/Taskbar/WindowList.cpp

@@ -0,0 +1,17 @@
+#include "WindowList.h"
+
+Window& WindowList::ensure_window(const WindowIdentifier& identifier)
+{
+    auto it = m_windows.find(identifier);
+    if (it != m_windows.end())
+        return *it->value;
+    auto window = make<Window>(identifier);
+    auto& window_ref = *window;
+    m_windows.set(identifier, move(window));
+    return window_ref;
+}
+
+void WindowList::remove_window(const WindowIdentifier& identifier)
+{
+    m_windows.remove(identifier);
+}

+ 80 - 0
Applications/Taskbar/WindowList.h

@@ -0,0 +1,80 @@
+#pragma once
+
+#include <AK/AKString.h>
+#include <AK/HashMap.h>
+#include <AK/Traits.h>
+#include <SharedGraphics/Rect.h>
+#include <LibGUI/GButton.h>
+
+class WindowIdentifier {
+public:
+    WindowIdentifier(int client_id, int window_id)
+        : m_client_id(client_id)
+        , m_window_id(window_id)
+    {
+    }
+
+    int client_id() const { return m_client_id; }
+    int window_id() const { return m_window_id; }
+
+    bool operator==(const WindowIdentifier& other) const
+    {
+        return m_client_id == other.m_client_id && m_window_id == other.m_window_id;
+    }
+private:
+    int m_client_id { -1 };
+    int m_window_id { -1 };
+};
+
+namespace AK {
+template<>
+struct Traits<WindowIdentifier> {
+    static unsigned hash(const WindowIdentifier& w) { return pair_int_hash(w.client_id(), w.window_id()); }
+    static void dump(const WindowIdentifier& w) { kprintf("WindowIdentifier(%d, %d)", w.client_id(), w.window_id()); }
+};
+}
+
+class Window {
+public:
+    explicit Window(const WindowIdentifier& identifier)
+        : m_identifier(identifier)
+    {
+    }
+
+    ~Window()
+    {
+        delete m_button;
+    }
+
+    WindowIdentifier identifier() const { return m_identifier; }
+
+    String title() const { return m_title; }
+    void set_title(const String& title) { m_title = title; }
+
+    Rect rect() const { return m_rect; }
+    void set_rect(const Rect& rect) { m_rect = rect; }
+
+    GButton* button() { return m_button; }
+    void set_button(GButton* button) { m_button = button; }
+
+private:
+    WindowIdentifier m_identifier;
+    String m_title;
+    Rect m_rect;
+    GButton* m_button { nullptr };
+};
+
+class WindowList {
+public:
+    template<typename Callback> void for_each_window(Callback callback)
+    {
+        for (auto& it : m_windows)
+            callback(*it.value);
+    }
+
+    Window& ensure_window(const WindowIdentifier&);
+    void remove_window(const WindowIdentifier&);
+
+private:
+    HashMap<WindowIdentifier, OwnPtr<Window>> m_windows;
+};

+ 1 - 1
LibC/SharedBuffer.cpp

@@ -51,6 +51,6 @@ void SharedBuffer::seal()
     int rc = seal_shared_buffer(m_shared_buffer_id);
     if (rc < 0) {
         perror("seal_shared_buffer");
-        exit(1);
+        ASSERT_NOT_REACHED();
     }
 }

+ 5 - 0
LibGUI/GBoxLayout.cpp

@@ -34,6 +34,9 @@ void GBoxLayout::run(GWidget& widget)
         dbgprintf("GBoxLayout:  Starting with available size: %s\n", available_size.to_string().characters());
 
     for (auto& entry : m_entries) {
+        if (!entry.widget)
+            continue;
+
         if (!entry.widget->is_visible())
             continue;
         ++number_of_visible_entries;
@@ -81,6 +84,8 @@ void GBoxLayout::run(GWidget& widget)
     int current_y = margins().top();
 
     for (auto& entry : m_entries) {
+        if (!entry.widget)
+            continue;
         if (!entry.widget->is_visible())
             continue;
         Rect rect(current_x, current_y, 0, 0);

+ 16 - 0
LibGUI/GEventLoop.cpp

@@ -268,6 +268,17 @@ void GEventLoop::handle_menu_event(const WSAPI_ServerMessage& event)
     ASSERT_NOT_REACHED();
 }
 
+void GEventLoop::handle_wm_event(const WSAPI_ServerMessage& event, GWindow& window)
+{
+    if (event.type == WSAPI_ServerMessage::WM_WindowAdded)
+        return post_event(window, make<GWMWindowAddedEvent>(event.wm.client_id, event.wm.window_id, String(event.text, event.text_length), event.wm.rect));
+    if (event.type == WSAPI_ServerMessage::WM_WindowStateChanged)
+        return post_event(window, make<GWMWindowStateChangedEvent>(event.wm.client_id, event.wm.window_id, String(event.text, event.text_length), event.wm.rect));
+    if (event.type == WSAPI_ServerMessage::WM_WindowRemoved)
+        return post_event(window, make<GWMWindowRemovedEvent>(event.wm.client_id, event.wm.window_id));
+    ASSERT_NOT_REACHED();
+}
+
 void GEventLoop::wait_for_event()
 {
     fd_set rfds;
@@ -397,6 +408,11 @@ void GEventLoop::process_unprocessed_messages()
         case WSAPI_ServerMessage::Type::WindowResized:
             handle_resize_event(event, *window);
             break;
+        case WSAPI_ServerMessage::Type::WM_WindowAdded:
+        case WSAPI_ServerMessage::Type::WM_WindowRemoved:
+        case WSAPI_ServerMessage::Type::WM_WindowStateChanged:
+            handle_wm_event(event, *window);
+            break;
         default:
             break;
         }

+ 1 - 0
LibGUI/GEventLoop.h

@@ -60,6 +60,7 @@ private:
     void handle_window_close_request_event(const WSAPI_ServerMessage&, GWindow&);
     void handle_menu_event(const WSAPI_ServerMessage&);
     void handle_window_entered_or_left_event(const WSAPI_ServerMessage&, GWindow&);
+    void handle_wm_event(const WSAPI_ServerMessage&, GWindow&);
     void get_next_timer_expiration(timeval&);
     void connect_to_server();
 

+ 9 - 0
LibGUI/GLayout.cpp

@@ -40,6 +40,15 @@ void GLayout::add_widget(GWidget& widget)
         m_owner->notify_layout_changed(Badge<GLayout>());
 }
 
+void GLayout::remove_widget(GWidget& widget)
+{
+    m_entries.remove_first_matching([&] (auto& entry) {
+        return entry.widget.ptr() == &widget;
+    });
+    if (m_owner)
+        m_owner->notify_layout_changed(Badge<GLayout>());
+}
+
 void GLayout::set_spacing(int spacing)
 {
     if (m_spacing == spacing)

+ 2 - 0
LibGUI/GLayout.h

@@ -16,6 +16,8 @@ public:
     void add_widget(GWidget&);
     void add_layout(OwnPtr<GLayout>&&);
 
+    void remove_widget(GWidget&);
+
     virtual void run(GWidget&) = 0;
 
     void notify_adopted(Badge<GWidget>, GWidget&);

+ 4 - 0
LibGUI/GWidget.cpp

@@ -27,6 +27,10 @@ void GWidget::child_event(GChildEvent& event)
         if (event.child() && event.child()->is_widget() && layout())
             layout()->add_widget(static_cast<GWidget&>(*event.child()));
     }
+    if (event.type() == GEvent::ChildRemoved) {
+        if (event.child() && event.child()->is_widget() && layout())
+            layout()->remove_widget(static_cast<GWidget&>(*event.child()));
+    }
     return GObject::child_event(event);
 }
 

+ 8 - 0
Servers/WindowServer/WSAPITypes.h

@@ -91,6 +91,9 @@ struct WSAPI_ServerMessage {
         DidSetWallpaper,
         DidGetWallpaper,
         ScreenRectChanged,
+        WM_WindowAdded,
+        WM_WindowRemoved,
+        WM_WindowStateChanged,
     };
     Type type { Invalid };
     int window_id { -1 };
@@ -103,6 +106,11 @@ struct WSAPI_ServerMessage {
             int server_pid;
             WSAPI_Rect screen_rect;
         } greeting;
+        struct {
+            int client_id;
+            int window_id;
+            WSAPI_Rect rect;
+        } wm;
         struct {
             WSAPI_Rect rect;
         } screen;

+ 63 - 0
Servers/WindowServer/WSMessage.h

@@ -26,6 +26,10 @@ public:
         WindowCloseRequest,
         WindowResized,
 
+        WM_WindowAdded,
+        WM_WindowRemoved,
+        WM_WindowStateChanged,
+
         __Begin_API_Client_Requests,
         APICreateMenubarRequest,
         APIDestroyMenubarRequest,
@@ -581,3 +585,62 @@ private:
     Rect m_old_rect;
     Rect m_rect;
 };
+
+class WSWMEvent : public WSMessage {
+public:
+    WSWMEvent(Type type, int client_id, int window_id)
+        : WSMessage(type)
+        , m_client_id(client_id)
+        , m_window_id(window_id)
+    {
+    }
+
+    int client_id() const { return m_client_id; }
+    int window_id() const { return m_window_id; }
+
+private:
+    int m_client_id;
+    int m_window_id;
+};
+
+class WSWMWindowAddedEvent : public WSWMEvent {
+public:
+    WSWMWindowAddedEvent(int client_id, int window_id, const String& title, const Rect& rect)
+        : WSWMEvent(WSMessage::WM_WindowAdded, client_id, window_id)
+        , m_title(title)
+        , m_rect(rect)
+    {
+    }
+
+    String title() const { return m_title; }
+    Rect rect() const { return m_rect; }
+
+private:
+    String m_title;
+    Rect m_rect;
+};
+
+class WSWMWindowRemovedEvent : public WSWMEvent {
+public:
+    WSWMWindowRemovedEvent(int client_id, int window_id)
+        : WSWMEvent(WSMessage::WM_WindowRemoved, client_id, window_id)
+    {
+    }
+};
+
+class WSWMWindowStateChangedEvent : public WSWMEvent {
+public:
+    WSWMWindowStateChangedEvent(int client_id, int window_id, const String& title, const Rect& rect)
+        : WSWMEvent(WSMessage::WM_WindowStateChanged, client_id, window_id)
+        , m_title(title)
+        , m_rect(rect)
+    {
+    }
+
+    String title() const { return m_title; }
+    Rect rect() const { return m_rect; }
+
+private:
+    String m_title;
+    Rect m_rect;
+};

+ 33 - 0
Servers/WindowServer/WSWindow.cpp

@@ -28,6 +28,9 @@ WSWindow::WSWindow(WSClientConnection& client, WSWindowType window_type, int win
     , m_window_id(window_id)
     , m_icon(default_window_icon())
 {
+    // FIXME: This should not be hard-coded here.
+    if (m_type == WSWindowType::Taskbar)
+        m_listens_to_wm_events = true;
     WSWindowManager::the().add_window(*this);
 }
 
@@ -138,6 +141,36 @@ void WSWindow::on_message(const WSMessage& message)
         server_message.window.old_rect = static_cast<const WSResizeEvent&>(message).old_rect();
         server_message.window.rect = static_cast<const WSResizeEvent&>(message).rect();
         break;
+    case WSMessage::WM_WindowAdded: {
+        auto& added_event = static_cast<const WSWMWindowAddedEvent&>(message);
+        server_message.type = WSAPI_ServerMessage::Type::WM_WindowAdded;
+        server_message.wm.client_id = added_event.client_id();
+        server_message.wm.window_id = added_event.window_id();
+        ASSERT(added_event.title().length() < sizeof(server_message.text));
+        memcpy(server_message.text, added_event.title().characters(), added_event.title().length());
+        server_message.text_length = added_event.title().length();
+        server_message.wm.rect = added_event.rect();
+        break;
+    }
+    case WSMessage::WM_WindowRemoved: {
+        auto& removed_event = static_cast<const WSWMWindowRemovedEvent&>(message);
+        server_message.type = WSAPI_ServerMessage::Type::WM_WindowRemoved;
+        server_message.wm.client_id = removed_event.client_id();
+        server_message.wm.window_id = removed_event.window_id();
+        break;
+    }
+    case WSMessage::WM_WindowStateChanged: {
+        auto& changed_event = static_cast<const WSWMWindowStateChangedEvent&>(message);
+        server_message.type = WSAPI_ServerMessage::Type::WM_WindowStateChanged;
+        server_message.wm.client_id = changed_event.client_id();
+        server_message.wm.window_id = changed_event.window_id();
+        ASSERT(changed_event.title().length() < sizeof(server_message.text));
+        memcpy(server_message.text, changed_event.title().characters(), changed_event.title().length());
+        server_message.text_length = changed_event.title().length();
+        server_message.wm.rect = changed_event.rect();
+        break;
+    }
+
     default:
         break;
     }

+ 3 - 0
Servers/WindowServer/WSWindow.h

@@ -20,6 +20,8 @@ public:
 
     bool is_blocked_by_modal_window() const;
 
+    bool listens_to_wm_events() const { return m_listens_to_wm_events; }
+
     WSClientConnection* client() { return m_client; }
     const WSClientConnection* client() const { return m_client; }
 
@@ -124,6 +126,7 @@ private:
     bool m_has_painted_since_last_resize { false };
     bool m_modal { false };
     bool m_resizable { false };
+    bool m_listens_to_wm_events { false };
     RetainPtr<GraphicsBitmap> m_backing_store;
     RetainPtr<GraphicsBitmap> m_last_backing_store;
     int m_window_id { -1 };

+ 25 - 1
Servers/WindowServer/WSWindowManager.cpp

@@ -294,7 +294,7 @@ void get_cpu_usage(unsigned& busy, unsigned& idle)
     FILE* fp = fopen("/proc/all", "r");
     if (!fp) {
         perror("failed to open /proc/all");
-        exit(1);
+        ASSERT_NOT_REACHED();
     }
     for (;;) {
         char buf[BUFSIZ];
@@ -507,6 +507,12 @@ void WSWindowManager::add_window(WSWindow& window)
         set_active_window(&window);
     if (m_switcher.is_visible() && window.type() != WSWindowType::WindowSwitcher)
         m_switcher.refresh();
+
+    for_each_window_listening_to_wm_events([&window] (WSWindow& listener) {
+        if (window.client())
+            WSMessageLoop::the().post_message(listener, make<WSWMWindowAddedEvent>(window.client()->client_id(), window.window_id(), window.title(), window.rect()));
+        return IterationDecision::Continue;
+    });
 }
 
 void WSWindowManager::move_to_front_and_make_active(WSWindow& window)
@@ -534,6 +540,12 @@ void WSWindowManager::remove_window(WSWindow& window)
         set_active_window(*m_windows.begin());
     if (m_switcher.is_visible() && window.type() != WSWindowType::WindowSwitcher)
         m_switcher.refresh();
+
+    for_each_window_listening_to_wm_events([&window] (WSWindow& listener) {
+        if (window.client())
+            WSMessageLoop::the().post_message(listener, make<WSWMWindowRemovedEvent>(window.client()->client_id(), window.window_id()));
+        return IterationDecision::Continue;
+    });
 }
 
 void WSWindowManager::notify_title_changed(WSWindow& window)
@@ -542,6 +554,12 @@ void WSWindowManager::notify_title_changed(WSWindow& window)
     invalidate(outer_window_rect(window));
     if (m_switcher.is_visible())
         m_switcher.refresh();
+
+    for_each_window_listening_to_wm_events([&window] (WSWindow& listener) {
+        if (window.client())
+            WSMessageLoop::the().post_message(listener, make<WSWMWindowStateChangedEvent>(window.client()->client_id(), window.window_id(), window.title(), window.rect()));
+        return IterationDecision::Continue;
+    });
 }
 
 void WSWindowManager::notify_rect_changed(WSWindow& window, const Rect& old_rect, const Rect& new_rect)
@@ -553,6 +571,12 @@ void WSWindowManager::notify_rect_changed(WSWindow& window, const Rect& old_rect
     invalidate(outer_window_rect(new_rect));
     if (m_switcher.is_visible() && window.type() != WSWindowType::WindowSwitcher)
         m_switcher.refresh();
+
+    for_each_window_listening_to_wm_events([&window] (WSWindow& listener) {
+        if (window.client())
+            WSMessageLoop::the().post_message(listener, make<WSWMWindowStateChangedEvent>(window.client()->client_id(), window.window_id(), window.title(), window.rect()));
+        return IterationDecision::Continue;
+    });
 }
 
 void WSWindowManager::handle_menu_mouse_event(WSMenu& menu, const WSMouseEvent& event)

+ 12 - 0
Servers/WindowServer/WSWindowManager.h

@@ -112,6 +112,7 @@ private:
     template<typename Callback> IterationDecision for_each_visible_window_of_type_from_front_to_back(WSWindowType, Callback);
     template<typename Callback> IterationDecision for_each_visible_window_from_front_to_back(Callback);
     template<typename Callback> IterationDecision for_each_visible_window_from_back_to_front(Callback);
+    template<typename Callback> void for_each_window_listening_to_wm_events(Callback);
     template<typename Callback> void for_each_active_menubar_menu(Callback);
     void close_current_menu();
     virtual void on_message(const WSMessage&) override;
@@ -265,3 +266,14 @@ IterationDecision WSWindowManager::for_each_visible_window_from_front_to_back(Ca
         return IterationDecision::Abort;
     return for_each_visible_window_of_type_from_front_to_back(WSWindowType::WindowSwitcher, callback);
 }
+
+template<typename Callback>
+void WSWindowManager::for_each_window_listening_to_wm_events(Callback callback)
+{
+    for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) {
+        if (!window->listens_to_wm_events())
+            continue;
+        if (callback(*window) == IterationDecision::Abort)
+            return;
+    }
+}