123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- #include "GWindow.h"
- #include "GEvent.h"
- #include "GEventLoop.h"
- #include "GWidget.h"
- #include <SharedGraphics/GraphicsBitmap.h>
- #include <LibC/stdio.h>
- #include <LibC/stdlib.h>
- #include <LibC/unistd.h>
- #include <AK/HashMap.h>
- //#define UPDATE_COALESCING_DEBUG
- static HashMap<int, GWindow*>* s_windows;
- static HashMap<int, GWindow*>& windows()
- {
- if (!s_windows)
- s_windows = new HashMap<int, GWindow*>;
- return *s_windows;
- }
- GWindow* GWindow::from_window_id(int window_id)
- {
- auto it = windows().find(window_id);
- if (it != windows().end())
- return (*it).value;
- return nullptr;
- }
- GWindow::GWindow(GObject* parent)
- : GObject(parent)
- {
- m_rect_when_windowless = { 100, 400, 140, 140 };
- m_title_when_windowless = "GWindow";
- }
- GWindow::~GWindow()
- {
- if (m_main_widget)
- delete m_main_widget;
- hide();
- }
- void GWindow::close()
- {
- // FIXME: If we exit the event loop, we're never gonna deal with the delete_later request!
- // This will become relevant once we support nested event loops.
- if (should_exit_app_on_close())
- GEventLoop::main().quit(0);
- delete_later();
- }
- void GWindow::show()
- {
- if (m_window_id)
- return;
- WSAPI_ClientMessage request;
- request.type = WSAPI_ClientMessage::Type::CreateWindow;
- request.window_id = m_window_id;
- request.window.rect = m_rect_when_windowless;
- request.window.has_alpha_channel = m_has_alpha_channel;
- request.window.opacity = m_opacity_when_windowless;
- request.window.size_increment = m_size_increment;
- request.window.base_size = m_base_size;
- ASSERT(m_title_when_windowless.length() < sizeof(request.text));
- strcpy(request.text, m_title_when_windowless.characters());
- request.text_length = m_title_when_windowless.length();
- auto response = GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidCreateWindow);
- m_window_id = response.window_id;
- windows().set(m_window_id, this);
- update();
- }
- void GWindow::hide()
- {
- if (!m_window_id)
- return;
- windows().remove(m_window_id);
- WSAPI_ClientMessage request;
- request.type = WSAPI_ClientMessage::Type::DestroyWindow;
- request.window_id = m_window_id;
- ASSERT(m_title_when_windowless.length() < sizeof(request.text));
- strcpy(request.text, m_title_when_windowless.characters());
- request.text_length = m_title_when_windowless.length();
- GEventLoop::main().post_message_to_server(request);
- }
- void GWindow::set_title(String&& title)
- {
- m_title_when_windowless = move(title);
- if (!m_window_id)
- return;
- WSAPI_ClientMessage request;
- request.type = WSAPI_ClientMessage::Type::SetWindowTitle;
- request.window_id = m_window_id;
- ASSERT(m_title_when_windowless.length() < sizeof(request.text));
- strcpy(request.text, m_title_when_windowless.characters());
- request.text_length = m_title_when_windowless.length();
- GEventLoop::main().post_message_to_server(request);
- }
- String GWindow::title() const
- {
- if (!m_window_id)
- return m_title_when_windowless;
- WSAPI_ClientMessage request;
- request.type = WSAPI_ClientMessage::Type::GetWindowTitle;
- request.window_id = m_window_id;
- ASSERT(m_title_when_windowless.length() < sizeof(request.text));
- strcpy(request.text, m_title_when_windowless.characters());
- request.text_length = m_title_when_windowless.length();
- auto response = GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidGetWindowTitle);
- return String(response.text, response.text_length);
- }
- Rect GWindow::rect() const
- {
- if (!m_window_id)
- return m_rect_when_windowless;
- WSAPI_ClientMessage request;
- request.type = WSAPI_ClientMessage::Type::GetWindowRect;
- request.window_id = m_window_id;
- auto response = GEventLoop::main().sync_request(request, WSAPI_ServerMessage::Type::DidGetWindowRect);
- ASSERT(response.window_id == m_window_id);
- return response.window.rect;
- }
- void GWindow::set_rect(const Rect& a_rect)
- {
- m_rect_when_windowless = a_rect;
- if (!m_window_id)
- return;
- WSAPI_ClientMessage request;
- request.type = WSAPI_ClientMessage::Type::SetWindowRect;
- request.window_id = m_window_id;
- request.window.rect = a_rect;
- GEventLoop::main().post_message_to_server(request);
- }
- void GWindow::event(GEvent& event)
- {
- if (event.is_mouse_event()) {
- if (m_global_cursor_tracking_widget) {
- // FIXME: This won't work for widgets-within-widgets.
- auto& mouse_event = static_cast<GMouseEvent&>(event);
- Point local_point { mouse_event.x() - m_global_cursor_tracking_widget->relative_rect().x(), mouse_event.y() - m_global_cursor_tracking_widget->relative_rect().y() };
- auto local_event = make<GMouseEvent>(event.type(), local_point, mouse_event.buttons(), mouse_event.button());
- m_global_cursor_tracking_widget->event(*local_event);
- }
- if (!m_main_widget)
- return;
- auto& mouse_event = static_cast<GMouseEvent&>(event);
- if (m_main_widget) {
- auto result = m_main_widget->hit_test(mouse_event.x(), mouse_event.y());
- auto local_event = make<GMouseEvent>(event.type(), Point { result.localX, result.localY }, mouse_event.buttons(), mouse_event.button());
- ASSERT(result.widget);
- set_hovered_widget(result.widget);
- return result.widget->event(*local_event);
- }
- return;
- }
- if (event.is_paint_event()) {
- m_pending_paint_event_rects.clear();
- if (!m_main_widget)
- return;
- auto& paint_event = static_cast<GPaintEvent&>(event);
- auto rect = paint_event.rect();
- bool created_new_backing_store = !m_backing;
- if (!m_backing) {
- // NOTE: size() may change at any time since it's synchronously retrieved from the WindowServer.
- ASSERT(GEventLoop::main().server_pid());
- Size new_backing_store_size = size();
- size_t size_in_bytes = new_backing_store_size.area() * sizeof(RGBA32);
- void* buffer;
- int shared_buffer_id = create_shared_buffer(GEventLoop::main().server_pid(), size_in_bytes, (void**)&buffer);
- ASSERT(shared_buffer_id >= 0);
- ASSERT(buffer);
- ASSERT(buffer != (void*)-1);
- m_backing = GraphicsBitmap::create_with_shared_buffer(
- m_has_alpha_channel ? GraphicsBitmap::Format::RGBA32 : GraphicsBitmap::Format::RGB32,
- shared_buffer_id,
- new_backing_store_size, (RGBA32*)buffer);
- }
- if (rect.is_empty() || created_new_backing_store)
- rect = m_main_widget->rect();
- m_main_widget->event(*make<GPaintEvent>(rect));
- if (created_new_backing_store) {
- WSAPI_ClientMessage message;
- message.type = WSAPI_ClientMessage::Type::SetWindowBackingStore;
- message.window_id = m_window_id;
- message.backing.bpp = 32;
- message.backing.pitch = m_backing->pitch();
- message.backing.shared_buffer_id = m_backing->shared_buffer_id();
- message.backing.has_alpha_channel = m_backing->has_alpha_channel();
- message.backing.size = m_backing->size();
- GEventLoop::main().post_message_to_server(message);
- }
- if (m_window_id) {
- WSAPI_ClientMessage message;
- message.type = WSAPI_ClientMessage::Type::DidFinishPainting;
- message.window_id = m_window_id;
- message.window.rect = rect;
- GEventLoop::main().post_message_to_server(message);
- }
- return;
- }
- if (event.is_key_event()) {
- if (m_focused_widget)
- return m_focused_widget->event(event);
- if (m_main_widget)
- return m_main_widget->event(event);
- return;
- }
- if (event.type() == GEvent::WindowBecameActive || event.type() == GEvent::WindowBecameInactive) {
- m_is_active = event.type() == GEvent::WindowBecameActive;
- if (m_main_widget)
- m_main_widget->event(event);
- if (m_focused_widget)
- m_focused_widget->update();
- return;
- }
- if (event.type() == GEvent::WindowCloseRequest) {
- close();
- return;
- }
- if (event.type() == GEvent::WindowLeft) {
- set_hovered_widget(nullptr);
- return;
- }
- if (event.type() == GEvent::Resize) {
- m_backing = nullptr;
- m_pending_paint_event_rects.clear();
- m_rect_when_windowless = { { }, static_cast<GResizeEvent&>(event).size() };
- m_main_widget->set_relative_rect({ { }, static_cast<GResizeEvent&>(event).size() });
- return;
- }
- GObject::event(event);
- }
- bool GWindow::is_visible() const
- {
- return false;
- }
- void GWindow::update(const Rect& a_rect)
- {
- if (!m_window_id)
- return;
- for (auto& pending_rect : m_pending_paint_event_rects) {
- if (pending_rect.contains(a_rect)) {
- #ifdef UPDATE_COALESCING_DEBUG
- dbgprintf("Ignoring %s since it's contained by pending rect %s\n", a_rect.to_string().characters(), pending_rect.to_string().characters());
- #endif
- //return;
- }
- }
- m_pending_paint_event_rects.append(a_rect);
- WSAPI_ClientMessage request;
- request.type = WSAPI_ClientMessage::Type::InvalidateRect;
- request.window_id = m_window_id;
- request.window.rect = a_rect;
- GEventLoop::main().post_message_to_server(request);
- }
- void GWindow::set_main_widget(GWidget* widget)
- {
- if (m_main_widget == widget)
- return;
- m_main_widget = widget;
- if (m_main_widget) {
- auto new_window_rect = rect();
- if (m_main_widget->horizontal_size_policy() == SizePolicy::Fixed)
- new_window_rect.set_width(m_main_widget->preferred_size().width());
- if (m_main_widget->vertical_size_policy() == SizePolicy::Fixed)
- new_window_rect.set_height(m_main_widget->preferred_size().height());
- set_rect(new_window_rect);
- m_main_widget->set_relative_rect({ { }, new_window_rect.size() });
- m_main_widget->set_window(this);
- if (m_main_widget->accepts_focus())
- m_main_widget->set_focus(true);
- }
- update();
- }
- void GWindow::set_focused_widget(GWidget* widget)
- {
- if (m_focused_widget == widget)
- return;
- if (m_focused_widget) {
- GEventLoop::main().post_event(m_focused_widget, make<GEvent>(GEvent::FocusOut));
- m_focused_widget->update();
- }
- m_focused_widget = widget;
- if (m_focused_widget) {
- GEventLoop::main().post_event(m_focused_widget, make<GEvent>(GEvent::FocusIn));
- m_focused_widget->update();
- }
- }
- void GWindow::set_global_cursor_tracking_widget(GWidget* widget)
- {
- ASSERT(m_window_id);
- if (widget == m_global_cursor_tracking_widget.ptr())
- return;
- m_global_cursor_tracking_widget = widget ? widget->make_weak_ptr() : nullptr;
- WSAPI_ClientMessage request;
- request.type = WSAPI_ClientMessage::Type::SetGlobalCursorTracking;
- request.window_id = m_window_id;
- request.value = widget != nullptr;
- // FIXME: What if the cursor moves out of our interest range before the server can handle this?
- // Maybe there could be a response that includes the current cursor location as of enabling.
- GEventLoop::main().post_message_to_server(request);
- }
- void GWindow::set_has_alpha_channel(bool value)
- {
- ASSERT(!m_window_id);
- m_has_alpha_channel = value;
- }
- void GWindow::set_opacity(float opacity)
- {
- m_opacity_when_windowless = opacity;
- if (!m_window_id)
- return;
- WSAPI_ClientMessage request;
- request.type = WSAPI_ClientMessage::Type::SetWindowOpacity;
- request.window_id = m_window_id;
- request.window.opacity = opacity;
- m_opacity_when_windowless = opacity;
- GEventLoop::main().post_message_to_server(request);
- }
- void GWindow::set_hovered_widget(GWidget* widget)
- {
- if (widget == m_hovered_widget.ptr())
- return;
- if (m_hovered_widget)
- GEventLoop::main().post_event(m_hovered_widget.ptr(), make<GEvent>(GEvent::Leave));
- m_hovered_widget = widget ? widget->make_weak_ptr() : nullptr;
- if (m_hovered_widget)
- GEventLoop::main().post_event(m_hovered_widget.ptr(), make<GEvent>(GEvent::Enter));
- }
|