WSWindowManager.cpp 45 KB

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