WSWindowManager.cpp 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320
  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 PaletteImpl& 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 = PaletteImpl::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. recompute_occlusions();
  276. if (window.listens_to_wm_events()) {
  277. for_each_window([&](WSWindow& other_window) {
  278. if (&window != &other_window) {
  279. tell_wm_listener_about_window(window, other_window);
  280. tell_wm_listener_about_window_icon(window, other_window);
  281. }
  282. return IterationDecision::Continue;
  283. });
  284. }
  285. tell_wm_listeners_window_state_changed(window);
  286. }
  287. void WSWindowManager::move_to_front_and_make_active(WSWindow& window)
  288. {
  289. if (window.is_blocked_by_modal_window())
  290. return;
  291. if (m_windows_in_order.tail() != &window)
  292. invalidate(window);
  293. m_windows_in_order.remove(&window);
  294. m_windows_in_order.append(&window);
  295. recompute_occlusions();
  296. set_active_window(&window);
  297. }
  298. void WSWindowManager::remove_window(WSWindow& window)
  299. {
  300. invalidate(window);
  301. m_windows_in_order.remove(&window);
  302. if (window.is_active())
  303. pick_new_active_window();
  304. if (m_active_window.ptr() == &window)
  305. set_active_window(nullptr);
  306. if (m_switcher.is_visible() && window.type() != WSWindowType::WindowSwitcher)
  307. m_switcher.refresh();
  308. recompute_occlusions();
  309. for_each_window_listening_to_wm_events([&window](WSWindow& listener) {
  310. if (!(listener.wm_event_mask() & WSWMEventMask::WindowRemovals))
  311. return IterationDecision::Continue;
  312. if (window.client())
  313. CEventLoop::current().post_event(listener, make<WSWMWindowRemovedEvent>(window.client()->client_id(), window.window_id()));
  314. return IterationDecision::Continue;
  315. });
  316. }
  317. void WSWindowManager::tell_wm_listener_about_window(WSWindow& listener, WSWindow& window)
  318. {
  319. if (!(listener.wm_event_mask() & WSWMEventMask::WindowStateChanges))
  320. return;
  321. if (window.client())
  322. 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()));
  323. }
  324. void WSWindowManager::tell_wm_listener_about_window_rect(WSWindow& listener, WSWindow& window)
  325. {
  326. if (!(listener.wm_event_mask() & WSWMEventMask::WindowRectChanges))
  327. return;
  328. if (window.client())
  329. CEventLoop::current().post_event(listener, make<WSWMWindowRectChangedEvent>(window.client()->client_id(), window.window_id(), window.rect()));
  330. }
  331. void WSWindowManager::tell_wm_listener_about_window_icon(WSWindow& listener, WSWindow& window)
  332. {
  333. if (!(listener.wm_event_mask() & WSWMEventMask::WindowIconChanges))
  334. return;
  335. if (window.client() && window.icon().shared_buffer_id() != -1)
  336. CEventLoop::current().post_event(listener, make<WSWMWindowIconBitmapChangedEvent>(window.client()->client_id(), window.window_id(), window.icon().shared_buffer_id(), window.icon().size()));
  337. }
  338. void WSWindowManager::tell_wm_listeners_window_state_changed(WSWindow& window)
  339. {
  340. for_each_window_listening_to_wm_events([&](WSWindow& listener) {
  341. tell_wm_listener_about_window(listener, window);
  342. return IterationDecision::Continue;
  343. });
  344. }
  345. void WSWindowManager::tell_wm_listeners_window_icon_changed(WSWindow& window)
  346. {
  347. for_each_window_listening_to_wm_events([&](WSWindow& listener) {
  348. tell_wm_listener_about_window_icon(listener, window);
  349. return IterationDecision::Continue;
  350. });
  351. }
  352. void WSWindowManager::tell_wm_listeners_window_rect_changed(WSWindow& window)
  353. {
  354. for_each_window_listening_to_wm_events([&](WSWindow& listener) {
  355. tell_wm_listener_about_window_rect(listener, window);
  356. return IterationDecision::Continue;
  357. });
  358. }
  359. void WSWindowManager::notify_title_changed(WSWindow& window)
  360. {
  361. if (window.type() != WSWindowType::Normal)
  362. return;
  363. dbg() << "[WM] WSWindow{" << &window << "} title set to \"" << window.title() << '"';
  364. invalidate(window.frame().rect());
  365. if (m_switcher.is_visible())
  366. m_switcher.refresh();
  367. tell_wm_listeners_window_state_changed(window);
  368. }
  369. void WSWindowManager::notify_rect_changed(WSWindow& window, const Rect& old_rect, const Rect& new_rect)
  370. {
  371. UNUSED_PARAM(old_rect);
  372. UNUSED_PARAM(new_rect);
  373. #ifdef RESIZE_DEBUG
  374. dbg() << "[WM] WSWindow " << &window << " rect changed " << old_rect << " -> " << new_rect;
  375. #endif
  376. if (m_switcher.is_visible() && window.type() != WSWindowType::WindowSwitcher)
  377. m_switcher.refresh();
  378. recompute_occlusions();
  379. tell_wm_listeners_window_rect_changed(window);
  380. m_menu_manager.refresh();
  381. }
  382. void WSWindowManager::recompute_occlusions()
  383. {
  384. for_each_visible_window_from_back_to_front([&](WSWindow& window) {
  385. if (m_switcher.is_visible()) {
  386. window.set_occluded(false);
  387. } else {
  388. if (any_opaque_window_above_this_one_contains_rect(window, window.frame().rect()))
  389. window.set_occluded(true);
  390. else
  391. window.set_occluded(false);
  392. }
  393. return IterationDecision::Continue;
  394. });
  395. }
  396. void WSWindowManager::notify_opacity_changed(WSWindow&)
  397. {
  398. recompute_occlusions();
  399. }
  400. void WSWindowManager::notify_minimization_state_changed(WSWindow& window)
  401. {
  402. tell_wm_listeners_window_state_changed(window);
  403. if (window.client())
  404. window.client()->post_message(WindowClient::WindowStateChanged(window.window_id(), window.is_minimized(), window.is_occluded()));
  405. if (window.is_active() && window.is_minimized())
  406. pick_new_active_window();
  407. }
  408. void WSWindowManager::notify_occlusion_state_changed(WSWindow& window)
  409. {
  410. if (window.client())
  411. window.client()->post_message(WindowClient::WindowStateChanged(window.window_id(), window.is_minimized(), window.is_occluded()));
  412. }
  413. void WSWindowManager::pick_new_active_window()
  414. {
  415. for_each_visible_window_of_type_from_front_to_back(WSWindowType::Normal, [&](WSWindow& candidate) {
  416. set_active_window(&candidate);
  417. return IterationDecision::Break;
  418. });
  419. }
  420. void WSWindowManager::start_window_move(WSWindow& window, const WSMouseEvent& event)
  421. {
  422. #ifdef MOVE_DEBUG
  423. dbg() << "[WM] Begin moving WSWindow{" << &window << "}";
  424. #endif
  425. move_to_front_and_make_active(window);
  426. m_move_window = window.make_weak_ptr();
  427. m_move_origin = event.position();
  428. m_move_window_origin = window.position();
  429. invalidate(window);
  430. }
  431. void WSWindowManager::start_window_resize(WSWindow& window, const Point& position, MouseButton button)
  432. {
  433. move_to_front_and_make_active(window);
  434. constexpr ResizeDirection direction_for_hot_area[3][3] = {
  435. { ResizeDirection::UpLeft, ResizeDirection::Up, ResizeDirection::UpRight },
  436. { ResizeDirection::Left, ResizeDirection::None, ResizeDirection::Right },
  437. { ResizeDirection::DownLeft, ResizeDirection::Down, ResizeDirection::DownRight },
  438. };
  439. Rect outer_rect = window.frame().rect();
  440. ASSERT(outer_rect.contains(position));
  441. int window_relative_x = position.x() - outer_rect.x();
  442. int window_relative_y = position.y() - outer_rect.y();
  443. int hot_area_row = min(2, window_relative_y / (outer_rect.height() / 3));
  444. int hot_area_column = min(2, window_relative_x / (outer_rect.width() / 3));
  445. m_resize_direction = direction_for_hot_area[hot_area_row][hot_area_column];
  446. if (m_resize_direction == ResizeDirection::None) {
  447. ASSERT(!m_resize_window);
  448. return;
  449. }
  450. #ifdef RESIZE_DEBUG
  451. dbg() << "[WM] Begin resizing WSWindow{" << &window << "}";
  452. #endif
  453. m_resizing_mouse_button = button;
  454. m_resize_window = window.make_weak_ptr();
  455. ;
  456. m_resize_origin = position;
  457. m_resize_window_original_rect = window.rect();
  458. invalidate(window);
  459. }
  460. void WSWindowManager::start_window_resize(WSWindow& window, const WSMouseEvent& event)
  461. {
  462. start_window_resize(window, event.position(), event.button());
  463. }
  464. bool WSWindowManager::process_ongoing_window_move(WSMouseEvent& event, WSWindow*& hovered_window)
  465. {
  466. if (!m_move_window)
  467. return false;
  468. if (event.type() == WSEvent::MouseUp && event.button() == MouseButton::Left) {
  469. #ifdef MOVE_DEBUG
  470. dbg() << "[WM] Finish moving WSWindow{" << m_move_window << "}";
  471. #endif
  472. invalidate(*m_move_window);
  473. if (m_move_window->rect().contains(event.position()))
  474. hovered_window = m_move_window;
  475. if (m_move_window->is_resizable()) {
  476. process_event_for_doubleclick(*m_move_window, event);
  477. if (event.type() == WSEvent::MouseDoubleClick) {
  478. #if defined(DOUBLECLICK_DEBUG)
  479. dbg() << "[WM] Click up became doubleclick!";
  480. #endif
  481. m_move_window->set_maximized(!m_move_window->is_maximized());
  482. }
  483. }
  484. m_move_window = nullptr;
  485. return true;
  486. }
  487. if (event.type() == WSEvent::MouseMove) {
  488. #ifdef MOVE_DEBUG
  489. dbg() << "[WM] Moving, origin: " << m_move_origin << ", now: " << event.position();
  490. if (m_move_window->is_maximized()) {
  491. dbg() << " [!] The window is still maximized. Not moving yet.";
  492. }
  493. #endif
  494. if (m_move_window->is_maximized()) {
  495. auto pixels_moved_from_start = event.position().pixels_moved(m_move_origin);
  496. // dbg() << "[WM] " << pixels_moved_from_start << " moved since start of window move";
  497. if (pixels_moved_from_start > 5) {
  498. // dbg() << "[WM] de-maximizing window";
  499. m_move_origin = event.position();
  500. auto width_before_resize = m_move_window->width();
  501. m_move_window->set_maximized(false);
  502. 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());
  503. m_move_window_origin = m_move_window->position();
  504. }
  505. } else {
  506. Point pos = m_move_window_origin.translated(event.position() - m_move_origin);
  507. m_move_window->set_position_without_repaint(pos);
  508. if (m_move_window->rect().contains(event.position()))
  509. hovered_window = m_move_window;
  510. return true;
  511. }
  512. }
  513. return false;
  514. }
  515. bool WSWindowManager::process_ongoing_window_resize(const WSMouseEvent& event, WSWindow*& hovered_window)
  516. {
  517. if (!m_resize_window)
  518. return false;
  519. if (event.type() == WSEvent::MouseUp && event.button() == m_resizing_mouse_button) {
  520. #ifdef RESIZE_DEBUG
  521. dbg() << "[WM] Finish resizing WSWindow{" << m_resize_window << "}";
  522. #endif
  523. CEventLoop::current().post_event(*m_resize_window, make<WSResizeEvent>(m_resize_window->rect(), m_resize_window->rect()));
  524. invalidate(*m_resize_window);
  525. if (m_resize_window->rect().contains(event.position()))
  526. hovered_window = m_resize_window;
  527. m_resize_window = nullptr;
  528. m_resizing_mouse_button = MouseButton::None;
  529. return true;
  530. }
  531. if (event.type() != WSEvent::MouseMove)
  532. return false;
  533. auto old_rect = m_resize_window->rect();
  534. int diff_x = event.x() - m_resize_origin.x();
  535. int diff_y = event.y() - m_resize_origin.y();
  536. int change_w = 0;
  537. int change_h = 0;
  538. switch (m_resize_direction) {
  539. case ResizeDirection::DownRight:
  540. change_w = diff_x;
  541. change_h = diff_y;
  542. break;
  543. case ResizeDirection::Right:
  544. change_w = diff_x;
  545. break;
  546. case ResizeDirection::UpRight:
  547. change_w = diff_x;
  548. change_h = -diff_y;
  549. break;
  550. case ResizeDirection::Up:
  551. change_h = -diff_y;
  552. break;
  553. case ResizeDirection::UpLeft:
  554. change_w = -diff_x;
  555. change_h = -diff_y;
  556. break;
  557. case ResizeDirection::Left:
  558. change_w = -diff_x;
  559. break;
  560. case ResizeDirection::DownLeft:
  561. change_w = -diff_x;
  562. change_h = diff_y;
  563. break;
  564. case ResizeDirection::Down:
  565. change_h = diff_y;
  566. break;
  567. default:
  568. ASSERT_NOT_REACHED();
  569. }
  570. auto new_rect = m_resize_window_original_rect;
  571. // First, size the new rect.
  572. Size minimum_size { 50, 50 };
  573. new_rect.set_width(max(minimum_size.width(), new_rect.width() + change_w));
  574. new_rect.set_height(max(minimum_size.height(), new_rect.height() + change_h));
  575. if (!m_resize_window->size_increment().is_null()) {
  576. int horizontal_incs = (new_rect.width() - m_resize_window->base_size().width()) / m_resize_window->size_increment().width();
  577. new_rect.set_width(m_resize_window->base_size().width() + horizontal_incs * m_resize_window->size_increment().width());
  578. int vertical_incs = (new_rect.height() - m_resize_window->base_size().height()) / m_resize_window->size_increment().height();
  579. new_rect.set_height(m_resize_window->base_size().height() + vertical_incs * m_resize_window->size_increment().height());
  580. }
  581. // Second, set its position so that the sides of the window
  582. // that end up moving are the same ones as the user is dragging,
  583. // no matter which part of the logic above caused us to decide
  584. // to resize by this much.
  585. switch (m_resize_direction) {
  586. case ResizeDirection::DownRight:
  587. case ResizeDirection::Right:
  588. case ResizeDirection::Down:
  589. break;
  590. case ResizeDirection::Left:
  591. case ResizeDirection::Up:
  592. case ResizeDirection::UpLeft:
  593. new_rect.set_right_without_resize(m_resize_window_original_rect.right());
  594. new_rect.set_bottom_without_resize(m_resize_window_original_rect.bottom());
  595. break;
  596. case ResizeDirection::UpRight:
  597. new_rect.set_bottom_without_resize(m_resize_window_original_rect.bottom());
  598. break;
  599. case ResizeDirection::DownLeft:
  600. new_rect.set_right_without_resize(m_resize_window_original_rect.right());
  601. break;
  602. default:
  603. ASSERT_NOT_REACHED();
  604. }
  605. if (new_rect.contains(event.position()))
  606. hovered_window = m_resize_window;
  607. if (m_resize_window->rect() == new_rect)
  608. return true;
  609. #ifdef RESIZE_DEBUG
  610. dbg() << "[WM] Resizing, original: " << m_resize_window_original_rect << ", now: " << new_rect;
  611. #endif
  612. m_resize_window->set_rect(new_rect);
  613. CEventLoop::current().post_event(*m_resize_window, make<WSResizeEvent>(old_rect, new_rect));
  614. return true;
  615. }
  616. bool WSWindowManager::process_ongoing_drag(WSMouseEvent& event, WSWindow*& hovered_window)
  617. {
  618. if (!m_dnd_client)
  619. return false;
  620. if (!(event.type() == WSEvent::MouseUp && event.button() == MouseButton::Left))
  621. return true;
  622. hovered_window = nullptr;
  623. for_each_visible_window_from_front_to_back([&](auto& window) {
  624. if (window.frame().rect().contains(event.position())) {
  625. hovered_window = &window;
  626. return IterationDecision::Break;
  627. }
  628. return IterationDecision::Continue;
  629. });
  630. if (hovered_window) {
  631. m_dnd_client->post_message(WindowClient::DragAccepted());
  632. if (hovered_window->client()) {
  633. auto translated_event = event.translated(-hovered_window->position());
  634. hovered_window->client()->post_message(WindowClient::DragDropped(hovered_window->window_id(), translated_event.position(), m_dnd_text, m_dnd_data_type, m_dnd_data));
  635. }
  636. } else {
  637. m_dnd_client->post_message(WindowClient::DragCancelled());
  638. }
  639. end_dnd_drag();
  640. return true;
  641. }
  642. void WSWindowManager::set_cursor_tracking_button(WSButton* button)
  643. {
  644. m_cursor_tracking_button = button ? button->make_weak_ptr() : nullptr;
  645. }
  646. auto WSWindowManager::DoubleClickInfo::metadata_for_button(MouseButton button) -> ClickMetadata&
  647. {
  648. switch (button) {
  649. case MouseButton::Left:
  650. return m_left;
  651. case MouseButton::Right:
  652. return m_right;
  653. case MouseButton::Middle:
  654. return m_middle;
  655. default:
  656. ASSERT_NOT_REACHED();
  657. }
  658. }
  659. // #define DOUBLECLICK_DEBUG
  660. void WSWindowManager::process_event_for_doubleclick(WSWindow& window, WSMouseEvent& event)
  661. {
  662. // We only care about button presses (because otherwise it's not a doubleclick, duh!)
  663. ASSERT(event.type() == WSEvent::MouseUp);
  664. if (&window != m_double_click_info.m_clicked_window) {
  665. // we either haven't clicked anywhere, or we haven't clicked on this
  666. // window. set the current click window, and reset the timers.
  667. #if defined(DOUBLECLICK_DEBUG)
  668. dbg() << "Initial mouseup on window " << &window << " (previous was " << m_double_click_info.m_clicked_window << ')';
  669. #endif
  670. m_double_click_info.m_clicked_window = window.make_weak_ptr();
  671. m_double_click_info.reset();
  672. }
  673. auto& metadata = m_double_click_info.metadata_for_button(event.button());
  674. // if the clock is invalid, we haven't clicked with this button on this
  675. // window yet, so there's nothing to do.
  676. if (!metadata.clock.is_valid()) {
  677. metadata.clock.start();
  678. } else {
  679. int elapsed_since_last_click = metadata.clock.elapsed();
  680. metadata.clock.start();
  681. if (elapsed_since_last_click < m_double_click_speed) {
  682. auto diff = event.position() - metadata.last_position;
  683. auto distance_travelled_squared = diff.x() * diff.x() + diff.y() * diff.y();
  684. if (distance_travelled_squared > (m_max_distance_for_double_click * m_max_distance_for_double_click)) {
  685. // too far; try again
  686. metadata.clock.start();
  687. } else {
  688. #if defined(DOUBLECLICK_DEBUG)
  689. dbg() << "Transforming MouseUp to MouseDoubleClick (" << elapsed_since_last_click << " < " << m_double_click_speed << ")!";
  690. #endif
  691. event = WSMouseEvent(WSEvent::MouseDoubleClick, event.position(), event.buttons(), event.button(), event.modifiers(), event.wheel_delta());
  692. // invalidate this now we've delivered a doubleclick, otherwise
  693. // tripleclick will deliver two doubleclick events (incorrectly).
  694. metadata.clock = {};
  695. }
  696. } else {
  697. // too slow; try again
  698. metadata.clock.start();
  699. }
  700. }
  701. metadata.last_position = event.position();
  702. }
  703. void WSWindowManager::deliver_mouse_event(WSWindow& window, WSMouseEvent& event)
  704. {
  705. window.dispatch_event(event);
  706. if (event.type() == WSEvent::MouseUp) {
  707. process_event_for_doubleclick(window, event);
  708. if (event.type() == WSEvent::MouseDoubleClick)
  709. window.dispatch_event(event);
  710. }
  711. }
  712. void WSWindowManager::process_mouse_event(WSMouseEvent& event, WSWindow*& hovered_window)
  713. {
  714. hovered_window = nullptr;
  715. if (process_ongoing_drag(event, hovered_window))
  716. return;
  717. if (process_ongoing_window_move(event, hovered_window))
  718. return;
  719. if (process_ongoing_window_resize(event, hovered_window))
  720. return;
  721. if (m_cursor_tracking_button)
  722. return m_cursor_tracking_button->on_mouse_event(event.translated(-m_cursor_tracking_button->screen_rect().location()));
  723. // This is quite hackish, but it's how the WSButton hover effect is implemented.
  724. if (m_hovered_button && event.type() == WSEvent::MouseMove)
  725. m_hovered_button->on_mouse_event(event.translated(-m_hovered_button->screen_rect().location()));
  726. HashTable<WSWindow*> windows_who_received_mouse_event_due_to_cursor_tracking;
  727. for (auto* window = m_windows_in_order.tail(); window; window = window->prev()) {
  728. if (!window->global_cursor_tracking())
  729. continue;
  730. ASSERT(window->is_visible()); // Maybe this should be supported? Idk. Let's catch it and think about it later.
  731. ASSERT(!window->is_minimized()); // Maybe this should also be supported? Idk.
  732. windows_who_received_mouse_event_due_to_cursor_tracking.set(window);
  733. auto translated_event = event.translated(-window->position());
  734. deliver_mouse_event(*window, translated_event);
  735. }
  736. // FIXME: Now that the menubar has a dedicated window, is this special-casing really necessary?
  737. if (!active_window_is_modal() && menubar_rect().contains(event.position())) {
  738. m_menu_manager.dispatch_event(event);
  739. return;
  740. }
  741. if (!menu_manager().open_menu_stack().is_empty()) {
  742. auto* topmost_menu = menu_manager().open_menu_stack().last().ptr();
  743. ASSERT(topmost_menu);
  744. auto* window = topmost_menu->menu_window();
  745. ASSERT(window);
  746. bool event_is_inside_current_menu = window->rect().contains(event.position());
  747. if (!event_is_inside_current_menu) {
  748. if (topmost_menu->hovered_item())
  749. topmost_menu->clear_hovered_item();
  750. if (event.type() == WSEvent::MouseDown || event.type() == WSEvent::MouseUp)
  751. m_menu_manager.close_bar();
  752. if (event.type() == WSEvent::MouseMove) {
  753. for (auto& menu : m_menu_manager.open_menu_stack()) {
  754. if (!menu)
  755. continue;
  756. if (!menu->menu_window()->rect().contains(event.position()))
  757. continue;
  758. hovered_window = menu->menu_window();
  759. auto translated_event = event.translated(-menu->menu_window()->position());
  760. deliver_mouse_event(*menu->menu_window(), translated_event);
  761. break;
  762. }
  763. }
  764. } else {
  765. hovered_window = window;
  766. auto translated_event = event.translated(-window->position());
  767. deliver_mouse_event(*window, translated_event);
  768. }
  769. return;
  770. }
  771. WSWindow* event_window_with_frame = nullptr;
  772. if (m_active_input_window) {
  773. // At this point, we have delivered the start of an input sequence to a
  774. // client application. We must keep delivering to that client
  775. // application until the input sequence is done.
  776. //
  777. // This prevents e.g. moving on one window out of the bounds starting
  778. // a move in that other unrelated window, and other silly shenanigans.
  779. if (!windows_who_received_mouse_event_due_to_cursor_tracking.contains(m_active_input_window)) {
  780. auto translated_event = event.translated(-m_active_input_window->position());
  781. deliver_mouse_event(*m_active_input_window, translated_event);
  782. windows_who_received_mouse_event_due_to_cursor_tracking.set(m_active_input_window.ptr());
  783. }
  784. if (event.type() == WSEvent::MouseUp && event.buttons() == 0) {
  785. m_active_input_window = nullptr;
  786. }
  787. for_each_visible_window_from_front_to_back([&](auto& window) {
  788. if (window.frame().rect().contains(event.position())) {
  789. hovered_window = &window;
  790. return IterationDecision::Break;
  791. }
  792. return IterationDecision::Continue;
  793. });
  794. } else {
  795. for_each_visible_window_from_front_to_back([&](WSWindow& window) {
  796. auto window_frame_rect = window.frame().rect();
  797. if (!window_frame_rect.contains(event.position()))
  798. return IterationDecision::Continue;
  799. if (&window != m_resize_candidate.ptr())
  800. clear_resize_candidate();
  801. // First check if we should initiate a move or resize (Logo+LMB or Logo+RMB).
  802. // In those cases, the event is swallowed by the window manager.
  803. if (window.is_movable()) {
  804. if (!window.is_fullscreen() && m_keyboard_modifiers == Mod_Logo && event.type() == WSEvent::MouseDown && event.button() == MouseButton::Left) {
  805. hovered_window = &window;
  806. start_window_move(window, event);
  807. return IterationDecision::Break;
  808. }
  809. if (window.is_resizable() && m_keyboard_modifiers == Mod_Logo && event.type() == WSEvent::MouseDown && event.button() == MouseButton::Right && !window.is_blocked_by_modal_window()) {
  810. hovered_window = &window;
  811. start_window_resize(window, event);
  812. return IterationDecision::Break;
  813. }
  814. }
  815. if (m_keyboard_modifiers == Mod_Logo && event.type() == WSEvent::MouseWheel) {
  816. float opacity_change = -event.wheel_delta() * 0.05f;
  817. float new_opacity = window.opacity() + opacity_change;
  818. if (new_opacity < 0.05f)
  819. new_opacity = 0.05f;
  820. if (new_opacity > 1.0f)
  821. new_opacity = 1.0f;
  822. window.set_opacity(new_opacity);
  823. window.invalidate();
  824. return IterationDecision::Break;
  825. }
  826. // Well okay, let's see if we're hitting the frame or the window inside the frame.
  827. if (window.rect().contains(event.position())) {
  828. if (window.type() == WSWindowType::Normal && event.type() == WSEvent::MouseDown)
  829. move_to_front_and_make_active(window);
  830. hovered_window = &window;
  831. if (!window.global_cursor_tracking() && !windows_who_received_mouse_event_due_to_cursor_tracking.contains(&window)) {
  832. auto translated_event = event.translated(-window.position());
  833. deliver_mouse_event(window, translated_event);
  834. if (event.type() == WSEvent::MouseDown) {
  835. m_active_input_window = window.make_weak_ptr();
  836. }
  837. }
  838. return IterationDecision::Break;
  839. }
  840. // We are hitting the frame, pass the event along to WSWindowFrame.
  841. window.frame().on_mouse_event(event.translated(-window_frame_rect.location()));
  842. event_window_with_frame = &window;
  843. return IterationDecision::Break;
  844. });
  845. // Clicked outside of any window
  846. if (!hovered_window && !event_window_with_frame && event.type() == WSEvent::MouseDown)
  847. set_active_window(nullptr);
  848. }
  849. if (event_window_with_frame != m_resize_candidate.ptr())
  850. clear_resize_candidate();
  851. }
  852. void WSWindowManager::clear_resize_candidate()
  853. {
  854. if (m_resize_candidate)
  855. WSCompositor::the().invalidate_cursor();
  856. m_resize_candidate = nullptr;
  857. }
  858. bool WSWindowManager::any_opaque_window_contains_rect(const Rect& rect)
  859. {
  860. bool found_containing_window = false;
  861. for_each_window([&](WSWindow& window) {
  862. if (!window.is_visible())
  863. return IterationDecision::Continue;
  864. if (window.is_minimized())
  865. return IterationDecision::Continue;
  866. if (window.opacity() < 1.0f)
  867. return IterationDecision::Continue;
  868. if (window.has_alpha_channel()) {
  869. // FIXME: Just because the window has an alpha channel doesn't mean it's not opaque.
  870. // Maybe there's some way we could know this?
  871. return IterationDecision::Continue;
  872. }
  873. if (window.frame().rect().contains(rect)) {
  874. found_containing_window = true;
  875. return IterationDecision::Break;
  876. }
  877. return IterationDecision::Continue;
  878. });
  879. return found_containing_window;
  880. };
  881. bool WSWindowManager::any_opaque_window_above_this_one_contains_rect(const WSWindow& a_window, const Rect& rect)
  882. {
  883. bool found_containing_window = false;
  884. bool checking = false;
  885. for_each_visible_window_from_back_to_front([&](WSWindow& window) {
  886. if (&window == &a_window) {
  887. checking = true;
  888. return IterationDecision::Continue;
  889. }
  890. if (!checking)
  891. return IterationDecision::Continue;
  892. if (!window.is_visible())
  893. return IterationDecision::Continue;
  894. if (window.is_minimized())
  895. return IterationDecision::Continue;
  896. if (window.opacity() < 1.0f)
  897. return IterationDecision::Continue;
  898. if (window.has_alpha_channel())
  899. return IterationDecision::Continue;
  900. if (window.frame().rect().contains(rect)) {
  901. found_containing_window = true;
  902. return IterationDecision::Break;
  903. }
  904. return IterationDecision::Continue;
  905. });
  906. return found_containing_window;
  907. };
  908. Rect WSWindowManager::menubar_rect() const
  909. {
  910. if (active_fullscreen_window())
  911. return {};
  912. return m_menu_manager.menubar_rect();
  913. }
  914. void WSWindowManager::draw_window_switcher()
  915. {
  916. if (m_switcher.is_visible())
  917. m_switcher.draw();
  918. }
  919. void WSWindowManager::event(CEvent& event)
  920. {
  921. if (static_cast<WSEvent&>(event).is_mouse_event()) {
  922. WSWindow* hovered_window = nullptr;
  923. process_mouse_event(static_cast<WSMouseEvent&>(event), hovered_window);
  924. set_hovered_window(hovered_window);
  925. return;
  926. }
  927. if (static_cast<WSEvent&>(event).is_key_event()) {
  928. auto& key_event = static_cast<const WSKeyEvent&>(event);
  929. m_keyboard_modifiers = key_event.modifiers();
  930. if (key_event.type() == WSEvent::KeyDown && key_event.key() == Key_Escape && m_dnd_client) {
  931. m_dnd_client->post_message(WindowClient::DragCancelled());
  932. end_dnd_drag();
  933. return;
  934. }
  935. if (key_event.type() == WSEvent::KeyDown && ((key_event.modifiers() == Mod_Logo && key_event.key() == Key_Tab) ||
  936. (key_event.modifiers() == (Mod_Logo | Mod_Shift) && key_event.key() == Key_Tab)))
  937. m_switcher.show();
  938. if (m_switcher.is_visible()) {
  939. m_switcher.on_key_event(key_event);
  940. return;
  941. }
  942. if (m_active_window)
  943. return m_active_window->dispatch_event(event);
  944. return;
  945. }
  946. CObject::event(event);
  947. }
  948. void WSWindowManager::set_highlight_window(WSWindow* window)
  949. {
  950. if (window == m_highlight_window)
  951. return;
  952. if (auto* previous_highlight_window = m_highlight_window.ptr())
  953. invalidate(*previous_highlight_window);
  954. m_highlight_window = window ? window->make_weak_ptr() : nullptr;
  955. if (m_highlight_window)
  956. invalidate(*m_highlight_window);
  957. }
  958. void WSWindowManager::set_active_window(WSWindow* window)
  959. {
  960. if (window && window->is_blocked_by_modal_window())
  961. return;
  962. if (window && window->type() != WSWindowType::Normal)
  963. return;
  964. if (window == m_active_window)
  965. return;
  966. auto* previously_active_window = m_active_window.ptr();
  967. WSClientConnection* previously_active_client = nullptr;
  968. WSClientConnection* active_client = nullptr;
  969. if (previously_active_window) {
  970. previously_active_client = previously_active_window->client();
  971. CEventLoop::current().post_event(*previously_active_window, make<WSEvent>(WSEvent::WindowDeactivated));
  972. invalidate(*previously_active_window);
  973. m_active_window = nullptr;
  974. tell_wm_listeners_window_state_changed(*previously_active_window);
  975. }
  976. if (window) {
  977. m_active_window = window->make_weak_ptr();
  978. active_client = m_active_window->client();
  979. CEventLoop::current().post_event(*m_active_window, make<WSEvent>(WSEvent::WindowActivated));
  980. invalidate(*m_active_window);
  981. auto* client = window->client();
  982. ASSERT(client);
  983. set_current_menubar(client->app_menubar());
  984. tell_wm_listeners_window_state_changed(*m_active_window);
  985. } else {
  986. set_current_menubar(nullptr);
  987. }
  988. if (active_client != previously_active_client) {
  989. if (previously_active_client)
  990. previously_active_client->deboost();
  991. if (active_client)
  992. active_client->boost();
  993. }
  994. }
  995. void WSWindowManager::set_hovered_window(WSWindow* window)
  996. {
  997. if (m_hovered_window == window)
  998. return;
  999. if (m_hovered_window)
  1000. CEventLoop::current().post_event(*m_hovered_window, make<WSEvent>(WSEvent::WindowLeft));
  1001. m_hovered_window = window ? window->make_weak_ptr() : nullptr;
  1002. if (m_hovered_window)
  1003. CEventLoop::current().post_event(*m_hovered_window, make<WSEvent>(WSEvent::WindowEntered));
  1004. }
  1005. void WSWindowManager::invalidate()
  1006. {
  1007. WSCompositor::the().invalidate();
  1008. }
  1009. void WSWindowManager::invalidate(const Rect& rect)
  1010. {
  1011. WSCompositor::the().invalidate(rect);
  1012. }
  1013. void WSWindowManager::invalidate(const WSWindow& window)
  1014. {
  1015. invalidate(window.frame().rect());
  1016. }
  1017. void WSWindowManager::invalidate(const WSWindow& window, const Rect& rect)
  1018. {
  1019. if (window.type() == WSWindowType::MenuApplet) {
  1020. menu_manager().invalidate_applet(window, rect);
  1021. return;
  1022. }
  1023. if (rect.is_empty()) {
  1024. invalidate(window);
  1025. return;
  1026. }
  1027. auto outer_rect = window.frame().rect();
  1028. auto inner_rect = rect;
  1029. inner_rect.move_by(window.position());
  1030. // FIXME: This seems slightly wrong; the inner rect shouldn't intersect the border part of the outer rect.
  1031. inner_rect.intersect(outer_rect);
  1032. invalidate(inner_rect);
  1033. }
  1034. void WSWindowManager::close_menubar(WSMenuBar& menubar)
  1035. {
  1036. if (current_menubar() == &menubar)
  1037. set_current_menubar(nullptr);
  1038. }
  1039. const WSClientConnection* WSWindowManager::active_client() const
  1040. {
  1041. if (m_active_window)
  1042. return m_active_window->client();
  1043. return nullptr;
  1044. }
  1045. void WSWindowManager::notify_client_changed_app_menubar(WSClientConnection& client)
  1046. {
  1047. if (active_client() == &client)
  1048. set_current_menubar(client.app_menubar());
  1049. m_menu_manager.refresh();
  1050. }
  1051. const WSCursor& WSWindowManager::active_cursor() const
  1052. {
  1053. if (m_dnd_client)
  1054. return *m_drag_cursor;
  1055. if (m_move_window)
  1056. return *m_move_cursor;
  1057. if (m_resize_window || m_resize_candidate) {
  1058. switch (m_resize_direction) {
  1059. case ResizeDirection::Up:
  1060. case ResizeDirection::Down:
  1061. return *m_resize_vertically_cursor;
  1062. case ResizeDirection::Left:
  1063. case ResizeDirection::Right:
  1064. return *m_resize_horizontally_cursor;
  1065. case ResizeDirection::UpLeft:
  1066. case ResizeDirection::DownRight:
  1067. return *m_resize_diagonally_tlbr_cursor;
  1068. case ResizeDirection::UpRight:
  1069. case ResizeDirection::DownLeft:
  1070. return *m_resize_diagonally_bltr_cursor;
  1071. case ResizeDirection::None:
  1072. break;
  1073. }
  1074. }
  1075. if (m_hovered_window && m_hovered_window->override_cursor())
  1076. return *m_hovered_window->override_cursor();
  1077. return *m_arrow_cursor;
  1078. }
  1079. void WSWindowManager::set_hovered_button(WSButton* button)
  1080. {
  1081. m_hovered_button = button ? button->make_weak_ptr() : nullptr;
  1082. }
  1083. void WSWindowManager::set_resize_candidate(WSWindow& window, ResizeDirection direction)
  1084. {
  1085. m_resize_candidate = window.make_weak_ptr();
  1086. m_resize_direction = direction;
  1087. }
  1088. ResizeDirection WSWindowManager::resize_direction_of_window(const WSWindow& window)
  1089. {
  1090. if (&window != m_resize_window)
  1091. return ResizeDirection::None;
  1092. return m_resize_direction;
  1093. }
  1094. Rect WSWindowManager::maximized_window_rect(const WSWindow& window) const
  1095. {
  1096. Rect rect = WSScreen::the().rect();
  1097. // Subtract window title bar (leaving the border)
  1098. rect.set_y(rect.y() + window.frame().title_bar_rect().height());
  1099. rect.set_height(rect.height() - window.frame().title_bar_rect().height());
  1100. // Subtract menu bar
  1101. rect.set_y(rect.y() + menubar_rect().height());
  1102. rect.set_height(rect.height() - menubar_rect().height());
  1103. // Subtract taskbar window height if present
  1104. const_cast<WSWindowManager*>(this)->for_each_visible_window_of_type_from_back_to_front(WSWindowType::Taskbar, [&rect](WSWindow& taskbar_window) {
  1105. rect.set_height(rect.height() - taskbar_window.height());
  1106. return IterationDecision::Break;
  1107. });
  1108. return rect;
  1109. }
  1110. WSMenu* WSWindowManager::find_internal_menu_by_id(int menu_id)
  1111. {
  1112. if (m_themes_menu->menu_id() == menu_id)
  1113. return m_themes_menu.ptr();
  1114. for (auto& it : m_app_category_menus) {
  1115. if (menu_id == it.value->menu_id())
  1116. return it.value;
  1117. }
  1118. return nullptr;
  1119. }
  1120. void WSWindowManager::start_dnd_drag(WSClientConnection& client, const String& text, GraphicsBitmap* bitmap, const String& data_type, const String& data)
  1121. {
  1122. ASSERT(!m_dnd_client);
  1123. m_dnd_client = client.make_weak_ptr();
  1124. m_dnd_text = text;
  1125. m_dnd_bitmap = bitmap;
  1126. m_dnd_data_type = data_type;
  1127. m_dnd_data = data;
  1128. WSCompositor::the().invalidate_cursor();
  1129. m_active_input_window = nullptr;
  1130. }
  1131. void WSWindowManager::end_dnd_drag()
  1132. {
  1133. ASSERT(m_dnd_client);
  1134. WSCompositor::the().invalidate_cursor();
  1135. m_dnd_client = nullptr;
  1136. m_dnd_text = {};
  1137. m_dnd_bitmap = nullptr;
  1138. }
  1139. Rect WSWindowManager::dnd_rect() const
  1140. {
  1141. int bitmap_width = m_dnd_bitmap ? m_dnd_bitmap->width() : 0;
  1142. int bitmap_height = m_dnd_bitmap ? m_dnd_bitmap->height() : 0;
  1143. int width = font().width(m_dnd_text) + bitmap_width;
  1144. int height = max((int)font().glyph_height(), bitmap_height);
  1145. auto location = WSCompositor::the().current_cursor_rect().center().translated(8, 8);
  1146. return Rect(location, { width, height }).inflated(4, 4);
  1147. }