Browse Source

LibCore: Add CEventLoop and make LibGUI/GEventLoop inherit from it.

This is shaping up to be quite nice.
Andreas Kling 6 years ago
parent
commit
b2542414d7
6 changed files with 366 additions and 289 deletions
  1. 1 1
      LibCore/CEvent.h
  2. 261 0
      LibCore/CEventLoop.cpp
  3. 76 0
      LibCore/CEventLoop.h
  4. 1 0
      LibCore/Makefile
  5. 3 233
      LibGUI/GEventLoop.cpp
  6. 24 55
      LibGUI/GEventLoop.h

+ 1 - 1
LibCore/CEvent.h

@@ -30,7 +30,7 @@ private:
 };
 };
 
 
 class CDeferredInvocationEvent : public CEvent {
 class CDeferredInvocationEvent : public CEvent {
-    friend class GEventLoop;
+    friend class CEventLoop;
 public:
 public:
     CDeferredInvocationEvent(Function<void(CObject&)> invokee)
     CDeferredInvocationEvent(Function<void(CObject&)> invokee)
         : CEvent(CEvent::Type::DeferredInvoke)
         : CEvent(CEvent::Type::DeferredInvoke)

+ 261 - 0
LibCore/CEventLoop.cpp

@@ -0,0 +1,261 @@
+#include <LibCore/CObject.h>
+#include <LibCore/CEventLoop.h>
+#include <LibCore/CEvent.h>
+#include <LibGUI/GNotifier.h>
+#include <LibC/unistd.h>
+#include <LibC/stdio.h>
+#include <LibC/fcntl.h>
+#include <LibC/string.h>
+#include <LibC/time.h>
+#include <LibC/sys/select.h>
+#include <LibC/sys/socket.h>
+#include <LibC/sys/time.h>
+#include <LibC/errno.h>
+#include <LibC/string.h>
+#include <LibC/stdlib.h>
+
+//#define CEVENTLOOP_DEBUG
+
+static CEventLoop* s_main_event_loop;
+static Vector<CEventLoop*>* s_event_loop_stack;
+HashMap<int, OwnPtr<CEventLoop::EventLoopTimer>>* CEventLoop::s_timers;
+HashTable<GNotifier*>* CEventLoop::s_notifiers;
+int CEventLoop::s_next_timer_id = 1;
+
+CEventLoop::CEventLoop()
+{
+    if (!s_event_loop_stack) {
+        s_event_loop_stack = new Vector<CEventLoop*>;
+        s_timers = new HashMap<int, OwnPtr<CEventLoop::EventLoopTimer>>;
+        s_notifiers = new HashTable<GNotifier*>;
+    }
+
+    if (!s_main_event_loop) {
+        s_main_event_loop = this;
+        s_event_loop_stack->append(this);
+    }
+
+#ifdef CEVENTLOOP_DEBUG
+    dbgprintf("(%u) CEventLoop constructed :)\n", getpid());
+#endif
+}
+
+CEventLoop::~CEventLoop()
+{
+}
+
+CEventLoop& CEventLoop::main()
+{
+    ASSERT(s_main_event_loop);
+    return *s_main_event_loop;
+}
+
+CEventLoop& CEventLoop::current()
+{
+    return *s_event_loop_stack->last();
+}
+
+void CEventLoop::quit(int code)
+{
+    m_exit_requested = true;
+    m_exit_code = code;
+}
+
+struct CEventLoopPusher {
+public:
+    CEventLoopPusher(CEventLoop& event_loop) : m_event_loop(event_loop)
+    {
+        if (&m_event_loop != s_main_event_loop) {
+            m_event_loop.take_pending_events_from(CEventLoop::current());
+            s_event_loop_stack->append(&event_loop);
+        }
+    }
+    ~CEventLoopPusher()
+    {
+        if (&m_event_loop != s_main_event_loop) {
+            s_event_loop_stack->take_last();
+            CEventLoop::current().take_pending_events_from(m_event_loop);
+        }
+    }
+private:
+    CEventLoop& m_event_loop;
+};
+
+int CEventLoop::exec()
+{
+    CEventLoopPusher pusher(*this);
+
+    m_running = true;
+    for (;;) {
+        if (m_exit_requested)
+            return m_exit_code;
+        do_processing();
+        if (m_queued_events.is_empty()) {
+            wait_for_event();
+            do_processing();
+        }
+        Vector<QueuedEvent> events = move(m_queued_events);
+        for (auto& queued_event : events) {
+            auto* receiver = queued_event.receiver.ptr();
+            auto& event = *queued_event.event;
+#ifdef CEVENTLOOP_DEBUG
+            dbgprintf("CEventLoop: %s{%p} event %u\n", receiver->class_name(), receiver, (unsigned)event.type());
+#endif
+            if (!receiver) {
+                switch (event.type()) {
+                case CEvent::Quit:
+                    ASSERT_NOT_REACHED();
+                    return 0;
+                default:
+                    dbgprintf("Event type %u with no receiver :(\n", event.type());
+                }
+            } else if (event.type() == CEvent::Type::DeferredInvoke) {
+                printf("DeferredInvoke: receiver=%s{%p}\n", receiver->class_name(), receiver);
+                static_cast<CDeferredInvocationEvent&>(event).m_invokee(*receiver);
+            } else {
+                receiver->event(event);
+            }
+
+            if (m_exit_requested) {
+                auto rejigged_event_queue = move(events);
+                rejigged_event_queue.append(move(m_queued_events));
+                m_queued_events = move(rejigged_event_queue);
+                return m_exit_code;
+            }
+        }
+    }
+    ASSERT_NOT_REACHED();
+}
+
+void CEventLoop::post_event(CObject& receiver, OwnPtr<CEvent>&& event)
+{
+#ifdef CEVENTLOOP_DEBUG
+    dbgprintf("CEventLoop::post_event: {%u} << receiver=%p, event=%p\n", m_queued_events.size(), &receiver, event.ptr());
+#endif
+    m_queued_events.append({ receiver.make_weak_ptr(), move(event) });
+}
+
+void CEventLoop::wait_for_event()
+{
+    fd_set rfds;
+    fd_set wfds;
+    FD_ZERO(&rfds);
+    FD_ZERO(&wfds);
+
+    int max_fd = 0;
+    auto add_fd_to_set = [&max_fd] (int fd, fd_set& set){
+        FD_SET(fd, &set);
+        if (fd > max_fd)
+            max_fd = fd;
+    };
+
+    int max_fd_added = -1;
+    add_file_descriptors_for_select(rfds, max_fd_added);
+    max_fd = max(max_fd, max_fd_added);
+    for (auto& notifier : *s_notifiers) {
+        if (notifier->event_mask() & GNotifier::Read)
+            add_fd_to_set(notifier->fd(), rfds);
+        if (notifier->event_mask() & GNotifier::Write)
+            add_fd_to_set(notifier->fd(), wfds);
+        if (notifier->event_mask() & GNotifier::Exceptional)
+            ASSERT_NOT_REACHED();
+    }
+
+    struct timeval timeout = { 0, 0 };
+    if (!s_timers->is_empty() && m_queued_events.is_empty())
+        get_next_timer_expiration(timeout);
+
+    int rc = select(max_fd + 1, &rfds, &wfds, nullptr, (m_queued_events.is_empty() && s_timers->is_empty()) ? nullptr : &timeout);
+    if (rc < 0) {
+        ASSERT_NOT_REACHED();
+    }
+
+    for (auto& it : *s_timers) {
+        auto& timer = *it.value;
+        if (!timer.has_expired())
+            continue;
+#ifdef CEVENTLOOP_DEBUG
+        dbgprintf("CEventLoop: Timer %d has expired, sending CTimerEvent to %p\n", timer.timer_id, timer.owner);
+#endif
+        post_event(*timer.owner, make<CTimerEvent>(timer.timer_id));
+        if (timer.should_reload) {
+            timer.reload();
+        } else {
+            // FIXME: Support removing expired timers that don't want to reload.
+            ASSERT_NOT_REACHED();
+        }
+    }
+
+    for (auto& notifier : *s_notifiers) {
+        if (FD_ISSET(notifier->fd(), &rfds)) {
+            if (notifier->on_ready_to_read)
+                notifier->on_ready_to_read(*notifier);
+        }
+        if (FD_ISSET(notifier->fd(), &wfds)) {
+            if (notifier->on_ready_to_write)
+                notifier->on_ready_to_write(*notifier);
+        }
+    }
+
+    process_file_descriptors_after_select(rfds);
+}
+
+bool CEventLoop::EventLoopTimer::has_expired() const
+{
+    timeval now;
+    gettimeofday(&now, nullptr);
+    return now.tv_sec > fire_time.tv_sec || (now.tv_sec == fire_time.tv_sec && now.tv_usec >= fire_time.tv_usec);
+}
+
+void CEventLoop::EventLoopTimer::reload()
+{
+    gettimeofday(&fire_time, nullptr);
+    fire_time.tv_sec += interval / 1000;
+    fire_time.tv_usec += (interval % 1000) * 1000;
+}
+
+void CEventLoop::get_next_timer_expiration(timeval& soonest)
+{
+    ASSERT(!s_timers->is_empty());
+    bool has_checked_any = false;
+    for (auto& it : *s_timers) {
+        auto& fire_time = it.value->fire_time;
+        if (!has_checked_any || fire_time.tv_sec < soonest.tv_sec || (fire_time.tv_sec == soonest.tv_sec && fire_time.tv_usec < soonest.tv_usec))
+            soonest = fire_time;
+        has_checked_any = true;
+    }
+}
+
+int CEventLoop::register_timer(CObject& object, int milliseconds, bool should_reload)
+{
+    ASSERT(milliseconds >= 0);
+    auto timer = make<EventLoopTimer>();
+    timer->owner = object.make_weak_ptr();
+    timer->interval = milliseconds;
+    timer->reload();
+    timer->should_reload = should_reload;
+    int timer_id = ++s_next_timer_id;  // FIXME: This will eventually wrap around.
+    ASSERT(timer_id); // FIXME: Aforementioned wraparound.
+    timer->timer_id = timer_id;
+    s_timers->set(timer->timer_id, move(timer));
+    return timer_id;
+}
+
+bool CEventLoop::unregister_timer(int timer_id)
+{
+    auto it = s_timers->find(timer_id);
+    if (it == s_timers->end())
+        return false;
+    s_timers->remove(it);
+    return true;
+}
+
+void CEventLoop::register_notifier(Badge<GNotifier>, GNotifier& notifier)
+{
+    s_notifiers->set(&notifier);
+}
+
+void CEventLoop::unregister_notifier(Badge<GNotifier>, GNotifier& notifier)
+{
+    s_notifiers->remove(&notifier);
+}

+ 76 - 0
LibCore/CEventLoop.h

@@ -0,0 +1,76 @@
+#pragma once
+
+#include <AK/Badge.h>
+#include <AK/HashMap.h>
+#include <AK/OwnPtr.h>
+#include <AK/Vector.h>
+#include <AK/WeakPtr.h>
+#include <sys/select.h>
+#include <time.h>
+
+class CEvent;
+class CObject;
+class GNotifier;
+
+class CEventLoop {
+public:
+    CEventLoop();
+    virtual ~CEventLoop();
+
+    int exec();
+
+    void post_event(CObject& receiver, OwnPtr<CEvent>&&);
+
+    static CEventLoop& main();
+    static CEventLoop& current();
+
+    bool running() const { return m_running; }
+
+    static int register_timer(CObject&, int milliseconds, bool should_reload);
+    static bool unregister_timer(int timer_id);
+
+    static void register_notifier(Badge<GNotifier>, GNotifier&);
+    static void unregister_notifier(Badge<GNotifier>, GNotifier&);
+
+    void quit(int);
+
+    virtual void take_pending_events_from(CEventLoop& other)
+    {
+        m_queued_events.append(move(other.m_queued_events));
+    }
+
+protected:
+    virtual void add_file_descriptors_for_select(fd_set&, int& max_fd) { UNUSED_PARAM(max_fd); }
+    virtual void process_file_descriptors_after_select(const fd_set&) { }
+    virtual void do_processing() { }
+
+private:
+    void wait_for_event();
+    void get_next_timer_expiration(timeval&);
+
+    struct QueuedEvent {
+        WeakPtr<CObject> receiver;
+        OwnPtr<CEvent> event;
+    };
+    Vector<QueuedEvent> m_queued_events;
+
+    bool m_running { false };
+    bool m_exit_requested { false };
+    int m_exit_code { 0 };
+
+    struct EventLoopTimer {
+        int timer_id { 0 };
+        int interval { 0 };
+        timeval fire_time;
+        bool should_reload { false };
+        WeakPtr<CObject> owner;
+
+        void reload();
+        bool has_expired() const;
+    };
+
+    static HashMap<int, OwnPtr<EventLoopTimer>>* s_timers;
+    static int s_next_timer_id;
+
+    static HashTable<GNotifier*>* s_notifiers;
+};

+ 1 - 0
LibCore/Makefile

@@ -1,6 +1,7 @@
 OBJS = \
 OBJS = \
     CElapsedTimer.o \
     CElapsedTimer.o \
     CObject.o \
     CObject.o \
+    CEventLoop.o \
     CEvent.o
     CEvent.o
 
 
 LIBRARY = libcore.a
 LIBRARY = libcore.a

+ 3 - 233
LibGUI/GEventLoop.cpp

@@ -23,13 +23,8 @@
 //#define COALESCING_DEBUG
 //#define COALESCING_DEBUG
 
 
 static HashMap<GShortcut, GAction*>* g_actions;
 static HashMap<GShortcut, GAction*>* g_actions;
-static GEventLoop* s_main_event_loop;
-static Vector<GEventLoop*>* s_event_loop_stack;
 int GEventLoop::s_event_fd = -1;
 int GEventLoop::s_event_fd = -1;
 pid_t GEventLoop::s_server_pid = -1;
 pid_t GEventLoop::s_server_pid = -1;
-HashMap<int, OwnPtr<GEventLoop::EventLoopTimer>>* GEventLoop::s_timers;
-HashTable<GNotifier*>* GEventLoop::s_notifiers;
-int GEventLoop::s_next_timer_id = 1;
 
 
 void GEventLoop::connect_to_server()
 void GEventLoop::connect_to_server()
 {
 {
@@ -70,16 +65,10 @@ void GEventLoop::connect_to_server()
 
 
 GEventLoop::GEventLoop()
 GEventLoop::GEventLoop()
 {
 {
-    if (!s_event_loop_stack) {
-        s_event_loop_stack = new Vector<GEventLoop*>;
-        s_timers = new HashMap<int, OwnPtr<GEventLoop::EventLoopTimer>>;
-        s_notifiers = new HashTable<GNotifier*>;
-    }
-
-    if (!s_main_event_loop) {
-        s_main_event_loop = this;
-        s_event_loop_stack->append(this);
+    static bool connected = false;
+    if (!connected) {
         connect_to_server();
         connect_to_server();
+        connected = true;
     }
     }
 
 
     if (!g_actions)
     if (!g_actions)
@@ -94,97 +83,6 @@ GEventLoop::~GEventLoop()
 {
 {
 }
 }
 
 
-GEventLoop& GEventLoop::main()
-{
-    ASSERT(s_main_event_loop);
-    return *s_main_event_loop;
-}
-
-GEventLoop& GEventLoop::current()
-{
-    return *s_event_loop_stack->last();
-}
-
-void GEventLoop::quit(int code)
-{
-    m_exit_requested = true;
-    m_exit_code = code;
-}
-
-struct GEventLoopPusher {
-public:
-    GEventLoopPusher(GEventLoop& event_loop) : m_event_loop(event_loop)
-    {
-        if (&m_event_loop != s_main_event_loop) {
-            m_event_loop.take_pending_events_from(GEventLoop::current());
-            s_event_loop_stack->append(&event_loop);
-        }
-    }
-    ~GEventLoopPusher()
-    {
-        if (&m_event_loop != s_main_event_loop) {
-            s_event_loop_stack->take_last();
-            GEventLoop::current().take_pending_events_from(m_event_loop);
-        }
-    }
-private:
-    GEventLoop& m_event_loop;
-};
-
-int GEventLoop::exec()
-{
-    GEventLoopPusher pusher(*this);
-
-    m_running = true;
-    for (;;) {
-        if (m_exit_requested)
-            return m_exit_code;
-        process_unprocessed_messages();
-        if (m_queued_events.is_empty()) {
-            wait_for_event();
-            process_unprocessed_messages();
-        }
-        Vector<QueuedEvent> events = move(m_queued_events);
-        for (auto& queued_event : events) {
-            auto* receiver = queued_event.receiver.ptr();
-            auto& event = *queued_event.event;
-#ifdef GEVENTLOOP_DEBUG
-            dbgprintf("GEventLoop: %s{%p} event %u\n", receiver->class_name(), receiver, (unsigned)event.type());
-#endif
-            if (!receiver) {
-                switch (event.type()) {
-                case CEvent::Quit:
-                    ASSERT_NOT_REACHED();
-                    return 0;
-                default:
-                    dbgprintf("Event type %u with no receiver :(\n", event.type());
-                }
-            } else if (event.type() == CEvent::Type::DeferredInvoke) {
-                printf("DeferredInvoke: receiver=%s{%p}\n", receiver->class_name(), receiver);
-                static_cast<CDeferredInvocationEvent&>(event).m_invokee(*receiver);
-            } else {
-                receiver->event(event);
-            }
-
-            if (m_exit_requested) {
-                auto rejigged_event_queue = move(events);
-                rejigged_event_queue.append(move(m_queued_events));
-                m_queued_events = move(rejigged_event_queue);
-                return m_exit_code;
-            }
-        }
-    }
-    ASSERT_NOT_REACHED();
-}
-
-void GEventLoop::post_event(CObject& receiver, OwnPtr<CEvent>&& event)
-{
-#ifdef GEVENTLOOP_DEBUG
-    dbgprintf("GEventLoop::post_event: {%u} << receiver=%p, event=%p\n", m_queued_events.size(), &receiver, event.ptr());
-#endif
-    m_queued_events.append({ receiver.make_weak_ptr(), move(event) });
-}
-
 void GEventLoop::handle_paint_event(const WSAPI_ServerMessage& event, GWindow& window)
 void GEventLoop::handle_paint_event(const WSAPI_ServerMessage& event, GWindow& window)
 {
 {
 #ifdef GEVENTLOOP_DEBUG
 #ifdef GEVENTLOOP_DEBUG
@@ -284,73 +182,6 @@ void GEventLoop::handle_wm_event(const WSAPI_ServerMessage& event, GWindow& wind
     ASSERT_NOT_REACHED();
     ASSERT_NOT_REACHED();
 }
 }
 
 
-void GEventLoop::wait_for_event()
-{
-    fd_set rfds;
-    fd_set wfds;
-    FD_ZERO(&rfds);
-    FD_ZERO(&wfds);
-
-    int max_fd = 0;
-    auto add_fd_to_set = [&max_fd] (int fd, fd_set& set){
-        FD_SET(fd, &set);
-        if (fd > max_fd)
-            max_fd = fd;
-    };
-
-    add_fd_to_set(s_event_fd, rfds);
-    for (auto& notifier : *s_notifiers) {
-        if (notifier->event_mask() & GNotifier::Read)
-            add_fd_to_set(notifier->fd(), rfds);
-        if (notifier->event_mask() & GNotifier::Write)
-            add_fd_to_set(notifier->fd(), wfds);
-        if (notifier->event_mask() & GNotifier::Exceptional)
-            ASSERT_NOT_REACHED();
-    }
-
-    struct timeval timeout = { 0, 0 };
-    if (!s_timers->is_empty() && m_queued_events.is_empty())
-        get_next_timer_expiration(timeout);
-    ASSERT(m_unprocessed_messages.is_empty());
-    int rc = select(max_fd + 1, &rfds, &wfds, nullptr, (m_queued_events.is_empty() && s_timers->is_empty()) ? nullptr : &timeout);
-    if (rc < 0) {
-        ASSERT_NOT_REACHED();
-    }
-
-    for (auto& it : *s_timers) {
-        auto& timer = *it.value;
-        if (!timer.has_expired())
-            continue;
-#ifdef GEVENTLOOP_DEBUG
-        dbgprintf("GEventLoop: Timer %d has expired, sending CTimerEvent to %p\n", timer.timer_id, timer.owner);
-#endif
-        post_event(*timer.owner, make<CTimerEvent>(timer.timer_id));
-        if (timer.should_reload) {
-            timer.reload();
-        } else {
-            // FIXME: Support removing expired timers that don't want to reload.
-            ASSERT_NOT_REACHED();
-        }
-    }
-
-    for (auto& notifier : *s_notifiers) {
-        if (FD_ISSET(notifier->fd(), &rfds)) {
-            if (notifier->on_ready_to_read)
-                notifier->on_ready_to_read(*notifier);
-        }
-        if (FD_ISSET(notifier->fd(), &wfds)) {
-            if (notifier->on_ready_to_write)
-                notifier->on_ready_to_write(*notifier);
-        }
-    }
-
-    if (!FD_ISSET(s_event_fd, &rfds))
-        return;
-
-    bool success = drain_messages_from_server();
-    ASSERT(success);
-}
-
 void GEventLoop::process_unprocessed_messages()
 void GEventLoop::process_unprocessed_messages()
 {
 {
     int coalesced_paints = 0;
     int coalesced_paints = 0;
@@ -491,66 +322,6 @@ bool GEventLoop::drain_messages_from_server()
     }
     }
 }
 }
 
 
-bool GEventLoop::EventLoopTimer::has_expired() const
-{
-    timeval now;
-    gettimeofday(&now, nullptr);
-    return now.tv_sec > fire_time.tv_sec || (now.tv_sec == fire_time.tv_sec && now.tv_usec >= fire_time.tv_usec);
-}
-
-void GEventLoop::EventLoopTimer::reload()
-{
-    gettimeofday(&fire_time, nullptr);
-    fire_time.tv_sec += interval / 1000;
-    fire_time.tv_usec += (interval % 1000) * 1000;
-}
-
-void GEventLoop::get_next_timer_expiration(timeval& soonest)
-{
-    ASSERT(!s_timers->is_empty());
-    bool has_checked_any = false;
-    for (auto& it : *s_timers) {
-        auto& fire_time = it.value->fire_time;
-        if (!has_checked_any || fire_time.tv_sec < soonest.tv_sec || (fire_time.tv_sec == soonest.tv_sec && fire_time.tv_usec < soonest.tv_usec))
-            soonest = fire_time;
-        has_checked_any = true;
-    }
-}
-
-int GEventLoop::register_timer(CObject& object, int milliseconds, bool should_reload)
-{
-    ASSERT(milliseconds >= 0);
-    auto timer = make<EventLoopTimer>();
-    timer->owner = object.make_weak_ptr();
-    timer->interval = milliseconds;
-    timer->reload();
-    timer->should_reload = should_reload;
-    int timer_id = ++s_next_timer_id;  // FIXME: This will eventually wrap around.
-    ASSERT(timer_id); // FIXME: Aforementioned wraparound.
-    timer->timer_id = timer_id;
-    s_timers->set(timer->timer_id, move(timer));
-    return timer_id;
-}
-
-bool GEventLoop::unregister_timer(int timer_id)
-{
-    auto it = s_timers->find(timer_id);
-    if (it == s_timers->end())
-        return false;
-    s_timers->remove(it);
-    return true;
-}
-
-void GEventLoop::register_notifier(Badge<GNotifier>, GNotifier& notifier)
-{
-    s_notifiers->set(&notifier);
-}
-
-void GEventLoop::unregister_notifier(Badge<GNotifier>, GNotifier& notifier)
-{
-    s_notifiers->remove(&notifier);
-}
-
 bool GEventLoop::post_message_to_server(const WSAPI_ClientMessage& message)
 bool GEventLoop::post_message_to_server(const WSAPI_ClientMessage& message)
 {
 {
     int nwritten = write(s_event_fd, &message, sizeof(WSAPI_ClientMessage));
     int nwritten = write(s_event_fd, &message, sizeof(WSAPI_ClientMessage));
@@ -559,7 +330,6 @@ bool GEventLoop::post_message_to_server(const WSAPI_ClientMessage& message)
 
 
 bool GEventLoop::wait_for_specific_event(WSAPI_ServerMessage::Type type, WSAPI_ServerMessage& event)
 bool GEventLoop::wait_for_specific_event(WSAPI_ServerMessage::Type type, WSAPI_ServerMessage& event)
 {
 {
-
     for (;;) {
     for (;;) {
         fd_set rfds;
         fd_set rfds;
         FD_ZERO(&rfds);
         FD_ZERO(&rfds);

+ 24 - 55
LibGUI/GEventLoop.h

@@ -1,10 +1,6 @@
 #pragma once
 #pragma once
 
 
-#include <AK/Badge.h>
-#include <AK/HashMap.h>
-#include <AK/OwnPtr.h>
-#include <AK/Vector.h>
-#include <AK/WeakPtr.h>
+#include <LibCore/CEventLoop.h>
 #include <WindowServer/WSAPITypes.h>
 #include <WindowServer/WSAPITypes.h>
 #include <LibGUI/GEvent.h>
 #include <LibGUI/GEvent.h>
 
 
@@ -13,42 +9,43 @@ class CObject;
 class GNotifier;
 class GNotifier;
 class GWindow;
 class GWindow;
 
 
-class GEventLoop {
+class GEventLoop final : public CEventLoop {
 public:
 public:
     GEventLoop();
     GEventLoop();
-    ~GEventLoop();
+    virtual ~GEventLoop() override;
 
 
-    int exec();
-
-    void post_event(CObject& receiver, OwnPtr<CEvent>&&);
-
-    static GEventLoop& main();
-    static GEventLoop& current();
-
-    bool running() const { return m_running; }
-
-    static int register_timer(CObject&, int milliseconds, bool should_reload);
-    static bool unregister_timer(int timer_id);
-
-    static void register_notifier(Badge<GNotifier>, GNotifier&);
-    static void unregister_notifier(Badge<GNotifier>, GNotifier&);
-
-    void quit(int);
+    static GEventLoop& current() { return static_cast<GEventLoop&>(CEventLoop::current()); }
 
 
     static bool post_message_to_server(const WSAPI_ClientMessage&);
     static bool post_message_to_server(const WSAPI_ClientMessage&);
     bool wait_for_specific_event(WSAPI_ServerMessage::Type, WSAPI_ServerMessage&);
     bool wait_for_specific_event(WSAPI_ServerMessage::Type, WSAPI_ServerMessage&);
-
     WSAPI_ServerMessage sync_request(const WSAPI_ClientMessage& request, WSAPI_ServerMessage::Type response_type);
     WSAPI_ServerMessage sync_request(const WSAPI_ClientMessage& request, WSAPI_ServerMessage::Type response_type);
 
 
     static pid_t server_pid() { return s_server_pid; }
     static pid_t server_pid() { return s_server_pid; }
 
 
-    void take_pending_events_from(GEventLoop& other)
+    virtual void take_pending_events_from(CEventLoop& other) override
     {
     {
-        m_queued_events.append(move(other.m_queued_events));
-        m_unprocessed_messages.append(move(other.m_unprocessed_messages));
+        CEventLoop::take_pending_events_from(other);
+        m_unprocessed_messages.append(move(static_cast<GEventLoop&>(other).m_unprocessed_messages));
     }
     }
 
 
 private:
 private:
+    virtual void add_file_descriptors_for_select(fd_set& fds, int& max_fd_added) override
+    {
+        FD_SET(s_event_fd, &fds);
+        max_fd_added = s_event_fd;
+    }
+
+    virtual void process_file_descriptors_after_select(const fd_set& fds) override
+    {
+        if (FD_ISSET(s_event_fd, &fds))
+            drain_messages_from_server();
+    }
+
+    virtual void do_processing() override
+    {
+        process_unprocessed_messages();
+    }
+
     void wait_for_event();
     void wait_for_event();
     bool drain_messages_from_server();
     bool drain_messages_from_server();
     void process_unprocessed_messages();
     void process_unprocessed_messages();
@@ -61,37 +58,9 @@ private:
     void handle_menu_event(const WSAPI_ServerMessage&);
     void handle_menu_event(const WSAPI_ServerMessage&);
     void handle_window_entered_or_left_event(const WSAPI_ServerMessage&, GWindow&);
     void handle_window_entered_or_left_event(const WSAPI_ServerMessage&, GWindow&);
     void handle_wm_event(const WSAPI_ServerMessage&, GWindow&);
     void handle_wm_event(const WSAPI_ServerMessage&, GWindow&);
-    void get_next_timer_expiration(timeval&);
     void connect_to_server();
     void connect_to_server();
 
 
-    struct QueuedEvent {
-        WeakPtr<CObject> receiver;
-        OwnPtr<CEvent> event;
-    };
-    Vector<QueuedEvent> m_queued_events;
-
     Vector<WSAPI_ServerMessage> m_unprocessed_messages;
     Vector<WSAPI_ServerMessage> m_unprocessed_messages;
-
-    bool m_running { false };
-    bool m_exit_requested { false };
-    int m_exit_code { 0 };
-
     static pid_t s_server_pid;
     static pid_t s_server_pid;
     static pid_t s_event_fd;
     static pid_t s_event_fd;
-
-    struct EventLoopTimer {
-        int timer_id { 0 };
-        int interval { 0 };
-        timeval fire_time;
-        bool should_reload { false };
-        WeakPtr<CObject> owner;
-
-        void reload();
-        bool has_expired() const;
-    };
-
-    static HashMap<int, OwnPtr<EventLoopTimer>>* s_timers;
-    static int s_next_timer_id;
-
-    static HashTable<GNotifier*>* s_notifiers;
 };
 };