winit/src/platform_impl/linux/x11/window.rs

1874 lines
68 KiB
Rust
Raw Normal View History

use std::{
cmp, env,
ffi::CString,
mem::replace,
os::raw::*,
path::Path,
2022-08-31 18:32:19 +02:00
sync::{Arc, Mutex, MutexGuard},
};
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, XlibDisplayHandle, XlibWindowHandle};
use x11rb::{
connection::Connection,
properties::{WmHints, WmHintsState, WmSizeHints, WmSizeHintsSpecification},
protocol::{
randr,
shape::SK,
xfixes::{ConnectionExt, RegionWrapper},
xinput,
xproto::{self, ConnectionExt as _, Rectangle},
},
};
use crate::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
event_loop::AsyncRequestSerial,
platform_impl::{
Linux: Implement EventLoopExtPumpEvents and EventLoopExtRunOnDemand Wayland: I found the calloop abstraction a little awkward to work with while I was trying to understand why there was surprising workaround code in the wayland backend for manually dispatching pending events. Investigating this further it looks like there may currently be several issues with the calloop WaylandSource (with how prepare_read is used and with (not) flushing writes before polling) Considering the current minimal needs for polling in all winit backends I do personally tend to think it would be simpler to just own the responsibility for polling more directly, so the logic for wayland-client `prepare_read` wouldn't be in a separate crate (and in this current situation would also be easier to fix) I've tried to maintain the status quo with calloop + workarounds. X11: I found that the recent changes (4ac2006cbc5a) to port the X11 backend from mio to calloop lost the ability to check for pending events before needing to poll/dispatch. (The `has_pending` state being queried before dispatching() was based on state that was filled in during dispatching) As part of the rebase this re-introduces the PeekableReceiver and WakeSender which are small utilities on top of `std::sync::mpsc::channel()`. This adds a calloop `PingSource` so we can use a `Ping` as a generic event loop waker. For taking into account false positive wake ups the X11 source now tracks when the file descriptor is readable so after we poll via calloop we can then specifically check if there are new X11 events or pending redraw/user events when deciding whether to skip the event loop iteration.
2023-06-18 12:40:03 +01:00
x11::{atoms::*, MonitorHandle as X11MonitorHandle, WakeSender, X11Error},
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformIcon,
2022-09-21 10:04:28 +02:00
PlatformSpecificWindowBuilderAttributes, VideoMode as PlatformVideoMode,
},
window::{
CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
WindowAttributes, WindowButtons, WindowLevel,
},
};
Move fullscreen modes to not touch physical resolutions (#270) * Fix X11 screen resolution change using XrandR The previous XF86 resolution switching was broken and everything seems to have moved on to xrandr. Use that instead while cleaning up the code a bit as well. * Use XRandR for actual multiscreen support in X11 * Use actual monitor names in X11 * Get rid of ptr::read usage in X11 * Use a bog standard Vec instead of VecDeque * Get rid of the XRandR mode switching stuff Wayland has made the decision that apps shouldn't change screen resolutions and just take the screens as they've been setup. In the modern world where GPU scaling is cheap and LCD panels are scaling anyway it makes no sense to make "physical" resolution changes when software should be taking care of it. This massively simplifies the code and makes it easier to extend to more niche setups like MST and videowalls. * Rename fullscreen options to match new semantics * Implement XRandR 1.5 support * Get rid of the FullScreen enum Moving to just having two states None and Some(MonitorId) and then being able to set full screen in the current monitor with something like: window.set_fullscreen(Some(window.current_monitor())); * Implement Window::get_current_monitor() Do it by iterating over the available monitors and finding which has the biggest overlap with the window. For this MonitorId needs a new get_position() that needs to be implemented for all platforms. * Add unimplemented get_position() to all MonitorId * Make get_current_monitor() platform specific * Add unimplemented get_current_monitor() to all * Implement proper primary monitor selection in X11 * Shut up some warnings * Remove libxxf86vm package from travis Since we're no longer using XF86 there's no need to keep the package around for CI. * Don't use new struct syntax * Fix indentation * Adjust Android/iOS fullscreen/maximized On Android and iOS we can assume single screen apps that are already fullscreen and maximized so there are a few methods that are implemented by just returning a fixed value or not doing anything. * Mark OSX/Win fullscreen/maximized unimplemented()! These would be safe as no-ops but we should make it explicit so there is more of an incentive to actually implement them.
2017-09-07 09:33:46 +01:00
use super::{
ffi, util, CookieResultExt, EventLoopWindowTarget, ImeRequest, ImeSender, VoidCookie, WindowId,
XConnection,
};
#[derive(Debug)]
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
pub struct SharedState {
2018-05-29 07:48:47 -04:00
pub cursor_pos: Option<(f64, f64)>,
pub size: Option<(u32, u32)>,
pub position: Option<(i32, i32)>,
pub inner_position: Option<(i32, i32)>,
pub inner_position_rel_parent: Option<(i32, i32)>,
pub is_resizable: bool,
pub is_decorated: bool,
pub last_monitor: X11MonitorHandle,
pub dpi_adjusted: Option<(u32, u32)>,
2022-09-21 10:04:28 +02:00
pub(crate) fullscreen: Option<Fullscreen>,
// Set when application calls `set_fullscreen` when window is not visible
2022-09-21 10:04:28 +02:00
pub(crate) desired_fullscreen: Option<Option<Fullscreen>>,
Add exclusive fullscreen mode (#925) * Add exclusive fullscreen mode * Add `WindowExtMacOS::set_fullscreen_presentation_options` * Capture display for exclusive fullscreen on macOS * Fix applying video mode on macOS after a fullscreen cycle * Fix compilation on iOS * Set monitor appropriately for fullscreen on macOS * Fix exclusive to borderless fullscreen transitions on macOS * Fix borderless to exclusive fullscreen transition on macOS * Sort video modes on Windows * Fix fullscreen issues on Windows * Fix video mode changes during exclusive fullscreen on Windows * Add video mode sorting for macOS and iOS * Fix monitor `ns_screen` returning `None` after video mode change * Fix "multithreaded" example on macOS * Restore video mode upon closing an exclusive fullscreen window * Fix "multithreaded" example closing multiple windows at once * Fix compilation on Linux * Update FEATURES.md * Don't care about logical monitor groups on X11 * Add exclusive fullscreen for X11 * Update FEATURES.md * Fix transitions between exclusive and borderless fullscreen on X11 * Update CHANGELOG.md * Document that Wayland doesn't support exclusive fullscreen * Replace core-graphics display mode bindings on macOS * Use `panic!()` instead of `unreachable!()` in "fullscreen" example * Fix fullscreen "always on top" flag on Windows * Track current monitor for fullscreen in "multithreaded" example * Fix exclusive fullscreen sometimes not positioning window properly * Format * More formatting and fix CI issues * Fix formatting * Fix changelog formatting
2019-07-29 21:16:14 +03:00
// Used to restore position after exiting fullscreen
2018-06-14 19:42:18 -04:00
pub restore_position: Option<(i32, i32)>,
Add exclusive fullscreen mode (#925) * Add exclusive fullscreen mode * Add `WindowExtMacOS::set_fullscreen_presentation_options` * Capture display for exclusive fullscreen on macOS * Fix applying video mode on macOS after a fullscreen cycle * Fix compilation on iOS * Set monitor appropriately for fullscreen on macOS * Fix exclusive to borderless fullscreen transitions on macOS * Fix borderless to exclusive fullscreen transition on macOS * Sort video modes on Windows * Fix fullscreen issues on Windows * Fix video mode changes during exclusive fullscreen on Windows * Add video mode sorting for macOS and iOS * Fix monitor `ns_screen` returning `None` after video mode change * Fix "multithreaded" example on macOS * Restore video mode upon closing an exclusive fullscreen window * Fix "multithreaded" example closing multiple windows at once * Fix compilation on Linux * Update FEATURES.md * Don't care about logical monitor groups on X11 * Add exclusive fullscreen for X11 * Update FEATURES.md * Fix transitions between exclusive and borderless fullscreen on X11 * Update CHANGELOG.md * Document that Wayland doesn't support exclusive fullscreen * Replace core-graphics display mode bindings on macOS * Use `panic!()` instead of `unreachable!()` in "fullscreen" example * Fix fullscreen "always on top" flag on Windows * Track current monitor for fullscreen in "multithreaded" example * Fix exclusive fullscreen sometimes not positioning window properly * Format * More formatting and fix CI issues * Fix formatting * Fix changelog formatting
2019-07-29 21:16:14 +03:00
// Used to restore video mode after exiting fullscreen
pub desktop_video_mode: Option<(randr::Crtc, randr::Mode)>,
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
pub frame_extents: Option<util::FrameExtentsHeuristic>,
pub min_inner_size: Option<Size>,
pub max_inner_size: Option<Size>,
pub resize_increments: Option<Size>,
pub base_size: Option<Size>,
pub visibility: Visibility,
pub has_focus: bool,
pub cursor_hittest: bool,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Visibility {
No,
Yes,
// Waiting for VisibilityNotify
YesWait,
2018-06-14 19:42:18 -04:00
}
impl SharedState {
fn new(last_monitor: X11MonitorHandle, window_attributes: &WindowAttributes) -> Mutex<Self> {
let visibility = if window_attributes.visible {
Visibility::YesWait
} else {
Visibility::No
};
Mutex::new(SharedState {
last_monitor,
visibility,
is_resizable: window_attributes.resizable,
is_decorated: window_attributes.decorations,
cursor_pos: None,
size: None,
position: None,
inner_position: None,
inner_position_rel_parent: None,
dpi_adjusted: None,
fullscreen: None,
desired_fullscreen: None,
restore_position: None,
desktop_video_mode: None,
frame_extents: None,
min_inner_size: None,
max_inner_size: None,
resize_increments: None,
base_size: None,
has_focus: false,
cursor_hittest: true,
})
2018-06-14 19:42:18 -04:00
}
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
}
2018-05-29 07:48:47 -04:00
unsafe impl Send for UnownedWindow {}
unsafe impl Sync for UnownedWindow {}
pub(crate) struct UnownedWindow {
pub(crate) xconn: Arc<XConnection>, // never changes
xwindow: xproto::Window, // never changes
visual: u32, // never changes
root: xproto::Window, // never changes
screen_id: i32, // never changes
cursor: Mutex<CursorIcon>,
cursor_grabbed_mode: Mutex<CursorGrabMode>,
2022-08-31 18:32:19 +02:00
#[allow(clippy::mutex_atomic)]
cursor_visible: Mutex<bool>,
2018-06-14 19:42:18 -04:00
ime_sender: Mutex<ImeSender>,
2018-05-29 07:48:47 -04:00
pub shared_state: Mutex<SharedState>,
Linux: Implement EventLoopExtPumpEvents and EventLoopExtRunOnDemand Wayland: I found the calloop abstraction a little awkward to work with while I was trying to understand why there was surprising workaround code in the wayland backend for manually dispatching pending events. Investigating this further it looks like there may currently be several issues with the calloop WaylandSource (with how prepare_read is used and with (not) flushing writes before polling) Considering the current minimal needs for polling in all winit backends I do personally tend to think it would be simpler to just own the responsibility for polling more directly, so the logic for wayland-client `prepare_read` wouldn't be in a separate crate (and in this current situation would also be easier to fix) I've tried to maintain the status quo with calloop + workarounds. X11: I found that the recent changes (4ac2006cbc5a) to port the X11 backend from mio to calloop lost the ability to check for pending events before needing to poll/dispatch. (The `has_pending` state being queried before dispatching() was based on state that was filled in during dispatching) As part of the rebase this re-introduces the PeekableReceiver and WakeSender which are small utilities on top of `std::sync::mpsc::channel()`. This adds a calloop `PingSource` so we can use a `Ping` as a generic event loop waker. For taking into account false positive wake ups the X11 source now tracks when the file descriptor is readable so after we poll via calloop we can then specifically check if there are new X11 events or pending redraw/user events when deciding whether to skip the event loop iteration.
2023-06-18 12:40:03 +01:00
redraw_sender: WakeSender<WindowId>,
activation_sender: WakeSender<super::ActivationToken>,
}
macro_rules! leap {
($e:expr) => {
match $e {
Ok(x) => x,
Err(err) => return Err(os_error!(OsError::XError(X11Error::from(err).into()))),
}
};
}
2018-05-29 07:48:47 -04:00
impl UnownedWindow {
#[allow(clippy::unnecessary_cast)]
pub(crate) fn new<T>(
event_loop: &EventLoopWindowTarget<T>,
2018-05-07 17:36:21 -04:00
window_attrs: WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes,
) -> Result<UnownedWindow, RootOsError> {
2018-05-29 07:48:47 -04:00
let xconn = &event_loop.xconn;
let atoms = xconn.atoms();
let root = match window_attrs.parent_window {
Some(RawWindowHandle::Xlib(handle)) => handle.window as xproto::Window,
Some(RawWindowHandle::Xcb(handle)) => handle.window,
Some(raw) => unreachable!("Invalid raw window handle {raw:?} on X11"),
None => event_loop.root,
};
let mut monitors = leap!(xconn.available_monitors());
let guessed_monitor = if monitors.is_empty() {
X11MonitorHandle::dummy()
} else {
xconn
.query_pointer(root, util::VIRTUAL_CORE_POINTER)
.ok()
.and_then(|pointer_state| {
let (x, y) = (pointer_state.root_x as i64, pointer_state.root_y as i64);
for i in 0..monitors.len() {
if monitors[i].rect.contains_point(x, y) {
return Some(monitors.swap_remove(i));
}
}
None
})
.unwrap_or_else(|| monitors.swap_remove(0))
};
let scale_factor = guessed_monitor.scale_factor();
info!("Guessed window scale factor: {}", scale_factor);
let max_inner_size: Option<(u32, u32)> = window_attrs
.max_inner_size
.map(|size| size.to_physical::<u32>(scale_factor).into());
let min_inner_size: Option<(u32, u32)> = window_attrs
.min_inner_size
.map(|size| size.to_physical::<u32>(scale_factor).into());
2018-06-14 19:42:18 -04:00
let position = window_attrs
.position
.map(|position| position.to_physical::<i32>(scale_factor));
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
let dimensions = {
// x11 only applies constraints when the window is actively resized
// by the user, so we have to manually apply the initial constraints
let mut dimensions: (u32, u32) = window_attrs
.inner_size
.map(|size| size.to_physical::<u32>(scale_factor))
.or_else(|| Some((800, 600).into()))
2018-06-14 19:42:18 -04:00
.map(Into::into)
.unwrap();
if let Some(max) = max_inner_size {
dimensions.0 = cmp::min(dimensions.0, max.0);
dimensions.1 = cmp::min(dimensions.1, max.1);
}
if let Some(min) = min_inner_size {
dimensions.0 = cmp::max(dimensions.0, min.0);
dimensions.1 = cmp::max(dimensions.1, min.1);
}
debug!(
"Calculated physical dimensions: {}x{}",
dimensions.0, dimensions.1
);
dimensions
};
let screen_id = match pl_attribs.x11.screen_id {
Some(id) => id,
None => xconn.default_screen_index() as c_int,
};
// An iterator over all of the visuals combined with their depths.
let mut all_visuals = xconn
.xcb_connection()
.setup()
.roots
.iter()
.flat_map(|root| &root.allowed_depths)
.flat_map(|depth| {
depth
.visuals
.iter()
.map(move |visual| (visual, depth.depth))
});
// creating
let (visualtype, depth, require_colormap) = match pl_attribs.x11.visual_id {
Some(vi) => {
// Find this specific visual.
let (visualtype, depth) = all_visuals
.find(|(visual, _)| visual.visual_id == vi)
.ok_or_else(|| os_error!(OsError::XError(X11Error::NoSuchVisual(vi).into())))?;
(Some(visualtype), depth, true)
}
None if window_attrs.transparent => {
// Find a suitable visual, true color with 32 bits of depth.
all_visuals
.find_map(|(visual, depth)| {
2023-09-25 14:05:15 -07:00
(depth == 32 && visual.class == xproto::VisualClass::TRUE_COLOR)
.then_some((Some(visual), depth, true))
})
.unwrap_or_else(|| {
debug!("Could not set transparency, because XMatchVisualInfo returned zero for the required parameters");
(None as _, x11rb::COPY_FROM_PARENT as _, false)
})
}
_ => (None, x11rb::COPY_FROM_PARENT as _, false),
};
let mut visual = visualtype.map_or(x11rb::COPY_FROM_PARENT, |v| v.visual_id);
let window_attributes = {
use xproto::EventMask;
let mut aux = xproto::CreateWindowAux::new();
let event_mask = EventMask::EXPOSURE
| EventMask::STRUCTURE_NOTIFY
| EventMask::VISIBILITY_CHANGE
| EventMask::KEY_PRESS
| EventMask::KEY_RELEASE
| EventMask::KEYMAP_STATE
| EventMask::BUTTON_PRESS
| EventMask::BUTTON_RELEASE
| EventMask::POINTER_MOTION;
aux = aux.event_mask(event_mask).border_pixel(0);
if pl_attribs.x11.override_redirect {
aux = aux.override_redirect(true as u32);
}
// Add a colormap if needed.
let colormap_visual = match pl_attribs.x11.visual_id {
Some(vi) => Some(vi),
None if require_colormap => Some(visual),
_ => None,
};
if let Some(visual) = colormap_visual {
let colormap = leap!(xconn.xcb_connection().generate_id());
leap!(xconn.xcb_connection().create_colormap(
xproto::ColormapAlloc::NONE,
colormap,
root,
visual,
));
aux = aux.colormap(colormap);
} else {
aux = aux.colormap(0);
}
2015-06-14 22:20:32 +01:00
aux
};
// Figure out the window's parent.
let parent = pl_attribs.x11.embed_window.unwrap_or(root);
// finally creating the window
let xwindow = {
let (x, y) = position.map_or((0, 0), Into::into);
let wid = leap!(xconn.xcb_connection().generate_id());
let result = xconn.xcb_connection().create_window(
depth,
wid,
parent,
x,
y,
dimensions.0.try_into().unwrap(),
dimensions.1.try_into().unwrap(),
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
0,
xproto::WindowClass::INPUT_OUTPUT,
visual,
&window_attributes,
);
leap!(leap!(result).check());
wid
};
// The COPY_FROM_PARENT is a special value for the visual used to copy
// the visual from the parent window, thus we have to query the visual
// we've got when we built the window above.
if visual == x11rb::COPY_FROM_PARENT {
visual = leap!(leap!(xconn
.xcb_connection()
.get_window_attributes(xwindow as xproto::Window))
.reply())
.visual;
}
2022-08-31 18:32:19 +02:00
#[allow(clippy::mutex_atomic)]
let mut window = UnownedWindow {
2018-05-29 07:48:47 -04:00
xconn: Arc::clone(xconn),
xwindow: xwindow as xproto::Window,
visual,
root,
screen_id,
2018-05-29 07:48:47 -04:00
cursor: Default::default(),
cursor_grabbed_mode: Mutex::new(CursorGrabMode::None),
cursor_visible: Mutex::new(true),
2018-06-14 19:42:18 -04:00
ime_sender: Mutex::new(event_loop.ime_sender.clone()),
shared_state: SharedState::new(guessed_monitor, &window_attrs),
redraw_sender: event_loop.redraw_sender.clone(),
activation_sender: event_loop.activation_sender.clone(),
};
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
// Title must be set before mapping. Some tiling window managers (i.e. i3) use the window
// title to determine placement/etc., so doing this after mapping would cause the WM to
// act on the wrong title state.
leap!(window.set_title_inner(&window_attrs.title)).ignore_error();
leap!(window.set_decorations_inner(window_attrs.decorations)).ignore_error();
if let Some(theme) = window_attrs.preferred_theme {
leap!(window.set_theme_inner(Some(theme))).ignore_error();
}
// Embed the window if needed.
if pl_attribs.x11.embed_window.is_some() {
window.embed_window()?;
}
{
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
// Enable drag and drop (TODO: extend API to make this toggleable)
{
let dnd_aware_atom = atoms[XdndAware];
let version = &[5u32]; // Latest version; hasn't changed since 2002
leap!(xconn.change_property(
2018-05-29 07:48:47 -04:00
window.xwindow,
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
dnd_aware_atom,
u32::from(xproto::AtomEnum::ATOM),
xproto::PropMode::REPLACE,
version,
))
.ignore_error();
}
2015-12-24 10:57:08 +01:00
// WM_CLASS must be set *before* mapping the window, as per ICCCM!
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
{
let (class, instance) = if let Some(name) = pl_attribs.name {
(name.instance, name.general)
} else {
let class = env::args_os()
.next()
.as_ref()
// Default to the name of the binary (via argv[0])
.and_then(|path| Path::new(path).file_name())
.and_then(|bin_name| bin_name.to_str())
.map(|bin_name| bin_name.to_owned())
.unwrap_or_else(|| window_attrs.title.clone());
// This environment variable is extraordinarily unlikely to actually be used...
let instance = env::var("RESOURCE_NAME")
.ok()
.unwrap_or_else(|| class.clone());
(instance, class)
};
let class = format!("{instance}\0{class}\0");
leap!(xconn.change_property(
window.xwindow,
xproto::Atom::from(xproto::AtomEnum::WM_CLASS),
xproto::Atom::from(xproto::AtomEnum::STRING),
xproto::PropMode::REPLACE,
class.as_bytes(),
))
.ignore_error();
}
if let Some(flusher) = leap!(window.set_pid()) {
flusher.ignore_error()
}
leap!(window.set_window_types(pl_attribs.x11.x11_window_types)).ignore_error();
// Set size hints.
let mut min_inner_size = window_attrs
.min_inner_size
.map(|size| size.to_physical::<u32>(scale_factor));
let mut max_inner_size = window_attrs
.max_inner_size
.map(|size| size.to_physical::<u32>(scale_factor));
2018-06-14 19:42:18 -04:00
if !window_attrs.resizable {
if util::wm_name_is_one_of(&["Xfwm4"]) {
warn!("To avoid a WM bug, disabling resizing has no effect on Xfwm4");
} else {
max_inner_size = Some(dimensions.into());
min_inner_size = Some(dimensions.into());
}
}
let shared_state = window.shared_state.get_mut().unwrap();
shared_state.min_inner_size = min_inner_size.map(Into::into);
shared_state.max_inner_size = max_inner_size.map(Into::into);
shared_state.resize_increments = window_attrs.resize_increments;
shared_state.base_size = pl_attribs.x11.base_size;
let normal_hints = WmSizeHints {
position: position.map(|PhysicalPosition { x, y }| {
(WmSizeHintsSpecification::UserSpecified, x, y)
}),
size: Some((
WmSizeHintsSpecification::UserSpecified,
cast_dimension_to_hint(dimensions.0),
cast_dimension_to_hint(dimensions.1),
)),
max_size: max_inner_size.map(cast_physical_size_to_hint),
min_size: min_inner_size.map(cast_physical_size_to_hint),
size_increment: window_attrs
.resize_increments
.map(|size| cast_size_to_hint(size, scale_factor)),
base_size: pl_attribs
.x11
.base_size
.map(|size| cast_size_to_hint(size, scale_factor)),
aspect: None,
win_gravity: None,
};
leap!(leap!(normal_hints.set(
xconn.xcb_connection(),
window.xwindow as xproto::Window,
xproto::AtomEnum::WM_NORMAL_HINTS,
))
.check());
2018-05-07 17:36:21 -04:00
// Set window icons
if let Some(icon) = window_attrs.window_icon {
leap!(window.set_icon_inner(icon.inner)).ignore_error();
2018-05-07 17:36:21 -04:00
}
// Opt into handling window close
let result = xconn.xcb_connection().change_property(
xproto::PropMode::REPLACE,
window.xwindow,
atoms[WM_PROTOCOLS],
xproto::AtomEnum::ATOM,
32,
2,
bytemuck::cast_slice::<xproto::Atom, u8>(&[
atoms[WM_DELETE_WINDOW],
atoms[_NET_WM_PING],
]),
);
leap!(result).ignore_error();
// Set visibility (map window)
if window_attrs.visible {
leap!(xconn.xcb_connection().map_window(window.xwindow)).ignore_error();
leap!(xconn.xcb_connection().configure_window(
xwindow,
&xproto::ConfigureWindowAux::new().stack_mode(xproto::StackMode::ABOVE)
))
.ignore_error();
}
// Attempt to make keyboard input repeat detectable
unsafe {
let mut supported_ptr = ffi::False;
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
(xconn.xlib.XkbSetDetectableAutoRepeat)(
xconn.display,
ffi::True,
&mut supported_ptr,
);
if supported_ptr == ffi::False {
return Err(os_error!(OsError::Misc(
"`XkbSetDetectableAutoRepeat` failed"
)));
}
}
// Select XInput2 events
let mask = xinput::XIEventMask::MOTION
| xinput::XIEventMask::BUTTON_PRESS
| xinput::XIEventMask::BUTTON_RELEASE
| xinput::XIEventMask::ENTER
| xinput::XIEventMask::LEAVE
| xinput::XIEventMask::FOCUS_IN
| xinput::XIEventMask::FOCUS_OUT
| xinput::XIEventMask::TOUCH_BEGIN
| xinput::XIEventMask::TOUCH_UPDATE
| xinput::XIEventMask::TOUCH_END;
leap!(xconn.select_xinput_events(window.xwindow, super::ALL_MASTER_DEVICES, mask))
.ignore_error();
2018-06-14 19:42:18 -04:00
{
let result = event_loop
.ime
.borrow_mut()
.create_context(window.xwindow as ffi::Window, false);
leap!(result);
2018-06-14 19:42:18 -04:00
}
// These properties must be set after mapping
if window_attrs.maximized {
leap!(window.set_maximized_inner(window_attrs.maximized)).ignore_error();
}
if window_attrs.fullscreen.is_some() {
if let Some(flusher) =
leap!(window
.set_fullscreen_inner(window_attrs.fullscreen.clone().map(Into::into)))
{
flusher.ignore_error()
}
if let Some(PhysicalPosition { x, y }) = position {
2022-08-31 18:32:19 +02:00
let shared_state = window.shared_state.get_mut().unwrap();
shared_state.restore_position = Some((x, y));
}
}
leap!(window.set_window_level_inner(window_attrs.window_level)).ignore_error();
}
// Remove the startup notification if we have one.
if let Some(startup) = pl_attribs.activation_token.as_ref() {
leap!(xconn.remove_activation_token(xwindow, &startup._token));
}
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
// We never want to give the user a broken window, since by then, it's too late to handle.
let window = leap!(xconn.sync_with_server().map(|_| window));
Ok(window)
}
/// Embed this window into a parent window.
pub(super) fn embed_window(&self) -> Result<(), RootOsError> {
let atoms = self.xconn.atoms();
leap!(leap!(self.xconn.change_property(
self.xwindow,
atoms[_XEMBED],
atoms[_XEMBED],
xproto::PropMode::REPLACE,
&[0u32, 1u32],
))
.check());
Ok(())
}
2022-08-31 18:32:19 +02:00
pub(super) fn shared_state_lock(&self) -> MutexGuard<'_, SharedState> {
self.shared_state.lock().unwrap()
}
fn set_pid(&self) -> Result<Option<VoidCookie<'_>>, X11Error> {
let atoms = self.xconn.atoms();
let pid_atom = atoms[_NET_WM_PID];
let client_machine_atom = atoms[WM_CLIENT_MACHINE];
// Get the hostname and the PID.
let uname = rustix::system::uname();
let pid = rustix::process::getpid();
self.xconn
.change_property(
2018-05-29 07:48:47 -04:00
self.xwindow,
pid_atom,
xproto::Atom::from(xproto::AtomEnum::CARDINAL),
xproto::PropMode::REPLACE,
&[pid.as_raw_nonzero().get() as util::Cardinal],
)?
.ignore_error();
let flusher = self.xconn.change_property(
self.xwindow,
client_machine_atom,
xproto::Atom::from(xproto::AtomEnum::STRING),
xproto::PropMode::REPLACE,
uname.nodename().to_bytes(),
);
flusher.map(Some)
}
fn set_window_types(
&self,
window_types: Vec<util::WindowType>,
) -> Result<VoidCookie<'_>, X11Error> {
let atoms = self.xconn.atoms();
let hint_atom = atoms[_NET_WM_WINDOW_TYPE];
let atoms: Vec<_> = window_types
.iter()
.map(|t| t.as_atom(&self.xconn))
.collect();
2018-05-29 07:48:47 -04:00
self.xconn.change_property(
self.xwindow,
2018-05-27 08:49:35 -04:00
hint_atom,
xproto::Atom::from(xproto::AtomEnum::ATOM),
xproto::PropMode::REPLACE,
&atoms,
2018-05-27 08:49:35 -04:00
)
}
pub fn set_theme_inner(&self, theme: Option<Theme>) -> Result<VoidCookie<'_>, X11Error> {
let atoms = self.xconn.atoms();
let hint_atom = atoms[_GTK_THEME_VARIANT];
let utf8_atom = atoms[UTF8_STRING];
let variant = match theme {
Some(Theme::Dark) => "dark",
Some(Theme::Light) => "light",
None => "dark",
};
let variant = CString::new(variant).expect("`_GTK_THEME_VARIANT` contained null byte");
self.xconn.change_property(
self.xwindow,
hint_atom,
utf8_atom,
xproto::PropMode::REPLACE,
variant.as_bytes(),
)
}
#[inline]
pub fn set_theme(&self, theme: Option<Theme>) {
self.set_theme_inner(theme)
.expect("Failed to change window theme")
.ignore_error();
self.xconn
.flush_requests()
.expect("Failed to change window theme");
}
fn set_netwm(
2018-05-29 07:48:47 -04:00
&self,
operation: util::StateOperation,
properties: (u32, u32, u32, u32),
) -> Result<VoidCookie<'_>, X11Error> {
let atoms = self.xconn.atoms();
let state_atom = atoms[_NET_WM_STATE];
2018-05-29 07:48:47 -04:00
self.xconn.send_client_msg(
self.xwindow,
self.root,
2018-05-27 08:49:35 -04:00
state_atom,
Some(xproto::EventMask::SUBSTRUCTURE_REDIRECT | xproto::EventMask::SUBSTRUCTURE_NOTIFY),
2018-05-27 08:49:35 -04:00
[
operation as u32,
2018-05-27 08:49:35 -04:00
properties.0,
properties.1,
properties.2,
properties.3,
],
)
}
fn set_fullscreen_hint(&self, fullscreen: bool) -> Result<VoidCookie<'_>, X11Error> {
let atoms = self.xconn.atoms();
let fullscreen_atom = atoms[_NET_WM_STATE_FULLSCREEN];
let flusher = self.set_netwm(fullscreen.into(), (fullscreen_atom, 0, 0, 0));
if fullscreen {
// Ensure that the fullscreen window receives input focus to prevent
// locking up the user's display.
self.xconn
.xcb_connection()
.set_input_focus(
xproto::InputFocus::PARENT,
self.xwindow,
x11rb::CURRENT_TIME,
)?
.ignore_error();
}
flusher
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
}
fn set_fullscreen_inner(
&self,
fullscreen: Option<Fullscreen>,
) -> Result<Option<VoidCookie<'_>>, X11Error> {
2022-08-31 18:32:19 +02:00
let mut shared_state_lock = self.shared_state_lock();
match shared_state_lock.visibility {
// Setting fullscreen on a window that is not visible will generate an error.
Visibility::No | Visibility::YesWait => {
shared_state_lock.desired_fullscreen = Some(fullscreen);
return Ok(None);
}
Visibility::Yes => (),
}
Add exclusive fullscreen mode (#925) * Add exclusive fullscreen mode * Add `WindowExtMacOS::set_fullscreen_presentation_options` * Capture display for exclusive fullscreen on macOS * Fix applying video mode on macOS after a fullscreen cycle * Fix compilation on iOS * Set monitor appropriately for fullscreen on macOS * Fix exclusive to borderless fullscreen transitions on macOS * Fix borderless to exclusive fullscreen transition on macOS * Sort video modes on Windows * Fix fullscreen issues on Windows * Fix video mode changes during exclusive fullscreen on Windows * Add video mode sorting for macOS and iOS * Fix monitor `ns_screen` returning `None` after video mode change * Fix "multithreaded" example on macOS * Restore video mode upon closing an exclusive fullscreen window * Fix "multithreaded" example closing multiple windows at once * Fix compilation on Linux * Update FEATURES.md * Don't care about logical monitor groups on X11 * Add exclusive fullscreen for X11 * Update FEATURES.md * Fix transitions between exclusive and borderless fullscreen on X11 * Update CHANGELOG.md * Document that Wayland doesn't support exclusive fullscreen * Replace core-graphics display mode bindings on macOS * Use `panic!()` instead of `unreachable!()` in "fullscreen" example * Fix fullscreen "always on top" flag on Windows * Track current monitor for fullscreen in "multithreaded" example * Fix exclusive fullscreen sometimes not positioning window properly * Format * More formatting and fix CI issues * Fix formatting * Fix changelog formatting
2019-07-29 21:16:14 +03:00
let old_fullscreen = shared_state_lock.fullscreen.clone();
if old_fullscreen == fullscreen {
return Ok(None);
Add exclusive fullscreen mode (#925) * Add exclusive fullscreen mode * Add `WindowExtMacOS::set_fullscreen_presentation_options` * Capture display for exclusive fullscreen on macOS * Fix applying video mode on macOS after a fullscreen cycle * Fix compilation on iOS * Set monitor appropriately for fullscreen on macOS * Fix exclusive to borderless fullscreen transitions on macOS * Fix borderless to exclusive fullscreen transition on macOS * Sort video modes on Windows * Fix fullscreen issues on Windows * Fix video mode changes during exclusive fullscreen on Windows * Add video mode sorting for macOS and iOS * Fix monitor `ns_screen` returning `None` after video mode change * Fix "multithreaded" example on macOS * Restore video mode upon closing an exclusive fullscreen window * Fix "multithreaded" example closing multiple windows at once * Fix compilation on Linux * Update FEATURES.md * Don't care about logical monitor groups on X11 * Add exclusive fullscreen for X11 * Update FEATURES.md * Fix transitions between exclusive and borderless fullscreen on X11 * Update CHANGELOG.md * Document that Wayland doesn't support exclusive fullscreen * Replace core-graphics display mode bindings on macOS * Use `panic!()` instead of `unreachable!()` in "fullscreen" example * Fix fullscreen "always on top" flag on Windows * Track current monitor for fullscreen in "multithreaded" example * Fix exclusive fullscreen sometimes not positioning window properly * Format * More formatting and fix CI issues * Fix formatting * Fix changelog formatting
2019-07-29 21:16:14 +03:00
}
shared_state_lock.fullscreen = fullscreen.clone();
match (&old_fullscreen, &fullscreen) {
// Store the desktop video mode before entering exclusive
// fullscreen, so we can restore it upon exit, as XRandR does not
// provide a mechanism to set this per app-session or restore this
// to the desktop video mode as macOS and Windows do
2022-09-21 10:04:28 +02:00
(&None, &Some(Fullscreen::Exclusive(PlatformVideoMode::X(ref video_mode))))
Add exclusive fullscreen mode (#925) * Add exclusive fullscreen mode * Add `WindowExtMacOS::set_fullscreen_presentation_options` * Capture display for exclusive fullscreen on macOS * Fix applying video mode on macOS after a fullscreen cycle * Fix compilation on iOS * Set monitor appropriately for fullscreen on macOS * Fix exclusive to borderless fullscreen transitions on macOS * Fix borderless to exclusive fullscreen transition on macOS * Sort video modes on Windows * Fix fullscreen issues on Windows * Fix video mode changes during exclusive fullscreen on Windows * Add video mode sorting for macOS and iOS * Fix monitor `ns_screen` returning `None` after video mode change * Fix "multithreaded" example on macOS * Restore video mode upon closing an exclusive fullscreen window * Fix "multithreaded" example closing multiple windows at once * Fix compilation on Linux * Update FEATURES.md * Don't care about logical monitor groups on X11 * Add exclusive fullscreen for X11 * Update FEATURES.md * Fix transitions between exclusive and borderless fullscreen on X11 * Update CHANGELOG.md * Document that Wayland doesn't support exclusive fullscreen * Replace core-graphics display mode bindings on macOS * Use `panic!()` instead of `unreachable!()` in "fullscreen" example * Fix fullscreen "always on top" flag on Windows * Track current monitor for fullscreen in "multithreaded" example * Fix exclusive fullscreen sometimes not positioning window properly * Format * More formatting and fix CI issues * Fix formatting * Fix changelog formatting
2019-07-29 21:16:14 +03:00
| (
&Some(Fullscreen::Borderless(_)),
2022-09-21 10:04:28 +02:00
&Some(Fullscreen::Exclusive(PlatformVideoMode::X(ref video_mode))),
Add exclusive fullscreen mode (#925) * Add exclusive fullscreen mode * Add `WindowExtMacOS::set_fullscreen_presentation_options` * Capture display for exclusive fullscreen on macOS * Fix applying video mode on macOS after a fullscreen cycle * Fix compilation on iOS * Set monitor appropriately for fullscreen on macOS * Fix exclusive to borderless fullscreen transitions on macOS * Fix borderless to exclusive fullscreen transition on macOS * Sort video modes on Windows * Fix fullscreen issues on Windows * Fix video mode changes during exclusive fullscreen on Windows * Add video mode sorting for macOS and iOS * Fix monitor `ns_screen` returning `None` after video mode change * Fix "multithreaded" example on macOS * Restore video mode upon closing an exclusive fullscreen window * Fix "multithreaded" example closing multiple windows at once * Fix compilation on Linux * Update FEATURES.md * Don't care about logical monitor groups on X11 * Add exclusive fullscreen for X11 * Update FEATURES.md * Fix transitions between exclusive and borderless fullscreen on X11 * Update CHANGELOG.md * Document that Wayland doesn't support exclusive fullscreen * Replace core-graphics display mode bindings on macOS * Use `panic!()` instead of `unreachable!()` in "fullscreen" example * Fix fullscreen "always on top" flag on Windows * Track current monitor for fullscreen in "multithreaded" example * Fix exclusive fullscreen sometimes not positioning window properly * Format * More formatting and fix CI issues * Fix formatting * Fix changelog formatting
2019-07-29 21:16:14 +03:00
) => {
let monitor = video_mode.monitor.as_ref().unwrap();
shared_state_lock.desktop_video_mode = Some((
monitor.id,
self.xconn
.get_crtc_mode(monitor.id)
.expect("Failed to get desktop video mode"),
));
Add exclusive fullscreen mode (#925) * Add exclusive fullscreen mode * Add `WindowExtMacOS::set_fullscreen_presentation_options` * Capture display for exclusive fullscreen on macOS * Fix applying video mode on macOS after a fullscreen cycle * Fix compilation on iOS * Set monitor appropriately for fullscreen on macOS * Fix exclusive to borderless fullscreen transitions on macOS * Fix borderless to exclusive fullscreen transition on macOS * Sort video modes on Windows * Fix fullscreen issues on Windows * Fix video mode changes during exclusive fullscreen on Windows * Add video mode sorting for macOS and iOS * Fix monitor `ns_screen` returning `None` after video mode change * Fix "multithreaded" example on macOS * Restore video mode upon closing an exclusive fullscreen window * Fix "multithreaded" example closing multiple windows at once * Fix compilation on Linux * Update FEATURES.md * Don't care about logical monitor groups on X11 * Add exclusive fullscreen for X11 * Update FEATURES.md * Fix transitions between exclusive and borderless fullscreen on X11 * Update CHANGELOG.md * Document that Wayland doesn't support exclusive fullscreen * Replace core-graphics display mode bindings on macOS * Use `panic!()` instead of `unreachable!()` in "fullscreen" example * Fix fullscreen "always on top" flag on Windows * Track current monitor for fullscreen in "multithreaded" example * Fix exclusive fullscreen sometimes not positioning window properly * Format * More formatting and fix CI issues * Fix formatting * Fix changelog formatting
2019-07-29 21:16:14 +03:00
}
// Restore desktop video mode upon exiting exclusive fullscreen
(&Some(Fullscreen::Exclusive(_)), &None)
| (&Some(Fullscreen::Exclusive(_)), &Some(Fullscreen::Borderless(_))) => {
let (monitor_id, mode_id) = shared_state_lock.desktop_video_mode.take().unwrap();
self.xconn
.set_crtc_config(monitor_id, mode_id)
.expect("failed to restore desktop video mode");
}
_ => (),
}
drop(shared_state_lock);
match fullscreen {
Move fullscreen modes to not touch physical resolutions (#270) * Fix X11 screen resolution change using XrandR The previous XF86 resolution switching was broken and everything seems to have moved on to xrandr. Use that instead while cleaning up the code a bit as well. * Use XRandR for actual multiscreen support in X11 * Use actual monitor names in X11 * Get rid of ptr::read usage in X11 * Use a bog standard Vec instead of VecDeque * Get rid of the XRandR mode switching stuff Wayland has made the decision that apps shouldn't change screen resolutions and just take the screens as they've been setup. In the modern world where GPU scaling is cheap and LCD panels are scaling anyway it makes no sense to make "physical" resolution changes when software should be taking care of it. This massively simplifies the code and makes it easier to extend to more niche setups like MST and videowalls. * Rename fullscreen options to match new semantics * Implement XRandR 1.5 support * Get rid of the FullScreen enum Moving to just having two states None and Some(MonitorId) and then being able to set full screen in the current monitor with something like: window.set_fullscreen(Some(window.current_monitor())); * Implement Window::get_current_monitor() Do it by iterating over the available monitors and finding which has the biggest overlap with the window. For this MonitorId needs a new get_position() that needs to be implemented for all platforms. * Add unimplemented get_position() to all MonitorId * Make get_current_monitor() platform specific * Add unimplemented get_current_monitor() to all * Implement proper primary monitor selection in X11 * Shut up some warnings * Remove libxxf86vm package from travis Since we're no longer using XF86 there's no need to keep the package around for CI. * Don't use new struct syntax * Fix indentation * Adjust Android/iOS fullscreen/maximized On Android and iOS we can assume single screen apps that are already fullscreen and maximized so there are a few methods that are implemented by just returning a fixed value or not doing anything. * Mark OSX/Win fullscreen/maximized unimplemented()! These would be safe as no-ops but we should make it explicit so there is more of an incentive to actually implement them.
2017-09-07 09:33:46 +01:00
None => {
2018-06-14 19:42:18 -04:00
let flusher = self.set_fullscreen_hint(false);
2022-08-31 18:32:19 +02:00
let mut shared_state_lock = self.shared_state_lock();
Add exclusive fullscreen mode (#925) * Add exclusive fullscreen mode * Add `WindowExtMacOS::set_fullscreen_presentation_options` * Capture display for exclusive fullscreen on macOS * Fix applying video mode on macOS after a fullscreen cycle * Fix compilation on iOS * Set monitor appropriately for fullscreen on macOS * Fix exclusive to borderless fullscreen transitions on macOS * Fix borderless to exclusive fullscreen transition on macOS * Sort video modes on Windows * Fix fullscreen issues on Windows * Fix video mode changes during exclusive fullscreen on Windows * Add video mode sorting for macOS and iOS * Fix monitor `ns_screen` returning `None` after video mode change * Fix "multithreaded" example on macOS * Restore video mode upon closing an exclusive fullscreen window * Fix "multithreaded" example closing multiple windows at once * Fix compilation on Linux * Update FEATURES.md * Don't care about logical monitor groups on X11 * Add exclusive fullscreen for X11 * Update FEATURES.md * Fix transitions between exclusive and borderless fullscreen on X11 * Update CHANGELOG.md * Document that Wayland doesn't support exclusive fullscreen * Replace core-graphics display mode bindings on macOS * Use `panic!()` instead of `unreachable!()` in "fullscreen" example * Fix fullscreen "always on top" flag on Windows * Track current monitor for fullscreen in "multithreaded" example * Fix exclusive fullscreen sometimes not positioning window properly * Format * More formatting and fix CI issues * Fix formatting * Fix changelog formatting
2019-07-29 21:16:14 +03:00
if let Some(position) = shared_state_lock.restore_position.take() {
drop(shared_state_lock);
self.set_position_inner(position.0, position.1)
.expect_then_ignore_error("Failed to restore window position");
2018-06-14 19:42:18 -04:00
}
flusher.map(Some)
2019-06-24 12:14:55 -04:00
}
Add exclusive fullscreen mode (#925) * Add exclusive fullscreen mode * Add `WindowExtMacOS::set_fullscreen_presentation_options` * Capture display for exclusive fullscreen on macOS * Fix applying video mode on macOS after a fullscreen cycle * Fix compilation on iOS * Set monitor appropriately for fullscreen on macOS * Fix exclusive to borderless fullscreen transitions on macOS * Fix borderless to exclusive fullscreen transition on macOS * Sort video modes on Windows * Fix fullscreen issues on Windows * Fix video mode changes during exclusive fullscreen on Windows * Add video mode sorting for macOS and iOS * Fix monitor `ns_screen` returning `None` after video mode change * Fix "multithreaded" example on macOS * Restore video mode upon closing an exclusive fullscreen window * Fix "multithreaded" example closing multiple windows at once * Fix compilation on Linux * Update FEATURES.md * Don't care about logical monitor groups on X11 * Add exclusive fullscreen for X11 * Update FEATURES.md * Fix transitions between exclusive and borderless fullscreen on X11 * Update CHANGELOG.md * Document that Wayland doesn't support exclusive fullscreen * Replace core-graphics display mode bindings on macOS * Use `panic!()` instead of `unreachable!()` in "fullscreen" example * Fix fullscreen "always on top" flag on Windows * Track current monitor for fullscreen in "multithreaded" example * Fix exclusive fullscreen sometimes not positioning window properly * Format * More formatting and fix CI issues * Fix formatting * Fix changelog formatting
2019-07-29 21:16:14 +03:00
Some(fullscreen) => {
let (video_mode, monitor) = match fullscreen {
2022-09-21 10:04:28 +02:00
Fullscreen::Exclusive(PlatformVideoMode::X(ref video_mode)) => {
(Some(video_mode), video_mode.monitor.clone().unwrap())
}
Fullscreen::Borderless(Some(PlatformMonitorHandle::X(monitor))) => {
(None, monitor)
}
Fullscreen::Borderless(None) => {
(None, self.shared_state_lock().last_monitor.clone())
}
#[cfg(wayland_platform)]
Add exclusive fullscreen mode (#925) * Add exclusive fullscreen mode * Add `WindowExtMacOS::set_fullscreen_presentation_options` * Capture display for exclusive fullscreen on macOS * Fix applying video mode on macOS after a fullscreen cycle * Fix compilation on iOS * Set monitor appropriately for fullscreen on macOS * Fix exclusive to borderless fullscreen transitions on macOS * Fix borderless to exclusive fullscreen transition on macOS * Sort video modes on Windows * Fix fullscreen issues on Windows * Fix video mode changes during exclusive fullscreen on Windows * Add video mode sorting for macOS and iOS * Fix monitor `ns_screen` returning `None` after video mode change * Fix "multithreaded" example on macOS * Restore video mode upon closing an exclusive fullscreen window * Fix "multithreaded" example closing multiple windows at once * Fix compilation on Linux * Update FEATURES.md * Don't care about logical monitor groups on X11 * Add exclusive fullscreen for X11 * Update FEATURES.md * Fix transitions between exclusive and borderless fullscreen on X11 * Update CHANGELOG.md * Document that Wayland doesn't support exclusive fullscreen * Replace core-graphics display mode bindings on macOS * Use `panic!()` instead of `unreachable!()` in "fullscreen" example * Fix fullscreen "always on top" flag on Windows * Track current monitor for fullscreen in "multithreaded" example * Fix exclusive fullscreen sometimes not positioning window properly * Format * More formatting and fix CI issues * Fix formatting * Fix changelog formatting
2019-07-29 21:16:14 +03:00
_ => unreachable!(),
};
// Don't set fullscreen on an invalid dummy monitor handle
if monitor.is_dummy() {
return Ok(None);
}
Add exclusive fullscreen mode (#925) * Add exclusive fullscreen mode * Add `WindowExtMacOS::set_fullscreen_presentation_options` * Capture display for exclusive fullscreen on macOS * Fix applying video mode on macOS after a fullscreen cycle * Fix compilation on iOS * Set monitor appropriately for fullscreen on macOS * Fix exclusive to borderless fullscreen transitions on macOS * Fix borderless to exclusive fullscreen transition on macOS * Sort video modes on Windows * Fix fullscreen issues on Windows * Fix video mode changes during exclusive fullscreen on Windows * Add video mode sorting for macOS and iOS * Fix monitor `ns_screen` returning `None` after video mode change * Fix "multithreaded" example on macOS * Restore video mode upon closing an exclusive fullscreen window * Fix "multithreaded" example closing multiple windows at once * Fix compilation on Linux * Update FEATURES.md * Don't care about logical monitor groups on X11 * Add exclusive fullscreen for X11 * Update FEATURES.md * Fix transitions between exclusive and borderless fullscreen on X11 * Update CHANGELOG.md * Document that Wayland doesn't support exclusive fullscreen * Replace core-graphics display mode bindings on macOS * Use `panic!()` instead of `unreachable!()` in "fullscreen" example * Fix fullscreen "always on top" flag on Windows * Track current monitor for fullscreen in "multithreaded" example * Fix exclusive fullscreen sometimes not positioning window properly * Format * More formatting and fix CI issues * Fix formatting * Fix changelog formatting
2019-07-29 21:16:14 +03:00
if let Some(video_mode) = video_mode {
// FIXME: this is actually not correct if we're setting the
// video mode to a resolution higher than the current
// desktop resolution, because XRandR does not automatically
// reposition the monitors to the right and below this
// monitor.
//
// What ends up happening is we will get the fullscreen
// window showing up on those monitors as well, because
// their virtual position now overlaps with the monitor that
// we just made larger..
//
// It'd be quite a bit of work to handle this correctly (and
// nobody else seems to bother doing this correctly either),
// so we're just leaving this broken. Fixing this would
// involve storing all CRTCs upon entering fullscreen,
// restoring them upon exit, and after entering fullscreen,
// repositioning displays to the right and below this
// display. I think there would still be edge cases that are
// difficult or impossible to handle correctly, e.g. what if
// a new monitor was plugged in while in fullscreen?
//
// I think we might just want to disallow setting the video
// mode higher than the current desktop video mode (I'm sure
// this will make someone unhappy, but it's very unusual for
// games to want to do this anyway).
self.xconn
.set_crtc_config(monitor.id, video_mode.native_mode)
.expect("failed to set video mode");
}
let window_position = self.outer_position_physical();
2022-08-31 18:32:19 +02:00
self.shared_state_lock().restore_position = Some(window_position);
let monitor_origin: (i32, i32) = monitor.position().into();
self.set_position_inner(monitor_origin.0, monitor_origin.1)
.expect_then_ignore_error("Failed to set window position");
self.set_fullscreen_hint(true).map(Some)
2019-06-24 12:14:55 -04:00
}
}
}
#[inline]
2022-09-21 10:04:28 +02:00
pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
2022-08-31 18:32:19 +02:00
let shared_state = self.shared_state_lock();
shared_state
.desired_fullscreen
.clone()
.unwrap_or_else(|| shared_state.fullscreen.clone())
}
2018-06-14 19:42:18 -04:00
#[inline]
2022-09-21 10:04:28 +02:00
pub(crate) fn set_fullscreen(&self, fullscreen: Option<Fullscreen>) {
if let Some(flusher) = self
.set_fullscreen_inner(fullscreen)
.expect("Failed to change window fullscreen state")
{
Add exclusive fullscreen mode (#925) * Add exclusive fullscreen mode * Add `WindowExtMacOS::set_fullscreen_presentation_options` * Capture display for exclusive fullscreen on macOS * Fix applying video mode on macOS after a fullscreen cycle * Fix compilation on iOS * Set monitor appropriately for fullscreen on macOS * Fix exclusive to borderless fullscreen transitions on macOS * Fix borderless to exclusive fullscreen transition on macOS * Sort video modes on Windows * Fix fullscreen issues on Windows * Fix video mode changes during exclusive fullscreen on Windows * Add video mode sorting for macOS and iOS * Fix monitor `ns_screen` returning `None` after video mode change * Fix "multithreaded" example on macOS * Restore video mode upon closing an exclusive fullscreen window * Fix "multithreaded" example closing multiple windows at once * Fix compilation on Linux * Update FEATURES.md * Don't care about logical monitor groups on X11 * Add exclusive fullscreen for X11 * Update FEATURES.md * Fix transitions between exclusive and borderless fullscreen on X11 * Update CHANGELOG.md * Document that Wayland doesn't support exclusive fullscreen * Replace core-graphics display mode bindings on macOS * Use `panic!()` instead of `unreachable!()` in "fullscreen" example * Fix fullscreen "always on top" flag on Windows * Track current monitor for fullscreen in "multithreaded" example * Fix exclusive fullscreen sometimes not positioning window properly * Format * More formatting and fix CI issues * Fix formatting * Fix changelog formatting
2019-07-29 21:16:14 +03:00
flusher
.check()
Add exclusive fullscreen mode (#925) * Add exclusive fullscreen mode * Add `WindowExtMacOS::set_fullscreen_presentation_options` * Capture display for exclusive fullscreen on macOS * Fix applying video mode on macOS after a fullscreen cycle * Fix compilation on iOS * Set monitor appropriately for fullscreen on macOS * Fix exclusive to borderless fullscreen transitions on macOS * Fix borderless to exclusive fullscreen transition on macOS * Sort video modes on Windows * Fix fullscreen issues on Windows * Fix video mode changes during exclusive fullscreen on Windows * Add video mode sorting for macOS and iOS * Fix monitor `ns_screen` returning `None` after video mode change * Fix "multithreaded" example on macOS * Restore video mode upon closing an exclusive fullscreen window * Fix "multithreaded" example closing multiple windows at once * Fix compilation on Linux * Update FEATURES.md * Don't care about logical monitor groups on X11 * Add exclusive fullscreen for X11 * Update FEATURES.md * Fix transitions between exclusive and borderless fullscreen on X11 * Update CHANGELOG.md * Document that Wayland doesn't support exclusive fullscreen * Replace core-graphics display mode bindings on macOS * Use `panic!()` instead of `unreachable!()` in "fullscreen" example * Fix fullscreen "always on top" flag on Windows * Track current monitor for fullscreen in "multithreaded" example * Fix exclusive fullscreen sometimes not positioning window properly * Format * More formatting and fix CI issues * Fix formatting * Fix changelog formatting
2019-07-29 21:16:14 +03:00
.expect("Failed to change window fullscreen state");
self.invalidate_cached_frame_extents();
}
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
}
// Called by EventProcessor when a VisibilityNotify event is received
pub(crate) fn visibility_notify(&self) {
2022-08-31 18:32:19 +02:00
let mut shared_state = self.shared_state_lock();
match shared_state.visibility {
Visibility::No => self
.xconn
.xcb_connection()
.unmap_window(self.xwindow)
.expect_then_ignore_error("Failed to unmap window"),
Visibility::Yes => (),
Visibility::YesWait => {
shared_state.visibility = Visibility::Yes;
if let Some(fullscreen) = shared_state.desired_fullscreen.take() {
drop(shared_state);
self.set_fullscreen(fullscreen);
}
}
}
}
pub fn current_monitor(&self) -> Option<X11MonitorHandle> {
Some(self.shared_state_lock().last_monitor.clone())
Move fullscreen modes to not touch physical resolutions (#270) * Fix X11 screen resolution change using XrandR The previous XF86 resolution switching was broken and everything seems to have moved on to xrandr. Use that instead while cleaning up the code a bit as well. * Use XRandR for actual multiscreen support in X11 * Use actual monitor names in X11 * Get rid of ptr::read usage in X11 * Use a bog standard Vec instead of VecDeque * Get rid of the XRandR mode switching stuff Wayland has made the decision that apps shouldn't change screen resolutions and just take the screens as they've been setup. In the modern world where GPU scaling is cheap and LCD panels are scaling anyway it makes no sense to make "physical" resolution changes when software should be taking care of it. This massively simplifies the code and makes it easier to extend to more niche setups like MST and videowalls. * Rename fullscreen options to match new semantics * Implement XRandR 1.5 support * Get rid of the FullScreen enum Moving to just having two states None and Some(MonitorId) and then being able to set full screen in the current monitor with something like: window.set_fullscreen(Some(window.current_monitor())); * Implement Window::get_current_monitor() Do it by iterating over the available monitors and finding which has the biggest overlap with the window. For this MonitorId needs a new get_position() that needs to be implemented for all platforms. * Add unimplemented get_position() to all MonitorId * Make get_current_monitor() platform specific * Add unimplemented get_current_monitor() to all * Implement proper primary monitor selection in X11 * Shut up some warnings * Remove libxxf86vm package from travis Since we're no longer using XF86 there's no need to keep the package around for CI. * Don't use new struct syntax * Fix indentation * Adjust Android/iOS fullscreen/maximized On Android and iOS we can assume single screen apps that are already fullscreen and maximized so there are a few methods that are implemented by just returning a fixed value or not doing anything. * Mark OSX/Win fullscreen/maximized unimplemented()! These would be safe as no-ops but we should make it explicit so there is more of an incentive to actually implement them.
2017-09-07 09:33:46 +01:00
}
pub fn available_monitors(&self) -> Vec<X11MonitorHandle> {
self.xconn
.available_monitors()
.expect("Failed to get available monitors")
}
pub fn primary_monitor(&self) -> Option<X11MonitorHandle> {
Some(
self.xconn
.primary_monitor()
.expect("Failed to get primary monitor"),
)
}
#[inline]
pub fn is_minimized(&self) -> Option<bool> {
let atoms = self.xconn.atoms();
let state_atom = atoms[_NET_WM_STATE];
let state = self.xconn.get_property(
self.xwindow,
state_atom,
xproto::Atom::from(xproto::AtomEnum::ATOM),
);
let hidden_atom = atoms[_NET_WM_STATE_HIDDEN];
Some(match state {
Ok(atoms) => atoms
.iter()
.any(|atom: &xproto::Atom| *atom as xproto::Atom == hidden_atom),
_ => false,
})
}
fn set_minimized_inner(&self, minimized: bool) -> Result<VoidCookie<'_>, X11Error> {
let atoms = self.xconn.atoms();
if minimized {
let root_window = self.xconn.default_root().root;
self.xconn.send_client_msg(
self.xwindow,
root_window,
atoms[WM_CHANGE_STATE],
Some(
xproto::EventMask::SUBSTRUCTURE_REDIRECT
| xproto::EventMask::SUBSTRUCTURE_NOTIFY,
),
[WmHintsState::Iconic as u32, 0, 0, 0, 0],
)
} else {
self.xconn.send_client_msg(
self.xwindow,
self.root,
atoms[_NET_ACTIVE_WINDOW],
Some(
xproto::EventMask::SUBSTRUCTURE_REDIRECT
| xproto::EventMask::SUBSTRUCTURE_NOTIFY,
),
[1, x11rb::CURRENT_TIME, 0, 0, 0],
)
}
}
#[inline]
pub fn set_minimized(&self, minimized: bool) {
self.set_minimized_inner(minimized)
.expect_then_ignore_error("Failed to change window minimization");
self.xconn
.flush_requests()
.expect("Failed to change window minimization");
}
#[inline]
pub fn is_maximized(&self) -> bool {
let atoms = self.xconn.atoms();
let state_atom = atoms[_NET_WM_STATE];
let state = self.xconn.get_property(
self.xwindow,
state_atom,
xproto::Atom::from(xproto::AtomEnum::ATOM),
);
let horz_atom = atoms[_NET_WM_STATE_MAXIMIZED_HORZ];
let vert_atom = atoms[_NET_WM_STATE_MAXIMIZED_VERT];
match state {
Ok(atoms) => {
let horz_maximized = atoms.iter().any(|atom: &xproto::Atom| *atom == horz_atom);
let vert_maximized = atoms.iter().any(|atom: &xproto::Atom| *atom == vert_atom);
horz_maximized && vert_maximized
}
_ => false,
}
}
fn set_maximized_inner(&self, maximized: bool) -> Result<VoidCookie<'_>, X11Error> {
let atoms = self.xconn.atoms();
let horz_atom = atoms[_NET_WM_STATE_MAXIMIZED_HORZ];
let vert_atom = atoms[_NET_WM_STATE_MAXIMIZED_VERT];
self.set_netwm(maximized.into(), (horz_atom, vert_atom, 0, 0))
}
2018-06-14 19:42:18 -04:00
#[inline]
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
pub fn set_maximized(&self, maximized: bool) {
self.set_maximized_inner(maximized)
.expect_then_ignore_error("Failed to change window maximization");
self.xconn
.flush_requests()
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
.expect("Failed to change window maximization");
self.invalidate_cached_frame_extents();
Move fullscreen modes to not touch physical resolutions (#270) * Fix X11 screen resolution change using XrandR The previous XF86 resolution switching was broken and everything seems to have moved on to xrandr. Use that instead while cleaning up the code a bit as well. * Use XRandR for actual multiscreen support in X11 * Use actual monitor names in X11 * Get rid of ptr::read usage in X11 * Use a bog standard Vec instead of VecDeque * Get rid of the XRandR mode switching stuff Wayland has made the decision that apps shouldn't change screen resolutions and just take the screens as they've been setup. In the modern world where GPU scaling is cheap and LCD panels are scaling anyway it makes no sense to make "physical" resolution changes when software should be taking care of it. This massively simplifies the code and makes it easier to extend to more niche setups like MST and videowalls. * Rename fullscreen options to match new semantics * Implement XRandR 1.5 support * Get rid of the FullScreen enum Moving to just having two states None and Some(MonitorId) and then being able to set full screen in the current monitor with something like: window.set_fullscreen(Some(window.current_monitor())); * Implement Window::get_current_monitor() Do it by iterating over the available monitors and finding which has the biggest overlap with the window. For this MonitorId needs a new get_position() that needs to be implemented for all platforms. * Add unimplemented get_position() to all MonitorId * Make get_current_monitor() platform specific * Add unimplemented get_current_monitor() to all * Implement proper primary monitor selection in X11 * Shut up some warnings * Remove libxxf86vm package from travis Since we're no longer using XF86 there's no need to keep the package around for CI. * Don't use new struct syntax * Fix indentation * Adjust Android/iOS fullscreen/maximized On Android and iOS we can assume single screen apps that are already fullscreen and maximized so there are a few methods that are implemented by just returning a fixed value or not doing anything. * Mark OSX/Win fullscreen/maximized unimplemented()! These would be safe as no-ops but we should make it explicit so there is more of an incentive to actually implement them.
2017-09-07 09:33:46 +01:00
}
fn set_title_inner(&self, title: &str) -> Result<VoidCookie<'_>, X11Error> {
let atoms = self.xconn.atoms();
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
let title = CString::new(title).expect("Window title contained null byte");
self.xconn
.change_property(
2018-05-29 07:48:47 -04:00
self.xwindow,
xproto::Atom::from(xproto::AtomEnum::WM_NAME),
xproto::Atom::from(xproto::AtomEnum::STRING),
xproto::PropMode::REPLACE,
title.as_bytes(),
)?
.ignore_error();
self.xconn.change_property(
self.xwindow,
atoms[_NET_WM_NAME],
atoms[UTF8_STRING],
xproto::PropMode::REPLACE,
title.as_bytes(),
)
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
}
2015-12-24 10:57:08 +01:00
2018-06-14 19:42:18 -04:00
#[inline]
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
pub fn set_title(&self, title: &str) {
self.set_title_inner(title)
.expect_then_ignore_error("Failed to set window title");
self.xconn
.flush_requests()
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
.expect("Failed to set window title");
}
#[inline]
pub fn set_transparent(&self, _transparent: bool) {}
#[inline]
pub fn set_blur(&self, _blur: bool) {}
fn set_decorations_inner(&self, decorations: bool) -> Result<VoidCookie<'_>, X11Error> {
2022-08-31 18:32:19 +02:00
self.shared_state_lock().is_decorated = decorations;
let mut hints = self.xconn.get_motif_hints(self.xwindow);
hints.set_decorations(decorations);
self.xconn.set_motif_hints(self.xwindow, &hints)
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
}
2018-06-14 19:42:18 -04:00
#[inline]
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
pub fn set_decorations(&self, decorations: bool) {
self.set_decorations_inner(decorations)
.expect_then_ignore_error("Failed to set decoration state");
self.xconn
.flush_requests()
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
.expect("Failed to set decoration state");
self.invalidate_cached_frame_extents();
}
#[inline]
pub fn is_decorated(&self) -> bool {
2022-08-31 18:32:19 +02:00
self.shared_state_lock().is_decorated
}
fn set_maximizable_inner(&self, maximizable: bool) -> Result<VoidCookie<'_>, X11Error> {
let mut hints = self.xconn.get_motif_hints(self.xwindow);
hints.set_maximizable(maximizable);
self.xconn.set_motif_hints(self.xwindow, &hints)
}
fn toggle_atom(&self, atom_name: AtomName, enable: bool) -> Result<VoidCookie<'_>, X11Error> {
let atoms = self.xconn.atoms();
let atom = atoms[atom_name];
self.set_netwm(enable.into(), (atom, 0, 0, 0))
}
fn set_window_level_inner(&self, level: WindowLevel) -> Result<VoidCookie<'_>, X11Error> {
self.toggle_atom(_NET_WM_STATE_ABOVE, level == WindowLevel::AlwaysOnTop)?
.ignore_error();
self.toggle_atom(_NET_WM_STATE_BELOW, level == WindowLevel::AlwaysOnBottom)
}
2018-06-14 19:42:18 -04:00
#[inline]
pub fn set_window_level(&self, level: WindowLevel) {
self.set_window_level_inner(level)
.expect_then_ignore_error("Failed to set window-level state");
self.xconn
.flush_requests()
.expect("Failed to set window-level state");
}
fn set_icon_inner(&self, icon: PlatformIcon) -> Result<VoidCookie<'_>, X11Error> {
let atoms = self.xconn.atoms();
let icon_atom = atoms[_NET_WM_ICON];
2018-05-07 17:36:21 -04:00
let data = icon.to_cardinals();
2018-05-29 07:48:47 -04:00
self.xconn.change_property(
self.xwindow,
2018-05-27 08:49:35 -04:00
icon_atom,
xproto::Atom::from(xproto::AtomEnum::CARDINAL),
xproto::PropMode::REPLACE,
2018-05-27 08:49:35 -04:00
data.as_slice(),
)
2018-05-07 17:36:21 -04:00
}
fn unset_icon_inner(&self) -> Result<VoidCookie<'_>, X11Error> {
let atoms = self.xconn.atoms();
let icon_atom = atoms[_NET_WM_ICON];
2018-05-07 17:36:21 -04:00
let empty_data: [util::Cardinal; 0] = [];
2018-05-29 07:48:47 -04:00
self.xconn.change_property(
self.xwindow,
2018-05-27 08:49:35 -04:00
icon_atom,
xproto::Atom::from(xproto::AtomEnum::CARDINAL),
xproto::PropMode::REPLACE,
2018-05-27 08:49:35 -04:00
&empty_data,
)
2018-05-07 17:36:21 -04:00
}
2018-06-14 19:42:18 -04:00
#[inline]
pub(crate) fn set_window_icon(&self, icon: Option<PlatformIcon>) {
2018-05-07 17:36:21 -04:00
match icon {
Some(icon) => self.set_icon_inner(icon),
None => self.unset_icon_inner(),
}
.expect_then_ignore_error("Failed to set icons");
self.xconn.flush_requests().expect("Failed to set icons");
2018-05-07 17:36:21 -04:00
}
2018-06-14 19:42:18 -04:00
#[inline]
pub fn set_visible(&self, visible: bool) {
2022-08-31 18:32:19 +02:00
let mut shared_state = self.shared_state_lock();
match (visible, shared_state.visibility) {
(true, Visibility::Yes) | (true, Visibility::YesWait) | (false, Visibility::No) => {
return
}
_ => (),
}
if visible {
self.xconn
.xcb_connection()
.map_window(self.xwindow)
.expect_then_ignore_error("Failed to call `xcb_map_window`");
self.xconn
.xcb_connection()
.configure_window(
self.xwindow,
&xproto::ConfigureWindowAux::new().stack_mode(xproto::StackMode::ABOVE),
)
.expect_then_ignore_error("Failed to call `xcb_configure_window`");
self.xconn
.flush_requests()
.expect("Failed to call XMapRaised");
shared_state.visibility = Visibility::YesWait;
} else {
self.xconn
.xcb_connection()
.unmap_window(self.xwindow)
.expect_then_ignore_error("Failed to call `xcb_unmap_window`");
self.xconn
.flush_requests()
.expect("Failed to call XUnmapWindow");
shared_state.visibility = Visibility::No;
}
}
#[inline]
pub fn is_visible(&self) -> Option<bool> {
2022-08-31 18:32:19 +02:00
Some(self.shared_state_lock().visibility == Visibility::Yes)
}
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
fn update_cached_frame_extents(&self) {
let extents = self
.xconn
.get_frame_extents_heuristic(self.xwindow, self.root);
self.shared_state_lock().frame_extents = Some(extents);
}
2018-06-14 19:42:18 -04:00
pub(crate) fn invalidate_cached_frame_extents(&self) {
self.shared_state_lock().frame_extents.take();
}
pub(crate) fn outer_position_physical(&self) -> (i32, i32) {
let extents = self.shared_state_lock().frame_extents.clone();
2018-06-14 19:42:18 -04:00
if let Some(extents) = extents {
let (x, y) = self.inner_position_physical();
extents.inner_pos_to_outer(x, y)
2018-06-14 19:42:18 -04:00
} else {
self.update_cached_frame_extents();
self.outer_position_physical()
2018-06-14 19:42:18 -04:00
}
}
2015-09-21 14:42:05 +02:00
#[inline]
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
let extents = self.shared_state_lock().frame_extents.clone();
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
if let Some(extents) = extents {
let (x, y) = self.inner_position_physical();
Ok(extents.inner_pos_to_outer(x, y).into())
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
} else {
self.update_cached_frame_extents();
self.outer_position()
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
}
}
pub(crate) fn inner_position_physical(&self) -> (i32, i32) {
// This should be okay to unwrap since the only error XTranslateCoordinates can return
// is BadWindow, and if the window handle is bad we have bigger problems.
self.xconn
.translate_coords(self.xwindow, self.root)
.map(|coords| (coords.dst_x.into(), coords.dst_y.into()))
.unwrap()
}
2018-06-14 19:42:18 -04:00
#[inline]
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError> {
Ok(self.inner_position_physical().into())
2018-06-14 19:42:18 -04:00
}
pub(crate) fn set_position_inner(
&self,
mut x: i32,
mut y: i32,
) -> Result<VoidCookie<'_>, X11Error> {
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
// There are a few WMs that set client area position rather than window position, so
// we'll translate for consistency.
if util::wm_name_is_one_of(&["Enlightenment", "FVWM"]) {
let extents = self.shared_state_lock().frame_extents.clone();
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
if let Some(extents) = extents {
x += cast_dimension_to_hint(extents.frame_extents.left);
y += cast_dimension_to_hint(extents.frame_extents.top);
X11: General cleanup (#491) * X11: General cleanup This is almost entirely internal changes, and as usual, doesn't actually fix any problems people have complained about. - `XSetInputFocus` can't be called before the window is visible. This was previously handled by looping (with a sleep) and querying for the window's state until it was visible. Now we use `XIfEvent`, which blocks until we receive `VisibilityNotify`. Note that this can't be replaced with an `XSync` (I tried). - We now call `XSync` at the end of window creation and check for errors, assuring that broken windows are never returned. When creating invisible windows, this is the only time the output buffer is flushed during the entire window creation process (AFAIK). For visible windows, `XIfEvent` will generally flush, but window creation has overall been reduced to the minimum number of flushes. - `check_errors().expect()` has been a common pattern throughout the backend, but it seems that people (myself included) didn't make a distinction between using it after synchronous requests and asynchronous requests. Now we only use it after async requests if we flush first, though this still isn't correct (since the request likely hasn't been processed yet). The only real solution (besides forcing a sync *every time*) is to handle asynchronous errors *asynchronously*. For future work, I plan on adding logging, though I don't plan on actually *handling* those errors; that's more of something to hope for in the hypothetical async/await XCB paradise. - We now flush whenever it makes sense to. `util::Flusher` was added to force contributors to be aware of the output buffer. - `Window::get_position`, `Window::get_inner_position`, `Window::get_inner_size`, and `Window::get_outer_size` previously all required *several* round-trips. On my machine, it took an average of around 80µs. They've now been reduced to one round-trip each, which reduces my measurement to 16µs. This was accomplished simply by caching the frame extents, which are expensive to calculate (due to various queries and heuristics), but change infrequently and predictably. I still recommend that application developers use these methods sparingly and generally prefer storing the values from `Resized`/`Moved`, as that's zero overhead. - The above change enabled me to change the `Moved` event to supply window positions, rather than client area positions. Additionally, we no longer generate `Moved` for real (as in, not synthetic) `ConfigureNotify` events. Real `ConfigureNotify` events contain positions relative to the parent window, which are typically constant and useless. Since that position would be completely different from the root-relative positions supplied by synthetic `ConfigureNotify` events (which are the vast majority of them), that meant real `ConfigureNotify` events would *always* be detected as the position having changed, so the resultant `Moved` was multiple levels of misleading. In practice, this meant a garbage `Moved` would be sent every time the window was resized; now a resize has to actually change the window's position to be accompanied by `Moved`. - Every time we processed an `XI_Enter` event, we would leak 4 bytes via `util::query_pointer` (`XIQueryPointer`). `XIButtonState` contains a dynamically-allocated mask field which we weren't freeing. As this event occurs with fairly high frequency, long-running applications could easily accumulate substantial leaks. `util::PointerState::drop` now takes care of this. - The `util` module has been split up into several sub-modules, as it was getting rather lengthy. This accounts for a significant part of this diff, unfortunately. - Atoms are now cached. Xlib caches them too, so `XInternAtom` wouldn't typically be a round-trip anyway, but the added complexity is negligible. - Switched from `std::sync::Mutex` to `parking_lot::Mutex` (within this backend). There appears to be no downside to this, but if anyone finds one, this would be easy to revert. - The WM name and supported hints are now global to the application, and are updated upon `ReparentNotify`, which should detect when the WM was replaced (assuming a reparenting WM was involved, that is). Previously, these values were per-window and would never update, meaning replacing the WM could potentially lead to (admittedly very minor) problems. - The result of `Window2::create_empty_cursor` will now only be used if it actually succeeds. - `Window2::load_cursor` no longer re-allocates the cursor name. - `util::lookup_utf8` previously allocated a 16-byte buffer on the heap. Now it allocates a 1024-byte buffer on the stack, and falls back to dynamic allocation if the buffer is too small. This base buffer size is admittedly gratuitous, but less so if you're using IME. - `with_c_str` was finally removed. - Added `util::Format` enum to help prevent goofs when dealing with format arguments. - `util::get_property`, something I added way back in my first winit PR, only calculated offsets correctly for `util::Format::Char`. This was concealed by the accomodating buffer size, as it would be very rare for the offset to be needed; however, testing with a buffer size of 1, `util::Format::Long` would read from the same offset multiple times, and `util::Format::Short` would miss data. This function now works correctly for all formats, relying on the simple fact that the offset increases by the buffer size on each iteration. We also account for the extra byte that `XGetWindowProperty` allocates at the end of the buffer, and copy data from the buffer instead of moving it and taking ownership of the pointer. - Drag and drop now reliably works in release mode. This is presumably related to the `util::get_property` changes. - `util::change_property` now exists, which should make it easier to add features in the future. - The `EventsLoop` device map is no longer in a mutex. - `XConnection` now implements `Debug`. - Valgrind no longer complains about anything related to winit (with either the system allocator or jemalloc, though "not having valgrind complain about jemalloc" isn't something to strive for). * X11: Add better diagnostics when initialization fails * X11: Handle XIQueryDevice failure * X11: Use correct types in error handler
2018-05-03 09:15:49 -04:00
} else {
self.update_cached_frame_extents();
2018-06-14 19:42:18 -04:00
return self.set_position_inner(x, y);
}
}
self.xconn
.xcb_connection()
.configure_window(self.xwindow, &xproto::ConfigureWindowAux::new().x(x).y(y))
.map_err(Into::into)
2018-06-14 19:42:18 -04:00
}
pub(crate) fn set_position_physical(&self, x: i32, y: i32) {
self.set_position_inner(x, y)
.expect_then_ignore_error("Failed to call `XMoveWindow`");
}
2015-09-21 14:42:05 +02:00
#[inline]
pub fn set_outer_position(&self, position: Position) {
let (x, y) = position.to_physical::<i32>(self.scale_factor()).into();
2018-06-14 19:42:18 -04:00
self.set_position_physical(x, y);
}
pub(crate) fn inner_size_physical(&self) -> (u32, u32) {
// This should be okay to unwrap since the only error XGetGeometry can return
// is BadWindow, and if the window handle is bad we have bigger problems.
self.xconn
.get_geometry(self.xwindow)
.map(|geo| (geo.width.into(), geo.height.into()))
.unwrap()
}
2015-09-21 14:42:05 +02:00
#[inline]
pub fn inner_size(&self) -> PhysicalSize<u32> {
self.inner_size_physical().into()
2018-06-14 19:42:18 -04:00
}
2015-09-21 14:42:05 +02:00
#[inline]
pub fn outer_size(&self) -> PhysicalSize<u32> {
2022-08-31 18:32:19 +02:00
let extents = self.shared_state_lock().frame_extents.clone();
2018-06-14 19:42:18 -04:00
if let Some(extents) = extents {
let (width, height) = self.inner_size_physical();
extents.inner_size_to_outer(width, height).into()
2018-06-14 19:42:18 -04:00
} else {
self.update_cached_frame_extents();
self.outer_size()
2018-06-14 19:42:18 -04:00
}
}
pub(crate) fn request_inner_size_physical(&self, width: u32, height: u32) {
self.xconn
.xcb_connection()
.configure_window(
2018-05-29 07:48:47 -04:00
self.xwindow,
&xproto::ConfigureWindowAux::new()
.width(width)
.height(height),
)
.expect_then_ignore_error("Failed to call `xcb_configure_window`");
self.xconn
.flush_requests()
.expect("Failed to call XResizeWindow");
// cursor_hittest needs to be reapplied after window resize
if self.shared_state_lock().cursor_hittest {
let _ = self.set_cursor_hittest(true);
}
}
2018-06-14 19:42:18 -04:00
#[inline]
pub fn request_inner_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
let scale_factor = self.scale_factor();
let size = size.to_physical::<u32>(scale_factor).into();
2022-08-31 18:32:19 +02:00
if !self.shared_state_lock().is_resizable {
self.update_normal_hints(|normal_hints| {
normal_hints.min_size = Some(size);
normal_hints.max_size = Some(size);
})
.expect("Failed to call `XSetWMNormalHints`");
}
self.request_inner_size_physical(size.0 as u32, size.1 as u32);
None
2018-06-14 19:42:18 -04:00
}
fn update_normal_hints<F>(&self, callback: F) -> Result<(), X11Error>
where
F: FnOnce(&mut WmSizeHints),
{
let mut normal_hints = WmSizeHints::get(
self.xconn.xcb_connection(),
self.xwindow as xproto::Window,
xproto::AtomEnum::WM_NORMAL_HINTS,
)?
.reply()?;
2018-06-14 19:42:18 -04:00
callback(&mut normal_hints);
normal_hints
.set(
self.xconn.xcb_connection(),
self.xwindow as xproto::Window,
xproto::AtomEnum::WM_NORMAL_HINTS,
)?
.ignore_error();
Ok(())
2018-06-14 19:42:18 -04:00
}
pub(crate) fn set_min_inner_size_physical(&self, dimensions: Option<(u32, u32)>) {
self.update_normal_hints(|normal_hints| {
normal_hints.min_size =
dimensions.map(|(w, h)| (cast_dimension_to_hint(w), cast_dimension_to_hint(h)))
})
.expect("Failed to call `XSetWMNormalHints`");
2018-06-14 19:42:18 -04:00
}
2018-06-14 19:42:18 -04:00
#[inline]
pub fn set_min_inner_size(&self, dimensions: Option<Size>) {
2022-08-31 18:32:19 +02:00
self.shared_state_lock().min_inner_size = dimensions;
let physical_dimensions =
dimensions.map(|dimensions| dimensions.to_physical::<u32>(self.scale_factor()).into());
self.set_min_inner_size_physical(physical_dimensions);
2018-06-14 19:42:18 -04:00
}
pub(crate) fn set_max_inner_size_physical(&self, dimensions: Option<(u32, u32)>) {
self.update_normal_hints(|normal_hints| {
normal_hints.max_size =
dimensions.map(|(w, h)| (cast_dimension_to_hint(w), cast_dimension_to_hint(h)))
})
.expect("Failed to call `XSetWMNormalHints`");
}
2018-06-14 19:42:18 -04:00
#[inline]
pub fn set_max_inner_size(&self, dimensions: Option<Size>) {
2022-08-31 18:32:19 +02:00
self.shared_state_lock().max_inner_size = dimensions;
let physical_dimensions =
dimensions.map(|dimensions| dimensions.to_physical::<u32>(self.scale_factor()).into());
self.set_max_inner_size_physical(physical_dimensions);
}
#[inline]
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
WmSizeHints::get(
self.xconn.xcb_connection(),
self.xwindow as xproto::Window,
xproto::AtomEnum::WM_NORMAL_HINTS,
)
.ok()
.and_then(|cookie| cookie.reply().ok())
.and_then(|hints| hints.size_increment)
.map(|(width, height)| (width as u32, height as u32).into())
}
#[inline]
pub fn set_resize_increments(&self, increments: Option<Size>) {
self.shared_state_lock().resize_increments = increments;
let physical_increments =
increments.map(|increments| cast_size_to_hint(increments, self.scale_factor()));
self.update_normal_hints(|hints| hints.size_increment = physical_increments)
.expect("Failed to call `XSetWMNormalHints`");
}
2018-06-14 19:42:18 -04:00
pub(crate) fn adjust_for_dpi(
&self,
old_scale_factor: f64,
new_scale_factor: f64,
width: u32,
height: u32,
shared_state: &SharedState,
) -> (u32, u32) {
let scale_factor = new_scale_factor / old_scale_factor;
2018-06-14 19:42:18 -04:00
self.update_normal_hints(|normal_hints| {
let dpi_adjuster = |size: Size| -> (i32, i32) { cast_size_to_hint(size, scale_factor) };
let max_size = shared_state.max_inner_size.map(dpi_adjuster);
let min_size = shared_state.min_inner_size.map(dpi_adjuster);
let resize_increments = shared_state.resize_increments.map(dpi_adjuster);
let base_size = shared_state.base_size.map(dpi_adjuster);
normal_hints.max_size = max_size;
normal_hints.min_size = min_size;
normal_hints.size_increment = resize_increments;
normal_hints.base_size = base_size;
})
.expect("Failed to update normal hints");
let new_width = (width as f64 * scale_factor).round() as u32;
let new_height = (height as f64 * scale_factor).round() as u32;
(new_width, new_height)
}
pub fn set_resizable(&self, resizable: bool) {
if util::wm_name_is_one_of(&["Xfwm4"]) {
// Making the window unresizable on Xfwm prevents further changes to `WM_NORMAL_HINTS` from being detected.
// This makes it impossible for resizing to be re-enabled, and also breaks DPI scaling. As such, we choose
// the lesser of two evils and do nothing.
warn!("To avoid a WM bug, disabling resizing has no effect on Xfwm4");
return;
}
2018-06-14 19:42:18 -04:00
let (min_size, max_size) = if resizable {
2022-08-31 18:32:19 +02:00
let shared_state_lock = self.shared_state_lock();
(
shared_state_lock.min_inner_size,
shared_state_lock.max_inner_size,
)
} else {
let window_size = Some(Size::from(self.inner_size()));
(window_size, window_size)
2018-06-14 19:42:18 -04:00
};
2022-08-31 18:32:19 +02:00
self.shared_state_lock().is_resizable = resizable;
2018-06-14 19:42:18 -04:00
self.set_maximizable_inner(resizable)
.expect_then_ignore_error("Failed to call `XSetWMNormalHints`");
let scale_factor = self.scale_factor();
let min_inner_size = min_size.map(|size| cast_size_to_hint(size, scale_factor));
let max_inner_size = max_size.map(|size| cast_size_to_hint(size, scale_factor));
2018-06-14 19:42:18 -04:00
self.update_normal_hints(|normal_hints| {
normal_hints.min_size = min_inner_size;
normal_hints.max_size = max_inner_size;
})
.expect("Failed to call `XSetWMNormalHints`");
}
#[inline]
pub fn is_resizable(&self) -> bool {
2022-08-31 18:32:19 +02:00
self.shared_state_lock().is_resizable
}
#[inline]
pub fn set_enabled_buttons(&self, _buttons: WindowButtons) {}
#[inline]
pub fn enabled_buttons(&self) -> WindowButtons {
WindowButtons::all()
}
#[inline]
pub fn xlib_display(&self) -> *mut c_void {
2018-05-29 07:48:47 -04:00
self.xconn.display as _
}
#[inline]
pub fn xlib_window(&self) -> c_ulong {
self.xwindow as ffi::Window
}
2018-06-14 19:42:18 -04:00
#[inline]
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
2022-08-31 18:32:19 +02:00
let old_cursor = replace(&mut *self.cursor.lock().unwrap(), cursor);
#[allow(clippy::mutex_atomic)]
if cursor != old_cursor && *self.cursor_visible.lock().unwrap() {
self.xconn.set_cursor_icon(self.xwindow, Some(cursor));
}
}
2018-06-14 19:42:18 -04:00
#[inline]
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError> {
2022-08-31 18:32:19 +02:00
let mut grabbed_lock = self.cursor_grabbed_mode.lock().unwrap();
if mode == *grabbed_lock {
return Ok(());
}
// We ungrab before grabbing to prevent passive grabs from causing `AlreadyGrabbed`.
// Therefore, this is common to both codepaths.
self.xconn
.xcb_connection()
.ungrab_pointer(x11rb::CURRENT_TIME)
.expect_then_ignore_error("Failed to call `xcb_ungrab_pointer`");
2016-03-21 18:42:54 +00:00
let result = match mode {
CursorGrabMode::None => self.xconn.flush_requests().map_err(|err| {
ExternalError::Os(os_error!(OsError::XError(X11Error::Xlib(err).into())))
}),
CursorGrabMode::Confined => {
let result = {
self.xconn
.xcb_connection()
.grab_pointer(
true as _,
self.xwindow,
xproto::EventMask::BUTTON_PRESS
| xproto::EventMask::BUTTON_RELEASE
| xproto::EventMask::ENTER_WINDOW
| xproto::EventMask::LEAVE_WINDOW
| xproto::EventMask::POINTER_MOTION
| xproto::EventMask::POINTER_MOTION_HINT
| xproto::EventMask::BUTTON1_MOTION
| xproto::EventMask::BUTTON2_MOTION
| xproto::EventMask::BUTTON3_MOTION
| xproto::EventMask::BUTTON4_MOTION
| xproto::EventMask::BUTTON5_MOTION
| xproto::EventMask::KEYMAP_STATE,
xproto::GrabMode::ASYNC,
xproto::GrabMode::ASYNC,
self.xwindow,
0u32,
x11rb::CURRENT_TIME,
)
.expect("Failed to call `grab_pointer`")
.reply()
.expect("Failed to receive reply from `grab_pointer`")
};
match result.status {
xproto::GrabStatus::SUCCESS => Ok(()),
xproto::GrabStatus::ALREADY_GRABBED => {
Err("Cursor could not be confined: already confined by another client")
}
xproto::GrabStatus::INVALID_TIME => {
Err("Cursor could not be confined: invalid time")
}
xproto::GrabStatus::NOT_VIEWABLE => {
Err("Cursor could not be confined: confine location not viewable")
}
xproto::GrabStatus::FROZEN => {
Err("Cursor could not be confined: frozen by another client")
}
_ => unreachable!(),
2019-06-24 12:14:55 -04:00
}
.map_err(|err| ExternalError::Os(os_error!(OsError::Misc(err))))
}
CursorGrabMode::Locked => {
return Err(ExternalError::NotSupported(NotSupportedError::new()));
}
};
if result.is_ok() {
*grabbed_lock = mode;
2016-03-21 18:42:54 +00:00
}
result
}
#[inline]
pub fn set_cursor_visible(&self, visible: bool) {
2022-08-31 18:32:19 +02:00
#[allow(clippy::mutex_atomic)]
let mut visible_lock = self.cursor_visible.lock().unwrap();
if visible == *visible_lock {
return;
}
let cursor = if visible {
2022-08-31 18:32:19 +02:00
Some(*self.cursor.lock().unwrap())
} else {
None
};
*visible_lock = visible;
drop(visible_lock);
self.xconn.set_cursor_icon(self.xwindow, cursor);
}
2018-06-14 19:42:18 -04:00
#[inline]
pub fn scale_factor(&self) -> f64 {
self.shared_state_lock().last_monitor.scale_factor
}
pub fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), ExternalError> {
{
self.xconn
.xcb_connection()
.warp_pointer(x11rb::NONE, self.xwindow, 0, 0, 0, 0, x as _, y as _)
.map_err(|e| {
ExternalError::Os(os_error!(OsError::XError(X11Error::from(e).into())))
})?;
self.xconn.flush_requests().map_err(|e| {
ExternalError::Os(os_error!(OsError::XError(X11Error::Xlib(e).into())))
})
}
}
2018-06-14 19:42:18 -04:00
#[inline]
pub fn set_cursor_position(&self, position: Position) -> Result<(), ExternalError> {
let (x, y) = position.to_physical::<i32>(self.scale_factor()).into();
2018-06-14 19:42:18 -04:00
self.set_cursor_position_physical(x, y)
}
#[inline]
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError> {
let mut rectangles: Vec<Rectangle> = Vec::new();
if hittest {
let size = self.inner_size();
rectangles.push(Rectangle {
x: 0,
y: 0,
width: size.width as u16,
height: size.height as u16,
})
}
let region = RegionWrapper::create_region(self.xconn.xcb_connection(), &rectangles)
.map_err(|_e| ExternalError::Ignored)?;
self.xconn
.xcb_connection()
.xfixes_set_window_shape_region(self.xwindow, SK::INPUT, 0, 0, region.region())
.map_err(|_e| ExternalError::Ignored)?;
self.shared_state_lock().cursor_hittest = hittest;
Ok(())
}
/// Moves the window while it is being dragged.
pub fn drag_window(&self) -> Result<(), ExternalError> {
self.drag_initiate(util::MOVERESIZE_MOVE)
}
/// Resizes the window while it is being dragged.
pub fn drag_resize_window(&self, direction: ResizeDirection) -> Result<(), ExternalError> {
self.drag_initiate(match direction {
ResizeDirection::East => util::MOVERESIZE_RIGHT,
ResizeDirection::North => util::MOVERESIZE_TOP,
ResizeDirection::NorthEast => util::MOVERESIZE_TOPRIGHT,
ResizeDirection::NorthWest => util::MOVERESIZE_TOPLEFT,
ResizeDirection::South => util::MOVERESIZE_BOTTOM,
ResizeDirection::SouthEast => util::MOVERESIZE_BOTTOMRIGHT,
ResizeDirection::SouthWest => util::MOVERESIZE_BOTTOMLEFT,
ResizeDirection::West => util::MOVERESIZE_LEFT,
})
}
/// Initiates a drag operation while the left mouse button is pressed.
fn drag_initiate(&self, action: isize) -> Result<(), ExternalError> {
let pointer = self
.xconn
.query_pointer(self.xwindow, util::VIRTUAL_CORE_POINTER)
.map_err(|err| ExternalError::Os(os_error!(OsError::XError(err.into()))))?;
let window = self.inner_position().map_err(ExternalError::NotSupported)?;
let atoms = self.xconn.atoms();
let message = atoms[_NET_WM_MOVERESIZE];
// we can't use `set_cursor_grab(false)` here because it doesn't run `XUngrabPointer`
// if the cursor isn't currently grabbed
2022-08-31 18:32:19 +02:00
let mut grabbed_lock = self.cursor_grabbed_mode.lock().unwrap();
self.xconn
.xcb_connection()
.ungrab_pointer(x11rb::CURRENT_TIME)
.map_err(|err| {
ExternalError::Os(os_error!(OsError::XError(X11Error::from(err).into())))
})?
.ignore_error();
self.xconn.flush_requests().map_err(|err| {
ExternalError::Os(os_error!(OsError::XError(X11Error::Xlib(err).into())))
})?;
*grabbed_lock = CursorGrabMode::None;
// we keep the lock until we are done
self.xconn
.send_client_msg(
self.xwindow,
self.root,
message,
Some(
xproto::EventMask::SUBSTRUCTURE_REDIRECT
| xproto::EventMask::SUBSTRUCTURE_NOTIFY,
),
[
(window.x as u32 + pointer.win_x as u32),
(window.y as u32 + pointer.win_y as u32),
action.try_into().unwrap(),
1, // Button 1
1,
],
)
.map_err(|err| ExternalError::Os(os_error!(OsError::XError(err.into()))))?;
self.xconn.flush_requests().map_err(|err| {
ExternalError::Os(os_error!(OsError::XError(X11Error::Xlib(err).into())))
})
}
#[inline]
pub fn set_ime_cursor_area(&self, spot: Position, _size: Size) {
let (x, y) = spot.to_physical::<i32>(self.scale_factor()).into();
let _ = self.ime_sender.lock().unwrap().send(ImeRequest::Position(
self.xwindow as ffi::Window,
x,
y,
));
2018-06-14 19:42:18 -04:00
}
#[inline]
pub fn set_ime_allowed(&self, allowed: bool) {
let _ = self
.ime_sender
.lock()
2022-08-31 18:32:19 +02:00
.unwrap()
.send(ImeRequest::Allow(self.xwindow as ffi::Window, allowed));
2018-06-14 19:42:18 -04:00
}
#[inline]
pub fn set_ime_purpose(&self, _purpose: ImePurpose) {}
2021-05-19 18:39:53 +02:00
#[inline]
pub fn focus_window(&self) {
let atoms = self.xconn.atoms();
let state_atom = atoms[WM_STATE];
let state_type_atom = atoms[CARD32];
2021-05-19 18:39:53 +02:00
let is_minimized = if let Ok(state) =
self.xconn
.get_property(self.xwindow, state_atom, state_type_atom)
{
state.contains(&(ffi::IconicState as c_ulong))
} else {
false
};
2022-08-31 18:32:19 +02:00
let is_visible = match self.shared_state_lock().visibility {
2021-05-19 18:39:53 +02:00
Visibility::Yes => true,
Visibility::YesWait | Visibility::No => false,
};
if is_visible && !is_minimized {
self.xconn
.send_client_msg(
self.xwindow,
self.root,
atoms[_NET_ACTIVE_WINDOW],
Some(
xproto::EventMask::SUBSTRUCTURE_REDIRECT
| xproto::EventMask::SUBSTRUCTURE_NOTIFY,
),
[1, x11rb::CURRENT_TIME, 0, 0, 0],
)
.expect_then_ignore_error("Failed to send client message");
if let Err(e) = self.xconn.flush_requests() {
2021-05-19 18:39:53 +02:00
log::error!(
"`flush` returned an error when focusing the window. Error was: {}",
e
);
}
}
}
#[inline]
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>) {
let mut wm_hints =
WmHints::get(self.xconn.xcb_connection(), self.xwindow as xproto::Window)
.ok()
.and_then(|cookie| cookie.reply().ok())
.unwrap_or_default();
wm_hints.urgent = request_type.is_some();
wm_hints
.set(self.xconn.xcb_connection(), self.xwindow as xproto::Window)
.expect_then_ignore_error("Failed to set WM hints");
}
#[inline]
pub(crate) fn generate_activation_token(&self) -> Result<String, X11Error> {
// Get the title from the WM_NAME property.
let atoms = self.xconn.atoms();
let title = {
let title_bytes = self
.xconn
.get_property(self.xwindow, atoms[_NET_WM_NAME], atoms[UTF8_STRING])
.expect("Failed to get title");
String::from_utf8(title_bytes).expect("Bad title")
};
// Get the activation token and then put it in the event queue.
let token = self.xconn.request_activation_token(&title)?;
Ok(token)
}
#[inline]
pub fn request_activation_token(&self) -> Result<AsyncRequestSerial, NotSupportedError> {
let serial = AsyncRequestSerial::get();
self.activation_sender
.send((self.id(), serial))
.expect("activation token channel should never be closed");
Ok(serial)
}
#[inline]
pub fn id(&self) -> WindowId {
WindowId(self.xwindow as _)
}
#[inline]
pub fn request_redraw(&self) {
self.redraw_sender
.send(WindowId(self.xwindow as _))
.unwrap();
}
#[inline]
pub fn pre_present_notify(&self) {
// TODO timer
}
#[inline]
pub fn raw_window_handle(&self) -> RawWindowHandle {
let mut window_handle = XlibWindowHandle::empty();
window_handle.window = self.xlib_window();
window_handle.visual_id = self.visual as c_ulong;
RawWindowHandle::Xlib(window_handle)
}
#[inline]
pub fn raw_display_handle(&self) -> RawDisplayHandle {
let mut display_handle = XlibDisplayHandle::empty();
display_handle.display = self.xlib_display();
display_handle.screen = self.screen_id;
RawDisplayHandle::Xlib(display_handle)
}
#[inline]
pub fn theme(&self) -> Option<Theme> {
None
}
pub fn set_content_protected(&self, _protected: bool) {}
#[inline]
pub fn has_focus(&self) -> bool {
self.shared_state_lock().has_focus
}
pub fn title(&self) -> String {
String::new()
}
}
/// Cast a dimension value into a hinted dimension for `WmSizeHints`, clamping if too large.
fn cast_dimension_to_hint(val: u32) -> i32 {
val.try_into().unwrap_or(i32::MAX)
}
/// Use the above strategy to cast a physical size into a hinted size.
fn cast_physical_size_to_hint(size: PhysicalSize<u32>) -> (i32, i32) {
let PhysicalSize { width, height } = size;
(
cast_dimension_to_hint(width),
cast_dimension_to_hint(height),
)
}
/// Use the above strategy to cast a size into a hinted size.
fn cast_size_to_hint(size: Size, scale_factor: f64) -> (i32, i32) {
match size {
Size::Physical(size) => cast_physical_size_to_hint(size),
Size::Logical(size) => size.to_physical::<i32>(scale_factor).into(),
}
}