winit-sonoma-fix/src/platform_impl/linux/x11/xdisplay.rs

156 lines
4.1 KiB
Rust
Raw Normal View History

use std::ptr;
use std::fmt;
use std::error::Error;
use libc;
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 23:15:49 +10:00
use parking_lot::Mutex;
use super::ffi;
/// A connection to an X server.
pub struct XConnection {
pub xlib: ffi::Xlib,
/// Exposes XRandR functions from version < 1.5
pub xrandr: ffi::Xrandr_2_2_0,
/// Exposes XRandR functions from version = 1.5
pub xrandr_1_5: Option<ffi::Xrandr>,
pub xcursor: ffi::Xcursor,
pub xinput2: ffi::XInput2,
pub xlib_xcb: ffi::Xlib_xcb,
pub display: *mut ffi::Display,
2015-12-24 20:57:08 +11:00
pub latest_error: Mutex<Option<XError>>,
}
unsafe impl Send for XConnection {}
unsafe impl Sync for XConnection {}
2015-12-15 09:05:07 +11:00
pub type XErrorHandler = Option<unsafe extern fn(*mut ffi::Display, *mut ffi::XErrorEvent) -> libc::c_int>;
impl XConnection {
2015-12-15 09:05:07 +11:00
pub fn new(error_handler: XErrorHandler) -> Result<XConnection, XNotSupported> {
// opening the libraries
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 23:15:49 +10:00
let xlib = ffi::Xlib::open()?;
let xcursor = ffi::Xcursor::open()?;
let xrandr = ffi::Xrandr_2_2_0::open()?;
let xrandr_1_5 = ffi::Xrandr::open().ok();
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 23:15:49 +10:00
let xinput2 = ffi::XInput2::open()?;
let xlib_xcb = ffi::Xlib_xcb::open()?;
unsafe { (xlib.XInitThreads)() };
2015-12-15 09:05:07 +11:00
unsafe { (xlib.XSetErrorHandler)(error_handler) };
// calling XOpenDisplay
let display = unsafe {
let display = (xlib.XOpenDisplay)(ptr::null());
if display.is_null() {
return Err(XNotSupported::XOpenDisplayFailed);
}
display
};
Ok(XConnection {
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 23:15:49 +10:00
xlib,
xrandr,
xrandr_1_5,
xcursor,
xinput2,
xlib_xcb,
display,
2015-12-24 20:57:08 +11:00
latest_error: Mutex::new(None),
})
}
2015-12-24 20:57:08 +11:00
/// Checks whether an error has been triggered by the previous function calls.
#[inline]
pub fn check_errors(&self) -> Result<(), XError> {
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 23:15:49 +10:00
let error = self.latest_error.lock().take();
2015-12-24 20:57:08 +11:00
if let Some(error) = error {
Err(error)
} else {
Ok(())
}
}
/// Ignores any previous error.
#[inline]
pub fn ignore_error(&self) {
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 23:15:49 +10:00
*self.latest_error.lock() = None;
}
}
impl fmt::Debug for XConnection {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.display.fmt(f)
2015-12-24 20:57:08 +11:00
}
}
impl Drop for XConnection {
2015-09-21 22:42:05 +10:00
#[inline]
fn drop(&mut self) {
unsafe { (self.xlib.XCloseDisplay)(self.display) };
}
}
2015-12-24 20:57:08 +11:00
/// Error triggered by xlib.
#[derive(Debug, Clone)]
pub struct XError {
pub description: String,
pub error_code: u8,
pub request_code: u8,
pub minor_code: u8,
}
impl Error for XError {
#[inline]
fn description(&self) -> &str {
&self.description
}
}
impl fmt::Display for XError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(formatter, "X error: {} (code: {}, request code: {}, minor code: {})",
self.description, self.error_code, self.request_code, self.minor_code)
}
}
/// Error returned if this system doesn't have XLib or can't create an X connection.
#[derive(Clone, Debug)]
pub enum XNotSupported {
/// Failed to load one or several shared libraries.
LibraryOpenError(ffi::OpenError),
/// Connecting to the X server with `XOpenDisplay` failed.
XOpenDisplayFailed, // TODO: add better message
}
impl From<ffi::OpenError> for XNotSupported {
2015-09-21 22:42:05 +10:00
#[inline]
fn from(err: ffi::OpenError) -> XNotSupported {
XNotSupported::LibraryOpenError(err)
}
}
impl Error for XNotSupported {
2015-09-21 22:42:05 +10:00
#[inline]
fn description(&self) -> &str {
match *self {
XNotSupported::LibraryOpenError(_) => "Failed to load one of xlib's shared libraries",
XNotSupported::XOpenDisplayFailed => "Failed to open connection to X server",
}
}
2015-09-21 22:42:05 +10:00
#[inline]
fn cause(&self) -> Option<&Error> {
match *self {
XNotSupported::LibraryOpenError(ref err) => Some(err),
_ => None
}
}
}
impl fmt::Display for XNotSupported {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
formatter.write_str(self.description())
}
}