1
0
Fork 0
forked from wry/wry

config: add Window

This commit is contained in:
Julian Orth 2025-04-29 15:29:39 +02:00
parent ab095b89cf
commit 9977f9dfdf
19 changed files with 1172 additions and 203 deletions

View file

@ -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 });

View file

@ -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)]

View file

@ -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.

View file

@ -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
}
}

View file

@ -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.

View file

@ -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
View 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());
}
}