1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! Metadata around X clients and manipulating them
use crate::core::xconnection::{Atom, Prop, WmHints, WmNormalHints, XClientProperties, Xid};
/**
* Meta-data around a client window that we are handling.
*
* Primarily state flags and information used when determining which clients
* to show for a given monitor and how they are tiled.
*/
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct Client {
pub(crate) id: Xid,
pub(crate) workspace: usize,
pub(crate) wm_name: String,
pub(crate) wm_class: Vec<String>, // should always be two elements but that's not enforced?
pub(crate) wm_type: Vec<String>, // Can't use Atom as it could be something arbitrary
pub(crate) wm_protocols: Vec<String>, // Can't use Atom as it could be something arbitrary
pub(crate) wm_hints: Option<WmHints>,
pub(crate) wm_normal_hints: Option<WmNormalHints>,
// state flags
pub(crate) accepts_focus: bool,
pub(crate) floating: bool,
pub(crate) fullscreen: bool,
pub(crate) mapped: bool,
pub(crate) urgent: bool,
pub(crate) wm_managed: bool,
}
impl Client {
/// Track a new client window on a specific workspace
///
/// This uses the provided [`XClientProperties`] to query state from the X server about the
/// client and cache that for later use. If any of the requests fail then we set defaults
/// rather than erroring as we always need to be able to track clients when they are mapped.
pub(crate) fn new<X>(conn: &X, id: Xid, workspace: usize, floating_classes: &[&str]) -> Self
where
X: XClientProperties,
{
// TODO: do we want error logging around setting defaults here?
// the xcb impl probably needs to catch BadAtom as "missing"?
let floating = conn.client_should_float(id, floating_classes);
let accepts_focus = conn.client_accepts_focus(id);
let wm_name = conn.client_name(id).unwrap_or("unknown".into());
let wm_class = match conn.get_prop(id, Atom::WmClass.as_ref()) {
Ok(Prop::UTF8String(strs)) => strs,
_ => vec![],
};
let wm_type = match conn.get_prop(id, Atom::NetWmWindowType.as_ref()) {
Ok(Prop::Atom(atoms)) => atoms,
_ => vec![Atom::NetWindowTypeNormal.as_ref().to_string()],
};
let wm_hints = match conn.get_prop(id, Atom::WmHints.as_ref()) {
Ok(Prop::WmHints(hints)) => Some(hints),
_ => None,
};
let wm_normal_hints = match conn.get_prop(id, Atom::WmNormalHints.as_ref()) {
Ok(Prop::WmNormalHints(hints)) => Some(hints),
_ => None,
};
let wm_protocols = match conn.get_prop(id, Atom::WmProtocols.as_ref()) {
Ok(Prop::Atom(protocols)) => protocols,
_ => vec![],
};
Self {
id,
workspace,
wm_name,
wm_class,
wm_type,
wm_protocols,
wm_hints,
wm_normal_hints,
floating,
accepts_focus,
fullscreen: false,
mapped: false,
urgent: false,
wm_managed: true,
}
}
/// The X window ID of this client
pub fn id(&self) -> Xid {
self.id
}
/// The WM_CLASS property of this client
pub fn wm_class(&self) -> &str {
self.class()
}
/// The WM_NAME property of this client
pub fn wm_name(&self) -> &str {
&self.wm_name
}
/// Whether or not this client is currently fullscreen
pub fn is_fullscreen(&self) -> bool {
self.fullscreen
}
/// The current workspace index that this client is showing on
pub fn workspace(&self) -> usize {
self.workspace
}
/// Mark this window as being on a new workspace
pub fn set_workspace(&mut self, workspace: usize) {
self.workspace = workspace
}
/// Set the floating state of this client
pub fn set_floating(&mut self, floating: bool) {
self.floating = floating
}
pub(crate) fn set_name(&mut self, name: impl Into<String>) {
self.wm_name = name.into()
}
/// The WM_CLASS of the window that this Client is tracking
pub fn class(&self) -> &str {
self.wm_class.get(0).map(AsRef::as_ref).unwrap_or("unknown")
}
/// Mark this client as not being managed by the WindowManager directly
pub fn externally_managed(&mut self) {
self.wm_managed = false;
}
/// Mark this client as being managed by the WindowManager directly
pub fn internally_managed(&mut self) {
self.wm_managed = true;
}
}