winit/platform_impl/linux/x11/util/
hint.rs1use std::slice;
2use std::sync::Arc;
3
4use super::*;
5
6#[derive(Debug)]
7#[allow(dead_code)]
8pub enum StateOperation {
9 Remove = 0, Add = 1, Toggle = 2, }
13
14impl From<bool> for StateOperation {
15 fn from(op: bool) -> Self {
16 if op {
17 StateOperation::Add
18 } else {
19 StateOperation::Remove
20 }
21 }
22}
23
24#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
27#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
28pub enum WindowType {
29 Desktop,
33 Dock,
35 Toolbar,
37 Menu,
39 Utility,
41 Splash,
43 Dialog,
45 DropdownMenu,
48 PopupMenu,
51 Tooltip,
54 Notification,
57 Combo,
60 Dnd,
63 Normal,
65}
66
67impl Default for WindowType {
68 fn default() -> Self {
69 WindowType::Normal
70 }
71}
72
73impl WindowType {
74 pub(crate) fn as_atom(&self, xconn: &Arc<XConnection>) -> ffi::Atom {
75 use self::WindowType::*;
76 let atom_name: &[u8] = match *self {
77 Desktop => b"_NET_WM_WINDOW_TYPE_DESKTOP\0",
78 Dock => b"_NET_WM_WINDOW_TYPE_DOCK\0",
79 Toolbar => b"_NET_WM_WINDOW_TYPE_TOOLBAR\0",
80 Menu => b"_NET_WM_WINDOW_TYPE_MENU\0",
81 Utility => b"_NET_WM_WINDOW_TYPE_UTILITY\0",
82 Splash => b"_NET_WM_WINDOW_TYPE_SPLASH\0",
83 Dialog => b"_NET_WM_WINDOW_TYPE_DIALOG\0",
84 DropdownMenu => b"_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0",
85 PopupMenu => b"_NET_WM_WINDOW_TYPE_POPUP_MENU\0",
86 Tooltip => b"_NET_WM_WINDOW_TYPE_TOOLTIP\0",
87 Notification => b"_NET_WM_WINDOW_TYPE_NOTIFICATION\0",
88 Combo => b"_NET_WM_WINDOW_TYPE_COMBO\0",
89 Dnd => b"_NET_WM_WINDOW_TYPE_DND\0",
90 Normal => b"_NET_WM_WINDOW_TYPE_NORMAL\0",
91 };
92 unsafe { xconn.get_atom_unchecked(atom_name) }
93 }
94}
95
96pub struct MotifHints {
97 hints: MwmHints,
98}
99
100#[repr(C)]
101struct MwmHints {
102 flags: c_ulong,
103 functions: c_ulong,
104 decorations: c_ulong,
105 input_mode: c_long,
106 status: c_ulong,
107}
108
109#[allow(dead_code)]
110mod mwm {
111 use libc::c_ulong;
112
113 pub const MWM_HINTS_FUNCTIONS: c_ulong = 1 << 0;
116 pub const MWM_HINTS_DECORATIONS: c_ulong = 1 << 1;
117
118 pub const MWM_FUNC_ALL: c_ulong = 1 << 0;
119 pub const MWM_FUNC_RESIZE: c_ulong = 1 << 1;
120 pub const MWM_FUNC_MOVE: c_ulong = 1 << 2;
121 pub const MWM_FUNC_MINIMIZE: c_ulong = 1 << 3;
122 pub const MWM_FUNC_MAXIMIZE: c_ulong = 1 << 4;
123 pub const MWM_FUNC_CLOSE: c_ulong = 1 << 5;
124}
125
126impl MotifHints {
127 pub fn new() -> MotifHints {
128 MotifHints {
129 hints: MwmHints {
130 flags: 0,
131 functions: 0,
132 decorations: 0,
133 input_mode: 0,
134 status: 0,
135 },
136 }
137 }
138
139 pub fn set_decorations(&mut self, decorations: bool) {
140 self.hints.flags |= mwm::MWM_HINTS_DECORATIONS;
141 self.hints.decorations = decorations as c_ulong;
142 }
143
144 pub fn set_maximizable(&mut self, maximizable: bool) {
145 if maximizable {
146 self.add_func(mwm::MWM_FUNC_MAXIMIZE);
147 } else {
148 self.remove_func(mwm::MWM_FUNC_MAXIMIZE);
149 }
150 }
151
152 fn add_func(&mut self, func: c_ulong) {
153 if self.hints.flags & mwm::MWM_HINTS_FUNCTIONS != 0 {
154 if self.hints.functions & mwm::MWM_FUNC_ALL != 0 {
155 self.hints.functions &= !func;
156 } else {
157 self.hints.functions |= func;
158 }
159 }
160 }
161
162 fn remove_func(&mut self, func: c_ulong) {
163 if self.hints.flags & mwm::MWM_HINTS_FUNCTIONS == 0 {
164 self.hints.flags |= mwm::MWM_HINTS_FUNCTIONS;
165 self.hints.functions = mwm::MWM_FUNC_ALL;
166 }
167
168 if self.hints.functions & mwm::MWM_FUNC_ALL != 0 {
169 self.hints.functions |= func;
170 } else {
171 self.hints.functions &= !func;
172 }
173 }
174}
175
176impl MwmHints {
177 fn as_slice(&self) -> &[c_ulong] {
178 unsafe { slice::from_raw_parts(self as *const _ as *const c_ulong, 5) }
179 }
180}
181
182pub struct NormalHints<'a> {
183 size_hints: XSmartPointer<'a, ffi::XSizeHints>,
184}
185
186impl<'a> NormalHints<'a> {
187 pub fn new(xconn: &'a XConnection) -> Self {
188 NormalHints {
189 size_hints: xconn.alloc_size_hints(),
190 }
191 }
192
193 pub fn get_position(&self) -> Option<(i32, i32)> {
194 if has_flag(self.size_hints.flags, ffi::PPosition) {
195 Some((self.size_hints.x as i32, self.size_hints.y as i32))
196 } else {
197 None
198 }
199 }
200
201 pub fn set_position(&mut self, position: Option<(i32, i32)>) {
202 if let Some((x, y)) = position {
203 self.size_hints.flags |= ffi::PPosition;
204 self.size_hints.x = x as c_int;
205 self.size_hints.y = y as c_int;
206 } else {
207 self.size_hints.flags &= !ffi::PPosition;
208 }
209 }
210
211 pub fn set_size(&mut self, size: Option<(u32, u32)>) {
213 if let Some((width, height)) = size {
214 self.size_hints.flags |= ffi::PSize;
215 self.size_hints.width = width as c_int;
216 self.size_hints.height = height as c_int;
217 } else {
218 self.size_hints.flags &= !ffi::PSize;
219 }
220 }
221
222 pub fn set_max_size(&mut self, max_size: Option<(u32, u32)>) {
223 if let Some((max_width, max_height)) = max_size {
224 self.size_hints.flags |= ffi::PMaxSize;
225 self.size_hints.max_width = max_width as c_int;
226 self.size_hints.max_height = max_height as c_int;
227 } else {
228 self.size_hints.flags &= !ffi::PMaxSize;
229 }
230 }
231
232 pub fn set_min_size(&mut self, min_size: Option<(u32, u32)>) {
233 if let Some((min_width, min_height)) = min_size {
234 self.size_hints.flags |= ffi::PMinSize;
235 self.size_hints.min_width = min_width as c_int;
236 self.size_hints.min_height = min_height as c_int;
237 } else {
238 self.size_hints.flags &= !ffi::PMinSize;
239 }
240 }
241
242 pub fn set_resize_increments(&mut self, resize_increments: Option<(u32, u32)>) {
243 if let Some((width_inc, height_inc)) = resize_increments {
244 self.size_hints.flags |= ffi::PResizeInc;
245 self.size_hints.width_inc = width_inc as c_int;
246 self.size_hints.height_inc = height_inc as c_int;
247 } else {
248 self.size_hints.flags &= !ffi::PResizeInc;
249 }
250 }
251
252 pub fn set_base_size(&mut self, base_size: Option<(u32, u32)>) {
253 if let Some((base_width, base_height)) = base_size {
254 self.size_hints.flags |= ffi::PBaseSize;
255 self.size_hints.base_width = base_width as c_int;
256 self.size_hints.base_height = base_height as c_int;
257 } else {
258 self.size_hints.flags &= !ffi::PBaseSize;
259 }
260 }
261}
262
263impl XConnection {
264 pub fn get_wm_hints(
265 &self,
266 window: ffi::Window,
267 ) -> Result<XSmartPointer<'_, ffi::XWMHints>, XError> {
268 let wm_hints = unsafe { (self.xlib.XGetWMHints)(self.display, window) };
269 self.check_errors()?;
270 let wm_hints = if wm_hints.is_null() {
271 self.alloc_wm_hints()
272 } else {
273 XSmartPointer::new(self, wm_hints).unwrap()
274 };
275 Ok(wm_hints)
276 }
277
278 pub fn set_wm_hints(
279 &self,
280 window: ffi::Window,
281 wm_hints: XSmartPointer<'_, ffi::XWMHints>,
282 ) -> Flusher<'_> {
283 unsafe {
284 (self.xlib.XSetWMHints)(self.display, window, wm_hints.ptr);
285 }
286 Flusher::new(self)
287 }
288
289 pub fn get_normal_hints(&self, window: ffi::Window) -> Result<NormalHints<'_>, XError> {
290 let size_hints = self.alloc_size_hints();
291 let mut supplied_by_user = MaybeUninit::uninit();
292 unsafe {
293 (self.xlib.XGetWMNormalHints)(
294 self.display,
295 window,
296 size_hints.ptr,
297 supplied_by_user.as_mut_ptr(),
298 );
299 }
300 self.check_errors().map(|_| NormalHints { size_hints })
301 }
302
303 pub fn set_normal_hints(
304 &self,
305 window: ffi::Window,
306 normal_hints: NormalHints<'_>,
307 ) -> Flusher<'_> {
308 unsafe {
309 (self.xlib.XSetWMNormalHints)(self.display, window, normal_hints.size_hints.ptr);
310 }
311 Flusher::new(self)
312 }
313
314 pub fn get_motif_hints(&self, window: ffi::Window) -> MotifHints {
315 let motif_hints = unsafe { self.get_atom_unchecked(b"_MOTIF_WM_HINTS\0") };
316
317 let mut hints = MotifHints::new();
318
319 if let Ok(props) = self.get_property::<c_ulong>(window, motif_hints, motif_hints) {
320 hints.hints.flags = props.get(0).cloned().unwrap_or(0);
321 hints.hints.functions = props.get(1).cloned().unwrap_or(0);
322 hints.hints.decorations = props.get(2).cloned().unwrap_or(0);
323 hints.hints.input_mode = props.get(3).cloned().unwrap_or(0) as c_long;
324 hints.hints.status = props.get(4).cloned().unwrap_or(0);
325 }
326
327 hints
328 }
329
330 pub fn set_motif_hints(&self, window: ffi::Window, hints: &MotifHints) -> Flusher<'_> {
331 let motif_hints = unsafe { self.get_atom_unchecked(b"_MOTIF_WM_HINTS\0") };
332
333 self.change_property(
334 window,
335 motif_hints,
336 motif_hints,
337 PropMode::Replace,
338 hints.hints.as_slice(),
339 )
340 }
341}