1 use super::event_translate_client_message;
2 use super::event_translate_property_notify;
3 use super::DisplayEvent;
4 use super::XWrap;
5 use crate::models;
6 use crate::models::Mode;
7 use crate::models::Window;
8 use crate::models::WindowChange;
9 use crate::models::WindowHandle;
10 use crate::models::WindowType;
11 use crate::models::Xyhw;
12 use crate::models::XyhwChange;
13 use models::FocusBehaviour;
14 use std::os::raw::c_ulong;
15 use x11_dl::xlib;
16
17 pub struct XEvent<'a>(pub &'a mut XWrap, pub xlib::XEvent);
18
19 impl<'a> From<XEvent<'a>> for Option<DisplayEvent> {
from(x_event: XEvent) -> Self20 fn from(x_event: XEvent) -> Self {
21 let xw = x_event.0;
22 let raw_event = x_event.1;
23
24 match raw_event.get_type() {
25 // new window is created
26 xlib::MapRequest => from_map_request(raw_event, xw),
27
28 // listen for keyboard changes
29 xlib::MappingNotify => from_mapping_notify(raw_event, xw),
30
31 // window is deleted
32 xlib::UnmapNotify | xlib::DestroyNotify => Some(from_unmap_event(raw_event)),
33
34 xlib::ClientMessage => {
35 match &xw.mode {
36 Mode::MovingWindow(_) | Mode::ResizingWindow(_) => return None,
37 Mode::Normal => {}
38 };
39 let event = xlib::XClientMessageEvent::from(raw_event);
40 event_translate_client_message::from_event(xw, event)
41 }
42
43 xlib::ButtonPress => {
44 let event = xlib::XButtonPressedEvent::from(raw_event);
45 let h = WindowHandle::XlibHandle(event.window);
46 let mut mod_mask = event.state;
47 mod_mask &= !(xlib::Mod2Mask | xlib::LockMask);
48 Some(DisplayEvent::MouseCombo(mod_mask, event.button, h))
49 }
50 xlib::ButtonRelease => match xw.mode {
51 models::Mode::Normal => None,
52 _ => Some(DisplayEvent::ChangeToNormalMode),
53 },
54
55 xlib::EnterNotify => from_enter_notify(xw, raw_event),
56
57 xlib::PropertyNotify => {
58 match &xw.mode {
59 Mode::MovingWindow(_) | Mode::ResizingWindow(_) => return None,
60 Mode::Normal => {}
61 };
62 let event = xlib::XPropertyEvent::from(raw_event);
63 event_translate_property_notify::from_event(xw, event)
64 }
65
66 xlib::KeyPress => {
67 let event = xlib::XKeyEvent::from(raw_event);
68 let sym = xw.keycode_to_keysym(event.keycode);
69 Some(DisplayEvent::KeyCombo(event.state, sym))
70 }
71
72 xlib::MotionNotify => from_motion_notify(raw_event, xw),
73
74 xlib::ConfigureRequest => from_configure_request(xw, raw_event),
75
76 _other => None,
77 }
78 }
79 }
80
from_map_request(raw_event: xlib::XEvent, xw: &mut XWrap) -> Option<DisplayEvent>81 fn from_map_request(raw_event: xlib::XEvent, xw: &mut XWrap) -> Option<DisplayEvent> {
82 let event = xlib::XMapRequestEvent::from(raw_event);
83 let handle = WindowHandle::XlibHandle(event.window);
84 xw.subscribe_to_window_events(&handle);
85 // Check that the window isn't requesting to be unmanaged
86 let attrs = match xw.get_window_attrs(event.window) {
87 Ok(attr) if attr.override_redirect == 0 => attr,
88 _ => return None,
89 };
90 // Gather info about the window from xlib.
91 let name = xw.get_window_name(event.window);
92 let pid = xw.get_window_pid(event.window);
93 let r#type = xw.get_window_type(event.window);
94 let states = xw.get_window_states(event.window);
95 let actions = xw.get_window_actions_atoms(event.window);
96 let mut can_resize = actions.contains(&xw.atoms.NetWMActionResize);
97 let trans = xw.get_transient_for(event.window);
98 let sizing_hint = xw.get_hint_sizing_as_xyhw(event.window);
99 let wm_hint = xw.get_wmhints(event.window);
100
101 // Build the new window, and fill in info about it.
102 let mut w = Window::new(handle, name, pid);
103 w.r#type = r#type.clone();
104 w.set_states(states);
105 if let Some(trans) = trans {
106 w.transient = Some(WindowHandle::XlibHandle(trans));
107 }
108 // Initialise the windows floating with the pre-mapped settings.
109 let xyhw = XyhwChange {
110 x: Some(attrs.x),
111 y: Some(attrs.y),
112 w: Some(attrs.width),
113 h: Some(attrs.height),
114 ..XyhwChange::default()
115 };
116 xyhw.update_window_floating(&mut w);
117 let mut requested = Xyhw::default();
118 xyhw.update(&mut requested);
119 if let Some(mut hint) = sizing_hint {
120 // Ignore this for now for non-splashes as it causes issues, e.g. mintstick is non-resizable but is too
121 // small, issue #614: https://github.com/leftwm/leftwm/issues/614.
122 can_resize = match (r#type, hint.minw, hint.minh, hint.maxw, hint.maxh) {
123 (
124 WindowType::Splash,
125 Some(min_width),
126 Some(min_height),
127 Some(max_width),
128 Some(max_height),
129 ) => can_resize || min_width != max_width || min_height != max_height,
130 _ => true,
131 };
132 // Use the pre-mapped sizes if they are bigger.
133 hint.w = std::cmp::max(xyhw.w, hint.w);
134 hint.h = std::cmp::max(xyhw.h, hint.h);
135 hint.update_window_floating(&mut w);
136 hint.update(&mut requested);
137 }
138 w.requested = Some(requested);
139 w.can_resize = can_resize;
140 if let Some(hint) = wm_hint {
141 w.never_focus = hint.flags & xlib::InputHint != 0 && hint.input == 0;
142 }
143 // Is this needed? Made it so it doens't overwrite prior sizing.
144 if w.floating() && sizing_hint.is_none() {
145 if let Ok(geo) = xw.get_window_geometry(event.window) {
146 geo.update_window_floating(&mut w);
147 }
148 }
149
150 let cursor = xw.get_cursor_point().unwrap_or_default();
151 Some(DisplayEvent::WindowCreate(w, cursor.0, cursor.1))
152 }
153
from_mapping_notify(raw_event: xlib::XEvent, xw: &XWrap) -> Option<DisplayEvent>154 fn from_mapping_notify(raw_event: xlib::XEvent, xw: &XWrap) -> Option<DisplayEvent> {
155 let mut event = xlib::XMappingEvent::from(raw_event);
156 if event.request == xlib::MappingModifier || event.request == xlib::MappingKeyboard {
157 // refresh keyboard
158 log::debug!("Updating keyboard");
159 xw.refresh_keyboard(&mut event).ok()?;
160
161 // SoftReload keybinds
162 Some(DisplayEvent::KeyGrabReload)
163 } else {
164 None
165 }
166 }
167
from_unmap_event(raw_event: xlib::XEvent) -> DisplayEvent168 fn from_unmap_event(raw_event: xlib::XEvent) -> DisplayEvent {
169 let event = xlib::XUnmapEvent::from(raw_event);
170 let h = WindowHandle::XlibHandle(event.window);
171 DisplayEvent::WindowDestroy(h)
172 }
173
from_enter_notify(xw: &XWrap, raw_event: xlib::XEvent) -> Option<DisplayEvent>174 fn from_enter_notify(xw: &XWrap, raw_event: xlib::XEvent) -> Option<DisplayEvent> {
175 match &xw.mode {
176 Mode::MovingWindow(_) | Mode::ResizingWindow(_) => return None,
177 Mode::Normal if xw.focus_behaviour != FocusBehaviour::Sloppy => return None,
178 Mode::Normal => {}
179 };
180 let event = xlib::XEnterWindowEvent::from(raw_event);
181 let crossing = xlib::XCrossingEvent::from(raw_event);
182 if crossing.detail == xlib::NotifyInferior && crossing.window != xw.get_default_root() {
183 return None;
184 }
185 let h = WindowHandle::XlibHandle(event.window);
186 Some(DisplayEvent::MouseEnteredWindow(h))
187 }
188
from_motion_notify(raw_event: xlib::XEvent, xw: &mut XWrap) -> Option<DisplayEvent>189 fn from_motion_notify(raw_event: xlib::XEvent, xw: &mut XWrap) -> Option<DisplayEvent> {
190 let event = xlib::XMotionEvent::from(raw_event);
191 // Limit motion events to current refresh rate.
192 if event.time - xw.motion_event_limiter > (1000 / xw.refresh_rate as c_ulong) {
193 xw.motion_event_limiter = event.time;
194 let event_h = WindowHandle::XlibHandle(event.window);
195 let offset_x = event.x_root - xw.mode_origin.0;
196 let offset_y = event.y_root - xw.mode_origin.1;
197 let display_event = match &xw.mode {
198 Mode::MovingWindow(h) => DisplayEvent::MoveWindow(*h, offset_x, offset_y),
199 Mode::ResizingWindow(h) => DisplayEvent::ResizeWindow(*h, offset_x, offset_y),
200 Mode::Normal if xw.focus_behaviour == FocusBehaviour::Sloppy => {
201 DisplayEvent::Movement(event_h, event.x_root, event.y_root)
202 }
203 Mode::Normal => return None,
204 };
205 return Some(display_event);
206 }
207 None
208 }
209
from_configure_request(xw: &XWrap, raw_event: xlib::XEvent) -> Option<DisplayEvent>210 fn from_configure_request(xw: &XWrap, raw_event: xlib::XEvent) -> Option<DisplayEvent> {
211 match &xw.mode {
212 Mode::MovingWindow(_) | Mode::ResizingWindow(_) => return None,
213 Mode::Normal => {}
214 };
215 let event = xlib::XConfigureRequestEvent::from(raw_event);
216 // If the window is not mapped, configure it.
217 if !xw.managed_windows.contains(&event.window) {
218 let window_changes = xlib::XWindowChanges {
219 x: event.x,
220 y: event.y,
221 width: event.width,
222 height: event.height,
223 border_width: event.border_width,
224 sibling: event.above,
225 stack_mode: event.detail,
226 };
227 let unlock = xlib::CWX
228 | xlib::CWY
229 | xlib::CWWidth
230 | xlib::CWHeight
231 | xlib::CWBorderWidth
232 | xlib::CWSibling
233 | xlib::CWStackMode;
234 xw.set_window_config(event.window, window_changes, u32::from(unlock));
235 xw.move_resize_window(
236 event.window,
237 event.x,
238 event.y,
239 event.width as u32,
240 event.height as u32,
241 );
242 return None;
243 }
244 let window_type = xw.get_window_type(event.window);
245 let trans = xw.get_transient_for(event.window);
246 let handle = WindowHandle::XlibHandle(event.window);
247 if window_type == WindowType::Normal && trans.is_none() {
248 return Some(DisplayEvent::ConfigureXlibWindow(handle));
249 }
250 let mut change = WindowChange::new(handle);
251 let xyhw = match window_type {
252 // We want to handle the window positioning when it is a dialog or a normal window with a
253 // parent.
254 WindowType::Dialog | WindowType::Normal => XyhwChange {
255 w: Some(event.width),
256 h: Some(event.height),
257 ..XyhwChange::default()
258 },
259 _ => XyhwChange {
260 w: Some(event.width),
261 h: Some(event.height),
262 x: Some(event.x),
263 y: Some(event.y),
264 ..XyhwChange::default()
265 },
266 };
267 change.floating = Some(xyhw);
268 Some(DisplayEvent::WindowChange(change))
269 }
270