1 | use std::sync::Arc; |
2 | |
3 | use super::*; |
4 | |
5 | #[derive (Debug)] |
6 | #[allow (dead_code)] |
7 | pub 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 | |
13 | impl 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))] |
27 | pub 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 | |
67 | impl 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 | |
92 | pub struct MotifHints { |
93 | hints: MwmHints, |
94 | } |
95 | |
96 | struct MwmHints { |
97 | flags: u32, |
98 | functions: u32, |
99 | decorations: u32, |
100 | input_mode: u32, |
101 | status: u32, |
102 | } |
103 | |
104 | #[allow (dead_code)] |
105 | mod 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 | |
119 | impl 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 | |
169 | impl Default for MotifHints { |
170 | fn default() -> Self { |
171 | Self::new() |
172 | } |
173 | } |
174 | |
175 | impl 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 | |