diff --git a/LibLemon/include/Lemon/Graphics/Rect.h b/LibLemon/include/Lemon/Graphics/Rect.h index c4b2304a..151b501f 100644 --- a/LibLemon/include/Lemon/Graphics/Rect.h +++ b/LibLemon/include/Lemon/Graphics/Rect.h @@ -44,7 +44,7 @@ typedef struct Rect { return y + height; } - std::list Split(const Rect& cut) { + std::list Split(const Rect& cut) const { std::list clips; Rect victim = *this; @@ -103,8 +103,31 @@ typedef struct Rect { return (left() < other.right() && right() > other.left() && top() < other.bottom() && bottom() > other.top()); } + // Get overlap of two rectangles + inline Rect GetIntersect(const Rect& cut) const { + Rect victim = *this; + + if (cut.left() >= victim.left() && cut.left() <= victim.right()) { // Clip left edge + victim.left(cut.left()); + } + + if (cut.top() >= victim.top() && cut.top() <= victim.bottom()) { // Clip top edge + victim.top(cut.top()); + } + + if (cut.right() >= victim.left() && cut.right() <= victim.right()) { // Clip right edge + victim.right(cut.right()); + } + + if (cut.bottom() >= victim.top() && cut.bottom() <= victim.bottom()) { // Clip bottom edge + victim.bottom(cut.bottom()); + } + + return victim; + } + inline bool Contains(const Rect& other) const { - return (left() < other.right() && left() < other.left() && right() > other.left() && right() > other.right() && top() < other.bottom() && top() < other.top() && bottom() > other.top() && bottom() > other.bottom()); + return (left() < other.right() && left() <= other.left() && right() > other.left() && right() >= other.right() && top() < other.bottom() && top() <= other.top() && bottom() > other.top() && bottom() >= other.bottom()); } inline bool Contains(const Vector2i& other) const { diff --git a/System/LemonWM/Compositor.cpp b/System/LemonWM/Compositor.cpp index d762b2e2..fb4608e9 100644 --- a/System/LemonWM/Compositor.cpp +++ b/System/LemonWM/Compositor.cpp @@ -25,6 +25,8 @@ Compositor::Compositor(const Surface& displaySurface) : m_displaySurface(display Logger::Debug("Error loading resize cursor image!"); m_cursorResize = m_cursorNormal; } + + clock_gettime(CLOCK_BOOTTIME, &m_lastRender); } void Compositor::Render() { @@ -37,7 +39,7 @@ void Compositor::Render() { m_avgFrametime += renderTime; - if (m_avgFrametime > 1000000000) { + if (m_avgFrametime > 1000000000 && m_fCount) { if (m_avgFrametime) m_fRate = 1000000000 / (m_avgFrametime / m_fCount); m_fCount = 0; @@ -57,8 +59,8 @@ void Compositor::Render() { Vector2i mousePos = WM::Instance().Input().mouse.pos; if (m_invalidateAll) { - RecalculateWindowClipping(); RecalculateBackgroundClipping(); + RecalculateWindowClipping(); // We fill the areas of the screen being redrawn in debug mode // This happens before the render surface is blitted to the display surface @@ -79,13 +81,13 @@ void Compositor::Render() { } } } else { - for(WMWindow* win : WM::Instance().m_windows){ - if(win->IsDirtyAndClear()){ + for (WMWindow* win : WM::Instance().m_windows) { + if (win->IsDirtyAndClear()) { Invalidate(win->GetContentRect()); } } } - + if (m_wallpaper.buffer) { for (BackgroundClipRect& rect : m_backgroundRects) { if (!rect.invalid) { @@ -106,7 +108,11 @@ void Compositor::Render() { WMWindow* win = it->win; if (m_invalidateAll || it->invalid) { // Window buffer not dirty, only draw invalid clips - win->DrawClip(it->rect, &m_renderSurface); + if(it->type == WindowClipRect::TypeWindowDecoration){ + win->DrawDecorationClip(it->rect, &m_renderSurface); + } else { + win->DrawClip(it->rect, &m_renderSurface); + } it->invalid = false; it++; @@ -135,11 +141,13 @@ void Compositor::Render() { rect.invalid = false; } - if(WM::Instance().m_showContextMenu){ - Lemon::Graphics::DrawRoundedRect(WM::Instance().m_contextMenu.bounds, WMWindow::theme.titlebarColour, 5, 5, 5, 5, &m_renderSurface); - for(const auto& ent : WM::Instance().m_contextMenu.entries){ + if (WM::Instance().m_showContextMenu) { + Lemon::Graphics::DrawRoundedRect(WM::Instance().m_contextMenu.bounds, WMWindow::theme.titlebarColour, 5, 5, 5, + 5, &m_renderSurface); + for (const auto& ent : WM::Instance().m_contextMenu.entries) { const Vector2i& pos = ent.bounds.pos; - Lemon::Graphics::DrawString(ent.text.c_str(), pos.x, pos.y, GUI::Theme::Current().ColourText(), &m_renderSurface); + Lemon::Graphics::DrawString(ent.text.c_str(), pos.x, pos.y, GUI::Theme::Current().ColourText(), + &m_renderSurface); } } @@ -165,83 +173,38 @@ void Compositor::Invalidate(const Rect& rect) { return; } - std::function invalidateDecorationRect; - std::function invalidateBackgroundRect; - - invalidateDecorationRect = [this, invalidateBackgroundRect](WindowClipRect& dRect, const Rect& rect) { - if (dRect.invalid) { - return; - } - - if (dRect.rect.Intersects(rect)) { - dRect.invalid = true; // Set bg rect as invalid - - // Make sure any background clips are redrawn - /*for (auto& bgRect : m_backgroundRects) { - invalidateBackgroundRect(bgRect, dRect.rect); - }*/ - - // Make sure any window clips are redrawn - for (auto& wRect : m_windowClipRects) { - if (wRect.invalid) { - continue; - } - - if (wRect.rect.Intersects(dRect.rect)) { - wRect.invalid = true; // Set wubdiw rect as invalid - } - } - } - }; - - invalidateBackgroundRect = [this, invalidateDecorationRect](BackgroundClipRect& bgRect, const Rect& rect) { + for (auto& bgRect : m_backgroundRects) { if (bgRect.invalid) { - return; + continue; } if (bgRect.rect.Intersects(rect)) { bgRect.invalid = true; // Set bg rect as invalid - - // If a decoration rect got clipped by a window, - // The background clip may extend beyond the clip - // To avoid drawing the background over the decorations - // Mark any intersecting window decorations as invalid - for (auto& dRect : m_windowDecorationClipRects) { - invalidateDecorationRect(dRect, bgRect.rect); - } - - for(auto& wRect : m_windowClipRects){ - if (wRect.invalid) { - continue; - } - - if(wRect.win->IsTransparent() && bgRect.rect.Intersects(wRect.rect)){ - wRect.invalid = true; - } - } } - }; - - for (auto& bgRect : m_backgroundRects) { - invalidateBackgroundRect(bgRect, rect); } for (auto& wRect : m_windowClipRects) { if (wRect.invalid) { + for (auto* bgRect : wRect.win->occludedBackgroundRects) { + bgRect->invalid = true; + } + + for (auto* oRect : wRect.win->occludedWindowRects) { + oRect->invalid = true; + } continue; } if (wRect.rect.Intersects(rect)) { wRect.invalid = true; - - for (auto& dRect : m_windowDecorationClipRects) { - invalidateDecorationRect(dRect, wRect.rect); + for (auto* bgRect : wRect.win->occludedBackgroundRects) { + bgRect->invalid = true; } - } - } - for (auto& dRect : m_windowDecorationClipRects) { - invalidateDecorationRect(dRect, rect); + for (auto* oRect : wRect.win->occludedWindowRects) { + oRect->invalid = true; + } + } } } @@ -268,46 +231,69 @@ void Compositor::SetWallpaper(const std::string& path) { void Compositor::RecalculateWindowClipping() { m_windowClipRects.clear(); - m_windowDecorationClipRects.clear(); for (WMWindow* win : WM::Instance().m_windows) { + win->occludedBackgroundRects.clear(); + win->occludedWindowRects.clear(); + if (win->IsMinimized()) { continue; } - if (win->IsTransparent()) { - // Transparent windows do not cut other windows - m_windowClipRects.push_back({win->GetContentRect(), win}); - goto retryDecoration; - } retry: for (auto it = m_windowClipRects.begin(); it != m_windowClipRects.end(); it++) { + if (win->IsTransparent() && win->GetContentRect().Contains(it->rect)) { + continue; + } + if (it->rect.Intersects(win->GetContentRect())) { - m_windowClipRects.erase(it); + auto result = it->SplitModify(win->GetContentRect()); - // Only use the window content to clip other windows - // This is so the windows render under the rounded corners - m_windowClipRects.splice(m_windowClipRects.end(), it->Split(win->GetContentRect())); + if (!win->IsTransparent()) { + m_windowClipRects.erase(it); + } + + m_windowClipRects.splice(m_windowClipRects.end(), std::move(result)); goto retry; } } - m_windowClipRects.push_back({win->GetContentRect(), win}); + if(win->ShouldDrawDecoration()){ + auto decorationRects = WindowClipRect{win->GetRect(), win, WindowClipRect::TypeWindowDecoration}.Split(win->GetContentRect()); - retryDecoration: - if (win->ShouldDrawDecoration()) { - for (auto it = m_windowDecorationClipRects.begin(); it != m_windowDecorationClipRects.end(); it++) { - if (it->rect.Intersects(win->GetRect())) { - m_windowDecorationClipRects.erase(it); - - // Only use the window content to clip other windows - // This is so the windows render under the rounded corners - m_windowDecorationClipRects.splice(m_windowDecorationClipRects.end(), it->Split(win->GetRect())); - goto retryDecoration; + for (auto& rect : decorationRects) { + for (auto it = m_windowClipRects.begin(); it != m_windowClipRects.end(); it++) { + // Decoration rects are treated as transparent + if (rect.rect.Contains(it->rect)) { + continue; + } + + if (it->rect.Intersects(rect.rect)) { + auto result = it->SplitModify(rect.rect); + + m_windowClipRects.splice(m_windowClipRects.end(), std::move(result)); + goto retry; + } + } + } + + m_windowClipRects.splice(m_windowClipRects.end(), std::move(decorationRects)); + } + + m_windowClipRects.push_back({win->GetContentRect(), win, WindowClipRect::TypeWindow}); + + if (win->IsTransparent()) { + for (auto& bgRect : m_backgroundRects) { + if (bgRect.rect.Intersects(win->GetContentRect())) { + win->occludedBackgroundRects.push_back(&bgRect); } } - m_windowDecorationClipRects.push_back({win->GetRect(), win}); + for (auto& bgRect : m_windowClipRects) { + if (bgRect.rect.Intersects(win->GetContentRect())) { + win->occludedWindowRects.push_back(&bgRect); + } + } } } } @@ -319,23 +305,41 @@ void Compositor::RecalculateBackgroundClipping() { auto& windows = WM::Instance().m_windows; for (WMWindow* win : windows) { - if (win->IsMinimized() || win->IsTransparent()) { + if (win->IsMinimized()) { continue; } + + std::list splitDecorationRects; + if (win->ShouldDrawDecoration()) { + splitDecorationRects = win->GetRect().Split(win->GetContentRect()); + } + retry: for (auto it = m_backgroundRects.begin(); it != m_backgroundRects.end(); it++) { - if (it->rect.Intersects(win->GetRect())) { - auto result = it->Split(win->GetRect()); + if (win->ShouldDrawDecoration()) { + for (auto& dRect : splitDecorationRects) { + if (it->rect.Intersects(dRect) && !dRect.Contains(it->rect)) { + auto result = it->SplitModify(dRect); + + m_backgroundRects.splice(m_backgroundRects.end(), std::move(result)); + goto retry; + } + } + } - m_backgroundRects.erase(it); + if (win->IsTransparent() && win->GetContentRect().Contains(it->rect)) { + continue; // Background rect entirely within transparent window + } + + if (it->rect.Intersects(win->GetContentRect())) { + auto result = it->SplitModify(win->GetContentRect()); + + if (!win->IsTransparent()) { + m_backgroundRects.erase(it); + } m_backgroundRects.splice(m_backgroundRects.end(), std::move(result)); goto retry; } } - - if (win->ShouldDrawDecoration()) { - // Add a background clip under the titlebar - m_backgroundRects.push_back({win->GetTitlebarRect(), true}); - } } } diff --git a/System/LemonWM/Compositor.h b/System/LemonWM/Compositor.h index 0980e214..0d596068 100644 --- a/System/LemonWM/Compositor.h +++ b/System/LemonWM/Compositor.h @@ -8,9 +8,7 @@ #include #include -//#define COMPOSITOR_DEBUG 1 - -template std::list Split(Rect victim, const Rect& cut, D... extraData) { +template std::list SplitModify(Rect& victim, const Rect& cut, D... extraData) { std::list clips; if (cut.left() >= victim.left() && cut.left() <= victim.right()) { // Clip left edge @@ -72,18 +70,28 @@ template std::list Split(Rect victim, const Rect& cu return clips; } +template inline std::list Split(Rect victim, const Rect& cut, D... extraData){ + return SplitModify(victim, cut, extraData...); +} + struct WindowClipRect { Rect rect; class WMWindow* win; + enum { + TypeWindow, + TypeWindowDecoration, + } type = TypeWindow; bool invalid = true; - std::list Split(const Rect& cut) { return ::Split(rect, cut, win, true); } + std::list SplitModify(const Rect& cut) { return ::SplitModify(rect, cut, win, type, true); } + std::list Split(const Rect& cut) { return ::Split(rect, cut, win, type, true); } }; struct BackgroundClipRect { Rect rect; bool invalid = true; + std::list SplitModify(const Rect& cut) { return ::SplitModify(rect, cut, true); } std::list Split(const Rect& cut) { return ::Split(rect, cut, true); } }; @@ -109,6 +117,10 @@ class Compositor { void RecalculateWindowClipping(); void RecalculateBackgroundClipping(); + void InvalidateBackgroundRect(BackgroundClipRect& bgRect); + void InvalidateWindowRect(WindowClipRect& wRect); + void InvalidateDecorationRect(WindowClipRect& dRect); + bool m_invalidateAll = true; bool m_displayFramerate = false; diff --git a/System/LemonWM/Window.h b/System/LemonWM/Window.h index 43c1ca44..69006b8e 100644 --- a/System/LemonWM/Window.h +++ b/System/LemonWM/Window.h @@ -102,6 +102,11 @@ class WMWindow : public LemonWMClientEndpoint { static WindowTheme theme; + // Used when invalidating transparent windows + std::list occludedBackgroundRects; + std::list occludedWindowRects; + std::list occludedWindowDecorationRects; + private: void UpdateWindowRects(); void CreateWindowBuffer();