WSWindowManager.cpp 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290
  1. #include "WSWindowManager.h"
  2. #include "WSCompositor.h"
  3. #include "WSEventLoop.h"
  4. #include "WSMenu.h"
  5. #include "WSMenuBar.h"
  6. #include "WSMenuItem.h"
  7. #include "WSScreen.h"
  8. #include "WSWindow.h"
  9. #include <AK/FileSystemPath.h>
  10. #include <AK/LogStream.h>
  11. #include <AK/QuickSort.h>
  12. #include <AK/StdLibExtras.h>
  13. #include <AK/Vector.h>
  14. #include <LibCore/CDirIterator.h>
  15. #include <LibCore/CTimer.h>
  16. #include <LibDraw/CharacterBitmap.h>
  17. #include <LibDraw/Font.h>
  18. #include <LibDraw/PNGLoader.h>
  19. #include <LibDraw/Painter.h>
  20. #include <LibDraw/StylePainter.h>
  21. #include <LibDraw/SystemTheme.h>
  22. #include <WindowServer/WSButton.h>
  23. #include <WindowServer/WSClientConnection.h>
  24. #include <WindowServer/WSCursor.h>
  25. #include <WindowServer/WindowClientEndpoint.h>
  26. #include <errno.h>
  27. #include <stdio.h>
  28. #include <time.h>
  29. #include <unistd.h>
  30. //#define DEBUG_COUNTERS
  31. //#define DEBUG_MENUS
  32. //#define RESIZE_DEBUG
  33. //#define MOVE_DEBUG
  34. //#define DOUBLECLICK_DEBUG
  35. static WSWindowManager* s_the;
  36. WSWindowManager& WSWindowManager::the()
  37. {
  38. ASSERT(s_the);
  39. return *s_the;
  40. }
  41. WSWindowManager::WSWindowManager(const Palette& palette)
  42. : m_palette(palette)
  43. {
  44. s_the = this;
  45. reload_config(false);
  46. HashTable<String> seen_app_categories;
  47. {
  48. CDirIterator dt("/res/apps", CDirIterator::SkipDots);
  49. while (dt.has_next()) {
  50. auto af_name = dt.next_path();
  51. auto af_path = String::format("/res/apps/%s", af_name.characters());
  52. auto af = CConfigFile::open(af_path);
  53. if (!af->has_key("App", "Name") || !af->has_key("App", "Executable"))
  54. continue;
  55. auto app_name = af->read_entry("App", "Name");
  56. auto app_executable = af->read_entry("App", "Executable");
  57. auto app_category = af->read_entry("App", "Category");
  58. auto app_icon_path = af->read_entry("Icons", "16x16");
  59. m_apps.append({ app_executable, app_name, app_icon_path, app_category });
  60. seen_app_categories.set(app_category);
  61. }
  62. }
  63. Vector<String> sorted_app_categories;
  64. for (auto& category : seen_app_categories)
  65. sorted_app_categories.append(category);
  66. quick_sort(sorted_app_categories.begin(), sorted_app_categories.end(), [](auto& a, auto& b) { return a < b; });
  67. u8 system_menu_name[] = { 0xc3, 0xb8, 0 };
  68. m_system_menu = WSMenu::construct(nullptr, -1, String((const char*)system_menu_name));
  69. // First we construct all the necessary app category submenus.
  70. for (const auto& category : sorted_app_categories) {
  71. if (m_app_category_menus.contains(category))
  72. continue;
  73. auto category_menu = WSMenu::construct(nullptr, 5000 + m_app_category_menus.size(), category);
  74. category_menu->on_item_activation = [this](auto& item) {
  75. if (item.identifier() >= 1 && item.identifier() <= 1u + m_apps.size() - 1) {
  76. if (fork() == 0) {
  77. const auto& bin = m_apps[item.identifier() - 1].executable;
  78. execl(bin.characters(), bin.characters(), nullptr);
  79. ASSERT_NOT_REACHED();
  80. }
  81. }
  82. };
  83. auto item = make<WSMenuItem>(*m_system_menu, -1, category);
  84. item->set_submenu_id(category_menu->menu_id());
  85. m_system_menu->add_item(move(item));
  86. m_app_category_menus.set(category, move(category_menu));
  87. }
  88. // Then we create and insert all the app menu items into the right place.
  89. int app_identifier = 1;
  90. for (const auto& app : m_apps) {
  91. auto parent_menu = m_app_category_menus.get(app.category).value_or(*m_system_menu);
  92. parent_menu->add_item(make<WSMenuItem>(*m_system_menu, app_identifier++, app.name, String(), true, false, false, load_png(app.icon_path)));
  93. }
  94. m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, WSMenuItem::Separator));
  95. m_themes_menu = WSMenu::construct(nullptr, 9000, "Themes");
  96. auto themes_menu_item = make<WSMenuItem>(*m_system_menu, 100, "Themes");
  97. themes_menu_item->set_submenu_id(m_themes_menu->menu_id());
  98. m_system_menu->add_item(move(themes_menu_item));
  99. {
  100. CDirIterator dt("/res/themes", CDirIterator::SkipDots);
  101. while (dt.has_next()) {
  102. auto theme_name = dt.next_path();
  103. auto theme_path = String::format("/res/themes/%s", theme_name.characters());
  104. m_themes.append({ FileSystemPath(theme_name).title(), theme_path });
  105. }
  106. quick_sort(m_themes.begin(), m_themes.end(), [](auto& a, auto& b) { return a.name < b.name; });
  107. }
  108. {
  109. int theme_identifier = 9000;
  110. for (auto& theme : m_themes) {
  111. m_themes_menu->add_item(make<WSMenuItem>(*m_themes_menu, theme_identifier++, theme.name));
  112. }
  113. }
  114. m_themes_menu->on_item_activation = [this](WSMenuItem& item) {
  115. auto& theme = m_themes[(int)item.identifier() - 9000];
  116. auto new_theme = load_system_theme(theme.path);
  117. ASSERT(new_theme);
  118. set_system_theme(*new_theme);
  119. m_palette = Palette::create_with_shared_buffer(*new_theme);
  120. HashTable<WSClientConnection*> notified_clients;
  121. for_each_window([&](WSWindow& window) {
  122. if (window.client()) {
  123. if (!notified_clients.contains(window.client())) {
  124. window.client()->post_message(WindowClient::UpdateSystemTheme(current_system_theme_buffer_id()));
  125. notified_clients.set(window.client());
  126. }
  127. }
  128. return IterationDecision::Continue;
  129. });
  130. ++m_theme_index;
  131. auto wm_config = CConfigFile::get_for_app("WindowManager");
  132. wm_config->write_entry("Theme", "Name", theme.name);
  133. wm_config->sync();
  134. invalidate();
  135. };
  136. m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, WSMenuItem::Separator));
  137. m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, 100, "Reload WM Config File"));
  138. m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, WSMenuItem::Separator));
  139. m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, 200, "About...", String(), true, false, false, load_png("/res/icons/16x16/ladybug.png")));
  140. m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, WSMenuItem::Separator));
  141. m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, 300, "Shutdown..."));
  142. m_system_menu->on_item_activation = [this](WSMenuItem& item) {
  143. if (item.identifier() >= 1 && item.identifier() <= 1u + m_apps.size() - 1) {
  144. if (fork() == 0) {
  145. const auto& bin = m_apps[item.identifier() - 1].executable;
  146. execl(bin.characters(), bin.characters(), nullptr);
  147. ASSERT_NOT_REACHED();
  148. }
  149. }
  150. switch (item.identifier()) {
  151. case 100:
  152. reload_config(true);
  153. break;
  154. case 200:
  155. if (fork() == 0) {
  156. execl("/bin/About", "/bin/About", nullptr);
  157. ASSERT_NOT_REACHED();
  158. }
  159. return;
  160. case 300:
  161. if (fork() == 0) {
  162. execl("/bin/SystemDialog", "/bin/SystemDialog", "--shutdown", nullptr);
  163. ASSERT_NOT_REACHED();
  164. }
  165. return;
  166. }
  167. #ifdef DEBUG_MENUS
  168. dbg() << "WSMenu 1 item activated: " << item.text();
  169. #endif
  170. };
  171. // NOTE: This ensures that the system menu has the correct dimensions.
  172. set_current_menubar(nullptr);
  173. m_menu_manager.setup();
  174. invalidate();
  175. WSCompositor::the().compose();
  176. }
  177. WSWindowManager::~WSWindowManager()
  178. {
  179. }
  180. NonnullRefPtr<WSCursor> WSWindowManager::get_cursor(const String& name, const Point& hotspot)
  181. {
  182. auto path = m_wm_config->read_entry("Cursor", name, "/res/cursors/arrow.png");
  183. auto gb = GraphicsBitmap::load_from_file(path);
  184. if (gb)
  185. return WSCursor::create(*gb, hotspot);
  186. return WSCursor::create(*GraphicsBitmap::load_from_file("/res/cursors/arrow.png"));
  187. }
  188. NonnullRefPtr<WSCursor> WSWindowManager::get_cursor(const String& name)
  189. {
  190. auto path = m_wm_config->read_entry("Cursor", name, "/res/cursors/arrow.png");
  191. auto gb = GraphicsBitmap::load_from_file(path);
  192. if (gb)
  193. return WSCursor::create(*gb);
  194. return WSCursor::create(*GraphicsBitmap::load_from_file("/res/cursors/arrow.png"));
  195. }
  196. void WSWindowManager::reload_config(bool set_screen)
  197. {
  198. m_wm_config = CConfigFile::get_for_app("WindowManager");
  199. m_double_click_speed = m_wm_config->read_num_entry("Input", "DoubleClickSpeed", 250);
  200. if (set_screen)
  201. set_resolution(m_wm_config->read_num_entry("Screen", "Width", 1920),
  202. m_wm_config->read_num_entry("Screen", "Height", 1080));
  203. m_arrow_cursor = get_cursor("Arrow", { 2, 2 });
  204. m_hand_cursor = get_cursor("Hand", { 8, 4 });
  205. m_resize_horizontally_cursor = get_cursor("ResizeH");
  206. m_resize_vertically_cursor = get_cursor("ResizeV");
  207. m_resize_diagonally_tlbr_cursor = get_cursor("ResizeDTLBR");
  208. m_resize_diagonally_bltr_cursor = get_cursor("ResizeDBLTR");
  209. m_i_beam_cursor = get_cursor("IBeam");
  210. m_disallowed_cursor = get_cursor("Disallowed");
  211. m_move_cursor = get_cursor("Move");
  212. m_drag_cursor = get_cursor("Drag");
  213. }
  214. const Font& WSWindowManager::font() const
  215. {
  216. return Font::default_font();
  217. }
  218. const Font& WSWindowManager::window_title_font() const
  219. {
  220. return Font::default_bold_font();
  221. }
  222. const Font& WSWindowManager::menu_font() const
  223. {
  224. return Font::default_font();
  225. }
  226. const Font& WSWindowManager::app_menu_font() const
  227. {
  228. return Font::default_bold_font();
  229. }
  230. void WSWindowManager::set_resolution(int width, int height)
  231. {
  232. WSCompositor::the().set_resolution(width, height);
  233. m_menu_manager.set_needs_window_resize();
  234. WSClientConnection::for_each_client([&](WSClientConnection& client) {
  235. client.notify_about_new_screen_rect(WSScreen::the().rect());
  236. });
  237. if (m_wm_config) {
  238. dbg() << "Saving resolution: " << Size(width, height) << " to config file at " << m_wm_config->file_name();
  239. m_wm_config->write_num_entry("Screen", "Width", width);
  240. m_wm_config->write_num_entry("Screen", "Height", height);
  241. m_wm_config->sync();
  242. }
  243. }
  244. void WSWindowManager::set_current_menubar(WSMenuBar* menubar)
  245. {
  246. if (menubar)
  247. m_current_menubar = menubar->make_weak_ptr();
  248. else
  249. m_current_menubar = nullptr;
  250. #ifdef DEBUG_MENUS
  251. dbg() << "[WM] Current menubar is now " << menubar;
  252. #endif
  253. Point next_menu_location { WSMenuManager::menubar_menu_margin() / 2, 0 };
  254. int index = 0;
  255. for_each_active_menubar_menu([&](WSMenu& menu) {
  256. int text_width = index == 1 ? Font::default_bold_font().width(menu.name()) : font().width(menu.name());
  257. menu.set_rect_in_menubar({ next_menu_location.x() - WSMenuManager::menubar_menu_margin() / 2, 0, text_width + WSMenuManager::menubar_menu_margin(), menubar_rect().height() - 1 });
  258. menu.set_text_rect_in_menubar({ next_menu_location, { text_width, menubar_rect().height() } });
  259. next_menu_location.move_by(menu.rect_in_menubar().width(), 0);
  260. ++index;
  261. return IterationDecision::Continue;
  262. });
  263. m_menu_manager.refresh();
  264. }
  265. void WSWindowManager::add_window(WSWindow& window)
  266. {
  267. m_windows_in_order.append(&window);
  268. if (window.is_fullscreen()) {
  269. CEventLoop::current().post_event(window, make<WSResizeEvent>(window.rect(), WSScreen::the().rect()));
  270. window.set_rect(WSScreen::the().rect());
  271. }
  272. set_active_window(&window);
  273. if (m_switcher.is_visible() && window.type() != WSWindowType::WindowSwitcher)
  274. m_switcher.refresh();
  275. if (window.listens_to_wm_events()) {
  276. for_each_window([&](WSWindow& other_window) {
  277. if (&window != &other_window) {
  278. tell_wm_listener_about_window(window, other_window);
  279. tell_wm_listener_about_window_icon(window, other_window);
  280. }
  281. return IterationDecision::Continue;
  282. });
  283. }
  284. tell_wm_listeners_window_state_changed(window);
  285. }
  286. void WSWindowManager::move_to_front_and_make_active(WSWindow& window)
  287. {
  288. if (window.is_blocked_by_modal_window())
  289. return;
  290. if (m_windows_in_order.tail() != &window)
  291. invalidate(window);
  292. m_windows_in_order.remove(&window);
  293. m_windows_in_order.append(&window);
  294. set_active_window(&window);
  295. }
  296. void WSWindowManager::remove_window(WSWindow& window)
  297. {
  298. invalidate(window);
  299. m_windows_in_order.remove(&window);
  300. if (window.is_active())
  301. pick_new_active_window();
  302. if (m_switcher.is_visible() && window.type() != WSWindowType::WindowSwitcher)
  303. m_switcher.refresh();
  304. for_each_window_listening_to_wm_events([&window](WSWindow& listener) {
  305. if (!(listener.wm_event_mask() & WSWMEventMask::WindowRemovals))
  306. return IterationDecision::Continue;
  307. if (window.client())
  308. CEventLoop::current().post_event(listener, make<WSWMWindowRemovedEvent>(window.client()->client_id(), window.window_id()));
  309. return IterationDecision::Continue;
  310. });
  311. }
  312. void WSWindowManager::tell_wm_listener_about_window(WSWindow& listener, WSWindow& window)
  313. {
  314. if (!(listener.wm_event_mask() & WSWMEventMask::WindowStateChanges))
  315. return;
  316. if (window.client())
  317. CEventLoop::current().post_event(listener, make<WSWMWindowStateChangedEvent>(window.client()->client_id(), window.window_id(), window.title(), window.rect(), window.is_active(), window.type(), window.is_minimized()));
  318. }
  319. void WSWindowManager::tell_wm_listener_about_window_rect(WSWindow& listener, WSWindow& window)
  320. {
  321. if (!(listener.wm_event_mask() & WSWMEventMask::WindowRectChanges))
  322. return;
  323. if (window.client())
  324. CEventLoop::current().post_event(listener, make<WSWMWindowRectChangedEvent>(window.client()->client_id(), window.window_id(), window.rect()));
  325. }
  326. void WSWindowManager::tell_wm_listener_about_window_icon(WSWindow& listener, WSWindow& window)
  327. {
  328. if (!(listener.wm_event_mask() & WSWMEventMask::WindowIconChanges))
  329. return;
  330. if (window.client() && window.icon().shared_buffer_id() != -1)
  331. CEventLoop::current().post_event(listener, make<WSWMWindowIconBitmapChangedEvent>(window.client()->client_id(), window.window_id(), window.icon().shared_buffer_id(), window.icon().size()));
  332. }
  333. void WSWindowManager::tell_wm_listeners_window_state_changed(WSWindow& window)
  334. {
  335. for_each_window_listening_to_wm_events([&](WSWindow& listener) {
  336. tell_wm_listener_about_window(listener, window);
  337. return IterationDecision::Continue;
  338. });
  339. }
  340. void WSWindowManager::tell_wm_listeners_window_icon_changed(WSWindow& window)
  341. {
  342. for_each_window_listening_to_wm_events([&](WSWindow& listener) {
  343. tell_wm_listener_about_window_icon(listener, window);
  344. return IterationDecision::Continue;
  345. });
  346. }
  347. void WSWindowManager::tell_wm_listeners_window_rect_changed(WSWindow& window)
  348. {
  349. for_each_window_listening_to_wm_events([&](WSWindow& listener) {
  350. tell_wm_listener_about_window_rect(listener, window);
  351. return IterationDecision::Continue;
  352. });
  353. }
  354. void WSWindowManager::notify_title_changed(WSWindow& window)
  355. {
  356. if (window.type() != WSWindowType::Normal)
  357. return;
  358. dbg() << "[WM] WSWindow{" << &window << "} title set to \"" << window.title() << '"';
  359. invalidate(window.frame().rect());
  360. if (m_switcher.is_visible())
  361. m_switcher.refresh();
  362. tell_wm_listeners_window_state_changed(window);
  363. }
  364. void WSWindowManager::notify_rect_changed(WSWindow& window, const Rect& old_rect, const Rect& new_rect)
  365. {
  366. UNUSED_PARAM(old_rect);
  367. UNUSED_PARAM(new_rect);
  368. #ifdef RESIZE_DEBUG
  369. dbg() << "[WM] WSWindow " << &window << " rect changed " << old_rect << " -> " << new_rect;
  370. #endif
  371. if (m_switcher.is_visible() && window.type() != WSWindowType::WindowSwitcher)
  372. m_switcher.refresh();
  373. recompute_occlusions();
  374. tell_wm_listeners_window_rect_changed(window);
  375. m_menu_manager.refresh();
  376. }
  377. void WSWindowManager::recompute_occlusions()
  378. {
  379. for_each_visible_window_from_back_to_front([&](WSWindow& window) {
  380. if (m_switcher.is_visible()) {
  381. window.set_occluded(false);
  382. } else {
  383. if (any_opaque_window_above_this_one_contains_rect(window, window.frame().rect()))
  384. window.set_occluded(true);
  385. else
  386. window.set_occluded(false);
  387. }
  388. return IterationDecision::Continue;
  389. });
  390. }
  391. void WSWindowManager::notify_opacity_changed(WSWindow&)
  392. {
  393. recompute_occlusions();
  394. }
  395. void WSWindowManager::notify_minimization_state_changed(WSWindow& window)
  396. {
  397. tell_wm_listeners_window_state_changed(window);
  398. if (window.client())
  399. window.client()->post_message(WindowClient::WindowStateChanged(window.window_id(), window.is_minimized(), window.is_occluded()));
  400. if (window.is_active() && window.is_minimized())
  401. pick_new_active_window();
  402. }
  403. void WSWindowManager::notify_occlusion_state_changed(WSWindow& window)
  404. {
  405. if (window.client())
  406. window.client()->post_message(WindowClient::WindowStateChanged(window.window_id(), window.is_minimized(), window.is_occluded()));
  407. }
  408. void WSWindowManager::pick_new_active_window()
  409. {
  410. for_each_visible_window_of_type_from_front_to_back(WSWindowType::Normal, [&](WSWindow& candidate) {
  411. set_active_window(&candidate);
  412. return IterationDecision::Break;
  413. });
  414. }
  415. void WSWindowManager::start_window_move(WSWindow& window, const WSMouseEvent& event)
  416. {
  417. #ifdef MOVE_DEBUG
  418. dbg() << "[WM] Begin moving WSWindow{" << &window << "}";
  419. #endif
  420. move_to_front_and_make_active(window);
  421. m_move_window = window.make_weak_ptr();
  422. m_move_origin = event.position();
  423. m_move_window_origin = window.position();
  424. invalidate(window);
  425. }
  426. void WSWindowManager::start_window_resize(WSWindow& window, const Point& position, MouseButton button)
  427. {
  428. move_to_front_and_make_active(window);
  429. constexpr ResizeDirection direction_for_hot_area[3][3] = {
  430. { ResizeDirection::UpLeft, ResizeDirection::Up, ResizeDirection::UpRight },
  431. { ResizeDirection::Left, ResizeDirection::None, ResizeDirection::Right },
  432. { ResizeDirection::DownLeft, ResizeDirection::Down, ResizeDirection::DownRight },
  433. };
  434. Rect outer_rect = window.frame().rect();
  435. ASSERT(outer_rect.contains(position));
  436. int window_relative_x = position.x() - outer_rect.x();
  437. int window_relative_y = position.y() - outer_rect.y();
  438. int hot_area_row = min(2, window_relative_y / (outer_rect.height() / 3));
  439. int hot_area_column = min(2, window_relative_x / (outer_rect.width() / 3));
  440. m_resize_direction = direction_for_hot_area[hot_area_row][hot_area_column];
  441. if (m_resize_direction == ResizeDirection::None) {
  442. ASSERT(!m_resize_window);
  443. return;
  444. }
  445. #ifdef RESIZE_DEBUG
  446. dbg() << "[WM] Begin resizing WSWindow{" << &window << "}";
  447. #endif
  448. m_resizing_mouse_button = button;
  449. m_resize_window = window.make_weak_ptr();
  450. ;
  451. m_resize_origin = position;
  452. m_resize_window_original_rect = window.rect();
  453. invalidate(window);
  454. }
  455. void WSWindowManager::start_window_resize(WSWindow& window, const WSMouseEvent& event)
  456. {
  457. start_window_resize(window, event.position(), event.button());
  458. }
  459. bool WSWindowManager::process_ongoing_window_move(WSMouseEvent& event, WSWindow*& hovered_window)
  460. {
  461. if (!m_move_window)
  462. return false;
  463. if (event.type() == WSEvent::MouseUp && event.button() == MouseButton::Left) {
  464. #ifdef MOVE_DEBUG
  465. dbg() << "[WM] Finish moving WSWindow{" << m_move_window << "}";
  466. #endif
  467. invalidate(*m_move_window);
  468. if (m_move_window->rect().contains(event.position()))
  469. hovered_window = m_move_window;
  470. if (m_move_window->is_resizable()) {
  471. process_event_for_doubleclick(*m_move_window, event);
  472. if (event.type() == WSEvent::MouseDoubleClick) {
  473. #if defined(DOUBLECLICK_DEBUG)
  474. dbg() << "[WM] Click up became doubleclick!";
  475. #endif
  476. m_move_window->set_maximized(!m_move_window->is_maximized());
  477. }
  478. }
  479. m_move_window = nullptr;
  480. return true;
  481. }
  482. if (event.type() == WSEvent::MouseMove) {
  483. #ifdef MOVE_DEBUG
  484. dbg() << "[WM] Moving, origin: " << m_move_origin << ", now: " << event.position();
  485. if (m_move_window->is_maximized()) {
  486. dbg() << " [!] The window is still maximized. Not moving yet.";
  487. }
  488. #endif
  489. if (m_move_window->is_maximized()) {
  490. auto pixels_moved_from_start = event.position().pixels_moved(m_move_origin);
  491. // dbg() << "[WM] " << pixels_moved_from_start << " moved since start of window move";
  492. if (pixels_moved_from_start > 5) {
  493. // dbg() << "[WM] de-maximizing window";
  494. m_move_origin = event.position();
  495. auto width_before_resize = m_move_window->width();
  496. m_move_window->set_maximized(false);
  497. m_move_window->move_to(m_move_origin.x() - (m_move_window->width() * ((float)m_move_origin.x() / width_before_resize)), m_move_origin.y());
  498. m_move_window_origin = m_move_window->position();
  499. }
  500. } else {
  501. Point pos = m_move_window_origin.translated(event.position() - m_move_origin);
  502. m_move_window->set_position_without_repaint(pos);
  503. if (m_move_window->rect().contains(event.position()))
  504. hovered_window = m_move_window;
  505. return true;
  506. }
  507. }
  508. return false;
  509. }
  510. bool WSWindowManager::process_ongoing_window_resize(const WSMouseEvent& event, WSWindow*& hovered_window)
  511. {
  512. if (!m_resize_window)
  513. return false;
  514. if (event.type() == WSEvent::MouseUp && event.button() == m_resizing_mouse_button) {
  515. #ifdef RESIZE_DEBUG
  516. dbg() << "[WM] Finish resizing WSWindow{" << m_resize_window << "}";
  517. #endif
  518. CEventLoop::current().post_event(*m_resize_window, make<WSResizeEvent>(m_resize_window->rect(), m_resize_window->rect()));
  519. invalidate(*m_resize_window);
  520. if (m_resize_window->rect().contains(event.position()))
  521. hovered_window = m_resize_window;
  522. m_resize_window = nullptr;
  523. m_resizing_mouse_button = MouseButton::None;
  524. return true;
  525. }
  526. if (event.type() != WSEvent::MouseMove)
  527. return false;
  528. auto old_rect = m_resize_window->rect();
  529. int diff_x = event.x() - m_resize_origin.x();
  530. int diff_y = event.y() - m_resize_origin.y();
  531. int change_w = 0;
  532. int change_h = 0;
  533. switch (m_resize_direction) {
  534. case ResizeDirection::DownRight:
  535. change_w = diff_x;
  536. change_h = diff_y;
  537. break;
  538. case ResizeDirection::Right:
  539. change_w = diff_x;
  540. break;
  541. case ResizeDirection::UpRight:
  542. change_w = diff_x;
  543. change_h = -diff_y;
  544. break;
  545. case ResizeDirection::Up:
  546. change_h = -diff_y;
  547. break;
  548. case ResizeDirection::UpLeft:
  549. change_w = -diff_x;
  550. change_h = -diff_y;
  551. break;
  552. case ResizeDirection::Left:
  553. change_w = -diff_x;
  554. break;
  555. case ResizeDirection::DownLeft:
  556. change_w = -diff_x;
  557. change_h = diff_y;
  558. break;
  559. case ResizeDirection::Down:
  560. change_h = diff_y;
  561. break;
  562. default:
  563. ASSERT_NOT_REACHED();
  564. }
  565. auto new_rect = m_resize_window_original_rect;
  566. // First, size the new rect.
  567. Size minimum_size { 50, 50 };
  568. new_rect.set_width(max(minimum_size.width(), new_rect.width() + change_w));
  569. new_rect.set_height(max(minimum_size.height(), new_rect.height() + change_h));
  570. if (!m_resize_window->size_increment().is_null()) {
  571. int horizontal_incs = (new_rect.width() - m_resize_window->base_size().width()) / m_resize_window->size_increment().width();
  572. new_rect.set_width(m_resize_window->base_size().width() + horizontal_incs * m_resize_window->size_increment().width());
  573. int vertical_incs = (new_rect.height() - m_resize_window->base_size().height()) / m_resize_window->size_increment().height();
  574. new_rect.set_height(m_resize_window->base_size().height() + vertical_incs * m_resize_window->size_increment().height());
  575. }
  576. // Second, set its position so that the sides of the window
  577. // that end up moving are the same ones as the user is dragging,
  578. // no matter which part of the logic above caused us to decide
  579. // to resize by this much.
  580. switch (m_resize_direction) {
  581. case ResizeDirection::DownRight:
  582. case ResizeDirection::Right:
  583. case ResizeDirection::Down:
  584. break;
  585. case ResizeDirection::Left:
  586. case ResizeDirection::Up:
  587. case ResizeDirection::UpLeft:
  588. new_rect.set_right_without_resize(m_resize_window_original_rect.right());
  589. new_rect.set_bottom_without_resize(m_resize_window_original_rect.bottom());
  590. break;
  591. case ResizeDirection::UpRight:
  592. new_rect.set_bottom_without_resize(m_resize_window_original_rect.bottom());
  593. break;
  594. case ResizeDirection::DownLeft:
  595. new_rect.set_right_without_resize(m_resize_window_original_rect.right());
  596. break;
  597. default:
  598. ASSERT_NOT_REACHED();
  599. }
  600. if (new_rect.contains(event.position()))
  601. hovered_window = m_resize_window;
  602. if (m_resize_window->rect() == new_rect)
  603. return true;
  604. #ifdef RESIZE_DEBUG
  605. dbg() << "[WM] Resizing, original: " << m_resize_window_original_rect << ", now: " << new_rect;
  606. #endif
  607. m_resize_window->set_rect(new_rect);
  608. CEventLoop::current().post_event(*m_resize_window, make<WSResizeEvent>(old_rect, new_rect));
  609. return true;
  610. }
  611. bool WSWindowManager::process_ongoing_drag(WSMouseEvent& event, WSWindow*& hovered_window)
  612. {
  613. if (!m_dnd_client)
  614. return false;
  615. if (!(event.type() == WSEvent::MouseUp && event.button() == MouseButton::Left))
  616. return true;
  617. hovered_window = nullptr;
  618. for_each_visible_window_from_front_to_back([&](auto& window) {
  619. if (window.frame().rect().contains(event.position())) {
  620. hovered_window = &window;
  621. return IterationDecision::Break;
  622. }
  623. return IterationDecision::Continue;
  624. });
  625. if (hovered_window) {
  626. m_dnd_client->post_message(WindowClient::DragAccepted());
  627. if (hovered_window->client()) {
  628. auto translated_event = event.translated(-hovered_window->position());
  629. hovered_window->client()->post_message(WindowClient::DragDropped(hovered_window->window_id(), translated_event.position(), m_dnd_text, m_dnd_data_type, m_dnd_data));
  630. }
  631. } else {
  632. m_dnd_client->post_message(WindowClient::DragCancelled());
  633. }
  634. end_dnd_drag();
  635. return true;
  636. }
  637. void WSWindowManager::set_cursor_tracking_button(WSButton* button)
  638. {
  639. m_cursor_tracking_button = button ? button->make_weak_ptr() : nullptr;
  640. }
  641. auto WSWindowManager::DoubleClickInfo::metadata_for_button(MouseButton button) -> ClickMetadata&
  642. {
  643. switch (button) {
  644. case MouseButton::Left:
  645. return m_left;
  646. case MouseButton::Right:
  647. return m_right;
  648. case MouseButton::Middle:
  649. return m_middle;
  650. default:
  651. ASSERT_NOT_REACHED();
  652. }
  653. }
  654. // #define DOUBLECLICK_DEBUG
  655. void WSWindowManager::process_event_for_doubleclick(WSWindow& window, WSMouseEvent& event)
  656. {
  657. // We only care about button presses (because otherwise it's not a doubleclick, duh!)
  658. ASSERT(event.type() == WSEvent::MouseUp);
  659. if (&window != m_double_click_info.m_clicked_window) {
  660. // we either haven't clicked anywhere, or we haven't clicked on this
  661. // window. set the current click window, and reset the timers.
  662. #if defined(DOUBLECLICK_DEBUG)
  663. dbg() << "Initial mouseup on window " << &window << " (previous was " << m_double_click_info.m_clicked_window << ')';
  664. #endif
  665. m_double_click_info.m_clicked_window = window.make_weak_ptr();
  666. m_double_click_info.reset();
  667. }
  668. auto& metadata = m_double_click_info.metadata_for_button(event.button());
  669. // if the clock is invalid, we haven't clicked with this button on this
  670. // window yet, so there's nothing to do.
  671. if (!metadata.clock.is_valid()) {
  672. metadata.clock.start();
  673. } else {
  674. int elapsed_since_last_click = metadata.clock.elapsed();
  675. metadata.clock.start();
  676. if (elapsed_since_last_click < m_double_click_speed) {
  677. auto diff = event.position() - metadata.last_position;
  678. auto distance_travelled_squared = diff.x() * diff.x() + diff.y() * diff.y();
  679. if (distance_travelled_squared > (m_max_distance_for_double_click * m_max_distance_for_double_click)) {
  680. // too far; try again
  681. metadata.clock.start();
  682. } else {
  683. #if defined(DOUBLECLICK_DEBUG)
  684. dbg() << "Transforming MouseUp to MouseDoubleClick (" << elapsed_since_last_click << " < " << m_double_click_speed << ")!";
  685. #endif
  686. event = WSMouseEvent(WSEvent::MouseDoubleClick, event.position(), event.buttons(), event.button(), event.modifiers(), event.wheel_delta());
  687. // invalidate this now we've delivered a doubleclick, otherwise
  688. // tripleclick will deliver two doubleclick events (incorrectly).
  689. metadata.clock = {};
  690. }
  691. } else {
  692. // too slow; try again
  693. metadata.clock.start();
  694. }
  695. }
  696. metadata.last_position = event.position();
  697. }
  698. void WSWindowManager::deliver_mouse_event(WSWindow& window, WSMouseEvent& event)
  699. {
  700. window.dispatch_event(event);
  701. if (event.type() == WSEvent::MouseUp) {
  702. process_event_for_doubleclick(window, event);
  703. if (event.type() == WSEvent::MouseDoubleClick)
  704. window.dispatch_event(event);
  705. }
  706. }
  707. void WSWindowManager::process_mouse_event(WSMouseEvent& event, WSWindow*& hovered_window)
  708. {
  709. hovered_window = nullptr;
  710. if (process_ongoing_drag(event, hovered_window))
  711. return;
  712. if (process_ongoing_window_move(event, hovered_window))
  713. return;
  714. if (process_ongoing_window_resize(event, hovered_window))
  715. return;
  716. if (m_cursor_tracking_button)
  717. return m_cursor_tracking_button->on_mouse_event(event.translated(-m_cursor_tracking_button->screen_rect().location()));
  718. // This is quite hackish, but it's how the WSButton hover effect is implemented.
  719. if (m_hovered_button && event.type() == WSEvent::MouseMove)
  720. m_hovered_button->on_mouse_event(event.translated(-m_hovered_button->screen_rect().location()));
  721. HashTable<WSWindow*> windows_who_received_mouse_event_due_to_cursor_tracking;
  722. for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) {
  723. if (!window->global_cursor_tracking())
  724. continue;
  725. ASSERT(window->is_visible()); // Maybe this should be supported? Idk. Let's catch it and think about it later.
  726. ASSERT(!window->is_minimized()); // Maybe this should also be supported? Idk.
  727. windows_who_received_mouse_event_due_to_cursor_tracking.set(window);
  728. auto translated_event = event.translated(-window->position());
  729. deliver_mouse_event(*window, translated_event);
  730. }
  731. // FIXME: Now that the menubar has a dedicated window, is this special-casing really necessary?
  732. if (!active_window_is_modal() && menubar_rect().contains(event.position())) {
  733. m_menu_manager.dispatch_event(event);
  734. return;
  735. }
  736. if (!menu_manager().open_menu_stack().is_empty()) {
  737. auto* topmost_menu = menu_manager().open_menu_stack().last().ptr();
  738. ASSERT(topmost_menu);
  739. auto* window = topmost_menu->menu_window();
  740. ASSERT(window);
  741. bool event_is_inside_current_menu = window->rect().contains(event.position());
  742. if (!event_is_inside_current_menu) {
  743. if (topmost_menu->hovered_item())
  744. topmost_menu->clear_hovered_item();
  745. if (event.type() == WSEvent::MouseDown || event.type() == WSEvent::MouseUp)
  746. m_menu_manager.close_bar();
  747. if (event.type() == WSEvent::MouseMove) {
  748. for (auto& menu : m_menu_manager.open_menu_stack()) {
  749. if (!menu)
  750. continue;
  751. if (!menu->menu_window()->rect().contains(event.position()))
  752. continue;
  753. hovered_window = menu->menu_window();
  754. auto translated_event = event.translated(-menu->menu_window()->position());
  755. deliver_mouse_event(*menu->menu_window(), translated_event);
  756. break;
  757. }
  758. }
  759. } else {
  760. hovered_window = window;
  761. auto translated_event = event.translated(-window->position());
  762. deliver_mouse_event(*window, translated_event);
  763. }
  764. return;
  765. }
  766. WSWindow* event_window_with_frame = nullptr;
  767. if (m_active_input_window) {
  768. // At this point, we have delivered the start of an input sequence to a
  769. // client application. We must keep delivering to that client
  770. // application until the input sequence is done.
  771. //
  772. // This prevents e.g. moving on one window out of the bounds starting
  773. // a move in that other unrelated window, and other silly shenanigans.
  774. if (!windows_who_received_mouse_event_due_to_cursor_tracking.contains(m_active_input_window)) {
  775. auto translated_event = event.translated(-m_active_input_window->position());
  776. deliver_mouse_event(*m_active_input_window, translated_event);
  777. windows_who_received_mouse_event_due_to_cursor_tracking.set(m_active_input_window.ptr());
  778. }
  779. if (event.type() == WSEvent::MouseUp && event.buttons() == 0) {
  780. m_active_input_window = nullptr;
  781. }
  782. for_each_visible_window_from_front_to_back([&](auto& window) {
  783. if (window.frame().rect().contains(event.position())) {
  784. hovered_window = &window;
  785. return IterationDecision::Break;
  786. }
  787. return IterationDecision::Continue;
  788. });
  789. } else {
  790. for_each_visible_window_from_front_to_back([&](WSWindow& window) {
  791. auto window_frame_rect = window.frame().rect();
  792. if (!window_frame_rect.contains(event.position()))
  793. return IterationDecision::Continue;
  794. if (&window != m_resize_candidate.ptr())
  795. clear_resize_candidate();
  796. // First check if we should initiate a move or resize (Logo+LMB or Logo+RMB).
  797. // In those cases, the event is swallowed by the window manager.
  798. if (window.is_movable()) {
  799. if (!window.is_fullscreen() && m_keyboard_modifiers == Mod_Logo && event.type() == WSEvent::MouseDown && event.button() == MouseButton::Left) {
  800. hovered_window = &window;
  801. start_window_move(window, event);
  802. return IterationDecision::Break;
  803. }
  804. if (window.is_resizable() && m_keyboard_modifiers == Mod_Logo && event.type() == WSEvent::MouseDown && event.button() == MouseButton::Right && !window.is_blocked_by_modal_window()) {
  805. hovered_window = &window;
  806. start_window_resize(window, event);
  807. return IterationDecision::Break;
  808. }
  809. }
  810. if (m_keyboard_modifiers == Mod_Logo && event.type() == WSEvent::MouseWheel) {
  811. float opacity_change = -event.wheel_delta() * 0.05f;
  812. float new_opacity = window.opacity() + opacity_change;
  813. if (new_opacity < 0.05f)
  814. new_opacity = 0.05f;
  815. if (new_opacity > 1.0f)
  816. new_opacity = 1.0f;
  817. window.set_opacity(new_opacity);
  818. window.invalidate();
  819. return IterationDecision::Break;
  820. }
  821. // Well okay, let's see if we're hitting the frame or the window inside the frame.
  822. if (window.rect().contains(event.position())) {
  823. if (window.type() == WSWindowType::Normal && event.type() == WSEvent::MouseDown)
  824. move_to_front_and_make_active(window);
  825. hovered_window = &window;
  826. if (!window.global_cursor_tracking() && !windows_who_received_mouse_event_due_to_cursor_tracking.contains(&window)) {
  827. auto translated_event = event.translated(-window.position());
  828. deliver_mouse_event(window, translated_event);
  829. if (event.type() == WSEvent::MouseDown) {
  830. m_active_input_window = window.make_weak_ptr();
  831. }
  832. }
  833. return IterationDecision::Break;
  834. }
  835. // We are hitting the frame, pass the event along to WSWindowFrame.
  836. window.frame().on_mouse_event(event.translated(-window_frame_rect.location()));
  837. event_window_with_frame = &window;
  838. return IterationDecision::Break;
  839. });
  840. }
  841. if (event_window_with_frame != m_resize_candidate.ptr())
  842. clear_resize_candidate();
  843. }
  844. void WSWindowManager::clear_resize_candidate()
  845. {
  846. if (m_resize_candidate)
  847. WSCompositor::the().invalidate_cursor();
  848. m_resize_candidate = nullptr;
  849. }
  850. bool WSWindowManager::any_opaque_window_contains_rect(const Rect& rect)
  851. {
  852. bool found_containing_window = false;
  853. for_each_window([&](WSWindow& window) {
  854. if (!window.is_visible())
  855. return IterationDecision::Continue;
  856. if (window.is_minimized())
  857. return IterationDecision::Continue;
  858. if (window.opacity() < 1.0f)
  859. return IterationDecision::Continue;
  860. if (window.has_alpha_channel()) {
  861. // FIXME: Just because the window has an alpha channel doesn't mean it's not opaque.
  862. // Maybe there's some way we could know this?
  863. return IterationDecision::Continue;
  864. }
  865. if (window.frame().rect().contains(rect)) {
  866. found_containing_window = true;
  867. return IterationDecision::Break;
  868. }
  869. return IterationDecision::Continue;
  870. });
  871. return found_containing_window;
  872. };
  873. bool WSWindowManager::any_opaque_window_above_this_one_contains_rect(const WSWindow& a_window, const Rect& rect)
  874. {
  875. bool found_containing_window = false;
  876. bool checking = false;
  877. for_each_visible_window_from_back_to_front([&](WSWindow& window) {
  878. if (&window == &a_window) {
  879. checking = true;
  880. return IterationDecision::Continue;
  881. }
  882. if (!checking)
  883. return IterationDecision::Continue;
  884. if (!window.is_visible())
  885. return IterationDecision::Continue;
  886. if (window.is_minimized())
  887. return IterationDecision::Continue;
  888. if (window.opacity() < 1.0f)
  889. return IterationDecision::Continue;
  890. if (window.has_alpha_channel())
  891. return IterationDecision::Continue;
  892. if (window.frame().rect().contains(rect)) {
  893. found_containing_window = true;
  894. return IterationDecision::Break;
  895. }
  896. return IterationDecision::Continue;
  897. });
  898. return found_containing_window;
  899. };
  900. Rect WSWindowManager::menubar_rect() const
  901. {
  902. if (active_fullscreen_window())
  903. return {};
  904. return m_menu_manager.menubar_rect();
  905. }
  906. void WSWindowManager::draw_window_switcher()
  907. {
  908. if (m_switcher.is_visible())
  909. m_switcher.draw();
  910. }
  911. void WSWindowManager::event(CEvent& event)
  912. {
  913. if (static_cast<WSEvent&>(event).is_mouse_event()) {
  914. WSWindow* hovered_window = nullptr;
  915. process_mouse_event(static_cast<WSMouseEvent&>(event), hovered_window);
  916. set_hovered_window(hovered_window);
  917. return;
  918. }
  919. if (static_cast<WSEvent&>(event).is_key_event()) {
  920. auto& key_event = static_cast<const WSKeyEvent&>(event);
  921. m_keyboard_modifiers = key_event.modifiers();
  922. if (key_event.type() == WSEvent::KeyDown && key_event.key() == Key_Escape && m_dnd_client) {
  923. m_dnd_client->post_message(WindowClient::DragCancelled());
  924. end_dnd_drag();
  925. return;
  926. }
  927. if (key_event.type() == WSEvent::KeyDown && key_event.modifiers() == Mod_Logo && key_event.key() == Key_Tab)
  928. m_switcher.show();
  929. if (m_switcher.is_visible()) {
  930. m_switcher.on_key_event(key_event);
  931. return;
  932. }
  933. if (m_active_window)
  934. return m_active_window->dispatch_event(event);
  935. return;
  936. }
  937. CObject::event(event);
  938. }
  939. void WSWindowManager::set_highlight_window(WSWindow* window)
  940. {
  941. if (window == m_highlight_window)
  942. return;
  943. if (auto* previous_highlight_window = m_highlight_window.ptr())
  944. invalidate(*previous_highlight_window);
  945. m_highlight_window = window ? window->make_weak_ptr() : nullptr;
  946. if (m_highlight_window)
  947. invalidate(*m_highlight_window);
  948. }
  949. void WSWindowManager::set_active_window(WSWindow* window)
  950. {
  951. if (window && window->is_blocked_by_modal_window())
  952. return;
  953. if (window->type() != WSWindowType::Normal)
  954. return;
  955. if (window == m_active_window)
  956. return;
  957. auto* previously_active_window = m_active_window.ptr();
  958. if (previously_active_window) {
  959. CEventLoop::current().post_event(*previously_active_window, make<WSEvent>(WSEvent::WindowDeactivated));
  960. invalidate(*previously_active_window);
  961. }
  962. m_active_window = window->make_weak_ptr();
  963. if (m_active_window) {
  964. CEventLoop::current().post_event(*m_active_window, make<WSEvent>(WSEvent::WindowActivated));
  965. invalidate(*m_active_window);
  966. auto* client = window->client();
  967. ASSERT(client);
  968. set_current_menubar(client->app_menubar());
  969. if (previously_active_window)
  970. tell_wm_listeners_window_state_changed(*previously_active_window);
  971. tell_wm_listeners_window_state_changed(*m_active_window);
  972. }
  973. }
  974. void WSWindowManager::set_hovered_window(WSWindow* window)
  975. {
  976. if (m_hovered_window == window)
  977. return;
  978. if (m_hovered_window)
  979. CEventLoop::current().post_event(*m_hovered_window, make<WSEvent>(WSEvent::WindowLeft));
  980. m_hovered_window = window ? window->make_weak_ptr() : nullptr;
  981. if (m_hovered_window)
  982. CEventLoop::current().post_event(*m_hovered_window, make<WSEvent>(WSEvent::WindowEntered));
  983. }
  984. void WSWindowManager::invalidate()
  985. {
  986. WSCompositor::the().invalidate();
  987. }
  988. void WSWindowManager::invalidate(const Rect& rect)
  989. {
  990. WSCompositor::the().invalidate(rect);
  991. }
  992. void WSWindowManager::invalidate(const WSWindow& window)
  993. {
  994. invalidate(window.frame().rect());
  995. }
  996. void WSWindowManager::invalidate(const WSWindow& window, const Rect& rect)
  997. {
  998. if (window.type() == WSWindowType::MenuApplet) {
  999. menu_manager().invalidate_applet(window, rect);
  1000. return;
  1001. }
  1002. if (rect.is_empty()) {
  1003. invalidate(window);
  1004. return;
  1005. }
  1006. auto outer_rect = window.frame().rect();
  1007. auto inner_rect = rect;
  1008. inner_rect.move_by(window.position());
  1009. // FIXME: This seems slightly wrong; the inner rect shouldn't intersect the border part of the outer rect.
  1010. inner_rect.intersect(outer_rect);
  1011. invalidate(inner_rect);
  1012. }
  1013. void WSWindowManager::close_menubar(WSMenuBar& menubar)
  1014. {
  1015. if (current_menubar() == &menubar)
  1016. set_current_menubar(nullptr);
  1017. }
  1018. const WSClientConnection* WSWindowManager::active_client() const
  1019. {
  1020. if (m_active_window)
  1021. return m_active_window->client();
  1022. return nullptr;
  1023. }
  1024. void WSWindowManager::notify_client_changed_app_menubar(WSClientConnection& client)
  1025. {
  1026. if (active_client() == &client)
  1027. set_current_menubar(client.app_menubar());
  1028. m_menu_manager.refresh();
  1029. }
  1030. const WSCursor& WSWindowManager::active_cursor() const
  1031. {
  1032. if (m_dnd_client)
  1033. return *m_drag_cursor;
  1034. if (m_move_window)
  1035. return *m_move_cursor;
  1036. if (m_resize_window || m_resize_candidate) {
  1037. switch (m_resize_direction) {
  1038. case ResizeDirection::Up:
  1039. case ResizeDirection::Down:
  1040. return *m_resize_vertically_cursor;
  1041. case ResizeDirection::Left:
  1042. case ResizeDirection::Right:
  1043. return *m_resize_horizontally_cursor;
  1044. case ResizeDirection::UpLeft:
  1045. case ResizeDirection::DownRight:
  1046. return *m_resize_diagonally_tlbr_cursor;
  1047. case ResizeDirection::UpRight:
  1048. case ResizeDirection::DownLeft:
  1049. return *m_resize_diagonally_bltr_cursor;
  1050. case ResizeDirection::None:
  1051. break;
  1052. }
  1053. }
  1054. if (m_hovered_window && m_hovered_window->override_cursor())
  1055. return *m_hovered_window->override_cursor();
  1056. return *m_arrow_cursor;
  1057. }
  1058. void WSWindowManager::set_hovered_button(WSButton* button)
  1059. {
  1060. m_hovered_button = button ? button->make_weak_ptr() : nullptr;
  1061. }
  1062. void WSWindowManager::set_resize_candidate(WSWindow& window, ResizeDirection direction)
  1063. {
  1064. m_resize_candidate = window.make_weak_ptr();
  1065. m_resize_direction = direction;
  1066. }
  1067. ResizeDirection WSWindowManager::resize_direction_of_window(const WSWindow& window)
  1068. {
  1069. if (&window != m_resize_window)
  1070. return ResizeDirection::None;
  1071. return m_resize_direction;
  1072. }
  1073. Rect WSWindowManager::maximized_window_rect(const WSWindow& window) const
  1074. {
  1075. Rect rect = WSScreen::the().rect();
  1076. // Subtract window title bar (leaving the border)
  1077. rect.set_y(rect.y() + window.frame().title_bar_rect().height());
  1078. rect.set_height(rect.height() - window.frame().title_bar_rect().height());
  1079. // Subtract menu bar
  1080. rect.set_y(rect.y() + menubar_rect().height());
  1081. rect.set_height(rect.height() - menubar_rect().height());
  1082. // Subtract taskbar window height if present
  1083. const_cast<WSWindowManager*>(this)->for_each_visible_window_of_type_from_back_to_front(WSWindowType::Taskbar, [&rect](WSWindow& taskbar_window) {
  1084. rect.set_height(rect.height() - taskbar_window.height());
  1085. return IterationDecision::Break;
  1086. });
  1087. return rect;
  1088. }
  1089. WSMenu* WSWindowManager::find_internal_menu_by_id(int menu_id)
  1090. {
  1091. if (m_themes_menu->menu_id() == menu_id)
  1092. return m_themes_menu.ptr();
  1093. for (auto& it : m_app_category_menus) {
  1094. if (menu_id == it.value->menu_id())
  1095. return it.value;
  1096. }
  1097. return nullptr;
  1098. }
  1099. void WSWindowManager::start_dnd_drag(WSClientConnection& client, const String& text, GraphicsBitmap* bitmap, const String& data_type, const String& data)
  1100. {
  1101. ASSERT(!m_dnd_client);
  1102. m_dnd_client = client.make_weak_ptr();
  1103. m_dnd_text = text;
  1104. m_dnd_bitmap = bitmap;
  1105. m_dnd_data_type = data_type;
  1106. m_dnd_data = data;
  1107. WSCompositor::the().invalidate_cursor();
  1108. m_active_input_window = nullptr;
  1109. }
  1110. void WSWindowManager::end_dnd_drag()
  1111. {
  1112. ASSERT(m_dnd_client);
  1113. WSCompositor::the().invalidate_cursor();
  1114. m_dnd_client = nullptr;
  1115. m_dnd_text = {};
  1116. m_dnd_bitmap = nullptr;
  1117. }
  1118. Rect WSWindowManager::dnd_rect() const
  1119. {
  1120. int bitmap_width = m_dnd_bitmap ? m_dnd_bitmap->width() : 0;
  1121. int bitmap_height = m_dnd_bitmap ? m_dnd_bitmap->height() : 0;
  1122. int width = font().width(m_dnd_text) + bitmap_width;
  1123. int height = max((int)font().glyph_height(), bitmap_height);
  1124. auto location = WSCompositor::the().current_cursor_rect().center().translated(8, 8);
  1125. return Rect(location, { width, height }).inflated(4, 4);
  1126. }