2020-01-18 05:38:21 -03:00
/*
2021-02-16 12:10:39 -03:00
* Copyright ( c ) 2018 - 2021 , Andreas Kling < kling @ serenityos . org >
2020-01-18 05:38:21 -03:00
*
2021-04-22 05:24:48 -03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-01-18 05:38:21 -03:00
*/
2020-02-06 16:03:37 -03:00
# include "WindowManager.h"
# include "Compositor.h"
# include "EventLoop.h"
# include "Menu.h"
# include "Screen.h"
# include "Window.h"
2021-04-21 15:10:06 -03:00
# include <AK/Debug.h>
2019-05-24 14:32:46 -03:00
# include <AK/StdLibExtras.h>
# include <AK/Vector.h>
2021-01-18 18:26:45 -03:00
# include <LibGfx/Bitmap.h>
2020-02-06 08:04:00 -03:00
# include <LibGfx/CharacterBitmap.h>
# include <LibGfx/Font.h>
# include <LibGfx/StylePainter.h>
# include <LibGfx/SystemTheme.h>
2020-02-09 14:37:42 -03:00
# include <WindowServer/AppletManager.h>
2020-02-06 16:03:37 -03:00
# include <WindowServer/Button.h>
# include <WindowServer/ClientConnection.h>
# include <WindowServer/Cursor.h>
2019-12-08 12:50:23 -03:00
# include <WindowServer/WindowClientEndpoint.h>
2019-02-16 09:25:47 -02:00
2020-02-06 16:03:37 -03:00
namespace WindowServer {
2019-01-10 20:19:29 -02:00
2020-02-06 16:03:37 -03:00
static WindowManager * s_the ;
WindowManager & WindowManager : : the ( )
2018-10-11 11:52:40 -03:00
{
2021-02-23 16:42:32 -03:00
VERIFY ( s_the ) ;
2019-01-19 11:33:59 -02:00
return * s_the ;
2019-01-10 20:19:29 -02:00
}
2021-06-17 12:42:39 -03:00
WindowManager : : WindowManager ( Gfx : : PaletteImpl const & palette )
2019-12-24 16:57:54 -03:00
: m_palette ( palette )
2018-10-11 11:52:40 -03:00
{
2019-02-16 21:13:47 -02:00
s_the = this ;
2021-01-15 12:30:31 -03:00
reload_config ( ) ;
2019-02-11 06:47:10 -02:00
2020-12-28 06:56:22 -03:00
Compositor : : the ( ) . did_construct_window_manager ( { } ) ;
2019-05-25 13:26:23 -03:00
}
2020-02-06 16:03:37 -03:00
WindowManager : : ~ WindowManager ( )
2019-05-25 13:26:23 -03:00
{
}
2021-06-17 12:42:39 -03:00
NonnullRefPtr < Cursor > WindowManager : : get_cursor ( String const & name )
2019-05-26 14:14:03 -03:00
{
2021-06-17 12:42:39 -03:00
static auto const s_default_cursor_path = " /res/cursors/arrow.x2y2.png " ;
2020-12-17 02:15:14 -03:00
auto path = m_config - > read_entry ( " Cursor " , name , s_default_cursor_path ) ;
2021-01-18 18:26:45 -03:00
auto gb = Gfx : : Bitmap : : load_from_file ( path , compositor_icon_scale ( ) ) ;
2019-05-26 14:14:03 -03:00
if ( gb )
2020-12-17 02:15:14 -03:00
return Cursor : : create ( * gb , path ) ;
return Cursor : : create ( * Gfx : : Bitmap : : load_from_file ( s_default_cursor_path ) , s_default_cursor_path ) ;
2019-05-26 14:14:03 -03:00
}
2021-01-15 12:30:31 -03:00
void WindowManager : : reload_config ( )
2019-05-25 13:26:23 -03:00
{
2021-04-29 16:40:59 -03:00
m_config = Core : : ConfigFile : : open ( " /etc/WindowServer.ini " ) ;
2019-05-25 01:10:23 -03:00
2020-05-18 07:16:17 -03:00
m_double_click_speed = m_config - > read_num_entry ( " Input " , " DoubleClickSpeed " , 250 ) ;
2020-11-02 03:28:14 -03:00
m_hidden_cursor = get_cursor ( " Hidden " ) ;
2020-12-17 02:15:14 -03:00
m_arrow_cursor = get_cursor ( " Arrow " ) ;
m_hand_cursor = get_cursor ( " Hand " ) ;
m_help_cursor = get_cursor ( " Help " ) ;
2019-05-26 14:14:03 -03:00
m_resize_horizontally_cursor = get_cursor ( " ResizeH " ) ;
m_resize_vertically_cursor = get_cursor ( " ResizeV " ) ;
m_resize_diagonally_tlbr_cursor = get_cursor ( " ResizeDTLBR " ) ;
m_resize_diagonally_bltr_cursor = get_cursor ( " ResizeDBLTR " ) ;
2020-07-07 15:19:56 -03:00
m_resize_column_cursor = get_cursor ( " ResizeColumn " ) ;
m_resize_row_cursor = get_cursor ( " ResizeRow " ) ;
2019-05-26 14:14:03 -03:00
m_i_beam_cursor = get_cursor ( " IBeam " ) ;
m_disallowed_cursor = get_cursor ( " Disallowed " ) ;
m_move_cursor = get_cursor ( " Move " ) ;
2019-12-08 16:28:48 -03:00
m_drag_cursor = get_cursor ( " Drag " ) ;
2020-07-07 16:23:40 -03:00
m_wait_cursor = get_cursor ( " Wait " ) ;
2020-10-30 14:06:32 -03:00
m_crosshair_cursor = get_cursor ( " Crosshair " ) ;
2021-02-09 18:13:06 -03:00
WindowFrame : : reload_config ( ) ;
2018-10-11 11:52:40 -03:00
}
2021-06-17 12:42:39 -03:00
Gfx : : Font const & WindowManager : : font ( ) const
2019-03-09 17:24:12 -03:00
{
2020-12-29 14:25:13 -03:00
return Gfx : : FontDatabase : : default_font ( ) ;
2019-03-09 17:24:12 -03:00
}
2021-06-17 12:42:39 -03:00
Gfx : : Font const & WindowManager : : window_title_font ( ) const
2019-03-09 17:24:12 -03:00
{
2021-05-20 13:40:48 -03:00
return Gfx : : FontDatabase : : default_font ( ) . bold_variant ( ) ;
2019-03-09 17:24:12 -03:00
}
2021-01-15 16:53:53 -03:00
bool WindowManager : : set_resolution ( int width , int height , int scale )
2019-02-17 09:12:59 -03:00
{
2021-01-15 16:53:53 -03:00
bool success = Compositor : : the ( ) . set_resolution ( width , height , scale ) ;
2020-02-06 16:03:37 -03:00
ClientConnection : : for_each_client ( [ & ] ( ClientConnection & client ) {
client . notify_about_new_screen_rect ( Screen : : the ( ) . rect ( ) ) ;
2019-04-03 12:22:14 -03:00
} ) ;
2020-03-13 19:21:24 -03:00
if ( success ) {
2021-06-17 12:28:14 -03:00
m_window_stack . for_each_window ( [ ] ( Window & window ) {
2020-03-13 19:21:24 -03:00
window . recalculate_rect ( ) ;
return IterationDecision : : Continue ;
} ) ;
}
2020-05-18 07:16:17 -03:00
if ( m_config ) {
2020-02-27 12:09:41 -03:00
if ( success ) {
2021-04-29 16:46:15 -03:00
dbgln ( " Saving resolution: {} @ {}x to config file at {} " , Gfx : : IntSize ( width , height ) , scale , m_config - > filename ( ) ) ;
2020-05-18 07:16:17 -03:00
m_config - > write_num_entry ( " Screen " , " Width " , width ) ;
m_config - > write_num_entry ( " Screen " , " Height " , height ) ;
2021-01-15 16:53:53 -03:00
m_config - > write_num_entry ( " Screen " , " ScaleFactor " , scale ) ;
2020-05-18 07:16:17 -03:00
m_config - > sync ( ) ;
2020-02-27 12:09:41 -03:00
} else {
2021-04-29 16:46:15 -03:00
dbgln ( " Saving fallback resolution: {} @1x to config file at {} " , resolution ( ) , m_config - > filename ( ) ) ;
2020-05-18 07:16:17 -03:00
m_config - > write_num_entry ( " Screen " , " Width " , resolution ( ) . width ( ) ) ;
m_config - > write_num_entry ( " Screen " , " Height " , resolution ( ) . height ( ) ) ;
2021-01-15 16:53:53 -03:00
m_config - > write_num_entry ( " Screen " , " ScaleFactor " , 1 ) ;
2020-05-18 07:16:17 -03:00
m_config - > sync ( ) ;
2020-02-27 12:09:41 -03:00
}
2019-05-25 13:26:23 -03:00
}
2020-02-27 12:09:41 -03:00
return success ;
}
2020-06-10 05:57:59 -03:00
Gfx : : IntSize WindowManager : : resolution ( ) const
2020-02-27 12:09:41 -03:00
{
return Screen : : the ( ) . size ( ) ;
2019-02-17 09:12:59 -03:00
}
2020-12-29 18:32:54 -03:00
void WindowManager : : set_acceleration_factor ( double factor )
{
Screen : : the ( ) . set_acceleration_factor ( factor ) ;
2021-04-29 16:46:15 -03:00
dbgln ( " Saving acceleration factor {} to config file at {} " , factor , m_config - > filename ( ) ) ;
2020-12-29 18:32:54 -03:00
m_config - > write_entry ( " Mouse " , " AccelerationFactor " , String : : formatted ( " {} " , factor ) ) ;
m_config - > sync ( ) ;
}
void WindowManager : : set_scroll_step_size ( unsigned step_size )
{
Screen : : the ( ) . set_scroll_step_size ( step_size ) ;
2021-04-29 16:46:15 -03:00
dbgln ( " Saving scroll step size {} to config file at {} " , step_size , m_config - > filename ( ) ) ;
2020-12-29 18:32:54 -03:00
m_config - > write_entry ( " Mouse " , " ScrollStepSize " , String : : number ( step_size ) ) ;
m_config - > sync ( ) ;
}
2021-04-02 11:08:18 -03:00
void WindowManager : : set_double_click_speed ( int speed )
{
VERIFY ( speed > = double_click_speed_min & & speed < = double_click_speed_max ) ;
m_double_click_speed = speed ;
2021-04-29 16:46:15 -03:00
dbgln ( " Saving double-click speed {} to config file at {} " , speed , m_config - > filename ( ) ) ;
2021-04-02 11:08:18 -03:00
m_config - > write_entry ( " Input " , " DoubleClickSpeed " , String : : number ( speed ) ) ;
m_config - > sync ( ) ;
}
int WindowManager : : double_click_speed ( ) const
{
return m_double_click_speed ;
}
2021-01-15 16:53:53 -03:00
int WindowManager : : scale_factor ( ) const
{
return Screen : : the ( ) . scale_factor ( ) ;
}
2020-02-06 16:03:37 -03:00
void WindowManager : : add_window ( Window & window )
2018-10-11 11:52:40 -03:00
{
2021-06-17 12:28:14 -03:00
bool is_first_window = m_window_stack . is_empty ( ) ;
2020-04-19 06:59:11 -03:00
2021-06-17 13:58:33 -03:00
m_window_stack . add ( window ) ;
2019-05-17 16:33:44 -03:00
if ( window . is_fullscreen ( ) ) {
2020-08-22 08:10:35 -03:00
Core : : EventLoop : : current ( ) . post_event ( window , make < ResizeEvent > ( Screen : : the ( ) . rect ( ) ) ) ;
2020-02-06 16:03:37 -03:00
window . set_rect ( Screen : : the ( ) . rect ( ) ) ;
2019-05-17 16:33:44 -03:00
}
2020-04-19 06:59:11 -03:00
if ( window . type ( ) ! = WindowType : : Desktop | | is_first_window )
set_active_window ( & window ) ;
2020-02-06 16:03:37 -03:00
if ( m_switcher . is_visible ( ) & & window . type ( ) ! = WindowType : : WindowSwitcher )
2019-03-06 06:03:10 -03:00
m_switcher . refresh ( ) ;
2019-04-03 20:44:35 -03:00
2020-08-18 01:45:10 -03:00
Compositor : : the ( ) . invalidate_occlusions ( ) ;
2019-12-27 07:44:37 -03:00
2021-03-25 17:01:19 -03:00
window . invalidate ( true , true ) ;
2021-04-14 18:38:53 -03:00
tell_wms_window_state_changed ( window ) ;
2018-10-11 11:52:40 -03:00
}
2020-02-06 16:03:37 -03:00
void WindowManager : : move_to_front_and_make_active ( Window & window )
2019-01-09 00:16:58 -02:00
{
2020-07-15 17:21:42 -03:00
auto move_window_to_front = [ & ] ( Window & wnd , bool make_active , bool make_input ) {
if ( wnd . is_accessory ( ) ) {
auto * parent = wnd . parent_window ( ) ;
do_move_to_front ( * parent , true , false ) ;
make_active = false ;
2020-07-24 17:05:05 -03:00
2020-07-15 17:21:42 -03:00
for ( auto & accessory_window : parent - > accessory_windows ( ) ) {
if ( accessory_window & & accessory_window . ptr ( ) ! = & wnd )
do_move_to_front ( * accessory_window , false , false ) ;
}
}
2019-03-18 20:52:39 -03:00
2020-07-15 17:21:42 -03:00
do_move_to_front ( wnd , make_active , make_input ) ;
} ;
2020-07-15 20:40:52 -03:00
// If a window that is currently blocked by a modal child is being
// brought to the front, bring the entire stack of modal windows
// to the front and activate the modal window. Also set the
// active input window to that same window (which would pull
// active input from any accessory window)
for_each_window_in_modal_stack ( window , [ & ] ( auto & w , bool is_stack_top ) {
move_window_to_front ( w , is_stack_top , is_stack_top ) ;
2020-08-18 16:33:43 -03:00
return IterationDecision : : Continue ;
2020-07-15 20:40:52 -03:00
} ) ;
2020-08-18 01:45:10 -03:00
Compositor : : the ( ) . invalidate_occlusions ( ) ;
2020-07-14 22:17:00 -03:00
}
void WindowManager : : do_move_to_front ( Window & window , bool make_active , bool make_input )
{
2021-06-17 12:28:14 -03:00
m_window_stack . move_to_front ( window ) ;
2019-03-24 09:09:46 -03:00
2020-07-14 22:17:00 -03:00
if ( make_active )
set_active_window ( & window , make_input ) ;
2020-02-11 14:38:48 -03:00
if ( m_switcher . is_visible ( ) ) {
m_switcher . refresh ( ) ;
2020-07-14 22:17:00 -03:00
if ( ! window . is_accessory ( ) ) {
m_switcher . select_window ( window ) ;
set_highlight_window ( & window ) ;
}
2020-02-11 14:38:48 -03:00
}
2020-05-01 18:12:15 -03:00
for ( auto & child_window : window . child_windows ( ) ) {
if ( child_window )
2020-07-14 22:17:00 -03:00
do_move_to_front ( * child_window , make_active , make_input ) ;
2020-05-01 18:12:15 -03:00
}
2019-01-09 00:16:58 -02:00
}
2020-02-06 16:03:37 -03:00
void WindowManager : : remove_window ( Window & window )
2018-10-13 09:26:37 -03:00
{
2021-06-17 12:28:14 -03:00
m_window_stack . remove ( window ) ;
2020-07-16 14:08:39 -03:00
auto * active = active_window ( ) ;
auto * active_input = active_input_window ( ) ;
if ( active = = & window | | active_input = = & window | | ( active & & window . is_descendant_of ( * active ) ) | | ( active_input & & active_input ! = active & & window . is_descendant_of ( * active_input ) ) )
2020-07-15 13:13:13 -03:00
pick_new_active_window ( & window ) ;
2020-08-18 01:45:10 -03:00
2021-02-08 21:27:51 -03:00
Compositor : : the ( ) . invalidate_screen ( window . frame ( ) . render_rect ( ) ) ;
2020-08-18 01:45:10 -03:00
2020-02-06 16:03:37 -03:00
if ( m_switcher . is_visible ( ) & & window . type ( ) ! = WindowType : : WindowSwitcher )
2019-03-06 06:03:10 -03:00
m_switcher . refresh ( ) ;
2019-04-03 20:44:35 -03:00
2020-08-18 01:45:10 -03:00
Compositor : : the ( ) . invalidate_occlusions ( ) ;
2019-12-27 07:44:37 -03:00
2021-04-14 18:38:53 -03:00
for_each_window_manager ( [ & window ] ( WMClientConnection & conn ) {
if ( conn . window_id ( ) < 0 | | ! ( conn . event_mask ( ) & WMEventMask : : WindowRemovals ) )
2019-04-20 09:40:38 -03:00
return IterationDecision : : Continue ;
2020-07-15 20:40:52 -03:00
if ( ! window . is_internal ( ) & & ! window . is_modal ( ) )
2021-05-03 08:33:59 -03:00
conn . async_window_removed ( conn . window_id ( ) , window . client_id ( ) , window . window_id ( ) ) ;
2019-04-03 20:44:35 -03:00
return IterationDecision : : Continue ;
} ) ;
2018-10-13 09:26:37 -03:00
}
2021-04-14 18:38:53 -03:00
void WindowManager : : greet_window_manager ( WMClientConnection & conn )
2019-04-05 10:01:28 -03:00
{
2021-04-14 18:38:53 -03:00
if ( conn . window_id ( ) < 0 )
return ;
2021-06-17 12:28:14 -03:00
m_window_stack . for_each_window ( [ & ] ( Window & other_window ) {
2021-04-14 18:38:53 -03:00
//if (conn.window_id() != other_window.window_id()) {
tell_wm_about_window ( conn , other_window ) ;
tell_wm_about_window_icon ( conn , other_window ) ;
//}
return IterationDecision : : Continue ;
} ) ;
if ( auto * applet_area_window = AppletManager : : the ( ) . window ( ) )
tell_wms_applet_area_size_changed ( applet_area_window - > size ( ) ) ;
}
void WindowManager : : tell_wm_about_window ( WMClientConnection & conn , Window & window )
{
if ( conn . window_id ( ) < 0 )
return ;
if ( ! ( conn . event_mask ( ) & WMEventMask : : WindowStateChanges ) )
2019-04-20 09:40:38 -03:00
return ;
2020-01-25 06:25:49 -03:00
if ( window . is_internal ( ) )
2020-01-01 23:29:21 -03:00
return ;
2020-07-15 20:40:52 -03:00
auto * parent = window . parent_window ( ) ;
2021-05-09 19:00:40 -03:00
conn . async_window_state_changed ( conn . window_id ( ) , window . client_id ( ) , window . window_id ( ) , parent ? parent - > client_id ( ) : - 1 , parent ? parent - > window_id ( ) : - 1 , window . is_active ( ) , window . is_minimized ( ) , window . is_modal_dont_unparent ( ) , window . is_frameless ( ) , ( i32 ) window . type ( ) , window . computed_title ( ) , window . rect ( ) , window . progress ( ) ) ;
2019-04-05 10:01:28 -03:00
}
2021-04-14 18:38:53 -03:00
void WindowManager : : tell_wm_about_window_rect ( WMClientConnection & conn , Window & window )
2019-04-20 09:40:38 -03:00
{
2021-04-14 18:38:53 -03:00
if ( conn . window_id ( ) < 0 )
return ;
if ( ! ( conn . event_mask ( ) & WMEventMask : : WindowRectChanges ) )
2019-04-20 09:40:38 -03:00
return ;
2020-01-25 06:25:49 -03:00
if ( window . is_internal ( ) )
2020-01-01 23:29:21 -03:00
return ;
2021-05-03 08:33:59 -03:00
conn . async_window_rect_changed ( conn . window_id ( ) , window . client_id ( ) , window . window_id ( ) , window . rect ( ) ) ;
2019-04-20 09:40:38 -03:00
}
2021-04-14 18:38:53 -03:00
void WindowManager : : tell_wm_about_window_icon ( WMClientConnection & conn , Window & window )
2019-04-18 14:42:48 -03:00
{
2021-04-14 18:38:53 -03:00
if ( conn . window_id ( ) < 0 )
return ;
if ( ! ( conn . event_mask ( ) & WMEventMask : : WindowIconChanges ) )
2019-04-20 09:40:38 -03:00
return ;
2020-01-25 06:25:49 -03:00
if ( window . is_internal ( ) )
2020-01-01 23:29:21 -03:00
return ;
2021-05-03 08:33:59 -03:00
conn . async_window_icon_bitmap_changed ( conn . window_id ( ) , window . client_id ( ) , window . window_id ( ) , window . icon ( ) . to_shareable_bitmap ( ) ) ;
2019-04-18 14:42:48 -03:00
}
2021-04-14 18:38:53 -03:00
void WindowManager : : tell_wms_window_state_changed ( Window & window )
2019-04-04 08:19:26 -03:00
{
2021-04-14 18:38:53 -03:00
for_each_window_manager ( [ & ] ( WMClientConnection & conn ) {
tell_wm_about_window ( conn , window ) ;
2019-04-04 08:19:26 -03:00
return IterationDecision : : Continue ;
} ) ;
}
2021-04-14 18:38:53 -03:00
void WindowManager : : tell_wms_window_icon_changed ( Window & window )
2019-04-17 19:39:11 -03:00
{
2021-04-14 18:38:53 -03:00
for_each_window_manager ( [ & ] ( WMClientConnection & conn ) {
tell_wm_about_window_icon ( conn , window ) ;
2019-04-17 19:39:11 -03:00
return IterationDecision : : Continue ;
} ) ;
}
2021-04-14 18:38:53 -03:00
void WindowManager : : tell_wms_window_rect_changed ( Window & window )
2019-04-20 09:40:38 -03:00
{
2021-04-14 18:38:53 -03:00
for_each_window_manager ( [ & ] ( WMClientConnection & conn ) {
tell_wm_about_window_rect ( conn , window ) ;
2019-04-20 09:40:38 -03:00
return IterationDecision : : Continue ;
} ) ;
}
2021-06-17 12:42:39 -03:00
void WindowManager : : tell_wms_applet_area_size_changed ( Gfx : : IntSize const & size )
2021-03-30 17:41:14 -03:00
{
2021-04-14 18:38:53 -03:00
for_each_window_manager ( [ & ] ( WMClientConnection & conn ) {
if ( conn . window_id ( ) < 0 )
return IterationDecision : : Continue ;
2021-05-03 08:33:59 -03:00
conn . async_applet_area_size_changed ( conn . window_id ( ) , size ) ;
2021-03-30 17:41:14 -03:00
return IterationDecision : : Continue ;
} ) ;
}
2021-04-17 19:21:24 -03:00
void WindowManager : : tell_wms_super_key_pressed ( )
{
for_each_window_manager ( [ ] ( WMClientConnection & conn ) {
if ( conn . window_id ( ) < 0 )
return IterationDecision : : Continue ;
2021-05-03 08:33:59 -03:00
conn . async_super_key_pressed ( conn . window_id ( ) ) ;
2021-04-17 19:21:24 -03:00
return IterationDecision : : Continue ;
} ) ;
}
2021-02-16 12:10:39 -03:00
static bool window_type_has_title ( WindowType type )
{
return type = = WindowType : : Normal | | type = = WindowType : : ToolWindow ;
}
2021-05-09 18:56:24 -03:00
void WindowManager : : notify_modified_changed ( Window & window )
{
if ( m_switcher . is_visible ( ) )
m_switcher . refresh ( ) ;
tell_wms_window_state_changed ( window ) ;
}
2020-02-06 16:03:37 -03:00
void WindowManager : : notify_title_changed ( Window & window )
2018-10-11 11:52:40 -03:00
{
2021-02-16 12:10:39 -03:00
if ( ! window_type_has_title ( window . type ( ) ) )
2019-04-25 12:38:16 -03:00
return ;
2021-01-17 16:28:43 -03:00
2021-02-07 09:03:24 -03:00
dbgln_if ( WINDOWMANAGER_DEBUG , " [WM] Window({}) title set to '{}' " , & window , window . title ( ) ) ;
2021-01-17 16:28:43 -03:00
2019-03-03 11:17:05 -03:00
if ( m_switcher . is_visible ( ) )
2019-03-06 06:03:10 -03:00
m_switcher . refresh ( ) ;
2019-04-03 20:44:35 -03:00
2021-04-14 18:38:53 -03:00
tell_wms_window_state_changed ( window ) ;
2018-10-11 11:52:40 -03:00
}
2020-07-15 20:40:52 -03:00
void WindowManager : : notify_modal_unparented ( Window & window )
{
if ( window . type ( ) ! = WindowType : : Normal )
return ;
2021-01-17 16:28:43 -03:00
2021-02-07 09:03:24 -03:00
dbgln_if ( WINDOWMANAGER_DEBUG , " [WM] Window({}) was unparented " , & window ) ;
2021-01-17 16:28:43 -03:00
2020-07-15 20:40:52 -03:00
if ( m_switcher . is_visible ( ) )
m_switcher . refresh ( ) ;
2021-04-14 18:38:53 -03:00
tell_wms_window_state_changed ( window ) ;
2020-07-15 20:40:52 -03:00
}
2021-06-17 12:42:39 -03:00
void WindowManager : : notify_rect_changed ( Window & window , Gfx : : IntRect const & old_rect , Gfx : : IntRect const & new_rect )
2018-10-11 20:03:22 -03:00
{
2021-02-07 09:03:24 -03:00
dbgln_if ( RESIZE_DEBUG , " [WM] Window({}) rect changed {} -> {} " , & window , old_rect , new_rect ) ;
2021-01-17 16:28:43 -03:00
2020-02-06 16:03:37 -03:00
if ( m_switcher . is_visible ( ) & & window . type ( ) ! = WindowType : : WindowSwitcher )
2019-03-06 06:03:10 -03:00
m_switcher . refresh ( ) ;
2019-12-27 07:34:40 -03:00
2021-04-14 18:38:53 -03:00
tell_wms_window_rect_changed ( window ) ;
2019-12-15 14:07:48 -03:00
2021-04-04 12:55:50 -03:00
if ( window . type ( ) = = WindowType : : Applet )
2021-03-30 17:41:14 -03:00
AppletManager : : the ( ) . relayout ( ) ;
2020-07-24 17:05:05 -03:00
2020-02-06 16:03:37 -03:00
MenuManager : : the ( ) . refresh ( ) ;
2021-04-26 20:17:00 -03:00
reevaluate_hovered_window ( & window ) ;
2018-10-11 20:03:22 -03:00
}
2018-10-11 20:10:16 -03:00
2020-02-06 16:03:37 -03:00
void WindowManager : : notify_opacity_changed ( Window & )
2019-12-27 07:34:40 -03:00
{
2020-08-18 01:45:10 -03:00
Compositor : : the ( ) . invalidate_occlusions ( ) ;
2019-12-27 07:34:40 -03:00
}
2020-02-06 16:03:37 -03:00
void WindowManager : : notify_minimization_state_changed ( Window & window )
2019-04-05 19:57:51 -03:00
{
2021-04-14 18:38:53 -03:00
tell_wms_window_state_changed ( window ) ;
2019-04-06 15:50:38 -03:00
2019-12-26 08:06:07 -03:00
if ( window . client ( ) )
2021-05-03 08:33:59 -03:00
window . client ( ) - > async_window_state_changed ( window . window_id ( ) , window . is_minimized ( ) , window . is_occluded ( ) ) ;
2019-12-26 08:06:07 -03:00
2019-04-06 15:50:38 -03:00
if ( window . is_active ( ) & & window . is_minimized ( ) )
2020-07-15 13:13:13 -03:00
pick_new_active_window ( & window ) ;
2019-04-06 15:50:38 -03:00
}
2020-02-06 16:03:37 -03:00
void WindowManager : : notify_occlusion_state_changed ( Window & window )
2019-12-27 07:34:40 -03:00
{
if ( window . client ( ) )
2021-05-03 08:33:59 -03:00
window . client ( ) - > async_window_state_changed ( window . window_id ( ) , window . is_minimized ( ) , window . is_occluded ( ) ) ;
2019-12-27 07:34:40 -03:00
}
2020-05-30 17:08:26 -03:00
void WindowManager : : notify_progress_changed ( Window & window )
{
2021-04-14 18:38:53 -03:00
tell_wms_window_state_changed ( window ) ;
2020-05-30 17:08:26 -03:00
}
2020-07-15 13:13:13 -03:00
bool WindowManager : : pick_new_active_window ( Window * previous_active )
2019-04-06 15:50:38 -03:00
{
2020-01-04 08:04:36 -03:00
bool new_window_picked = false ;
2020-07-15 13:13:13 -03:00
Window * first_candidate = nullptr ;
2021-06-17 12:28:14 -03:00
m_window_stack . for_each_visible_window_from_front_to_back ( [ & ] ( Window & candidate ) {
2021-02-17 12:52:08 -03:00
if ( candidate . type ( ) ! = WindowType : : Normal & & candidate . type ( ) ! = WindowType : : ToolWindow )
2021-02-16 12:10:39 -03:00
return IterationDecision : : Continue ;
2020-07-16 14:08:39 -03:00
if ( candidate . is_destroyed ( ) )
return IterationDecision : : Continue ;
if ( previous_active ! = first_candidate )
first_candidate = & candidate ;
2020-07-15 13:13:13 -03:00
if ( ( ! previous_active & & ! candidate . is_accessory ( ) ) | | ( previous_active & & ! candidate . is_accessory_of ( * previous_active ) ) ) {
2020-07-14 22:17:00 -03:00
set_active_window ( & candidate ) ;
new_window_picked = true ;
2020-07-15 13:13:13 -03:00
return IterationDecision : : Break ;
2020-07-14 22:17:00 -03:00
}
2020-07-15 13:13:13 -03:00
return IterationDecision : : Continue ;
2019-04-06 15:50:38 -03:00
} ) ;
2020-07-15 13:13:13 -03:00
if ( ! new_window_picked ) {
set_active_window ( first_candidate ) ;
new_window_picked = first_candidate ! = nullptr ;
}
return new_window_picked ;
2019-04-05 19:57:51 -03:00
}
2021-06-17 12:42:39 -03:00
void WindowManager : : start_window_move ( Window & window , Gfx : : IntPoint const & origin )
2019-03-03 09:18:58 -03:00
{
2021-05-03 18:12:33 -03:00
MenuManager : : the ( ) . close_everyone ( ) ;
2021-02-07 09:03:24 -03:00
dbgln_if ( MOVE_DEBUG , " [WM] Begin moving Window({}) " , & window ) ;
2021-01-17 16:28:43 -03:00
2019-03-24 09:09:46 -03:00
move_to_front_and_make_active ( window ) ;
AK: Make RefPtr, NonnullRefPtr, WeakPtr thread safe
This makes most operations thread safe, especially so that they
can safely be used in the Kernel. This includes obtaining a strong
reference from a weak reference, which now requires an explicit
call to WeakPtr::strong_ref(). Another major change is that
Weakable::make_weak_ref() may require the explicit target type.
Previously we used reinterpret_cast in WeakPtr, assuming that it
can be properly converted. But WeakPtr does not necessarily have
the knowledge to be able to do this. Instead, we now ask the class
itself to deliver a WeakPtr to the type that we want.
Also, WeakLink is no longer specific to a target type. The reason
for this is that we want to be able to safely convert e.g. WeakPtr<T>
to WeakPtr<U>, and before this we just reinterpret_cast the internal
WeakLink<T> to WeakLink<U>, which is a bold assumption that it would
actually produce the correct code. Instead, WeakLink now operates
on just a raw pointer and we only make those constructors/operators
available if we can verify that it can be safely cast.
In order to guarantee thread safety, we now use the least significant
bit in the pointer for locking purposes. This also means that only
properly aligned pointers can be used.
2020-09-29 19:26:13 -03:00
m_move_window = window ;
2020-07-30 22:52:45 -03:00
m_move_window - > set_default_positioned ( false ) ;
2021-05-10 04:04:05 -03:00
m_move_origin = origin ;
2019-12-08 16:34:37 -03:00
m_move_window_origin = window . position ( ) ;
2021-02-10 15:54:58 -03:00
window . invalidate ( true , true ) ;
2019-03-03 09:18:58 -03:00
}
2021-06-17 12:42:39 -03:00
void WindowManager : : start_window_move ( Window & window , MouseEvent const & event )
2021-05-10 04:04:05 -03:00
{
start_window_move ( window , event . position ( ) ) ;
}
2021-06-17 12:42:39 -03:00
void WindowManager : : start_window_resize ( Window & window , Gfx : : IntPoint const & position , MouseButton button )
2019-02-20 11:34:55 -03:00
{
2021-05-03 18:12:33 -03:00
MenuManager : : the ( ) . close_everyone ( ) ;
2019-03-24 09:09:46 -03:00
move_to_front_and_make_active ( window ) ;
2019-02-21 07:06:37 -03:00
constexpr ResizeDirection direction_for_hot_area [ 3 ] [ 3 ] = {
{ ResizeDirection : : UpLeft , ResizeDirection : : Up , ResizeDirection : : UpRight } ,
{ ResizeDirection : : Left , ResizeDirection : : None , ResizeDirection : : Right } ,
{ ResizeDirection : : DownLeft , ResizeDirection : : Down , ResizeDirection : : DownRight } ,
} ;
2020-06-10 05:57:59 -03:00
Gfx : : IntRect outer_rect = window . frame ( ) . rect ( ) ;
2020-07-07 12:08:51 -03:00
if ( ! outer_rect . contains ( position ) ) {
2021-02-23 16:42:32 -03:00
// FIXME: This used to be an VERIFY but crashing WindowServer over this seems silly.
2021-01-17 16:28:43 -03:00
dbgln ( " FIXME: !outer_rect.contains(position): outer_rect={}, position={} " , outer_rect , position ) ;
2020-07-07 12:08:51 -03:00
}
2019-05-02 20:38:24 -03:00
int window_relative_x = position . x ( ) - outer_rect . x ( ) ;
int window_relative_y = position . y ( ) - outer_rect . y ( ) ;
2019-03-14 14:49:12 -03:00
int hot_area_row = min ( 2 , window_relative_y / ( outer_rect . height ( ) / 3 ) ) ;
int hot_area_column = min ( 2 , window_relative_x / ( outer_rect . width ( ) / 3 ) ) ;
2019-02-21 07:06:37 -03:00
m_resize_direction = direction_for_hot_area [ hot_area_row ] [ hot_area_column ] ;
2019-02-25 20:54:10 -03:00
if ( m_resize_direction = = ResizeDirection : : None ) {
2021-02-23 16:42:32 -03:00
VERIFY ( ! m_resize_window ) ;
2019-02-21 07:06:37 -03:00
return ;
2019-02-25 20:54:10 -03:00
}
2019-02-21 07:06:37 -03:00
2021-02-07 09:03:24 -03:00
dbgln_if ( RESIZE_DEBUG , " [WM] Begin resizing Window({}) " , & window ) ;
2021-01-17 16:28:43 -03:00
2019-05-02 20:38:24 -03:00
m_resizing_mouse_button = button ;
AK: Make RefPtr, NonnullRefPtr, WeakPtr thread safe
This makes most operations thread safe, especially so that they
can safely be used in the Kernel. This includes obtaining a strong
reference from a weak reference, which now requires an explicit
call to WeakPtr::strong_ref(). Another major change is that
Weakable::make_weak_ref() may require the explicit target type.
Previously we used reinterpret_cast in WeakPtr, assuming that it
can be properly converted. But WeakPtr does not necessarily have
the knowledge to be able to do this. Instead, we now ask the class
itself to deliver a WeakPtr to the type that we want.
Also, WeakLink is no longer specific to a target type. The reason
for this is that we want to be able to safely convert e.g. WeakPtr<T>
to WeakPtr<U>, and before this we just reinterpret_cast the internal
WeakLink<T> to WeakLink<U>, which is a bold assumption that it would
actually produce the correct code. Instead, WeakLink now operates
on just a raw pointer and we only make those constructors/operators
available if we can verify that it can be safely cast.
In order to guarantee thread safety, we now use the least significant
bit in the pointer for locking purposes. This also means that only
properly aligned pointers can be used.
2020-09-29 19:26:13 -03:00
m_resize_window = window ;
2019-05-02 20:38:24 -03:00
m_resize_origin = position ;
2019-02-20 11:34:55 -03:00
m_resize_window_original_rect = window . rect ( ) ;
2019-02-21 07:06:37 -03:00
2021-01-09 11:50:47 -03:00
m_active_input_tracking_window = nullptr ;
2021-02-10 15:54:58 -03:00
window . invalidate ( true , true ) ;
2020-07-30 22:52:45 -03:00
if ( hot_area_row = = 0 | | hot_area_column = = 0 ) {
m_resize_window - > set_default_positioned ( false ) ;
}
2019-02-20 11:34:55 -03:00
}
2021-06-17 12:42:39 -03:00
void WindowManager : : start_window_resize ( Window & window , MouseEvent const & event )
2019-05-02 20:38:24 -03:00
{
start_window_resize ( window , event . position ( ) , event . button ( ) ) ;
}
2020-02-06 16:03:37 -03:00
bool WindowManager : : process_ongoing_window_move ( MouseEvent & event , Window * & hovered_window )
2018-10-11 20:10:16 -03:00
{
2019-12-08 16:34:37 -03:00
if ( ! m_move_window )
2019-03-24 09:00:12 -03:00
return false ;
2020-02-06 16:03:37 -03:00
if ( event . type ( ) = = Event : : MouseUp & & event . button ( ) = = MouseButton : : Left ) {
2021-01-17 16:28:43 -03:00
2021-02-07 09:03:24 -03:00
dbgln_if ( MOVE_DEBUG , " [WM] Finish moving Window({}) " , m_move_window ) ;
2020-01-01 14:24:14 -03:00
2021-02-10 15:54:58 -03:00
m_move_window - > invalidate ( true , true ) ;
2019-12-08 16:34:37 -03:00
if ( m_move_window - > rect ( ) . contains ( event . position ( ) ) )
hovered_window = m_move_window ;
if ( m_move_window - > is_resizable ( ) ) {
process_event_for_doubleclick ( * m_move_window , event ) ;
2020-02-06 16:03:37 -03:00
if ( event . type ( ) = = Event : : MouseDoubleClick ) {
2021-04-21 15:10:06 -03:00
dbgln_if ( DOUBLECLICK_DEBUG , " [WM] Click up became doubleclick! " ) ;
2019-12-08 16:34:37 -03:00
m_move_window - > set_maximized ( ! m_move_window - > is_maximized ( ) ) ;
2019-05-18 10:42:26 -03:00
}
}
2019-12-08 16:34:37 -03:00
m_move_window = nullptr ;
2019-03-24 09:00:12 -03:00
return true ;
}
2020-02-06 16:03:37 -03:00
if ( event . type ( ) = = Event : : MouseMove ) {
2021-01-23 19:59:27 -03:00
if constexpr ( MOVE_DEBUG ) {
2021-01-17 16:28:43 -03:00
dbgln ( " [WM] Moving, origin: {}, now: {} " , m_move_origin , event . position ( ) ) ;
if ( m_move_window - > is_maximized ( ) )
dbgln ( " [!] The window is still maximized. Not moving yet. " ) ;
2019-11-10 14:28:01 -03:00
}
2020-01-01 14:24:14 -03:00
2020-12-04 22:26:21 -03:00
const int tiling_deadzone = 10 ;
const int secondary_deadzone = 2 ;
2021-01-22 22:45:03 -03:00
auto desktop = desktop_rect ( ) ;
2020-01-04 10:24:38 -03:00
2019-12-08 16:34:37 -03:00
if ( m_move_window - > is_maximized ( ) ) {
auto pixels_moved_from_start = event . position ( ) . pixels_moved ( m_move_origin ) ;
2019-11-10 14:28:01 -03:00
if ( pixels_moved_from_start > 5 ) {
2021-01-09 14:51:44 -03:00
// dbgln("[WM] de-maximizing window");
2019-12-08 16:34:37 -03:00
m_move_origin = event . position ( ) ;
2021-01-22 22:45:03 -03:00
if ( m_move_origin . y ( ) < = secondary_deadzone + desktop . top ( ) )
2020-01-04 10:24:38 -03:00
return true ;
2021-01-22 21:40:19 -03:00
m_move_window - > set_maximized ( false , event . position ( ) ) ;
2019-12-08 16:34:37 -03:00
m_move_window_origin = m_move_window - > position ( ) ;
2019-11-10 14:28:01 -03:00
}
} else {
2020-01-04 10:24:38 -03:00
bool is_resizable = m_move_window - > is_resizable ( ) ;
2020-01-01 14:24:14 -03:00
auto pixels_moved_from_start = event . position ( ) . pixels_moved ( m_move_origin ) ;
2020-01-04 10:24:38 -03:00
if ( is_resizable & & event . x ( ) < = tiling_deadzone ) {
2020-12-04 22:26:21 -03:00
if ( event . y ( ) < = tiling_deadzone + desktop . top ( ) )
m_move_window - > set_tiled ( WindowTileType : : TopLeft ) ;
else if ( event . y ( ) > = desktop . height ( ) - tiling_deadzone )
m_move_window - > set_tiled ( WindowTileType : : BottomLeft ) ;
else
m_move_window - > set_tiled ( WindowTileType : : Left ) ;
2020-02-06 16:03:37 -03:00
} else if ( is_resizable & & event . x ( ) > = Screen : : the ( ) . width ( ) - tiling_deadzone ) {
2020-12-04 22:26:21 -03:00
if ( event . y ( ) < = tiling_deadzone + desktop . top ( ) )
m_move_window - > set_tiled ( WindowTileType : : TopRight ) ;
else if ( event . y ( ) > = desktop . height ( ) - tiling_deadzone )
m_move_window - > set_tiled ( WindowTileType : : BottomRight ) ;
else
m_move_window - > set_tiled ( WindowTileType : : Right ) ;
} else if ( is_resizable & & event . y ( ) < = secondary_deadzone + desktop . top ( ) ) {
m_move_window - > set_tiled ( WindowTileType : : Top ) ;
} else if ( is_resizable & & event . y ( ) > = desktop . bottom ( ) - secondary_deadzone ) {
m_move_window - > set_tiled ( WindowTileType : : Bottom ) ;
2021-01-22 21:40:19 -03:00
} else if ( m_move_window - > tiled ( ) = = WindowTileType : : None ) {
2020-06-10 05:57:59 -03:00
Gfx : : IntPoint pos = m_move_window_origin . translated ( event . position ( ) - m_move_origin ) ;
2020-01-01 14:24:14 -03:00
m_move_window - > set_position_without_repaint ( pos ) ;
2021-01-28 17:14:25 -03:00
// "Bounce back" the window if it would end up too far outside the screen.
2021-03-11 14:50:23 -03:00
// If the user has let go of Mod_Super, maybe they didn't intentionally press it to begin with. Therefore, refuse to go into a state where knowledge about super-drags is necessary.
bool force_titlebar_visible = ! ( m_keyboard_modifiers & Mod_Super ) ;
2021-01-28 18:47:32 -03:00
m_move_window - > nudge_into_desktop ( force_titlebar_visible ) ;
2021-01-22 21:40:19 -03:00
} else if ( pixels_moved_from_start > 5 ) {
m_move_window - > set_untiled ( event . position ( ) ) ;
m_move_origin = event . position ( ) ;
m_move_window_origin = m_move_window - > position ( ) ;
2020-01-01 14:24:14 -03:00
}
2019-11-10 14:28:01 -03:00
}
2021-01-22 21:40:19 -03:00
if ( m_move_window - > rect ( ) . contains ( event . position ( ) ) )
hovered_window = m_move_window ;
2018-10-11 21:24:05 -03:00
}
2021-02-18 01:15:01 -03:00
return true ;
2019-03-24 09:00:12 -03:00
}
2021-06-17 12:42:39 -03:00
bool WindowManager : : process_ongoing_window_resize ( MouseEvent const & event , Window * & hovered_window )
2019-03-24 09:00:12 -03:00
{
if ( ! m_resize_window )
return false ;
2018-10-11 21:24:05 -03:00
2020-02-06 16:03:37 -03:00
if ( event . type ( ) = = Event : : MouseUp & & event . button ( ) = = m_resizing_mouse_button ) {
2021-02-07 09:03:24 -03:00
dbgln_if ( RESIZE_DEBUG , " [WM] Finish resizing Window({}) " , m_resize_window ) ;
2021-01-17 16:28:43 -03:00
2021-01-26 17:50:02 -03:00
auto max_rect = maximized_window_rect ( * m_resize_window ) ;
if ( event . y ( ) > max_rect . bottom ( ) ) {
2021-02-23 19:35:43 -03:00
dbgln_if ( RESIZE_DEBUG , " Should Maximize vertically " ) ;
2021-01-26 17:50:02 -03:00
m_resize_window - > set_vertically_maximized ( ) ;
m_resize_window = nullptr ;
m_resizing_mouse_button = MouseButton : : None ;
return true ;
}
2020-08-22 08:10:35 -03:00
Core : : EventLoop : : current ( ) . post_event ( * m_resize_window , make < ResizeEvent > ( m_resize_window - > rect ( ) ) ) ;
2021-02-10 15:54:58 -03:00
m_resize_window - > invalidate ( true , true ) ;
2019-05-02 20:38:24 -03:00
if ( m_resize_window - > rect ( ) . contains ( event . position ( ) ) )
hovered_window = m_resize_window ;
2019-03-24 09:00:12 -03:00
m_resize_window = nullptr ;
2019-04-06 18:20:06 -03:00
m_resizing_mouse_button = MouseButton : : None ;
2019-03-24 09:00:12 -03:00
return true ;
}
2019-02-20 11:34:55 -03:00
2020-02-06 16:03:37 -03:00
if ( event . type ( ) ! = Event : : MouseMove )
2021-02-18 01:15:01 -03:00
return true ;
2019-02-21 07:06:37 -03:00
2019-03-24 09:00:12 -03:00
int diff_x = event . x ( ) - m_resize_origin . x ( ) ;
int diff_y = event . y ( ) - m_resize_origin . y ( ) ;
int change_w = 0 ;
int change_h = 0 ;
switch ( m_resize_direction ) {
case ResizeDirection : : DownRight :
change_w = diff_x ;
change_h = diff_y ;
break ;
case ResizeDirection : : Right :
change_w = diff_x ;
break ;
case ResizeDirection : : UpRight :
change_w = diff_x ;
change_h = - diff_y ;
break ;
case ResizeDirection : : Up :
change_h = - diff_y ;
break ;
case ResizeDirection : : UpLeft :
change_w = - diff_x ;
change_h = - diff_y ;
break ;
case ResizeDirection : : Left :
change_w = - diff_x ;
break ;
case ResizeDirection : : DownLeft :
change_w = - diff_x ;
change_h = diff_y ;
break ;
case ResizeDirection : : Down :
change_h = diff_y ;
break ;
default :
2021-02-23 16:42:32 -03:00
VERIFY_NOT_REACHED ( ) ;
2019-03-24 09:00:12 -03:00
}
2019-02-21 07:06:37 -03:00
2019-03-24 09:00:12 -03:00
auto new_rect = m_resize_window_original_rect ;
2019-11-22 13:06:25 -03:00
// First, size the new rect.
2021-01-28 18:47:32 -03:00
new_rect . set_width ( new_rect . width ( ) + change_w ) ;
new_rect . set_height ( new_rect . height ( ) + change_h ) ;
m_resize_window - > apply_minimum_size ( new_rect ) ;
2019-02-20 20:21:23 -03:00
2019-03-24 09:00:12 -03:00
if ( ! m_resize_window - > size_increment ( ) . is_null ( ) ) {
int horizontal_incs = ( new_rect . width ( ) - m_resize_window - > base_size ( ) . width ( ) ) / m_resize_window - > size_increment ( ) . width ( ) ;
new_rect . set_width ( m_resize_window - > base_size ( ) . width ( ) + horizontal_incs * m_resize_window - > size_increment ( ) . width ( ) ) ;
int vertical_incs = ( new_rect . height ( ) - m_resize_window - > base_size ( ) . height ( ) ) / m_resize_window - > size_increment ( ) . height ( ) ;
new_rect . set_height ( m_resize_window - > base_size ( ) . height ( ) + vertical_incs * m_resize_window - > size_increment ( ) . height ( ) ) ;
2019-11-22 13:06:25 -03:00
}
2020-08-21 17:19:10 -03:00
if ( m_resize_window - > resize_aspect_ratio ( ) . has_value ( ) ) {
auto & ratio = m_resize_window - > resize_aspect_ratio ( ) . value ( ) ;
2021-05-17 07:01:17 -03:00
auto base_size = m_resize_window - > base_size ( ) ;
2020-08-21 17:19:10 -03:00
if ( abs ( change_w ) > abs ( change_h ) ) {
2021-05-17 07:01:17 -03:00
new_rect . set_height ( base_size . height ( ) + ( new_rect . width ( ) - base_size . width ( ) ) * ratio . height ( ) / ratio . width ( ) ) ;
2020-08-21 17:19:10 -03:00
} else {
2021-05-17 07:01:17 -03:00
new_rect . set_width ( base_size . width ( ) + ( new_rect . height ( ) - base_size . height ( ) ) * ratio . width ( ) / ratio . height ( ) ) ;
2020-08-21 17:19:10 -03:00
}
}
2019-11-22 13:06:25 -03:00
// Second, set its position so that the sides of the window
// that end up moving are the same ones as the user is dragging,
// no matter which part of the logic above caused us to decide
// to resize by this much.
switch ( m_resize_direction ) {
case ResizeDirection : : DownRight :
case ResizeDirection : : Right :
case ResizeDirection : : Down :
break ;
case ResizeDirection : : Left :
case ResizeDirection : : Up :
case ResizeDirection : : UpLeft :
new_rect . set_right_without_resize ( m_resize_window_original_rect . right ( ) ) ;
new_rect . set_bottom_without_resize ( m_resize_window_original_rect . bottom ( ) ) ;
break ;
case ResizeDirection : : UpRight :
new_rect . set_bottom_without_resize ( m_resize_window_original_rect . bottom ( ) ) ;
break ;
case ResizeDirection : : DownLeft :
new_rect . set_right_without_resize ( m_resize_window_original_rect . right ( ) ) ;
break ;
default :
2021-02-23 16:42:32 -03:00
VERIFY_NOT_REACHED ( ) ;
2019-03-24 09:00:12 -03:00
}
2019-05-02 20:38:24 -03:00
if ( new_rect . contains ( event . position ( ) ) )
hovered_window = m_resize_window ;
2019-03-24 09:00:12 -03:00
if ( m_resize_window - > rect ( ) = = new_rect )
return true ;
2021-01-17 16:28:43 -03:00
2021-02-07 09:03:24 -03:00
dbgln_if ( RESIZE_DEBUG , " [WM] Resizing, original: {}, now: {} " , m_resize_window_original_rect , new_rect ) ;
2021-01-17 16:28:43 -03:00
2019-03-24 09:00:12 -03:00
m_resize_window - > set_rect ( new_rect ) ;
2020-08-22 08:10:35 -03:00
Core : : EventLoop : : current ( ) . post_event ( * m_resize_window , make < ResizeEvent > ( new_rect ) ) ;
2019-03-24 09:00:12 -03:00
return true ;
}
2020-02-06 16:03:37 -03:00
bool WindowManager : : process_ongoing_drag ( MouseEvent & event , Window * & hovered_window )
2019-12-08 12:50:23 -03:00
{
if ( ! m_dnd_client )
return false ;
2020-02-13 17:43:32 -03:00
if ( event . type ( ) = = Event : : MouseMove ) {
// We didn't let go of the drag yet, see if we should send some drag move events..
2021-06-17 12:28:14 -03:00
m_window_stack . for_each_visible_window_from_front_to_back ( [ & ] ( Window & window ) {
2020-02-13 17:43:32 -03:00
if ( ! window . rect ( ) . contains ( event . position ( ) ) )
return IterationDecision : : Continue ;
hovered_window = & window ;
auto translated_event = event . translated ( - window . position ( ) ) ;
translated_event . set_drag ( true ) ;
2020-11-07 16:44:21 -03:00
translated_event . set_mime_data ( * m_dnd_mime_data ) ;
2021-02-14 17:48:28 -03:00
deliver_mouse_event ( window , translated_event , false ) ;
2020-02-13 17:43:32 -03:00
return IterationDecision : : Break ;
} ) ;
}
2020-02-06 16:03:37 -03:00
if ( ! ( event . type ( ) = = Event : : MouseUp & & event . button ( ) = = MouseButton : : Left ) )
2019-12-08 12:50:23 -03:00
return true ;
hovered_window = nullptr ;
2021-06-17 12:28:14 -03:00
m_window_stack . for_each_visible_window_from_front_to_back ( [ & ] ( auto & window ) {
2021-02-14 20:42:37 -03:00
if ( window . hit_test ( event . position ( ) ) ) {
2019-12-08 12:50:23 -03:00
hovered_window = & window ;
return IterationDecision : : Break ;
}
return IterationDecision : : Continue ;
} ) ;
if ( hovered_window ) {
2021-05-03 08:33:59 -03:00
m_dnd_client - > async_drag_accepted ( ) ;
2019-12-08 12:50:23 -03:00
if ( hovered_window - > client ( ) ) {
auto translated_event = event . translated ( - hovered_window - > position ( ) ) ;
2021-05-03 08:33:59 -03:00
hovered_window - > client ( ) - > async_drag_dropped ( hovered_window - > window_id ( ) , translated_event . position ( ) , m_dnd_text , m_dnd_mime_data - > all_data ( ) ) ;
2019-12-08 12:50:23 -03:00
}
} else {
2021-05-03 08:33:59 -03:00
m_dnd_client - > async_drag_cancelled ( ) ;
2019-12-08 12:50:23 -03:00
}
end_dnd_drag ( ) ;
return true ;
}
2020-02-06 16:03:37 -03:00
void WindowManager : : set_cursor_tracking_button ( Button * button )
2019-04-06 16:16:41 -03:00
{
AK: Make RefPtr, NonnullRefPtr, WeakPtr thread safe
This makes most operations thread safe, especially so that they
can safely be used in the Kernel. This includes obtaining a strong
reference from a weak reference, which now requires an explicit
call to WeakPtr::strong_ref(). Another major change is that
Weakable::make_weak_ref() may require the explicit target type.
Previously we used reinterpret_cast in WeakPtr, assuming that it
can be properly converted. But WeakPtr does not necessarily have
the knowledge to be able to do this. Instead, we now ask the class
itself to deliver a WeakPtr to the type that we want.
Also, WeakLink is no longer specific to a target type. The reason
for this is that we want to be able to safely convert e.g. WeakPtr<T>
to WeakPtr<U>, and before this we just reinterpret_cast the internal
WeakLink<T> to WeakLink<U>, which is a bold assumption that it would
actually produce the correct code. Instead, WeakLink now operates
on just a raw pointer and we only make those constructors/operators
available if we can verify that it can be safely cast.
In order to guarantee thread safety, we now use the least significant
bit in the pointer for locking purposes. This also means that only
properly aligned pointers can be used.
2020-09-29 19:26:13 -03:00
m_cursor_tracking_button = button ;
2019-04-06 16:16:41 -03:00
}
2021-06-17 12:42:39 -03:00
auto WindowManager : : DoubleClickInfo : : metadata_for_button ( MouseButton button ) const - > ClickMetadata const &
2020-07-07 15:09:18 -03:00
{
switch ( button ) {
case MouseButton : : Left :
return m_left ;
case MouseButton : : Right :
return m_right ;
case MouseButton : : Middle :
return m_middle ;
case MouseButton : : Back :
return m_back ;
case MouseButton : : Forward :
return m_forward ;
default :
2021-02-23 16:42:32 -03:00
VERIFY_NOT_REACHED ( ) ;
2020-07-07 15:09:18 -03:00
}
}
2020-02-06 16:03:37 -03:00
auto WindowManager : : DoubleClickInfo : : metadata_for_button ( MouseButton button ) - > ClickMetadata &
2019-05-15 17:17:09 -03:00
{
switch ( button ) {
2019-06-07 06:47:19 -03:00
case MouseButton : : Left :
2019-09-08 05:15:08 -03:00
return m_left ;
2019-06-07 06:47:19 -03:00
case MouseButton : : Right :
2019-09-08 05:15:08 -03:00
return m_right ;
2019-06-07 06:47:19 -03:00
case MouseButton : : Middle :
2019-09-08 05:15:08 -03:00
return m_middle ;
2020-05-02 17:07:43 -03:00
case MouseButton : : Back :
return m_back ;
case MouseButton : : Forward :
return m_forward ;
2019-05-15 17:17:09 -03:00
default :
2021-02-23 16:42:32 -03:00
VERIFY_NOT_REACHED ( ) ;
2019-05-15 17:17:09 -03:00
}
}
2021-06-17 12:42:39 -03:00
bool WindowManager : : is_considered_doubleclick ( MouseEvent const & event , DoubleClickInfo : : ClickMetadata const & metadata ) const
2020-07-07 15:09:18 -03:00
{
int elapsed_since_last_click = metadata . clock . elapsed ( ) ;
if ( elapsed_since_last_click < m_double_click_speed ) {
auto diff = event . position ( ) - metadata . last_position ;
auto distance_travelled_squared = diff . x ( ) * diff . x ( ) + diff . y ( ) * diff . y ( ) ;
if ( distance_travelled_squared < = ( m_max_distance_for_double_click * m_max_distance_for_double_click ) )
return true ;
}
return false ;
}
2021-06-17 12:42:39 -03:00
void WindowManager : : start_menu_doubleclick ( Window & window , MouseEvent const & event )
2020-07-07 15:09:18 -03:00
{
// This is a special case. Basically, we're trying to determine whether
// double clicking on the window menu icon happened. In this case, the
// WindowFrame only receives a MouseDown event, and since the window
// menu popus up, it does not see the MouseUp event. But, if they subsequently
// click there again, the menu is closed and we receive a MouseUp event.
// So, in order to be able to detect a double click when a menu is being
// opened by the MouseDown event, we need to consider the MouseDown event
// as a potential double-click trigger
2021-02-23 16:42:32 -03:00
VERIFY ( event . type ( ) = = Event : : MouseDown ) ;
2020-07-07 15:09:18 -03:00
auto & metadata = m_double_click_info . metadata_for_button ( event . button ( ) ) ;
if ( & window ! = m_double_click_info . m_clicked_window ) {
// we either haven't clicked anywhere, or we haven't clicked on this
// window. set the current click window, and reset the timers.
2021-01-17 16:28:43 -03:00
2021-02-07 09:03:24 -03:00
dbgln_if ( DOUBLECLICK_DEBUG , " Initial mousedown on Window({}) for menus (previous was {}) " , & window , m_double_click_info . m_clicked_window ) ;
2021-01-17 16:28:43 -03:00
AK: Make RefPtr, NonnullRefPtr, WeakPtr thread safe
This makes most operations thread safe, especially so that they
can safely be used in the Kernel. This includes obtaining a strong
reference from a weak reference, which now requires an explicit
call to WeakPtr::strong_ref(). Another major change is that
Weakable::make_weak_ref() may require the explicit target type.
Previously we used reinterpret_cast in WeakPtr, assuming that it
can be properly converted. But WeakPtr does not necessarily have
the knowledge to be able to do this. Instead, we now ask the class
itself to deliver a WeakPtr to the type that we want.
Also, WeakLink is no longer specific to a target type. The reason
for this is that we want to be able to safely convert e.g. WeakPtr<T>
to WeakPtr<U>, and before this we just reinterpret_cast the internal
WeakLink<T> to WeakLink<U>, which is a bold assumption that it would
actually produce the correct code. Instead, WeakLink now operates
on just a raw pointer and we only make those constructors/operators
available if we can verify that it can be safely cast.
In order to guarantee thread safety, we now use the least significant
bit in the pointer for locking purposes. This also means that only
properly aligned pointers can be used.
2020-09-29 19:26:13 -03:00
m_double_click_info . m_clicked_window = window ;
2020-07-07 15:09:18 -03:00
m_double_click_info . reset ( ) ;
}
metadata . last_position = event . position ( ) ;
metadata . clock . start ( ) ;
}
2021-06-17 12:42:39 -03:00
bool WindowManager : : is_menu_doubleclick ( Window & window , MouseEvent const & event ) const
2020-07-07 15:09:18 -03:00
{
2021-02-23 16:42:32 -03:00
VERIFY ( event . type ( ) = = Event : : MouseUp ) ;
2020-07-07 15:09:18 -03:00
if ( & window ! = m_double_click_info . m_clicked_window )
return false ;
auto & metadata = m_double_click_info . metadata_for_button ( event . button ( ) ) ;
if ( ! metadata . clock . is_valid ( ) )
return false ;
2020-07-24 17:05:05 -03:00
2020-07-07 15:09:18 -03:00
return is_considered_doubleclick ( event , metadata ) ;
}
2020-02-06 16:03:37 -03:00
void WindowManager : : process_event_for_doubleclick ( Window & window , MouseEvent & event )
2019-05-15 17:17:09 -03:00
{
2019-05-18 10:42:26 -03:00
// We only care about button presses (because otherwise it's not a doubleclick, duh!)
2021-02-23 16:42:32 -03:00
VERIFY ( event . type ( ) = = Event : : MouseUp ) ;
2019-05-18 10:42:26 -03:00
if ( & window ! = m_double_click_info . m_clicked_window ) {
// we either haven't clicked anywhere, or we haven't clicked on this
// window. set the current click window, and reset the timers.
2021-02-07 09:03:24 -03:00
dbgln_if ( DOUBLECLICK_DEBUG , " Initial mouseup on Window({}) for menus (previous was {}) " , & window , m_double_click_info . m_clicked_window ) ;
2021-01-17 16:28:43 -03:00
AK: Make RefPtr, NonnullRefPtr, WeakPtr thread safe
This makes most operations thread safe, especially so that they
can safely be used in the Kernel. This includes obtaining a strong
reference from a weak reference, which now requires an explicit
call to WeakPtr::strong_ref(). Another major change is that
Weakable::make_weak_ref() may require the explicit target type.
Previously we used reinterpret_cast in WeakPtr, assuming that it
can be properly converted. But WeakPtr does not necessarily have
the knowledge to be able to do this. Instead, we now ask the class
itself to deliver a WeakPtr to the type that we want.
Also, WeakLink is no longer specific to a target type. The reason
for this is that we want to be able to safely convert e.g. WeakPtr<T>
to WeakPtr<U>, and before this we just reinterpret_cast the internal
WeakLink<T> to WeakLink<U>, which is a bold assumption that it would
actually produce the correct code. Instead, WeakLink now operates
on just a raw pointer and we only make those constructors/operators
available if we can verify that it can be safely cast.
In order to guarantee thread safety, we now use the least significant
bit in the pointer for locking purposes. This also means that only
properly aligned pointers can be used.
2020-09-29 19:26:13 -03:00
m_double_click_info . m_clicked_window = window ;
2019-05-18 10:42:26 -03:00
m_double_click_info . reset ( ) ;
}
2019-05-15 17:17:09 -03:00
2019-09-08 05:15:08 -03:00
auto & metadata = m_double_click_info . metadata_for_button ( event . button ( ) ) ;
2019-05-15 17:17:09 -03:00
2020-07-07 15:09:18 -03:00
if ( ! metadata . clock . is_valid ( ) | | ! is_considered_doubleclick ( event , metadata ) ) {
// either the clock is invalid because we haven't clicked on this
// button on this window yet, so there's nothing to do, or this
// isn't considered to be a double click. either way, restart the
// clock
2019-09-08 05:15:08 -03:00
metadata . clock . start ( ) ;
2019-05-18 10:42:26 -03:00
} else {
2021-02-07 09:03:24 -03:00
dbgln_if ( DOUBLECLICK_DEBUG , " Transforming MouseUp to MouseDoubleClick ({} < {})! " , metadata . clock . elapsed ( ) , m_double_click_speed ) ;
2021-01-17 16:28:43 -03:00
2020-07-07 15:09:18 -03:00
event = MouseEvent ( Event : : MouseDoubleClick , event . position ( ) , event . buttons ( ) , event . button ( ) , event . modifiers ( ) , event . wheel_delta ( ) ) ;
// invalidate this now we've delivered a doubleclick, otherwise
// tripleclick will deliver two doubleclick events (incorrectly).
metadata . clock = { } ;
2019-05-15 17:17:09 -03:00
}
2019-09-08 05:15:08 -03:00
metadata . last_position = event . position ( ) ;
2019-05-18 10:42:26 -03:00
}
2019-05-15 17:17:09 -03:00
2021-02-14 17:48:28 -03:00
void WindowManager : : deliver_mouse_event ( Window & window , MouseEvent & event , bool process_double_click )
2019-05-18 10:42:26 -03:00
{
2019-09-20 15:37:31 -03:00
window . dispatch_event ( event ) ;
2021-02-14 17:48:28 -03:00
if ( process_double_click & & event . type ( ) = = Event : : MouseUp ) {
2019-05-18 10:42:26 -03:00
process_event_for_doubleclick ( window , event ) ;
2020-02-06 16:03:37 -03:00
if ( event . type ( ) = = Event : : MouseDoubleClick )
2019-09-20 15:37:31 -03:00
window . dispatch_event ( event ) ;
2019-05-18 10:42:26 -03:00
}
2019-05-15 17:17:09 -03:00
}
2020-02-06 16:03:37 -03:00
void WindowManager : : process_mouse_event ( MouseEvent & event , Window * & hovered_window )
2019-03-24 09:00:12 -03:00
{
2021-02-14 17:48:28 -03:00
Window * received_mouse_event = nullptr ;
2020-08-13 09:48:23 -03:00
2020-08-19 10:14:49 -03:00
// We need to process ongoing drag events first. Otherwise, global tracking
// and dnd collides, leading to duplicate GUI::DragOperation instances
if ( process_ongoing_drag ( event , hovered_window ) )
return ;
2019-05-02 20:38:24 -03:00
hovered_window = nullptr ;
2019-03-24 09:00:12 -03:00
2019-12-08 16:34:37 -03:00
if ( process_ongoing_window_move ( event , hovered_window ) )
2019-03-24 09:00:12 -03:00
return ;
2019-05-02 20:38:24 -03:00
if ( process_ongoing_window_resize ( event , hovered_window ) )
2019-03-24 09:00:12 -03:00
return ;
2019-02-20 11:34:55 -03:00
2019-04-05 16:53:45 -03:00
if ( m_cursor_tracking_button )
return m_cursor_tracking_button - > on_mouse_event ( event . translated ( - m_cursor_tracking_button - > screen_rect ( ) . location ( ) ) ) ;
2020-02-06 16:03:37 -03:00
// This is quite hackish, but it's how the Button hover effect is implemented.
if ( m_hovered_button & & event . type ( ) = = Event : : MouseMove )
2019-04-05 23:08:09 -03:00
m_hovered_button - > on_mouse_event ( event . translated ( - m_hovered_button - > screen_rect ( ) . location ( ) ) ) ;
2021-03-25 17:01:19 -03:00
bool hitting_menu_in_window_with_active_menu = [ & ] {
if ( ! m_window_with_active_menu )
return false ;
auto & frame = m_window_with_active_menu - > frame ( ) ;
return frame . menubar_rect ( ) . contains ( event . position ( ) . translated ( - frame . rect ( ) . location ( ) ) ) ;
} ( ) ;
// FIXME: This is quite hackish, we clear the hovered menu before potentially setting the same menu
// as hovered again. This makes sure that the hovered state doesn't linger after moving the
// cursor away from a hovered menu.
MenuManager : : the ( ) . set_hovered_menu ( nullptr ) ;
if ( MenuManager : : the ( ) . has_open_menu ( )
2021-03-25 19:27:11 -03:00
| | hitting_menu_in_window_with_active_menu ) {
2021-06-17 12:28:14 -03:00
m_window_stack . for_each_visible_window_of_type_from_front_to_back ( WindowType : : Applet , [ & ] ( auto & window ) {
2021-03-30 17:41:14 -03:00
if ( ! window . rect_in_applet_area ( ) . contains ( event . position ( ) ) )
2020-08-13 17:44:11 -03:00
return IterationDecision : : Continue ;
hovered_window = & window ;
return IterationDecision : : Break ;
} ) ;
2020-03-01 10:17:35 -03:00
clear_resize_candidate ( ) ;
2021-03-25 17:01:19 -03:00
if ( ! hitting_menu_in_window_with_active_menu ) {
MenuManager : : the ( ) . dispatch_event ( event ) ;
return ;
}
2020-01-08 09:13:08 -03:00
}
2020-02-06 16:03:37 -03:00
Window * event_window_with_frame = nullptr ;
2019-04-06 18:20:06 -03:00
2020-07-14 22:17:00 -03:00
if ( m_active_input_tracking_window ) {
2019-06-28 10:11:08 -03:00
// At this point, we have delivered the start of an input sequence to a
// client application. We must keep delivering to that client
// application until the input sequence is done.
//
2019-12-08 16:34:37 -03:00
// This prevents e.g. moving on one window out of the bounds starting
// a move in that other unrelated window, and other silly shenanigans.
2021-02-14 17:48:28 -03:00
auto translated_event = event . translated ( - m_active_input_tracking_window - > position ( ) ) ;
deliver_mouse_event ( * m_active_input_tracking_window , translated_event , true ) ;
received_mouse_event = m_active_input_tracking_window . ptr ( ) ;
2020-02-06 16:03:37 -03:00
if ( event . type ( ) = = Event : : MouseUp & & event . buttons ( ) = = 0 ) {
2020-07-14 22:17:00 -03:00
m_active_input_tracking_window = nullptr ;
2019-06-28 10:11:08 -03:00
}
2019-08-12 13:36:33 -03:00
2021-06-17 12:28:14 -03:00
m_window_stack . for_each_visible_window_from_front_to_back ( [ & ] ( auto & window ) {
2021-02-14 20:42:37 -03:00
if ( window . hit_test ( event . position ( ) ) ) {
2019-08-12 13:36:33 -03:00
hovered_window = & window ;
return IterationDecision : : Break ;
}
return IterationDecision : : Continue ;
} ) ;
2019-06-28 10:11:08 -03:00
} else {
2020-05-19 13:12:31 -03:00
auto process_mouse_event_for_window = [ & ] ( Window & window ) {
2019-06-28 10:11:08 -03:00
if ( & window ! = m_resize_candidate . ptr ( ) )
clear_resize_candidate ( ) ;
2021-03-11 14:50:23 -03:00
// First check if we should initiate a move or resize (Super+LMB or Super+RMB).
2019-06-28 10:11:08 -03:00
// In those cases, the event is swallowed by the window manager.
2019-07-31 12:49:40 -03:00
if ( window . is_movable ( ) ) {
2021-03-11 14:50:23 -03:00
if ( ! window . is_fullscreen ( ) & & m_keyboard_modifiers = = Mod_Super & & event . type ( ) = = Event : : MouseDown & & event . button ( ) = = MouseButton : : Left ) {
2019-06-28 10:11:08 -03:00
hovered_window = & window ;
2019-12-08 16:34:37 -03:00
start_window_move ( window , event ) ;
2020-05-19 13:12:31 -03:00
return ;
2019-06-28 10:11:08 -03:00
}
2021-03-11 14:50:23 -03:00
if ( window . is_resizable ( ) & & m_keyboard_modifiers = = Mod_Super & & event . type ( ) = = Event : : MouseDown & & event . button ( ) = = MouseButton : : Right & & ! window . blocking_modal_window ( ) ) {
2019-06-28 10:11:08 -03:00
hovered_window = & window ;
start_window_resize ( window , event ) ;
2020-05-19 13:12:31 -03:00
return ;
2019-06-28 10:11:08 -03:00
}
2019-03-03 09:18:58 -03:00
}
2019-06-28 10:11:08 -03:00
2021-03-11 14:50:23 -03:00
if ( m_keyboard_modifiers = = Mod_Super & & event . type ( ) = = Event : : MouseWheel ) {
2019-06-29 04:27:55 -03:00
float opacity_change = - event . wheel_delta ( ) * 0.05f ;
float new_opacity = window . opacity ( ) + opacity_change ;
if ( new_opacity < 0.05f )
new_opacity = 0.05f ;
if ( new_opacity > 1.0f )
new_opacity = 1.0f ;
window . set_opacity ( new_opacity ) ;
2020-05-19 13:12:31 -03:00
return ;
2019-06-29 04:27:55 -03:00
}
2019-05-15 17:17:09 -03:00
2021-02-23 16:42:32 -03:00
VERIFY ( window . hit_test ( event . position ( ) ) ) ;
2021-01-08 02:17:45 -03:00
if ( event . type ( ) = = Event : : MouseDown ) {
// We're clicking on something that's blocked by a modal window.
// Flash the modal window to let the user know about it.
2021-01-07 16:12:17 -03:00
if ( auto * blocking_modal_window = window . blocking_modal_window ( ) )
2021-01-08 02:17:45 -03:00
blocking_modal_window - > frame ( ) . start_flash_animation ( ) ;
2021-02-16 12:10:39 -03:00
if ( window . type ( ) = = WindowType : : Normal | | window . type ( ) = = WindowType : : ToolWindow )
2021-01-08 02:17:45 -03:00
move_to_front_and_make_active ( window ) ;
else if ( window . type ( ) = = WindowType : : Desktop )
set_active_window ( & window ) ;
}
2021-02-14 20:42:37 -03:00
if ( window . frame ( ) . hit_test ( event . position ( ) ) ) {
// We are hitting the frame, pass the event along to WindowFrame.
window . frame ( ) . on_mouse_event ( event . translated ( - window . frame ( ) . rect ( ) . location ( ) ) ) ;
event_window_with_frame = & window ;
} else if ( window . hit_test ( event . position ( ) , false ) ) {
// We are hitting the window content
2019-06-28 10:11:08 -03:00
hovered_window = & window ;
2021-02-14 17:48:28 -03:00
if ( ! window . global_cursor_tracking ( ) & & ! window . blocking_modal_window ( ) ) {
2019-06-28 10:11:08 -03:00
auto translated_event = event . translated ( - window . position ( ) ) ;
2021-02-14 17:48:28 -03:00
deliver_mouse_event ( window , translated_event , true ) ;
received_mouse_event = & window ;
2020-02-06 16:03:37 -03:00
if ( event . type ( ) = = Event : : MouseDown ) {
AK: Make RefPtr, NonnullRefPtr, WeakPtr thread safe
This makes most operations thread safe, especially so that they
can safely be used in the Kernel. This includes obtaining a strong
reference from a weak reference, which now requires an explicit
call to WeakPtr::strong_ref(). Another major change is that
Weakable::make_weak_ref() may require the explicit target type.
Previously we used reinterpret_cast in WeakPtr, assuming that it
can be properly converted. But WeakPtr does not necessarily have
the knowledge to be able to do this. Instead, we now ask the class
itself to deliver a WeakPtr to the type that we want.
Also, WeakLink is no longer specific to a target type. The reason
for this is that we want to be able to safely convert e.g. WeakPtr<T>
to WeakPtr<U>, and before this we just reinterpret_cast the internal
WeakLink<T> to WeakLink<U>, which is a bold assumption that it would
actually produce the correct code. Instead, WeakLink now operates
on just a raw pointer and we only make those constructors/operators
available if we can verify that it can be safely cast.
In order to guarantee thread safety, we now use the least significant
bit in the pointer for locking purposes. This also means that only
properly aligned pointers can be used.
2020-09-29 19:26:13 -03:00
m_active_input_tracking_window = window ;
2019-06-28 10:11:08 -03:00
}
}
2019-04-14 00:15:22 -03:00
}
2020-05-19 13:12:31 -03:00
} ;
if ( auto * fullscreen_window = active_fullscreen_window ( ) ) {
process_mouse_event_for_window ( * fullscreen_window ) ;
} else {
2021-06-17 12:28:14 -03:00
m_window_stack . for_each_visible_window_from_front_to_back ( [ & ] ( Window & window ) {
2021-02-14 20:42:37 -03:00
if ( ! window . hit_test ( event . position ( ) ) )
2020-05-19 13:12:31 -03:00
return IterationDecision : : Continue ;
process_mouse_event_for_window ( window ) ;
return IterationDecision : : Break ;
} ) ;
}
2019-12-30 21:38:56 -03:00
// Clicked outside of any window
2020-02-06 16:03:37 -03:00
if ( ! hovered_window & & ! event_window_with_frame & & event . type ( ) = = Event : : MouseDown )
2019-12-30 21:38:56 -03:00
set_active_window ( nullptr ) ;
2019-06-28 10:11:08 -03:00
}
2019-04-06 18:20:06 -03:00
2021-06-17 12:28:14 -03:00
auto reverse_iterator = m_window_stack . windows ( ) . rbegin ( ) ;
for ( ; reverse_iterator ! = m_window_stack . windows ( ) . rend ( ) ; + + reverse_iterator ) {
2021-06-03 07:23:01 -03:00
auto & window = * reverse_iterator ;
if ( received_mouse_event = = & window )
2021-02-14 17:48:28 -03:00
continue ;
2021-06-03 07:23:01 -03:00
if ( ! window . global_cursor_tracking ( ) | | ! window . is_visible ( ) | | window . is_minimized ( ) | | window . blocking_modal_window ( ) )
2021-02-14 17:48:28 -03:00
continue ;
2021-06-03 07:23:01 -03:00
auto translated_event = event . translated ( - window . position ( ) ) ;
deliver_mouse_event ( window , translated_event , false ) ;
2021-02-14 17:48:28 -03:00
}
2019-04-06 18:20:06 -03:00
if ( event_window_with_frame ! = m_resize_candidate . ptr ( ) )
clear_resize_candidate ( ) ;
}
2021-02-21 15:56:59 -03:00
void WindowManager : : reevaluate_hovered_window ( Window * updated_window )
{
if ( m_dnd_client | | m_resize_window | | m_move_window | | m_cursor_tracking_button | | MenuManager : : the ( ) . has_open_menu ( ) )
return ;
auto cursor_location = Screen : : the ( ) . cursor_location ( ) ;
auto * currently_hovered = hovered_window ( ) ;
if ( updated_window ) {
if ( ! ( updated_window = = currently_hovered | | updated_window - > frame ( ) . rect ( ) . contains ( cursor_location ) | | ( currently_hovered & & currently_hovered - > frame ( ) . rect ( ) . contains ( cursor_location ) ) ) )
return ;
}
Window * hovered_window = nullptr ;
if ( auto * fullscreen_window = active_fullscreen_window ( ) ) {
if ( fullscreen_window - > hit_test ( cursor_location ) )
hovered_window = fullscreen_window ;
} else {
2021-06-17 12:28:14 -03:00
m_window_stack . for_each_visible_window_from_front_to_back ( [ & ] ( Window & window ) {
2021-02-21 15:56:59 -03:00
if ( ! window . hit_test ( cursor_location ) )
return IterationDecision : : Continue ;
hovered_window = & window ;
return IterationDecision : : Break ;
} ) ;
}
if ( set_hovered_window ( hovered_window ) ) {
if ( currently_hovered & & m_resize_candidate = = currently_hovered )
clear_resize_candidate ( ) ;
if ( hovered_window ) {
// Send a fake MouseMove event. This allows the new hovering window
// to determine which widget we're hovering, and also update the cursor
// accordingly. We do this because this re-evaluation of the currently
// hovered window wasn't triggered by a mouse move event, but rather
// e.g. a hit-test result change due to a transparent window repaint.
if ( hovered_window - > hit_test ( cursor_location , false ) ) {
MouseEvent event ( Event : : MouseMove , cursor_location . translated ( - hovered_window - > rect ( ) . location ( ) ) , 0 , MouseButton : : None , 0 ) ;
2021-03-28 06:18:05 -03:00
hovered_window - > dispatch_event ( event ) ;
2021-02-21 15:56:59 -03:00
} else if ( ! hovered_window - > is_frameless ( ) ) {
MouseEvent event ( Event : : MouseMove , cursor_location . translated ( - hovered_window - > frame ( ) . rect ( ) . location ( ) ) , 0 , MouseButton : : None , 0 ) ;
hovered_window - > frame ( ) . on_mouse_event ( event ) ;
}
}
}
}
2020-02-06 16:03:37 -03:00
void WindowManager : : clear_resize_candidate ( )
2019-04-06 18:20:06 -03:00
{
if ( m_resize_candidate )
2020-02-06 16:03:37 -03:00
Compositor : : the ( ) . invalidate_cursor ( ) ;
2019-04-06 18:20:06 -03:00
m_resize_candidate = nullptr ;
2018-10-11 20:20:06 -03:00
}
2020-06-10 05:57:59 -03:00
Gfx : : IntRect WindowManager : : desktop_rect ( ) const
2020-04-18 16:18:11 -03:00
{
if ( active_fullscreen_window ( ) )
2021-01-28 16:51:37 -03:00
return Screen : : the ( ) . rect ( ) ;
2020-04-18 16:18:11 -03:00
return {
0 ,
2021-03-25 19:27:11 -03:00
0 ,
2020-04-18 16:18:11 -03:00
Screen : : the ( ) . width ( ) ,
2021-03-25 19:27:11 -03:00
Screen : : the ( ) . height ( ) - 28
2020-04-18 16:18:11 -03:00
} ;
}
2021-01-28 16:51:37 -03:00
Gfx : : IntRect WindowManager : : arena_rect_for_type ( WindowType type ) const
{
switch ( type ) {
case WindowType : : Desktop :
case WindowType : : Normal :
2021-02-16 12:10:39 -03:00
case WindowType : : ToolWindow :
2021-01-28 16:51:37 -03:00
return desktop_rect ( ) ;
case WindowType : : Menu :
case WindowType : : WindowSwitcher :
case WindowType : : Taskbar :
case WindowType : : Tooltip :
2021-04-04 12:55:50 -03:00
case WindowType : : Applet :
2021-01-28 16:51:37 -03:00
case WindowType : : Notification :
return Screen : : the ( ) . rect ( ) ;
default :
2021-02-23 16:42:32 -03:00
VERIFY_NOT_REACHED ( ) ;
2021-01-28 16:51:37 -03:00
}
}
2020-02-06 16:03:37 -03:00
void WindowManager : : event ( Core : : Event & event )
2018-10-11 20:20:06 -03:00
{
2020-02-06 16:03:37 -03:00
if ( static_cast < Event & > ( event ) . is_mouse_event ( ) ) {
2021-04-17 19:21:24 -03:00
if ( event . type ( ) ! = Event : : MouseMove )
m_previous_event_was_super_keydown = false ;
2020-02-06 16:03:37 -03:00
Window * hovered_window = nullptr ;
process_mouse_event ( static_cast < MouseEvent & > ( event ) , hovered_window ) ;
2019-05-02 20:38:24 -03:00
set_hovered_window ( hovered_window ) ;
2019-02-20 06:12:19 -03:00
return ;
}
2018-10-11 20:20:06 -03:00
2020-02-06 16:03:37 -03:00
if ( static_cast < Event & > ( event ) . is_key_event ( ) ) {
2021-06-17 12:42:39 -03:00
auto & key_event = static_cast < KeyEvent const & > ( event ) ;
2019-03-03 11:17:05 -03:00
m_keyboard_modifiers = key_event . modifiers ( ) ;
2021-01-08 19:47:53 -03:00
// Escape key cancels an ongoing drag.
2020-02-06 16:03:37 -03:00
if ( key_event . type ( ) = = Event : : KeyDown & & key_event . key ( ) = = Key_Escape & & m_dnd_client ) {
2021-01-08 19:47:53 -03:00
// Notify the drag-n-drop client that the drag was cancelled.
2021-05-03 08:33:59 -03:00
m_dnd_client - > async_drag_cancelled ( ) ;
2021-01-08 19:47:53 -03:00
// Also notify the currently hovered window (if any) that the ongoing drag was cancelled.
if ( m_hovered_window & & m_hovered_window - > client ( ) & & m_hovered_window - > client ( ) ! = m_dnd_client )
2021-05-03 08:33:59 -03:00
m_hovered_window - > client ( ) - > async_drag_cancelled ( ) ;
2021-01-08 19:47:53 -03:00
2019-12-08 12:50:23 -03:00
end_dnd_drag ( ) ;
return ;
}
2021-03-11 14:50:23 -03:00
if ( key_event . type ( ) = = Event : : KeyDown & & ( key_event . modifiers ( ) = = ( Mod_Ctrl | Mod_Super | Mod_Shift ) & & key_event . key ( ) = = Key_I ) ) {
2021-01-18 18:26:45 -03:00
reload_icon_bitmaps_after_scale_change ( ! m_allow_hidpi_icons ) ;
Compositor : : the ( ) . invalidate_screen ( ) ;
return ;
}
2021-04-17 19:21:24 -03:00
if ( key_event . type ( ) = = Event : : KeyDown & & key_event . key ( ) = = Key_Super ) {
m_previous_event_was_super_keydown = true ;
} else if ( m_previous_event_was_super_keydown ) {
m_previous_event_was_super_keydown = false ;
if ( ! m_dnd_client & & key_event . type ( ) = = Event : : KeyUp & & key_event . key ( ) = = Key_Super ) {
tell_wms_super_key_pressed ( ) ;
return ;
}
}
if ( MenuManager : : the ( ) . current_menu ( ) & & key_event . key ( ) ! = Key_Super ) {
2020-02-06 16:03:37 -03:00
MenuManager : : the ( ) . dispatch_event ( event ) ;
2020-01-09 04:13:08 -03:00
return ;
}
2021-03-11 14:50:23 -03:00
if ( key_event . type ( ) = = Event : : KeyDown & & ( ( key_event . modifiers ( ) = = Mod_Super & & key_event . key ( ) = = Key_Tab ) | | ( key_event . modifiers ( ) = = ( Mod_Super | Mod_Shift ) & & key_event . key ( ) = = Key_Tab ) ) )
2019-03-03 11:17:05 -03:00
m_switcher . show ( ) ;
if ( m_switcher . is_visible ( ) ) {
m_switcher . on_key_event ( key_event ) ;
return ;
}
2020-01-04 10:30:11 -03:00
2020-07-14 22:17:00 -03:00
if ( m_active_input_window ) {
2021-04-05 11:39:18 -03:00
if ( key_event . type ( ) = = Event : : KeyDown & & key_event . modifiers ( ) = = Mod_Super & & m_active_input_window - > type ( ) ! = WindowType : : Desktop ) {
2020-01-04 10:30:11 -03:00
if ( key_event . key ( ) = = Key_Down ) {
2020-07-14 22:17:00 -03:00
if ( m_active_input_window - > is_resizable ( ) & & m_active_input_window - > is_maximized ( ) ) {
2020-08-18 13:41:54 -03:00
maximize_windows ( * m_active_input_window , false ) ;
2020-01-04 10:30:11 -03:00
return ;
}
2020-07-14 22:17:00 -03:00
if ( m_active_input_window - > is_minimizable ( ) )
2020-08-18 13:41:54 -03:00
minimize_windows ( * m_active_input_window , true ) ;
2020-01-04 10:30:11 -03:00
return ;
}
2020-07-14 22:17:00 -03:00
if ( m_active_input_window - > is_resizable ( ) ) {
2020-01-04 10:30:11 -03:00
if ( key_event . key ( ) = = Key_Up ) {
2020-08-18 13:41:54 -03:00
maximize_windows ( * m_active_input_window , ! m_active_input_window - > is_maximized ( ) ) ;
2020-01-04 10:30:11 -03:00
return ;
}
if ( key_event . key ( ) = = Key_Left ) {
2021-01-30 15:57:27 -03:00
if ( m_active_input_window - > tiled ( ) = = WindowTileType : : Left )
return ;
2020-07-14 22:17:00 -03:00
if ( m_active_input_window - > tiled ( ) ! = WindowTileType : : None ) {
2021-01-30 15:57:27 -03:00
m_active_input_window - > set_untiled ( ) ;
2020-01-04 10:30:11 -03:00
return ;
}
2020-07-14 22:17:00 -03:00
if ( m_active_input_window - > is_maximized ( ) )
2020-08-18 13:41:54 -03:00
maximize_windows ( * m_active_input_window , false ) ;
2020-07-14 22:17:00 -03:00
m_active_input_window - > set_tiled ( WindowTileType : : Left ) ;
2020-01-04 10:30:11 -03:00
return ;
}
if ( key_event . key ( ) = = Key_Right ) {
2021-01-30 15:57:27 -03:00
if ( m_active_input_window - > tiled ( ) = = WindowTileType : : Right )
return ;
2020-07-14 22:17:00 -03:00
if ( m_active_input_window - > tiled ( ) ! = WindowTileType : : None ) {
2021-01-30 15:57:27 -03:00
m_active_input_window - > set_untiled ( ) ;
2020-01-04 10:30:11 -03:00
return ;
}
2020-07-14 22:17:00 -03:00
if ( m_active_input_window - > is_maximized ( ) )
2020-08-18 13:41:54 -03:00
maximize_windows ( * m_active_input_window , false ) ;
2020-07-14 22:17:00 -03:00
m_active_input_window - > set_tiled ( WindowTileType : : Right ) ;
2020-01-04 10:30:11 -03:00
return ;
}
}
}
2020-07-14 22:17:00 -03:00
m_active_input_window - > dispatch_event ( event ) ;
WSMenu: Support menu navigation through key presses
Add event handling for key presses for navigating a menu. The currently
hovered menu item is tracked through an index which is either
incremented or decremented on up or down arrow key presses, changing the
hovered item.
Whenever there is a mouse move event, we ensure that the current index
matches the currently hovered item so that the mouse and keyboard do not
get out of sync.
If the right key is pressed, and we are on a submenu menu item, we
'enter' that submenu. While we are currently in a submenu, we forward
all keypress events to that submenu for handling. This allows us to
traverse the heirachy of a menu. While in a submenu, if the left key is
pressed, we leave that submenu and start handling the keypresses
ourselves again.
There is currently a small issue where the mouse hover and key hover can
get out of sync. The mouse can be traversing a submenu, but the parent
menu has no idea that the mouse has 'entered' a submenu, so will handle
the key presses itself, instead of forwarding them to the submenu. One
potential fix for this is for a menu to tell its menu parent that the
submenu is being traversed.
2020-01-02 22:11:49 -03:00
return ;
}
2018-10-11 20:10:16 -03:00
}
2020-02-02 08:34:39 -03:00
Core : : Object : : event ( event ) ;
2018-10-11 20:10:16 -03:00
}
2021-06-17 12:28:14 -03:00
void WindowManager : : set_highlight_window ( Window * new_highlight_window )
2019-03-03 11:17:05 -03:00
{
2021-06-17 12:28:14 -03:00
if ( new_highlight_window = = m_window_stack . highlight_window ( ) )
2019-03-03 11:17:05 -03:00
return ;
2021-06-17 12:28:14 -03:00
auto * previous_highlight_window = m_window_stack . highlight_window ( ) ;
m_window_stack . set_highlight_window ( new_highlight_window ) ;
2021-03-03 01:13:42 -03:00
if ( previous_highlight_window ) {
2021-02-10 15:54:58 -03:00
previous_highlight_window - > invalidate ( true , true ) ;
2021-03-03 01:13:42 -03:00
Compositor : : the ( ) . invalidate_screen ( previous_highlight_window - > frame ( ) . render_rect ( ) ) ;
}
2021-06-17 12:28:14 -03:00
if ( new_highlight_window ) {
new_highlight_window - > invalidate ( true , true ) ;
Compositor : : the ( ) . invalidate_screen ( new_highlight_window - > frame ( ) . render_rect ( ) ) ;
2021-03-03 01:13:42 -03:00
}
// Invalidate occlusions in case the state change changes geometry
Compositor : : the ( ) . invalidate_occlusions ( ) ;
2019-03-03 11:17:05 -03:00
}
2020-07-14 22:17:00 -03:00
bool WindowManager : : is_active_window_or_accessory ( Window & window ) const
{
2021-06-17 15:11:07 -03:00
if ( window . is_accessory ( ) )
return window . parent_window ( ) - > is_active ( ) ;
2020-07-14 22:17:00 -03:00
2021-06-17 15:11:07 -03:00
return window . is_active ( ) ;
2020-07-14 22:17:00 -03:00
}
2020-04-18 17:15:59 -03:00
static bool window_type_can_become_active ( WindowType type )
{
2021-02-16 12:10:39 -03:00
return type = = WindowType : : Normal | | type = = WindowType : : ToolWindow | | type = = WindowType : : Desktop ;
2020-04-18 17:15:59 -03:00
}
2020-07-15 13:13:13 -03:00
void WindowManager : : restore_active_input_window ( Window * window )
{
// If the previous active input window is gone, fall back to the
// current active window
if ( ! window )
window = active_window ( ) ;
// If the current active window is also gone, pick some other window
if ( ! window & & pick_new_active_window ( nullptr ) )
return ;
2020-07-24 17:05:05 -03:00
2021-04-28 00:21:36 -03:00
if ( window & & ! window - > is_minimized ( ) & & window - > is_visible ( ) )
set_active_input_window ( window ) ;
else
set_active_input_window ( nullptr ) ;
2020-07-15 13:13:13 -03:00
}
Window * WindowManager : : set_active_input_window ( Window * window )
{
if ( window = = m_active_input_window )
return window ;
Window * previous_input_window = m_active_input_window ;
if ( previous_input_window )
Core : : EventLoop : : current ( ) . post_event ( * previous_input_window , make < Event > ( Event : : WindowInputLeft ) ) ;
if ( window ) {
AK: Make RefPtr, NonnullRefPtr, WeakPtr thread safe
This makes most operations thread safe, especially so that they
can safely be used in the Kernel. This includes obtaining a strong
reference from a weak reference, which now requires an explicit
call to WeakPtr::strong_ref(). Another major change is that
Weakable::make_weak_ref() may require the explicit target type.
Previously we used reinterpret_cast in WeakPtr, assuming that it
can be properly converted. But WeakPtr does not necessarily have
the knowledge to be able to do this. Instead, we now ask the class
itself to deliver a WeakPtr to the type that we want.
Also, WeakLink is no longer specific to a target type. The reason
for this is that we want to be able to safely convert e.g. WeakPtr<T>
to WeakPtr<U>, and before this we just reinterpret_cast the internal
WeakLink<T> to WeakLink<U>, which is a bold assumption that it would
actually produce the correct code. Instead, WeakLink now operates
on just a raw pointer and we only make those constructors/operators
available if we can verify that it can be safely cast.
In order to guarantee thread safety, we now use the least significant
bit in the pointer for locking purposes. This also means that only
properly aligned pointers can be used.
2020-09-29 19:26:13 -03:00
m_active_input_window = * window ;
2020-07-15 13:13:13 -03:00
Core : : EventLoop : : current ( ) . post_event ( * window , make < Event > ( Event : : WindowInputEntered ) ) ;
} else {
m_active_input_window = nullptr ;
}
return previous_input_window ;
}
2021-06-17 15:11:07 -03:00
void WindowManager : : set_active_window ( Window * new_active_window , bool make_input )
2018-10-13 12:42:24 -03:00
{
2021-06-17 15:11:07 -03:00
if ( new_active_window ) {
if ( auto * modal_window = new_active_window - > blocking_modal_window ( ) ) {
2021-02-23 16:42:32 -03:00
VERIFY ( modal_window - > is_modal ( ) ) ;
2021-06-17 15:11:07 -03:00
VERIFY ( modal_window ! = new_active_window ) ;
new_active_window = modal_window ;
2020-07-15 17:21:42 -03:00
make_input = true ;
}
2019-03-18 20:52:39 -03:00
2021-06-17 15:11:07 -03:00
if ( ! window_type_can_become_active ( new_active_window - > type ( ) ) )
2020-07-16 14:08:39 -03:00
return ;
}
2019-02-14 07:56:45 -02:00
2021-06-17 15:11:07 -03:00
auto * new_active_input_window = new_active_window ;
if ( new_active_window & & new_active_window - > is_accessory ( ) ) {
2020-07-15 13:13:13 -03:00
// The parent of an accessory window is always the active
// window, but input is routed to the accessory window
2021-06-17 15:11:07 -03:00
new_active_window = new_active_window - > parent_window ( ) ;
2020-07-14 22:17:00 -03:00
}
2020-07-15 13:13:13 -03:00
if ( make_input )
set_active_input_window ( new_active_input_window ) ;
2021-06-17 15:11:07 -03:00
if ( new_active_window = = m_window_stack . active_window ( ) )
2018-10-13 12:42:24 -03:00
return ;
2021-06-17 15:11:07 -03:00
if ( auto * previously_active_window = m_window_stack . active_window ( ) ) {
2021-06-12 02:28:38 -03:00
for ( auto & child_window : previously_active_window - > child_windows ( ) ) {
if ( child_window & & child_window - > type ( ) = = WindowType : : Tooltip )
child_window - > request_close ( ) ;
}
2020-02-06 16:03:37 -03:00
Core : : EventLoop : : current ( ) . post_event ( * previously_active_window , make < Event > ( Event : : WindowDeactivated ) ) ;
2021-02-10 15:54:58 -03:00
previously_active_window - > invalidate ( true , true ) ;
2021-06-17 15:11:07 -03:00
m_window_stack . set_active_window ( nullptr ) ;
2020-07-14 22:17:00 -03:00
m_active_input_tracking_window = nullptr ;
2021-04-14 18:38:53 -03:00
tell_wms_window_state_changed ( * previously_active_window ) ;
2019-01-17 14:38:04 -02:00
}
2019-12-30 21:38:56 -03:00
2021-06-17 15:11:07 -03:00
if ( new_active_window ) {
m_window_stack . set_active_window ( new_active_window ) ;
Core : : EventLoop : : current ( ) . post_event ( * new_active_window , make < Event > ( Event : : WindowActivated ) ) ;
new_active_window - > invalidate ( true , true ) ;
tell_wms_window_state_changed ( * new_active_window ) ;
2019-01-17 14:38:04 -02:00
}
2021-02-11 19:12:19 -03:00
// Window shapes may have changed (e.g. shadows for inactive/active windows)
Compositor : : the ( ) . invalidate_occlusions ( ) ;
2018-10-13 20:23:01 -03:00
}
2019-01-11 23:57:14 -02:00
2021-02-21 15:56:59 -03:00
bool WindowManager : : set_hovered_window ( Window * window )
2019-02-20 06:12:19 -03:00
{
2019-04-13 21:36:06 -03:00
if ( m_hovered_window = = window )
2021-02-21 15:56:59 -03:00
return false ;
2019-02-20 06:12:19 -03:00
if ( m_hovered_window )
2020-02-06 16:03:37 -03:00
Core : : EventLoop : : current ( ) . post_event ( * m_hovered_window , make < Event > ( Event : : WindowLeft ) ) ;
2019-02-20 06:12:19 -03:00
AK: Make RefPtr, NonnullRefPtr, WeakPtr thread safe
This makes most operations thread safe, especially so that they
can safely be used in the Kernel. This includes obtaining a strong
reference from a weak reference, which now requires an explicit
call to WeakPtr::strong_ref(). Another major change is that
Weakable::make_weak_ref() may require the explicit target type.
Previously we used reinterpret_cast in WeakPtr, assuming that it
can be properly converted. But WeakPtr does not necessarily have
the knowledge to be able to do this. Instead, we now ask the class
itself to deliver a WeakPtr to the type that we want.
Also, WeakLink is no longer specific to a target type. The reason
for this is that we want to be able to safely convert e.g. WeakPtr<T>
to WeakPtr<U>, and before this we just reinterpret_cast the internal
WeakLink<T> to WeakLink<U>, which is a bold assumption that it would
actually produce the correct code. Instead, WeakLink now operates
on just a raw pointer and we only make those constructors/operators
available if we can verify that it can be safely cast.
In order to guarantee thread safety, we now use the least significant
bit in the pointer for locking purposes. This also means that only
properly aligned pointers can be used.
2020-09-29 19:26:13 -03:00
m_hovered_window = window ;
2019-02-20 06:12:19 -03:00
if ( m_hovered_window )
2020-02-06 16:03:37 -03:00
Core : : EventLoop : : current ( ) . post_event ( * m_hovered_window , make < Event > ( Event : : WindowEntered ) ) ;
2021-02-21 15:56:59 -03:00
return true ;
2019-02-20 06:12:19 -03:00
}
2021-06-17 12:42:39 -03:00
ClientConnection const * WindowManager : : active_client ( ) const
2019-02-12 21:19:21 -02:00
{
2021-06-17 15:11:07 -03:00
if ( auto * window = m_window_stack . active_window ( ) )
return window - > client ( ) ;
2019-02-25 21:07:10 -03:00
return nullptr ;
2019-02-11 21:52:19 -02:00
}
2019-02-12 07:41:09 -02:00
2021-06-17 12:42:39 -03:00
Cursor const & WindowManager : : active_cursor ( ) const
2019-03-31 17:27:37 -03:00
{
2019-12-08 16:28:48 -03:00
if ( m_dnd_client )
return * m_drag_cursor ;
2019-12-08 16:34:37 -03:00
if ( m_move_window )
2019-03-31 17:42:13 -03:00
return * m_move_cursor ;
2019-04-06 18:20:06 -03:00
if ( m_resize_window | | m_resize_candidate ) {
2019-03-31 17:27:37 -03:00
switch ( m_resize_direction ) {
case ResizeDirection : : Up :
case ResizeDirection : : Down :
return * m_resize_vertically_cursor ;
case ResizeDirection : : Left :
case ResizeDirection : : Right :
return * m_resize_horizontally_cursor ;
case ResizeDirection : : UpLeft :
case ResizeDirection : : DownRight :
return * m_resize_diagonally_tlbr_cursor ;
case ResizeDirection : : UpRight :
case ResizeDirection : : DownLeft :
return * m_resize_diagonally_bltr_cursor ;
case ResizeDirection : : None :
2019-04-06 18:20:06 -03:00
break ;
2019-03-31 17:27:37 -03:00
}
}
2020-11-10 15:30:22 -03:00
if ( m_hovered_window ) {
2021-01-07 16:12:17 -03:00
if ( auto * modal_window = const_cast < Window & > ( * m_hovered_window ) . blocking_modal_window ( ) ) {
2020-11-10 15:30:22 -03:00
if ( modal_window - > cursor ( ) )
return * modal_window - > cursor ( ) ;
} else if ( m_hovered_window - > cursor ( ) ) {
return * m_hovered_window - > cursor ( ) ;
}
}
2019-03-31 18:52:02 -03:00
2019-03-31 17:27:37 -03:00
return * m_arrow_cursor ;
}
2019-04-05 23:08:09 -03:00
2020-02-06 16:03:37 -03:00
void WindowManager : : set_hovered_button ( Button * button )
2019-04-05 23:08:09 -03:00
{
AK: Make RefPtr, NonnullRefPtr, WeakPtr thread safe
This makes most operations thread safe, especially so that they
can safely be used in the Kernel. This includes obtaining a strong
reference from a weak reference, which now requires an explicit
call to WeakPtr::strong_ref(). Another major change is that
Weakable::make_weak_ref() may require the explicit target type.
Previously we used reinterpret_cast in WeakPtr, assuming that it
can be properly converted. But WeakPtr does not necessarily have
the knowledge to be able to do this. Instead, we now ask the class
itself to deliver a WeakPtr to the type that we want.
Also, WeakLink is no longer specific to a target type. The reason
for this is that we want to be able to safely convert e.g. WeakPtr<T>
to WeakPtr<U>, and before this we just reinterpret_cast the internal
WeakLink<T> to WeakLink<U>, which is a bold assumption that it would
actually produce the correct code. Instead, WeakLink now operates
on just a raw pointer and we only make those constructors/operators
available if we can verify that it can be safely cast.
In order to guarantee thread safety, we now use the least significant
bit in the pointer for locking purposes. This also means that only
properly aligned pointers can be used.
2020-09-29 19:26:13 -03:00
m_hovered_button = button ;
2019-04-05 23:08:09 -03:00
}
2019-04-06 18:20:06 -03:00
2020-02-06 16:03:37 -03:00
void WindowManager : : set_resize_candidate ( Window & window , ResizeDirection direction )
2019-04-06 18:20:06 -03:00
{
AK: Make RefPtr, NonnullRefPtr, WeakPtr thread safe
This makes most operations thread safe, especially so that they
can safely be used in the Kernel. This includes obtaining a strong
reference from a weak reference, which now requires an explicit
call to WeakPtr::strong_ref(). Another major change is that
Weakable::make_weak_ref() may require the explicit target type.
Previously we used reinterpret_cast in WeakPtr, assuming that it
can be properly converted. But WeakPtr does not necessarily have
the knowledge to be able to do this. Instead, we now ask the class
itself to deliver a WeakPtr to the type that we want.
Also, WeakLink is no longer specific to a target type. The reason
for this is that we want to be able to safely convert e.g. WeakPtr<T>
to WeakPtr<U>, and before this we just reinterpret_cast the internal
WeakLink<T> to WeakLink<U>, which is a bold assumption that it would
actually produce the correct code. Instead, WeakLink now operates
on just a raw pointer and we only make those constructors/operators
available if we can verify that it can be safely cast.
In order to guarantee thread safety, we now use the least significant
bit in the pointer for locking purposes. This also means that only
properly aligned pointers can be used.
2020-09-29 19:26:13 -03:00
m_resize_candidate = window ;
2019-04-06 18:20:06 -03:00
m_resize_direction = direction ;
}
2019-05-12 16:32:02 -03:00
2021-06-17 12:42:39 -03:00
ResizeDirection WindowManager : : resize_direction_of_window ( Window const & window )
2019-11-22 13:13:32 -03:00
{
if ( & window ! = m_resize_window )
return ResizeDirection : : None ;
return m_resize_direction ;
}
2021-06-17 12:42:39 -03:00
Gfx : : IntRect WindowManager : : maximized_window_rect ( Window const & window ) const
2019-05-12 16:32:02 -03:00
{
2020-06-10 05:57:59 -03:00
Gfx : : IntRect rect = Screen : : the ( ) . rect ( ) ;
2019-05-12 16:32:02 -03:00
// Subtract window title bar (leaving the border)
2021-04-18 10:35:57 -03:00
rect . set_y ( rect . y ( ) + window . frame ( ) . titlebar_rect ( ) . height ( ) + window . frame ( ) . menubar_rect ( ) . height ( ) ) ;
rect . set_height ( rect . height ( ) - window . frame ( ) . titlebar_rect ( ) . height ( ) - window . frame ( ) . menubar_rect ( ) . height ( ) ) ;
2019-05-12 16:32:02 -03:00
// Subtract taskbar window height if present
2021-06-17 12:28:14 -03:00
const_cast < WindowManager * > ( this ) - > m_window_stack . for_each_visible_window_of_type_from_back_to_front ( WindowType : : Taskbar , [ & rect ] ( Window & taskbar_window ) {
2019-05-12 16:32:02 -03:00
rect . set_height ( rect . height ( ) - taskbar_window . height ( ) ) ;
2019-06-07 12:13:23 -03:00
return IterationDecision : : Break ;
2019-05-12 16:32:02 -03:00
} ) ;
2021-05-09 16:35:14 -03:00
constexpr int tasteful_space_above_maximized_window = 1 ;
2020-05-10 09:46:23 -03:00
rect . set_y ( rect . y ( ) + tasteful_space_above_maximized_window ) ;
rect . set_height ( rect . height ( ) - tasteful_space_above_maximized_window ) ;
2019-05-12 16:32:02 -03:00
return rect ;
}
2019-11-11 06:43:03 -03:00
2021-06-17 12:42:39 -03:00
void WindowManager : : start_dnd_drag ( ClientConnection & client , String const & text , Gfx : : Bitmap const * bitmap , Core : : MimeData const & mime_data )
2019-12-08 12:50:23 -03:00
{
2021-02-23 16:42:32 -03:00
VERIFY ( ! m_dnd_client ) ;
AK: Make RefPtr, NonnullRefPtr, WeakPtr thread safe
This makes most operations thread safe, especially so that they
can safely be used in the Kernel. This includes obtaining a strong
reference from a weak reference, which now requires an explicit
call to WeakPtr::strong_ref(). Another major change is that
Weakable::make_weak_ref() may require the explicit target type.
Previously we used reinterpret_cast in WeakPtr, assuming that it
can be properly converted. But WeakPtr does not necessarily have
the knowledge to be able to do this. Instead, we now ask the class
itself to deliver a WeakPtr to the type that we want.
Also, WeakLink is no longer specific to a target type. The reason
for this is that we want to be able to safely convert e.g. WeakPtr<T>
to WeakPtr<U>, and before this we just reinterpret_cast the internal
WeakLink<T> to WeakLink<U>, which is a bold assumption that it would
actually produce the correct code. Instead, WeakLink now operates
on just a raw pointer and we only make those constructors/operators
available if we can verify that it can be safely cast.
In order to guarantee thread safety, we now use the least significant
bit in the pointer for locking purposes. This also means that only
properly aligned pointers can be used.
2020-09-29 19:26:13 -03:00
m_dnd_client = client ;
2019-12-08 12:50:23 -03:00
m_dnd_text = text ;
m_dnd_bitmap = bitmap ;
2020-11-07 16:44:21 -03:00
m_dnd_mime_data = mime_data ;
2020-02-06 16:03:37 -03:00
Compositor : : the ( ) . invalidate_cursor ( ) ;
2020-07-14 22:17:00 -03:00
m_active_input_tracking_window = nullptr ;
2019-12-08 12:50:23 -03:00
}
2020-02-06 16:03:37 -03:00
void WindowManager : : end_dnd_drag ( )
2019-12-08 12:50:23 -03:00
{
2021-02-23 16:42:32 -03:00
VERIFY ( m_dnd_client ) ;
2020-02-06 16:03:37 -03:00
Compositor : : the ( ) . invalidate_cursor ( ) ;
2019-12-08 12:50:23 -03:00
m_dnd_client = nullptr ;
m_dnd_text = { } ;
m_dnd_bitmap = nullptr ;
}
2020-06-10 05:57:59 -03:00
Gfx : : IntRect WindowManager : : dnd_rect ( ) const
2019-12-08 12:50:23 -03:00
{
2019-12-08 13:08:39 -03:00
int bitmap_width = m_dnd_bitmap ? m_dnd_bitmap - > width ( ) : 0 ;
2019-12-09 05:23:39 -03:00
int bitmap_height = m_dnd_bitmap ? m_dnd_bitmap - > height ( ) : 0 ;
2019-12-08 13:08:39 -03:00
int width = font ( ) . width ( m_dnd_text ) + bitmap_width ;
int height = max ( ( int ) font ( ) . glyph_height ( ) , bitmap_height ) ;
2020-02-06 16:03:37 -03:00
auto location = Compositor : : the ( ) . current_cursor_rect ( ) . center ( ) . translated ( 8 , 8 ) ;
2020-10-27 16:45:38 -03:00
return Gfx : : IntRect ( location , { width , height } ) . inflated ( 16 , 8 ) ;
2019-12-08 12:50:23 -03:00
}
2020-01-04 23:05:41 -03:00
2021-05-21 13:53:03 -03:00
void WindowManager : : invalidate_after_theme_or_font_change ( )
2020-01-04 23:05:41 -03:00
{
2021-06-01 20:04:05 -03:00
Compositor : : the ( ) . set_background_color ( m_config - > read_entry ( " Background " , " Color " , palette ( ) . desktop_background ( ) . to_string ( ) ) ) ;
2021-02-09 18:13:06 -03:00
WindowFrame : : reload_config ( ) ;
2021-06-17 12:28:14 -03:00
m_window_stack . for_each_window ( [ & ] ( Window & window ) {
2021-02-09 17:12:59 -03:00
window . frame ( ) . theme_changed ( ) ;
2020-01-04 23:05:41 -03:00
return IterationDecision : : Continue ;
} ) ;
2021-05-22 04:46:24 -03:00
ClientConnection : : for_each_client ( [ & ] ( ClientConnection & client ) {
client . async_update_system_theme ( Gfx : : current_system_theme_buffer ( ) ) ;
} ) ;
2020-02-17 16:05:14 -03:00
MenuManager : : the ( ) . did_change_theme ( ) ;
2021-04-04 15:22:45 -03:00
AppletManager : : the ( ) . did_change_theme ( ) ;
2021-05-21 13:53:03 -03:00
Compositor : : the ( ) . invalidate_occlusions ( ) ;
Compositor : : the ( ) . invalidate_screen ( ) ;
}
bool WindowManager : : update_theme ( String theme_path , String theme_name )
{
auto new_theme = Gfx : : load_system_theme ( theme_path ) ;
if ( ! new_theme . is_valid ( ) )
return false ;
Gfx : : set_system_theme ( new_theme ) ;
m_palette = Gfx : : PaletteImpl : : create_with_anonymous_buffer ( new_theme ) ;
2021-04-29 16:40:59 -03:00
auto wm_config = Core : : ConfigFile : : open ( " /etc/WindowServer.ini " ) ;
2020-01-04 23:05:41 -03:00
wm_config - > write_entry ( " Theme " , " Name " , theme_name ) ;
2021-06-01 20:04:05 -03:00
wm_config - > remove_entry ( " Background " , " Color " ) ;
2020-01-04 23:05:41 -03:00
wm_config - > sync ( ) ;
2021-05-21 13:53:03 -03:00
invalidate_after_theme_or_font_change ( ) ;
2020-02-17 16:05:14 -03:00
return true ;
2020-01-04 23:05:41 -03:00
}
2020-02-06 16:03:37 -03:00
2020-05-09 11:40:13 -03:00
void WindowManager : : did_popup_a_menu ( Badge < Menu > )
{
// Clear any ongoing input gesture
2020-07-14 22:17:00 -03:00
if ( ! m_active_input_tracking_window )
2020-05-09 11:40:13 -03:00
return ;
2020-07-14 22:17:00 -03:00
m_active_input_tracking_window - > set_automatic_cursor_tracking_enabled ( false ) ;
m_active_input_tracking_window = nullptr ;
2020-05-09 11:40:13 -03:00
}
2020-07-15 20:40:52 -03:00
void WindowManager : : minimize_windows ( Window & window , bool minimized )
{
for_each_window_in_modal_stack ( window , [ & ] ( auto & w , bool ) {
w . set_minimized ( minimized ) ;
2020-08-18 16:33:43 -03:00
return IterationDecision : : Continue ;
2020-07-15 20:40:52 -03:00
} ) ;
}
void WindowManager : : maximize_windows ( Window & window , bool maximized )
{
2020-08-10 15:58:44 -03:00
for_each_window_in_modal_stack ( window , [ & ] ( auto & w , bool stack_top ) {
if ( stack_top )
w . set_maximized ( maximized ) ;
2020-07-15 20:40:52 -03:00
if ( w . is_minimized ( ) )
w . set_minimized ( false ) ;
2020-08-18 16:33:43 -03:00
return IterationDecision : : Continue ;
2020-07-15 20:40:52 -03:00
} ) ;
}
2021-06-17 12:42:39 -03:00
Gfx : : IntPoint WindowManager : : get_recommended_window_position ( Gfx : : IntPoint const & desired )
2020-07-30 22:52:45 -03:00
{
// FIXME: Find a better source for the width and height to shift by.
2021-04-18 10:35:57 -03:00
Gfx : : IntPoint shift ( 8 , Gfx : : WindowTheme : : current ( ) . titlebar_height ( Gfx : : WindowTheme : : WindowType : : Normal , palette ( ) ) + 10 ) ;
2020-07-30 22:52:45 -03:00
// FIXME: Find a better source for this.
int taskbar_height = 28 ;
2021-06-17 12:42:39 -03:00
Window const * overlap_window = nullptr ;
2021-06-17 12:28:14 -03:00
m_window_stack . for_each_visible_window_of_type_from_front_to_back ( WindowType : : Normal , [ & ] ( Window & window ) {
2020-07-30 22:52:45 -03:00
if ( window . default_positioned ( ) & & ( ! overlap_window | | overlap_window - > window_id ( ) < window . window_id ( ) ) ) {
overlap_window = & window ;
}
return IterationDecision : : Continue ;
} ) ;
Gfx : : IntPoint point ;
if ( overlap_window ) {
point = overlap_window - > position ( ) + shift ;
point = { point . x ( ) % Screen : : the ( ) . width ( ) ,
( point . y ( ) > = ( Screen : : the ( ) . height ( ) - taskbar_height ) )
2021-04-18 10:35:57 -03:00
? Gfx : : WindowTheme : : current ( ) . titlebar_height ( Gfx : : WindowTheme : : WindowType : : Normal , palette ( ) )
2020-07-30 22:52:45 -03:00
: point . y ( ) } ;
} else {
point = desired ;
}
return point ;
}
2021-01-18 18:26:45 -03:00
int WindowManager : : compositor_icon_scale ( ) const
{
if ( ! m_allow_hidpi_icons )
return 1 ;
return scale_factor ( ) ;
}
void WindowManager : : reload_icon_bitmaps_after_scale_change ( bool allow_hidpi_icons )
{
m_allow_hidpi_icons = allow_hidpi_icons ;
reload_config ( ) ;
2021-06-17 12:28:14 -03:00
m_window_stack . for_each_window ( [ & ] ( Window & window ) {
2021-02-08 02:26:31 -03:00
auto & window_frame = window . frame ( ) ;
2021-02-09 18:13:06 -03:00
window_frame . theme_changed ( ) ;
2021-01-18 18:26:45 -03:00
return IterationDecision : : Continue ;
} ) ;
}
2021-03-25 17:01:19 -03:00
void WindowManager : : set_window_with_active_menu ( Window * window )
{
if ( m_window_with_active_menu = = window )
return ;
if ( window )
m_window_with_active_menu = window - > make_weak_ptr < Window > ( ) ;
else
m_window_with_active_menu = nullptr ;
}
2020-02-06 16:03:37 -03:00
}