added cursor_follows_focus and focus_follows_cursor
This commit is contained in:
parent
52280e9823
commit
7da0bc430a
22 changed files with 844 additions and 384 deletions
|
|
@ -5,6 +5,8 @@
|
||||||
pub use input::{AccelProfile, ClickMethod, ScrollMethod, TapButtonMap};
|
pub use input::{AccelProfile, ClickMethod, ScrollMethod, TapButtonMap};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
// Note: For the following values, None is used to represent the system default
|
||||||
|
// Configuration for input devices
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct InputConfig {
|
pub struct InputConfig {
|
||||||
pub state: DeviceState,
|
pub state: DeviceState,
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,12 @@ pub struct CosmicCompConfig {
|
||||||
pub autotile_behavior: TileBehavior,
|
pub autotile_behavior: TileBehavior,
|
||||||
/// Active hint enabled
|
/// Active hint enabled
|
||||||
pub active_hint: bool,
|
pub active_hint: bool,
|
||||||
|
/// Enables changing keyboard focus to windows when the cursor passes into them
|
||||||
|
pub focus_follows_cursor: bool,
|
||||||
|
/// Enables warping the cursor to the focused window when focus changes due to keyboard input
|
||||||
|
pub cursor_follows_focus: bool,
|
||||||
|
/// The delay in milliseconds before focus follows mouse (if enabled)
|
||||||
|
pub focus_follows_cursor_delay: u64,
|
||||||
/// Let X11 applications scale themselves
|
/// Let X11 applications scale themselves
|
||||||
pub descale_xwayland: bool,
|
pub descale_xwayland: bool,
|
||||||
}
|
}
|
||||||
|
|
@ -50,6 +56,9 @@ impl Default for CosmicCompConfig {
|
||||||
autotile: Default::default(),
|
autotile: Default::default(),
|
||||||
autotile_behavior: Default::default(),
|
autotile_behavior: Default::default(),
|
||||||
active_hint: true,
|
active_hint: true,
|
||||||
|
focus_follows_cursor: false,
|
||||||
|
cursor_follows_focus: false,
|
||||||
|
focus_follows_cursor_delay: 250,
|
||||||
descale_xwayland: false,
|
descale_xwayland: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -660,7 +660,7 @@ where
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.is_some();
|
.is_some();
|
||||||
let active_output = last_active_seat.active_output();
|
let focused_output = last_active_seat.focused_or_active_output();
|
||||||
let output_size = output.geometry().size;
|
let output_size = output.geometry().size;
|
||||||
let output_scale = output.current_scale().fractional_scale();
|
let output_scale = output.current_scale().fractional_scale();
|
||||||
|
|
||||||
|
|
@ -670,7 +670,7 @@ where
|
||||||
.iter()
|
.iter()
|
||||||
.find(|w| w.handle == current.0)
|
.find(|w| w.handle == current.0)
|
||||||
.ok_or(OutputNoMode)?;
|
.ok_or(OutputNoMode)?;
|
||||||
let is_active_space = workspace.outputs().any(|o| o == &active_output);
|
let is_active_space = workspace.outputs().any(|o| o == &focused_output);
|
||||||
|
|
||||||
let has_fullscreen = workspace
|
let has_fullscreen = workspace
|
||||||
.fullscreen
|
.fullscreen
|
||||||
|
|
@ -775,7 +775,7 @@ where
|
||||||
.space_for_handle(&previous)
|
.space_for_handle(&previous)
|
||||||
.ok_or(OutputNoMode)?;
|
.ok_or(OutputNoMode)?;
|
||||||
let has_fullscreen = workspace.fullscreen.is_some();
|
let has_fullscreen = workspace.fullscreen.is_some();
|
||||||
let is_active_space = workspace.outputs().any(|o| o == &active_output);
|
let is_active_space = workspace.outputs().any(|o| o == &focused_output);
|
||||||
|
|
||||||
let percentage = match start {
|
let percentage = match start {
|
||||||
WorkspaceDelta::Shortcut(st) => ease(
|
WorkspaceDelta::Shortcut(st) => ease(
|
||||||
|
|
|
||||||
|
|
@ -658,6 +658,24 @@ fn config_changed(config: cosmic_config::Config, keys: Vec<String>, state: &mut
|
||||||
state.common.update_xwayland_scale();
|
state.common.update_xwayland_scale();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"focus_follows_cursor" => {
|
||||||
|
let new = get_config::<bool>(&config, "focus_follows_cursor");
|
||||||
|
if new != state.common.config.cosmic_conf.focus_follows_cursor {
|
||||||
|
state.common.config.cosmic_conf.focus_follows_cursor = new;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"cursor_follows_focus" => {
|
||||||
|
let new = get_config::<bool>(&config, "cursor_follows_focus");
|
||||||
|
if new != state.common.config.cosmic_conf.cursor_follows_focus {
|
||||||
|
state.common.config.cosmic_conf.cursor_follows_focus = new;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"focus_follows_cursor_delay" => {
|
||||||
|
let new = get_config::<u64>(&config, "focus_follows_cursor_delay");
|
||||||
|
if new != state.common.config.cosmic_conf.focus_follows_cursor_delay {
|
||||||
|
state.common.config.cosmic_conf.focus_follows_cursor_delay = new;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
541
src/input/mod.rs
541
src/input/mod.rs
|
|
@ -27,7 +27,10 @@ use crate::{
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use calloop::{timer::Timer, RegistrationToken};
|
use calloop::{
|
||||||
|
timer::{TimeoutAction, Timer},
|
||||||
|
RegistrationToken,
|
||||||
|
};
|
||||||
use cosmic_comp_config::{workspace::WorkspaceLayout, TileBehavior};
|
use cosmic_comp_config::{workspace::WorkspaceLayout, TileBehavior};
|
||||||
use cosmic_config::ConfigSet;
|
use cosmic_config::ConfigSet;
|
||||||
use cosmic_settings_config::shortcuts;
|
use cosmic_settings_config::shortcuts;
|
||||||
|
|
@ -86,6 +89,17 @@ use std::{
|
||||||
|
|
||||||
pub mod gestures;
|
pub mod gestures;
|
||||||
|
|
||||||
|
/// Used for debouncing focus updates due to pointer motion, if after the focus change is
|
||||||
|
/// triggered the event will cancel if the pointer moves to the original target
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PointerFocusState {
|
||||||
|
//the window under the cursor prior to it's movement
|
||||||
|
originally_focused_window: Option<KeyboardFocusTarget>,
|
||||||
|
//the window under the cursor after it's movement
|
||||||
|
scheduled_focused_window: Option<KeyboardFocusTarget>,
|
||||||
|
token: RegistrationToken,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SupressedKeys(RefCell<Vec<(Keycode, Option<RegistrationToken>)>>);
|
pub struct SupressedKeys(RefCell<Vec<(Keycode, Option<RegistrationToken>)>>);
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
@ -203,24 +217,19 @@ impl State {
|
||||||
.cloned();
|
.cloned();
|
||||||
if let Some(seat) = maybe_seat {
|
if let Some(seat) = maybe_seat {
|
||||||
self.common.idle_notifier_state.notify_activity(&seat);
|
self.common.idle_notifier_state.notify_activity(&seat);
|
||||||
let current_output = seat.active_output();
|
|
||||||
let shortcuts_inhibited = self
|
|
||||||
.common
|
|
||||||
.shell
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.active_space(¤t_output)
|
|
||||||
.focus_stack
|
|
||||||
.get(&seat)
|
|
||||||
.last()
|
|
||||||
.and_then(|window| {
|
|
||||||
window.wl_surface().and_then(|surface| {
|
|
||||||
seat.keyboard_shortcuts_inhibitor_for_surface(&surface)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map(|inhibitor| inhibitor.is_active())
|
|
||||||
.unwrap_or(false);
|
|
||||||
|
|
||||||
|
let current_focus = seat.get_keyboard().unwrap().current_focus();
|
||||||
|
//this should fall back to active output since there may not be a focused output
|
||||||
|
let focused_output = seat.focused_or_active_output();
|
||||||
|
|
||||||
|
let shortcuts_inhibited = current_focus.is_some_and(|f| {
|
||||||
|
f.wl_surface()
|
||||||
|
.and_then(|surface| {
|
||||||
|
seat.keyboard_shortcuts_inhibitor_for_surface(&surface)
|
||||||
|
.map(|inhibitor| inhibitor.is_active())
|
||||||
|
})
|
||||||
|
.unwrap_or(false)
|
||||||
|
});
|
||||||
let keycode = event.key_code();
|
let keycode = event.key_code();
|
||||||
let state = event.state();
|
let state = event.state();
|
||||||
trace!(?keycode, ?state, "key");
|
trace!(?keycode, ?state, "key");
|
||||||
|
|
@ -266,7 +275,7 @@ impl State {
|
||||||
shell.set_overview_mode(None, data.common.event_loop_handle.clone());
|
shell.set_overview_mode(None, data.common.event_loop_handle.clone());
|
||||||
|
|
||||||
if let Some(focus) = current_focus {
|
if let Some(focus) = current_focus {
|
||||||
if let Some(new_descriptor) = shell.workspaces.active(¤t_output).1.node_desc(focus) {
|
if let Some(new_descriptor) = shell.workspaces.active(&focused_output).1.node_desc(focus) {
|
||||||
let mut spaces = shell.workspaces.spaces_mut();
|
let mut spaces = shell.workspaces.spaces_mut();
|
||||||
if old_descriptor.handle != new_descriptor.handle {
|
if old_descriptor.handle != new_descriptor.handle {
|
||||||
let (mut old_w, mut other_w) = spaces.partition::<Vec<_>, _>(|w| w.handle == old_descriptor.handle);
|
let (mut old_w, mut other_w) = spaces.partition::<Vec<_>, _>(|w| w.handle == old_descriptor.handle);
|
||||||
|
|
@ -275,7 +284,7 @@ impl State {
|
||||||
if let Some(focus) = TilingLayout::swap_trees(&mut old_workspace.tiling_layer, Some(&mut new_workspace.tiling_layer), &old_descriptor, &new_descriptor) {
|
if let Some(focus) = TilingLayout::swap_trees(&mut old_workspace.tiling_layer, Some(&mut new_workspace.tiling_layer), &old_descriptor, &new_descriptor) {
|
||||||
let seat = seat.clone();
|
let seat = seat.clone();
|
||||||
data.common.event_loop_handle.insert_idle(move |state| {
|
data.common.event_loop_handle.insert_idle(move |state| {
|
||||||
Shell::set_focus(state, Some(&focus), &seat, None);
|
Shell::set_focus(state, Some(&focus), &seat, None,true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
old_workspace.refresh_focus_stack();
|
old_workspace.refresh_focus_stack();
|
||||||
|
|
@ -288,7 +297,7 @@ impl State {
|
||||||
std::mem::drop(spaces);
|
std::mem::drop(spaces);
|
||||||
let seat = seat.clone();
|
let seat = seat.clone();
|
||||||
data.common.event_loop_handle.insert_idle(move |state| {
|
data.common.event_loop_handle.insert_idle(move |state| {
|
||||||
Shell::set_focus(state, Some(&focus), &seat, None);
|
Shell::set_focus(state, Some(&focus), &seat, None,true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
workspace.refresh_focus_stack();
|
workspace.refresh_focus_stack();
|
||||||
|
|
@ -296,7 +305,7 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let new_workspace = shell.workspaces.active(¤t_output).1.handle;
|
let new_workspace = shell.workspaces.active(&focused_output).1.handle;
|
||||||
if new_workspace != old_descriptor.handle {
|
if new_workspace != old_descriptor.handle {
|
||||||
let spaces = shell.workspaces.spaces_mut();
|
let spaces = shell.workspaces.spaces_mut();
|
||||||
let (mut old_w, mut other_w) = spaces.partition::<Vec<_>, _>(|w| w.handle == old_descriptor.handle);
|
let (mut old_w, mut other_w) = spaces.partition::<Vec<_>, _>(|w| w.handle == old_descriptor.handle);
|
||||||
|
|
@ -306,7 +315,7 @@ impl State {
|
||||||
if let Some(focus) = TilingLayout::move_tree(&mut old_workspace.tiling_layer, &mut new_workspace.tiling_layer, &new_workspace.handle, &seat, new_workspace.focus_stack.get(&seat).iter(), old_descriptor.clone()) {
|
if let Some(focus) = TilingLayout::move_tree(&mut old_workspace.tiling_layer, &mut new_workspace.tiling_layer, &new_workspace.handle, &seat, new_workspace.focus_stack.get(&seat).iter(), old_descriptor.clone()) {
|
||||||
let seat = seat.clone();
|
let seat = seat.clone();
|
||||||
data.common.event_loop_handle.insert_idle(move |state| {
|
data.common.event_loop_handle.insert_idle(move |state| {
|
||||||
Shell::set_focus(state, Some(&focus), &seat, None);
|
Shell::set_focus(state, Some(&focus), &seat, None,true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
old_workspace.refresh_focus_stack();
|
old_workspace.refresh_focus_stack();
|
||||||
|
|
@ -559,7 +568,7 @@ impl State {
|
||||||
_ => {}
|
_ => {}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
let original_position = position;
|
||||||
position += event.delta().as_global();
|
position += event.delta().as_global();
|
||||||
|
|
||||||
let output = shell
|
let output = shell
|
||||||
|
|
@ -587,8 +596,8 @@ impl State {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ptr.is_grabbed()
|
if ptr.is_grabbed() {
|
||||||
&& seat
|
if seat
|
||||||
.user_data()
|
.user_data()
|
||||||
.get::<ResizeGrabMarker>()
|
.get::<ResizeGrabMarker>()
|
||||||
.map(|marker| marker.get())
|
.map(|marker| marker.get())
|
||||||
|
|
@ -599,6 +608,101 @@ impl State {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//If the pointer isn't grabbed, we should check if the focused element should be updated
|
||||||
|
} else if self.common.config.cosmic_conf.focus_follows_cursor {
|
||||||
|
let shell = self.common.shell.read().unwrap();
|
||||||
|
let (old_keyboard_target, _) =
|
||||||
|
shell.keyboard_target_from_position(original_position, &seat);
|
||||||
|
let (new_keyboard_target, _) =
|
||||||
|
shell.keyboard_target_from_position(position, &seat);
|
||||||
|
|
||||||
|
if old_keyboard_target != new_keyboard_target
|
||||||
|
&& new_keyboard_target.is_some()
|
||||||
|
{
|
||||||
|
let create_source = if self.common.pointer_focus_state.is_none() {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
let PointerFocusState {
|
||||||
|
originally_focused_window,
|
||||||
|
scheduled_focused_window,
|
||||||
|
token,
|
||||||
|
} = self.common.pointer_focus_state.as_ref().unwrap();
|
||||||
|
|
||||||
|
if &new_keyboard_target == originally_focused_window {
|
||||||
|
//if we moved to the original window, just cancel the event
|
||||||
|
self.common.event_loop_handle.remove(*token);
|
||||||
|
//clear the state
|
||||||
|
self.common.pointer_focus_state = None;
|
||||||
|
false
|
||||||
|
} else if &new_keyboard_target != scheduled_focused_window {
|
||||||
|
//if we moved to a new window, update the scheduled focus
|
||||||
|
self.common.event_loop_handle.remove(*token);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
//the state doesn't need to be updated or cleared
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if create_source {
|
||||||
|
// prevent popups from being unfocusable if there is a gap between them and their parent
|
||||||
|
let delay = calloop::timer::Timer::from_duration(
|
||||||
|
//default to 250ms
|
||||||
|
std::time::Duration::from_millis(
|
||||||
|
self.common.config.cosmic_conf.focus_follows_cursor_delay,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
let seat = seat.clone();
|
||||||
|
let token = self
|
||||||
|
.common
|
||||||
|
.event_loop_handle
|
||||||
|
.insert_source(delay, move |_, _, state| {
|
||||||
|
let target = state
|
||||||
|
.common
|
||||||
|
.pointer_focus_state
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.scheduled_focused_window
|
||||||
|
.clone();
|
||||||
|
//clear it prior in case the user twitches in the microsecond it
|
||||||
|
//takes this function to run
|
||||||
|
state.common.pointer_focus_state = None;
|
||||||
|
|
||||||
|
Shell::set_focus(
|
||||||
|
state,
|
||||||
|
target.as_ref(),
|
||||||
|
&seat,
|
||||||
|
Some(SERIAL_COUNTER.next_serial()),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
TimeoutAction::Drop
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
if token.is_some() {
|
||||||
|
let originally_focused_window =
|
||||||
|
if self.common.pointer_focus_state.is_none() {
|
||||||
|
old_keyboard_target
|
||||||
|
} else {
|
||||||
|
// In this case, the pointer has moved to a new window (neither original, nor scheduled)
|
||||||
|
// so we should preserve the original window for the focus state
|
||||||
|
self.common
|
||||||
|
.pointer_focus_state
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.originally_focused_window
|
||||||
|
.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
self.common.pointer_focus_state = Some(PointerFocusState {
|
||||||
|
originally_focused_window,
|
||||||
|
scheduled_focused_window: new_keyboard_target,
|
||||||
|
token: token.unwrap(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let output_geometry = output.geometry();
|
let output_geometry = output.geometry();
|
||||||
|
|
||||||
|
|
@ -780,8 +884,18 @@ impl State {
|
||||||
InputEvent::PointerButton { event, .. } => {
|
InputEvent::PointerButton { event, .. } => {
|
||||||
use smithay::backend::input::{ButtonState, PointerButtonEvent};
|
use smithay::backend::input::{ButtonState, PointerButtonEvent};
|
||||||
|
|
||||||
let mut shell = self.common.shell.write().unwrap();
|
//
|
||||||
if let Some(seat) = shell.seats.for_device(&event.device()).cloned() {
|
let Some(seat) = self
|
||||||
|
.common
|
||||||
|
.shell
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.seats
|
||||||
|
.for_device(&event.device())
|
||||||
|
.cloned()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
self.common.idle_notifier_state.notify_activity(&seat);
|
self.common.idle_notifier_state.notify_activity(&seat);
|
||||||
|
|
||||||
let serial = SERIAL_COUNTER.next_serial();
|
let serial = SERIAL_COUNTER.next_serial();
|
||||||
|
|
@ -795,77 +909,19 @@ impl State {
|
||||||
if !seat.get_pointer().unwrap().is_grabbed() {
|
if !seat.get_pointer().unwrap().is_grabbed() {
|
||||||
let output = seat.active_output();
|
let output = seat.active_output();
|
||||||
|
|
||||||
let pos = seat.get_pointer().unwrap().current_location().as_global();
|
let global_position =
|
||||||
let relative_pos = pos.to_local(&output);
|
seat.get_pointer().unwrap().current_location().as_global();
|
||||||
let mut under: Option<KeyboardFocusTarget> = None;
|
let shell = self.common.shell.write().unwrap();
|
||||||
|
let (under, trigger_move) =
|
||||||
if let Some(session_lock) = shell.session_lock.as_ref() {
|
shell.keyboard_target_from_position(global_position, &seat);
|
||||||
under = session_lock
|
if trigger_move {
|
||||||
.surfaces
|
|
||||||
.get(&output)
|
|
||||||
.map(|lock| lock.clone().into());
|
|
||||||
} else if let Some(window) =
|
|
||||||
shell.active_space(&output).get_fullscreen()
|
|
||||||
{
|
|
||||||
let layers = layer_map_for_output(&output);
|
|
||||||
if let Some(layer) =
|
|
||||||
layers.layer_under(WlrLayer::Overlay, relative_pos.as_logical())
|
|
||||||
{
|
|
||||||
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
|
|
||||||
if layer.can_receive_keyboard_focus()
|
|
||||||
&& layer
|
|
||||||
.surface_under(
|
|
||||||
relative_pos.as_logical() - layer_loc.to_f64(),
|
|
||||||
WindowSurfaceType::ALL,
|
|
||||||
)
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
under = Some(layer.clone().into());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
under = Some(window.clone().into());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let done = {
|
|
||||||
let layers = layer_map_for_output(&output);
|
|
||||||
if let Some(layer) = layers
|
|
||||||
.layer_under(WlrLayer::Overlay, relative_pos.as_logical())
|
|
||||||
.or_else(|| {
|
|
||||||
layers.layer_under(
|
|
||||||
WlrLayer::Top,
|
|
||||||
relative_pos.as_logical(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
{
|
|
||||||
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
|
|
||||||
if layer.can_receive_keyboard_focus()
|
|
||||||
&& layer
|
|
||||||
.surface_under(
|
|
||||||
relative_pos.as_logical() - layer_loc.to_f64(),
|
|
||||||
WindowSurfaceType::ALL,
|
|
||||||
)
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
under = Some(layer.clone().into());
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if !done {
|
|
||||||
// Don't check override redirect windows, because we don't set keyboard focus to them explicitly.
|
// Don't check override redirect windows, because we don't set keyboard focus to them explicitly.
|
||||||
// These cases are handled by the XwaylandKeyboardGrab.
|
// These cases are handled by the XwaylandKeyboardGrab.
|
||||||
if let Some(target) = shell.element_under(pos, &output) {
|
if let Some(target) = shell.element_under(global_position, &output) {
|
||||||
if seat.get_keyboard().unwrap().modifier_state().logo {
|
if seat.get_keyboard().unwrap().modifier_state().logo {
|
||||||
if let Some(surface) =
|
if let Some(surface) = target.toplevel().map(Cow::into_owned) {
|
||||||
target.toplevel().map(Cow::into_owned)
|
|
||||||
{
|
|
||||||
let seat_clone = seat.clone();
|
let seat_clone = seat.clone();
|
||||||
let mouse_button =
|
let mouse_button = PointerButtonEvent::button(&event);
|
||||||
PointerButtonEvent::button(&event);
|
|
||||||
|
|
||||||
let mut supress_button = || {
|
let mut supress_button = || {
|
||||||
// If the logo is held then the pointer event is
|
// If the logo is held then the pointer event is
|
||||||
|
|
@ -875,13 +931,8 @@ impl State {
|
||||||
seat.supressed_buttons().add(button);
|
seat.supressed_buttons().add(button);
|
||||||
};
|
};
|
||||||
|
|
||||||
fn dispatch_grab<
|
fn dispatch_grab<G: PointerGrab<State> + 'static>(
|
||||||
G: PointerGrab<State> + 'static,
|
grab: Option<(G, smithay::input::pointer::Focus)>,
|
||||||
>(
|
|
||||||
grab: Option<(
|
|
||||||
G,
|
|
||||||
smithay::input::pointer::Focus,
|
|
||||||
)>,
|
|
||||||
seat: Seat<State>,
|
seat: Seat<State>,
|
||||||
serial: Serial,
|
serial: Serial,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
|
|
@ -915,28 +966,52 @@ impl State {
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
drop(shell);
|
drop(shell);
|
||||||
dispatch_grab(res, seat_clone, serial, state);
|
dispatch_grab(
|
||||||
}
|
res, seat_clone, serial, state,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
smithay::backend::input::MouseButton::Right => {
|
smithay::backend::input::MouseButton::Right => {
|
||||||
supress_button();
|
supress_button();
|
||||||
self.common.event_loop_handle.insert_idle(
|
self.common.event_loop_handle.insert_idle(
|
||||||
move |state| {
|
move |state| {
|
||||||
let mut shell =
|
let mut shell =
|
||||||
state.common.shell.write().unwrap();
|
state.common.shell.write().unwrap();
|
||||||
let Some(target_elem) = shell.element_for_surface(&surface) else { return };
|
let Some(target_elem) =
|
||||||
|
shell.element_for_surface(&surface)
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let Some(geom) = shell
|
let Some(geom) = shell
|
||||||
.space_for(target_elem)
|
.space_for(target_elem)
|
||||||
.and_then(|f| f.element_geometry(target_elem)) else { return };
|
.and_then(|f| {
|
||||||
|
f.element_geometry(target_elem)
|
||||||
|
})
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let geom = geom.to_f64();
|
let geom = geom.to_f64();
|
||||||
let center = geom.loc + geom.size.downscale(2.0);
|
let center =
|
||||||
let offset = center.to_global(&output) - pos;
|
geom.loc + geom.size.downscale(2.0);
|
||||||
let edge = match (offset.x > 0.0, offset.y > 0.0) {
|
let offset = center.to_global(&output)
|
||||||
(true, true) => ResizeEdge::TOP_LEFT,
|
- global_position;
|
||||||
(false, true) => ResizeEdge::TOP_RIGHT,
|
let edge = match (
|
||||||
(true, false) => ResizeEdge::BOTTOM_LEFT,
|
offset.x > 0.0,
|
||||||
(false, false) => ResizeEdge::BOTTOM_RIGHT
|
offset.y > 0.0,
|
||||||
|
) {
|
||||||
|
(true, true) => {
|
||||||
|
ResizeEdge::TOP_LEFT
|
||||||
|
}
|
||||||
|
(false, true) => {
|
||||||
|
ResizeEdge::TOP_RIGHT
|
||||||
|
}
|
||||||
|
(true, false) => {
|
||||||
|
ResizeEdge::BOTTOM_LEFT
|
||||||
|
}
|
||||||
|
(false, false) => {
|
||||||
|
ResizeEdge::BOTTOM_RIGHT
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let res = shell.resize_request(
|
let res = shell.resize_request(
|
||||||
&surface,
|
&surface,
|
||||||
|
|
@ -946,67 +1021,36 @@ impl State {
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
drop(shell);
|
drop(shell);
|
||||||
dispatch_grab(res, seat_clone, serial, state);
|
dispatch_grab(
|
||||||
}
|
res, seat_clone, serial, state,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
_ => {},
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
under = Some(target);
|
|
||||||
} else {
|
|
||||||
let layers = layer_map_for_output(&output);
|
|
||||||
if let Some(layer) = layers
|
|
||||||
.layer_under(
|
|
||||||
WlrLayer::Bottom,
|
|
||||||
relative_pos.as_logical(),
|
|
||||||
)
|
|
||||||
.or_else(|| {
|
|
||||||
layers.layer_under(
|
|
||||||
WlrLayer::Background,
|
|
||||||
relative_pos.as_logical(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
{
|
|
||||||
let layer_loc =
|
|
||||||
layers.layer_geometry(layer).unwrap().loc;
|
|
||||||
if layer.can_receive_keyboard_focus()
|
|
||||||
&& layer
|
|
||||||
.surface_under(
|
|
||||||
relative_pos.as_logical()
|
|
||||||
- layer_loc.to_f64(),
|
|
||||||
WindowSurfaceType::ALL,
|
|
||||||
)
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
under = Some(layer.clone().into());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
Shell::set_focus(self, under.as_ref(), &seat, Some(serial));
|
Shell::set_focus(self, under.as_ref(), &seat, Some(serial), false);
|
||||||
} else {
|
|
||||||
std::mem::drop(shell);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
let mut shell = self.common.shell.write().unwrap();
|
||||||
if let Some(Trigger::Pointer(action_button)) =
|
if let Some(Trigger::Pointer(action_button)) =
|
||||||
shell.overview_mode().0.active_trigger()
|
shell.overview_mode().0.active_trigger()
|
||||||
{
|
{
|
||||||
if *action_button == button {
|
if *action_button == button {
|
||||||
shell
|
shell.set_overview_mode(None, self.common.event_loop_handle.clone());
|
||||||
.set_overview_mode(None, self.common.event_loop_handle.clone());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
};
|
};
|
||||||
|
|
||||||
let ptr = seat.get_pointer().unwrap();
|
let ptr = seat.get_pointer().unwrap();
|
||||||
if pass_event {
|
|
||||||
ptr.button(
|
ptr.button(
|
||||||
self,
|
self,
|
||||||
&ButtonEvent {
|
&ButtonEvent {
|
||||||
|
|
@ -1017,11 +1061,8 @@ impl State {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
ptr.frame(self);
|
ptr.frame(self);
|
||||||
} else if event.state() == ButtonState::Released {
|
|
||||||
ptr.unset_grab(self, serial, event.time_msec())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InputEvent::PointerAxis { event, .. } => {
|
InputEvent::PointerAxis { event, .. } => {
|
||||||
let scroll_factor =
|
let scroll_factor =
|
||||||
if let Some(device) = <dyn Any>::downcast_ref::<InputDevice>(&event.device()) {
|
if let Some(device) = <dyn Any>::downcast_ref::<InputDevice>(&event.device()) {
|
||||||
|
|
@ -1121,7 +1162,9 @@ impl State {
|
||||||
// Decide on action if first update
|
// Decide on action if first update
|
||||||
if first_update {
|
if first_update {
|
||||||
let mut natural_scroll = false;
|
let mut natural_scroll = false;
|
||||||
if let Some(scroll_config) = &self.common.config.cosmic_conf.input_touchpad.scroll_config {
|
if let Some(scroll_config) =
|
||||||
|
&self.common.config.cosmic_conf.input_touchpad.scroll_config
|
||||||
|
{
|
||||||
if let Some(natural) = scroll_config.natural_scroll {
|
if let Some(natural) = scroll_config.natural_scroll {
|
||||||
natural_scroll = natural;
|
natural_scroll = natural;
|
||||||
}
|
}
|
||||||
|
|
@ -1157,7 +1200,7 @@ impl State {
|
||||||
} else {
|
} else {
|
||||||
Some(SwipeAction::PrevWorkspace)
|
Some(SwipeAction::PrevWorkspace)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Some(Direction::Down) => {
|
Some(Direction::Down) => {
|
||||||
if natural_scroll {
|
if natural_scroll {
|
||||||
Some(SwipeAction::PrevWorkspace)
|
Some(SwipeAction::PrevWorkspace)
|
||||||
|
|
@ -1775,11 +1818,12 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
Action::Close => {
|
Action::Close => {
|
||||||
let current_output = seat.active_output();
|
if let Some(focus_target) = seat.get_keyboard().unwrap().current_focus() {
|
||||||
let shell = self.common.shell.read().unwrap();
|
self.common
|
||||||
let workspace = shell.active_space(¤t_output);
|
.shell
|
||||||
if let Some(window) = workspace.focus_stack.get(seat).last() {
|
.read()
|
||||||
window.send_close();
|
.unwrap()
|
||||||
|
.close_focused(&focus_target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1854,7 +1898,9 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
x @ Action::MoveToWorkspace(_) | x @ Action::SendToWorkspace(_) => {
|
x @ Action::MoveToWorkspace(_) | x @ Action::SendToWorkspace(_) => {
|
||||||
let current_output = seat.active_output();
|
let Some(focused_output) = seat.focused_output() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let follow = matches!(x, Action::MoveToWorkspace(_));
|
let follow = matches!(x, Action::MoveToWorkspace(_));
|
||||||
let workspace = match x {
|
let workspace = match x {
|
||||||
Action::MoveToWorkspace(0) | Action::SendToWorkspace(0) => 9,
|
Action::MoveToWorkspace(0) | Action::SendToWorkspace(0) => 9,
|
||||||
|
|
@ -1863,48 +1909,65 @@ impl State {
|
||||||
};
|
};
|
||||||
let res = self.common.shell.write().unwrap().move_current_window(
|
let res = self.common.shell.write().unwrap().move_current_window(
|
||||||
seat,
|
seat,
|
||||||
¤t_output,
|
&focused_output,
|
||||||
(¤t_output, Some(workspace as usize)),
|
(&focused_output, Some(workspace as usize)),
|
||||||
follow,
|
follow,
|
||||||
None,
|
None,
|
||||||
&mut self.common.workspace_state.update(),
|
&mut self.common.workspace_state.update(),
|
||||||
);
|
);
|
||||||
if let Ok(Some((target, _point))) = res {
|
if let Ok(Some((target, _point))) = res {
|
||||||
Shell::set_focus(self, Some(&target), seat, None);
|
Shell::set_focus(
|
||||||
|
self,
|
||||||
|
Some(&target),
|
||||||
|
seat,
|
||||||
|
None,
|
||||||
|
matches!(x, Action::MoveToWorkspace(_)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
x @ Action::MoveToLastWorkspace | x @ Action::SendToLastWorkspace => {
|
x @ Action::MoveToLastWorkspace | x @ Action::SendToLastWorkspace => {
|
||||||
let current_output = seat.active_output();
|
let Some(focused_output) = seat.focused_output() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let mut shell = self.common.shell.write().unwrap();
|
let mut shell = self.common.shell.write().unwrap();
|
||||||
let workspace = shell.workspaces.len(¤t_output).saturating_sub(1);
|
let workspace = shell.workspaces.len(&focused_output).saturating_sub(1);
|
||||||
let res = shell.move_current_window(
|
let res = shell.move_current_window(
|
||||||
seat,
|
seat,
|
||||||
¤t_output,
|
&focused_output,
|
||||||
(¤t_output, Some(workspace)),
|
(&focused_output, Some(workspace)),
|
||||||
matches!(x, Action::MoveToLastWorkspace),
|
matches!(x, Action::MoveToLastWorkspace),
|
||||||
None,
|
None,
|
||||||
&mut self.common.workspace_state.update(),
|
&mut self.common.workspace_state.update(),
|
||||||
);
|
);
|
||||||
|
// If the active workspace changed, the cursor_follows_focus should probably be checked
|
||||||
if let Ok(Some((target, _point))) = res {
|
if let Ok(Some((target, _point))) = res {
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
Shell::set_focus(self, Some(&target), seat, None);
|
Shell::set_focus(
|
||||||
|
self,
|
||||||
|
Some(&target),
|
||||||
|
seat,
|
||||||
|
None,
|
||||||
|
matches!(x, Action::MoveToLastWorkspace),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
x @ Action::MoveToNextWorkspace | x @ Action::SendToNextWorkspace => {
|
x @ Action::MoveToNextWorkspace | x @ Action::SendToNextWorkspace => {
|
||||||
let current_output = seat.active_output();
|
let Some(focused_output) = seat.focused_output() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let res = {
|
let res = {
|
||||||
let mut shell = self.common.shell.write().unwrap();
|
let mut shell = self.common.shell.write().unwrap();
|
||||||
let workspace = shell
|
let workspace = shell
|
||||||
.workspaces
|
.workspaces
|
||||||
.active_num(¤t_output)
|
.active_num(&focused_output)
|
||||||
.1
|
.1
|
||||||
.saturating_add(1);
|
.saturating_add(1);
|
||||||
shell.move_current_window(
|
shell.move_current_window(
|
||||||
seat,
|
seat,
|
||||||
¤t_output,
|
&focused_output,
|
||||||
(¤t_output, Some(workspace)),
|
(&focused_output, Some(workspace)),
|
||||||
matches!(x, Action::MoveToNextWorkspace),
|
matches!(x, Action::MoveToNextWorkspace),
|
||||||
direction,
|
direction,
|
||||||
&mut self.common.workspace_state.update(),
|
&mut self.common.workspace_state.update(),
|
||||||
|
|
@ -1913,7 +1976,14 @@ impl State {
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(Some((target, _point))) => {
|
Ok(Some((target, _point))) => {
|
||||||
Shell::set_focus(self, Some(&target), seat, None);
|
// If the active workspace changed, the cursor_follows_focus should probably be checked
|
||||||
|
Shell::set_focus(
|
||||||
|
self,
|
||||||
|
Some(&target),
|
||||||
|
seat,
|
||||||
|
None,
|
||||||
|
matches!(x, Action::MoveToNextWorkspace),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Err(_) if propagate => {
|
Err(_) if propagate => {
|
||||||
if let Some(inferred) = pattern.inferred_direction() {
|
if let Some(inferred) = pattern.inferred_direction() {
|
||||||
|
|
@ -1937,19 +2007,21 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
x @ Action::MoveToPreviousWorkspace | x @ Action::SendToPreviousWorkspace => {
|
x @ Action::MoveToPreviousWorkspace | x @ Action::SendToPreviousWorkspace => {
|
||||||
let current_output = seat.active_output();
|
let Some(focused_output) = seat.focused_output() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let res = {
|
let res = {
|
||||||
let mut shell = self.common.shell.write().unwrap();
|
let mut shell = self.common.shell.write().unwrap();
|
||||||
let workspace = shell
|
let workspace = shell
|
||||||
.workspaces
|
.workspaces
|
||||||
.active_num(¤t_output)
|
.active_num(&focused_output)
|
||||||
.1
|
.1
|
||||||
.saturating_sub(1);
|
.saturating_sub(1);
|
||||||
// TODO: Possibly move to prev output, if idx < 0
|
// TODO: Possibly move to prev output, if idx < 0
|
||||||
shell.move_current_window(
|
shell.move_current_window(
|
||||||
seat,
|
seat,
|
||||||
¤t_output,
|
&focused_output,
|
||||||
(¤t_output, Some(workspace)),
|
(&focused_output, Some(workspace)),
|
||||||
matches!(x, Action::MoveToPreviousWorkspace),
|
matches!(x, Action::MoveToPreviousWorkspace),
|
||||||
direction,
|
direction,
|
||||||
&mut self.common.workspace_state.update(),
|
&mut self.common.workspace_state.update(),
|
||||||
|
|
@ -1958,7 +2030,13 @@ impl State {
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
Ok(Some((target, _point))) => {
|
Ok(Some((target, _point))) => {
|
||||||
Shell::set_focus(self, Some(&target), seat, None);
|
Shell::set_focus(
|
||||||
|
self,
|
||||||
|
Some(&target),
|
||||||
|
seat,
|
||||||
|
None,
|
||||||
|
matches!(x, Action::MoveToPreviousWorkspace),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Err(_) if propagate => {
|
Err(_) if propagate => {
|
||||||
if let Some(inferred) = pattern.inferred_direction() {
|
if let Some(inferred) = pattern.inferred_direction() {
|
||||||
|
|
@ -2144,14 +2222,16 @@ impl State {
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let current_output = seat.active_output();
|
let Some(focused_output) = seat.focused_output() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let mut shell = self.common.shell.write().unwrap();
|
let mut shell = self.common.shell.write().unwrap();
|
||||||
let next_output = shell.next_output(¤t_output, direction).cloned();
|
let next_output = shell.next_output(&focused_output, direction).cloned();
|
||||||
|
|
||||||
if let Some(next_output) = next_output {
|
if let Some(next_output) = next_output {
|
||||||
let res = shell.move_current_window(
|
let res = shell.move_current_window(
|
||||||
seat,
|
seat,
|
||||||
¤t_output,
|
&focused_output,
|
||||||
(&next_output, None),
|
(&next_output, None),
|
||||||
is_move_action,
|
is_move_action,
|
||||||
Some(direction),
|
Some(direction),
|
||||||
|
|
@ -2159,7 +2239,7 @@ impl State {
|
||||||
);
|
);
|
||||||
if let Ok(Some((target, new_pos))) = res {
|
if let Ok(Some((target, new_pos))) = res {
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
Shell::set_focus(self, Some(&target), seat, None);
|
Shell::set_focus(self, Some(&target), seat, None, is_move_action);
|
||||||
if let Some(ptr) = seat.get_pointer() {
|
if let Some(ptr) = seat.get_pointer() {
|
||||||
ptr.motion(
|
ptr.motion(
|
||||||
self,
|
self,
|
||||||
|
|
@ -2208,19 +2288,21 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
x @ Action::MoveToNextOutput | x @ Action::SendToNextOutput => {
|
x @ Action::MoveToNextOutput | x @ Action::SendToNextOutput => {
|
||||||
let current_output = seat.active_output();
|
let Some(focused_output) = seat.focused_output() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let mut shell = self.common.shell.write().unwrap();
|
let mut shell = self.common.shell.write().unwrap();
|
||||||
|
|
||||||
let next_output = shell
|
let next_output = shell
|
||||||
.outputs()
|
.outputs()
|
||||||
.skip_while(|o| *o != ¤t_output)
|
.skip_while(|o| *o != &focused_output)
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.next()
|
.next()
|
||||||
.cloned();
|
.cloned();
|
||||||
if let Some(next_output) = next_output {
|
if let Some(next_output) = next_output {
|
||||||
let res = shell.move_current_window(
|
let res = shell.move_current_window(
|
||||||
seat,
|
seat,
|
||||||
¤t_output,
|
&focused_output,
|
||||||
(&next_output, None),
|
(&next_output, None),
|
||||||
matches!(x, Action::MoveToNextOutput),
|
matches!(x, Action::MoveToNextOutput),
|
||||||
direction,
|
direction,
|
||||||
|
|
@ -2228,7 +2310,13 @@ impl State {
|
||||||
);
|
);
|
||||||
if let Ok(Some((target, new_pos))) = res {
|
if let Ok(Some((target, new_pos))) = res {
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
Shell::set_focus(self, Some(&target), seat, None);
|
Shell::set_focus(
|
||||||
|
self,
|
||||||
|
Some(&target),
|
||||||
|
seat,
|
||||||
|
None,
|
||||||
|
matches!(x, Action::MoveToNextOutput),
|
||||||
|
);
|
||||||
if let Some(ptr) = seat.get_pointer() {
|
if let Some(ptr) = seat.get_pointer() {
|
||||||
ptr.motion(
|
ptr.motion(
|
||||||
self,
|
self,
|
||||||
|
|
@ -2246,20 +2334,22 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
x @ Action::MoveToPreviousOutput | x @ Action::SendToPreviousOutput => {
|
x @ Action::MoveToPreviousOutput | x @ Action::SendToPreviousOutput => {
|
||||||
let current_output = seat.active_output();
|
let Some(focused_output) = seat.focused_output() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let mut shell = self.common.shell.write().unwrap();
|
let mut shell = self.common.shell.write().unwrap();
|
||||||
|
|
||||||
let prev_output = shell
|
let prev_output = shell
|
||||||
.outputs()
|
.outputs()
|
||||||
.rev()
|
.rev()
|
||||||
.skip_while(|o| *o != ¤t_output)
|
.skip_while(|o| *o != &focused_output)
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.next()
|
.next()
|
||||||
.cloned();
|
.cloned();
|
||||||
if let Some(prev_output) = prev_output {
|
if let Some(prev_output) = prev_output {
|
||||||
let res = shell.move_current_window(
|
let res = shell.move_current_window(
|
||||||
seat,
|
seat,
|
||||||
¤t_output,
|
&focused_output,
|
||||||
(&prev_output, None),
|
(&prev_output, None),
|
||||||
matches!(x, Action::MoveToPreviousOutput),
|
matches!(x, Action::MoveToPreviousOutput),
|
||||||
direction,
|
direction,
|
||||||
|
|
@ -2267,7 +2357,13 @@ impl State {
|
||||||
);
|
);
|
||||||
if let Ok(Some((target, new_pos))) = res {
|
if let Ok(Some((target, new_pos))) = res {
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
Shell::set_focus(self, Some(&target), seat, None);
|
Shell::set_focus(
|
||||||
|
self,
|
||||||
|
Some(&target),
|
||||||
|
seat,
|
||||||
|
None,
|
||||||
|
matches!(x, Action::MoveToPreviousOutput),
|
||||||
|
);
|
||||||
if let Some(ptr) = seat.get_pointer() {
|
if let Some(ptr) = seat.get_pointer() {
|
||||||
ptr.motion(
|
ptr.motion(
|
||||||
self,
|
self,
|
||||||
|
|
@ -2285,58 +2381,58 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
Action::MigrateWorkspaceToNextOutput => {
|
Action::MigrateWorkspaceToNextOutput => {
|
||||||
let current_output = seat.active_output();
|
let active_output = seat.active_output();
|
||||||
let (active, next_output) = {
|
let (active, next_output) = {
|
||||||
let shell = self.common.shell.read().unwrap();
|
let shell = self.common.shell.read().unwrap();
|
||||||
let output = shell
|
let output = shell
|
||||||
.outputs()
|
.outputs()
|
||||||
.skip_while(|o| *o != ¤t_output)
|
.skip_while(|o| *o != &active_output)
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.next()
|
.next()
|
||||||
.cloned();
|
.cloned();
|
||||||
|
|
||||||
(shell.active_space(¤t_output).handle, output)
|
(shell.active_space(&active_output).handle, output)
|
||||||
};
|
};
|
||||||
if let Some(next_output) = next_output {
|
if let Some(next_output) = next_output {
|
||||||
self.common
|
self.common
|
||||||
.migrate_workspace(¤t_output, &next_output, &active);
|
.migrate_workspace(&active_output, &next_output, &active);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Action::MigrateWorkspaceToPreviousOutput => {
|
Action::MigrateWorkspaceToPreviousOutput => {
|
||||||
let current_output = seat.active_output();
|
let active_output = seat.active_output();
|
||||||
let (active, prev_output) = {
|
let (active, prev_output) = {
|
||||||
let shell = self.common.shell.read().unwrap();
|
let shell = self.common.shell.read().unwrap();
|
||||||
let output = shell
|
let output = shell
|
||||||
.outputs()
|
.outputs()
|
||||||
.rev()
|
.rev()
|
||||||
.skip_while(|o| *o != ¤t_output)
|
.skip_while(|o| *o != &active_output)
|
||||||
.skip(1)
|
.skip(1)
|
||||||
.next()
|
.next()
|
||||||
.cloned();
|
.cloned();
|
||||||
|
|
||||||
(shell.active_space(¤t_output).handle, output)
|
(shell.active_space(&active_output).handle, output)
|
||||||
};
|
};
|
||||||
if let Some(prev_output) = prev_output {
|
if let Some(prev_output) = prev_output {
|
||||||
self.common
|
self.common
|
||||||
.migrate_workspace(¤t_output, &prev_output, &active);
|
.migrate_workspace(&active_output, &prev_output, &active);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Action::MigrateWorkspaceToOutput(direction) => {
|
Action::MigrateWorkspaceToOutput(direction) => {
|
||||||
let current_output = seat.active_output();
|
let active_output = seat.active_output();
|
||||||
let (active, next_output) = {
|
let (active, next_output) = {
|
||||||
let shell = self.common.shell.read().unwrap();
|
let shell = self.common.shell.read().unwrap();
|
||||||
|
|
||||||
(
|
(
|
||||||
shell.active_space(¤t_output).handle,
|
shell.active_space(&active_output).handle,
|
||||||
shell.next_output(¤t_output, direction).cloned(),
|
shell.next_output(&active_output, direction).cloned(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(next_output) = next_output {
|
if let Some(next_output) = next_output {
|
||||||
self.common
|
self.common
|
||||||
.migrate_workspace(¤t_output, &next_output, &active);
|
.migrate_workspace(&active_output, &next_output, &active);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2367,7 +2463,7 @@ impl State {
|
||||||
}
|
}
|
||||||
FocusResult::Handled => {}
|
FocusResult::Handled => {}
|
||||||
FocusResult::Some(target) => {
|
FocusResult::Some(target) => {
|
||||||
Shell::set_focus(self, Some(&target), seat, None);
|
Shell::set_focus(self, Some(&target), seat, None, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2390,7 +2486,7 @@ impl State {
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
MoveResult::ShiftFocus(shift) => {
|
MoveResult::ShiftFocus(shift) => {
|
||||||
Shell::set_focus(self, Some(&shift), seat, None);
|
Shell::set_focus(self, Some(&shift), seat, None, true);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let current_output = seat.active_output();
|
let current_output = seat.active_output();
|
||||||
|
|
@ -2409,10 +2505,12 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
Action::SwapWindow => {
|
Action::SwapWindow => {
|
||||||
let current_output = seat.active_output();
|
let Some(focused_output) = seat.focused_output() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let mut shell = self.common.shell.write().unwrap();
|
let mut shell = self.common.shell.write().unwrap();
|
||||||
|
|
||||||
let workspace = shell.active_space_mut(¤t_output);
|
let workspace = shell.active_space_mut(&focused_output);
|
||||||
if workspace.get_fullscreen().is_some() {
|
if workspace.get_fullscreen().is_some() {
|
||||||
return; // TODO, is this what we want? Maybe disengage fullscreen instead?
|
return; // TODO, is this what we want? Maybe disengage fullscreen instead?
|
||||||
}
|
}
|
||||||
|
|
@ -2433,9 +2531,11 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
Action::Minimize => {
|
Action::Minimize => {
|
||||||
let current_output = seat.active_output();
|
let Some(focused_output) = seat.focused_output() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let mut shell = self.common.shell.write().unwrap();
|
let mut shell = self.common.shell.write().unwrap();
|
||||||
let workspace = shell.active_space_mut(¤t_output);
|
let workspace = shell.active_space_mut(&focused_output);
|
||||||
let focus_stack = workspace.focus_stack.get(seat);
|
let focus_stack = workspace.focus_stack.get(seat);
|
||||||
let focused_window = focus_stack.last().cloned();
|
let focused_window = focus_stack.last().cloned();
|
||||||
if let Some(window) = focused_window {
|
if let Some(window) = focused_window {
|
||||||
|
|
@ -2444,9 +2544,11 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
Action::Maximize => {
|
Action::Maximize => {
|
||||||
let current_output = seat.active_output();
|
let Some(focused_output) = seat.focused_output() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let mut shell = self.common.shell.write().unwrap();
|
let mut shell = self.common.shell.write().unwrap();
|
||||||
let workspace = shell.active_space(¤t_output);
|
let workspace = shell.active_space(&focused_output);
|
||||||
let focus_stack = workspace.focus_stack.get(seat);
|
let focus_stack = workspace.focus_stack.get(seat);
|
||||||
let focused_window = focus_stack.last().cloned();
|
let focused_window = focus_stack.last().cloned();
|
||||||
if let Some(window) = focused_window {
|
if let Some(window) = focused_window {
|
||||||
|
|
@ -2459,7 +2561,8 @@ impl State {
|
||||||
&self.common.config,
|
&self.common.config,
|
||||||
self.common.event_loop_handle.clone(),
|
self.common.event_loop_handle.clone(),
|
||||||
),
|
),
|
||||||
|
// NOTE: implementation currently assumes actions that apply to outputs should apply to the active output
|
||||||
|
// rather than the output that has keyboard focus
|
||||||
Action::ToggleOrientation => {
|
Action::ToggleOrientation => {
|
||||||
let output = seat.active_output();
|
let output = seat.active_output();
|
||||||
let mut shell = self.common.shell.write().unwrap();
|
let mut shell = self.common.shell.write().unwrap();
|
||||||
|
|
@ -2484,7 +2587,7 @@ impl State {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.toggle_stacking_focused(seat);
|
.toggle_stacking_focused(seat);
|
||||||
if let Some(new_focus) = res {
|
if let Some(new_focus) = res {
|
||||||
Shell::set_focus(self, Some(&new_focus), seat, Some(serial));
|
Shell::set_focus(self, Some(&new_focus), seat, Some(serial), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2521,7 +2624,9 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
Action::ToggleWindowFloating => {
|
Action::ToggleWindowFloating => {
|
||||||
let output = seat.active_output();
|
let Some(output) = seat.focused_output() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let mut shell = self.common.shell.write().unwrap();
|
let mut shell = self.common.shell.write().unwrap();
|
||||||
let workspace = shell.active_space_mut(&output);
|
let workspace = shell.active_space_mut(&output);
|
||||||
workspace.toggle_floating_window_focused(seat);
|
workspace.toggle_floating_window_focused(seat);
|
||||||
|
|
|
||||||
|
|
@ -177,7 +177,7 @@ impl CosmicWindowInternal {
|
||||||
pub fn current_focus(&self) -> Option<Focus> {
|
pub fn current_focus(&self) -> Option<Focus> {
|
||||||
unsafe { Focus::from_u8(self.pointer_entered.load(Ordering::SeqCst)) }
|
unsafe { Focus::from_u8(self.pointer_entered.load(Ordering::SeqCst)) }
|
||||||
}
|
}
|
||||||
|
/// returns if the window has any current or pending server-side decorations
|
||||||
pub fn has_ssd(&self, pending: bool) -> bool {
|
pub fn has_ssd(&self, pending: bool) -> bool {
|
||||||
!self.window.is_decorated(pending)
|
!self.window.is_decorated(pending)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,12 @@ use smithay::{
|
||||||
utils::{IsAlive, Serial, SERIAL_COUNTER},
|
utils::{IsAlive, Serial, SERIAL_COUNTER},
|
||||||
wayland::{
|
wayland::{
|
||||||
seat::WaylandFocus,
|
seat::WaylandFocus,
|
||||||
selection::data_device::set_data_device_focus,
|
selection::{data_device::set_data_device_focus, primary_selection::set_primary_focus},
|
||||||
selection::primary_selection::set_primary_focus,
|
|
||||||
shell::wlr_layer::{KeyboardInteractivity, Layer},
|
shell::wlr_layer::{KeyboardInteractivity, Layer},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::{borrow::Cow, sync::Mutex};
|
use std::{borrow::Cow, mem, sync::Mutex};
|
||||||
|
|
||||||
use tracing::{debug, trace};
|
use tracing::{debug, trace};
|
||||||
|
|
||||||
use self::target::{KeyboardFocusTarget, WindowGroup};
|
use self::target::{KeyboardFocusTarget, WindowGroup};
|
||||||
|
|
@ -31,6 +31,7 @@ pub struct FocusStack<'a>(pub(super) Option<&'a IndexSet<CosmicMapped>>);
|
||||||
pub struct FocusStackMut<'a>(pub(super) &'a mut IndexSet<CosmicMapped>);
|
pub struct FocusStackMut<'a>(pub(super) &'a mut IndexSet<CosmicMapped>);
|
||||||
|
|
||||||
impl<'a> FocusStack<'a> {
|
impl<'a> FocusStack<'a> {
|
||||||
|
/// returns the last unminimized window in the focus stack that is still alive
|
||||||
pub fn last(&self) -> Option<&CosmicMapped> {
|
pub fn last(&self) -> Option<&CosmicMapped> {
|
||||||
self.0
|
self.0
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
@ -93,11 +94,15 @@ impl ActiveFocus {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Shell {
|
impl Shell {
|
||||||
|
/// Set the keyboard focus to the given target
|
||||||
|
/// Note: `update_cursor` is used to determine whether to update the pointer location if cursor_follows_focus is enabled
|
||||||
|
/// if the focus change was due to a pointer event, this should be set to false
|
||||||
pub fn set_focus(
|
pub fn set_focus(
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
target: Option<&KeyboardFocusTarget>,
|
target: Option<&KeyboardFocusTarget>,
|
||||||
seat: &Seat<State>,
|
seat: &Seat<State>,
|
||||||
serial: Option<Serial>,
|
serial: Option<Serial>,
|
||||||
|
update_cursor: bool,
|
||||||
) {
|
) {
|
||||||
let element = match target {
|
let element = match target {
|
||||||
Some(KeyboardFocusTarget::Element(mapped)) => Some(mapped.clone()),
|
Some(KeyboardFocusTarget::Element(mapped)) => Some(mapped.clone()),
|
||||||
|
|
@ -115,6 +120,7 @@ impl Shell {
|
||||||
if mapped.is_minimized() {
|
if mapped.is_minimized() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state
|
state
|
||||||
.common
|
.common
|
||||||
.shell
|
.shell
|
||||||
|
|
@ -123,15 +129,7 @@ impl Shell {
|
||||||
.append_focus_stack(&mapped, seat);
|
.append_focus_stack(&mapped, seat);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update keyboard focus
|
update_focus_state(seat, target, state, serial, update_cursor);
|
||||||
if let Some(keyboard) = seat.get_keyboard() {
|
|
||||||
ActiveFocus::set(seat, target.cloned());
|
|
||||||
keyboard.set_focus(
|
|
||||||
state,
|
|
||||||
target.cloned(),
|
|
||||||
serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
state.common.shell.write().unwrap().update_active();
|
state.common.shell.write().unwrap().update_active();
|
||||||
}
|
}
|
||||||
|
|
@ -144,7 +142,8 @@ impl Shell {
|
||||||
// update FocusStack and notify layouts about new focus (if any window)
|
// update FocusStack and notify layouts about new focus (if any window)
|
||||||
let workspace = self.space_for_mut(&mapped);
|
let workspace = self.space_for_mut(&mapped);
|
||||||
let workspace = if workspace.is_none() {
|
let workspace = if workspace.is_none() {
|
||||||
self.active_space_mut(&seat.active_output())
|
//should this be the active output or the focused output?
|
||||||
|
self.active_space_mut(&seat.focused_or_active_output())
|
||||||
} else {
|
} else {
|
||||||
workspace.unwrap()
|
workspace.unwrap()
|
||||||
};
|
};
|
||||||
|
|
@ -218,6 +217,77 @@ impl Shell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Internal, used to ensure that ActiveFocus, KeyboardFocusTarget, and FocusedOutput are all in sync
|
||||||
|
fn update_focus_state(
|
||||||
|
seat: &Seat<State>,
|
||||||
|
target: Option<&KeyboardFocusTarget>,
|
||||||
|
state: &mut State,
|
||||||
|
serial: Option<Serial>,
|
||||||
|
should_update_cursor: bool,
|
||||||
|
) {
|
||||||
|
// update keyboard focus
|
||||||
|
if let Some(keyboard) = seat.get_keyboard() {
|
||||||
|
if should_update_cursor && state.common.config.cosmic_conf.cursor_follows_focus {
|
||||||
|
if ActiveFocus::get(seat).as_ref() != target && target.is_some() {
|
||||||
|
//need to borrow mutably for surface under
|
||||||
|
let mut shell = state.common.shell.write().unwrap();
|
||||||
|
// get the top left corner of the target element
|
||||||
|
let geometry = shell.focused_geometry(target.unwrap());
|
||||||
|
//to avoid the nested mutable borrow of state
|
||||||
|
if geometry.is_some() {
|
||||||
|
let top_left = geometry.unwrap().loc.to_f64();
|
||||||
|
|
||||||
|
// create a pointer target from the target element
|
||||||
|
let output = shell
|
||||||
|
.outputs()
|
||||||
|
.find(|output| output.geometry().to_f64().contains(top_left))
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or(seat.active_output());
|
||||||
|
|
||||||
|
let focus = shell
|
||||||
|
.surface_under(top_left, &output)
|
||||||
|
.map(|(focus, loc)| (focus, loc.as_logical()));
|
||||||
|
//drop here to avoid multiple mutable borrows
|
||||||
|
mem::drop(shell);
|
||||||
|
seat.get_pointer().unwrap().motion(
|
||||||
|
state,
|
||||||
|
focus,
|
||||||
|
&MotionEvent {
|
||||||
|
location: top_left.as_logical(),
|
||||||
|
serial: SERIAL_COUNTER.next_serial(),
|
||||||
|
time: 0,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ActiveFocus::set(seat, target.cloned());
|
||||||
|
keyboard.set_focus(
|
||||||
|
state,
|
||||||
|
target.cloned(),
|
||||||
|
serial.unwrap_or_else(|| SERIAL_COUNTER.next_serial()),
|
||||||
|
);
|
||||||
|
|
||||||
|
//update the focused output or set it to the active output
|
||||||
|
if target.is_some() {
|
||||||
|
// Get focused output calls visible_output_for_surface internally
|
||||||
|
// what should happen if the target is some, but it's not visible?
|
||||||
|
// should this be an error?
|
||||||
|
seat.set_focused_output(
|
||||||
|
state
|
||||||
|
.common
|
||||||
|
.shell
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.get_focused_output(target.unwrap()),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
seat.set_focused_output(None);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn raise_with_children(floating_layer: &mut FloatingLayout, focused: &CosmicMapped) {
|
fn raise_with_children(floating_layer: &mut FloatingLayout, focused: &CosmicMapped) {
|
||||||
if floating_layer.mapped().any(|m| m == focused) {
|
if floating_layer.mapped().any(|m| m == focused) {
|
||||||
floating_layer.space.raise_element(focused, true);
|
floating_layer.space.raise_element(focused, true);
|
||||||
|
|
@ -264,13 +334,16 @@ impl Common {
|
||||||
update_pointer_focus(state, &seat);
|
update_pointer_focus(state, &seat);
|
||||||
|
|
||||||
let mut shell = state.common.shell.write().unwrap();
|
let mut shell = state.common.shell.write().unwrap();
|
||||||
let output = seat.active_output();
|
let output = seat.focused_or_active_output();
|
||||||
|
|
||||||
|
// If the focused or active output is not in the list of outputs, switch to the first output
|
||||||
if !shell.outputs().any(|o| o == &output) {
|
if !shell.outputs().any(|o| o == &output) {
|
||||||
if let Some(other) = shell.outputs().next() {
|
if let Some(other) = shell.outputs().next() {
|
||||||
seat.set_active_output(other);
|
seat.set_active_output(other);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let last_known_focus = ActiveFocus::get(&seat);
|
let last_known_focus = ActiveFocus::get(&seat);
|
||||||
|
|
||||||
if let Some(target) = last_known_focus {
|
if let Some(target) = last_known_focus {
|
||||||
|
|
@ -291,15 +364,14 @@ impl Common {
|
||||||
if let Some(new) = popup_grab.current_grab() {
|
if let Some(new) = popup_grab.current_grab() {
|
||||||
trace!("restore focus to previous popup grab");
|
trace!("restore focus to previous popup grab");
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
|
// TODO: verify whether cursor should be updated at end of popup grab
|
||||||
if let Some(keyboard) = seat.get_keyboard() {
|
update_focus_state(
|
||||||
keyboard.set_focus(
|
seat,
|
||||||
|
Some(&new),
|
||||||
state,
|
state,
|
||||||
Some(new.clone()),
|
Some(SERIAL_COUNTER.next_serial()),
|
||||||
SERIAL_COUNTER.next_serial(),
|
false,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
ActiveFocus::set(&seat, Some(new));
|
|
||||||
seat.user_data()
|
seat.user_data()
|
||||||
.get_or_insert::<PopupGrabData, _>(PopupGrabData::default)
|
.get_or_insert::<PopupGrabData, _>(PopupGrabData::default)
|
||||||
.set(Some(popup_grab));
|
.set(Some(popup_grab));
|
||||||
|
|
@ -337,12 +409,10 @@ impl Common {
|
||||||
// update keyboard focus
|
// update keyboard focus
|
||||||
let target = update_focus_target(&*shell, &seat, &output);
|
let target = update_focus_target(&*shell, &seat, &output);
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
|
//I can probably feature gate this condition
|
||||||
if let Some(keyboard) = seat.get_keyboard() {
|
|
||||||
debug!("Restoring focus to {:?}", target.as_ref());
|
debug!("Restoring focus to {:?}", target.as_ref());
|
||||||
keyboard.set_focus(state, target.clone(), SERIAL_COUNTER.next_serial());
|
|
||||||
ActiveFocus::set(&seat, target);
|
update_focus_state(seat, target.as_ref(), state, None, false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ fn toggle_stacking(state: &mut State, mapped: &CosmicMapped) {
|
||||||
let seat = shell.seats.last_active().clone();
|
let seat = shell.seats.last_active().clone();
|
||||||
if let Some(new_focus) = shell.toggle_stacking(&seat, mapped) {
|
if let Some(new_focus) = shell.toggle_stacking(&seat, mapped) {
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
Shell::set_focus(state, Some(&new_focus), &seat, None);
|
Shell::set_focus(state, Some(&new_focus), &seat, None, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,7 +52,7 @@ fn move_prev_workspace(state: &mut State, mapped: &CosmicMapped) {
|
||||||
);
|
);
|
||||||
if let Some((target, _)) = res {
|
if let Some((target, _)) = res {
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
Shell::set_focus(state, Some(&target), &seat, None);
|
Shell::set_focus(state, Some(&target), &seat, None, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -85,7 +85,7 @@ fn move_next_workspace(state: &mut State, mapped: &CosmicMapped) {
|
||||||
);
|
);
|
||||||
if let Some((target, _point)) = res {
|
if let Some((target, _point)) = res {
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
Shell::set_focus(state, Some(&target), &seat, None)
|
Shell::set_focus(state, Some(&target), &seat, None, true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,7 @@ impl Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Menu that comes up when right-clicking an application header bar
|
||||||
pub struct ContextMenu {
|
pub struct ContextMenu {
|
||||||
items: Vec<Item>,
|
items: Vec<Item>,
|
||||||
selected: AtomicBool,
|
selected: AtomicBool,
|
||||||
|
|
|
||||||
|
|
@ -882,6 +882,7 @@ impl Drop for MoveGrab {
|
||||||
Some(&KeyboardFocusTarget::from(mapped)),
|
Some(&KeyboardFocusTarget::from(mapped)),
|
||||||
&seat,
|
&seat,
|
||||||
Some(serial),
|
Some(serial),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -728,7 +728,7 @@ impl FloatingLayout {
|
||||||
self.space.element_geometry(elem).map(RectExt::as_local)
|
self.space.element_geometry(elem).map(RectExt::as_local)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn element_under(&mut self, location: Point<f64, Local>) -> Option<KeyboardFocusTarget> {
|
pub fn element_under(&self, location: Point<f64, Local>) -> Option<KeyboardFocusTarget> {
|
||||||
self.space
|
self.space
|
||||||
.element_under(location.as_logical())
|
.element_under(location.as_logical())
|
||||||
.map(|(mapped, _)| mapped.clone().into())
|
.map(|(mapped, _)| mapped.clone().into())
|
||||||
|
|
|
||||||
|
|
@ -3088,10 +3088,7 @@ impl TilingLayout {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn element_under(
|
pub fn element_under(&self, location_f64: Point<f64, Local>) -> Option<KeyboardFocusTarget> {
|
||||||
&mut self,
|
|
||||||
location_f64: Point<f64, Local>,
|
|
||||||
) -> Option<KeyboardFocusTarget> {
|
|
||||||
let location = location_f64.to_i32_round();
|
let location = location_f64.to_i32_round();
|
||||||
|
|
||||||
for (mapped, geo) in self.mapped() {
|
for (mapped, geo) in self.mapped() {
|
||||||
|
|
|
||||||
219
src/shell/mod.rs
219
src/shell/mod.rs
|
|
@ -8,6 +8,7 @@ use std::{
|
||||||
};
|
};
|
||||||
use wayland_backend::server::ClientId;
|
use wayland_backend::server::ClientId;
|
||||||
|
|
||||||
|
use crate::wayland::protocols::workspace::WorkspaceCapabilities;
|
||||||
use cosmic_comp_config::{
|
use cosmic_comp_config::{
|
||||||
workspace::{WorkspaceLayout, WorkspaceMode},
|
workspace::{WorkspaceLayout, WorkspaceMode},
|
||||||
TileBehavior,
|
TileBehavior,
|
||||||
|
|
@ -50,6 +51,8 @@ use smithay::{
|
||||||
xwayland::X11Surface,
|
xwayland::X11Surface,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use smithay::wayland::shell::wlr_layer::Layer as WlrLayer;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backend::render::animations::spring::{Spring, SpringParams},
|
backend::render::animations::spring::{Spring, SpringParams},
|
||||||
config::Config,
|
config::Config,
|
||||||
|
|
@ -69,8 +72,7 @@ use crate::{
|
||||||
toplevel_leave_workspace, ToplevelInfoState,
|
toplevel_leave_workspace, ToplevelInfoState,
|
||||||
},
|
},
|
||||||
workspace::{
|
workspace::{
|
||||||
WorkspaceCapabilities, WorkspaceGroupHandle, WorkspaceHandle, WorkspaceState,
|
WorkspaceGroupHandle, WorkspaceHandle, WorkspaceState, WorkspaceUpdateGuard,
|
||||||
WorkspaceUpdateGuard,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -1402,6 +1404,148 @@ impl Shell {
|
||||||
self.workspaces.active_mut(output)
|
self.workspaces.active_mut(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// get the parent output of the window which has keyboard focus (for a given seat)
|
||||||
|
pub fn get_focused_output(&self, focus_target: &KeyboardFocusTarget) -> Option<&Output> {
|
||||||
|
if let Some(focused_surface) = focus_target.wl_surface() {
|
||||||
|
self.visible_output_for_surface(&focused_surface)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Derives a keyboard focus target from a global position, and indicates whether the
|
||||||
|
/// the shell should start a move request event. Used during cursor related focus checks
|
||||||
|
pub fn keyboard_target_from_position(
|
||||||
|
&self,
|
||||||
|
global_position: Point<f64, Global>,
|
||||||
|
seat: &Seat<State>,
|
||||||
|
) -> (Option<KeyboardFocusTarget>, bool) {
|
||||||
|
let output = seat.active_output();
|
||||||
|
// if not done and super key pressed
|
||||||
|
let mut grab_conditions_met = false;
|
||||||
|
let relative_pos = global_position.to_local(&output);
|
||||||
|
|
||||||
|
let mut under: Option<KeyboardFocusTarget> = None;
|
||||||
|
// if the lockscreen is active
|
||||||
|
if let Some(session_lock) = self.session_lock.as_ref() {
|
||||||
|
under = session_lock
|
||||||
|
.surfaces
|
||||||
|
.get(&output)
|
||||||
|
.map(|lock| lock.clone().into());
|
||||||
|
// if the output can receive keyboard focus
|
||||||
|
} else if let Some(window) = self.active_space(&output).get_fullscreen() {
|
||||||
|
let layers = layer_map_for_output(&output);
|
||||||
|
if let Some(layer) = layers.layer_under(WlrLayer::Overlay, relative_pos.as_logical()) {
|
||||||
|
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
|
||||||
|
if layer.can_receive_keyboard_focus()
|
||||||
|
&& layer
|
||||||
|
.surface_under(
|
||||||
|
relative_pos.as_logical() - layer_loc.to_f64(),
|
||||||
|
WindowSurfaceType::ALL,
|
||||||
|
)
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
under = Some(layer.clone().into());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
under = Some(window.clone().into());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let done = {
|
||||||
|
let layers = layer_map_for_output(&output);
|
||||||
|
if let Some(layer) = layers
|
||||||
|
.layer_under(WlrLayer::Overlay, relative_pos.as_logical())
|
||||||
|
.or_else(|| layers.layer_under(WlrLayer::Top, relative_pos.as_logical()))
|
||||||
|
{
|
||||||
|
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
|
||||||
|
if layer.can_receive_keyboard_focus()
|
||||||
|
&& layer
|
||||||
|
.surface_under(
|
||||||
|
relative_pos.as_logical() - layer_loc.to_f64(),
|
||||||
|
WindowSurfaceType::ALL,
|
||||||
|
)
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
under = Some(layer.clone().into());
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if !done {
|
||||||
|
// Don't check override redirect windows, because we don't set keyboard focus to them explicitly.
|
||||||
|
// These cases are handled by the XwaylandKeyboardGrab.
|
||||||
|
if let Some(target) = self.element_under(global_position, &output) {
|
||||||
|
if !seat.get_keyboard().unwrap().modifier_state().logo {
|
||||||
|
under = Some(target);
|
||||||
|
} else {
|
||||||
|
grab_conditions_met = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let layers = layer_map_for_output(&output);
|
||||||
|
if let Some(layer) = layers
|
||||||
|
.layer_under(WlrLayer::Bottom, relative_pos.as_logical())
|
||||||
|
.or_else(|| {
|
||||||
|
layers.layer_under(WlrLayer::Background, relative_pos.as_logical())
|
||||||
|
})
|
||||||
|
{
|
||||||
|
let layer_loc = layers.layer_geometry(layer).unwrap().loc;
|
||||||
|
if layer.can_receive_keyboard_focus()
|
||||||
|
&& layer
|
||||||
|
.surface_under(
|
||||||
|
relative_pos.as_logical() - layer_loc.to_f64(),
|
||||||
|
WindowSurfaceType::ALL,
|
||||||
|
)
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
under = Some(layer.clone().into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(under, grab_conditions_met)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Coerce a keyboard focus target into a CosmicMapped element. This is useful when performing window specific
|
||||||
|
/// actions, such as closing a window
|
||||||
|
pub fn focused_element(&self, focus_target: &KeyboardFocusTarget) -> Option<CosmicMapped> {
|
||||||
|
match focus_target {
|
||||||
|
KeyboardFocusTarget::Fullscreen(surface) => self.element_for_surface(surface).cloned(),
|
||||||
|
KeyboardFocusTarget::Element(window) => Some(window).cloned(),
|
||||||
|
KeyboardFocusTarget::Popup(PopupKind::Xdg(popup)) => {
|
||||||
|
if let Some(parent) = popup.get_parent_surface() {
|
||||||
|
self.element_for_surface(&parent).cloned()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KeyboardFocusTarget::Popup(PopupKind::InputMethod(popup)) => {
|
||||||
|
if let Some(parent) = popup.get_parent() {
|
||||||
|
self.element_for_surface(&parent.surface).cloned()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Close the focused keyboard focus target
|
||||||
|
pub fn close_focused(&self, focus_target: &KeyboardFocusTarget) {
|
||||||
|
if let KeyboardFocusTarget::Group(_group) = focus_target {
|
||||||
|
//TODO: decide if we want close actions to apply to groups
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
if let Some(mapped) = self.focused_element(focus_target) {
|
||||||
|
mapped.send_close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn refresh_active_space(
|
pub fn refresh_active_space(
|
||||||
&mut self,
|
&mut self,
|
||||||
output: &Output,
|
output: &Output,
|
||||||
|
|
@ -2130,11 +2274,11 @@ impl Shell {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn element_under(
|
pub fn element_under(
|
||||||
&mut self,
|
&self,
|
||||||
location: Point<f64, Global>,
|
location: Point<f64, Global>,
|
||||||
output: &Output,
|
output: &Output,
|
||||||
) -> Option<KeyboardFocusTarget> {
|
) -> Option<KeyboardFocusTarget> {
|
||||||
self.workspaces.sets.get_mut(output).and_then(|set| {
|
self.workspaces.sets.get(output).and_then(|set| {
|
||||||
set.sticky_layer
|
set.sticky_layer
|
||||||
.space
|
.space
|
||||||
.element_under(location.to_local(output).as_logical())
|
.element_under(location.to_local(output).as_logical())
|
||||||
|
|
@ -2641,20 +2785,59 @@ impl Shell {
|
||||||
Some((grab, Focus::Clear))
|
Some((grab, Focus::Clear))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Just to avoid a longer lived shell reference
|
||||||
|
/// Get the window geometry of a keyboard focus target
|
||||||
|
pub fn focused_geometry(&self, target: &KeyboardFocusTarget) -> Option<Rectangle<i32, Global>> {
|
||||||
|
if let Some(element) = self.focused_element(target) {
|
||||||
|
self.element_geometry(&element)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn element_geometry(&self, mapped: &CosmicMapped) -> Option<Rectangle<i32, Global>> {
|
||||||
|
if let Some(set) = self
|
||||||
|
.workspaces
|
||||||
|
.sets
|
||||||
|
.values()
|
||||||
|
.find(|set| set.sticky_layer.mapped().any(|m| m == mapped))
|
||||||
|
{
|
||||||
|
let geometry = set
|
||||||
|
.sticky_layer
|
||||||
|
.element_geometry(mapped)
|
||||||
|
.unwrap()
|
||||||
|
.to_global(&set.output);
|
||||||
|
Some(geometry)
|
||||||
|
} else if let Some(workspace) = self.space_for(&mapped) {
|
||||||
|
let geometry = workspace
|
||||||
|
.element_geometry(&mapped)
|
||||||
|
.unwrap()
|
||||||
|
.to_global(workspace.output());
|
||||||
|
Some(geometry)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn next_focus(&self, direction: FocusDirection, seat: &Seat<State>) -> FocusResult {
|
pub fn next_focus(&self, direction: FocusDirection, seat: &Seat<State>) -> FocusResult {
|
||||||
let overview = self.overview_mode().0;
|
let overview = self.overview_mode().0;
|
||||||
let output = seat.active_output();
|
|
||||||
|
|
||||||
let Some(target) = seat.get_keyboard().unwrap().current_focus() else {
|
let Some(target) = seat.get_keyboard().unwrap().current_focus() else {
|
||||||
return FocusResult::None;
|
return FocusResult::None;
|
||||||
};
|
};
|
||||||
|
let output = self.get_focused_output(&target).unwrap();
|
||||||
|
|
||||||
|
let workspace = self.active_space(output);
|
||||||
|
|
||||||
|
if workspace.fullscreen.is_some() {
|
||||||
|
return FocusResult::None;
|
||||||
|
}
|
||||||
|
|
||||||
if matches!(target, KeyboardFocusTarget::Fullscreen(_)) {
|
if matches!(target, KeyboardFocusTarget::Fullscreen(_)) {
|
||||||
return FocusResult::None;
|
return FocusResult::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let set = self.workspaces.sets.get(&output).unwrap();
|
let set = self.workspaces.sets.get(output).unwrap();
|
||||||
let sticky_layer = &set.sticky_layer;
|
let sticky_layer = &set.sticky_layer;
|
||||||
let workspace = &set.workspaces[set.active];
|
let workspace = &set.workspaces[set.active];
|
||||||
|
|
||||||
|
|
@ -2791,8 +2974,17 @@ impl Shell {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn move_current_element(&mut self, direction: Direction, seat: &Seat<State>) -> MoveResult {
|
pub fn move_current_element<'a>(
|
||||||
let output = seat.active_output();
|
&mut self,
|
||||||
|
direction: Direction,
|
||||||
|
seat: &Seat<State>,
|
||||||
|
) -> MoveResult {
|
||||||
|
let output = seat
|
||||||
|
.get_keyboard()
|
||||||
|
.unwrap()
|
||||||
|
.current_focus()
|
||||||
|
.and_then(|target| self.get_focused_output(&target).cloned())
|
||||||
|
.unwrap();
|
||||||
let workspace = self.active_space(&output);
|
let workspace = self.active_space(&output);
|
||||||
let focus_stack = workspace.focus_stack.get(seat);
|
let focus_stack = workspace.focus_stack.get(seat);
|
||||||
let Some(last) = focus_stack.last().cloned() else {
|
let Some(last) = focus_stack.last().cloned() else {
|
||||||
|
|
@ -3133,7 +3325,9 @@ impl Shell {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize(&mut self, seat: &Seat<State>, direction: ResizeDirection, edge: ResizeEdge) {
|
pub fn resize(&mut self, seat: &Seat<State>, direction: ResizeDirection, edge: ResizeEdge) {
|
||||||
let output = seat.active_output();
|
let Some(output) = seat.focused_output() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let (_, idx) = self.workspaces.active_num(&output);
|
let (_, idx) = self.workspaces.active_num(&output);
|
||||||
let Some(focused) = seat.get_keyboard().unwrap().current_focus() else {
|
let Some(focused) = seat.get_keyboard().unwrap().current_focus() else {
|
||||||
return;
|
return;
|
||||||
|
|
@ -3234,7 +3428,10 @@ impl Shell {
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn toggle_stacking_focused(&mut self, seat: &Seat<State>) -> Option<KeyboardFocusTarget> {
|
pub fn toggle_stacking_focused(&mut self, seat: &Seat<State>) -> Option<KeyboardFocusTarget> {
|
||||||
let set = self.workspaces.sets.get_mut(&seat.active_output()).unwrap();
|
let Some(focused_output) = seat.focused_output() else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let set = self.workspaces.sets.get_mut(&focused_output).unwrap();
|
||||||
let workspace = &mut set.workspaces[set.active];
|
let workspace = &mut set.workspaces[set.active];
|
||||||
let maybe_window = workspace.focus_stack.get(seat).iter().next().cloned();
|
let maybe_window = workspace.focus_stack.get(seat).iter().next().cloned();
|
||||||
if let Some(window) = maybe_window {
|
if let Some(window) = maybe_window {
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,10 @@ use super::grabs::{SeatMenuGrabState, SeatMoveGrabState};
|
||||||
|
|
||||||
crate::utils::id_gen!(next_seat_id, SEAT_ID, SEAT_IDS);
|
crate::utils::id_gen!(next_seat_id, SEAT_ID, SEAT_IDS);
|
||||||
|
|
||||||
|
// for more information on seats, see:
|
||||||
|
// <https://wayland-book.com/print.html#seats-handling-input>
|
||||||
|
/// Seats are an abstraction over a set of input devices grouped together, such as a keyboard, pointer and touch device.
|
||||||
|
/// i.e. Those used by a user to operate the computer.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Seats {
|
pub struct Seats {
|
||||||
seats: Vec<Seat<State>>,
|
seats: Vec<Seat<State>>,
|
||||||
|
|
@ -155,8 +159,13 @@ impl Drop for SeatId {
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
struct SeatId(pub usize);
|
struct SeatId(pub usize);
|
||||||
|
|
||||||
|
/// The output which contains the cursor associated with a seat.
|
||||||
struct ActiveOutput(pub Mutex<Output>);
|
struct ActiveOutput(pub Mutex<Output>);
|
||||||
|
|
||||||
|
/// The output which currently has keyboard focus
|
||||||
|
struct FocusedOutput(pub Mutex<Option<Output>>);
|
||||||
|
|
||||||
pub fn create_seat(
|
pub fn create_seat(
|
||||||
dh: &DisplayHandle,
|
dh: &DisplayHandle,
|
||||||
seat_state: &mut SeatState<State>,
|
seat_state: &mut SeatState<State>,
|
||||||
|
|
@ -175,6 +184,7 @@ pub fn create_seat(
|
||||||
userdata.insert_if_missing_threadsafe(SeatMenuGrabState::default);
|
userdata.insert_if_missing_threadsafe(SeatMenuGrabState::default);
|
||||||
userdata.insert_if_missing_threadsafe(CursorState::default);
|
userdata.insert_if_missing_threadsafe(CursorState::default);
|
||||||
userdata.insert_if_missing_threadsafe(|| ActiveOutput(Mutex::new(output.clone())));
|
userdata.insert_if_missing_threadsafe(|| ActiveOutput(Mutex::new(output.clone())));
|
||||||
|
userdata.insert_if_missing_threadsafe(|| FocusedOutput(Mutex::new(None)));
|
||||||
userdata.insert_if_missing_threadsafe(|| Mutex::new(CursorImageStatus::default_named()));
|
userdata.insert_if_missing_threadsafe(|| Mutex::new(CursorImageStatus::default_named()));
|
||||||
|
|
||||||
// A lot of clients bind keyboard and pointer unconditionally once on launch..
|
// A lot of clients bind keyboard and pointer unconditionally once on launch..
|
||||||
|
|
@ -213,7 +223,13 @@ pub trait SeatExt {
|
||||||
fn id(&self) -> usize;
|
fn id(&self) -> usize;
|
||||||
|
|
||||||
fn active_output(&self) -> Output;
|
fn active_output(&self) -> Output;
|
||||||
|
fn focused_output(&self) -> Option<Output>;
|
||||||
|
fn focused_or_active_output(&self) -> Output {
|
||||||
|
self.focused_output()
|
||||||
|
.unwrap_or_else(|| self.active_output())
|
||||||
|
}
|
||||||
fn set_active_output(&self, output: &Output);
|
fn set_active_output(&self, output: &Output);
|
||||||
|
fn set_focused_output(&self, output: Option<&Output>);
|
||||||
fn devices(&self) -> &Devices;
|
fn devices(&self) -> &Devices;
|
||||||
fn supressed_keys(&self) -> &SupressedKeys;
|
fn supressed_keys(&self) -> &SupressedKeys;
|
||||||
fn supressed_buttons(&self) -> &SupressedButtons;
|
fn supressed_buttons(&self) -> &SupressedButtons;
|
||||||
|
|
@ -231,6 +247,9 @@ impl SeatExt for Seat<State> {
|
||||||
self.user_data().get::<SeatId>().unwrap().0
|
self.user_data().get::<SeatId>().unwrap().0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the output that contains the cursor associated with a seat. Note that the window which has keyboard focus
|
||||||
|
/// may be on a different output. Currently, to get the focused output, first get the keyboard focus target and pass
|
||||||
|
/// it to get_focused_output in the shell.
|
||||||
fn active_output(&self) -> Output {
|
fn active_output(&self) -> Output {
|
||||||
self.user_data()
|
self.user_data()
|
||||||
.get::<ActiveOutput>()
|
.get::<ActiveOutput>()
|
||||||
|
|
@ -238,6 +257,21 @@ impl SeatExt for Seat<State> {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the output which currently has keyboard focus. If no window has keyboard focus (e.g. when there are no windows)
|
||||||
|
/// the focused output will be the same as the active output.
|
||||||
|
fn focused_output(&self) -> Option<Output> {
|
||||||
|
if self
|
||||||
|
.get_keyboard()
|
||||||
|
.is_some_and(|k| k.current_focus().is_some())
|
||||||
|
{
|
||||||
|
self.user_data()
|
||||||
|
.get::<FocusedOutput>()
|
||||||
|
.map(|x| x.0.lock().unwrap().clone())?
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn set_active_output(&self, output: &Output) {
|
fn set_active_output(&self, output: &Output) {
|
||||||
*self
|
*self
|
||||||
.user_data()
|
.user_data()
|
||||||
|
|
@ -248,6 +282,16 @@ impl SeatExt for Seat<State> {
|
||||||
.unwrap() = output.clone();
|
.unwrap() = output.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_focused_output(&self, output: Option<&Output>) {
|
||||||
|
*self
|
||||||
|
.user_data()
|
||||||
|
.get::<FocusedOutput>()
|
||||||
|
.unwrap()
|
||||||
|
.0
|
||||||
|
.lock()
|
||||||
|
.unwrap() = output.cloned();
|
||||||
|
}
|
||||||
|
|
||||||
fn devices(&self) -> &Devices {
|
fn devices(&self) -> &Devices {
|
||||||
self.user_data().get::<Devices>().unwrap()
|
self.user_data().get::<Devices>().unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -177,6 +177,7 @@ impl IsAlive for FullscreenSurface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// LIFO stack of focus targets
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct FocusStacks(HashMap<Seat<State>, IndexSet<CosmicMapped>>);
|
pub struct FocusStacks(HashMap<Seat<State>, IndexSet<CosmicMapped>>);
|
||||||
|
|
||||||
|
|
@ -444,7 +445,7 @@ impl Workspace {
|
||||||
.find(|e| e.windows().any(|(w, _)| &w == surface))
|
.find(|e| e.windows().any(|(w, _)| &w == surface))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn element_under(&mut self, location: Point<f64, Global>) -> Option<KeyboardFocusTarget> {
|
pub fn element_under(&self, location: Point<f64, Global>) -> Option<KeyboardFocusTarget> {
|
||||||
let location = location.to_local(&self.output);
|
let location = location.to_local(&self.output);
|
||||||
self.floating_layer
|
self.floating_layer
|
||||||
.element_under(location)
|
.element_under(location)
|
||||||
|
|
@ -784,6 +785,8 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the content of the current display if it is alive and
|
||||||
|
/// not in the process of rendering an animation
|
||||||
pub fn get_fullscreen(&self) -> Option<&CosmicSurface> {
|
pub fn get_fullscreen(&self) -> Option<&CosmicSurface> {
|
||||||
self.fullscreen
|
self.fullscreen
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
x11::X11State,
|
x11::X11State,
|
||||||
},
|
},
|
||||||
config::{Config, OutputConfig, OutputState},
|
config::{Config, OutputConfig, OutputState},
|
||||||
input::gestures::GestureState,
|
input::{gestures::GestureState, PointerFocusState},
|
||||||
shell::{grabs::SeatMoveGrabState, CosmicSurface, SeatExt, Shell},
|
shell::{grabs::SeatMoveGrabState, CosmicSurface, SeatExt, Shell},
|
||||||
utils::prelude::OutputExt,
|
utils::prelude::OutputExt,
|
||||||
wayland::protocols::{
|
wayland::protocols::{
|
||||||
|
|
@ -226,6 +226,7 @@ pub struct Common {
|
||||||
pub xwayland_scale: Option<i32>,
|
pub xwayland_scale: Option<i32>,
|
||||||
pub xwayland_state: Option<XWaylandState>,
|
pub xwayland_state: Option<XWaylandState>,
|
||||||
pub xwayland_shell_state: XWaylandShellState,
|
pub xwayland_shell_state: XWaylandShellState,
|
||||||
|
pub pointer_focus_state: Option<PointerFocusState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -621,6 +622,7 @@ impl State {
|
||||||
xwayland_scale: None,
|
xwayland_scale: None,
|
||||||
xwayland_state: None,
|
xwayland_state: None,
|
||||||
xwayland_shell_state,
|
xwayland_shell_state,
|
||||||
|
pointer_focus_state: None,
|
||||||
},
|
},
|
||||||
backend: BackendData::Unset,
|
backend: BackendData::Unset,
|
||||||
ready: Once::new(),
|
ready: Once::new(),
|
||||||
|
|
|
||||||
|
|
@ -272,7 +272,7 @@ impl State {
|
||||||
if let Some(target) = res {
|
if let Some(target) = res {
|
||||||
let seat = shell.seats.last_active().clone();
|
let seat = shell.seats.last_active().clone();
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
Shell::set_focus(self, Some(&target), &seat, None);
|
Shell::set_focus(self, Some(&target), &seat, None, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -290,7 +290,7 @@ impl State {
|
||||||
if let Some(target) = shell.map_layer(&layer_surface) {
|
if let Some(target) = shell.map_layer(&layer_surface) {
|
||||||
let seat = shell.seats.last_active().clone();
|
let seat = shell.seats.last_active().clone();
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
Shell::set_focus(self, Some(&target), &seat, None);
|
Shell::set_focus(self, Some(&target), &seat, None, false);
|
||||||
}
|
}
|
||||||
layer_surface.layer_surface().send_configure();
|
layer_surface.layer_surface().send_configure();
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ impl ToplevelManagementHandler for State {
|
||||||
}
|
}
|
||||||
|
|
||||||
mapped.focus_window(window);
|
mapped.focus_window(window);
|
||||||
Shell::set_focus(self, Some(&mapped.clone().into()), &seat, None);
|
Shell::set_focus(self, Some(&mapped.clone().into()), &seat, None, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -133,7 +133,7 @@ impl ToplevelManagementHandler for State {
|
||||||
);
|
);
|
||||||
if let Some((target, _)) = res {
|
if let Some((target, _)) = res {
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
Shell::set_focus(self, Some(&target), &seat, None);
|
Shell::set_focus(self, Some(&target), &seat, None, true);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,7 @@ impl XdgActivationHandler for State {
|
||||||
let target = element.into();
|
let target = element.into();
|
||||||
|
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
Shell::set_focus(self, Some(&target), &seat, None);
|
Shell::set_focus(self, Some(&target), &seat, None, false);
|
||||||
} else if let Some(w) = shell.space_for(&element).map(|w| w.handle.clone())
|
} else if let Some(w) = shell.space_for(&element).map(|w| w.handle.clone())
|
||||||
{
|
{
|
||||||
shell.append_focus_stack(&element, &seat);
|
shell.append_focus_stack(&element, &seat);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use smithay::{delegate_xdg_foreign, wayland::xdg_foreign::{XdgForeignHandler, XdgForeignState}};
|
|
||||||
use crate::state::State;
|
use crate::state::State;
|
||||||
|
use smithay::{
|
||||||
|
delegate_xdg_foreign,
|
||||||
|
wayland::xdg_foreign::{XdgForeignHandler, XdgForeignState},
|
||||||
|
};
|
||||||
|
|
||||||
impl XdgForeignHandler for State {
|
impl XdgForeignHandler for State {
|
||||||
fn xdg_foreign_state(&mut self) -> &mut XdgForeignState {
|
fn xdg_foreign_state(&mut self) -> &mut XdgForeignState {
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,13 @@ impl XdgShellHandler for State {
|
||||||
grab.ungrab(PopupUngrabStrategy::All);
|
grab.ungrab(PopupUngrabStrategy::All);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Shell::set_focus(self, grab.current_grab().as_ref(), &seat, Some(serial));
|
Shell::set_focus(
|
||||||
|
self,
|
||||||
|
grab.current_grab().as_ref(),
|
||||||
|
&seat,
|
||||||
|
Some(serial),
|
||||||
|
false,
|
||||||
|
);
|
||||||
keyboard.set_grab(self, PopupKeyboardGrab::new(&grab), serial);
|
keyboard.set_grab(self, PopupKeyboardGrab::new(&grab), serial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -226,11 +232,13 @@ impl XdgShellHandler for State {
|
||||||
fn fullscreen_request(&mut self, surface: ToplevelSurface, output: Option<WlOutput>) {
|
fn fullscreen_request(&mut self, surface: ToplevelSurface, output: Option<WlOutput>) {
|
||||||
let mut shell = self.common.shell.write().unwrap();
|
let mut shell = self.common.shell.write().unwrap();
|
||||||
let seat = shell.seats.last_active().clone();
|
let seat = shell.seats.last_active().clone();
|
||||||
let active_output = seat.active_output();
|
let Some(focused_output) = seat.focused_output() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let output = output
|
let output = output
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(Output::from_resource)
|
.and_then(Output::from_resource)
|
||||||
.unwrap_or_else(|| active_output.clone());
|
.unwrap_or_else(|| focused_output.clone());
|
||||||
|
|
||||||
if let Some(mapped) = shell.element_for_surface(surface.wl_surface()).cloned() {
|
if let Some(mapped) = shell.element_for_surface(surface.wl_surface()).cloned() {
|
||||||
let from = minimize_rectangle(&output, &mapped.active_window());
|
let from = minimize_rectangle(&output, &mapped.active_window());
|
||||||
|
|
|
||||||
|
|
@ -390,7 +390,7 @@ impl XwmHandler for State {
|
||||||
if let Some(target) = res {
|
if let Some(target) = res {
|
||||||
let seat = shell.seats.last_active().clone();
|
let seat = shell.seats.last_active().clone();
|
||||||
std::mem::drop(shell);
|
std::mem::drop(shell);
|
||||||
Shell::set_focus(self, Some(&target), &seat, None);
|
Shell::set_focus(self, Some(&target), &seat, None, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue