WSWindowManager.cpp 30 KB

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