diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-02-19 01:42:53 +0100 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-02-19 01:42:53 +0100 |
commit | 9b71307d499924834d029adc91baf9689bfa49fd (patch) | |
tree | 910481262bce702d78f3af9a806965a769d4c644 /WindowServer | |
parent | d4973842c913ca79e21f7dd53fe1a4d5720886e3 (diff) | |
download | serenity-9b71307d499924834d029adc91baf9689bfa49fd.zip |
WindowServer: Support windows with alpha channels. And per-WSWindow opacity.
This patch also adds a Format concept to GraphicsBitmap. For now there are
only two formats: RGB32 and RGBA32. Windows with alpha channel have their
backing stores created in the RGBA32 format.
Use this to make Terminal windows semi-transparent for that comfy rice look.
There is one problem here, in that window compositing overdraw incurs
multiple passes of blending of the same pixels. This leads to a mismatch in
opacity which is obviously not good. I will work on this in a later patch.
The alpha blending is currently straight C++. It should be relatively easy
to optimize this using SSE instructions.
For now I'm just happy with the cute effect. :^)
Diffstat (limited to 'WindowServer')
-rw-r--r-- | WindowServer/WSAPITypes.h | 4 | ||||
-rw-r--r-- | WindowServer/WSClientConnection.cpp | 21 | ||||
-rw-r--r-- | WindowServer/WSClientConnection.h | 3 | ||||
-rw-r--r-- | WindowServer/WSMessage.h | 29 | ||||
-rw-r--r-- | WindowServer/WSMessageLoop.cpp | 2 | ||||
-rw-r--r-- | WindowServer/WSWindow.cpp | 7 | ||||
-rw-r--r-- | WindowServer/WSWindow.h | 10 | ||||
-rw-r--r-- | WindowServer/WSWindowManager.cpp | 31 |
8 files changed, 86 insertions, 21 deletions
diff --git a/WindowServer/WSAPITypes.h b/WindowServer/WSAPITypes.h index 8f9257c715..3ed1b55077 100644 --- a/WindowServer/WSAPITypes.h +++ b/WindowServer/WSAPITypes.h @@ -117,6 +117,7 @@ struct WSAPI_ServerMessage { size_t bpp; size_t pitch; int shared_buffer_id; + bool has_alpha_channel; } backing; }; }; @@ -142,6 +143,7 @@ struct WSAPI_ClientMessage { DidFinishPainting, GetWindowBackingStore, SetGlobalCursorTracking, + SetWindowOpacity, }; Type type { Invalid }; int window_id { -1 }; @@ -157,6 +159,8 @@ struct WSAPI_ClientMessage { } menu; struct { WSAPI_Rect rect; + bool has_alpha_channel; + float opacity; } window; }; }; diff --git a/WindowServer/WSClientConnection.cpp b/WindowServer/WSClientConnection.cpp index 841d58a7c9..ad11034333 100644 --- a/WindowServer/WSClientConnection.cpp +++ b/WindowServer/WSClientConnection.cpp @@ -79,14 +79,14 @@ void WSClientConnection::post_message(const WSAPI_ServerMessage& message) ASSERT(nwritten == sizeof(message)); } -RetainPtr<GraphicsBitmap> WSClientConnection::create_shared_bitmap(const Size& size) +RetainPtr<GraphicsBitmap> WSClientConnection::create_shared_bitmap(GraphicsBitmap::Format format, const Size& size) { RGBA32* buffer; int shared_buffer_id = create_shared_buffer(m_pid, size.area() * sizeof(RGBA32), (void**)&buffer); ASSERT(shared_buffer_id >= 0); ASSERT(buffer); ASSERT(buffer != (void*)-1); - return GraphicsBitmap::create_with_shared_buffer(shared_buffer_id, size, buffer); + return GraphicsBitmap::create_with_shared_buffer(format, shared_buffer_id, size, buffer); } void WSClientConnection::on_message(WSMessage& message) @@ -236,6 +236,18 @@ void WSClientConnection::handle_request(WSAPIAddMenuSeparatorRequest& request) post_message(response); } +void WSClientConnection::handle_request(WSAPISetWindowOpacityRequest& request) +{ + int window_id = request.window_id(); + auto it = m_windows.find(window_id); + if (it == m_windows.end()) { + post_error("Bad window ID"); + return; + } + auto& window = *(*it).value; + window.set_opacity(request.opacity()); +} + void WSClientConnection::handle_request(WSAPISetWindowTitleRequest& request) { int window_id = request.window_id(); @@ -298,8 +310,10 @@ void WSClientConnection::handle_request(WSAPICreateWindowRequest& request) { int window_id = m_next_window_id++; auto window = make<WSWindow>(*this, window_id); + window->set_has_alpha_channel(request.has_alpha_channel()); window->set_title(request.title()); window->set_rect(request.rect()); + window->set_opacity(request.opacity()); m_windows.set(window_id, move(window)); WSAPI_ServerMessage response; response.type = WSAPI_ServerMessage::Type::DidCreateWindow; @@ -364,6 +378,7 @@ void WSClientConnection::handle_request(WSAPIGetWindowBackingStoreRequest& reque response.backing.bpp = sizeof(RGBA32); response.backing.pitch = backing_store->pitch(); response.backing.size = backing_store->size(); + response.backing.has_alpha_channel = backing_store->has_alpha_channel(); response.backing.shared_buffer_id = backing_store->shared_buffer_id(); post_message(response); } @@ -419,6 +434,8 @@ void WSClientConnection::on_request(WSAPIClientRequest& request) return handle_request(static_cast<WSAPIGetWindowBackingStoreRequest&>(request)); case WSMessage::APISetGlobalCursorTrackingRequest: return handle_request(static_cast<WSAPISetGlobalCursorTrackingRequest&>(request)); + case WSMessage::APISetWindowOpacityRequest: + return handle_request(static_cast<WSAPISetWindowOpacityRequest&>(request)); default: break; } diff --git a/WindowServer/WSClientConnection.h b/WindowServer/WSClientConnection.h index f2467c14fd..fdcc27adab 100644 --- a/WindowServer/WSClientConnection.h +++ b/WindowServer/WSClientConnection.h @@ -22,7 +22,7 @@ public: static void for_each_client(Function<void(WSClientConnection&)>); void post_message(const WSAPI_ServerMessage&); - RetainPtr<GraphicsBitmap> create_shared_bitmap(const Size&); + RetainPtr<GraphicsBitmap> create_shared_bitmap(GraphicsBitmap::Format, const Size&); int client_id() const { return m_client_id; } WSMenuBar* app_menubar() { return m_app_menubar.ptr(); } @@ -52,6 +52,7 @@ private: void handle_request(WSAPIDidFinishPaintingNotification&); void handle_request(WSAPIGetWindowBackingStoreRequest&); void handle_request(WSAPISetGlobalCursorTrackingRequest&); + void handle_request(WSAPISetWindowOpacityRequest&); void post_error(const String&); diff --git a/WindowServer/WSMessage.h b/WindowServer/WSMessage.h index fdbd67dbe3..0656bc4bd5 100644 --- a/WindowServer/WSMessage.h +++ b/WindowServer/WSMessage.h @@ -39,6 +39,7 @@ public: APIDidFinishPaintingNotification, APIGetWindowBackingStoreRequest, APISetGlobalCursorTrackingRequest, + APISetWindowOpacityRequest, __End_API_Client_Requests, }; @@ -253,6 +254,26 @@ private: int m_window_id { 0 }; }; +class WSAPISetWindowOpacityRequest final : public WSAPIClientRequest { +public: + explicit WSAPISetWindowOpacityRequest(int client_id, int window_id, float opacity) + : WSAPIClientRequest(WSMessage::APISetWindowOpacityRequest, client_id) + , m_client_id(client_id) + , m_window_id(window_id) + , m_opacity(opacity) + { + } + + int client_id() const { return m_client_id; } + int window_id() const { return m_window_id; } + float opacity() const { return m_opacity; } + +private: + int m_client_id { 0 }; + int m_window_id { 0 }; + float m_opacity { 0 }; +}; + class WSAPISetWindowRectRequest final : public WSAPIClientRequest { public: explicit WSAPISetWindowRectRequest(int client_id, int window_id, const Rect& rect) @@ -292,19 +313,25 @@ private: class WSAPICreateWindowRequest : public WSAPIClientRequest { public: - WSAPICreateWindowRequest(int client_id, const Rect& rect, const String& title) + WSAPICreateWindowRequest(int client_id, const Rect& rect, const String& title, bool has_alpha_channel, float opacity) : WSAPIClientRequest(WSMessage::APICreateWindowRequest, client_id) , m_rect(rect) , m_title(title) + , m_opacity(opacity) + , m_has_alpha_channel(has_alpha_channel) { } Rect rect() const { return m_rect; } String title() const { return m_title; } + bool has_alpha_channel() const { return m_has_alpha_channel; } + float opacity() const { return m_opacity; } private: Rect m_rect; String m_title; + float m_opacity { 0 }; + bool m_has_alpha_channel { false }; }; class WSAPIDestroyWindowRequest : public WSAPIClientRequest { diff --git a/WindowServer/WSMessageLoop.cpp b/WindowServer/WSMessageLoop.cpp index 6cf92b8374..199222a4dd 100644 --- a/WindowServer/WSMessageLoop.cpp +++ b/WindowServer/WSMessageLoop.cpp @@ -302,7 +302,7 @@ void WSMessageLoop::on_receive_from_client(int client_id, const WSAPI_ClientMess break; case WSAPI_ClientMessage::Type::CreateWindow: ASSERT(message.text_length < sizeof(message.text)); - post_message(client, make<WSAPICreateWindowRequest>(client_id, message.window.rect, String(message.text, message.text_length))); + post_message(client, make<WSAPICreateWindowRequest>(client_id, message.window.rect, String(message.text, message.text_length), message.window.has_alpha_channel, message.window.opacity)); break; case WSAPI_ClientMessage::Type::DestroyWindow: post_message(client, make<WSAPIDestroyWindowRequest>(client_id, message.window_id)); diff --git a/WindowServer/WSWindow.cpp b/WindowServer/WSWindow.cpp index 8e440e1ec8..61e9073123 100644 --- a/WindowServer/WSWindow.cpp +++ b/WindowServer/WSWindow.cpp @@ -44,9 +44,10 @@ void WSWindow::set_rect(const Rect& rect) m_rect = rect; if (!m_backing || old_rect.size() != rect.size()) { if (m_menu) - m_backing = GraphicsBitmap::create(m_rect.size()); - else if (m_client) - m_backing = m_client->create_shared_bitmap(m_rect.size()); + m_backing = GraphicsBitmap::create(GraphicsBitmap::Format::RGB32, m_rect.size()); + else if (m_client) { + m_backing = m_client->create_shared_bitmap(m_has_alpha_channel ? GraphicsBitmap::Format::RGBA32 : GraphicsBitmap::Format::RGB32, m_rect.size()); + } } WSWindowManager::the().notify_rect_changed(*this, old_rect, rect); diff --git a/WindowServer/WSWindow.h b/WindowServer/WSWindow.h index 050ec7f63f..3cb02b5a35 100644 --- a/WindowServer/WSWindow.h +++ b/WindowServer/WSWindow.h @@ -25,6 +25,9 @@ public: String title() const { return m_title; } void set_title(String&&); + float opacity() const { return m_opacity; } + void set_opacity(float opacity) { m_opacity = opacity; } + int x() const { return m_rect.x(); } int y() const { return m_rect.y(); } int width() const { return m_rect.width(); } @@ -59,6 +62,9 @@ public: void set_global_cursor_tracking_enabled(bool); bool global_cursor_tracking() const { return m_global_cursor_tracking_enabled; } + bool has_alpha_channel() const { return m_has_alpha_channel; } + void set_has_alpha_channel(bool value) { m_has_alpha_channel = value; } + // For InlineLinkedList. // FIXME: Maybe make a ListHashSet and then WSWindowManager can just use that. WSWindow* m_next { nullptr }; @@ -72,9 +78,9 @@ private: bool m_is_being_dragged { false }; bool m_global_cursor_tracking_enabled { false }; bool m_visible { true }; - + bool m_has_alpha_channel { false }; WSMenu* m_menu { nullptr }; - RetainPtr<GraphicsBitmap> m_backing; int m_window_id { -1 }; + float m_opacity { 1 }; }; diff --git a/WindowServer/WSWindowManager.cpp b/WindowServer/WSWindowManager.cpp index 1772a68346..02154287ef 100644 --- a/WindowServer/WSWindowManager.cpp +++ b/WindowServer/WSWindowManager.cpp @@ -144,8 +144,8 @@ WSWindowManager::WSWindowManager() (void)m_flush_count; #endif auto size = m_screen_rect.size(); - m_front_bitmap = GraphicsBitmap::create_wrapper(size, m_screen.scanline(0)); - m_back_bitmap = GraphicsBitmap::create_wrapper(size, m_screen.scanline(size.height())); + m_front_bitmap = GraphicsBitmap::create_wrapper(GraphicsBitmap::Format::RGB32, size, m_screen.scanline(0)); + m_back_bitmap = GraphicsBitmap::create_wrapper(GraphicsBitmap::Format::RGB32, size, m_screen.scanline(size.height())); m_front_painter = make<Painter>(*m_front_bitmap); m_back_painter = make<Painter>(*m_back_bitmap); @@ -170,17 +170,17 @@ WSWindowManager::WSWindowManager() m_cursor_bitmap_outer = CharacterBitmap::create_from_ascii(cursor_bitmap_outer_ascii, 12, 17); m_wallpaper_path = "/res/wallpapers/cool.rgb"; - m_wallpaper = GraphicsBitmap::load_from_file(m_wallpaper_path, { 1024, 768 }); + m_wallpaper = GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, m_wallpaper_path, { 1024, 768 }); #ifdef KERNEL ProcFS::the().add_sys_bool("wm_flash_flush", m_flash_flush); ProcFS::the().add_sys_string("wm_wallpaper", m_wallpaper_path, [this] { - m_wallpaper = GraphicsBitmap::load_from_file(m_wallpaper_path, m_screen_rect.size()); + m_wallpaper = GraphicsBitmap::load_from_file(GraphicsBitmap::Format::RGBA32, m_wallpaper_path, m_screen_rect.size()); invalidate(m_screen_rect); }); #endif - m_menu_selection_color = Color(0x84351a); + m_menu_selection_color = Color::from_rgb(0x84351a); { byte system_menu_name[] = { 0xf8, 0 }; @@ -242,8 +242,8 @@ void WSWindowManager::set_resolution(int width, int height) return; m_screen.set_resolution(width, height); m_screen_rect = m_screen.rect(); - m_front_bitmap = GraphicsBitmap::create_wrapper({ width, height }, m_screen.scanline(0)); - m_back_bitmap = GraphicsBitmap::create_wrapper({ width, height }, m_screen.scanline(height)); + m_front_bitmap = GraphicsBitmap::create_wrapper(GraphicsBitmap::Format::RGB32, { width, height }, m_screen.scanline(0)); + m_back_bitmap = GraphicsBitmap::create_wrapper(GraphicsBitmap::Format::RGB32, { width, height }, m_screen.scanline(height)); m_front_painter = make<Painter>(*m_front_bitmap); m_back_painter = make<Painter>(*m_back_bitmap); m_buffers_are_flipped = false; @@ -620,10 +620,17 @@ void WSWindowManager::compose() dbgprintf("[WM] compose #%u (%u rects)\n", ++m_compose_count, dirty_rects.size()); #endif - auto any_window_contains_rect = [this] (const Rect& r) { + auto any_opaque_window_contains_rect = [this] (const Rect& r) { for (auto* window = m_windows_in_order.head(); window; window = window->next()) { if (!window->is_visible()) continue; + if (window->opacity() < 1.0f) + continue; + if (window->has_alpha_channel()) { + // FIXME: Just because the window has an alpha channel doesn't mean it's not opaque. + // Maybe there's some way we could know this? + continue; + } if (outer_window_rect(window->rect()).contains(r)) return true; } @@ -640,9 +647,8 @@ void WSWindowManager::compose() }; for (auto& dirty_rect : dirty_rects) { - if (any_window_contains_rect(dirty_rect)) { + if (any_opaque_window_contains_rect(dirty_rect)) continue; - } if (!m_wallpaper) m_back_painter->fill_rect(dirty_rect, m_background_color); else @@ -665,7 +671,10 @@ void WSWindowManager::compose() dirty_rect_in_window_coordinates.set_y(dirty_rect_in_window_coordinates.y() - window.y()); auto dst = window.position(); dst.move_by(dirty_rect_in_window_coordinates.location()); - m_back_painter->blit(dst, *backing, dirty_rect_in_window_coordinates); + if (window.opacity() == 1.0f) + m_back_painter->blit(dst, *backing, dirty_rect_in_window_coordinates); + else + m_back_painter->blit_with_opacity(dst, *backing, dirty_rect_in_window_coordinates, window.opacity()); m_back_painter->clear_clip_rect(); } m_back_painter->clear_clip_rect(); |