1/*
2 * Copyright (C) 2020-2022 The opuntiaOS Project Authors.
3 * + Contributed by Nikita Melekhin <nimelehin@gmail.com>
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include "Compositor.h"
10#include "Components/Base/BaseWindow.h"
11#include "Components/ControlBar/ControlBar.h"
12#include "Components/MenuBar/MenuBar.h"
13#include "Components/Popup/Popup.h"
14#include "Devices/Screen.h"
15#include "Managers/CursorManager.h"
16#include "Managers/ResourceManager.h"
17#include "Managers/WindowManager.h"
18#include <libfoundation/EventLoop.h>
19#include <libfoundation/Memory.h>
20#include <libg/Context.h>
21
22namespace WinServer {
23
24Compositor* s_WinServer_Compositor_the = nullptr;
25
26Compositor::Compositor()
27 : m_cursor_manager(CursorManager::the())
28 , m_resource_manager(ResourceManager::the())
29 , m_popup(Popup::the())
30 , m_menu_bar(MenuBar::the())
31#ifdef TARGET_MOBILE
32 , m_control_bar(ControlBar::the())
33#endif // TARGET_MOBILE
34{
35 s_WinServer_Compositor_the = this;
36 invalidate(Screen::the().bounds());
37 LFoundation::EventLoop::the().add(LFoundation::Timer([] {
38 Compositor::the().refresh();
39 },
40 1000 / 60, LFoundation::Timer::Repeat));
41}
42
43void Compositor::copy_changes_to_second_buffer(const std::vector<LG::Rect>& areas)
44{
45 auto& screen = Screen::the();
46
47 for (int i = 0; i < areas.size(); i++) {
48 auto bounds = areas[i].intersection(screen.bounds());
49 auto* buf1_ptr = reinterpret_cast<uint32_t*>(&screen.display_bitmap()[bounds.min_y()][bounds.min_x()]);
50 auto* buf2_ptr = reinterpret_cast<uint32_t*>(&screen.write_bitmap()[bounds.min_y()][bounds.min_x()]);
51 for (int j = 0; j < bounds.height(); j++) {
52 LFoundation::fast_copy(buf2_ptr, buf1_ptr, bounds.width());
53 buf1_ptr += screen.width();
54 buf2_ptr += screen.width();
55 }
56 }
57}
58
59[[gnu::flatten]] void Compositor::refresh()
60{
61 if (m_invalidated_areas.empty()) {
62 return;
63 }
64
65 auto& screen = Screen::the();
66 auto& wm = WindowManager::the();
67 auto invalidated_areas = std::move(m_invalidated_areas);
68 LG::Context ctx(screen.write_bitmap());
69
70 auto is_window_area_invalidated = [&](const std::vector<LG::Rect>& areas, const LG::Rect& area) -> bool {
71 for (int i = 0; i < areas.size(); i++) {
72 if (area.intersects(areas[i])) {
73 return true;
74 }
75 }
76 return false;
77 };
78
79 auto draw_wallpaper_for_area = [&](const LG::Rect& area) {
80 ctx.add_clip(area);
81 ctx.draw({ 0, 0 }, m_resource_manager.background());
82 ctx.reset_clip();
83 };
84
85#ifdef TARGET_DESKTOP
86 auto draw_window = [&](Desktop::Window& window, const LG::Rect& area) {
87 ctx.add_clip(area);
88 ctx.add_clip(window.bounds());
89 window.frame().draw(ctx);
90 ctx.draw_rounded(window.content_bounds().origin(), window.content_bitmap(), window.corner_mask());
91 ctx.reset_clip();
92 };
93#elif TARGET_MOBILE
94 auto draw_window = [&](Mobile::Window& window, const LG::Rect& area) {
95 ctx.add_clip(area);
96 ctx.add_clip(window.bounds());
97 ctx.draw(window.content_bounds().origin(), window.content_bitmap());
98 ctx.reset_clip();
99 };
100#endif // TARGET_DESKTOP
101
102 auto& windows = wm.windows();
103 auto top_std_window = windows.rbegin();
104#ifdef TARGET_DESKTOP
105 for (int i = 0; i < invalidated_areas.size(); i++) {
106 draw_wallpaper_for_area(invalidated_areas[i]);
107 }
108#elif TARGET_MOBILE
109 // Standard windows could not be transparent in mobile view, thus
110 // no need to render everything behind the first standard window.
111 for (auto it = windows.rbegin(); it != windows.rend(); it++) {
112 top_std_window = it;
113 }
114
115 if (top_std_window == windows.rend() || (*top_std_window)->type() != WindowType::Standard) {
116 for (int i = 0; i < invalidated_areas.size(); i++) {
117 draw_wallpaper_for_area(invalidated_areas[i]);
118 }
119 }
120#endif // TARGET_DESKTOP
121
122 for (auto it = top_std_window; it != windows.rend(); it++) {
123 auto& window = *(*it);
124 if (window.visible() && is_window_area_invalidated(invalidated_areas, window.bounds())) {
125 for (int i = 0; i < invalidated_areas.size(); i++) {
126 draw_window(window, invalidated_areas[i]);
127 }
128 }
129 }
130
131 if (m_popup.visible()) {
132 for (int i = 0; i < invalidated_areas.size(); i++) {
133 ctx.add_clip(invalidated_areas[i]);
134 m_popup.draw(ctx);
135 ctx.reset_clip();
136 }
137 }
138
139 for (int i = 0; i < invalidated_areas.size(); i++) {
140 ctx.add_clip(invalidated_areas[i]);
141 m_menu_bar.draw(ctx);
142 ctx.reset_clip();
143 }
144
145#ifdef TARGET_MOBILE
146 for (int i = 0; i < invalidated_areas.size(); i++) {
147 ctx.add_clip(invalidated_areas[i]);
148 m_control_bar.draw(ctx);
149 ctx.reset_clip();
150 }
151#endif // TARGET_MOBILE
152
153 auto mouse_draw_position = m_cursor_manager.draw_position();
154 auto& current_mouse_bitmap = m_cursor_manager.current_cursor();
155 for (int i = 0; i < invalidated_areas.size(); i++) {
156 ctx.add_clip(invalidated_areas[i]);
157 ctx.draw(mouse_draw_position, current_mouse_bitmap);
158 ctx.reset_clip();
159 }
160
161 screen.swap_buffers();
162 copy_changes_to_second_buffer(invalidated_areas);
163}
164
165} // namespace WinServer