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