WSWindowManager.cpp 33 KB

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