WSWindowManager.cpp 47 KB

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