diff options
author | Robin Burchell <robin+git@viroteck.net> | 2019-07-17 18:28:30 +0200 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-07-17 20:16:44 +0200 |
commit | 2177594c960976d82c5eb6fac3f90f3345d37854 (patch) | |
tree | 24db096f16c2c414cb9b76ebabc1464a1d8a5a2a | |
parent | 41bece0682c2211eddbf9d7153e72e39ab1979d0 (diff) | |
download | serenity-2177594c960976d82c5eb6fac3f90f3345d37854.zip |
Port LibGUI to use CIPCClientSideConnection
As a consequence, move to use an explicit handshake() method rather than
calling virtuals from the constructor. This seemed to not bother
AClientConnection, but LibGUI crashes (rightfully) because of it.
-rw-r--r-- | Applications/Piano/main.cpp | 1 | ||||
-rw-r--r-- | Applications/Taskbar/TaskbarButton.cpp | 2 | ||||
-rw-r--r-- | Applications/Taskbar/WindowList.cpp | 2 | ||||
-rw-r--r-- | Libraries/LibAudio/AClientConnection.cpp | 4 | ||||
-rw-r--r-- | Libraries/LibAudio/AClientConnection.h | 2 | ||||
-rw-r--r-- | Libraries/LibCore/CIPCClientSideConnection.h | 91 | ||||
-rw-r--r-- | Libraries/LibCore/CIPCServerSideClient.h | 16 | ||||
-rw-r--r-- | Libraries/LibGUI/GClipboard.cpp | 6 | ||||
-rw-r--r-- | Libraries/LibGUI/GDesktop.cpp | 6 | ||||
-rw-r--r-- | Libraries/LibGUI/GDesktop.h | 4 | ||||
-rw-r--r-- | Libraries/LibGUI/GEvent.h | 2 | ||||
-rw-r--r-- | Libraries/LibGUI/GEventLoop.cpp | 208 | ||||
-rw-r--r-- | Libraries/LibGUI/GEventLoop.h | 66 | ||||
-rw-r--r-- | Libraries/LibGUI/GMenu.cpp | 12 | ||||
-rw-r--r-- | Libraries/LibGUI/GMenuBar.cpp | 8 | ||||
-rw-r--r-- | Libraries/LibGUI/GMenuItem.cpp | 2 | ||||
-rw-r--r-- | Libraries/LibGUI/GWindow.cpp | 36 | ||||
-rw-r--r-- | Userland/aplay.cpp | 1 |
18 files changed, 192 insertions, 277 deletions
diff --git a/Applications/Piano/main.cpp b/Applications/Piano/main.cpp index 73a5f78a58..ddc5ccb8a6 100644 --- a/Applications/Piano/main.cpp +++ b/Applications/Piano/main.cpp @@ -11,6 +11,7 @@ int main(int argc, char** argv) { GApplication app(argc, argv); AClientConnection audio_connection; + audio_connection.handshake(); auto* window = new GWindow; window->set_title("Piano"); diff --git a/Applications/Taskbar/TaskbarButton.cpp b/Applications/Taskbar/TaskbarButton.cpp index e821dc4e14..3fd1f336e9 100644 --- a/Applications/Taskbar/TaskbarButton.cpp +++ b/Applications/Taskbar/TaskbarButton.cpp @@ -20,5 +20,5 @@ void TaskbarButton::context_menu_event(GContextMenuEvent&) request.wm.client_id = m_identifier.client_id(); request.wm.window_id = m_identifier.window_id(); request.wm.position = screen_relative_rect().location(); - GEventLoop::post_message_to_server(request); + GEventLoop::current().connection().post_message_to_server(request); } diff --git a/Applications/Taskbar/WindowList.cpp b/Applications/Taskbar/WindowList.cpp index 6147ed1e92..4ca85919eb 100644 --- a/Applications/Taskbar/WindowList.cpp +++ b/Applications/Taskbar/WindowList.cpp @@ -35,7 +35,7 @@ Window& WindowList::ensure_window(const WindowIdentifier& identifier) } message.wm.client_id = identifier.client_id(); message.wm.window_id = identifier.window_id(); - bool success = GEventLoop::post_message_to_server(message); + bool success = GEventLoop::current().connection().post_message_to_server(message); ASSERT(success); }; auto& window_ref = *window; diff --git a/Libraries/LibAudio/AClientConnection.cpp b/Libraries/LibAudio/AClientConnection.cpp index 75e3af1126..7f5da5f937 100644 --- a/Libraries/LibAudio/AClientConnection.cpp +++ b/Libraries/LibAudio/AClientConnection.cpp @@ -3,11 +3,11 @@ #include "AClientConnection.h" AClientConnection::AClientConnection() - : CIPCClientSideConnection() + : CIPCClientSideConnection("/tmp/asportal") { } -void AClientConnection::send_greeting() +void AClientConnection::handshake() { ASAPI_ClientMessage request; request.type = ASAPI_ClientMessage::Type::Greeting; diff --git a/Libraries/LibAudio/AClientConnection.h b/Libraries/LibAudio/AClientConnection.h index d98f9c5789..6a8701635c 100644 --- a/Libraries/LibAudio/AClientConnection.h +++ b/Libraries/LibAudio/AClientConnection.h @@ -11,6 +11,6 @@ class AClientConnection : public CIPCClientSideConnection<ASAPI_ServerMessage, A public: AClientConnection(); - void send_greeting() override; + void handshake() override; void play(const ABuffer& buffer); }; diff --git a/Libraries/LibCore/CIPCClientSideConnection.h b/Libraries/LibCore/CIPCClientSideConnection.h index 2255754d0a..9877ba6f6b 100644 --- a/Libraries/LibCore/CIPCClientSideConnection.h +++ b/Libraries/LibCore/CIPCClientSideConnection.h @@ -1,6 +1,7 @@ #pragma once #include <LibCore/CEventLoop.h> +#include <LibCore/CEvent.h> #include <LibCore/CLocalSocket.h> #include <LibCore/CNotifier.h> #include <LibAudio/ASAPI.h> @@ -12,26 +13,51 @@ #include <sys/socket.h> #include <sys/uio.h> +//#define CIPC_DEBUG + +class CIPCClientEvent : public CEvent { +public: + enum Type { + Invalid = 2000, + DoPostprocess, + }; + CIPCClientEvent() {} + explicit CIPCClientEvent(Type type) + : CEvent(type) + { + } +}; + +class CIPCClientPostprocessEvent : public CIPCClientEvent { +public: + explicit CIPCClientPostprocessEvent(int client_id) + : CIPCClientEvent(DoPostprocess) + , m_client_id(client_id) + { + } + + int client_id() const { return m_client_id; } + +private: + int m_client_id { 0 }; +}; + template <typename ServerMessage, typename ClientMessage> -class CIPCClientSideConnection { +class CIPCClientSideConnection : public CObject { public: - CIPCClientSideConnection() + CIPCClientSideConnection(const StringView& address) : m_notifier(CNotifier(m_connection.fd(), CNotifier::Read)) { // We want to rate-limit our clients m_connection.set_blocking(true); m_notifier.on_ready_to_read = [this] { drain_messages_from_server(); - }; - m_connection.on_connected = [this] { - dbg() << "IPC: Connected, sending greeting"; - send_greeting(); - dbg() << "IPC: Greeting sent!"; + CEventLoop::current().post_event(*this, make<CIPCClientPostprocessEvent>(m_connection.fd())); }; int retries = 1000; while (retries) { - if (m_connection.connect(CSocketAddress::local("/tmp/asportal"))) { + if (m_connection.connect(CSocketAddress::local(address))) { break; } @@ -39,18 +65,39 @@ public: sleep(1); --retries; } + ASSERT(m_connection.is_connected()); + } + + virtual void handshake() = 0; + + + virtual void event(CEvent& event) override + { + if (event.type() == CIPCClientEvent::DoPostprocess) { + postprocess_bundles(m_unprocessed_bundles); + } else { + CObject::event(event); + } } - virtual void send_greeting() = 0; void set_server_pid(pid_t pid) { m_server_pid = pid; } pid_t server_pid() const { return m_server_pid; } void set_my_client_id(int id) { m_my_client_id = id; } int my_client_id() const { return m_my_client_id; } -protected: template <typename MessageType> bool wait_for_specific_event(MessageType type, ServerMessage& event) { + // Double check we don't already have the event waiting for us. + // Otherwise we might end up blocked for a while for no reason. + for (ssize_t i = 0; i < m_unprocessed_bundles.size(); ++i) { + if (m_unprocessed_bundles[i].message.type == type) { + event = move(m_unprocessed_bundles[i].message); + m_unprocessed_bundles.remove(i); + CEventLoop::current().post_event(*this, make<CIPCClientPostprocessEvent>(m_connection.fd())); + return true; + } + } for (;;) { fd_set rfds; FD_ZERO(&rfds); @@ -68,6 +115,7 @@ protected: if (m_unprocessed_bundles[i].message.type == type) { event = move(m_unprocessed_bundles[i].message); m_unprocessed_bundles.remove(i); + CEventLoop::current().post_event(*this, make<CIPCClientPostprocessEvent>(m_connection.fd())); return true; } } @@ -76,6 +124,9 @@ protected: bool post_message_to_server(const ClientMessage& message, const ByteBuffer&& extra_data = {}) { +#if defined(CIPC_DEBUG) + dbg() << "C: -> S " << int(message.type) << " extra " << extra_data.size(); +#endif if (!extra_data.is_empty()) const_cast<ClientMessage&>(message).extra_size = extra_data.size(); @@ -112,6 +163,18 @@ protected: return response; } +protected: + struct IncomingASMessageBundle { + ServerMessage message; + ByteBuffer extra_data; + }; + + virtual void postprocess_bundles(Vector<IncomingASMessageBundle>& new_bundles) + { + dbg() << "CIPCClientSideConnection " << " warning: discarding " << new_bundles.size() << " unprocessed bundles; this may not be what you want"; + new_bundles.clear(); + } + private: bool drain_messages_from_server() { @@ -142,17 +205,15 @@ private: } ASSERT((size_t)extra_nread == message.extra_size); } +#if defined(CIPC_DEBUG) + dbg() << "C: <- S " << int(message.type) << " extra " << extra_data.size(); +#endif m_unprocessed_bundles.append({ move(message), move(extra_data) }); } } CLocalSocket m_connection; CNotifier m_notifier; - - struct IncomingASMessageBundle { - ServerMessage message; - ByteBuffer extra_data; - }; Vector<IncomingASMessageBundle> m_unprocessed_bundles; int m_server_pid; int m_my_client_id; diff --git a/Libraries/LibCore/CIPCServerSideClient.h b/Libraries/LibCore/CIPCServerSideClient.h index 4d28b447ef..7bb17a9e98 100644 --- a/Libraries/LibCore/CIPCServerSideClient.h +++ b/Libraries/LibCore/CIPCServerSideClient.h @@ -13,6 +13,8 @@ #include <sys/socket.h> #include <stdio.h> +//#define CIPC_DEBUG + class CIPCServerEvent : public CEvent { public: enum Type { @@ -58,16 +60,23 @@ public: , m_client_id(client_id) { m_notifier.on_ready_to_read = [this] { drain_client(); }; - dbg() << "********** S: Created new CIPCServerSideClient " << fd << client_id << " and said hello"; +#if defined(CIPC_DEBUG) + dbg() << "S: Created new CIPCServerSideClient " << fd << client_id << " and said hello"; +#endif } ~CIPCServerSideClient() { - dbg() << "********** S: Destroyed CIPCServerSideClient " << m_socket.fd() << client_id(); +#if defined(CIPC_DEBUG) + dbg() << "S: Destroyed CIPCServerSideClient " << m_socket.fd() << client_id(); +#endif } void post_message(const ServerMessage& message, const ByteBuffer& extra_data = {}) { +#if defined(CIPC_DEBUG) + dbg() << "S: -> C " << int(message.type) << " extra " << extra_data.size(); +#endif if (!extra_data.is_empty()) const_cast<ServerMessage&>(message).extra_size = extra_data.size(); @@ -139,6 +148,9 @@ public: return did_misbehave(); } } +#if defined(CIPC_DEBUG) + dbg() << "S: <- C " << int(message.type) << " extra " << extra_data.size(); +#endif if (!handle_message(message, move(extra_data))) return; ++messages_received; diff --git a/Libraries/LibGUI/GClipboard.cpp b/Libraries/LibGUI/GClipboard.cpp index 51818348a6..f069be5cac 100644 --- a/Libraries/LibGUI/GClipboard.cpp +++ b/Libraries/LibGUI/GClipboard.cpp @@ -19,7 +19,7 @@ String GClipboard::data() const { WSAPI_ClientMessage request; request.type = WSAPI_ClientMessage::Type::GetClipboardContents; - auto response = GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidGetClipboardContents); + auto response = GEventLoop::current().connection().sync_request(request, WSAPI_ServerMessage::Type::DidGetClipboardContents); if (response.clipboard.shared_buffer_id < 0) return {}; auto shared_buffer = SharedBuffer::create_from_shared_buffer_id(response.clipboard.shared_buffer_id); @@ -38,7 +38,7 @@ void GClipboard::set_data(const StringView& data) { WSAPI_ClientMessage request; request.type = WSAPI_ClientMessage::Type::SetClipboardContents; - auto shared_buffer = SharedBuffer::create(GEventLoop::current().server_pid(), data.length() + 1); + auto shared_buffer = SharedBuffer::create(GEventLoop::current().connection().server_pid(), data.length() + 1); if (!shared_buffer) { dbgprintf("GClipboard::set_data() failed to create a shared buffer\n"); return; @@ -50,6 +50,6 @@ void GClipboard::set_data(const StringView& data) shared_buffer->seal(); request.clipboard.shared_buffer_id = shared_buffer->shared_buffer_id(); request.clipboard.contents_size = data.length(); - auto response = GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidSetClipboardContents); + auto response = GEventLoop::current().connection().sync_request(request, WSAPI_ServerMessage::Type::DidSetClipboardContents); ASSERT(response.clipboard.shared_buffer_id == shared_buffer->shared_buffer_id()); } diff --git a/Libraries/LibGUI/GDesktop.cpp b/Libraries/LibGUI/GDesktop.cpp index 0c2786b1da..0e7c30fd0b 100644 --- a/Libraries/LibGUI/GDesktop.cpp +++ b/Libraries/LibGUI/GDesktop.cpp @@ -15,7 +15,7 @@ GDesktop::GDesktop() { } -void GDesktop::did_receive_screen_rect(Badge<GEventLoop>, const Rect& rect) +void GDesktop::did_receive_screen_rect(Badge<GWindowServerConnection>, const Rect& rect) { if (m_rect == rect) return; @@ -31,7 +31,7 @@ bool GDesktop::set_wallpaper(const StringView& path) ASSERT(path.length() < (int)sizeof(message.text)); strncpy(message.text, path.characters_without_null_termination(), path.length()); message.text_length = path.length(); - auto response = GEventLoop::current().sync_request(message, WSAPI_ServerMessage::Type::DidSetWallpaper); + auto response = GEventLoop::current().connection().sync_request(message, WSAPI_ServerMessage::Type::DidSetWallpaper); return response.value; } @@ -39,6 +39,6 @@ String GDesktop::wallpaper() const { WSAPI_ClientMessage message; message.type = WSAPI_ClientMessage::Type::GetWallpaper; - auto response = GEventLoop::current().sync_request(message, WSAPI_ServerMessage::Type::DidGetWallpaper); + auto response = GEventLoop::current().connection().sync_request(message, WSAPI_ServerMessage::Type::DidGetWallpaper); return String(response.text, response.text_length); } diff --git a/Libraries/LibGUI/GDesktop.h b/Libraries/LibGUI/GDesktop.h index 250e7138ce..a76142fe0f 100644 --- a/Libraries/LibGUI/GDesktop.h +++ b/Libraries/LibGUI/GDesktop.h @@ -5,7 +5,7 @@ #include <AK/Function.h> #include <SharedGraphics/Rect.h> -class GEventLoop; +class GWindowServerConnection; class GDesktop { public: @@ -16,7 +16,7 @@ public: bool set_wallpaper(const StringView& path); Rect rect() const { return m_rect; } - void did_receive_screen_rect(Badge<GEventLoop>, const Rect&); + void did_receive_screen_rect(Badge<GWindowServerConnection>, const Rect&); Function<void(const Rect&)> on_rect_change; diff --git a/Libraries/LibGUI/GEvent.h b/Libraries/LibGUI/GEvent.h index ac5fc719b7..9d98188836 100644 --- a/Libraries/LibGUI/GEvent.h +++ b/Libraries/LibGUI/GEvent.h @@ -242,7 +242,7 @@ public: String text() const { return m_text; } private: - friend class GEventLoop; + friend class GWindowServerConnection; int m_key { 0 }; u8 m_modifiers { 0 }; String m_text; diff --git a/Libraries/LibGUI/GEventLoop.cpp b/Libraries/LibGUI/GEventLoop.cpp index 589bc4ca34..9815464123 100644 --- a/Libraries/LibGUI/GEventLoop.cpp +++ b/Libraries/LibGUI/GEventLoop.cpp @@ -23,39 +23,8 @@ //#define GEVENTLOOP_DEBUG //#define COALESCING_DEBUG -int GEventLoop::s_windowserver_fd = -1; -int GEventLoop::s_my_client_id = -1; -pid_t GEventLoop::s_server_pid = -1; - -void GEventLoop::connect_to_server() +void GWindowServerConnection::handshake() { - ASSERT(s_windowserver_fd == -1); - s_windowserver_fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); - if (s_windowserver_fd < 0) { - perror("socket"); - ASSERT_NOT_REACHED(); - } - - sockaddr_un address; - address.sun_family = AF_LOCAL; - strcpy(address.sun_path, "/tmp/wsportal"); - - int retries = 1000; - int rc = 0; - while (retries) { - rc = connect(s_windowserver_fd, (const sockaddr*)&address, sizeof(address)); - if (rc == 0) - break; -#ifdef GEVENTLOOP_DEBUG - dbgprintf("connect failed: %d, %s\n", errno, strerror(errno)); -#endif - sleep(1); - --retries; - } - if (rc < 0) { - ASSERT_NOT_REACHED(); - } - WSAPI_ClientMessage request; request.type = WSAPI_ClientMessage::Type::Greeting; request.greeting.client_pid = getpid(); @@ -65,25 +34,17 @@ void GEventLoop::connect_to_server() GEventLoop::GEventLoop() { - static bool connected = false; - if (!connected) { - connect_to_server(); - connected = true; - } - -#ifdef GEVENTLOOP_DEBUG - dbgprintf("(%u) GEventLoop constructed :)\n", getpid()); -#endif + m_connection.handshake(); } GEventLoop::~GEventLoop() { } -void GEventLoop::handle_paint_event(const WSAPI_ServerMessage& event, GWindow& window, const ByteBuffer& extra_data) +void GWindowServerConnection::handle_paint_event(const WSAPI_ServerMessage& event, GWindow& window, const ByteBuffer& extra_data) { #ifdef GEVENTLOOP_DEBUG - dbgprintf("WID=%x Paint [%d,%d %dx%d]\n", event.window_id, event.paint.rect.location.x, event.paint.rect.location.y, event.paint.rect.size.width, event.paint.rect.size.height); + dbgprintf("WID=%x Paint\n", event.window_id); #endif Vector<Rect, 32> rects; for (int i = 0; i < min(WSAPI_ServerMessage::max_inline_rect_count, event.rect_count); ++i) @@ -93,33 +54,33 @@ void GEventLoop::handle_paint_event(const WSAPI_ServerMessage& event, GWindow& w for (int i = 0; i < event.rect_count - WSAPI_ServerMessage::max_inline_rect_count; ++i) rects.append(extra_rects[i]); } - post_event(window, make<GMultiPaintEvent>(rects, event.paint.window_size)); + CEventLoop::current().post_event(window, make<GMultiPaintEvent>(rects, event.paint.window_size)); } -void GEventLoop::handle_resize_event(const WSAPI_ServerMessage& event, GWindow& window) +void GWindowServerConnection::handle_resize_event(const WSAPI_ServerMessage& event, GWindow& window) { - post_event(window, make<GResizeEvent>(event.window.old_rect.size, event.window.rect.size)); + CEventLoop::current().post_event(window, make<GResizeEvent>(event.window.old_rect.size, event.window.rect.size)); } -void GEventLoop::handle_window_activation_event(const WSAPI_ServerMessage& event, GWindow& window) +void GWindowServerConnection::handle_window_activation_event(const WSAPI_ServerMessage& event, GWindow& window) { #ifdef GEVENTLOOP_DEBUG dbgprintf("WID=%x WindowActivation\n", event.window_id); #endif - post_event(window, make<GEvent>(event.type == WSAPI_ServerMessage::Type::WindowActivated ? GEvent::WindowBecameActive : GEvent::WindowBecameInactive)); + CEventLoop::current().post_event(window, make<GEvent>(event.type == WSAPI_ServerMessage::Type::WindowActivated ? GEvent::WindowBecameActive : GEvent::WindowBecameInactive)); } -void GEventLoop::handle_window_close_request_event(const WSAPI_ServerMessage&, GWindow& window) +void GWindowServerConnection::handle_window_close_request_event(const WSAPI_ServerMessage&, GWindow& window) { - post_event(window, make<GEvent>(GEvent::WindowCloseRequest)); + CEventLoop::current().post_event(window, make<GEvent>(GEvent::WindowCloseRequest)); } -void GEventLoop::handle_window_entered_or_left_event(const WSAPI_ServerMessage& message, GWindow& window) +void GWindowServerConnection::handle_window_entered_or_left_event(const WSAPI_ServerMessage& message, GWindow& window) { - post_event(window, make<GEvent>(message.type == WSAPI_ServerMessage::Type::WindowEntered ? GEvent::WindowEntered : GEvent::WindowLeft)); + CEventLoop::current().post_event(window, make<GEvent>(message.type == WSAPI_ServerMessage::Type::WindowEntered ? GEvent::WindowEntered : GEvent::WindowLeft)); } -void GEventLoop::handle_key_event(const WSAPI_ServerMessage& event, GWindow& window) +void GWindowServerConnection::handle_key_event(const WSAPI_ServerMessage& event, GWindow& window) { #ifdef GEVENTLOOP_DEBUG dbgprintf("WID=%x KeyEvent character=0x%b\n", event.window_id, event.key.character); @@ -145,10 +106,10 @@ void GEventLoop::handle_key_event(const WSAPI_ServerMessage& event, GWindow& win } } } - post_event(window, move(key_event)); + CEventLoop::current().post_event(window, move(key_event)); } -void GEventLoop::handle_mouse_event(const WSAPI_ServerMessage& event, GWindow& window) +void GWindowServerConnection::handle_mouse_event(const WSAPI_ServerMessage& event, GWindow& window) { #ifdef GEVENTLOOP_DEBUG dbgprintf("WID=%x MouseEvent %d,%d,%d\n", event.window_id, event.mouse.position.x, event.mouse.position.y, event.mouse.wheel_delta); @@ -192,10 +153,10 @@ void GEventLoop::handle_mouse_event(const WSAPI_ServerMessage& event, GWindow& w ASSERT_NOT_REACHED(); break; } - post_event(window, make<GMouseEvent>(type, event.mouse.position, event.mouse.buttons, button, event.mouse.modifiers, event.mouse.wheel_delta)); + CEventLoop::current().post_event(window, make<GMouseEvent>(type, event.mouse.position, event.mouse.buttons, button, event.mouse.modifiers, event.mouse.wheel_delta)); } -void GEventLoop::handle_menu_event(const WSAPI_ServerMessage& event) +void GWindowServerConnection::handle_menu_event(const WSAPI_ServerMessage& event) { if (event.type == WSAPI_ServerMessage::Type::MenuItemActivated) { auto* menu = GMenu::from_menu_id(event.menu.menu_id); @@ -210,27 +171,28 @@ void GEventLoop::handle_menu_event(const WSAPI_ServerMessage& event) ASSERT_NOT_REACHED(); } -void GEventLoop::handle_wm_event(const WSAPI_ServerMessage& event, GWindow& window) +void GWindowServerConnection::handle_wm_event(const WSAPI_ServerMessage& event, GWindow& window) { #ifdef GEVENTLOOP_DEBUG dbgprintf("GEventLoop: handle_wm_event: %d\n", (int)event.type); #endif 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, event.wm.is_active, (GWindowType)event.wm.window_type, event.wm.is_minimized)); - if (event.type == WSAPI_ServerMessage::WM_WindowRectChanged) - return post_event(window, make<GWMWindowRectChangedEvent>(event.wm.client_id, event.wm.window_id, event.wm.rect)); - if (event.type == WSAPI_ServerMessage::WM_WindowIconChanged) - return post_event(window, make<GWMWindowIconChangedEvent>(event.wm.client_id, event.wm.window_id, String(event.text, event.text_length))); - if (event.type == WSAPI_ServerMessage::WM_WindowRemoved) - return post_event(window, make<GWMWindowRemovedEvent>(event.wm.client_id, event.wm.window_id)); - ASSERT_NOT_REACHED(); + CEventLoop::current().post_event(window, make<GWMWindowStateChangedEvent>(event.wm.client_id, event.wm.window_id, String(event.text, event.text_length), event.wm.rect, event.wm.is_active, (GWindowType)event.wm.window_type, event.wm.is_minimized)); + else if (event.type == WSAPI_ServerMessage::WM_WindowRectChanged) + CEventLoop::current().post_event(window, make<GWMWindowRectChangedEvent>(event.wm.client_id, event.wm.window_id, event.wm.rect)); + else if (event.type == WSAPI_ServerMessage::WM_WindowIconChanged) + CEventLoop::current().post_event(window, make<GWMWindowIconChangedEvent>(event.wm.client_id, event.wm.window_id, String(event.text, event.text_length))); + else if (event.type == WSAPI_ServerMessage::WM_WindowRemoved) + CEventLoop::current().post_event(window, make<GWMWindowRemovedEvent>(event.wm.client_id, event.wm.window_id)); + else + ASSERT_NOT_REACHED(); } -void GEventLoop::process_unprocessed_bundles() +void GWindowServerConnection::postprocess_bundles(Vector<CIPCClientSideConnection::IncomingASMessageBundle>& bundles) { int coalesced_paints = 0; int coalesced_resizes = 0; - auto unprocessed_bundles = move(m_unprocessed_bundles); + auto unprocessed_bundles = move(bundles); HashMap<int, Size> latest_size_for_window_id; for (auto& bundle : unprocessed_bundles) { @@ -247,7 +209,7 @@ void GEventLoop::process_unprocessed_bundles() if (event.type == WSAPI_ServerMessage::Type::Paint) { ++paint_count; #ifdef COALESCING_DEBUG - dbgprintf(" %s (window: %s)\n", Rect(event.paint.rect).to_string().characters(), Size(event.paint.window_size).to_string().characters()); + dbgprintf(" (window: %s)\n", Size(event.paint.window_size).to_string().characters()); #endif latest_paint_size_for_window_id.set(event.window_id, event.paint.window_size); } @@ -259,7 +221,9 @@ void GEventLoop::process_unprocessed_bundles() for (auto& bundle : unprocessed_bundles) { auto& event = bundle.message; if (event.type == WSAPI_ServerMessage::Type::Greeting) { - handle_greeting(event); + // Shouldn't get a second greeting + dbg() << "Got second Greeting!?"; + ASSERT_NOT_REACHED(); continue; } @@ -271,7 +235,7 @@ void GEventLoop::process_unprocessed_bundles() if (event.type == WSAPI_ServerMessage::Error) { dbgprintf("GEventLoop got error message from server\n"); dbgprintf(" - error message: %s\n", String(event.text, event.text_length).characters()); - quit(1); + CEventLoop::current().quit(1); return; } @@ -343,105 +307,9 @@ void GEventLoop::process_unprocessed_bundles() #endif } -bool GEventLoop::drain_messages_from_server() -{ - for (;;) { - WSAPI_ServerMessage message; - ssize_t nread = recv(s_windowserver_fd, &message, sizeof(WSAPI_ServerMessage), MSG_DONTWAIT); - if (nread < 0) { - if (errno == EAGAIN) { - return true; - } - perror("read"); - quit(1); - return false; - } - if (nread == 0) { - fprintf(stderr, "EOF on WindowServer fd\n"); - quit(1); - exit(-1); - return false; - } - ASSERT(nread == sizeof(message)); - ByteBuffer extra_data; - if (message.extra_size) { - extra_data = ByteBuffer::create_uninitialized(message.extra_size); - int extra_nread = read(s_windowserver_fd, extra_data.data(), extra_data.size()); - if (extra_nread < 0) { - perror("read"); - ASSERT_NOT_REACHED(); - } - ASSERT((size_t)extra_nread == message.extra_size); - } - m_unprocessed_bundles.append({ move(message), move(extra_data) }); - } -} - -bool GEventLoop::post_message_to_server(const WSAPI_ClientMessage& message, const ByteBuffer& extra_data) -{ - if (!extra_data.is_empty()) - const_cast<WSAPI_ClientMessage&>(message).extra_size = extra_data.size(); - - struct iovec iov[2]; - int iov_count = 1; - iov[0].iov_base = const_cast<WSAPI_ClientMessage*>(&message); - iov[0].iov_len = sizeof(message); - - if (!extra_data.is_empty()) { - iov[1].iov_base = const_cast<u8*>(extra_data.data()); - iov[1].iov_len = extra_data.size(); - ++iov_count; - } - - int nwritten = writev(s_windowserver_fd, iov, iov_count); - if (nwritten < 0) { - perror("writev"); - ASSERT_NOT_REACHED(); - } - ASSERT((size_t)nwritten == sizeof(message) + extra_data.size()); - - return true; -} - -bool GEventLoop::wait_for_specific_event(WSAPI_ServerMessage::Type type, WSAPI_ServerMessage& event) -{ - for (;;) { - fd_set rfds; - FD_ZERO(&rfds); - FD_SET(s_windowserver_fd, &rfds); - int rc = select(s_windowserver_fd + 1, &rfds, nullptr, nullptr, nullptr); - if (rc < 0) { - perror("select"); - } - ASSERT(rc > 0); - ASSERT(FD_ISSET(s_windowserver_fd, &rfds)); - bool success = drain_messages_from_server(); - if (!success) - return false; - for (ssize_t i = 0; i < m_unprocessed_bundles.size(); ++i) { - if (m_unprocessed_bundles[i].message.type == type) { - event = move(m_unprocessed_bundles[i].message); - m_unprocessed_bundles.remove(i); - return true; - } - } - } -} - -WSAPI_ServerMessage GEventLoop::sync_request(const WSAPI_ClientMessage& request, WSAPI_ServerMessage::Type response_type) -{ - bool success = post_message_to_server(request); - ASSERT(success); - - WSAPI_ServerMessage response; - success = wait_for_specific_event(response_type, response); - ASSERT(success); - return response; -} - -void GEventLoop::handle_greeting(WSAPI_ServerMessage& message) +void GWindowServerConnection::handle_greeting(WSAPI_ServerMessage& message) { - s_server_pid = message.greeting.server_pid; - s_my_client_id = message.greeting.your_client_id; + set_server_pid(message.greeting.server_pid); + set_my_client_id(message.greeting.your_client_id); GDesktop::the().did_receive_screen_rect({}, message.greeting.screen_rect); } diff --git a/Libraries/LibGUI/GEventLoop.h b/Libraries/LibGUI/GEventLoop.h index aa54682545..303a77e835 100644 --- a/Libraries/LibGUI/GEventLoop.h +++ b/Libraries/LibGUI/GEventLoop.h @@ -1,6 +1,7 @@ #pragma once #include <LibCore/CEventLoop.h> +#include <LibCore/CIPCClientSideConnection.h> #include <LibGUI/GEvent.h> #include <WindowServer/WSAPITypes.h> @@ -9,48 +10,16 @@ class CObject; class CNotifier; class GWindow; -class GEventLoop final : public CEventLoop { +class GWindowServerConnection : public CIPCClientSideConnection<WSAPI_ServerMessage, WSAPI_ClientMessage> { public: - GEventLoop(); - virtual ~GEventLoop() override; - - static GEventLoop& current() { return static_cast<GEventLoop&>(CEventLoop::current()); } - - static bool post_message_to_server(const WSAPI_ClientMessage&, const ByteBuffer& extra_data = {}); - bool wait_for_specific_event(WSAPI_ServerMessage::Type, WSAPI_ServerMessage&); - WSAPI_ServerMessage sync_request(const WSAPI_ClientMessage& request, WSAPI_ServerMessage::Type response_type); + GWindowServerConnection() + : CIPCClientSideConnection("/tmp/wsportal") + {} - static pid_t server_pid() { return s_server_pid; } - static int my_client_id() { return s_my_client_id; } - - virtual void take_pending_events_from(CEventLoop& other) override - { - CEventLoop::take_pending_events_from(other); - m_unprocessed_bundles.append(move(static_cast<GEventLoop&>(other).m_unprocessed_bundles)); - } + void handshake() override; private: - virtual void add_file_descriptors_for_select(fd_set& fds, int& max_fd_added) override - { - FD_SET(s_windowserver_fd, &fds); - max_fd_added = s_windowserver_fd; - } - - virtual void process_file_descriptors_after_select(const fd_set& fds) override - { - if (FD_ISSET(s_windowserver_fd, &fds)) - drain_messages_from_server(); - } - - virtual void do_processing() override - { - while (!m_unprocessed_bundles.is_empty()) - process_unprocessed_bundles(); - } - - void wait_for_event(); - bool drain_messages_from_server(); - void process_unprocessed_bundles(); + void postprocess_bundles(Vector<IncomingASMessageBundle>& m_unprocessed_bundles) override; void handle_paint_event(const WSAPI_ServerMessage&, GWindow&, const ByteBuffer& extra_data); void handle_resize_event(const WSAPI_ServerMessage&, GWindow&); void handle_mouse_event(const WSAPI_ServerMessage&, GWindow&); @@ -61,15 +30,18 @@ private: void handle_window_entered_or_left_event(const WSAPI_ServerMessage&, GWindow&); void handle_wm_event(const WSAPI_ServerMessage&, GWindow&); void handle_greeting(WSAPI_ServerMessage&); - void connect_to_server(); +}; + +class GEventLoop final : public CEventLoop { +public: + GEventLoop(); + virtual ~GEventLoop() override; + + static GEventLoop& current() { return static_cast<GEventLoop&>(CEventLoop::current()); } - struct IncomingWSMessageBundle { - WSAPI_ServerMessage message; - ByteBuffer extra_data; - }; + GWindowServerConnection& connection() { return m_connection; } - Vector<IncomingWSMessageBundle> m_unprocessed_bundles; - static pid_t s_server_pid; - static int s_my_client_id; - static int s_windowserver_fd; +private: + void process_unprocessed_bundles(); + GWindowServerConnection m_connection; }; diff --git a/Libraries/LibGUI/GMenu.cpp b/Libraries/LibGUI/GMenu.cpp index 048ac38e67..f30789c8e3 100644 --- a/Libraries/LibGUI/GMenu.cpp +++ b/Libraries/LibGUI/GMenu.cpp @@ -52,7 +52,7 @@ void GMenu::popup(const Point& screen_position) request.type = WSAPI_ClientMessage::Type::PopupMenu; request.menu.menu_id = m_menu_id; request.menu.position = screen_position; - GEventLoop::post_message_to_server(request); + GEventLoop::current().connection().post_message_to_server(request); } void GMenu::dismiss() @@ -62,7 +62,7 @@ void GMenu::dismiss() WSAPI_ClientMessage request; request.type = WSAPI_ClientMessage::Type::DismissMenu; request.menu.menu_id = m_menu_id; - GEventLoop::post_message_to_server(request); + GEventLoop::current().connection().post_message_to_server(request); } int GMenu::realize_menu() @@ -72,7 +72,7 @@ int GMenu::realize_menu() ASSERT(m_name.length() < (ssize_t)sizeof(request.text)); strcpy(request.text, m_name.characters()); request.text_length = m_name.length(); - auto response = GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidCreateMenu); + auto response = GEventLoop::current().connection().sync_request(request, WSAPI_ServerMessage::Type::DidCreateMenu); m_menu_id = response.menu.menu_id; #ifdef GMENU_DEBUG @@ -87,7 +87,7 @@ int GMenu::realize_menu() WSAPI_ClientMessage request; request.type = WSAPI_ClientMessage::Type::AddMenuSeparator; request.menu.menu_id = m_menu_id; - GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidAddMenuSeparator); + GEventLoop::current().connection().sync_request(request, WSAPI_ServerMessage::Type::DidAddMenuSeparator); continue; } if (item.type() == GMenuItem::Action) { @@ -113,7 +113,7 @@ int GMenu::realize_menu() request.menu.shortcut_text_length = 0; } - GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidAddMenuItem); + GEventLoop::current().connection().sync_request(request, WSAPI_ServerMessage::Type::DidAddMenuItem); } } all_menus().set(m_menu_id, this); @@ -128,7 +128,7 @@ void GMenu::unrealize_menu() WSAPI_ClientMessage request; request.type = WSAPI_ClientMessage::Type::DestroyMenu; request.menu.menu_id = m_menu_id; - GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidDestroyMenu); + GEventLoop::current().connection().sync_request(request, WSAPI_ServerMessage::Type::DidDestroyMenu); m_menu_id = 0; } diff --git a/Libraries/LibGUI/GMenuBar.cpp b/Libraries/LibGUI/GMenuBar.cpp index 19c736d8bc..0b8f688466 100644 --- a/Libraries/LibGUI/GMenuBar.cpp +++ b/Libraries/LibGUI/GMenuBar.cpp @@ -19,7 +19,7 @@ int GMenuBar::realize_menubar() { WSAPI_ClientMessage request; request.type = WSAPI_ClientMessage::Type::CreateMenubar; - WSAPI_ServerMessage response = GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidCreateMenubar); + WSAPI_ServerMessage response = GEventLoop::current().connection().sync_request(request, WSAPI_ServerMessage::Type::DidCreateMenubar); return response.menu.menubar_id; } @@ -30,7 +30,7 @@ void GMenuBar::unrealize_menubar() WSAPI_ClientMessage request; request.type = WSAPI_ClientMessage::Type::DestroyMenubar; request.menu.menubar_id = m_menubar_id; - GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidDestroyMenubar); + GEventLoop::current().connection().sync_request(request, WSAPI_ServerMessage::Type::DidDestroyMenubar); m_menubar_id = -1; } @@ -47,12 +47,12 @@ void GMenuBar::notify_added_to_application(Badge<GApplication>) request.type = WSAPI_ClientMessage::Type::AddMenuToMenubar; request.menu.menubar_id = m_menubar_id; request.menu.menu_id = menu_id; - GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidAddMenuToMenubar); + GEventLoop::current().connection().sync_request(request, WSAPI_ServerMessage::Type::DidAddMenuToMenubar); } WSAPI_ClientMessage request; request.type = WSAPI_ClientMessage::Type::SetApplicationMenubar; request.menu.menubar_id = m_menubar_id; - GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidSetApplicationMenubar); + GEventLoop::current().connection().sync_request(request, WSAPI_ServerMessage::Type::DidSetApplicationMenubar); } void GMenuBar::notify_removed_from_application(Badge<GApplication>) diff --git a/Libraries/LibGUI/GMenuItem.cpp b/Libraries/LibGUI/GMenuItem.cpp index c07333c514..1c0c6ab57b 100644 --- a/Libraries/LibGUI/GMenuItem.cpp +++ b/Libraries/LibGUI/GMenuItem.cpp @@ -70,5 +70,5 @@ void GMenuItem::update_window_server() request.menu.shortcut_text_length = 0; } - GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidUpdateMenuItem); + GEventLoop::current().connection().sync_request(request, WSAPI_ServerMessage::Type::DidUpdateMenuItem); } diff --git a/Libraries/LibGUI/GWindow.cpp b/Libraries/LibGUI/GWindow.cpp index 0d1c475a45..d0e23a7d39 100644 --- a/Libraries/LibGUI/GWindow.cpp +++ b/Libraries/LibGUI/GWindow.cpp @@ -59,7 +59,7 @@ void GWindow::move_to_front() WSAPI_ClientMessage request; request.type = WSAPI_ClientMessage::Type::MoveWindowToFront; request.window_id = m_window_id; - GEventLoop::post_message_to_server(request); + GEventLoop::current().connection().post_message_to_server(request); } void GWindow::show() @@ -84,7 +84,7 @@ void GWindow::show() ASSERT(m_title_when_windowless.length() < (ssize_t)sizeof(request.text)); strcpy(request.text, m_title_when_windowless.characters()); request.text_length = m_title_when_windowless.length(); - auto response = GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidCreateWindow); + auto response = GEventLoop::current().connection().sync_request(request, WSAPI_ServerMessage::Type::DidCreateWindow); m_window_id = response.window_id; windows().set(m_window_id, this); @@ -99,7 +99,7 @@ void GWindow::hide() WSAPI_ClientMessage request; request.type = WSAPI_ClientMessage::Type::DestroyWindow; request.window_id = m_window_id; - GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidDestroyWindow); + GEventLoop::current().connection().sync_request(request, WSAPI_ServerMessage::Type::DidDestroyWindow); m_window_id = 0; m_pending_paint_event_rects.clear(); m_back_bitmap = nullptr; @@ -118,7 +118,7 @@ void GWindow::set_title(const StringView& title) ASSERT(m_title_when_windowless.length() < (ssize_t)sizeof(request.text)); strcpy(request.text, m_title_when_windowless.characters()); request.text_length = m_title_when_windowless.length(); - GEventLoop::current().post_message_to_server(request); + GEventLoop::current().connection().post_message_to_server(request); } String GWindow::title() const @@ -129,7 +129,7 @@ String GWindow::title() const WSAPI_ClientMessage request; request.type = WSAPI_ClientMessage::Type::GetWindowTitle; request.window_id = m_window_id; - auto response = GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidGetWindowTitle); + auto response = GEventLoop::current().connection().sync_request(request, WSAPI_ServerMessage::Type::DidGetWindowTitle); return String(response.text, response.text_length); } @@ -141,7 +141,7 @@ Rect GWindow::rect() const WSAPI_ClientMessage request; request.type = WSAPI_ClientMessage::Type::GetWindowRect; request.window_id = m_window_id; - auto response = GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidGetWindowRect); + auto response = GEventLoop::current().connection().sync_request(request, WSAPI_ServerMessage::Type::DidGetWindowRect); ASSERT(response.window_id == m_window_id); return response.window.rect; } @@ -158,7 +158,7 @@ void GWindow::set_rect(const Rect& a_rect) request.type = WSAPI_ClientMessage::Type::SetWindowRect; request.window_id = m_window_id; request.window.rect = a_rect; - GEventLoop::current().post_message_to_server(request); + GEventLoop::current().connection().post_message_to_server(request); if (m_back_bitmap && m_back_bitmap->size() != a_rect.size()) m_back_bitmap = nullptr; if (m_front_bitmap && m_front_bitmap->size() != a_rect.size()) @@ -180,7 +180,7 @@ void GWindow::set_override_cursor(GStandardCursor cursor) request.type = WSAPI_ClientMessage::Type::SetWindowOverrideCursor; request.window_id = m_window_id; request.cursor.cursor = (WSAPI_StandardCursor)cursor; - GEventLoop::current().post_message_to_server(request); + GEventLoop::current().connection().post_message_to_server(request); } void GWindow::event(CEvent& event) @@ -260,7 +260,7 @@ void GWindow::event(CEvent& event) ByteBuffer extra_data; if (rects.size() > WSAPI_ClientMessage::max_inline_rect_count) extra_data = ByteBuffer::wrap(&rects[WSAPI_ClientMessage::max_inline_rect_count], (rects.size() - WSAPI_ClientMessage::max_inline_rect_count) * sizeof(WSAPI_Rect)); - GEventLoop::current().post_message_to_server(message, extra_data); + GEventLoop::current().connection().post_message_to_server(message, move(extra_data)); } return; } @@ -453,7 +453,7 @@ void GWindow::update(const Rect& a_rect) if (rects.size() > WSAPI_ClientMessage::max_inline_rect_count) extra_data = ByteBuffer::wrap(&rects[WSAPI_ClientMessage::max_inline_rect_count], (rects.size() - WSAPI_ClientMessage::max_inline_rect_count) * sizeof(WSAPI_Rect)); request.rect_count = rects.size(); - GEventLoop::current().post_message_to_server(request, extra_data); + GEventLoop::current().connection().post_message_to_server(request, move(extra_data)); }); } m_pending_paint_event_rects.append(a_rect); @@ -524,7 +524,7 @@ void GWindow::set_has_alpha_channel(bool value) message.type = WSAPI_ClientMessage::Type::SetWindowHasAlphaChannel; message.window_id = m_window_id; message.value = value; - GEventLoop::current().sync_request(message, WSAPI_ServerMessage::DidSetWindowHasAlphaChannel); + GEventLoop::current().connection().sync_request(message, WSAPI_ServerMessage::DidSetWindowHasAlphaChannel); update(); } @@ -545,7 +545,7 @@ void GWindow::set_opacity(float opacity) request.window_id = m_window_id; request.window.opacity = opacity; m_opacity_when_windowless = opacity; - GEventLoop::current().post_message_to_server(request); + GEventLoop::current().connection().post_message_to_server(request); } void GWindow::set_hovered_widget(GWidget* widget) @@ -573,7 +573,7 @@ void GWindow::set_current_backing_bitmap(GraphicsBitmap& bitmap, bool flush_imme message.backing.has_alpha_channel = bitmap.has_alpha_channel(); message.backing.size = bitmap.size(); message.backing.flush_immediately = flush_immediately; - GEventLoop::current().sync_request(message, WSAPI_ServerMessage::Type::DidSetWindowBackingStore); + GEventLoop::current().connection().sync_request(message, WSAPI_ServerMessage::Type::DidSetWindowBackingStore); } void GWindow::flip(const Vector<Rect, 32>& dirty_rects) @@ -596,11 +596,11 @@ void GWindow::flip(const Vector<Rect, 32>& dirty_rects) NonnullRefPtr<GraphicsBitmap> GWindow::create_backing_bitmap(const Size& size) { - ASSERT(GEventLoop::server_pid()); + ASSERT(GEventLoop::current().connection().server_pid()); ASSERT(!size.is_empty()); size_t pitch = round_up_to_power_of_two(size.width() * sizeof(RGBA32), 16); size_t size_in_bytes = size.height() * pitch; - auto shared_buffer = SharedBuffer::create(GEventLoop::server_pid(), size_in_bytes); + auto shared_buffer = SharedBuffer::create(GEventLoop::current().connection().server_pid(), size_in_bytes); ASSERT(shared_buffer); auto format = m_has_alpha_channel ? GraphicsBitmap::Format::RGBA32 : GraphicsBitmap::Format::RGB32; return GraphicsBitmap::create_with_shared_buffer(format, *shared_buffer, size); @@ -629,16 +629,16 @@ void GWindow::set_icon_path(const StringView& path) ASSERT(path.length() < (int)sizeof(message.text)); strcpy(message.text, String(path).characters()); message.text_length = path.length(); - GEventLoop::post_message_to_server(message); + GEventLoop::current().connection().post_message_to_server(message); } void GWindow::start_wm_resize() { WSAPI_ClientMessage message; message.type = WSAPI_ClientMessage::Type::WM_StartWindowResize; - message.wm.client_id = GEventLoop::my_client_id(); + message.wm.client_id = GEventLoop::current().connection().my_client_id(); message.wm.window_id = m_window_id; - GEventLoop::post_message_to_server(message); + GEventLoop::current().connection().post_message_to_server(message); } Vector<GWidget*> GWindow::focusable_widgets() const diff --git a/Userland/aplay.cpp b/Userland/aplay.cpp index e50fe16edc..8d8ef71d56 100644 --- a/Userland/aplay.cpp +++ b/Userland/aplay.cpp @@ -14,6 +14,7 @@ int main(int argc, char **argv) printf("Establishing connection\n"); AClientConnection a_conn; + a_conn.handshake(); printf("Established connection\n"); AWavLoader loader; const auto& buffer = loader.load_wav(argv[1]); |