mirror of
https://github.com/YaLTeR/niri.git
synced 2024-10-05 16:27:51 +03:00
Compare commits
4 Commits
1161738fc5
...
65889d3b63
Author | SHA1 | Date | |
---|---|---|---|
|
65889d3b63 | ||
|
e394a7ff20 | ||
|
921ed63204 | ||
|
e9a93cae11 |
@ -495,7 +495,7 @@ pub enum Transform {
|
||||
}
|
||||
|
||||
/// Toplevel window.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[derive(Eq, PartialEq, Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct Window {
|
||||
/// Title, if set.
|
||||
pub title: Option<String>,
|
||||
@ -527,6 +527,8 @@ pub struct Workspace {
|
||||
pub output: Option<String>,
|
||||
/// Whether the workspace is currently active on its output.
|
||||
pub is_active: bool,
|
||||
/// The windows currently on this workspace
|
||||
pub windows: Vec<Window>,
|
||||
}
|
||||
|
||||
impl FromStr for WorkspaceReferenceArg {
|
||||
|
@ -372,6 +372,7 @@ impl ForeignToplevelHandler for State {
|
||||
if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&wl_surface) {
|
||||
let window = mapped.window.clone();
|
||||
self.niri.layout.activate_window(&window);
|
||||
self.niri.layer_shell_on_demand_focus = None;
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
}
|
||||
@ -540,6 +541,7 @@ impl XdgActivationHandler for State {
|
||||
if let Some((mapped, _)) = self.niri.layout.find_window_and_output(&surface) {
|
||||
let window = mapped.window.clone();
|
||||
self.niri.layout.activate_window(&window);
|
||||
self.niri.layer_shell_on_demand_focus = None;
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
}
|
||||
|
@ -110,11 +110,13 @@ impl XdgShellHandler for State {
|
||||
if intersection.intersects(ResizeEdge::LEFT_RIGHT) {
|
||||
// FIXME: don't activate once we can pass specific windows to actions.
|
||||
self.niri.layout.activate_window(&window);
|
||||
self.niri.layer_shell_on_demand_focus = None;
|
||||
self.niri.layout.toggle_full_width();
|
||||
}
|
||||
if intersection.intersects(ResizeEdge::TOP_BOTTOM) {
|
||||
// FIXME: don't activate once we can pass specific windows to actions.
|
||||
self.niri.layout.activate_window(&window);
|
||||
self.niri.layer_shell_on_demand_focus = None;
|
||||
self.niri.layout.reset_window_height();
|
||||
}
|
||||
// FIXME: granular.
|
||||
@ -184,10 +186,13 @@ impl XdgShellHandler for State {
|
||||
let _ = PopupManager::dismiss_popup(&root, &popup);
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: popup grabs for on-demand bottom and background layers.
|
||||
} else {
|
||||
if layers.layers_on(Layer::Overlay).any(|l| {
|
||||
l.cached_state().keyboard_interactivity
|
||||
== wlr_layer::KeyboardInteractivity::Exclusive
|
||||
|| Some(l) == self.niri.layer_shell_on_demand_focus.as_ref()
|
||||
}) {
|
||||
let _ = PopupManager::dismiss_popup(&root, &popup);
|
||||
return;
|
||||
@ -198,6 +203,7 @@ impl XdgShellHandler for State {
|
||||
&& layers.layers_on(Layer::Top).any(|l| {
|
||||
l.cached_state().keyboard_interactivity
|
||||
== wlr_layer::KeyboardInteractivity::Exclusive
|
||||
|| Some(l) == self.niri.layer_shell_on_demand_focus.as_ref()
|
||||
})
|
||||
{
|
||||
let _ = PopupManager::dismiss_popup(&root, &popup);
|
||||
|
@ -1404,6 +1404,11 @@ impl State {
|
||||
|
||||
self.update_pointer_focus();
|
||||
|
||||
if ButtonState::Pressed == button_state {
|
||||
let layer_focus = self.niri.pointer_focus.layer.clone();
|
||||
self.niri.focus_layer_surface_if_on_demand(layer_focus);
|
||||
}
|
||||
|
||||
if let Some(button) = event.button() {
|
||||
let pos = pointer.current_location();
|
||||
if let Some((output, _)) = self.niri.output_under(pos) {
|
||||
@ -1685,19 +1690,19 @@ impl State {
|
||||
tool.tip_down(serial, event.time_msec());
|
||||
|
||||
if let Some(pos) = self.niri.tablet_cursor_location {
|
||||
if let Some(mapped) = self.niri.window_under(pos) {
|
||||
let window = mapped.window.clone();
|
||||
let under = self.niri.surface_under_and_global_space(pos);
|
||||
if let Some(window) = under.window {
|
||||
self.niri.layout.activate_window(&window);
|
||||
|
||||
// FIXME: granular.
|
||||
self.niri.queue_redraw_all();
|
||||
} else if let Some((output, _)) = self.niri.output_under(pos) {
|
||||
let output = output.clone();
|
||||
} else if let Some(output) = under.output {
|
||||
self.niri.layout.activate_output(&output);
|
||||
|
||||
// FIXME: granular.
|
||||
self.niri.queue_redraw_all();
|
||||
}
|
||||
self.niri.focus_layer_surface_if_on_demand(under.layer);
|
||||
}
|
||||
}
|
||||
TabletToolTipState::Up => {
|
||||
@ -2042,29 +2047,24 @@ impl State {
|
||||
return;
|
||||
};
|
||||
|
||||
let under = self.niri.surface_under_and_global_space(touch_location);
|
||||
|
||||
if !handle.is_grabbed() {
|
||||
let output_under_touch = self
|
||||
.niri
|
||||
.global_space
|
||||
.output_under(touch_location)
|
||||
.next()
|
||||
.cloned();
|
||||
if let Some(mapped) = self.niri.window_under(touch_location) {
|
||||
let window = mapped.window.clone();
|
||||
if let Some(window) = under.window {
|
||||
self.niri.layout.activate_window(&window);
|
||||
|
||||
// FIXME: granular.
|
||||
self.niri.queue_redraw_all();
|
||||
} else if let Some(output) = output_under_touch {
|
||||
} else if let Some(output) = under.output {
|
||||
self.niri.layout.activate_output(&output);
|
||||
|
||||
// FIXME: granular.
|
||||
self.niri.queue_redraw_all();
|
||||
};
|
||||
}
|
||||
self.niri.focus_layer_surface_if_on_demand(under.layer);
|
||||
};
|
||||
|
||||
let serial = SERIAL_COUNTER.next_serial();
|
||||
let under = self.niri.surface_under_and_global_space(touch_location);
|
||||
handle.down(
|
||||
self,
|
||||
under.surface,
|
||||
|
@ -147,23 +147,12 @@ async fn process(ctx: &ClientCtx, request: Request) -> Reply {
|
||||
Response::Outputs(outputs.collect())
|
||||
}
|
||||
Request::FocusedWindow => {
|
||||
let window = ctx.ipc_focused_window.lock().unwrap().clone();
|
||||
let window = window.map(|window| {
|
||||
let wl_surface = window.toplevel().expect("no X11 support").wl_surface();
|
||||
with_states(wl_surface, |states| {
|
||||
let role = states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
|
||||
niri_ipc::Window {
|
||||
title: role.title.clone(),
|
||||
app_id: role.app_id.clone(),
|
||||
}
|
||||
})
|
||||
});
|
||||
let window = ctx
|
||||
.ipc_focused_window
|
||||
.lock()
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.map(smithay_window_to_ipc);
|
||||
Response::FocusedWindow(window)
|
||||
}
|
||||
Request::Action(action) => {
|
||||
@ -239,3 +228,20 @@ async fn process(ctx: &ClientCtx, request: Request) -> Reply {
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
pub fn smithay_window_to_ipc(window: &smithay::desktop::Window) -> niri_ipc::Window {
|
||||
let wl_surface = window.toplevel().expect("no X11 support").wl_surface();
|
||||
with_states(wl_surface, |states| {
|
||||
let role = states
|
||||
.data_map
|
||||
.get::<XdgToplevelSurfaceData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
|
||||
niri_ipc::Window {
|
||||
title: role.title.clone(),
|
||||
app_id: role.app_id.clone(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ use smithay::utils::{Logical, Point, Scale, Serial, Size, Transform};
|
||||
pub use self::monitor::MonitorRenderElement;
|
||||
use self::monitor::{Monitor, WorkspaceSwitch};
|
||||
use self::workspace::{compute_working_area, Column, ColumnWidth, OutputId, Workspace};
|
||||
use crate::ipc::server::smithay_window_to_ipc;
|
||||
use crate::niri_render_elements;
|
||||
use crate::render_helpers::renderer::NiriRenderer;
|
||||
use crate::render_helpers::snapshot::RenderSnapshot;
|
||||
@ -53,7 +54,7 @@ use crate::render_helpers::solid_color::{SolidColorBuffer, SolidColorRenderEleme
|
||||
use crate::render_helpers::texture::TextureBuffer;
|
||||
use crate::render_helpers::{BakedBuffer, RenderTarget, SplitElements};
|
||||
use crate::utils::{output_size, round_logical_in_physical_max1, ResizeEdge};
|
||||
use crate::window::ResolvedWindowRules;
|
||||
use crate::window::{Mapped, ResolvedWindowRules};
|
||||
|
||||
pub mod closing_window;
|
||||
pub mod focus_ring;
|
||||
@ -2440,7 +2441,9 @@ impl<W: LayoutElement> Layout<W> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout<Mapped> {
|
||||
pub fn ipc_workspaces(&self) -> Vec<niri_ipc::Workspace> {
|
||||
match &self.monitor_set {
|
||||
MonitorSet::Normal {
|
||||
@ -2457,6 +2460,10 @@ impl<W: LayoutElement> Layout<W> {
|
||||
name: workspace.name.clone(),
|
||||
output: Some(monitor.output.name()),
|
||||
is_active: monitor.active_workspace_idx == idx,
|
||||
windows: workspace
|
||||
.windows()
|
||||
.map(|mapped| smithay_window_to_ipc(&mapped.window))
|
||||
.collect(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -2471,6 +2478,10 @@ impl<W: LayoutElement> Layout<W> {
|
||||
name: ws.name.clone(),
|
||||
output: None,
|
||||
is_active: false,
|
||||
windows: ws
|
||||
.windows()
|
||||
.map(|mapped| smithay_window_to_ipc(&mapped.window))
|
||||
.collect(),
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
|
83
src/niri.rs
83
src/niri.rs
@ -59,8 +59,8 @@ use smithay::reexports::wayland_server::protocol::wl_shm;
|
||||
use smithay::reexports::wayland_server::protocol::wl_surface::WlSurface;
|
||||
use smithay::reexports::wayland_server::{Display, DisplayHandle, Resource};
|
||||
use smithay::utils::{
|
||||
ClockSource, Logical, Monotonic, Physical, Point, Rectangle, Scale, Size, Transform,
|
||||
SERIAL_COUNTER,
|
||||
ClockSource, IsAlive as _, Logical, Monotonic, Physical, Point, Rectangle, Scale, Size,
|
||||
Transform, SERIAL_COUNTER,
|
||||
};
|
||||
use smithay::wayland::compositor::{
|
||||
with_states, with_surface_tree_downward, CompositorClientState, CompositorState, SurfaceData,
|
||||
@ -236,6 +236,7 @@ pub struct Niri {
|
||||
pub bind_cooldown_timers: HashMap<Key, RegistrationToken>,
|
||||
pub bind_repeat_timer: Option<RegistrationToken>,
|
||||
pub keyboard_focus: KeyboardFocus,
|
||||
pub layer_shell_on_demand_focus: Option<LayerSurface>,
|
||||
pub idle_inhibiting_surfaces: HashSet<WlSurface>,
|
||||
pub is_fdo_idle_inhibited: Arc<AtomicBool>,
|
||||
|
||||
@ -367,6 +368,8 @@ pub struct PointerFocus {
|
||||
pub surface: Option<(WlSurface, Point<f64, Logical>)>,
|
||||
// If surface belongs to a window, this is that window.
|
||||
pub window: Option<Window>,
|
||||
// If surface belongs to a layer surface, this is that layer surface.
|
||||
pub layer: Option<LayerSurface>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -714,6 +717,18 @@ impl State {
|
||||
}
|
||||
|
||||
pub fn update_keyboard_focus(&mut self) {
|
||||
// Clean up on-demand layer surface focus if necessary.
|
||||
if let Some(surface) = &self.niri.layer_shell_on_demand_focus {
|
||||
// Still alive and has on-demand interactivity.
|
||||
let good = surface.alive()
|
||||
&& surface.cached_state().keyboard_interactivity
|
||||
== wlr_layer::KeyboardInteractivity::OnDemand;
|
||||
if !good {
|
||||
self.niri.layer_shell_on_demand_focus = None;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the current focus.
|
||||
let focus = if self.niri.is_locked() {
|
||||
KeyboardFocus::LockScreen {
|
||||
surface: self.niri.lock_surface_focus(),
|
||||
@ -747,10 +762,19 @@ impl State {
|
||||
})
|
||||
};
|
||||
let layer_focus = |surface: &LayerSurface| {
|
||||
// FIXME: support on-demand.
|
||||
let can_receive_keyboard_focus = surface.cached_state().keyboard_interactivity
|
||||
let can_receive_exclusive_focus = surface.cached_state().keyboard_interactivity
|
||||
== wlr_layer::KeyboardInteractivity::Exclusive;
|
||||
can_receive_keyboard_focus
|
||||
let is_on_demand_surface =
|
||||
Some(surface) == self.niri.layer_shell_on_demand_focus.as_ref();
|
||||
|
||||
(can_receive_exclusive_focus || is_on_demand_surface)
|
||||
.then(|| surface.wl_surface().clone())
|
||||
.map(|surface| KeyboardFocus::LayerShell { surface })
|
||||
};
|
||||
let on_d_focus = |surface: &LayerSurface| {
|
||||
let is_on_demand_surface =
|
||||
Some(surface) == self.niri.layer_shell_on_demand_focus.as_ref();
|
||||
is_on_demand_surface
|
||||
.then(|| surface.wl_surface().clone())
|
||||
.map(|surface| KeyboardFocus::LayerShell { surface })
|
||||
};
|
||||
@ -771,6 +795,10 @@ impl State {
|
||||
surface = surface.or_else(layout_focus);
|
||||
}
|
||||
|
||||
// Bottom and background layers can receive on-demand focus only.
|
||||
surface = surface.or_else(|| layers.layers_on(Layer::Bottom).find_map(on_d_focus));
|
||||
surface = surface.or_else(|| layers.layers_on(Layer::Background).find_map(on_d_focus));
|
||||
|
||||
surface.unwrap_or(KeyboardFocus::Layout { surface: None })
|
||||
} else {
|
||||
KeyboardFocus::Layout { surface: None }
|
||||
@ -1666,6 +1694,7 @@ impl Niri {
|
||||
|
||||
seat,
|
||||
keyboard_focus: KeyboardFocus::Layout { surface: None },
|
||||
layer_shell_on_demand_focus: None,
|
||||
idle_inhibiting_surfaces: HashSet::new(),
|
||||
is_fdo_idle_inhibited: Arc::new(AtomicBool::new(false)),
|
||||
cursor_manager,
|
||||
@ -2143,10 +2172,13 @@ impl Niri {
|
||||
WindowSurfaceType::ALL,
|
||||
)
|
||||
.map(|(surface, pos_within_layer)| {
|
||||
(surface, pos_within_layer.to_f64() + layer_pos_within_output)
|
||||
(
|
||||
(surface, pos_within_layer.to_f64() + layer_pos_within_output),
|
||||
layer,
|
||||
)
|
||||
})
|
||||
})
|
||||
.map(|s| (s, None))
|
||||
.map(|(s, l)| (s, (None, Some(l.clone()))))
|
||||
};
|
||||
|
||||
let window_under = || {
|
||||
@ -2163,7 +2195,7 @@ impl Niri {
|
||||
.map(|(s, pos_within_window)| {
|
||||
(s, pos_within_window.to_f64() + win_pos_within_output)
|
||||
})
|
||||
.map(|s| (s, Some(window.clone())))
|
||||
.map(|s| (s, (Some(window.clone()), None)))
|
||||
})
|
||||
};
|
||||
|
||||
@ -2181,7 +2213,7 @@ impl Niri {
|
||||
.or_else(window_under);
|
||||
}
|
||||
|
||||
let Some(((surface, surface_pos_within_output), window)) = under
|
||||
let Some(((surface, surface_pos_within_output), (window, layer))) = under
|
||||
.or_else(|| layer_surface_under(Layer::Bottom))
|
||||
.or_else(|| layer_surface_under(Layer::Background))
|
||||
else {
|
||||
@ -2193,6 +2225,7 @@ impl Niri {
|
||||
|
||||
rv.surface = Some((surface, surface_loc_in_global_space));
|
||||
rv.window = window;
|
||||
rv.layer = layer;
|
||||
rv
|
||||
}
|
||||
|
||||
@ -4101,6 +4134,31 @@ impl Niri {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn focus_layer_surface_if_on_demand(&mut self, surface: Option<LayerSurface>) {
|
||||
if let Some(surface) = surface {
|
||||
if surface.cached_state().keyboard_interactivity
|
||||
== wlr_layer::KeyboardInteractivity::OnDemand
|
||||
{
|
||||
if self.layer_shell_on_demand_focus.as_ref() != Some(&surface) {
|
||||
self.layer_shell_on_demand_focus = Some(surface);
|
||||
|
||||
// FIXME: granular.
|
||||
self.queue_redraw_all();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Something else got clicked, clear on-demand layer-shell focus.
|
||||
if self.layer_shell_on_demand_focus.is_some() {
|
||||
self.layer_shell_on_demand_focus = None;
|
||||
|
||||
// FIXME: granular.
|
||||
self.queue_redraw_all();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dbus")]
|
||||
pub fn on_ipc_outputs_changed(&self) {
|
||||
let _span = tracy_client::span!("Niri::on_ipc_outputs_changed");
|
||||
@ -4167,6 +4225,13 @@ impl Niri {
|
||||
}
|
||||
|
||||
self.layout.activate_window(window);
|
||||
self.layer_shell_on_demand_focus = None;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(layer) = &new_focus.layer {
|
||||
if current_focus.layer.as_ref() != Some(layer) {
|
||||
self.layer_shell_on_demand_focus = Some(layer.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user