WSWindowManager.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. #include "WSWindowManager.h"
  2. #include "WSWindow.h"
  3. #include "WSScreen.h"
  4. #include "WSMessageLoop.h"
  5. #include "Process.h"
  6. #include "MemoryManager.h"
  7. #include <Kernel/ProcFileSystem.h>
  8. #include <SharedGraphics/Font.h>
  9. #include <SharedGraphics/Painter.h>
  10. #include <SharedGraphics/CharacterBitmap.h>
  11. #include <AK/StdLibExtras.h>
  12. //#define DEBUG_COUNTERS
  13. //#define DEBUG_WID_IN_TITLE_BAR
  14. static const int window_titlebar_height = 16;
  15. static inline Rect title_bar_rect(const Rect& window)
  16. {
  17. return {
  18. window.x() - 1,
  19. window.y() - window_titlebar_height,
  20. window.width() + 2,
  21. window_titlebar_height
  22. };
  23. }
  24. static inline Rect title_bar_text_rect(const Rect& window)
  25. {
  26. auto titlebar_rect = title_bar_rect(window);
  27. return {
  28. titlebar_rect.x() + 2,
  29. titlebar_rect.y(),
  30. titlebar_rect.width() - 4,
  31. titlebar_rect.height()
  32. };
  33. }
  34. static inline Rect border_window_rect(const Rect& window)
  35. {
  36. auto titlebar_rect = title_bar_rect(window);
  37. return { titlebar_rect.x() - 1,
  38. titlebar_rect.y() - 1,
  39. titlebar_rect.width() + 2,
  40. window_titlebar_height + window.height() + 3
  41. };
  42. }
  43. static inline Rect outer_window_rect(const Rect& window)
  44. {
  45. auto rect = border_window_rect(window);
  46. rect.inflate(2, 2);
  47. return rect;
  48. }
  49. static WSWindowManager* s_the;
  50. WSWindowManager& WSWindowManager::the()
  51. {
  52. if (!s_the)
  53. s_the = new WSWindowManager;
  54. return *s_the;
  55. }
  56. void WSWindowManager::initialize()
  57. {
  58. s_the = nullptr;
  59. }
  60. static const char* cursor_bitmap_inner_ascii = {
  61. " # "
  62. " ## "
  63. " ### "
  64. " #### "
  65. " ##### "
  66. " ###### "
  67. " ####### "
  68. " ######## "
  69. " ######### "
  70. " ########## "
  71. " ###### "
  72. " ## ## "
  73. " # ## "
  74. " ## "
  75. " ## "
  76. " ## "
  77. " "
  78. };
  79. static const char* cursor_bitmap_outer_ascii = {
  80. "## "
  81. "# # "
  82. "# # "
  83. "# # "
  84. "# # "
  85. "# # "
  86. "# # "
  87. "# # "
  88. "# # "
  89. "# # "
  90. "# #### "
  91. "# ## # "
  92. "# # # # "
  93. "## # # "
  94. " # # "
  95. " # # "
  96. " ## "
  97. };
  98. WSWindowManager::WSWindowManager()
  99. : m_screen(WSScreen::the())
  100. , m_screen_rect(m_screen.rect())
  101. {
  102. #ifndef DEBUG_COUNTERS
  103. (void)m_compose_count;
  104. (void)m_flush_count;
  105. #endif
  106. auto size = m_screen_rect.size();
  107. m_front_bitmap = GraphicsBitmap::create_wrapper(size, m_screen.scanline(0));
  108. auto* region = current->allocate_region(LinearAddress(), size.width() * size.height() * sizeof(RGBA32), "BackBitmap", true, true, true);
  109. m_back_bitmap = GraphicsBitmap::create_wrapper(m_screen_rect.size(), (RGBA32*)region->laddr().get());
  110. m_front_painter = make<Painter>(*m_front_bitmap);
  111. m_back_painter = make<Painter>(*m_back_bitmap);
  112. m_background_color = Color(50, 50, 50);
  113. m_active_window_border_color = Color(110, 34, 9);
  114. m_active_window_border_color2 = Color(244, 202, 158);
  115. m_active_window_title_color = Color::White;
  116. m_inactive_window_border_color = Color(128, 128, 128);
  117. m_inactive_window_border_color2 = Color(192, 192, 192);
  118. m_inactive_window_title_color = Color(213, 208, 199);
  119. m_dragging_window_border_color = Color(161, 50, 13);
  120. m_dragging_window_border_color2 = Color(250, 220, 187);
  121. m_dragging_window_title_color = Color::White;
  122. m_cursor_bitmap_inner = CharacterBitmap::create_from_ascii(cursor_bitmap_inner_ascii, 12, 17);
  123. m_cursor_bitmap_outer = CharacterBitmap::create_from_ascii(cursor_bitmap_outer_ascii, 12, 17);
  124. ProcFS::the().add_sys_bool("wm_flash_flush", &m_flash_flush);
  125. invalidate();
  126. compose();
  127. }
  128. WSWindowManager::~WSWindowManager()
  129. {
  130. }
  131. void WSWindowManager::paint_window_frame(WSWindow& window)
  132. {
  133. LOCKER(m_lock);
  134. //printf("[WM] paint_window_frame {%p}, rect: %d,%d %dx%d\n", &window, window.rect().x(), window.rect().y(), window.rect().width(), window.rect().height());
  135. auto titlebar_rect = title_bar_rect(window.rect());
  136. auto titlebar_inner_rect = title_bar_text_rect(window.rect());
  137. auto outer_rect = outer_window_rect(window.rect());
  138. auto border_rect = border_window_rect(window.rect());
  139. auto titlebar_title_rect = titlebar_inner_rect;
  140. titlebar_title_rect.set_width(Font::default_font().glyph_width() * window.title().length());
  141. Rect inner_border_rect {
  142. window.x() - 1,
  143. window.y() - 1,
  144. window.width() + 2,
  145. window.height() + 2
  146. };
  147. Color title_color;
  148. Color border_color;
  149. Color border_color2;
  150. Color middle_border_color;
  151. if (&window == m_drag_window.ptr()) {
  152. border_color = m_dragging_window_border_color;
  153. border_color2 = m_dragging_window_border_color2;
  154. title_color = m_dragging_window_title_color;
  155. middle_border_color = Color::White;
  156. } else if (&window == m_active_window.ptr()) {
  157. border_color = m_active_window_border_color;
  158. border_color2 = m_active_window_border_color2;
  159. title_color = m_active_window_title_color;
  160. middle_border_color = Color::MidGray;
  161. } else {
  162. border_color = m_inactive_window_border_color;
  163. border_color2 = m_inactive_window_border_color2;
  164. title_color = m_inactive_window_title_color;
  165. middle_border_color = Color::MidGray;
  166. }
  167. m_back_painter->fill_rect_with_gradient(titlebar_rect, border_color, border_color2);
  168. for (int i = 2; i <= titlebar_inner_rect.height() - 4; i += 2) {
  169. m_back_painter->draw_line({ titlebar_title_rect.right() + 4, titlebar_inner_rect.y() + i }, { titlebar_inner_rect.right(), titlebar_inner_rect.y() + i }, border_color);
  170. }
  171. m_back_painter->draw_rect(border_rect, middle_border_color);
  172. m_back_painter->draw_rect(outer_rect, border_color);
  173. m_back_painter->draw_rect(inner_border_rect, border_color);
  174. m_back_painter->draw_text(titlebar_title_rect, window.title(), Painter::TextAlignment::CenterLeft, title_color);
  175. #ifdef DEBUG_WID_IN_TITLE_BAR
  176. Color metadata_color(96, 96, 96);
  177. m_back_painter->draw_text(
  178. titlebar_inner_rect,
  179. String::format("%d:%d", window.pid(), window.window_id()),
  180. Painter::TextAlignment::CenterRight,
  181. metadata_color
  182. );
  183. #endif
  184. }
  185. void WSWindowManager::add_window(WSWindow& window)
  186. {
  187. LOCKER(m_lock);
  188. m_windows.set(&window);
  189. m_windows_in_order.append(&window);
  190. if (!active_window())
  191. set_active_window(&window);
  192. }
  193. void WSWindowManager::move_to_front(WSWindow& window)
  194. {
  195. LOCKER(m_lock);
  196. if (m_windows_in_order.tail() != &window)
  197. invalidate(window);
  198. m_windows_in_order.remove(&window);
  199. m_windows_in_order.append(&window);
  200. }
  201. void WSWindowManager::remove_window(WSWindow& window)
  202. {
  203. LOCKER(m_lock);
  204. if (!m_windows.contains(&window))
  205. return;
  206. invalidate(window);
  207. m_windows.remove(&window);
  208. m_windows_in_order.remove(&window);
  209. if (!active_window() && !m_windows.is_empty())
  210. set_active_window(*m_windows.begin());
  211. }
  212. void WSWindowManager::notify_title_changed(WSWindow& window)
  213. {
  214. printf("[WM] WSWindow{%p} title set to '%s'\n", &window, window.title().characters());
  215. invalidate(outer_window_rect(window.rect()));
  216. }
  217. void WSWindowManager::notify_rect_changed(WSWindow& window, const Rect& old_rect, const Rect& new_rect)
  218. {
  219. printf("[WM] WSWindow %p rect changed (%d,%d %dx%d) -> (%d,%d %dx%d)\n", &window, old_rect.x(), old_rect.y(), old_rect.width(), old_rect.height(), new_rect.x(), new_rect.y(), new_rect.width(), new_rect.height());
  220. ASSERT_INTERRUPTS_ENABLED();
  221. invalidate(outer_window_rect(old_rect));
  222. invalidate(outer_window_rect(new_rect));
  223. }
  224. void WSWindowManager::handle_titlebar_mouse_event(WSWindow& window, WSMouseEvent& event)
  225. {
  226. if (event.type() == WSMessage::MouseDown && event.button() == MouseButton::Left) {
  227. #ifdef DRAG_DEBUG
  228. printf("[WM] Begin dragging WSWindow{%p}\n", &window);
  229. #endif
  230. m_drag_window = window.make_weak_ptr();;
  231. m_drag_origin = event.position();
  232. m_drag_window_origin = window.position();
  233. m_drag_start_rect = outer_window_rect(window.rect());
  234. window.set_is_being_dragged(true);
  235. invalidate(window);
  236. return;
  237. }
  238. }
  239. void WSWindowManager::process_mouse_event(WSMouseEvent& event)
  240. {
  241. if (event.type() == WSMessage::MouseUp && event.button() == MouseButton::Left) {
  242. if (m_drag_window) {
  243. #ifdef DRAG_DEBUG
  244. printf("[WM] Finish dragging WSWindow{%p}\n", m_drag_window.ptr());
  245. #endif
  246. invalidate(*m_drag_window);
  247. m_drag_window->set_is_being_dragged(false);
  248. m_drag_end_rect = outer_window_rect(m_drag_window->rect());
  249. m_drag_window = nullptr;
  250. return;
  251. }
  252. }
  253. if (event.type() == WSMessage::MouseMove) {
  254. if (m_drag_window) {
  255. auto old_window_rect = m_drag_window->rect();
  256. Point pos = m_drag_window_origin;
  257. #ifdef DRAG_DEBUG
  258. dbgprintf("[WM] Dragging [origin: %d,%d] now: %d,%d\n", m_drag_origin.x(), m_drag_origin.y(), event.x(), event.y());
  259. #endif
  260. pos.move_by(event.x() - m_drag_origin.x(), event.y() - m_drag_origin.y());
  261. m_drag_window->set_position_without_repaint(pos);
  262. invalidate(outer_window_rect(old_window_rect));
  263. invalidate(outer_window_rect(m_drag_window->rect()));
  264. return;
  265. }
  266. }
  267. for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) {
  268. if (!window->global_cursor_tracking())
  269. continue;
  270. Point position { event.x() - window->rect().x(), event.y() - window->rect().y() };
  271. auto local_event = make<WSMouseEvent>(event.type(), position, event.buttons(), event.button());
  272. window->on_message(*local_event);
  273. }
  274. for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) {
  275. if (title_bar_rect(window->rect()).contains(event.position())) {
  276. if (event.type() == WSMessage::MouseDown) {
  277. move_to_front(*window);
  278. set_active_window(window);
  279. }
  280. handle_titlebar_mouse_event(*window, event);
  281. return;
  282. }
  283. if (window->rect().contains(event.position())) {
  284. if (event.type() == WSMessage::MouseDown) {
  285. move_to_front(*window);
  286. set_active_window(window);
  287. }
  288. // FIXME: Should we just alter the coordinates of the existing MouseEvent and pass it through?
  289. Point position { event.x() - window->rect().x(), event.y() - window->rect().y() };
  290. auto local_event = make<WSMouseEvent>(event.type(), position, event.buttons(), event.button());
  291. window->on_message(*local_event);
  292. return;
  293. }
  294. }
  295. }
  296. void WSWindowManager::compose()
  297. {
  298. LOCKER(m_lock);
  299. auto dirty_rects = move(m_dirty_rects);
  300. #ifdef DEBUG_COUNTERS
  301. dbgprintf("[WM] compose #%u (%u rects)\n", ++m_compose_count, dirty_rects.size());
  302. dbgprintf("kmalloc stats: alloc:%u free:%u eternal:%u\n", sum_alloc, sum_free, kmalloc_sum_eternal);
  303. #endif
  304. auto any_window_contains_rect = [this] (const Rect& r) {
  305. for (auto* window = m_windows_in_order.head(); window; window = window->next()) {
  306. if (outer_window_rect(window->rect()).contains(r))
  307. return true;
  308. }
  309. return false;
  310. };
  311. auto any_dirty_rect_intersects_window = [&dirty_rects] (const WSWindow& window) {
  312. auto window_rect = outer_window_rect(window.rect());
  313. for (auto& dirty_rect : dirty_rects) {
  314. if (dirty_rect.intersects(window_rect))
  315. return true;
  316. }
  317. return false;
  318. };
  319. for (auto& dirty_rect : dirty_rects) {
  320. if (any_window_contains_rect(dirty_rect)) {
  321. continue;
  322. }
  323. //dbgprintf("Repaint root %d,%d %dx%d\n", dirty_rect.x(), dirty_rect.y(), dirty_rect.width(), dirty_rect.height());
  324. m_back_painter->fill_rect(dirty_rect, m_background_color);
  325. }
  326. for (auto* window = m_windows_in_order.head(); window; window = window->next()) {
  327. WSWindowLocker locker(*window);
  328. RetainPtr<GraphicsBitmap> backing = window->backing();
  329. if (!backing)
  330. continue;
  331. if (!any_dirty_rect_intersects_window(*window))
  332. continue;
  333. for (auto& dirty_rect : dirty_rects) {
  334. m_back_painter->set_clip_rect(dirty_rect);
  335. paint_window_frame(*window);
  336. Rect dirty_rect_in_window_coordinates = Rect::intersection(dirty_rect, window->rect());
  337. if (dirty_rect_in_window_coordinates.is_empty())
  338. continue;
  339. dirty_rect_in_window_coordinates.set_x(dirty_rect_in_window_coordinates.x() - window->x());
  340. dirty_rect_in_window_coordinates.set_y(dirty_rect_in_window_coordinates.y() - window->y());
  341. auto dst = window->position();
  342. dst.move_by(dirty_rect_in_window_coordinates.location());
  343. m_back_painter->blit(dst, *backing, dirty_rect_in_window_coordinates);
  344. m_back_painter->clear_clip_rect();
  345. }
  346. m_back_painter->clear_clip_rect();
  347. }
  348. for (auto& r : dirty_rects)
  349. flush(r);
  350. draw_cursor();
  351. }
  352. void WSWindowManager::draw_cursor()
  353. {
  354. ASSERT_INTERRUPTS_ENABLED();
  355. LOCKER(m_lock);
  356. auto cursor_location = m_screen.cursor_location();
  357. Rect cursor_rect { cursor_location.x(), cursor_location.y(), (int)m_cursor_bitmap_inner->width(), (int)m_cursor_bitmap_inner->height() };
  358. flush(m_last_cursor_rect.united(cursor_rect));
  359. Color inner_color = Color::White;
  360. Color outer_color = Color::Black;
  361. if (m_screen.left_mouse_button_pressed())
  362. swap(inner_color, outer_color);
  363. m_front_painter->draw_bitmap(cursor_location, *m_cursor_bitmap_inner, inner_color);
  364. m_front_painter->draw_bitmap(cursor_location, *m_cursor_bitmap_outer, outer_color);
  365. m_last_cursor_rect = cursor_rect;
  366. }
  367. void WSWindowManager::on_message(WSMessage& message)
  368. {
  369. ASSERT_INTERRUPTS_ENABLED();
  370. LOCKER(m_lock);
  371. if (message.is_mouse_event())
  372. return process_mouse_event(static_cast<WSMouseEvent&>(message));
  373. if (message.is_key_event()) {
  374. // FIXME: This is a good place to hook key events globally. :)
  375. if (m_active_window)
  376. return m_active_window->on_message(message);
  377. return;
  378. }
  379. if (message.type() == WSMessage::WM_DeferredCompose) {
  380. m_pending_compose_event = false;
  381. compose();
  382. return;
  383. }
  384. }
  385. void WSWindowManager::set_active_window(WSWindow* window)
  386. {
  387. LOCKER(m_lock);
  388. if (window == m_active_window.ptr())
  389. return;
  390. if (auto* previously_active_window = m_active_window.ptr()) {
  391. WSMessageLoop::the().post_message(previously_active_window, make<WSMessage>(WSMessage::WindowDeactivated));
  392. invalidate(*previously_active_window);
  393. }
  394. m_active_window = window->make_weak_ptr();
  395. if (m_active_window) {
  396. WSMessageLoop::the().post_message(m_active_window.ptr(), make<WSMessage>(WSMessage::WindowActivated));
  397. invalidate(*m_active_window);
  398. }
  399. }
  400. void WSWindowManager::invalidate()
  401. {
  402. LOCKER(m_lock);
  403. m_dirty_rects.clear_with_capacity();
  404. m_dirty_rects.append(m_screen_rect);
  405. }
  406. void WSWindowManager::invalidate(const Rect& a_rect)
  407. {
  408. LOCKER(m_lock);
  409. auto rect = Rect::intersection(a_rect, m_screen_rect);
  410. if (rect.is_empty())
  411. return;
  412. for (auto& r : m_dirty_rects) {
  413. if (r.contains(rect))
  414. return;
  415. if (r.intersects(rect)) {
  416. // Unite with the existing dirty rect.
  417. // FIXME: It would be much nicer to compute the exact rects needing repaint.
  418. r = r.united(rect);
  419. return;
  420. }
  421. }
  422. m_dirty_rects.append(rect);
  423. if (!m_pending_compose_event) {
  424. ASSERT_INTERRUPTS_ENABLED();
  425. WSMessageLoop::the().post_message(this, make<WSMessage>(WSMessage::WM_DeferredCompose));
  426. m_pending_compose_event = true;
  427. }
  428. }
  429. void WSWindowManager::invalidate(const WSWindow& window)
  430. {
  431. ASSERT_INTERRUPTS_ENABLED();
  432. LOCKER(m_lock);
  433. invalidate(outer_window_rect(window.rect()));
  434. }
  435. void WSWindowManager::invalidate(const WSWindow& window, const Rect& rect)
  436. {
  437. if (rect.is_empty()) {
  438. invalidate(window);
  439. return;
  440. }
  441. ASSERT_INTERRUPTS_ENABLED();
  442. LOCKER(m_lock);
  443. auto outer_rect = outer_window_rect(window.rect());
  444. auto inner_rect = rect;
  445. inner_rect.move_by(window.position());
  446. // FIXME: This seems slightly wrong; the inner rect shouldn't intersect the border part of the outer rect.
  447. inner_rect.intersect(outer_rect);
  448. invalidate(inner_rect);
  449. }
  450. void WSWindowManager::flush(const Rect& a_rect)
  451. {
  452. auto rect = Rect::intersection(a_rect, m_screen_rect);
  453. #ifdef DEBUG_COUNTERS
  454. dbgprintf("[WM] flush #%u (%d,%d %dx%d)\n", ++m_flush_count, rect.x(), rect.y(), rect.width(), rect.height());
  455. #endif
  456. RGBA32* front_ptr = m_front_bitmap->scanline(rect.y()) + rect.x();
  457. const RGBA32* back_ptr = m_back_bitmap->scanline(rect.y()) + rect.x();
  458. size_t pitch = m_back_bitmap->pitch();
  459. if (m_flash_flush)
  460. m_front_painter->fill_rect(rect, Color::Yellow);
  461. for (int y = 0; y < rect.height(); ++y) {
  462. fast_dword_copy(front_ptr, back_ptr, rect.width());
  463. front_ptr = (RGBA32*)((byte*)front_ptr + pitch);
  464. back_ptr = (const RGBA32*)((const byte*)back_ptr + pitch);
  465. }
  466. }