WSWindowManager.cpp 36 KB

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