1use std::sync::Arc;
2
3use super::*;
4
5#[derive(Debug)]
6#[allow(dead_code)]
7pub enum StateOperation {
8 Remove = 0, // _NET_WM_STATE_REMOVE
9 Add = 1, // _NET_WM_STATE_ADD
10 Toggle = 2, // _NET_WM_STATE_TOGGLE
11}
12
13impl From<bool> for StateOperation {
14 fn from(op: bool) -> Self {
15 if op {
16 StateOperation::Add
17 } else {
18 StateOperation::Remove
19 }
20 }
21}
22
23/// X window type. Maps directly to
24/// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html).
25#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
26#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
27pub enum WindowType {
28 /// A desktop feature. This can include a single window containing desktop icons with the same dimensions as the
29 /// screen, allowing the desktop environment to have full control of the desktop, without the need for proxying
30 /// root window clicks.
31 Desktop,
32 /// A dock or panel feature. Typically a Window Manager would keep such windows on top of all other windows.
33 Dock,
34 /// Toolbar windows. "Torn off" from the main application.
35 Toolbar,
36 /// Pinnable menu windows. "Torn off" from the main application.
37 Menu,
38 /// A small persistent utility window, such as a palette or toolbox.
39 Utility,
40 /// The window is a splash screen displayed as an application is starting up.
41 Splash,
42 /// This is a dialog window.
43 Dialog,
44 /// A dropdown menu that usually appears when the user clicks on an item in a menu bar.
45 /// This property is typically used on override-redirect windows.
46 DropdownMenu,
47 /// A popup menu that usually appears when the user right clicks on an object.
48 /// This property is typically used on override-redirect windows.
49 PopupMenu,
50 /// A tooltip window. Usually used to show additional information when hovering over an object with the cursor.
51 /// This property is typically used on override-redirect windows.
52 Tooltip,
53 /// The window is a notification.
54 /// This property is typically used on override-redirect windows.
55 Notification,
56 /// This should be used on the windows that are popped up by combo boxes.
57 /// This property is typically used on override-redirect windows.
58 Combo,
59 /// This indicates the the window is being dragged.
60 /// This property is typically used on override-redirect windows.
61 Dnd,
62 /// This is a normal, top-level window.
63 #[default]
64 Normal,
65}
66
67impl WindowType {
68 pub(crate) fn as_atom(&self, xconn: &Arc<XConnection>) -> xproto::Atom {
69 use self::WindowType::*;
70 let atom_name: AtomName = match *self {
71 Desktop => _NET_WM_WINDOW_TYPE_DESKTOP,
72 Dock => _NET_WM_WINDOW_TYPE_DOCK,
73 Toolbar => _NET_WM_WINDOW_TYPE_TOOLBAR,
74 Menu => _NET_WM_WINDOW_TYPE_MENU,
75 Utility => _NET_WM_WINDOW_TYPE_UTILITY,
76 Splash => _NET_WM_WINDOW_TYPE_SPLASH,
77 Dialog => _NET_WM_WINDOW_TYPE_DIALOG,
78 DropdownMenu => _NET_WM_WINDOW_TYPE_DROPDOWN_MENU,
79 PopupMenu => _NET_WM_WINDOW_TYPE_POPUP_MENU,
80 Tooltip => _NET_WM_WINDOW_TYPE_TOOLTIP,
81 Notification => _NET_WM_WINDOW_TYPE_NOTIFICATION,
82 Combo => _NET_WM_WINDOW_TYPE_COMBO,
83 Dnd => _NET_WM_WINDOW_TYPE_DND,
84 Normal => _NET_WM_WINDOW_TYPE_NORMAL,
85 };
86
87 let atoms: &Atoms = xconn.atoms();
88 atoms[atom_name]
89 }
90}
91
92pub struct MotifHints {
93 hints: MwmHints,
94}
95
96struct MwmHints {
97 flags: u32,
98 functions: u32,
99 decorations: u32,
100 input_mode: u32,
101 status: u32,
102}
103
104#[allow(dead_code)]
105mod mwm {
106 // Motif WM hints are obsolete, but still widely supported.
107 // https://stackoverflow.com/a/1909708
108 pub const MWM_HINTS_FUNCTIONS: u32 = 1 << 0;
109 pub const MWM_HINTS_DECORATIONS: u32 = 1 << 1;
110
111 pub const MWM_FUNC_ALL: u32 = 1 << 0;
112 pub const MWM_FUNC_RESIZE: u32 = 1 << 1;
113 pub const MWM_FUNC_MOVE: u32 = 1 << 2;
114 pub const MWM_FUNC_MINIMIZE: u32 = 1 << 3;
115 pub const MWM_FUNC_MAXIMIZE: u32 = 1 << 4;
116 pub const MWM_FUNC_CLOSE: u32 = 1 << 5;
117}
118
119impl MotifHints {
120 pub fn new() -> MotifHints {
121 MotifHints {
122 hints: MwmHints {
123 flags: 0,
124 functions: 0,
125 decorations: 0,
126 input_mode: 0,
127 status: 0,
128 },
129 }
130 }
131
132 pub fn set_decorations(&mut self, decorations: bool) {
133 self.hints.flags |= mwm::MWM_HINTS_DECORATIONS;
134 self.hints.decorations = decorations as u32;
135 }
136
137 pub fn set_maximizable(&mut self, maximizable: bool) {
138 if maximizable {
139 self.add_func(mwm::MWM_FUNC_MAXIMIZE);
140 } else {
141 self.remove_func(mwm::MWM_FUNC_MAXIMIZE);
142 }
143 }
144
145 fn add_func(&mut self, func: u32) {
146 if self.hints.flags & mwm::MWM_HINTS_FUNCTIONS != 0 {
147 if self.hints.functions & mwm::MWM_FUNC_ALL != 0 {
148 self.hints.functions &= !func;
149 } else {
150 self.hints.functions |= func;
151 }
152 }
153 }
154
155 fn remove_func(&mut self, func: u32) {
156 if self.hints.flags & mwm::MWM_HINTS_FUNCTIONS == 0 {
157 self.hints.flags |= mwm::MWM_HINTS_FUNCTIONS;
158 self.hints.functions = mwm::MWM_FUNC_ALL;
159 }
160
161 if self.hints.functions & mwm::MWM_FUNC_ALL != 0 {
162 self.hints.functions |= func;
163 } else {
164 self.hints.functions &= !func;
165 }
166 }
167}
168
169impl Default for MotifHints {
170 fn default() -> Self {
171 Self::new()
172 }
173}
174
175impl XConnection {
176 pub fn get_motif_hints(&self, window: xproto::Window) -> MotifHints {
177 let atoms = self.atoms();
178 let motif_hints = atoms[_MOTIF_WM_HINTS];
179
180 let mut hints = MotifHints::new();
181
182 if let Ok(props) = self.get_property::<u32>(window, motif_hints, motif_hints) {
183 hints.hints.flags = props.first().cloned().unwrap_or(0);
184 hints.hints.functions = props.get(1).cloned().unwrap_or(0);
185 hints.hints.decorations = props.get(2).cloned().unwrap_or(0);
186 hints.hints.input_mode = props.get(3).cloned().unwrap_or(0);
187 hints.hints.status = props.get(4).cloned().unwrap_or(0);
188 }
189
190 hints
191 }
192
193 #[allow(clippy::unnecessary_cast)]
194 pub fn set_motif_hints(
195 &self,
196 window: xproto::Window,
197 hints: &MotifHints,
198 ) -> Result<VoidCookie<'_>, X11Error> {
199 let atoms = self.atoms();
200 let motif_hints = atoms[_MOTIF_WM_HINTS];
201
202 let hints_data: [u32; 5] = [
203 hints.hints.flags as u32,
204 hints.hints.functions as u32,
205 hints.hints.decorations as u32,
206 hints.hints.input_mode as u32,
207 hints.hints.status as u32,
208 ];
209
210 self.change_property(
211 window,
212 motif_hints,
213 motif_hints,
214 xproto::PropMode::REPLACE,
215 &hints_data,
216 )
217 }
218}
219