use crate::XlibWindowHandle;
use super::{
event_translate_client_message, event_translate_property_notify, xwrap::WITHDRAWN_STATE,
DisplayEvent, XWrap,
};
use leftwm_core::{
models::{Mode, WindowChange, WindowHandle, WindowType, XyhwChange},
utils::modmask_lookup::{Button, ModMask},
};
use std::os::raw::c_ulong;
use x11_dl::xlib;
pub struct XEvent<'a>(pub &'a mut XWrap, pub xlib::XEvent);
impl From<XEvent<'_>> for Option<DisplayEvent<XlibWindowHandle>> {
fn from(x_event: XEvent) -> Self {
let raw_event = x_event.1;
let normal_mode = x_event.0.mode == Mode::Normal;
let sloppy_behaviour = x_event.0.focus_behaviour.is_sloppy();
match raw_event.get_type() {
xlib::MapRequest => from_map_request(x_event),
xlib::UnmapNotify => from_unmap_event(x_event),
xlib::DestroyNotify => from_destroy_notify(x_event),
xlib::FocusIn => from_focus_in(x_event),
xlib::ClientMessage if normal_mode => from_client_message(&x_event),
xlib::PropertyNotify if normal_mode => from_property_notify(&x_event),
xlib::ConfigureRequest if normal_mode => from_configure_request(x_event),
xlib::EnterNotify if normal_mode && sloppy_behaviour => from_enter_notify(&x_event),
xlib::MotionNotify => from_motion_notify(x_event),
xlib::ButtonPress => Some(from_button_press(raw_event)),
xlib::ButtonRelease if !normal_mode => Some(from_button_release(x_event)),
_other => None,
}
}
}
fn from_map_request(x_event: XEvent) -> Option<DisplayEvent<XlibWindowHandle>> {
let xw = x_event.0;
let event = xlib::XMapRequestEvent::from(x_event.1);
xw.setup_window(event.window)
}
fn from_unmap_event(x_event: XEvent) -> Option<DisplayEvent<XlibWindowHandle>> {
let xw = x_event.0;
let event = xlib::XUnmapEvent::from(x_event.1);
if xw.managed_windows.contains(&event.window) {
if event.send_event == xlib::False {
let h = WindowHandle(XlibWindowHandle(event.window));
xw.teardown_managed_window(&h, false);
return Some(DisplayEvent::WindowDestroy(h));
}
xw.set_wm_states(event.window, &[WITHDRAWN_STATE]);
}
None
}
fn from_destroy_notify(x_event: XEvent) -> Option<DisplayEvent<XlibWindowHandle>> {
let xw = x_event.0;
let event = xlib::XDestroyWindowEvent::from(x_event.1);
if xw.managed_windows.contains(&event.window) {
let h = WindowHandle(XlibWindowHandle(event.window));
xw.teardown_managed_window(&h, true);
return Some(DisplayEvent::WindowDestroy(h));
}
None
}
#[allow(clippy::unnecessary_wraps)]
fn from_focus_in(x_event: XEvent) -> Option<DisplayEvent<XlibWindowHandle>> {
let xw = x_event.0;
let event = xlib::XFocusChangeEvent::from(x_event.1);
if xw.focused_window != event.window {
let never_focus = match xw.get_wmhints(xw.focused_window) {
Some(hint) => hint.flags & xlib::InputHint != 0 && hint.input == 0,
None => false,
};
xw.focus(xw.focused_window, never_focus);
}
let handle = WindowHandle(XlibWindowHandle(event.window));
let mut change = WindowChange::new(handle);
change.urgent = Some(false);
Some(DisplayEvent::WindowChange(change))
}
fn from_client_message(x_event: &XEvent) -> Option<DisplayEvent<XlibWindowHandle>> {
let event = xlib::XClientMessageEvent::from(x_event.1);
event_translate_client_message::from_event(x_event.0, event)
}
fn from_property_notify(x_event: &XEvent) -> Option<DisplayEvent<XlibWindowHandle>> {
let event = xlib::XPropertyEvent::from(x_event.1);
event_translate_property_notify::from_event(x_event.0, event)
}
fn from_configure_request(x_event: XEvent) -> Option<DisplayEvent<XlibWindowHandle>> {
let xw = x_event.0;
let event = xlib::XConfigureRequestEvent::from(x_event.1);
if !xw.managed_windows.contains(&event.window) {
let window_changes = xlib::XWindowChanges {
x: event.x,
y: event.y,
width: event.width,
height: event.height,
border_width: event.border_width,
sibling: event.above,
stack_mode: event.detail,
};
let unlock = xlib::CWX
| xlib::CWY
| xlib::CWWidth
| xlib::CWHeight
| xlib::CWBorderWidth
| xlib::CWSibling
| xlib::CWStackMode;
xw.set_window_config(event.window, window_changes, u32::from(unlock));
xw.move_resize_window(
event.window,
event.x,
event.y,
event.width as u32,
event.height as u32,
);
return None;
}
let window_type = xw.get_window_type(event.window);
let trans = xw.get_transient_for(event.window);
let handle = WindowHandle(XlibWindowHandle(event.window));
if window_type == WindowType::Normal && trans.is_none() {
return Some(DisplayEvent::ConfigureXlibWindow(handle));
}
let mut change = WindowChange::new(handle);
let xyhw = match window_type {
WindowType::Dialog | WindowType::Normal => XyhwChange {
w: Some(event.width),
h: Some(event.height),
..XyhwChange::default()
},
_ => XyhwChange {
w: Some(event.width),
h: Some(event.height),
x: Some(event.x),
y: Some(event.y),
..XyhwChange::default()
},
};
change.floating = Some(xyhw);
Some(DisplayEvent::WindowChange(change))
}
fn from_enter_notify(x_event: &XEvent) -> Option<DisplayEvent<XlibWindowHandle>> {
let event = xlib::XCrossingEvent::from(x_event.1);
if event.mode != xlib::NotifyNormal
|| event.detail == xlib::NotifyInferior
|| event.window == x_event.0.get_default_root()
{
return None;
}
let h = WindowHandle(XlibWindowHandle(event.window));
Some(DisplayEvent::WindowTakeFocus(h))
}
fn from_motion_notify(x_event: XEvent) -> Option<DisplayEvent<XlibWindowHandle>> {
let xw = x_event.0;
let event = xlib::XMotionEvent::from(x_event.1);
if xw.refresh_rate as c_ulong > 0
&& event.time - xw.motion_event_limiter > (1000 / xw.refresh_rate as c_ulong)
{
xw.motion_event_limiter = event.time;
let event_h = WindowHandle(XlibWindowHandle(event.window));
let offset_x = event.x_root - xw.mode_origin.0;
let offset_y = event.y_root - xw.mode_origin.1;
let display_event = match xw.mode {
Mode::ReadyToMove(h) => {
xw.set_mode(Mode::MovingWindow(h));
DisplayEvent::MoveWindow(h, offset_x, offset_y)
}
Mode::MovingWindow(h) => DisplayEvent::MoveWindow(h, offset_x, offset_y),
Mode::ReadyToResize(h) => {
xw.set_mode(Mode::ResizingWindow(h));
DisplayEvent::ResizeWindow(h, offset_x, offset_y)
}
Mode::ResizingWindow(h) => DisplayEvent::ResizeWindow(h, offset_x, offset_y),
Mode::Normal if xw.focus_behaviour.is_sloppy() => {
DisplayEvent::Movement(event_h, event.x_root, event.y_root)
}
Mode::Normal => return None,
};
return Some(display_event);
}
None
}
fn from_button_press(raw_event: xlib::XEvent) -> DisplayEvent<XlibWindowHandle> {
let event = xlib::XButtonPressedEvent::from(raw_event);
let h = WindowHandle(XlibWindowHandle(event.window));
let mut mod_mask = event.state;
mod_mask &= !(xlib::Mod2Mask | xlib::LockMask);
DisplayEvent::MouseCombo(
ModMask::from_bits_retain(mod_mask as u16),
Button::from(event.button as u8),
h,
event.x,
event.y,
)
}
fn from_button_release(x_event: XEvent) -> DisplayEvent<XlibWindowHandle> {
let xw = x_event.0;
xw.set_mode(Mode::Normal);
DisplayEvent::ChangeToNormalMode
}