config: add Window
This commit is contained in:
parent
ab095b89cf
commit
9977f9dfdf
19 changed files with 1172 additions and 203 deletions
|
|
@ -30,6 +30,7 @@ use {
|
|||
Transform, VrrMode,
|
||||
connector_type::{CON_UNKNOWN, ConnectorType},
|
||||
},
|
||||
window::{Window, WindowType},
|
||||
xwayland::XScalingMode,
|
||||
},
|
||||
bincode::Options,
|
||||
|
|
@ -342,6 +343,74 @@ impl ConfigClient {
|
|||
self.send(&ClientMessage::SeatMove { seat, direction });
|
||||
}
|
||||
|
||||
pub fn window_move(&self, window: Window, direction: Direction) {
|
||||
self.send(&ClientMessage::WindowMove { window, direction });
|
||||
}
|
||||
|
||||
pub fn window_exists(&self, window: Window) -> bool {
|
||||
let res = self.send_with_response(&ClientMessage::WindowExists { window });
|
||||
get_response!(res, false, WindowExists { exists });
|
||||
exists
|
||||
}
|
||||
|
||||
pub fn window_client(&self, window: Window) -> Client {
|
||||
let res = self.send_with_response(&ClientMessage::GetWindowClient { window });
|
||||
get_response!(res, Client(0), GetWindowClient { client });
|
||||
client
|
||||
}
|
||||
|
||||
pub fn get_workspace_window(&self, workspace: Workspace) -> Window {
|
||||
let res = self.send_with_response(&ClientMessage::GetWorkspaceWindow { workspace });
|
||||
get_response!(res, Window(0), GetWorkspaceWindow { window });
|
||||
window
|
||||
}
|
||||
|
||||
pub fn get_seat_keyboard_window(&self, seat: Seat) -> Window {
|
||||
let res = self.send_with_response(&ClientMessage::GetSeatKeyboardWindow { seat });
|
||||
get_response!(res, Window(0), GetSeatKeyboardWindow { window });
|
||||
window
|
||||
}
|
||||
|
||||
pub fn focus_window(&self, seat: Seat, window: Window) {
|
||||
self.send(&ClientMessage::SeatFocusWindow { seat, window });
|
||||
}
|
||||
|
||||
pub fn window_title(&self, window: Window) -> String {
|
||||
let res = self.send_with_response(&ClientMessage::GetWindowTitle { window });
|
||||
get_response!(res, String::new(), GetWindowTitle { title });
|
||||
title
|
||||
}
|
||||
|
||||
pub fn window_type(&self, window: Window) -> WindowType {
|
||||
let res = self.send_with_response(&ClientMessage::GetWindowType { window });
|
||||
get_response!(res, WindowType(0), GetWindowType { kind });
|
||||
kind
|
||||
}
|
||||
|
||||
pub fn window_id(&self, window: Window) -> String {
|
||||
let res = self.send_with_response(&ClientMessage::GetWindowId { window });
|
||||
get_response!(res, String::new(), GetWindowId { id });
|
||||
id
|
||||
}
|
||||
|
||||
pub fn window_parent(&self, window: Window) -> Window {
|
||||
let res = self.send_with_response(&ClientMessage::GetWindowParent { window });
|
||||
get_response!(res, Window(0), GetWindowParent { window });
|
||||
window
|
||||
}
|
||||
|
||||
pub fn window_children(&self, window: Window) -> Vec<Window> {
|
||||
let res = self.send_with_response(&ClientMessage::GetWindowChildren { window });
|
||||
get_response!(res, vec![], GetWindowChildren { windows });
|
||||
windows
|
||||
}
|
||||
|
||||
pub fn window_is_visible(&self, window: Window) -> bool {
|
||||
let res = self.send_with_response(&ClientMessage::GetWindowIsVisible { window });
|
||||
get_response!(res, false, GetWindowIsVisible { visible });
|
||||
visible
|
||||
}
|
||||
|
||||
pub fn unbind<T: Into<ModifiedKeySym>>(&self, seat: Seat, mod_sym: T) {
|
||||
let mod_sym = mod_sym.into();
|
||||
if let Entry::Occupied(mut oe) = self.key_handlers.borrow_mut().entry((seat, mod_sym)) {
|
||||
|
|
@ -374,6 +443,12 @@ impl ConfigClient {
|
|||
mono
|
||||
}
|
||||
|
||||
pub fn window_mono(&self, window: Window) -> bool {
|
||||
let res = self.send_with_response(&ClientMessage::GetWindowMono { window });
|
||||
get_response!(res, false, GetWindowMono { mono });
|
||||
mono
|
||||
}
|
||||
|
||||
pub fn get_timer(&self, name: &str) -> Timer {
|
||||
let res = self.send_with_response(&ClientMessage::GetTimer { name });
|
||||
get_response!(res, Timer(0), GetTimer { timer });
|
||||
|
|
@ -421,6 +496,12 @@ impl ConfigClient {
|
|||
workspace
|
||||
}
|
||||
|
||||
pub fn get_window_workspace(&self, window: Window) -> Workspace {
|
||||
let res = self.send_with_response(&ClientMessage::GetWindowWorkspace { window });
|
||||
get_response!(res, Workspace(0), GetWindowWorkspace { workspace });
|
||||
workspace
|
||||
}
|
||||
|
||||
pub fn get_seat_keyboard_workspace(&self, seat: Seat) -> Workspace {
|
||||
let res = self.send_with_response(&ClientMessage::GetSeatKeyboardWorkspace { seat });
|
||||
get_response!(res, Workspace(0), GetSeatKeyboardWorkspace { workspace });
|
||||
|
|
@ -455,12 +536,22 @@ impl ConfigClient {
|
|||
self.send(&ClientMessage::SetSeatWorkspace { seat, workspace });
|
||||
}
|
||||
|
||||
pub fn set_window_workspace(&self, window: Window, workspace: Workspace) {
|
||||
self.send(&ClientMessage::SetWindowWorkspace { window, workspace });
|
||||
}
|
||||
|
||||
pub fn seat_split(&self, seat: Seat) -> Axis {
|
||||
let res = self.send_with_response(&ClientMessage::GetSeatSplit { seat });
|
||||
get_response!(res, Axis::Horizontal, GetSplit { axis });
|
||||
axis
|
||||
}
|
||||
|
||||
pub fn window_split(&self, window: Window) -> Axis {
|
||||
let res = self.send_with_response(&ClientMessage::GetWindowSplit { window });
|
||||
get_response!(res, Axis::Horizontal, GetWindowSplit { axis });
|
||||
axis
|
||||
}
|
||||
|
||||
pub fn disable_pointer_constraint(&self, seat: Seat) {
|
||||
self.send(&ClientMessage::DisablePointerConstraint { seat });
|
||||
}
|
||||
|
|
@ -482,6 +573,16 @@ impl ConfigClient {
|
|||
fullscreen
|
||||
}
|
||||
|
||||
pub fn set_window_fullscreen(&self, window: Window, fullscreen: bool) {
|
||||
self.send(&ClientMessage::SetWindowFullscreen { window, fullscreen });
|
||||
}
|
||||
|
||||
pub fn get_window_fullscreen(&self, window: Window) -> bool {
|
||||
let res = self.send_with_response(&ClientMessage::GetWindowFullscreen { window });
|
||||
get_response!(res, false, GetWindowFullscreen { fullscreen });
|
||||
fullscreen
|
||||
}
|
||||
|
||||
pub fn reset_font(&self) {
|
||||
self.send(&ClientMessage::ResetFont);
|
||||
}
|
||||
|
|
@ -510,6 +611,16 @@ impl ConfigClient {
|
|||
self.set_seat_floating(seat, !self.get_seat_floating(seat));
|
||||
}
|
||||
|
||||
pub fn get_window_floating(&self, window: Window) -> bool {
|
||||
let res = self.send_with_response(&ClientMessage::GetWindowFloating { window });
|
||||
get_response!(res, false, GetWindowFloating { floating });
|
||||
floating
|
||||
}
|
||||
|
||||
pub fn set_window_floating(&self, window: Window, floating: bool) {
|
||||
self.send(&ClientMessage::SetWindowFloating { window, floating });
|
||||
}
|
||||
|
||||
pub fn reset_colors(&self) {
|
||||
self.send(&ClientMessage::ResetColors);
|
||||
}
|
||||
|
|
@ -553,6 +664,10 @@ impl ConfigClient {
|
|||
self.send(&ClientMessage::SetSeatMono { seat, mono });
|
||||
}
|
||||
|
||||
pub fn set_window_mono(&self, window: Window, mono: bool) {
|
||||
self.send(&ClientMessage::SetWindowMono { window, mono });
|
||||
}
|
||||
|
||||
pub fn set_env(&self, key: &str, val: &str) {
|
||||
self.send(&ClientMessage::SetEnv { key, val });
|
||||
}
|
||||
|
|
@ -587,14 +702,26 @@ impl ConfigClient {
|
|||
self.send(&ClientMessage::SetSeatSplit { seat, axis });
|
||||
}
|
||||
|
||||
pub fn set_window_split(&self, window: Window, axis: Axis) {
|
||||
self.send(&ClientMessage::SetWindowSplit { window, axis });
|
||||
}
|
||||
|
||||
pub fn create_seat_split(&self, seat: Seat, axis: Axis) {
|
||||
self.send(&ClientMessage::CreateSeatSplit { seat, axis });
|
||||
}
|
||||
|
||||
pub fn create_window_split(&self, window: Window, axis: Axis) {
|
||||
self.send(&ClientMessage::CreateWindowSplit { window, axis });
|
||||
}
|
||||
|
||||
pub fn seat_close(&self, seat: Seat) {
|
||||
self.send(&ClientMessage::SeatClose { seat });
|
||||
}
|
||||
|
||||
pub fn close_window(&self, window: Window) {
|
||||
self.send(&ClientMessage::WindowClose { window });
|
||||
}
|
||||
|
||||
pub fn focus_seat_parent(&self, seat: Seat) {
|
||||
self.send(&ClientMessage::FocusSeatParent { seat });
|
||||
}
|
||||
|
|
@ -802,6 +929,16 @@ impl ConfigClient {
|
|||
self.send(&ClientMessage::SetSeatFloatPinned { seat, pinned });
|
||||
}
|
||||
|
||||
pub fn get_window_pinned(&self, window: Window) -> bool {
|
||||
let res = self.send_with_response(&ClientMessage::GetWindowFloatPinned { window });
|
||||
get_response!(res, false, GetWindowFloatPinned { pinned });
|
||||
pinned
|
||||
}
|
||||
|
||||
pub fn set_window_pinned(&self, window: Window, pinned: bool) {
|
||||
self.send(&ClientMessage::SetWindowFloatPinned { window, pinned });
|
||||
}
|
||||
|
||||
pub fn connector_connected(&self, connector: Connector) -> bool {
|
||||
let res = self.send_with_response(&ClientMessage::ConnectorConnected { connector });
|
||||
get_response!(res, false, ConnectorConnected { connected });
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use {
|
|||
ColorSpace, Connector, DrmDevice, Format, GfxApi, TearingMode, TransferFunction,
|
||||
Transform, VrrMode, connector_type::ConnectorType,
|
||||
},
|
||||
window::{Window, WindowType},
|
||||
xwayland::XScalingMode,
|
||||
},
|
||||
serde::{Deserialize, Serialize},
|
||||
|
|
@ -576,6 +577,93 @@ pub enum ClientMessage<'a> {
|
|||
ClientIsXwayland {
|
||||
client: Client,
|
||||
},
|
||||
WindowExists {
|
||||
window: Window,
|
||||
},
|
||||
GetWindowClient {
|
||||
window: Window,
|
||||
},
|
||||
GetWorkspaceWindow {
|
||||
workspace: Workspace,
|
||||
},
|
||||
GetSeatKeyboardWindow {
|
||||
seat: Seat,
|
||||
},
|
||||
SeatFocusWindow {
|
||||
seat: Seat,
|
||||
window: Window,
|
||||
},
|
||||
GetWindowTitle {
|
||||
window: Window,
|
||||
},
|
||||
GetWindowType {
|
||||
window: Window,
|
||||
},
|
||||
GetWindowId {
|
||||
window: Window,
|
||||
},
|
||||
GetWindowIsVisible {
|
||||
window: Window,
|
||||
},
|
||||
GetWindowParent {
|
||||
window: Window,
|
||||
},
|
||||
GetWindowWorkspace {
|
||||
window: Window,
|
||||
},
|
||||
GetWindowChildren {
|
||||
window: Window,
|
||||
},
|
||||
GetWindowSplit {
|
||||
window: Window,
|
||||
},
|
||||
SetWindowSplit {
|
||||
window: Window,
|
||||
axis: Axis,
|
||||
},
|
||||
GetWindowMono {
|
||||
window: Window,
|
||||
},
|
||||
SetWindowMono {
|
||||
window: Window,
|
||||
mono: bool,
|
||||
},
|
||||
WindowMove {
|
||||
window: Window,
|
||||
direction: Direction,
|
||||
},
|
||||
CreateWindowSplit {
|
||||
window: Window,
|
||||
axis: Axis,
|
||||
},
|
||||
WindowClose {
|
||||
window: Window,
|
||||
},
|
||||
GetWindowFloating {
|
||||
window: Window,
|
||||
},
|
||||
SetWindowFloating {
|
||||
window: Window,
|
||||
floating: bool,
|
||||
},
|
||||
SetWindowWorkspace {
|
||||
window: Window,
|
||||
workspace: Workspace,
|
||||
},
|
||||
SetWindowFullscreen {
|
||||
window: Window,
|
||||
fullscreen: bool,
|
||||
},
|
||||
GetWindowFullscreen {
|
||||
window: Window,
|
||||
},
|
||||
GetWindowFloatPinned {
|
||||
window: Window,
|
||||
},
|
||||
SetWindowFloatPinned {
|
||||
window: Window,
|
||||
pinned: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
|
@ -748,6 +836,54 @@ pub enum Response {
|
|||
ClientIsXwayland {
|
||||
is_xwayland: bool,
|
||||
},
|
||||
WindowExists {
|
||||
exists: bool,
|
||||
},
|
||||
GetWindowClient {
|
||||
client: Client,
|
||||
},
|
||||
GetSeatKeyboardWindow {
|
||||
window: Window,
|
||||
},
|
||||
GetWorkspaceWindow {
|
||||
window: Window,
|
||||
},
|
||||
GetWindowParent {
|
||||
window: Window,
|
||||
},
|
||||
GetWindowChildren {
|
||||
windows: Vec<Window>,
|
||||
},
|
||||
GetWindowTitle {
|
||||
title: String,
|
||||
},
|
||||
GetWindowType {
|
||||
kind: WindowType,
|
||||
},
|
||||
GetWindowId {
|
||||
id: String,
|
||||
},
|
||||
GetWindowWorkspace {
|
||||
workspace: Workspace,
|
||||
},
|
||||
GetWindowFloating {
|
||||
floating: bool,
|
||||
},
|
||||
GetWindowSplit {
|
||||
axis: Axis,
|
||||
},
|
||||
GetWindowMono {
|
||||
mono: bool,
|
||||
},
|
||||
GetWindowFullscreen {
|
||||
fullscreen: bool,
|
||||
},
|
||||
GetWindowFloatPinned {
|
||||
pinned: bool,
|
||||
},
|
||||
GetWindowIsVisible {
|
||||
visible: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use {
|
|||
input::{acceleration::AccelProfile, capability::Capability},
|
||||
keyboard::{Keymap, mods::Modifiers},
|
||||
video::Connector,
|
||||
window::Window,
|
||||
},
|
||||
serde::{Deserialize, Serialize},
|
||||
std::time::Duration,
|
||||
|
|
@ -478,6 +479,20 @@ impl Seat {
|
|||
pub fn toggle_float_pinned(self) {
|
||||
self.set_float_pinned(!self.float_pinned());
|
||||
}
|
||||
|
||||
/// Returns the focused window.
|
||||
///
|
||||
/// If no window is focused, [`Window::exists`] returns false.
|
||||
pub fn window(self) -> Window {
|
||||
get!(Window(0)).get_seat_keyboard_window(self)
|
||||
}
|
||||
|
||||
/// Puts the keyboard focus on the window.
|
||||
///
|
||||
/// This has no effect if the window is not visible.
|
||||
pub fn focus_window(self, window: Window) {
|
||||
get!().focus_window(self, window)
|
||||
}
|
||||
}
|
||||
|
||||
/// A focus-follows-mouse mode.
|
||||
|
|
|
|||
|
|
@ -3,35 +3,42 @@
|
|||
use {
|
||||
crate::{ModifiedKeySym, keyboard::syms::KeySym},
|
||||
serde::{Deserialize, Serialize},
|
||||
std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign},
|
||||
std::ops::BitOr,
|
||||
};
|
||||
|
||||
/// Zero or more keyboard modifiers
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Default, Hash, Debug)]
|
||||
pub struct Modifiers(pub u32);
|
||||
bitflags! {
|
||||
/// Zero or more keyboard modifiers
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Eq, PartialEq, Default, Hash)]
|
||||
pub struct Modifiers(pub u32) {
|
||||
/// The Shift modifier
|
||||
pub const SHIFT = 1 << 0,
|
||||
/// The CapsLock modifier.
|
||||
pub const LOCK = 1 << 1,
|
||||
/// The Ctrl modifier.
|
||||
pub const CTRL = 1 << 2,
|
||||
/// The Mod1 modifier, i.e., Alt.
|
||||
pub const MOD1 = 1 << 3,
|
||||
/// The Mod2 modifier, i.e., NumLock.
|
||||
pub const MOD2 = 1 << 4,
|
||||
/// The Mod3 modifier.
|
||||
pub const MOD3 = 1 << 5,
|
||||
/// The Mod4 modifier, i.e., Logo.
|
||||
pub const MOD4 = 1 << 6,
|
||||
/// The Mod5 modifier.
|
||||
pub const MOD5 = 1 << 7,
|
||||
|
||||
/// Synthetic modifier matching key release events.
|
||||
///
|
||||
/// This can be used to execute a callback on key release.
|
||||
pub const RELEASE = 1 << 31,
|
||||
}
|
||||
}
|
||||
|
||||
impl Modifiers {
|
||||
/// No modifiers.
|
||||
pub const NONE: Self = Modifiers(0);
|
||||
}
|
||||
|
||||
/// The Shift modifier
|
||||
pub const SHIFT: Modifiers = Modifiers(1 << 0);
|
||||
/// The CapsLock modifier.
|
||||
pub const LOCK: Modifiers = Modifiers(1 << 1);
|
||||
/// The Ctrl modifier.
|
||||
pub const CTRL: Modifiers = Modifiers(1 << 2);
|
||||
/// The Mod1 modifier, i.e., Alt.
|
||||
pub const MOD1: Modifiers = Modifiers(1 << 3);
|
||||
/// The Mod2 modifier, i.e., NumLock.
|
||||
pub const MOD2: Modifiers = Modifiers(1 << 4);
|
||||
/// The Mod3 modifier.
|
||||
pub const MOD3: Modifiers = Modifiers(1 << 5);
|
||||
/// The Mod4 modifier, i.e., Logo.
|
||||
pub const MOD4: Modifiers = Modifiers(1 << 6);
|
||||
/// The Mod5 modifier.
|
||||
pub const MOD5: Modifiers = Modifiers(1 << 7);
|
||||
|
||||
/// Alias for `LOCK`.
|
||||
pub const CAPS: Modifiers = LOCK;
|
||||
/// Alias for `MOD1`.
|
||||
|
|
@ -41,19 +48,6 @@ pub const NUM: Modifiers = MOD2;
|
|||
/// Alias for `MOD4`.
|
||||
pub const LOGO: Modifiers = MOD4;
|
||||
|
||||
/// Synthetic modifier matching key release events.
|
||||
///
|
||||
/// This can be used to execute a callback on key release.
|
||||
pub const RELEASE: Modifiers = Modifiers(1 << 31);
|
||||
|
||||
impl BitOr for Modifiers {
|
||||
type Output = Self;
|
||||
|
||||
fn bitor(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 | rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr<KeySym> for Modifiers {
|
||||
type Output = ModifiedKeySym;
|
||||
|
||||
|
|
@ -64,23 +58,3 @@ impl BitOr<KeySym> for Modifiers {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAnd for Modifiers {
|
||||
type Output = Self;
|
||||
|
||||
fn bitand(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 & rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOrAssign for Modifiers {
|
||||
fn bitor_assign(&mut self, rhs: Self) {
|
||||
self.0 |= rhs.0
|
||||
}
|
||||
}
|
||||
|
||||
impl BitAndAssign for Modifiers {
|
||||
fn bitand_assign(&mut self, rhs: Self) {
|
||||
self.0 &= rhs.0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,9 @@
|
|||
#[expect(unused_imports)]
|
||||
use crate::input::Seat;
|
||||
use {
|
||||
crate::{_private::ipc::WorkspaceSource, keyboard::ModifiedKeySym, video::Connector},
|
||||
crate::{
|
||||
_private::ipc::WorkspaceSource, keyboard::ModifiedKeySym, video::Connector, window::Window,
|
||||
},
|
||||
serde::{Deserialize, Serialize},
|
||||
std::{
|
||||
fmt::{Debug, Display, Formatter},
|
||||
|
|
@ -70,6 +72,7 @@ pub mod tasks;
|
|||
pub mod theme;
|
||||
pub mod timer;
|
||||
pub mod video;
|
||||
pub mod window;
|
||||
pub mod xwayland;
|
||||
|
||||
/// A planar direction.
|
||||
|
|
@ -174,6 +177,13 @@ impl Workspace {
|
|||
pub fn move_to_output(self, output: Connector) {
|
||||
get!().move_to_output(WorkspaceSource::Explicit(self), output);
|
||||
}
|
||||
|
||||
/// Returns the root container of this workspace.
|
||||
///
|
||||
/// If no such container exists, [`Window::exists`] returns false.
|
||||
pub fn window(self) -> Window {
|
||||
get!(Window(0)).get_workspace_window(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the workspace with the given name.
|
||||
|
|
|
|||
|
|
@ -43,40 +43,89 @@ macro_rules! get {
|
|||
}};
|
||||
}
|
||||
|
||||
// #[macro_export]
|
||||
// macro_rules! log {
|
||||
// ($lvl:expr, $($arg:tt)+) => ({
|
||||
// $crate::log(
|
||||
// $lvl,
|
||||
// &format!($($args)*),
|
||||
// );
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// #[macro_export]
|
||||
// macro_rules! trace {
|
||||
// ($($arg:tt)+) => {
|
||||
// $crate::log!($crate::LogLevel::Trace, $($arg)+)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[macro_export]
|
||||
// macro_rules! debug {
|
||||
// ($($arg:tt)+) => {
|
||||
// $crate::log!($crate::LogLevel::Debug, $($arg)+)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[macro_export]
|
||||
// macro_rules! info {
|
||||
// ($($arg:tt)+) => {
|
||||
// $crate::log!($crate::LogLevel::Info, $($arg)+)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[macro_export]
|
||||
// macro_rules! info {
|
||||
// ($($arg:tt)+) => {
|
||||
// $crate::log!($crate::LogLevel::Info, $($arg)+)
|
||||
// }
|
||||
// }
|
||||
macro_rules! bitflags {
|
||||
(
|
||||
$(#[$attr1:meta])*
|
||||
$vis1:vis struct $name:ident($vis2:vis $rep:ty) {
|
||||
$(
|
||||
$(#[$attr2:meta])*
|
||||
$vis3:vis const $var:ident = $val:expr,
|
||||
)*
|
||||
}
|
||||
) => {
|
||||
$(#[$attr1])*
|
||||
$vis1 struct $name($vis2 $rep);
|
||||
|
||||
$(
|
||||
$(#[$attr2])*
|
||||
$vis3 const $var: $name = $name($val);
|
||||
)*
|
||||
|
||||
impl std::ops::BitOr for $name {
|
||||
type Output = Self;
|
||||
|
||||
fn bitor(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 | rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::BitAnd for $name {
|
||||
type Output = Self;
|
||||
|
||||
fn bitand(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 & rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::BitOrAssign for $name {
|
||||
fn bitor_assign(&mut self, rhs: Self) {
|
||||
self.0 |= rhs.0;
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::BitAndAssign for $name {
|
||||
fn bitand_assign(&mut self, rhs: Self) {
|
||||
self.0 &= rhs.0;
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::BitXorAssign for $name {
|
||||
fn bitxor_assign(&mut self, rhs: Self) {
|
||||
self.0 ^= rhs.0;
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Not for $name {
|
||||
type Output = Self;
|
||||
|
||||
fn not(self) -> Self::Output {
|
||||
Self(!self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for $name {
|
||||
#[allow(clippy::allow_attributes, clippy::bad_bit_mask, unused_mut)]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut any = false;
|
||||
let mut v = self.0;
|
||||
$(
|
||||
if $val != 0 && v & $val == $val {
|
||||
if any {
|
||||
write!(f, "|")?;
|
||||
}
|
||||
any = true;
|
||||
write!(f, "{}", stringify!($var))?;
|
||||
v &= !$val;
|
||||
}
|
||||
)*
|
||||
if !any || v != 0 {
|
||||
if any {
|
||||
write!(f, "|")?;
|
||||
}
|
||||
write!(f, "0x{:x}", v)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
204
jay-config/src/window.rs
Normal file
204
jay-config/src/window.rs
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
//! Tools for inspecting and manipulating windows.
|
||||
|
||||
use {
|
||||
crate::{Axis, Direction, Workspace, client::Client},
|
||||
serde::{Deserialize, Serialize},
|
||||
};
|
||||
|
||||
/// A toplevel window.
|
||||
///
|
||||
/// A toplevel window is anything that can be stored within a container tile or within a
|
||||
/// floating window.
|
||||
///
|
||||
/// There are currently four types of windows:
|
||||
///
|
||||
/// - Containers
|
||||
/// - Placeholders that take the place of a window when it goes fullscreen
|
||||
/// - XDG toplevels
|
||||
/// - X windows
|
||||
///
|
||||
/// You can find out the type of a window by using the [`Window::type_`] function.
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
pub struct Window(pub u64);
|
||||
|
||||
bitflags! {
|
||||
/// The type of a window.
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Hash, Eq, PartialEq)]
|
||||
pub struct WindowType(pub u64) {
|
||||
/// A container.
|
||||
pub const CONTAINER = 1 << 0,
|
||||
/// A placeholder.
|
||||
pub const PLACEHOLDER = 1 << 1,
|
||||
/// An XDG toplevel.
|
||||
pub const XDG_TOPLEVEL = 1 << 2,
|
||||
/// An X window.
|
||||
pub const X_WINDOW = 1 << 3,
|
||||
}
|
||||
}
|
||||
|
||||
/// A window created by a client.
|
||||
///
|
||||
/// This is the same as `XDG_TOPLEVEL | X_WINDOW`.
|
||||
pub const CLIENT_WINDOW: WindowType = WindowType(XDG_TOPLEVEL.0 | X_WINDOW.0);
|
||||
|
||||
impl Window {
|
||||
/// Returns whether the window exists.
|
||||
pub fn exists(self) -> bool {
|
||||
self.0 != 0 && get!(false).window_exists(self)
|
||||
}
|
||||
|
||||
/// Returns whether the window does not exist.
|
||||
///
|
||||
/// This is a shorthand for `!self.exists()`.
|
||||
pub fn does_not_exist(self) -> bool {
|
||||
!self.exists()
|
||||
}
|
||||
|
||||
/// Returns the client of the window.
|
||||
///
|
||||
/// If the window does not have a client, [`Client::exists`] return false.
|
||||
pub fn client(self) -> Client {
|
||||
get!(Client(0)).window_client(self)
|
||||
}
|
||||
|
||||
/// Returns the title of the window.
|
||||
pub fn title(self) -> String {
|
||||
get!().window_title(self)
|
||||
}
|
||||
|
||||
/// Returns the type of the window.
|
||||
pub fn type_(self) -> WindowType {
|
||||
get!(WindowType(0)).window_type(self)
|
||||
}
|
||||
|
||||
/// Returns the identifier of the window.
|
||||
///
|
||||
/// This is the identifier used in the `ext-foreign-toplevel-list-v1` protocol.
|
||||
pub fn id(self) -> String {
|
||||
get!().window_id(self)
|
||||
}
|
||||
|
||||
/// Returns whether this window is visible.
|
||||
pub fn is_visible(self) -> bool {
|
||||
get!().window_is_visible(self)
|
||||
}
|
||||
|
||||
/// Returns the parent of this window.
|
||||
///
|
||||
/// If this window has no parent, [`Window::exists`] returns false.
|
||||
pub fn parent(self) -> Window {
|
||||
get!(Window(0)).window_parent(self)
|
||||
}
|
||||
|
||||
/// Returns the children of this window.
|
||||
///
|
||||
/// Only containers have children.
|
||||
pub fn children(self) -> Vec<Window> {
|
||||
get!().window_children(self)
|
||||
}
|
||||
|
||||
/// Moves the window in the specified direction.
|
||||
pub fn move_(self, direction: Direction) {
|
||||
get!().window_move(self, direction)
|
||||
}
|
||||
|
||||
/// Returns whether the parent-container of the window is in mono-mode.
|
||||
pub fn mono(self) -> bool {
|
||||
get!(false).window_mono(self)
|
||||
}
|
||||
|
||||
/// Sets whether the parent-container of the window is in mono-mode.
|
||||
pub fn set_mono(self, mono: bool) {
|
||||
get!().set_window_mono(self, mono)
|
||||
}
|
||||
|
||||
/// Toggles whether the parent-container of the window is in mono-mode.
|
||||
pub fn toggle_mono(self) {
|
||||
self.set_mono(!self.mono());
|
||||
}
|
||||
|
||||
/// Returns the split axis of the parent-container of the window.
|
||||
pub fn split(self) -> Axis {
|
||||
get!(Axis::Horizontal).window_split(self)
|
||||
}
|
||||
|
||||
/// Sets the split axis of the parent-container of the window.
|
||||
pub fn set_split(self, axis: Axis) {
|
||||
get!().set_window_split(self, axis)
|
||||
}
|
||||
|
||||
/// Toggles the split axis of the parent-container of the window.
|
||||
pub fn toggle_split(self) {
|
||||
self.set_split(self.split().other());
|
||||
}
|
||||
|
||||
/// Creates a new container with the specified split in place of the window.
|
||||
pub fn create_split(self, axis: Axis) {
|
||||
get!().create_window_split(self, axis);
|
||||
}
|
||||
|
||||
/// Requests the window to be closed.
|
||||
pub fn close(self) {
|
||||
get!().close_window(self);
|
||||
}
|
||||
|
||||
/// Returns whether the window is floating.
|
||||
pub fn floating(self) -> bool {
|
||||
get!().get_window_floating(self)
|
||||
}
|
||||
/// Sets whether the window is floating.
|
||||
pub fn set_floating(self, floating: bool) {
|
||||
get!().set_window_floating(self, floating);
|
||||
}
|
||||
|
||||
/// Toggles whether the window is floating.
|
||||
///
|
||||
/// You can do the same by double-clicking on the header.
|
||||
pub fn toggle_floating(self) {
|
||||
self.set_floating(!self.floating());
|
||||
}
|
||||
|
||||
/// Returns the workspace that this window belongs to.
|
||||
///
|
||||
/// If no such workspace exists, `exists` returns `false` for the returned workspace.
|
||||
pub fn workspace(self) -> Workspace {
|
||||
get!(Workspace(0)).get_window_workspace(self)
|
||||
}
|
||||
|
||||
/// Moves the window to the workspace.
|
||||
pub fn set_workspace(self, workspace: Workspace) {
|
||||
get!().set_window_workspace(self, workspace)
|
||||
}
|
||||
|
||||
/// Toggles whether the currently focused window is fullscreen.
|
||||
pub fn toggle_fullscreen(self) {
|
||||
self.set_fullscreen(!self.fullscreen())
|
||||
}
|
||||
/// Returns whether the window is fullscreen.
|
||||
pub fn fullscreen(self) -> bool {
|
||||
get!(false).get_window_fullscreen(self)
|
||||
}
|
||||
|
||||
/// Sets whether the window is fullscreen.
|
||||
pub fn set_fullscreen(self, fullscreen: bool) {
|
||||
get!().set_window_fullscreen(self, fullscreen)
|
||||
}
|
||||
|
||||
/// Gets whether the window is pinned.
|
||||
///
|
||||
/// If a floating window is pinned, it will stay visible even when switching to a
|
||||
/// different workspace.
|
||||
pub fn float_pinned(self) -> bool {
|
||||
get!().get_window_pinned(self)
|
||||
}
|
||||
|
||||
/// Sets whether the window is pinned.
|
||||
pub fn set_float_pinned(self, pinned: bool) {
|
||||
get!().set_window_pinned(self, pinned);
|
||||
}
|
||||
|
||||
/// Toggles whether the window is pinned.
|
||||
pub fn toggle_float_pinned(self) {
|
||||
self.set_float_pinned(!self.float_pinned());
|
||||
}
|
||||
}
|
||||
|
|
@ -9,8 +9,8 @@ use {
|
|||
ifs::wl_seat::SeatId,
|
||||
state::State,
|
||||
utils::{
|
||||
clonecell::CloneCell, numcell::NumCell, ptr_ext::PtrExt, unlink_on_drop::UnlinkOnDrop,
|
||||
xrd::xrd,
|
||||
clonecell::CloneCell, numcell::NumCell, ptr_ext::PtrExt,
|
||||
toplevel_identifier::ToplevelIdentifier, unlink_on_drop::UnlinkOnDrop, xrd::xrd,
|
||||
},
|
||||
},
|
||||
bincode::Options,
|
||||
|
|
@ -151,6 +151,15 @@ impl ConfigProxy {
|
|||
event,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn toplevel_removed(&self, id: ToplevelIdentifier) {
|
||||
let Some(handler) = self.handler.get() else {
|
||||
return;
|
||||
};
|
||||
if let Some(win) = handler.windows_from_tl_id.remove(&id) {
|
||||
handler.windows_to_tl_id.remove(&win);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ConfigProxy {
|
||||
|
|
@ -202,6 +211,9 @@ impl ConfigProxy {
|
|||
timers_by_id: Default::default(),
|
||||
pollable_id: Default::default(),
|
||||
pollables: Default::default(),
|
||||
window_ids: NumCell::new(1),
|
||||
windows_from_tl_id: Default::default(),
|
||||
windows_to_tl_id: Default::default(),
|
||||
});
|
||||
let init_msg = bincode_ops()
|
||||
.serialize(&InitMessage::V1(V1InitMessage {}))
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ use {
|
|||
theme::{Color, ThemeSized},
|
||||
tree::{
|
||||
ContainerNode, ContainerSplit, FloatNode, Node, NodeVisitorBase, OutputNode,
|
||||
TearingMode, VrrMode, WsMoveConfig, move_ws_to_output,
|
||||
TearingMode, ToplevelNode, VrrMode, WorkspaceNode, WsMoveConfig, move_ws_to_output,
|
||||
toplevel_create_split, toplevel_parent_container, toplevel_set_floating,
|
||||
toplevel_set_workspace,
|
||||
},
|
||||
utils::{
|
||||
asyncevent::AsyncEvent,
|
||||
|
|
@ -30,6 +32,7 @@ use {
|
|||
oserror::OsError,
|
||||
stack::Stack,
|
||||
timer::{TimerError, TimerFd},
|
||||
toplevel_identifier::ToplevelIdentifier,
|
||||
},
|
||||
},
|
||||
bincode::Options,
|
||||
|
|
@ -57,6 +60,7 @@ use {
|
|||
TearingMode as ConfigTearingMode, TransferFunction as ConfigTransferFunction,
|
||||
Transform, VrrMode as ConfigVrrMode,
|
||||
},
|
||||
window::Window,
|
||||
xwayland::XScalingMode,
|
||||
},
|
||||
libloading::Library,
|
||||
|
|
@ -89,6 +93,10 @@ pub(super) struct ConfigProxyHandler {
|
|||
|
||||
pub pollable_id: NumCell<u64>,
|
||||
pub pollables: CopyHashMap<PollableId, Rc<Pollable>>,
|
||||
|
||||
pub window_ids: NumCell<u64>,
|
||||
pub windows_from_tl_id: CopyHashMap<ToplevelIdentifier, Window>,
|
||||
pub windows_to_tl_id: CopyHashMap<Window, ToplevelIdentifier>,
|
||||
}
|
||||
|
||||
pub struct Pollable {
|
||||
|
|
@ -315,6 +323,24 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_fullscreen(&self, window: Window) -> Result<(), CphError> {
|
||||
let tl = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowFullscreen {
|
||||
fullscreen: tl.tl_data().is_fullscreen.get(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_window_fullscreen(
|
||||
&self,
|
||||
window: Window,
|
||||
fullscreen: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let tl = self.get_window(window)?;
|
||||
tl.tl_set_fullscreen(fullscreen);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_keymap(&self, seat: Seat, keymap: Keymap) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
let keymap = if keymap.is_invalid() {
|
||||
|
|
@ -492,6 +518,12 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_window_close(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
window.tl_close();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_seat_focus(&self, seat: Seat, direction: Direction) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.move_focus(direction.into());
|
||||
|
|
@ -504,6 +536,14 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_window_move(&self, window: Window, direction: Direction) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
if let Some(c) = toplevel_parent_container(&*window) {
|
||||
c.move_child(window, direction.into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_repeat_rate(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
let (rate, delay) = seat.get_rate();
|
||||
|
|
@ -530,6 +570,11 @@ impl ConfigProxyHandler {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_existing_workspace(&self, ws: Workspace) -> Result<Option<Rc<WorkspaceNode>>, CphError> {
|
||||
self.get_workspace(ws)
|
||||
.map(|ws| self.state.workspaces.get(&*ws))
|
||||
}
|
||||
|
||||
fn get_device_handler_data(
|
||||
&self,
|
||||
device: InputDevice,
|
||||
|
|
@ -720,8 +765,8 @@ impl ConfigProxyHandler {
|
|||
}
|
||||
|
||||
fn handle_get_workspace_capture(&self, workspace: Workspace) -> Result<(), CphError> {
|
||||
let name = self.get_workspace(workspace)?;
|
||||
let capture = match self.state.workspaces.get(name.as_str()) {
|
||||
let ws = self.get_existing_workspace(workspace)?;
|
||||
let capture = match ws {
|
||||
Some(ws) => ws.may_capture.get(),
|
||||
None => self.state.default_workspace_capture.get(),
|
||||
};
|
||||
|
|
@ -734,8 +779,7 @@ impl ConfigProxyHandler {
|
|||
workspace: Workspace,
|
||||
capture: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let name = self.get_workspace(workspace)?;
|
||||
if let Some(ws) = self.state.workspaces.get(name.as_str()) {
|
||||
if let Some(ws) = self.get_existing_workspace(workspace)? {
|
||||
ws.may_capture.set(capture);
|
||||
ws.update_has_captures();
|
||||
}
|
||||
|
|
@ -856,6 +900,20 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_window_workspace(&self, window: Window, ws: Workspace) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
let name = self.get_workspace(ws)?;
|
||||
let workspace = match self.state.workspaces.get(name.deref()) {
|
||||
Some(ws) => ws,
|
||||
_ => match window.node_output() {
|
||||
Some(o) => o.create_workspace(name.deref()),
|
||||
_ => return Ok(()),
|
||||
},
|
||||
};
|
||||
toplevel_set_workspace(&self.state, window, &workspace);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_device_name(&self, device: InputDevice) -> Result<(), CphError> {
|
||||
let dev = self.get_device_handler_data(device)?;
|
||||
let name = dev.device.name();
|
||||
|
|
@ -888,13 +946,10 @@ impl ConfigProxyHandler {
|
|||
) -> Result<(), CphError> {
|
||||
let output = self.get_output_node(connector)?;
|
||||
let ws = match workspace {
|
||||
WorkspaceSource::Explicit(ws) => {
|
||||
let name = self.get_workspace(ws)?;
|
||||
match self.state.workspaces.get(name.as_str()) {
|
||||
Some(ws) => ws,
|
||||
_ => return Ok(()),
|
||||
}
|
||||
}
|
||||
WorkspaceSource::Explicit(ws) => match self.get_existing_workspace(ws)? {
|
||||
Some(ws) => ws,
|
||||
_ => return Ok(()),
|
||||
},
|
||||
WorkspaceSource::Seat(s) => match self.get_seat(s)?.get_output().workspace.get() {
|
||||
Some(ws) => ws,
|
||||
_ => return Ok(()),
|
||||
|
|
@ -1180,6 +1235,20 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_float_pinned(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowFloatPinned {
|
||||
pinned: window.tl_pinned(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_window_float_pinned(&self, window: Window, pinned: bool) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
window.tl_set_pinned(true, pinned);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_vrr_mode(
|
||||
&self,
|
||||
connector: Option<Connector>,
|
||||
|
|
@ -1360,6 +1429,24 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_mono(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowMono {
|
||||
mono: toplevel_parent_container(&*window)
|
||||
.map(|c| c.mono_child.is_some())
|
||||
.unwrap_or(false),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_window_mono(&self, window: Window, mono: bool) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
if let Some(c) = toplevel_parent_container(&*window) {
|
||||
c.set_mono(mono.then_some(window.as_ref()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_seat_split(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
self.respond(Response::GetSplit {
|
||||
|
|
@ -1377,6 +1464,25 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_split(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowSplit {
|
||||
axis: toplevel_parent_container(&*window)
|
||||
.map(|c| c.split.get())
|
||||
.unwrap_or(ContainerSplit::Horizontal)
|
||||
.into(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_window_split(&self, window: Window, axis: Axis) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
if let Some(c) = toplevel_parent_container(&*window) {
|
||||
c.set_split(axis.into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_add_shortcut(
|
||||
&self,
|
||||
seat: Seat,
|
||||
|
|
@ -1480,6 +1586,12 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_create_window_split(&self, window: Window, axis: Axis) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
toplevel_create_split(&self.state, window, axis.into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_focus_seat_parent(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
seat.focus_parent();
|
||||
|
|
@ -1509,6 +1621,20 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_floating(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowFloating {
|
||||
floating: window.tl_data().is_floating.get(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_window_floating(&self, window: Window, floating: bool) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
toplevel_set_floating(&self.state, window, floating);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_add_pollable(self: &Rc<Self>, fd: i32) -> Result<(), CphError> {
|
||||
let fd = match fcntl_dupfd_cloexec(fd, 0) {
|
||||
Ok(fd) => Rc::new(fd),
|
||||
|
|
@ -1577,6 +1703,28 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn tl_to_window(&self, tl: &dyn ToplevelNode) -> Window {
|
||||
self.tl_id_to_window(tl.tl_data().identifier.get())
|
||||
}
|
||||
|
||||
fn tl_id_to_window(&self, tl: ToplevelIdentifier) -> Window {
|
||||
if let Some(win) = self.windows_from_tl_id.get(&tl) {
|
||||
return win;
|
||||
}
|
||||
let id = Window(self.window_ids.fetch_add(1));
|
||||
self.windows_from_tl_id.set(tl, id);
|
||||
self.windows_to_tl_id.set(id, tl);
|
||||
id
|
||||
}
|
||||
|
||||
fn get_window(&self, window: Window) -> Result<Rc<dyn ToplevelNode>, CphError> {
|
||||
self.windows_to_tl_id
|
||||
.get(&window)
|
||||
.and_then(|id| self.state.toplevels.get(&id))
|
||||
.and_then(|tl| tl.upgrade())
|
||||
.ok_or(CphError::WindowDoesNotExist(window))
|
||||
}
|
||||
|
||||
fn spaces_change(&self) {
|
||||
struct V;
|
||||
impl NodeVisitorBase for V {
|
||||
|
|
@ -1752,6 +1900,123 @@ impl ConfigProxyHandler {
|
|||
self.state.clients.kill(ClientId::from_raw(client.0));
|
||||
}
|
||||
|
||||
fn handle_get_workspace_window(&self, ws: Workspace) -> Result<(), CphError> {
|
||||
let window = self
|
||||
.get_existing_workspace(ws)?
|
||||
.and_then(|ws| ws.container.get())
|
||||
.map(|c| self.tl_to_window(&*c))
|
||||
.unwrap_or(Window(0));
|
||||
self.respond(Response::GetWorkspaceWindow { window });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_seat_keyboard_window(&self, seat: Seat) -> Result<(), CphError> {
|
||||
let window = self
|
||||
.get_seat(seat)?
|
||||
.get_keyboard_node()
|
||||
.node_toplevel()
|
||||
.map(|tl| self.tl_to_window(&*tl))
|
||||
.unwrap_or(Window(0));
|
||||
self.respond(Response::GetSeatKeyboardWindow { window });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_seat_focus_window(&self, seat: Seat, window_id: Window) -> Result<(), CphError> {
|
||||
let seat = self.get_seat(seat)?;
|
||||
let window = self.get_window(window_id)?;
|
||||
if !window.node_visible() {
|
||||
return Err(CphError::WindowNotVisible(window_id));
|
||||
}
|
||||
seat.focus_toplevel(window);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_title(&self, window: Window) -> Result<(), CphError> {
|
||||
let title = self.get_window(window)?.tl_data().title.borrow().clone();
|
||||
self.respond(Response::GetWindowTitle { title });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_type(&self, window: Window) -> Result<(), CphError> {
|
||||
let kind = self.get_window(window)?.tl_data().kind.to_window_type();
|
||||
self.respond(Response::GetWindowType { kind });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_window_exists(&self, window: Window) {
|
||||
self.respond(Response::WindowExists {
|
||||
exists: self.get_window(window).is_ok(),
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_get_window_id(&self, window: Window) -> Result<(), CphError> {
|
||||
let id = self
|
||||
.get_window(window)?
|
||||
.tl_data()
|
||||
.identifier
|
||||
.get()
|
||||
.to_string();
|
||||
self.respond(Response::GetWindowId { id: id.to_string() });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_is_visible(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowIsVisible {
|
||||
visible: window.node_visible(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_client(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self.get_window(window)?;
|
||||
self.respond(Response::GetWindowClient {
|
||||
client: window
|
||||
.tl_data()
|
||||
.client
|
||||
.as_ref()
|
||||
.map(|c| ConfigClient(c.id.raw()))
|
||||
.unwrap_or(ConfigClient(0)),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_parent(&self, window: Window) -> Result<(), CphError> {
|
||||
let window = self
|
||||
.get_window(window)?
|
||||
.tl_data()
|
||||
.parent
|
||||
.get()
|
||||
.and_then(|tl| tl.node_into_toplevel())
|
||||
.map(|tl| self.tl_to_window(&*tl))
|
||||
.unwrap_or(Window(0));
|
||||
self.respond(Response::GetWindowParent { window });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_workspace(&self, window: Window) -> Result<(), CphError> {
|
||||
let workspace = self
|
||||
.get_window(window)?
|
||||
.tl_data()
|
||||
.workspace
|
||||
.get()
|
||||
.map(|ws| self.get_workspace_by_name(&ws.name))
|
||||
.unwrap_or(Workspace(0));
|
||||
self.respond(Response::GetWindowWorkspace { workspace });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_get_window_children(&self, window: Window) -> Result<(), CphError> {
|
||||
let mut windows = vec![];
|
||||
if let Some(c) = self.get_window(window)?.node_into_container() {
|
||||
for c in c.children.iter() {
|
||||
windows.push(self.tl_to_window(&*c.node));
|
||||
}
|
||||
}
|
||||
self.respond(Response::GetWindowChildren { windows });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn handle_request(self: &Rc<Self>, msg: &[u8]) {
|
||||
if let Err(e) = self.handle_request_(msg) {
|
||||
log::error!("Could not handle client request: {}", ErrorFmt(e));
|
||||
|
|
@ -2171,6 +2436,82 @@ impl ConfigProxyHandler {
|
|||
.handle_client_is_xwayland(client)
|
||||
.wrn("client_is_xwayland")?,
|
||||
ClientMessage::ClientKill { client } => self.handle_client_kill(client),
|
||||
ClientMessage::WindowExists { window } => self.handle_window_exists(window),
|
||||
ClientMessage::GetWorkspaceWindow { workspace } => self
|
||||
.handle_get_workspace_window(workspace)
|
||||
.wrn("get_workspace_window")?,
|
||||
ClientMessage::GetSeatKeyboardWindow { seat } => self
|
||||
.handle_get_seat_keyboard_window(seat)
|
||||
.wrn("get_seat_keyboard_window")?,
|
||||
ClientMessage::SeatFocusWindow { seat, window } => self
|
||||
.handle_seat_focus_window(seat, window)
|
||||
.wrn("seat_focus_window")?,
|
||||
ClientMessage::GetWindowTitle { window } => self
|
||||
.handle_get_window_title(window)
|
||||
.wrn("get_window_title")?,
|
||||
ClientMessage::GetWindowType { window } => {
|
||||
self.handle_get_window_type(window).wrn("get_window_type")?
|
||||
}
|
||||
ClientMessage::GetWindowId { window } => {
|
||||
self.handle_get_window_id(window).wrn("get_window_id")?
|
||||
}
|
||||
ClientMessage::GetWindowParent { window } => self
|
||||
.handle_get_window_parent(window)
|
||||
.wrn("get_window_parent")?,
|
||||
ClientMessage::GetWindowWorkspace { window } => self
|
||||
.handle_get_window_workspace(window)
|
||||
.wrn("get_window_workspace")?,
|
||||
ClientMessage::GetWindowChildren { window } => self
|
||||
.handle_get_window_children(window)
|
||||
.wrn("get_window_children")?,
|
||||
ClientMessage::GetWindowSplit { window } => self
|
||||
.handle_get_window_split(window)
|
||||
.wrn("get_window_split")?,
|
||||
ClientMessage::SetWindowSplit { window, axis } => self
|
||||
.handle_set_window_split(window, axis)
|
||||
.wrn("set_window_split")?,
|
||||
ClientMessage::GetWindowMono { window } => {
|
||||
self.handle_get_window_mono(window).wrn("get_window_mono")?
|
||||
}
|
||||
ClientMessage::SetWindowMono { window, mono } => self
|
||||
.handle_set_window_mono(window, mono)
|
||||
.wrn("set_window_mono")?,
|
||||
ClientMessage::WindowMove { window, direction } => self
|
||||
.handle_window_move(window, direction)
|
||||
.wrn("window_move")?,
|
||||
ClientMessage::CreateWindowSplit { window, axis } => self
|
||||
.handle_create_window_split(window, axis)
|
||||
.wrn("create_window_split")?,
|
||||
ClientMessage::WindowClose { window } => {
|
||||
self.handle_window_close(window).wrn("close_window")?
|
||||
}
|
||||
ClientMessage::GetWindowFloating { window } => self
|
||||
.handle_get_window_floating(window)
|
||||
.wrn("get_window_floating")?,
|
||||
ClientMessage::SetWindowFloating { window, floating } => self
|
||||
.handle_set_window_floating(window, floating)
|
||||
.wrn("set_window_floating")?,
|
||||
ClientMessage::SetWindowWorkspace { window, workspace } => self
|
||||
.handle_set_window_workspace(window, workspace)
|
||||
.wrn("set_window_workspace")?,
|
||||
ClientMessage::SetWindowFullscreen { window, fullscreen } => self
|
||||
.handle_set_window_fullscreen(window, fullscreen)
|
||||
.wrn("set_window_fullscreen")?,
|
||||
ClientMessage::GetWindowFullscreen { window } => self
|
||||
.handle_get_window_fullscreen(window)
|
||||
.wrn("get_window_fullscreen")?,
|
||||
ClientMessage::GetWindowFloatPinned { window } => self
|
||||
.handle_get_window_float_pinned(window)
|
||||
.wrn("get_window_float_pinned")?,
|
||||
ClientMessage::SetWindowFloatPinned { window, pinned } => self
|
||||
.handle_set_window_float_pinned(window, pinned)
|
||||
.wrn("set_window_float_pinned")?,
|
||||
ClientMessage::GetWindowIsVisible { window } => self
|
||||
.handle_get_window_is_visible(window)
|
||||
.wrn("get_window_is_visible")?,
|
||||
ClientMessage::GetWindowClient { window } => self
|
||||
.handle_get_window_client(window)
|
||||
.wrn("get_window_client")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -2248,6 +2589,10 @@ enum CphError {
|
|||
UnknownTransferFunction(ConfigTransferFunction),
|
||||
#[error("Client {0:?} does not exist")]
|
||||
ClientDoesNotExist(ConfigClient),
|
||||
#[error("Window {0:?} does not exist")]
|
||||
WindowDoesNotExist(Window),
|
||||
#[error("Window {0:?} is not visible")]
|
||||
WindowNotVisible(Window),
|
||||
}
|
||||
|
||||
trait WithRequestName {
|
||||
|
|
|
|||
|
|
@ -79,7 +79,8 @@ use {
|
|||
state::{DeviceHandlerData, State},
|
||||
tree::{
|
||||
ContainerNode, ContainerSplit, Direction, FoundNode, Node, OutputNode, ToplevelNode,
|
||||
WorkspaceNode, generic_node_visitor,
|
||||
WorkspaceNode, generic_node_visitor, toplevel_create_split, toplevel_parent_container,
|
||||
toplevel_set_floating, toplevel_set_workspace,
|
||||
},
|
||||
utils::{
|
||||
asyncevent::AsyncEvent, bindings::PerClientBindings, clonecell::CloneCell,
|
||||
|
|
@ -401,6 +402,10 @@ impl WlSeatGlobal {
|
|||
self.cursor_user_group.latest_output()
|
||||
}
|
||||
|
||||
pub fn get_keyboard_node(&self) -> Rc<dyn Node> {
|
||||
self.keyboard_node.get()
|
||||
}
|
||||
|
||||
pub fn get_keyboard_output(&self) -> Option<Rc<OutputNode>> {
|
||||
self.keyboard_node.get().node_output()
|
||||
}
|
||||
|
|
@ -410,38 +415,7 @@ impl WlSeatGlobal {
|
|||
Some(tl) => tl,
|
||||
_ => return,
|
||||
};
|
||||
if tl.tl_data().is_fullscreen.get() {
|
||||
return;
|
||||
}
|
||||
let old_ws = match tl.tl_data().workspace.get() {
|
||||
Some(ws) => ws,
|
||||
_ => return,
|
||||
};
|
||||
if old_ws.id == ws.id {
|
||||
return;
|
||||
}
|
||||
let cn = match tl.tl_data().parent.get() {
|
||||
Some(cn) => cn,
|
||||
_ => return,
|
||||
};
|
||||
let kb_foci = collect_kb_foci(tl.clone());
|
||||
cn.cnode_remove_child2(&*tl, true);
|
||||
if !ws.visible.get() {
|
||||
for focus in kb_foci {
|
||||
old_ws.clone().node_do_focus(&focus, Direction::Unspecified);
|
||||
}
|
||||
}
|
||||
if tl.tl_data().is_floating.get() {
|
||||
self.state.map_floating(
|
||||
tl.clone(),
|
||||
tl.tl_data().float_width.get(),
|
||||
tl.tl_data().float_height.get(),
|
||||
ws,
|
||||
None,
|
||||
);
|
||||
} else {
|
||||
self.state.map_tiled_on(tl, ws);
|
||||
}
|
||||
toplevel_set_workspace(&self.state, tl, ws);
|
||||
}
|
||||
|
||||
pub fn mark_last_active(self: &Rc<Self>) {
|
||||
|
|
@ -556,11 +530,7 @@ impl WlSeatGlobal {
|
|||
|
||||
pub fn kb_parent_container(&self) -> Option<Rc<ContainerNode>> {
|
||||
if let Some(tl) = self.keyboard_node.get().node_toplevel() {
|
||||
if let Some(parent) = tl.tl_data().parent.get() {
|
||||
if let Some(container) = parent.node_into_container() {
|
||||
return Some(container);
|
||||
}
|
||||
}
|
||||
return toplevel_parent_container(&*tl);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
@ -595,21 +565,7 @@ impl WlSeatGlobal {
|
|||
Some(tl) => tl,
|
||||
_ => return,
|
||||
};
|
||||
if tl.tl_data().is_fullscreen.get() {
|
||||
return;
|
||||
}
|
||||
let ws = match tl.tl_data().workspace.get() {
|
||||
Some(ws) => ws,
|
||||
_ => return,
|
||||
};
|
||||
let pn = match tl.tl_data().parent.get() {
|
||||
Some(pn) => pn,
|
||||
_ => return,
|
||||
};
|
||||
if let Some(pn) = pn.node_into_containing_node() {
|
||||
let cn = ContainerNode::new(&self.state, &ws, tl.clone(), axis);
|
||||
pn.cnode_replace_child(&*tl, cn);
|
||||
}
|
||||
toplevel_create_split(&self.state, tl, axis);
|
||||
}
|
||||
|
||||
pub fn focus_parent(self: &Rc<Self>) {
|
||||
|
|
@ -634,29 +590,7 @@ impl WlSeatGlobal {
|
|||
Some(tl) => tl,
|
||||
_ => return,
|
||||
};
|
||||
self.set_tl_floating(tl, floating);
|
||||
}
|
||||
|
||||
pub fn set_tl_floating(self: &Rc<Self>, tl: Rc<dyn ToplevelNode>, floating: bool) {
|
||||
let data = tl.tl_data();
|
||||
if data.is_fullscreen.get() {
|
||||
return;
|
||||
}
|
||||
if data.is_floating.get() == floating {
|
||||
return;
|
||||
}
|
||||
let parent = match data.parent.get() {
|
||||
Some(p) => p,
|
||||
_ => return,
|
||||
};
|
||||
if !floating {
|
||||
parent.cnode_remove_child2(&*tl, true);
|
||||
self.state.map_tiled(tl);
|
||||
} else if let Some(ws) = data.workspace.get() {
|
||||
parent.cnode_remove_child2(&*tl, true);
|
||||
let (width, height) = data.float_size(&ws);
|
||||
self.state.map_floating(tl, width, height, &ws, None);
|
||||
}
|
||||
toplevel_set_floating(&self.state, tl, floating);
|
||||
}
|
||||
|
||||
pub fn get_rate(&self) -> (i32, i32) {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use {
|
|||
tree::{
|
||||
ContainerSplit, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId,
|
||||
NodeVisitor, OutputNode, StackedNode, TileDragDestination, ToplevelData, ToplevelNode,
|
||||
ToplevelNodeBase, WorkspaceNode, default_tile_drag_destination,
|
||||
ToplevelNodeBase, ToplevelType, WorkspaceNode, default_tile_drag_destination,
|
||||
},
|
||||
utils::{clonecell::CloneCell, copyhashmap::CopyHashMap, linkedlist::LinkedNode},
|
||||
wire::WlSurfaceId,
|
||||
|
|
@ -205,16 +205,19 @@ impl Xwindow {
|
|||
if xsurface.xwindow.is_some() {
|
||||
return Err(XWindowError::AlreadyAttached);
|
||||
}
|
||||
let id = data.state.node_ids.next();
|
||||
let slf = Rc::new_cyclic(|weak| {
|
||||
let tld = ToplevelData::new(
|
||||
&data.state,
|
||||
data.info.title.borrow_mut().clone().unwrap_or_default(),
|
||||
Some(surface.client.clone()),
|
||||
ToplevelType::XWindow,
|
||||
id,
|
||||
weak,
|
||||
);
|
||||
tld.pos.set(surface.extents.get());
|
||||
Self {
|
||||
id: data.state.node_ids.next(),
|
||||
id,
|
||||
data: data.clone(),
|
||||
display_link: Default::default(),
|
||||
toplevel_data: tld,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ use {
|
|||
tree::{
|
||||
ContainerSplit, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId,
|
||||
NodeVisitor, OutputNode, TileDragDestination, ToplevelData, ToplevelNode,
|
||||
ToplevelNodeBase, ToplevelNodeId, WorkspaceNode, default_tile_drag_destination,
|
||||
ToplevelNodeBase, ToplevelNodeId, ToplevelType, WorkspaceNode,
|
||||
default_tile_drag_destination,
|
||||
},
|
||||
utils::{clonecell::CloneCell, hash_map_ext::HashMapExt},
|
||||
wire::{XdgToplevelId, xdg_toplevel::*},
|
||||
|
|
@ -133,11 +134,12 @@ impl XdgToplevel {
|
|||
states.insert(STATE_CONSTRAINED_BOTTOM);
|
||||
}
|
||||
let state = &surface.surface.client.state;
|
||||
let node_id = state.node_ids.next();
|
||||
Self {
|
||||
id,
|
||||
state: state.clone(),
|
||||
xdg: surface.clone(),
|
||||
node_id: state.node_ids.next(),
|
||||
node_id,
|
||||
parent: Default::default(),
|
||||
children: RefCell::new(Default::default()),
|
||||
states: RefCell::new(states),
|
||||
|
|
@ -152,6 +154,8 @@ impl XdgToplevel {
|
|||
state,
|
||||
String::new(),
|
||||
Some(surface.surface.client.clone()),
|
||||
ToplevelType::XdgToplevel,
|
||||
node_id,
|
||||
slf,
|
||||
),
|
||||
drag: Default::default(),
|
||||
|
|
|
|||
|
|
@ -951,6 +951,13 @@ impl State {
|
|||
self.workspace_managers.clear();
|
||||
}
|
||||
|
||||
pub fn remove_toplevel_id(&self, id: ToplevelIdentifier) {
|
||||
self.toplevels.remove(&id);
|
||||
if let Some(config) = self.config.get() {
|
||||
config.toplevel_removed(id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn damage_hardware_cursors(&self, render: bool) {
|
||||
for output in self.root.outputs.lock().values() {
|
||||
if let Some(hc) = output.hardware_cursor.get() {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ use {
|
|||
tree::{
|
||||
ContainingNode, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId,
|
||||
OutputNode, TddType, TileDragDestination, ToplevelData, ToplevelNode, ToplevelNodeBase,
|
||||
WorkspaceNode, default_tile_drag_bounds, walker::NodeVisitor,
|
||||
ToplevelType, WorkspaceNode, default_tile_drag_bounds, toplevel_set_floating,
|
||||
walker::NodeVisitor,
|
||||
},
|
||||
utils::{
|
||||
asyncevent::AsyncEvent,
|
||||
|
|
@ -212,8 +213,9 @@ impl ContainerNode {
|
|||
let child_node_ref = child_node.clone();
|
||||
let mut child_nodes = AHashMap::new();
|
||||
child_nodes.insert(child.node_id(), child_node);
|
||||
let id = state.node_ids.next();
|
||||
let slf = Rc::new_cyclic(|weak| Self {
|
||||
id: state.node_ids.next(),
|
||||
id,
|
||||
split: Cell::new(split),
|
||||
mono_child: CloneCell::new(None),
|
||||
mono_body: Cell::new(Default::default()),
|
||||
|
|
@ -237,7 +239,14 @@ impl ContainerNode {
|
|||
state: state.clone(),
|
||||
render_data: Default::default(),
|
||||
scroller: Default::default(),
|
||||
toplevel_data: ToplevelData::new(state, Default::default(), None, weak),
|
||||
toplevel_data: ToplevelData::new(
|
||||
state,
|
||||
Default::default(),
|
||||
None,
|
||||
ToplevelType::Container,
|
||||
id,
|
||||
weak,
|
||||
),
|
||||
attention_requests: Default::default(),
|
||||
});
|
||||
child.tl_set_parent(slf.clone());
|
||||
|
|
@ -1239,7 +1248,7 @@ impl ContainerNode {
|
|||
&& kind == SeatOpKind::Move
|
||||
{
|
||||
drop(seat_datas);
|
||||
seat.set_tl_floating(child.node.clone(), true);
|
||||
toplevel_set_floating(&self.state, child.node.clone(), true);
|
||||
return;
|
||||
}
|
||||
seat_data.op = Some(SeatOp {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use {
|
|||
tree::{
|
||||
ContainingNode, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId,
|
||||
OutputNode, PinnedNode, StackedNode, TileDragDestination, ToplevelNode, WorkspaceNode,
|
||||
walker::NodeVisitor,
|
||||
toplevel_set_floating, walker::NodeVisitor,
|
||||
},
|
||||
utils::{
|
||||
asyncevent::AsyncEvent, clonecell::CloneCell, double_click_state::DoubleClickState,
|
||||
|
|
@ -603,7 +603,7 @@ impl FloatNode {
|
|||
{
|
||||
if let Some(tl) = self.child.get() {
|
||||
drop(cursors);
|
||||
seat.set_tl_floating(tl, false);
|
||||
toplevel_set_floating(&self.state, tl, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use {
|
|||
tree::{
|
||||
ContainerSplit, Direction, FindTreeResult, FindTreeUsecase, FoundNode, Node, NodeId,
|
||||
NodeVisitor, OutputNode, TileDragDestination, ToplevelData, ToplevelNode,
|
||||
ToplevelNodeBase, default_tile_drag_destination,
|
||||
ToplevelNodeBase, ToplevelType, default_tile_drag_destination,
|
||||
},
|
||||
utils::{
|
||||
asyncevent::AsyncEvent, errorfmt::ErrorFmt, on_drop_event::OnDropEvent,
|
||||
|
|
@ -49,12 +49,15 @@ pub async fn placeholder_render_textures(state: Rc<State>) {
|
|||
|
||||
impl PlaceholderNode {
|
||||
pub fn new_for(state: &Rc<State>, node: Rc<dyn ToplevelNode>, slf: &Weak<Self>) -> Self {
|
||||
let id = state.node_ids.next();
|
||||
Self {
|
||||
id: state.node_ids.next(),
|
||||
id,
|
||||
toplevel: ToplevelData::new(
|
||||
state,
|
||||
node.tl_data().title.borrow().clone(),
|
||||
node.node_client(),
|
||||
ToplevelType::Placeholder,
|
||||
id,
|
||||
slf,
|
||||
),
|
||||
destroyed: Default::default(),
|
||||
|
|
@ -65,9 +68,17 @@ impl PlaceholderNode {
|
|||
}
|
||||
|
||||
pub fn new_empty(state: &Rc<State>, slf: &Weak<Self>) -> Self {
|
||||
let id = state.node_ids.next();
|
||||
Self {
|
||||
id: state.node_ids.next(),
|
||||
toplevel: ToplevelData::new(state, String::new(), None, slf),
|
||||
id,
|
||||
toplevel: ToplevelData::new(
|
||||
state,
|
||||
String::new(),
|
||||
None,
|
||||
ToplevelType::Placeholder,
|
||||
id,
|
||||
slf,
|
||||
),
|
||||
destroyed: Default::default(),
|
||||
update_textures_scheduled: Default::default(),
|
||||
state: state.clone(),
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ use {
|
|||
JayToplevelId,
|
||||
},
|
||||
},
|
||||
jay_config::{window, window::WindowType},
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
ops::Deref,
|
||||
|
|
@ -254,7 +255,29 @@ impl ToplevelOpt {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ToplevelType {
|
||||
Container,
|
||||
Placeholder,
|
||||
XdgToplevel,
|
||||
XWindow,
|
||||
}
|
||||
|
||||
impl ToplevelType {
|
||||
pub fn to_window_type(&self) -> WindowType {
|
||||
match self {
|
||||
ToplevelType::Container => window::CONTAINER,
|
||||
ToplevelType::Placeholder => window::PLACEHOLDER,
|
||||
ToplevelType::XdgToplevel => window::XDG_TOPLEVEL,
|
||||
ToplevelType::XWindow => window::X_WINDOW,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ToplevelData {
|
||||
#[expect(dead_code)]
|
||||
pub node_id: NodeId,
|
||||
pub kind: ToplevelType,
|
||||
pub self_active: Cell<bool>,
|
||||
pub client: Option<Rc<Client>>,
|
||||
pub state: Rc<State>,
|
||||
|
|
@ -291,11 +314,16 @@ impl ToplevelData {
|
|||
state: &Rc<State>,
|
||||
title: String,
|
||||
client: Option<Rc<Client>>,
|
||||
kind: ToplevelType,
|
||||
node_id: impl Into<NodeId>,
|
||||
slf: &Weak<T>,
|
||||
) -> Self {
|
||||
let node_id = node_id.into();
|
||||
let id = toplevel_identifier();
|
||||
state.toplevels.set(id, slf.clone());
|
||||
Self {
|
||||
node_id,
|
||||
kind,
|
||||
self_active: Cell::new(false),
|
||||
client,
|
||||
state: state.clone(),
|
||||
|
|
@ -372,7 +400,7 @@ impl ToplevelData {
|
|||
{
|
||||
let id = toplevel_identifier();
|
||||
let prev = self.identifier.replace(id);
|
||||
self.state.toplevels.remove(&prev);
|
||||
self.state.remove_toplevel_id(prev);
|
||||
self.state.toplevels.set(id, self.slf.clone());
|
||||
}
|
||||
{
|
||||
|
|
@ -620,7 +648,7 @@ impl ToplevelData {
|
|||
|
||||
impl Drop for ToplevelData {
|
||||
fn drop(&mut self) {
|
||||
self.state.toplevels.remove(&self.identifier.get());
|
||||
self.state.remove_toplevel_id(self.identifier.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -662,3 +690,82 @@ pub fn default_tile_drag_bounds<T: ToplevelNodeBase + ?Sized>(t: &T, split: Cont
|
|||
ContainerSplit::Vertical => t.node_absolute_position().height() / FACTOR,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toplevel_parent_container(tl: &dyn ToplevelNode) -> Option<Rc<ContainerNode>> {
|
||||
if let Some(parent) = tl.tl_data().parent.get() {
|
||||
if let Some(container) = parent.node_into_container() {
|
||||
return Some(container);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn toplevel_create_split(state: &Rc<State>, tl: Rc<dyn ToplevelNode>, axis: ContainerSplit) {
|
||||
if tl.tl_data().is_fullscreen.get() {
|
||||
return;
|
||||
}
|
||||
let ws = match tl.tl_data().workspace.get() {
|
||||
Some(ws) => ws,
|
||||
_ => return,
|
||||
};
|
||||
let pn = match tl.tl_data().parent.get() {
|
||||
Some(pn) => pn,
|
||||
_ => return,
|
||||
};
|
||||
if let Some(pn) = pn.node_into_containing_node() {
|
||||
let cn = ContainerNode::new(state, &ws, tl.clone(), axis);
|
||||
pn.cnode_replace_child(&*tl, cn);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toplevel_set_floating(state: &Rc<State>, tl: Rc<dyn ToplevelNode>, floating: bool) {
|
||||
let data = tl.tl_data();
|
||||
if data.is_fullscreen.get() {
|
||||
return;
|
||||
}
|
||||
if data.is_floating.get() == floating {
|
||||
return;
|
||||
}
|
||||
let parent = match data.parent.get() {
|
||||
Some(p) => p,
|
||||
_ => return,
|
||||
};
|
||||
if !floating {
|
||||
parent.cnode_remove_child2(&*tl, true);
|
||||
state.map_tiled(tl);
|
||||
} else if let Some(ws) = data.workspace.get() {
|
||||
parent.cnode_remove_child2(&*tl, true);
|
||||
let (width, height) = data.float_size(&ws);
|
||||
state.map_floating(tl, width, height, &ws, None);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toplevel_set_workspace(state: &Rc<State>, tl: Rc<dyn ToplevelNode>, ws: &Rc<WorkspaceNode>) {
|
||||
if tl.tl_data().is_fullscreen.get() {
|
||||
return;
|
||||
}
|
||||
let old_ws = match tl.tl_data().workspace.get() {
|
||||
Some(ws) => ws,
|
||||
_ => return,
|
||||
};
|
||||
if old_ws.id == ws.id {
|
||||
return;
|
||||
}
|
||||
let cn = match tl.tl_data().parent.get() {
|
||||
Some(cn) => cn,
|
||||
_ => return,
|
||||
};
|
||||
let kb_foci = collect_kb_foci(tl.clone());
|
||||
cn.cnode_remove_child2(&*tl, true);
|
||||
if !ws.visible.get() {
|
||||
for focus in kb_foci {
|
||||
old_ws.clone().node_do_focus(&focus, Direction::Unspecified);
|
||||
}
|
||||
}
|
||||
if tl.tl_data().is_floating.get() {
|
||||
let (width, height) = tl.tl_data().float_size(ws);
|
||||
state.map_floating(tl.clone(), width, height, ws, None);
|
||||
} else {
|
||||
state.map_tiled_on(tl, ws);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
use {
|
||||
crate::utils::{
|
||||
linkedlist::NodeRef,
|
||||
ptr_ext::{MutPtrExt, PtrExt},
|
||||
crate::{
|
||||
tree::NodeId,
|
||||
utils::{
|
||||
linkedlist::NodeRef,
|
||||
ptr_ext::{MutPtrExt, PtrExt},
|
||||
},
|
||||
},
|
||||
jay_config::keyboard::mods::Modifiers,
|
||||
jay_config::{keyboard::mods::Modifiers, window::Window},
|
||||
std::{
|
||||
cell::UnsafeCell,
|
||||
fmt::{Debug, Formatter},
|
||||
|
|
@ -97,3 +100,7 @@ unsafe impl UnsafeCellCloneSafe for usize {}
|
|||
unsafe impl<A: UnsafeCellCloneSafe, B: UnsafeCellCloneSafe> UnsafeCellCloneSafe for (A, B) {}
|
||||
|
||||
unsafe impl UnsafeCellCloneSafe for Modifiers {}
|
||||
|
||||
unsafe impl UnsafeCellCloneSafe for NodeId {}
|
||||
|
||||
unsafe impl UnsafeCellCloneSafe for Window {}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
use {
|
||||
crate::utils::opaque::{OPAQUE_LEN, Opaque, OpaqueError, opaque},
|
||||
crate::utils::{
|
||||
clonecell::UnsafeCellCloneSafe,
|
||||
opaque::{OPAQUE_LEN, Opaque, OpaqueError, opaque},
|
||||
},
|
||||
arrayvec::ArrayString,
|
||||
std::{
|
||||
fmt::{Display, Formatter},
|
||||
|
|
@ -10,6 +13,8 @@ use {
|
|||
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
|
||||
pub struct ToplevelIdentifier(Opaque);
|
||||
|
||||
unsafe impl UnsafeCellCloneSafe for ToplevelIdentifier {}
|
||||
|
||||
pub fn toplevel_identifier() -> ToplevelIdentifier {
|
||||
ToplevelIdentifier(opaque())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue