seat: add focus history
This commit is contained in:
parent
9941263a82
commit
d12234b38b
21 changed files with 546 additions and 22 deletions
|
|
@ -15,8 +15,8 @@ use {
|
|||
client::{Client, ClientCriterion, ClientMatcher, MatchedClient},
|
||||
exec::Command,
|
||||
input::{
|
||||
FocusFollowsMouseMode, InputDevice, Seat, SwitchEvent, acceleration::AccelProfile,
|
||||
capability::Capability, clickmethod::ClickMethod,
|
||||
FocusFollowsMouseMode, InputDevice, Seat, SwitchEvent, Timeline,
|
||||
acceleration::AccelProfile, capability::Capability, clickmethod::ClickMethod,
|
||||
},
|
||||
keyboard::{
|
||||
Keymap,
|
||||
|
|
@ -364,6 +364,21 @@ impl ConfigClient {
|
|||
self.send(&ClientMessage::GrabKb { kb, grab });
|
||||
}
|
||||
|
||||
pub fn seat_focus_history(&self, seat: Seat, timeline: Timeline) {
|
||||
self.send(&ClientMessage::SeatFocusHistory { seat, timeline });
|
||||
}
|
||||
|
||||
pub fn seat_focus_history_set_only_visible(&self, seat: Seat, only_visible: bool) {
|
||||
self.send(&ClientMessage::SeatFocusHistorySetOnlyVisible { seat, only_visible });
|
||||
}
|
||||
|
||||
pub fn seat_focus_history_set_same_workspace(&self, seat: Seat, same_workspace: bool) {
|
||||
self.send(&ClientMessage::SeatFocusHistorySetSameWorkspace {
|
||||
seat,
|
||||
same_workspace,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn seat_focus(&self, seat: Seat, direction: Direction) {
|
||||
self.send(&ClientMessage::SeatFocus { seat, direction });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ use {
|
|||
Axis, Direction, PciId, Workspace,
|
||||
client::{Client, ClientMatcher},
|
||||
input::{
|
||||
FocusFollowsMouseMode, InputDevice, Seat, SwitchEvent, acceleration::AccelProfile,
|
||||
capability::Capability, clickmethod::ClickMethod,
|
||||
FocusFollowsMouseMode, InputDevice, Seat, SwitchEvent, Timeline,
|
||||
acceleration::AccelProfile, capability::Capability, clickmethod::ClickMethod,
|
||||
},
|
||||
keyboard::{Keymap, mods::Modifiers, syms::KeySym},
|
||||
logging::LogLevel,
|
||||
|
|
@ -725,6 +725,18 @@ pub enum ClientMessage<'a> {
|
|||
show: bool,
|
||||
},
|
||||
GetShowBar,
|
||||
SeatFocusHistory {
|
||||
seat: Seat,
|
||||
timeline: Timeline,
|
||||
},
|
||||
SeatFocusHistorySetOnlyVisible {
|
||||
seat: Seat,
|
||||
only_visible: bool,
|
||||
},
|
||||
SeatFocusHistorySetSameWorkspace {
|
||||
seat: Seat,
|
||||
same_workspace: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
|
|
|||
|
|
@ -182,6 +182,13 @@ impl InputDevice {
|
|||
}
|
||||
}
|
||||
|
||||
/// A direction in a timeline.
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
pub enum Timeline {
|
||||
Older,
|
||||
Newer,
|
||||
}
|
||||
|
||||
/// A seat.
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
pub struct Seat(pub u64);
|
||||
|
|
@ -273,6 +280,29 @@ impl Seat {
|
|||
get!().unbind(self, mod_sym.into())
|
||||
}
|
||||
|
||||
/// Moves the focus in the focus history.
|
||||
pub fn focus_history(self, timeline: Timeline) {
|
||||
get!().seat_focus_history(self, timeline)
|
||||
}
|
||||
|
||||
/// Configures whether the focus history only includes visible windows.
|
||||
///
|
||||
/// If this is `false`, then hidden windows will be made visible before moving the
|
||||
/// focus to them.
|
||||
///
|
||||
/// The default is `false`.
|
||||
pub fn focus_history_set_only_visible(self, only_visible: bool) {
|
||||
get!().seat_focus_history_set_only_visible(self, only_visible)
|
||||
}
|
||||
|
||||
/// Configures whether the focus history only includes windows on the same workspace
|
||||
/// as the currently focused window.
|
||||
///
|
||||
/// The default is `false`.
|
||||
pub fn focus_history_set_same_workspace(self, same_workspace: bool) {
|
||||
get!().seat_focus_history_set_same_workspace(self, same_workspace)
|
||||
}
|
||||
|
||||
/// Moves the keyboard focus of the seat in the specified direction.
|
||||
pub fn focus(self, direction: Direction) {
|
||||
get!().seat_focus(self, direction)
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ use {
|
|||
Axis, Direction, Workspace,
|
||||
client::{Client as ConfigClient, ClientMatcher},
|
||||
input::{
|
||||
FocusFollowsMouseMode, InputDevice, Seat,
|
||||
FocusFollowsMouseMode, InputDevice, Seat, Timeline,
|
||||
acceleration::{ACCEL_PROFILE_ADAPTIVE, ACCEL_PROFILE_FLAT, AccelProfile},
|
||||
capability::{
|
||||
CAP_GESTURE, CAP_KEYBOARD, CAP_POINTER, CAP_SWITCH, CAP_TABLET_PAD,
|
||||
|
|
@ -2150,11 +2150,41 @@ impl ConfigProxyHandler {
|
|||
.set(matcher, (m, tile_state));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_pointer_revert_key(&self, seat: Seat, key: KeySym) -> Result<(), CphError> {
|
||||
self.get_seat(seat)?.set_pointer_revert_key(key);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_seat_focus_history(&self, seat: Seat, timeline: Timeline) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
match timeline {
|
||||
Timeline::Older => seat.focus_prev(),
|
||||
Timeline::Newer => seat.focus_next(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_seat_focus_history_set_only_visible(
|
||||
&self,
|
||||
seat: Seat,
|
||||
visible: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.focus_history_set_visible(visible);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_seat_focus_history_set_same_workspace(
|
||||
&self,
|
||||
seat: Seat,
|
||||
same_workspace: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.focus_history_set_same_workspace(same_workspace);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn spaces_change(&self) {
|
||||
struct V;
|
||||
impl NodeVisitorBase for V {
|
||||
|
|
@ -2997,6 +3027,18 @@ impl ConfigProxyHandler {
|
|||
.wrn("get_content_type")?,
|
||||
ClientMessage::SetShowBar { show } => self.handle_set_show_bar(show),
|
||||
ClientMessage::GetShowBar => self.handle_get_show_bar(),
|
||||
ClientMessage::SeatFocusHistory { seat, timeline } => self
|
||||
.handle_seat_focus_history(seat, timeline)
|
||||
.wrn("seat_focus_history")?,
|
||||
ClientMessage::SeatFocusHistorySetOnlyVisible { seat, only_visible } => self
|
||||
.handle_seat_focus_history_set_only_visible(seat, only_visible)
|
||||
.wrn("seat_focus_history_set_only_visible")?,
|
||||
ClientMessage::SeatFocusHistorySetSameWorkspace {
|
||||
seat,
|
||||
same_workspace,
|
||||
} => self
|
||||
.handle_seat_focus_history_set_same_workspace(seat, same_workspace)
|
||||
.wrn("seat_focus_history_set_same_workspace")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ use {
|
|||
},
|
||||
wl_output::WlOutputGlobal,
|
||||
wl_seat::{
|
||||
event_handling::FocusHistoryData,
|
||||
gesture_owner::GestureOwnerHolder,
|
||||
kb_owner::KbOwnerHolder,
|
||||
pointer_owner::PointerOwnerHolder,
|
||||
|
|
@ -84,8 +85,14 @@ use {
|
|||
toplevel_parent_container, toplevel_set_floating, toplevel_set_workspace,
|
||||
},
|
||||
utils::{
|
||||
asyncevent::AsyncEvent, bindings::PerClientBindings, clonecell::CloneCell,
|
||||
copyhashmap::CopyHashMap, linkedlist::LinkedNode, numcell::NumCell, rc_eq::rc_eq,
|
||||
asyncevent::AsyncEvent,
|
||||
bindings::PerClientBindings,
|
||||
clonecell::CloneCell,
|
||||
copyhashmap::CopyHashMap,
|
||||
linkedlist::{LinkedList, LinkedNode, NodeRef},
|
||||
numcell::NumCell,
|
||||
on_drop::OnDrop,
|
||||
rc_eq::{rc_eq, rc_weak_eq},
|
||||
smallmap::SmallMap,
|
||||
},
|
||||
wire::{
|
||||
|
|
@ -218,6 +225,11 @@ pub struct WlSeatGlobal {
|
|||
keyboard_node_serial: Cell<u64>,
|
||||
tray_popups: CopyHashMap<(TrayItemId, XdgPopupId), Rc<dyn DynTrayItem>>,
|
||||
revert_key: Cell<KeySym>,
|
||||
last_focus_location: Cell<Option<NodeLocation>>,
|
||||
focus_history: LinkedList<FocusHistoryData>,
|
||||
focus_history_rotate: NumCell<u64>,
|
||||
focus_history_visible_only: Cell<bool>,
|
||||
focus_history_same_workspace: Cell<bool>,
|
||||
}
|
||||
|
||||
const CHANGE_CURSOR_MOVED: u32 = 1 << 0;
|
||||
|
|
@ -292,6 +304,11 @@ impl WlSeatGlobal {
|
|||
ui_drag_highlight: Default::default(),
|
||||
tray_popups: Default::default(),
|
||||
revert_key: Cell::new(SYM_Escape),
|
||||
last_focus_location: Default::default(),
|
||||
focus_history: Default::default(),
|
||||
focus_history_rotate: Default::default(),
|
||||
focus_history_visible_only: Cell::new(false),
|
||||
focus_history_same_workspace: Cell::new(false),
|
||||
});
|
||||
slf.pointer_cursor.set_owner(slf.clone());
|
||||
let seat = slf.clone();
|
||||
|
|
@ -649,6 +666,141 @@ impl WlSeatGlobal {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_last_focus_on_workspace(&self, ws: &WorkspaceNode) -> Option<Rc<dyn Node>> {
|
||||
let mut node = self.focus_history.last()?;
|
||||
loop {
|
||||
if let Some(node) = node.node.upgrade()
|
||||
&& let Some(NodeLocation::Workspace(_, new)) = node.node_location()
|
||||
&& new == ws.id
|
||||
{
|
||||
return Some(node);
|
||||
}
|
||||
node = node.prev()?;
|
||||
}
|
||||
}
|
||||
|
||||
fn get_focus_history(
|
||||
&self,
|
||||
next: impl Fn(&NodeRef<FocusHistoryData>) -> Option<NodeRef<FocusHistoryData>>,
|
||||
first: impl FnOnce(&LinkedList<FocusHistoryData>) -> Option<NodeRef<FocusHistoryData>>,
|
||||
) -> Option<(Rc<dyn Node>, bool)> {
|
||||
let original = self.keyboard_node.get();
|
||||
let mut output = None;
|
||||
let mut workspace = None;
|
||||
if let Some(old) = original.node_location() {
|
||||
match old {
|
||||
NodeLocation::Workspace(o, w) => {
|
||||
workspace = Some(w);
|
||||
output = Some(o);
|
||||
}
|
||||
NodeLocation::Output(o) => {
|
||||
output = Some(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (output.is_none() || workspace.is_none())
|
||||
&& let Some(old) = self.last_focus_location.get()
|
||||
{
|
||||
match old {
|
||||
NodeLocation::Workspace(o, w) => {
|
||||
workspace = workspace.or(Some(w));
|
||||
output = output.or(Some(o));
|
||||
}
|
||||
NodeLocation::Output(o) => {
|
||||
output = output.or(Some(o));
|
||||
}
|
||||
}
|
||||
}
|
||||
if workspace.is_none()
|
||||
&& let Some(output) = original.node_output()
|
||||
&& let Some(ws) = output.workspace.get()
|
||||
{
|
||||
workspace = Some(ws.id);
|
||||
}
|
||||
let matches = |node: &FocusHistoryData| {
|
||||
let visible = node.visible.get();
|
||||
if self.focus_history_visible_only.get() && !visible {
|
||||
return None;
|
||||
}
|
||||
let node = node.node.upgrade()?;
|
||||
if self.focus_history_same_workspace.get() {
|
||||
let new = node.node_location()?;
|
||||
let o = match new {
|
||||
NodeLocation::Workspace(o, w) => {
|
||||
if workspace != Some(w) {
|
||||
return None;
|
||||
}
|
||||
o
|
||||
}
|
||||
NodeLocation::Output(o) => o,
|
||||
};
|
||||
if output != Some(o) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some((node, visible))
|
||||
};
|
||||
let node = original.node_seat_state().get_focus_history(self);
|
||||
if let Some(mut node) = node {
|
||||
loop {
|
||||
node = match next(&node) {
|
||||
Some(n) => n,
|
||||
_ => break,
|
||||
};
|
||||
if let Some(matches) = matches(&node) {
|
||||
return Some(matches);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut node = first(&self.focus_history)?;
|
||||
loop {
|
||||
if rc_weak_eq(&original, &node.node) {
|
||||
return None;
|
||||
}
|
||||
if let Some(matches) = matches(&node) {
|
||||
return Some(matches);
|
||||
}
|
||||
node = next(&node)?;
|
||||
}
|
||||
}
|
||||
|
||||
fn focus_history(
|
||||
self: &Rc<Self>,
|
||||
next: impl Fn(&NodeRef<FocusHistoryData>) -> Option<NodeRef<FocusHistoryData>>,
|
||||
first: impl FnOnce(&LinkedList<FocusHistoryData>) -> Option<NodeRef<FocusHistoryData>>,
|
||||
) {
|
||||
let Some((node, visible)) = self.get_focus_history(next, first) else {
|
||||
return;
|
||||
};
|
||||
self.focus_history_rotate.fetch_add(1);
|
||||
let _reset = OnDrop(|| {
|
||||
self.focus_history_rotate.fetch_sub(1);
|
||||
});
|
||||
if !visible {
|
||||
node.clone().node_make_visible();
|
||||
if !node.node_visible() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.focus_node(node);
|
||||
}
|
||||
|
||||
pub fn focus_prev(self: &Rc<Self>) {
|
||||
self.focus_history(|s| s.prev(), |l| l.last());
|
||||
}
|
||||
|
||||
pub fn focus_next(self: &Rc<Self>) {
|
||||
self.focus_history(|s| s.next(), |l| l.first());
|
||||
}
|
||||
|
||||
pub fn focus_history_set_visible(&self, visible: bool) {
|
||||
self.focus_history_visible_only.set(visible);
|
||||
}
|
||||
|
||||
pub fn focus_history_set_same_workspace(&self, same_workspace: bool) {
|
||||
self.focus_history_same_workspace.set(same_workspace);
|
||||
}
|
||||
|
||||
fn set_selection_<T, X, S>(
|
||||
self: &Rc<Self>,
|
||||
field: &CloneCell<Option<Rc<dyn DynDataSource>>>,
|
||||
|
|
|
|||
|
|
@ -40,7 +40,10 @@ use {
|
|||
state::DeviceHandlerData,
|
||||
tree::{Direction, Node, ToplevelNode},
|
||||
utils::{
|
||||
bitflags::BitflagsExt, hash_map_ext::HashMapExt, smallmap::SmallMap,
|
||||
bitflags::BitflagsExt,
|
||||
hash_map_ext::HashMapExt,
|
||||
linkedlist::{LinkedNode, NodeRef},
|
||||
smallmap::{SmallMap, SmallMapMut},
|
||||
syncqueue::SyncQueue,
|
||||
},
|
||||
wire::WlDataOfferId,
|
||||
|
|
@ -56,13 +59,20 @@ use {
|
|||
kbvm::{ModifierMask, state_machine::Event},
|
||||
linearize::LinearizeExt,
|
||||
smallvec::SmallVec,
|
||||
std::{cell::RefCell, collections::hash_map::Entry, mem, rc::Rc},
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
collections::hash_map::Entry,
|
||||
mem,
|
||||
rc::{Rc, Weak},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct NodeSeatState {
|
||||
pointer_foci: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||
kb_foci: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||
no_focus_history: Cell<bool>,
|
||||
kb_focus_histories: RefCell<SmallMapMut<SeatId, LinkedNode<FocusHistoryData>, 1>>,
|
||||
gesture_foci: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||
touch_foci: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||
pointer_grabs: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||
|
|
@ -72,6 +82,11 @@ pub struct NodeSeatState {
|
|||
ui_drags: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||
}
|
||||
|
||||
pub struct FocusHistoryData {
|
||||
pub visible: Cell<bool>,
|
||||
pub node: Weak<dyn Node>,
|
||||
}
|
||||
|
||||
impl NodeSeatState {
|
||||
pub(super) fn enter(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
self.pointer_foci.insert(seat.id, seat.clone());
|
||||
|
|
@ -81,7 +96,26 @@ impl NodeSeatState {
|
|||
self.pointer_foci.remove(&seat.id);
|
||||
}
|
||||
|
||||
pub(super) fn focus(&self, seat: &Rc<WlSeatGlobal>) -> bool {
|
||||
pub fn disable_focus_history(&self) {
|
||||
self.no_focus_history.set(true);
|
||||
}
|
||||
|
||||
pub(super) fn focus(&self, node: &Rc<dyn Node>, seat: &Rc<WlSeatGlobal>) -> bool {
|
||||
if !self.no_focus_history.get() {
|
||||
let hist = &mut *self.kb_focus_histories.borrow_mut();
|
||||
let hist = hist.get_or_insert_with(seat.id, || {
|
||||
seat.focus_history.add_last(FocusHistoryData {
|
||||
visible: Cell::new(node.node_visible()),
|
||||
node: Rc::downgrade(node),
|
||||
})
|
||||
});
|
||||
if seat.focus_history_rotate.is_zero() {
|
||||
seat.last_focus_location.set(node.node_location());
|
||||
seat.focus_history.add_last_existing(hist);
|
||||
} else {
|
||||
seat.focus_history.rotate_last(hist);
|
||||
}
|
||||
}
|
||||
self.kb_foci.insert(seat.id, seat.clone());
|
||||
self.kb_foci.len() == 1
|
||||
}
|
||||
|
|
@ -179,6 +213,10 @@ impl NodeSeatState {
|
|||
}
|
||||
|
||||
pub fn destroy_node(&self, node: &dyn Node) {
|
||||
for (_, entry) in self.kb_focus_histories.borrow_mut().iter_mut() {
|
||||
entry.visible.set(false);
|
||||
entry.detach();
|
||||
}
|
||||
self.destroy_node2(node, true);
|
||||
}
|
||||
|
||||
|
|
@ -223,11 +261,22 @@ impl NodeSeatState {
|
|||
}
|
||||
|
||||
pub fn set_visible(&self, node: &dyn Node, visible: bool) {
|
||||
for (_, entry) in self.kb_focus_histories.borrow_mut().iter_mut() {
|
||||
entry.visible.set(visible);
|
||||
}
|
||||
if !visible {
|
||||
self.destroy_node2(node, false);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn get_focus_history(
|
||||
&self,
|
||||
seat: &WlSeatGlobal,
|
||||
) -> Option<NodeRef<FocusHistoryData>> {
|
||||
let hist = &*self.kb_focus_histories.borrow();
|
||||
Some(hist.get(&seat.id)?.to_ref())
|
||||
}
|
||||
|
||||
pub fn on_seat_remove(&self, seat: &WlSeatGlobal) {
|
||||
self.kb_foci.remove(&seat.id);
|
||||
self.pointer_foci.remove(&seat.id);
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ impl KbOwner for DefaultKbOwner {
|
|||
old.node_active_changed(false);
|
||||
}
|
||||
|
||||
if node.node_seat_state().focus(seat) {
|
||||
if node.node_seat_state().focus(&node, seat) {
|
||||
node.node_active_changed(true);
|
||||
}
|
||||
// log::info!("focus {}", node.node_id());
|
||||
|
|
|
|||
|
|
@ -117,11 +117,8 @@ pub enum FindTreeUsecase {
|
|||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum NodeLocation {
|
||||
Workspace(
|
||||
#[expect(dead_code)] OutputNodeId,
|
||||
#[expect(dead_code)] WorkspaceNodeId,
|
||||
),
|
||||
Output(#[expect(dead_code)] OutputNodeId),
|
||||
Workspace(OutputNodeId, WorkspaceNodeId),
|
||||
Output(OutputNodeId),
|
||||
}
|
||||
|
||||
pub trait Node: 'static {
|
||||
|
|
|
|||
|
|
@ -28,14 +28,16 @@ pub struct DisplayNode {
|
|||
|
||||
impl DisplayNode {
|
||||
pub fn new(id: NodeId) -> Self {
|
||||
Self {
|
||||
let slf = Self {
|
||||
id,
|
||||
extents: Default::default(),
|
||||
outputs: Default::default(),
|
||||
stacked: Default::default(),
|
||||
stacked_above_layers: Default::default(),
|
||||
seat_state: Default::default(),
|
||||
}
|
||||
};
|
||||
slf.seat_state.disable_focus_history();
|
||||
slf
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
|
|
|
|||
|
|
@ -324,6 +324,10 @@ impl Node for WorkspaceNode {
|
|||
fn node_do_focus(self: Rc<Self>, seat: &Rc<WlSeatGlobal>, direction: Direction) {
|
||||
if let Some(fs) = self.fullscreen.get() {
|
||||
fs.node_do_focus(seat, direction);
|
||||
} else if self.stacked.is_not_empty()
|
||||
&& let Some(last) = seat.get_last_focus_on_workspace(&self)
|
||||
{
|
||||
seat.focus_node(last);
|
||||
} else if let Some(container) = self.container.get() {
|
||||
container.node_do_focus(seat, direction);
|
||||
} else if let Some(float) = self
|
||||
|
|
|
|||
|
|
@ -97,7 +97,6 @@ impl<T> LinkedList<T> {
|
|||
self.root.append_existing(t)
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn rotate_last(&self, t: &NodeRef<T>) {
|
||||
unsafe {
|
||||
let root = self.root.data.as_ref();
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
use std::{ops::Deref, rc::Rc};
|
||||
use std::{
|
||||
ops::Deref,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
|
||||
pub fn rc_eq<T: ?Sized>(a: &Rc<T>, b: &Rc<T>) -> bool {
|
||||
Rc::as_ptr(a) as *const u8 == Rc::as_ptr(b) as *const u8
|
||||
}
|
||||
|
||||
pub fn rc_weak_eq<T: ?Sized>(a: &Rc<T>, b: &Weak<T>) -> bool {
|
||||
Rc::as_ptr(a) as *const u8 == b.as_ptr() as *const u8
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RcEq<T>(pub Rc<T>);
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use {
|
|||
color_management::ColorManagement,
|
||||
config::{ConfigParser, ConfigParserError},
|
||||
float::Float,
|
||||
focus_history::FocusHistory,
|
||||
},
|
||||
},
|
||||
toml::{self},
|
||||
|
|
@ -22,7 +23,7 @@ use {
|
|||
ahash::AHashMap,
|
||||
jay_config::{
|
||||
Axis, Direction, Workspace,
|
||||
input::{SwitchEvent, acceleration::AccelProfile, clickmethod::ClickMethod},
|
||||
input::{SwitchEvent, Timeline, acceleration::AccelProfile, clickmethod::ClickMethod},
|
||||
keyboard::{Keymap, ModifiedKeySym, mods::Modifiers, syms::KeySym},
|
||||
logging::LogLevel,
|
||||
status::MessageFormat,
|
||||
|
|
@ -70,6 +71,7 @@ pub enum SimpleCommand {
|
|||
KillClient,
|
||||
ShowBar(bool),
|
||||
ToggleBar,
|
||||
FocusHistory(Timeline),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -486,6 +488,7 @@ pub struct Config {
|
|||
pub pointer_revert_key: Option<KeySym>,
|
||||
pub use_hardware_cursor: Option<bool>,
|
||||
pub show_bar: Option<bool>,
|
||||
pub focus_history: Option<FocusHistory>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ mod drm_device_match;
|
|||
mod env;
|
||||
pub mod exec;
|
||||
pub mod float;
|
||||
pub mod focus_history;
|
||||
mod format;
|
||||
mod gfx_api;
|
||||
mod idle;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ use {
|
|||
jay_config::{
|
||||
Axis::{Horizontal, Vertical},
|
||||
get_workspace,
|
||||
input::Timeline,
|
||||
},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
|
@ -136,6 +137,8 @@ impl ActionParser<'_> {
|
|||
"show-bar" => ShowBar(true),
|
||||
"hide-bar" => ShowBar(false),
|
||||
"toggle-bar" => ToggleBar,
|
||||
"focus-prev" => FocusHistory(Timeline::Older),
|
||||
"focus-next" => FocusHistory(Timeline::Newer),
|
||||
_ => {
|
||||
return Err(
|
||||
ActionParserError::UnknownSimpleAction(string.to_string()).spanned(span)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ use {
|
|||
drm_device_match::DrmDeviceMatchParser,
|
||||
env::EnvParser,
|
||||
float::FloatParser,
|
||||
focus_history::FocusHistoryParser,
|
||||
gfx_api::GfxApiParser,
|
||||
idle::IdleParser,
|
||||
input::InputsParser,
|
||||
|
|
@ -133,6 +134,7 @@ impl Parser for ConfigParser<'_> {
|
|||
pointer_revert_key_str,
|
||||
use_hardware_cursor,
|
||||
show_bar,
|
||||
focus_history_val,
|
||||
),
|
||||
) = ext.extract((
|
||||
(
|
||||
|
|
@ -181,6 +183,7 @@ impl Parser for ConfigParser<'_> {
|
|||
recover(opt(str("pointer-revert-key"))),
|
||||
recover(opt(bol("use-hardware-cursor"))),
|
||||
recover(opt(bol("show-bar"))),
|
||||
opt(val("focus-history")),
|
||||
),
|
||||
))?;
|
||||
let mut keymap = None;
|
||||
|
|
@ -458,6 +461,18 @@ impl Parser for ConfigParser<'_> {
|
|||
None => log::warn!("Unknown keysym: {}", self.0.error3(value.span)),
|
||||
}
|
||||
}
|
||||
let mut focus_history = None;
|
||||
if let Some(value) = focus_history_val {
|
||||
match value.parse(&mut FocusHistoryParser(self.0)) {
|
||||
Ok(v) => focus_history = Some(v),
|
||||
Err(e) => {
|
||||
log::warn!(
|
||||
"Could not parse the focus-history settings: {}",
|
||||
self.0.error(e)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Config {
|
||||
keymap,
|
||||
repeat_rate,
|
||||
|
|
@ -497,6 +512,7 @@ impl Parser for ConfigParser<'_> {
|
|||
pointer_revert_key,
|
||||
use_hardware_cursor: use_hardware_cursor.despan(),
|
||||
show_bar: show_bar.despan(),
|
||||
focus_history,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
53
toml-config/src/config/parsers/focus_history.rs
Normal file
53
toml-config/src/config/parsers/focus_history.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
use {
|
||||
crate::{
|
||||
config::{
|
||||
context::Context,
|
||||
extractor::{Extractor, ExtractorError, bol, opt, recover},
|
||||
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||
},
|
||||
toml::{
|
||||
toml_span::{DespanExt, Span, Spanned},
|
||||
toml_value::Value,
|
||||
},
|
||||
},
|
||||
indexmap::IndexMap,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum FocusHistoryParserError {
|
||||
#[error(transparent)]
|
||||
Expected(#[from] UnexpectedDataType),
|
||||
#[error(transparent)]
|
||||
Extract(#[from] ExtractorError),
|
||||
}
|
||||
|
||||
pub struct FocusHistoryParser<'a>(pub &'a Context<'a>);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FocusHistory {
|
||||
pub only_visible: Option<bool>,
|
||||
pub same_workspace: Option<bool>,
|
||||
}
|
||||
|
||||
impl Parser for FocusHistoryParser<'_> {
|
||||
type Value = FocusHistory;
|
||||
type Error = FocusHistoryParserError;
|
||||
const EXPECTED: &'static [DataType] = &[DataType::Table];
|
||||
|
||||
fn parse_table(
|
||||
&mut self,
|
||||
span: Span,
|
||||
table: &IndexMap<Spanned<String>, Spanned<Value>>,
|
||||
) -> ParseResult<Self> {
|
||||
let mut ext = Extractor::new(self.0, span, table);
|
||||
let (only_visible, same_workspace) = ext.extract((
|
||||
recover(opt(bol("only-visible"))),
|
||||
recover(opt(bol("same-workspace"))),
|
||||
))?;
|
||||
Ok(FocusHistory {
|
||||
only_visible: only_visible.despan(),
|
||||
same_workspace: same_workspace.despan(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -156,6 +156,10 @@ impl Action {
|
|||
SimpleCommand::KillClient => client_action!(c, c.kill()),
|
||||
SimpleCommand::ShowBar(show) => B::new(move || set_show_bar(show)),
|
||||
SimpleCommand::ToggleBar => B::new(toggle_show_bar),
|
||||
SimpleCommand::FocusHistory(timeline) => {
|
||||
let persistent = state.persistent.clone();
|
||||
B::new(move || persistent.seat.focus_history(timeline))
|
||||
}
|
||||
},
|
||||
Action::Multi { actions } => {
|
||||
let actions: Vec<_> = actions.into_iter().map(|a| a.into_fn(state)).collect();
|
||||
|
|
@ -1252,6 +1256,14 @@ fn load_config(initial_load: bool, persistent: &Rc<PersistentState>) {
|
|||
if let Some(v) = config.show_bar {
|
||||
set_show_bar(v);
|
||||
}
|
||||
if let Some(v) = config.focus_history {
|
||||
if let Some(v) = v.only_visible {
|
||||
persistent.seat.focus_history_set_only_visible(v);
|
||||
}
|
||||
if let Some(v) = v.same_workspace {
|
||||
persistent.seat.focus_history_set_same_workspace(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_command(exec: &Exec) -> Command {
|
||||
|
|
|
|||
|
|
@ -887,6 +887,10 @@
|
|||
"show-bar": {
|
||||
"type": "boolean",
|
||||
"description": "Configures whether the built-in bar is shown.\n\nThe default is `true`.\n"
|
||||
},
|
||||
"focus-history": {
|
||||
"description": "Configures the focus-history settings.\n\n- Example:\n\n ```toml\n [focus-history]\n only-visible: true\n same-workspace: true\n ```\n",
|
||||
"$ref": "#/$defs/FocusHistory"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
|
|
@ -1094,6 +1098,21 @@
|
|||
},
|
||||
"required": []
|
||||
},
|
||||
"FocusHistory": {
|
||||
"description": "Describes settings of the focus history.\n\n- Example:\n\n ```toml\n [focus-history]\n only-visible: true\n same-workspace: true\n ```\n",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"only-visible": {
|
||||
"type": "boolean",
|
||||
"description": "Sets whether the focus history only moves to windows that are already visible.\n\nIf this is false, then the window will be made visible before focusing it.\n\nThe default is `false`.\n"
|
||||
},
|
||||
"same-workspace": {
|
||||
"type": "boolean",
|
||||
"description": "Sets whether the focus history only moves to windows on the same workspace.\n\nThe default is `false`.\n"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
},
|
||||
"Format": {
|
||||
"type": "string",
|
||||
"description": "A graphics format.\n\nThese formats are documented in https://github.com/torvalds/linux/blob/master/include/uapi/drm/drm_fourcc.h\n\n- Example:\n\n ```toml\n [[outputs]]\n match.serial-number = \"33K03894SL0\"\n format = \"rgb565\"\n ```\n",
|
||||
|
|
@ -1593,7 +1612,9 @@
|
|||
"kill-client",
|
||||
"show-bar",
|
||||
"hide-bar",
|
||||
"toggle-bar"
|
||||
"toggle-bar",
|
||||
"focus-prev",
|
||||
"focus-next"
|
||||
]
|
||||
},
|
||||
"Status": {
|
||||
|
|
|
|||
|
|
@ -1802,6 +1802,20 @@ The table has the following fields:
|
|||
|
||||
The value of this field should be a boolean.
|
||||
|
||||
- `focus-history` (optional):
|
||||
|
||||
Configures the focus-history settings.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[focus-history]
|
||||
only-visible: true
|
||||
same-workspace: true
|
||||
```
|
||||
|
||||
The value of this field should be a [FocusHistory](#types-FocusHistory).
|
||||
|
||||
|
||||
<a name="types-Connector"></a>
|
||||
### `Connector`
|
||||
|
|
@ -2232,6 +2246,42 @@ The table has the following fields:
|
|||
The value of this field should be a boolean.
|
||||
|
||||
|
||||
<a name="types-FocusHistory"></a>
|
||||
### `FocusHistory`
|
||||
|
||||
Describes settings of the focus history.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[focus-history]
|
||||
only-visible: true
|
||||
same-workspace: true
|
||||
```
|
||||
|
||||
Values of this type should be tables.
|
||||
|
||||
The table has the following fields:
|
||||
|
||||
- `only-visible` (optional):
|
||||
|
||||
Sets whether the focus history only moves to windows that are already visible.
|
||||
|
||||
If this is false, then the window will be made visible before focusing it.
|
||||
|
||||
The default is `false`.
|
||||
|
||||
The value of this field should be a boolean.
|
||||
|
||||
- `same-workspace` (optional):
|
||||
|
||||
Sets whether the focus history only moves to windows on the same workspace.
|
||||
|
||||
The default is `false`.
|
||||
|
||||
The value of this field should be a boolean.
|
||||
|
||||
|
||||
<a name="types-Format"></a>
|
||||
### `Format`
|
||||
|
||||
|
|
@ -3617,6 +3667,14 @@ The string should have one of the following values:
|
|||
|
||||
Toggles the built-in bar.
|
||||
|
||||
- `focus-prev`:
|
||||
|
||||
Focuses the previous window in the focus history.
|
||||
|
||||
- `focus-next`:
|
||||
|
||||
Focuses the next window in the focus history.
|
||||
|
||||
|
||||
|
||||
<a name="types-Status"></a>
|
||||
|
|
|
|||
|
|
@ -860,6 +860,10 @@ SimpleActionName:
|
|||
description: Hides the built-in bar.
|
||||
- value: toggle-bar
|
||||
description: Toggles the built-in bar.
|
||||
- value: focus-prev
|
||||
description: Focuses the previous window in the focus history.
|
||||
- value: focus-next
|
||||
description: Focuses the next window in the focus history.
|
||||
|
||||
|
||||
Color:
|
||||
|
|
@ -2627,6 +2631,19 @@ Config:
|
|||
Configures whether the built-in bar is shown.
|
||||
|
||||
The default is `true`.
|
||||
focus-history:
|
||||
ref: FocusHistory
|
||||
required: false
|
||||
description: |
|
||||
Configures the focus-history settings.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[focus-history]
|
||||
only-visible: true
|
||||
same-workspace: true
|
||||
```
|
||||
|
||||
|
||||
Idle:
|
||||
|
|
@ -3716,3 +3733,34 @@ ContentTypeMask:
|
|||
description: An array of masks that are OR'd.
|
||||
items:
|
||||
ref: ContentTypeMask
|
||||
|
||||
|
||||
FocusHistory:
|
||||
kind: table
|
||||
description: |
|
||||
Describes settings of the focus history.
|
||||
|
||||
- Example:
|
||||
|
||||
```toml
|
||||
[focus-history]
|
||||
only-visible: true
|
||||
same-workspace: true
|
||||
```
|
||||
fields:
|
||||
only-visible:
|
||||
description: |
|
||||
Sets whether the focus history only moves to windows that are already visible.
|
||||
|
||||
If this is false, then the window will be made visible before focusing it.
|
||||
|
||||
The default is `false`.
|
||||
kind: boolean
|
||||
required: false
|
||||
same-workspace:
|
||||
description: |
|
||||
Sets whether the focus history only moves to windows on the same workspace.
|
||||
|
||||
The default is `false`.
|
||||
kind: boolean
|
||||
required: false
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue