mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-24 22:31:30 +11:00
x11: Set window title prior to mapping window (#362)
Fixes #282 Some tiling window managers (i3, dwm, etc.) determine how a window should behave based on its name. If the name is set after mapping, then window managers will check the name before we set it, followed by them detecting it as a change when the name is actually set. That results in the window briefly behaving in an unexpected way, followed by a rapid switch to the expected behavior. In accordance to section 4.1.2 of ICCCM, the name, decorations, size hints, and window deletion redirection have all been moved up to be set before mapping.
This commit is contained in:
parent
d18db208ff
commit
9698d0a8d8
|
@ -125,103 +125,6 @@ impl Window2 {
|
||||||
win
|
win
|
||||||
};
|
};
|
||||||
|
|
||||||
// Enable drag and drop
|
|
||||||
unsafe {
|
|
||||||
let atom_name: *const libc::c_char = b"XdndAware\0".as_ptr() as _;
|
|
||||||
let atom = (display.xlib.XInternAtom)(display.display, atom_name, ffi::False);
|
|
||||||
let version = &5; // Latest version; hasn't changed since 2002
|
|
||||||
(display.xlib.XChangeProperty)(
|
|
||||||
display.display,
|
|
||||||
window,
|
|
||||||
atom,
|
|
||||||
ffi::XA_ATOM,
|
|
||||||
32,
|
|
||||||
ffi::PropModeReplace,
|
|
||||||
version,
|
|
||||||
1
|
|
||||||
);
|
|
||||||
display.check_errors().expect("Failed to set drag and drop properties");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set ICCCM WM_CLASS property based on initial window title
|
|
||||||
// Must be done *before* mapping the window by ICCCM 4.1.2.5
|
|
||||||
unsafe {
|
|
||||||
with_c_str(&*window_attrs.title, |c_name| {
|
|
||||||
let hint = (display.xlib.XAllocClassHint)();
|
|
||||||
(*hint).res_name = c_name as *mut libc::c_char;
|
|
||||||
(*hint).res_class = c_name as *mut libc::c_char;
|
|
||||||
(display.xlib.XSetClassHint)(display.display, window, hint);
|
|
||||||
display.check_errors().expect("Failed to call XSetClassHint");
|
|
||||||
(display.xlib.XFree)(hint as *mut _);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// set visibility
|
|
||||||
if window_attrs.visible {
|
|
||||||
unsafe {
|
|
||||||
(display.xlib.XMapRaised)(display.display, window);
|
|
||||||
(display.xlib.XFlush)(display.display);
|
|
||||||
}
|
|
||||||
|
|
||||||
display.check_errors().expect("Failed to set window visibility");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Opt into handling window close
|
|
||||||
unsafe {
|
|
||||||
(display.xlib.XSetWMProtocols)(display.display, window, &ctx.wm_delete_window as *const _ as *mut _, 1);
|
|
||||||
display.check_errors().expect("Failed to call XSetWMProtocols");
|
|
||||||
(display.xlib.XFlush)(display.display);
|
|
||||||
display.check_errors().expect("Failed to call XFlush");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to make keyboard input repeat detectable
|
|
||||||
unsafe {
|
|
||||||
let mut supported_ptr = ffi::False;
|
|
||||||
(display.xlib.XkbSetDetectableAutoRepeat)(display.display, ffi::True, &mut supported_ptr);
|
|
||||||
if supported_ptr == ffi::False {
|
|
||||||
return Err(OsError(format!("XkbSetDetectableAutoRepeat failed")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set size hints
|
|
||||||
let mut size_hints: ffi::XSizeHints = unsafe { mem::zeroed() };
|
|
||||||
size_hints.flags = ffi::PSize;
|
|
||||||
size_hints.width = dimensions.0 as i32;
|
|
||||||
size_hints.height = dimensions.1 as i32;
|
|
||||||
if let Some(dimensions) = window_attrs.min_dimensions {
|
|
||||||
size_hints.flags |= ffi::PMinSize;
|
|
||||||
size_hints.min_width = dimensions.0 as i32;
|
|
||||||
size_hints.min_height = dimensions.1 as i32;
|
|
||||||
}
|
|
||||||
if let Some(dimensions) = window_attrs.max_dimensions {
|
|
||||||
size_hints.flags |= ffi::PMaxSize;
|
|
||||||
size_hints.max_width = dimensions.0 as i32;
|
|
||||||
size_hints.max_height = dimensions.1 as i32;
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
(display.xlib.XSetNormalHints)(display.display, window, &mut size_hints);
|
|
||||||
display.check_errors().expect("Failed to call XSetNormalHints");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select XInput2 events
|
|
||||||
{
|
|
||||||
let mask = ffi::XI_MotionMask
|
|
||||||
| ffi::XI_ButtonPressMask | ffi::XI_ButtonReleaseMask
|
|
||||||
// | ffi::XI_KeyPressMask | ffi::XI_KeyReleaseMask
|
|
||||||
| ffi::XI_EnterMask | ffi::XI_LeaveMask
|
|
||||||
| ffi::XI_FocusInMask | ffi::XI_FocusOutMask
|
|
||||||
| if window_attrs.multitouch { ffi::XI_TouchBeginMask | ffi::XI_TouchUpdateMask | ffi::XI_TouchEndMask } else { 0 };
|
|
||||||
unsafe {
|
|
||||||
let mut event_mask = ffi::XIEventMask{
|
|
||||||
deviceid: ffi::XIAllMasterDevices,
|
|
||||||
mask: mem::transmute::<*const i32, *mut c_uchar>(&mask as *const i32),
|
|
||||||
mask_len: mem::size_of_val(&mask) as c_int,
|
|
||||||
};
|
|
||||||
(display.xinput2.XISelectEvents)(display.display, window,
|
|
||||||
&mut event_mask as *mut ffi::XIEventMask, 1);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let window = Window2 {
|
let window = Window2 {
|
||||||
x: Arc::new(XWindow {
|
x: Arc::new(XWindow {
|
||||||
display: display.clone(),
|
display: display.clone(),
|
||||||
|
@ -232,35 +135,138 @@ impl Window2 {
|
||||||
cursor_state: Mutex::new(CursorState::Normal),
|
cursor_state: Mutex::new(CursorState::Normal),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Title must be set before mapping, lest some tiling window managers briefly pick up on
|
||||||
|
// the initial un-titled window state
|
||||||
window.set_title(&window_attrs.title);
|
window.set_title(&window_attrs.title);
|
||||||
window.set_decorations(window_attrs.decorations);
|
window.set_decorations(window_attrs.decorations);
|
||||||
window.set_maximized(window_attrs.maximized);
|
|
||||||
window.set_fullscreen(window_attrs.fullscreen.clone());
|
|
||||||
|
|
||||||
if window_attrs.visible {
|
{
|
||||||
|
let ref x_window: &XWindow = window.x.borrow();
|
||||||
|
|
||||||
|
// Enable drag and drop
|
||||||
unsafe {
|
unsafe {
|
||||||
let ref x_window: &XWindow = window.x.borrow();
|
let atom_name: *const libc::c_char = b"XdndAware\0".as_ptr() as _;
|
||||||
|
let atom = (display.xlib.XInternAtom)(display.display, atom_name, ffi::False);
|
||||||
|
let version = &5; // Latest version; hasn't changed since 2002
|
||||||
|
(display.xlib.XChangeProperty)(
|
||||||
|
display.display,
|
||||||
|
x_window.window,
|
||||||
|
atom,
|
||||||
|
ffi::XA_ATOM,
|
||||||
|
32,
|
||||||
|
ffi::PropModeReplace,
|
||||||
|
version,
|
||||||
|
1
|
||||||
|
);
|
||||||
|
display.check_errors().expect("Failed to set drag and drop properties");
|
||||||
|
}
|
||||||
|
|
||||||
// XSetInputFocus generates an error if the window is not visible,
|
// Set ICCCM WM_CLASS property based on initial window title
|
||||||
// therefore we wait until it's the case.
|
// Must be done *before* mapping the window by ICCCM 4.1.2.5
|
||||||
loop {
|
unsafe {
|
||||||
let mut window_attributes = mem::uninitialized();
|
with_c_str(&*window_attrs.title, |c_name| {
|
||||||
(display.xlib.XGetWindowAttributes)(display.display, x_window.window, &mut window_attributes);
|
let hint = (display.xlib.XAllocClassHint)();
|
||||||
display.check_errors().expect("Failed to call XGetWindowAttributes");
|
(*hint).res_name = c_name as *mut libc::c_char;
|
||||||
|
(*hint).res_class = c_name as *mut libc::c_char;
|
||||||
|
(display.xlib.XSetClassHint)(display.display, x_window.window, hint);
|
||||||
|
display.check_errors().expect("Failed to call XSetClassHint");
|
||||||
|
(display.xlib.XFree)(hint as *mut _);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if window_attributes.map_state == ffi::IsViewable {
|
// set size hints
|
||||||
(display.xlib.XSetInputFocus)(
|
let mut size_hints: ffi::XSizeHints = unsafe { mem::zeroed() };
|
||||||
display.display,
|
size_hints.flags = ffi::PSize;
|
||||||
x_window.window,
|
size_hints.width = dimensions.0 as i32;
|
||||||
ffi::RevertToParent,
|
size_hints.height = dimensions.1 as i32;
|
||||||
ffi::CurrentTime
|
if let Some(dimensions) = window_attrs.min_dimensions {
|
||||||
);
|
size_hints.flags |= ffi::PMinSize;
|
||||||
display.check_errors().expect("Failed to call XSetInputFocus");
|
size_hints.min_width = dimensions.0 as i32;
|
||||||
break;
|
size_hints.min_height = dimensions.1 as i32;
|
||||||
|
}
|
||||||
|
if let Some(dimensions) = window_attrs.max_dimensions {
|
||||||
|
size_hints.flags |= ffi::PMaxSize;
|
||||||
|
size_hints.max_width = dimensions.0 as i32;
|
||||||
|
size_hints.max_height = dimensions.1 as i32;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
(display.xlib.XSetNormalHints)(display.display, x_window.window, &mut size_hints);
|
||||||
|
display.check_errors().expect("Failed to call XSetNormalHints");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opt into handling window close
|
||||||
|
unsafe {
|
||||||
|
(display.xlib.XSetWMProtocols)(display.display, x_window.window, &ctx.wm_delete_window as *const _ as *mut _, 1);
|
||||||
|
display.check_errors().expect("Failed to call XSetWMProtocols");
|
||||||
|
(display.xlib.XFlush)(display.display);
|
||||||
|
display.check_errors().expect("Failed to call XFlush");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set visibility (map window)
|
||||||
|
if window_attrs.visible {
|
||||||
|
unsafe {
|
||||||
|
(display.xlib.XMapRaised)(display.display, x_window.window);
|
||||||
|
(display.xlib.XFlush)(display.display);
|
||||||
|
}
|
||||||
|
|
||||||
|
display.check_errors().expect("Failed to set window visibility");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to make keyboard input repeat detectable
|
||||||
|
unsafe {
|
||||||
|
let mut supported_ptr = ffi::False;
|
||||||
|
(display.xlib.XkbSetDetectableAutoRepeat)(display.display, ffi::True, &mut supported_ptr);
|
||||||
|
if supported_ptr == ffi::False {
|
||||||
|
return Err(OsError(format!("XkbSetDetectableAutoRepeat failed")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select XInput2 events
|
||||||
|
{
|
||||||
|
let mask = ffi::XI_MotionMask
|
||||||
|
| ffi::XI_ButtonPressMask | ffi::XI_ButtonReleaseMask
|
||||||
|
// | ffi::XI_KeyPressMask | ffi::XI_KeyReleaseMask
|
||||||
|
| ffi::XI_EnterMask | ffi::XI_LeaveMask
|
||||||
|
| ffi::XI_FocusInMask | ffi::XI_FocusOutMask
|
||||||
|
| if window_attrs.multitouch { ffi::XI_TouchBeginMask | ffi::XI_TouchUpdateMask | ffi::XI_TouchEndMask } else { 0 };
|
||||||
|
unsafe {
|
||||||
|
let mut event_mask = ffi::XIEventMask{
|
||||||
|
deviceid: ffi::XIAllMasterDevices,
|
||||||
|
mask: mem::transmute::<*const i32, *mut c_uchar>(&mask as *const i32),
|
||||||
|
mask_len: mem::size_of_val(&mask) as c_int,
|
||||||
|
};
|
||||||
|
(display.xinput2.XISelectEvents)(display.display, x_window.window,
|
||||||
|
&mut event_mask as *mut ffi::XIEventMask, 1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// These properties must be set after mapping
|
||||||
|
window.set_maximized(window_attrs.maximized);
|
||||||
|
window.set_fullscreen(window_attrs.fullscreen.clone());
|
||||||
|
|
||||||
|
if window_attrs.visible {
|
||||||
|
unsafe {
|
||||||
|
// XSetInputFocus generates an error if the window is not visible,
|
||||||
|
// therefore we wait until it's the case.
|
||||||
|
loop {
|
||||||
|
let mut window_attributes = mem::uninitialized();
|
||||||
|
(display.xlib.XGetWindowAttributes)(display.display, x_window.window, &mut window_attributes);
|
||||||
|
display.check_errors().expect("Failed to call XGetWindowAttributes");
|
||||||
|
|
||||||
|
if window_attributes.map_state == ffi::IsViewable {
|
||||||
|
(display.xlib.XSetInputFocus)(
|
||||||
|
display.display,
|
||||||
|
x_window.window,
|
||||||
|
ffi::RevertToParent,
|
||||||
|
ffi::CurrentTime
|
||||||
|
);
|
||||||
|
display.check_errors().expect("Failed to call XSetInputFocus");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait about a frame to avoid too-busy waiting
|
||||||
|
thread::sleep(Duration::from_millis(16));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait about a frame to avoid too-busy waiting
|
|
||||||
thread::sleep(Duration::from_millis(16));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue