WSWindowManager.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  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/ProcFS.h>
  8. #include <SharedGraphics/Font.h>
  9. #include <SharedGraphics/Painter.h>
  10. #include <SharedGraphics/CharacterBitmap.h>
  11. #include <AK/StdLibExtras.h>
  12. #include <Kernel/BochsVGADevice.h>
  13. #include "WSMenu.h"
  14. #include "WSMenuBar.h"
  15. #include "WSMenuItem.h"
  16. //#define DEBUG_COUNTERS
  17. //#define DEBUG_WID_IN_TITLE_BAR
  18. static const int window_titlebar_height = 16;
  19. static inline Rect title_bar_rect(const Rect& window)
  20. {
  21. return {
  22. window.x() - 1,
  23. window.y() - window_titlebar_height,
  24. window.width() + 2,
  25. window_titlebar_height
  26. };
  27. }
  28. static inline Rect title_bar_text_rect(const Rect& window)
  29. {
  30. auto titlebar_rect = title_bar_rect(window);
  31. return {
  32. titlebar_rect.x() + 2,
  33. titlebar_rect.y(),
  34. titlebar_rect.width() - 4,
  35. titlebar_rect.height()
  36. };
  37. }
  38. static inline Rect close_button_rect_for_window(const Rect& window_rect)
  39. {
  40. auto titlebar_inner_rect = title_bar_text_rect(window_rect);
  41. int close_button_margin = 1;
  42. int close_button_size = titlebar_inner_rect.height() - close_button_margin * 2;
  43. return Rect {
  44. titlebar_inner_rect.right() - close_button_size + 1,
  45. titlebar_inner_rect.top() + close_button_margin,
  46. close_button_size,
  47. close_button_size - 1
  48. };
  49. }
  50. static inline Rect border_window_rect(const Rect& window)
  51. {
  52. auto titlebar_rect = title_bar_rect(window);
  53. return { titlebar_rect.x() - 1,
  54. titlebar_rect.y() - 1,
  55. titlebar_rect.width() + 2,
  56. window_titlebar_height + window.height() + 3
  57. };
  58. }
  59. static inline Rect outer_window_rect(const Rect& window)
  60. {
  61. auto rect = border_window_rect(window);
  62. rect.inflate(2, 2);
  63. return rect;
  64. }
  65. static WSWindowManager* s_the;
  66. WSWindowManager& WSWindowManager::the()
  67. {
  68. if (!s_the)
  69. s_the = new WSWindowManager;
  70. return *s_the;
  71. }
  72. static const char* cursor_bitmap_inner_ascii = {
  73. " # "
  74. " ## "
  75. " ### "
  76. " #### "
  77. " ##### "
  78. " ###### "
  79. " ####### "
  80. " ######## "
  81. " ######### "
  82. " ########## "
  83. " ###### "
  84. " ## ## "
  85. " # ## "
  86. " ## "
  87. " ## "
  88. " ## "
  89. " "
  90. };
  91. static const char* cursor_bitmap_outer_ascii = {
  92. "## "
  93. "# # "
  94. "# # "
  95. "# # "
  96. "# # "
  97. "# # "
  98. "# # "
  99. "# # "
  100. "# # "
  101. "# # "
  102. "# #### "
  103. "# ## # "
  104. "# # # # "
  105. "## # # "
  106. " # # "
  107. " # # "
  108. " ## "
  109. };
  110. void WSWindowManager::flip_buffers()
  111. {
  112. swap(m_front_bitmap, m_back_bitmap);
  113. swap(m_front_painter, m_back_painter);
  114. if (m_buffers_are_flipped)
  115. BochsVGADevice::the().set_y_offset(0);
  116. else
  117. BochsVGADevice::the().set_y_offset(m_screen_rect.height());
  118. m_buffers_are_flipped = !m_buffers_are_flipped;
  119. }
  120. WSWindowManager::WSWindowManager()
  121. : m_screen(WSScreen::the())
  122. , m_screen_rect(m_screen.rect())
  123. , m_lock("WSWindowManager")
  124. , m_flash_flush(false)
  125. {
  126. #ifndef DEBUG_COUNTERS
  127. (void)m_compose_count;
  128. (void)m_flush_count;
  129. #endif
  130. auto size = m_screen_rect.size();
  131. m_front_bitmap = GraphicsBitmap::create_wrapper(size, m_screen.scanline(0));
  132. m_back_bitmap = GraphicsBitmap::create_wrapper(size, m_screen.scanline(size.height()));
  133. m_front_painter = make<Painter>(*m_front_bitmap);
  134. m_back_painter = make<Painter>(*m_back_bitmap);
  135. m_font = Font::default_font();
  136. m_front_painter->set_font(font());
  137. m_back_painter->set_font(font());
  138. m_background_color = Color(50, 50, 50);
  139. m_active_window_border_color = Color(110, 34, 9);
  140. m_active_window_border_color2 = Color(244, 202, 158);
  141. m_active_window_title_color = Color::White;
  142. m_inactive_window_border_color = Color(128, 128, 128);
  143. m_inactive_window_border_color2 = Color(192, 192, 192);
  144. m_inactive_window_title_color = Color(213, 208, 199);
  145. m_dragging_window_border_color = Color(161, 50, 13);
  146. m_dragging_window_border_color2 = Color(250, 220, 187);
  147. m_dragging_window_title_color = Color::White;
  148. m_cursor_bitmap_inner = CharacterBitmap::create_from_ascii(cursor_bitmap_inner_ascii, 12, 17);
  149. m_cursor_bitmap_outer = CharacterBitmap::create_from_ascii(cursor_bitmap_outer_ascii, 12, 17);
  150. {
  151. LOCKER(m_wallpaper_path.lock());
  152. m_wallpaper_path.resource() = "/res/wallpapers/cool.rgb";
  153. m_wallpaper = GraphicsBitmap::load_from_file(m_wallpaper_path.resource(), m_screen_rect.size());
  154. }
  155. ProcFS::the().add_sys_bool("wm_flash_flush", m_flash_flush);
  156. ProcFS::the().add_sys_string("wm_wallpaper", m_wallpaper_path, [this] {
  157. LOCKER(m_wallpaper_path.lock());
  158. m_wallpaper = GraphicsBitmap::load_from_file(m_wallpaper_path.resource(), m_screen_rect.size());
  159. invalidate(m_screen_rect);
  160. });
  161. {
  162. auto menubar = make<WSMenuBar>();
  163. {
  164. byte system_menu_name[] = { 0xfc, 0 };
  165. auto menu = make<WSMenu>(String((const char*)system_menu_name));
  166. menu->add_item(make<WSMenuItem>(0, "Launch Terminal"));
  167. menu->add_item(make<WSMenuItem>(WSMenuItem::Separator));
  168. menu->add_item(make<WSMenuItem>(1, "Hello again"));
  169. menu->add_item(make<WSMenuItem>(2, "To all my friends"));
  170. menu->add_item(make<WSMenuItem>(3, "Together we can play some rock&roll"));
  171. menu->add_item(make<WSMenuItem>(WSMenuItem::Separator));
  172. menu->add_item(make<WSMenuItem>(4, "About..."));
  173. menu->on_item_activation = [] (WSMenuItem& item) {
  174. kprintf("WSMenu 1 item activated: '%s'\n", item.text().characters());
  175. };
  176. menubar->add_menu(move(menu));
  177. }
  178. {
  179. auto menu = make<WSMenu>("Application");
  180. menu->add_item(make<WSMenuItem>(5, "Foo."));
  181. menu->add_item(make<WSMenuItem>(6, "Bar?"));
  182. menu->add_item(make<WSMenuItem>(7, "Baz!"));
  183. menu->on_item_activation = [] (WSMenuItem& item) {
  184. kprintf("WSMenu 2 item activated: '%s'\n", item.text().characters());
  185. };
  186. menubar->add_menu(move(menu));
  187. }
  188. set_current_menubar(menubar.ptr());
  189. m_menubars.set(1, move(menubar));
  190. }
  191. invalidate();
  192. compose();
  193. }
  194. WSWindowManager::~WSWindowManager()
  195. {
  196. }
  197. void WSWindowManager::set_current_menubar(WSMenuBar* menubar)
  198. {
  199. if (m_current_menubar == menubar)
  200. return;
  201. m_current_menubar = menubar;
  202. if (!m_current_menubar)
  203. return;
  204. int menu_margin = 16;
  205. Point next_menu_location { menu_margin / 2, 3 };
  206. m_current_menubar->for_each_menu([&] (WSMenu& menu) {
  207. int text_width = font().width(menu.name());
  208. menu.set_rect_in_menubar({ next_menu_location.x() - menu_margin / 2, 0, text_width + menu_margin, menubar_rect().height() - 1 });
  209. menu.set_text_rect_in_menubar({ next_menu_location, { text_width, font().glyph_height() } });
  210. next_menu_location.move_by(menu.rect_in_menubar().width(), 0);
  211. return true;
  212. });
  213. }
  214. static const char* s_close_button_bitmap_data = {
  215. "## ##"
  216. "### ###"
  217. " ###### "
  218. " #### "
  219. " ## "
  220. " #### "
  221. " ###### "
  222. "### ###"
  223. "## ##"
  224. };
  225. static CharacterBitmap* s_close_button_bitmap;
  226. static const int s_close_button_bitmap_width = 8;
  227. static const int s_close_button_bitmap_height = 9;
  228. void WSWindowManager::paint_window_frame(WSWindow& window)
  229. {
  230. LOCKER(m_lock);
  231. //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());
  232. if (window.is_menu()) {
  233. m_back_painter->draw_rect(window.rect().inflated(2, 2), Color::LightGray);
  234. return;
  235. }
  236. auto titlebar_rect = title_bar_rect(window.rect());
  237. auto titlebar_inner_rect = title_bar_text_rect(window.rect());
  238. auto outer_rect = outer_window_rect(window.rect());
  239. auto border_rect = border_window_rect(window.rect());
  240. auto close_button_rect = close_button_rect_for_window(window.rect());
  241. auto titlebar_title_rect = titlebar_inner_rect;
  242. titlebar_title_rect.set_width(font().glyph_width() * window.title().length());
  243. Rect inner_border_rect {
  244. window.x() - 1,
  245. window.y() - 1,
  246. window.width() + 2,
  247. window.height() + 2
  248. };
  249. Color title_color;
  250. Color border_color;
  251. Color border_color2;
  252. Color middle_border_color;
  253. if (&window == m_drag_window.ptr()) {
  254. border_color = m_dragging_window_border_color;
  255. border_color2 = m_dragging_window_border_color2;
  256. title_color = m_dragging_window_title_color;
  257. middle_border_color = Color::White;
  258. } else if (&window == m_active_window.ptr()) {
  259. border_color = m_active_window_border_color;
  260. border_color2 = m_active_window_border_color2;
  261. title_color = m_active_window_title_color;
  262. middle_border_color = Color::MidGray;
  263. } else {
  264. border_color = m_inactive_window_border_color;
  265. border_color2 = m_inactive_window_border_color2;
  266. title_color = m_inactive_window_title_color;
  267. middle_border_color = Color::MidGray;
  268. }
  269. m_back_painter->fill_rect_with_gradient(titlebar_rect, border_color, border_color2);
  270. for (int i = 2; i <= titlebar_inner_rect.height() - 4; i += 2) {
  271. m_back_painter->draw_line({ titlebar_title_rect.right() + 4, titlebar_inner_rect.y() + i }, { close_button_rect.left() - 3, titlebar_inner_rect.y() + i }, border_color);
  272. }
  273. m_back_painter->draw_rect(border_rect, middle_border_color);
  274. m_back_painter->draw_rect(outer_rect, border_color);
  275. m_back_painter->draw_rect(inner_border_rect, border_color);
  276. m_back_painter->draw_text(titlebar_title_rect, window.title(), TextAlignment::CenterLeft, title_color);
  277. if (!s_close_button_bitmap)
  278. s_close_button_bitmap = CharacterBitmap::create_from_ascii(s_close_button_bitmap_data, s_close_button_bitmap_width, s_close_button_bitmap_height).leak_ref();
  279. m_back_painter->fill_rect_with_gradient(close_button_rect.shrunken(2, 2), Color::LightGray, Color::White);
  280. m_back_painter->draw_rect(close_button_rect, Color::DarkGray, true);
  281. auto x_location = close_button_rect.center();
  282. x_location.move_by(-(s_close_button_bitmap_width / 2), -(s_close_button_bitmap_height / 2));
  283. m_back_painter->draw_bitmap(x_location, *s_close_button_bitmap, Color::Black);
  284. #ifdef DEBUG_WID_IN_TITLE_BAR
  285. Color metadata_color(96, 96, 96);
  286. m_back_painter->draw_text(
  287. titlebar_inner_rect,
  288. String::format("%d:%d", window.pid(), window.window_id()),
  289. TextAlignment::CenterRight,
  290. metadata_color
  291. );
  292. #endif
  293. }
  294. void WSWindowManager::add_window(WSWindow& window)
  295. {
  296. LOCKER(m_lock);
  297. m_windows.set(&window);
  298. m_windows_in_order.append(&window);
  299. if (!active_window())
  300. set_active_window(&window);
  301. }
  302. void WSWindowManager::move_to_front(WSWindow& window)
  303. {
  304. LOCKER(m_lock);
  305. if (m_windows_in_order.tail() != &window)
  306. invalidate(window);
  307. m_windows_in_order.remove(&window);
  308. m_windows_in_order.append(&window);
  309. }
  310. void WSWindowManager::remove_window(WSWindow& window)
  311. {
  312. LOCKER(m_lock);
  313. if (!m_windows.contains(&window))
  314. return;
  315. invalidate(window);
  316. m_windows.remove(&window);
  317. m_windows_in_order.remove(&window);
  318. if (!active_window() && !m_windows.is_empty())
  319. set_active_window(*m_windows.begin());
  320. }
  321. void WSWindowManager::notify_title_changed(WSWindow& window)
  322. {
  323. printf("[WM] WSWindow{%p} title set to '%s'\n", &window, window.title().characters());
  324. invalidate(outer_window_rect(window.rect()));
  325. }
  326. void WSWindowManager::notify_rect_changed(WSWindow& window, const Rect& old_rect, const Rect& new_rect)
  327. {
  328. 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());
  329. ASSERT_INTERRUPTS_ENABLED();
  330. invalidate(outer_window_rect(old_rect));
  331. invalidate(outer_window_rect(new_rect));
  332. }
  333. void WSWindowManager::handle_menu_mouse_event(WSMenu& menu, WSMouseEvent& event)
  334. {
  335. bool should_open_menu = (event.type() == WSMouseEvent::MouseMove && event.buttons() & (unsigned)MouseButton::Left)
  336. || (event.type() == WSMouseEvent::MouseDown && event.button() == MouseButton::Left);
  337. if (should_open_menu) {
  338. if (m_current_menu == &menu)
  339. return;
  340. close_current_menu();
  341. auto& menu_window = menu.ensure_menu_window();
  342. menu_window.move_to({ menu.rect_in_menubar().x(), menu.rect_in_menubar().bottom() });
  343. menu_window.set_visible(true);
  344. m_current_menu = &menu;
  345. return;
  346. }
  347. if (event.type() == WSMouseEvent::MouseUp && event.button() == MouseButton::Left) {
  348. close_current_menu();
  349. return;
  350. }
  351. }
  352. void WSWindowManager::close_current_menu()
  353. {
  354. if (m_current_menu && m_current_menu->menu_window())
  355. m_current_menu->menu_window()->set_visible(false);
  356. m_current_menu = nullptr;
  357. }
  358. void WSWindowManager::handle_menubar_mouse_event(WSMenuBar& menu, WSMouseEvent& event)
  359. {
  360. m_current_menubar->for_each_menu([&] (WSMenu& menu) {
  361. if (menu.rect_in_menubar().contains(event.position())) {
  362. handle_menu_mouse_event(menu, event);
  363. return false;
  364. }
  365. return true;
  366. });
  367. }
  368. void WSWindowManager::handle_titlebar_mouse_event(WSWindow& window, WSMouseEvent& event)
  369. {
  370. if (event.type() == WSMessage::MouseDown && event.button() == MouseButton::Left) {
  371. #ifdef DRAG_DEBUG
  372. printf("[WM] Begin dragging WSWindow{%p}\n", &window);
  373. #endif
  374. m_drag_window = window.make_weak_ptr();;
  375. m_drag_origin = event.position();
  376. m_drag_window_origin = window.position();
  377. m_drag_start_rect = outer_window_rect(window.rect());
  378. window.set_is_being_dragged(true);
  379. invalidate(window);
  380. return;
  381. }
  382. }
  383. void WSWindowManager::handle_close_button_mouse_event(WSWindow& window, WSMouseEvent& event)
  384. {
  385. if (event.type() == WSMessage::MouseDown && event.button() == MouseButton::Left) {
  386. WSMessage message(WSMessage::WindowCloseRequest);
  387. window.on_message(message);
  388. return;
  389. }
  390. }
  391. void WSWindowManager::process_mouse_event(WSMouseEvent& event)
  392. {
  393. if (event.type() == WSMessage::MouseUp && event.button() == MouseButton::Left) {
  394. if (m_drag_window) {
  395. #ifdef DRAG_DEBUG
  396. printf("[WM] Finish dragging WSWindow{%p}\n", m_drag_window.ptr());
  397. #endif
  398. invalidate(*m_drag_window);
  399. m_drag_window->set_is_being_dragged(false);
  400. m_drag_end_rect = outer_window_rect(m_drag_window->rect());
  401. m_drag_window = nullptr;
  402. return;
  403. }
  404. }
  405. if (event.type() == WSMessage::MouseMove) {
  406. if (m_drag_window) {
  407. auto old_window_rect = m_drag_window->rect();
  408. Point pos = m_drag_window_origin;
  409. #ifdef DRAG_DEBUG
  410. dbgprintf("[WM] Dragging [origin: %d,%d] now: %d,%d\n", m_drag_origin.x(), m_drag_origin.y(), event.x(), event.y());
  411. #endif
  412. pos.move_by(event.x() - m_drag_origin.x(), event.y() - m_drag_origin.y());
  413. m_drag_window->set_position_without_repaint(pos);
  414. invalidate(outer_window_rect(old_window_rect));
  415. invalidate(outer_window_rect(m_drag_window->rect()));
  416. return;
  417. }
  418. }
  419. for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) {
  420. if (!window->global_cursor_tracking())
  421. continue;
  422. ASSERT(window->is_visible()); // Maybe this should be supported? Idk. Let's catch it and think about it later.
  423. Point position { event.x() - window->rect().x(), event.y() - window->rect().y() };
  424. auto local_event = make<WSMouseEvent>(event.type(), position, event.buttons(), event.button());
  425. window->on_message(*local_event);
  426. }
  427. if (m_current_menubar && menubar_rect().contains(event.position())) {
  428. handle_menubar_mouse_event(*m_current_menubar, event);
  429. return;
  430. }
  431. if (m_current_menu && m_current_menu->hovered_item() && !m_current_menu->menu_window()->rect().contains(event.position())) {
  432. m_current_menu->clear_hovered_item();
  433. }
  434. // FIXME: Figure out an automatic menu dismissal logic that feels right.
  435. #if 0
  436. if (m_current_menu && event.type() == WSMouseEvent::MouseUp && event.button() == MouseButton::Left)
  437. close_current_menu();
  438. #endif
  439. for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) {
  440. if (!window->is_visible())
  441. continue;
  442. if (title_bar_rect(window->rect()).contains(event.position())) {
  443. if (event.type() == WSMessage::MouseDown) {
  444. move_to_front(*window);
  445. set_active_window(window);
  446. }
  447. if (close_button_rect_for_window(window->rect()).contains(event.position())) {
  448. handle_close_button_mouse_event(*window, event);
  449. return;
  450. }
  451. handle_titlebar_mouse_event(*window, event);
  452. return;
  453. }
  454. if (window->rect().contains(event.position())) {
  455. if (event.type() == WSMessage::MouseDown) {
  456. move_to_front(*window);
  457. set_active_window(window);
  458. }
  459. // FIXME: Should we just alter the coordinates of the existing MouseEvent and pass it through?
  460. Point position { event.x() - window->rect().x(), event.y() - window->rect().y() };
  461. auto local_event = make<WSMouseEvent>(event.type(), position, event.buttons(), event.button());
  462. window->on_message(*local_event);
  463. return;
  464. }
  465. }
  466. }
  467. void WSWindowManager::compose()
  468. {
  469. LOCKER(m_lock);
  470. auto dirty_rects = move(m_dirty_rects);
  471. auto cursor_location = m_screen.cursor_location();
  472. dirty_rects.append(m_last_cursor_rect);
  473. dirty_rects.append({ cursor_location.x(), cursor_location.y(), (int)m_cursor_bitmap_inner->width(), (int)m_cursor_bitmap_inner->height() });
  474. #ifdef DEBUG_COUNTERS
  475. dbgprintf("[WM] compose #%u (%u rects)\n", ++m_compose_count, dirty_rects.size());
  476. dbgprintf("kmalloc stats: alloc:%u free:%u eternal:%u\n", sum_alloc, sum_free, kmalloc_sum_eternal);
  477. #endif
  478. auto any_window_contains_rect = [this] (const Rect& r) {
  479. for (auto* window = m_windows_in_order.head(); window; window = window->next()) {
  480. if (!window->is_visible())
  481. continue;
  482. if (outer_window_rect(window->rect()).contains(r))
  483. return true;
  484. }
  485. return false;
  486. };
  487. auto any_dirty_rect_intersects_window = [&dirty_rects] (const WSWindow& window) {
  488. auto window_rect = outer_window_rect(window.rect());
  489. for (auto& dirty_rect : dirty_rects) {
  490. if (dirty_rect.intersects(window_rect))
  491. return true;
  492. }
  493. return false;
  494. };
  495. for (auto& dirty_rect : dirty_rects) {
  496. if (any_window_contains_rect(dirty_rect)) {
  497. continue;
  498. }
  499. LOCKER(m_wallpaper_path.lock());
  500. if (!m_wallpaper)
  501. m_back_painter->fill_rect(dirty_rect, m_background_color);
  502. else
  503. m_back_painter->blit(dirty_rect.location(), *m_wallpaper, dirty_rect);
  504. }
  505. for (auto* window = m_windows_in_order.head(); window; window = window->next()) {
  506. if (!window->is_visible())
  507. continue;
  508. WSWindowLocker locker(*window);
  509. RetainPtr<GraphicsBitmap> backing = window->backing();
  510. if (!backing)
  511. continue;
  512. if (!any_dirty_rect_intersects_window(*window))
  513. continue;
  514. for (auto& dirty_rect : dirty_rects) {
  515. m_back_painter->set_clip_rect(dirty_rect);
  516. paint_window_frame(*window);
  517. Rect dirty_rect_in_window_coordinates = Rect::intersection(dirty_rect, window->rect());
  518. if (dirty_rect_in_window_coordinates.is_empty())
  519. continue;
  520. dirty_rect_in_window_coordinates.set_x(dirty_rect_in_window_coordinates.x() - window->x());
  521. dirty_rect_in_window_coordinates.set_y(dirty_rect_in_window_coordinates.y() - window->y());
  522. auto dst = window->position();
  523. dst.move_by(dirty_rect_in_window_coordinates.location());
  524. m_back_painter->blit(dst, *backing, dirty_rect_in_window_coordinates);
  525. m_back_painter->clear_clip_rect();
  526. }
  527. m_back_painter->clear_clip_rect();
  528. }
  529. draw_menubar();
  530. draw_cursor();
  531. if (m_flash_flush.lock_and_copy()) {
  532. for (auto& rect : dirty_rects)
  533. m_front_painter->fill_rect(rect, Color::Yellow);
  534. }
  535. flip_buffers();
  536. for (auto& r : dirty_rects)
  537. flush(r);
  538. }
  539. void WSWindowManager::invalidate_cursor()
  540. {
  541. LOCKER(m_lock);
  542. auto cursor_location = m_screen.cursor_location();
  543. Rect cursor_rect { cursor_location.x(), cursor_location.y(), (int)m_cursor_bitmap_inner->width(), (int)m_cursor_bitmap_inner->height() };
  544. invalidate(cursor_rect);
  545. }
  546. Rect WSWindowManager::menubar_rect() const
  547. {
  548. return { 0, 0, m_screen_rect.width(), 16 };
  549. }
  550. void WSWindowManager::draw_menubar()
  551. {
  552. if (!m_current_menubar)
  553. return;
  554. m_back_painter->fill_rect(menubar_rect(), Color::LightGray);
  555. m_back_painter->draw_line({ 0, menubar_rect().bottom() }, { menubar_rect().right(), menubar_rect().bottom() }, Color::White);
  556. m_current_menubar->for_each_menu([&] (WSMenu& menu) {
  557. Color text_color = Color::Black;
  558. if (&menu == m_current_menu) {
  559. m_back_painter->fill_rect(menu.rect_in_menubar(), Color(0, 0, 104));
  560. text_color = Color::White;
  561. }
  562. m_back_painter->draw_text(menu.text_rect_in_menubar(), menu.name(), TextAlignment::CenterLeft, text_color);
  563. return true;
  564. });
  565. }
  566. void WSWindowManager::draw_cursor()
  567. {
  568. ASSERT_INTERRUPTS_ENABLED();
  569. LOCKER(m_lock);
  570. auto cursor_location = m_screen.cursor_location();
  571. Rect cursor_rect { cursor_location.x(), cursor_location.y(), (int)m_cursor_bitmap_inner->width(), (int)m_cursor_bitmap_inner->height() };
  572. Color inner_color = Color::White;
  573. Color outer_color = Color::Black;
  574. if (m_screen.left_mouse_button_pressed())
  575. swap(inner_color, outer_color);
  576. m_back_painter->draw_bitmap(cursor_location, *m_cursor_bitmap_inner, inner_color);
  577. m_back_painter->draw_bitmap(cursor_location, *m_cursor_bitmap_outer, outer_color);
  578. m_last_cursor_rect = cursor_rect;
  579. }
  580. void WSWindowManager::on_message(WSMessage& message)
  581. {
  582. ASSERT_INTERRUPTS_ENABLED();
  583. LOCKER(m_lock);
  584. if (message.is_mouse_event())
  585. return process_mouse_event(static_cast<WSMouseEvent&>(message));
  586. if (message.is_key_event()) {
  587. // FIXME: This is a good place to hook key events globally. :)
  588. if (m_active_window)
  589. return m_active_window->on_message(message);
  590. return;
  591. }
  592. if (message.type() == WSMessage::WM_DeferredCompose) {
  593. m_pending_compose_event = false;
  594. compose();
  595. return;
  596. }
  597. }
  598. void WSWindowManager::set_active_window(WSWindow* window)
  599. {
  600. LOCKER(m_lock);
  601. if (window == m_active_window.ptr())
  602. return;
  603. if (auto* previously_active_window = m_active_window.ptr()) {
  604. WSMessageLoop::the().post_message(previously_active_window, make<WSMessage>(WSMessage::WindowDeactivated));
  605. invalidate(*previously_active_window);
  606. }
  607. m_active_window = window->make_weak_ptr();
  608. if (m_active_window) {
  609. WSMessageLoop::the().post_message(m_active_window.ptr(), make<WSMessage>(WSMessage::WindowActivated));
  610. invalidate(*m_active_window);
  611. }
  612. }
  613. void WSWindowManager::invalidate()
  614. {
  615. LOCKER(m_lock);
  616. m_dirty_rects.clear_with_capacity();
  617. m_dirty_rects.append(m_screen_rect);
  618. }
  619. void WSWindowManager::invalidate(const Rect& a_rect)
  620. {
  621. LOCKER(m_lock);
  622. auto rect = Rect::intersection(a_rect, m_screen_rect);
  623. if (rect.is_empty())
  624. return;
  625. for (auto& r : m_dirty_rects) {
  626. if (r.contains(rect))
  627. return;
  628. if (r.intersects(rect)) {
  629. // Unite with the existing dirty rect.
  630. // FIXME: It would be much nicer to compute the exact rects needing repaint.
  631. r = r.united(rect);
  632. return;
  633. }
  634. }
  635. m_dirty_rects.append(rect);
  636. if (!m_pending_compose_event) {
  637. ASSERT_INTERRUPTS_ENABLED();
  638. WSMessageLoop::the().post_message(this, make<WSMessage>(WSMessage::WM_DeferredCompose));
  639. m_pending_compose_event = true;
  640. }
  641. }
  642. void WSWindowManager::invalidate(const WSWindow& window)
  643. {
  644. ASSERT_INTERRUPTS_ENABLED();
  645. LOCKER(m_lock);
  646. invalidate(outer_window_rect(window.rect()));
  647. }
  648. void WSWindowManager::invalidate(const WSWindow& window, const Rect& rect)
  649. {
  650. if (rect.is_empty()) {
  651. invalidate(window);
  652. return;
  653. }
  654. ASSERT_INTERRUPTS_ENABLED();
  655. LOCKER(m_lock);
  656. auto outer_rect = outer_window_rect(window.rect());
  657. auto inner_rect = rect;
  658. inner_rect.move_by(window.position());
  659. // FIXME: This seems slightly wrong; the inner rect shouldn't intersect the border part of the outer rect.
  660. inner_rect.intersect(outer_rect);
  661. invalidate(inner_rect);
  662. }
  663. void WSWindowManager::flush(const Rect& a_rect)
  664. {
  665. auto rect = Rect::intersection(a_rect, m_screen_rect);
  666. #ifdef DEBUG_COUNTERS
  667. dbgprintf("[WM] flush #%u (%d,%d %dx%d)\n", ++m_flush_count, rect.x(), rect.y(), rect.width(), rect.height());
  668. #endif
  669. RGBA32* front_ptr = m_front_bitmap->scanline(rect.y()) + rect.x();
  670. RGBA32* back_ptr = m_back_bitmap->scanline(rect.y()) + rect.x();
  671. size_t pitch = m_back_bitmap->pitch();
  672. for (int y = 0; y < rect.height(); ++y) {
  673. fast_dword_copy(back_ptr, front_ptr, rect.width());
  674. front_ptr = (RGBA32*)((byte*)front_ptr + pitch);
  675. back_ptr = (RGBA32*)((byte*)back_ptr + pitch);
  676. }
  677. }