2018-07-16 23:25:27 +09:00
|
|
|
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
|
2015-04-24 09:51:23 +02:00
|
|
|
|
2018-05-07 17:36:21 -04:00
|
|
|
pub mod ffi;
|
|
|
|
|
mod events;
|
|
|
|
|
mod monitor;
|
|
|
|
|
mod window;
|
|
|
|
|
mod xdisplay;
|
|
|
|
|
mod dnd;
|
|
|
|
|
mod ime;
|
2018-05-20 10:47:22 -04:00
|
|
|
pub mod util;
|
2019-04-27 18:06:51 +02:00
|
|
|
mod event_processor;
|
2018-05-07 17:36:21 -04:00
|
|
|
|
2019-02-05 10:30:33 -05:00
|
|
|
pub use self::monitor::MonitorHandle;
|
2018-05-29 07:48:47 -04:00
|
|
|
pub use self::window::UnownedWindow;
|
2015-12-24 10:57:08 +01:00
|
|
|
pub use self::xdisplay::{XConnection, XNotSupported, XError};
|
2015-04-24 14:22:57 +02:00
|
|
|
|
2019-04-27 18:06:51 +02:00
|
|
|
use std::{mem, slice};
|
2018-04-05 14:58:10 -04:00
|
|
|
use std::cell::RefCell;
|
2019-04-27 18:06:51 +02:00
|
|
|
use std::collections::{VecDeque, HashMap, HashSet};
|
2017-04-22 13:52:35 -07:00
|
|
|
use std::ffi::CStr;
|
2018-05-29 07:48:47 -04:00
|
|
|
use std::ops::Deref;
|
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 std::os::raw::*;
|
2019-04-27 18:06:51 +02:00
|
|
|
use std::rc::Rc;
|
|
|
|
|
use std::sync::{Arc, mpsc, Weak, Mutex};
|
2017-04-22 13:52:35 -07:00
|
|
|
|
2018-04-10 22:18:30 -04:00
|
|
|
use libc::{self, setlocale, LC_CTYPE};
|
2017-03-03 21:41:51 +01:00
|
|
|
|
2019-04-27 18:06:51 +02:00
|
|
|
use event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW};
|
|
|
|
|
use event::{WindowEvent, Event};
|
2019-02-05 10:30:33 -05:00
|
|
|
use platform_impl::PlatformSpecificWindowBuilderAttributes;
|
2019-04-27 18:06:51 +02:00
|
|
|
use platform_impl::platform::sticky_exit_callback;
|
|
|
|
|
use window::{CreationError, WindowAttributes};
|
2017-12-13 06:22:03 -05:00
|
|
|
use self::dnd::{Dnd, DndState};
|
2018-04-10 22:18:30 -04:00
|
|
|
use self::ime::{ImeReceiver, ImeSender, ImeCreationError, Ime};
|
2019-04-27 18:06:51 +02:00
|
|
|
use self::event_processor::EventProcessor;
|
2017-03-03 21:41:51 +01:00
|
|
|
|
2019-04-27 18:06:51 +02:00
|
|
|
pub struct EventLoopWindowTarget<T> {
|
2018-05-29 07:48:47 -04:00
|
|
|
xconn: Arc<XConnection>,
|
2017-04-22 13:52:35 -07:00
|
|
|
wm_delete_window: ffi::Atom,
|
2018-04-10 22:18:30 -04:00
|
|
|
ime_sender: ImeSender,
|
2019-04-27 18:06:51 +02:00
|
|
|
root: ffi::Window,
|
2018-04-10 22:18:30 -04:00
|
|
|
ime: RefCell<Ime>,
|
2018-05-29 07:48:47 -04:00
|
|
|
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
|
2019-04-27 18:06:51 +02:00
|
|
|
pending_redraws: Arc<Mutex<HashSet<WindowId>>>,
|
|
|
|
|
_marker: ::std::marker::PhantomData<T>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct EventLoop<T: 'static> {
|
|
|
|
|
inner_loop: ::calloop::EventLoop<()>,
|
|
|
|
|
_x11_source: ::calloop::Source<::calloop::generic::Generic<::calloop::generic::EventedRawFd>>,
|
|
|
|
|
_user_source: ::calloop::Source<::calloop::channel::Channel<T>>,
|
|
|
|
|
pending_user_events: Rc<RefCell<VecDeque<T>>>,
|
|
|
|
|
user_sender: ::calloop::channel::Sender<T>,
|
|
|
|
|
pending_events: Rc<RefCell<VecDeque<Event<T>>>>,
|
|
|
|
|
target: Rc<RootELW<T>>
|
2017-03-03 21:41:51 +01:00
|
|
|
}
|
|
|
|
|
|
2017-10-25 11:03:57 -07:00
|
|
|
#[derive(Clone)]
|
2019-04-27 18:06:51 +02:00
|
|
|
pub struct EventLoopProxy<T: 'static> {
|
|
|
|
|
user_sender: ::calloop::channel::Sender<T>,
|
2017-05-25 23:19:13 +10:00
|
|
|
}
|
|
|
|
|
|
2019-04-27 18:06:51 +02:00
|
|
|
impl<T: 'static> EventLoop<T> {
|
|
|
|
|
pub fn new(xconn: Arc<XConnection>) -> EventLoop<T> {
|
2018-05-29 07:48:47 -04:00
|
|
|
let root = unsafe { (xconn.xlib.XDefaultRootWindow)(xconn.display) };
|
2018-05-14 08:14:57 -04:00
|
|
|
|
2018-05-29 07:48:47 -04:00
|
|
|
let wm_delete_window = unsafe { xconn.get_atom_unchecked(b"WM_DELETE_WINDOW\0") };
|
2017-04-22 13:52:35 -07:00
|
|
|
|
2018-05-29 07:48:47 -04:00
|
|
|
let dnd = Dnd::new(Arc::clone(&xconn))
|
2017-12-13 06:22:03 -05:00
|
|
|
.expect("Failed to call XInternAtoms when initializing drag and drop");
|
|
|
|
|
|
2018-04-10 22:18:30 -04:00
|
|
|
let (ime_sender, ime_receiver) = mpsc::channel();
|
|
|
|
|
// Input methods will open successfully without setting the locale, but it won't be
|
|
|
|
|
// possible to actually commit pre-edit sequences.
|
|
|
|
|
unsafe { setlocale(LC_CTYPE, b"\0".as_ptr() as *const _); }
|
|
|
|
|
let ime = RefCell::new({
|
2018-05-29 07:48:47 -04:00
|
|
|
let result = Ime::new(Arc::clone(&xconn));
|
2018-04-10 22:18:30 -04:00
|
|
|
if let Err(ImeCreationError::OpenFailure(ref state)) = result {
|
|
|
|
|
panic!(format!("Failed to open input method: {:#?}", state));
|
|
|
|
|
}
|
|
|
|
|
result.expect("Failed to set input method destruction callback")
|
|
|
|
|
});
|
2018-04-05 14:58:10 -04:00
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
let randr_event_offset = xconn.select_xrandr_input(root)
|
2018-05-14 08:14:57 -04:00
|
|
|
.expect("Failed to query XRandR extension");
|
|
|
|
|
|
2017-04-22 13:52:35 -07:00
|
|
|
let xi2ext = unsafe {
|
|
|
|
|
let mut result = XExtension {
|
|
|
|
|
opcode: mem::uninitialized(),
|
|
|
|
|
first_event_id: mem::uninitialized(),
|
|
|
|
|
first_error_id: mem::uninitialized(),
|
|
|
|
|
};
|
2018-05-29 07:48:47 -04:00
|
|
|
let res = (xconn.xlib.XQueryExtension)(
|
|
|
|
|
xconn.display,
|
2017-04-22 13:52:35 -07:00
|
|
|
b"XInputExtension\0".as_ptr() as *const c_char,
|
|
|
|
|
&mut result.opcode as *mut c_int,
|
|
|
|
|
&mut result.first_event_id as *mut c_int,
|
|
|
|
|
&mut result.first_error_id as *mut c_int);
|
|
|
|
|
if res == ffi::False {
|
|
|
|
|
panic!("X server missing XInput extension");
|
|
|
|
|
}
|
|
|
|
|
result
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
|
let mut xinput_major_ver = ffi::XI_2_Major;
|
|
|
|
|
let mut xinput_minor_ver = ffi::XI_2_Minor;
|
2018-05-29 07:48:47 -04:00
|
|
|
if (xconn.xinput2.XIQueryVersion)(
|
|
|
|
|
xconn.display,
|
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
|
|
|
&mut xinput_major_ver,
|
|
|
|
|
&mut xinput_minor_ver,
|
|
|
|
|
) != ffi::Success as libc::c_int {
|
|
|
|
|
panic!(
|
|
|
|
|
"X server has XInput extension {}.{} but does not support XInput2",
|
|
|
|
|
xinput_major_ver,
|
|
|
|
|
xinput_minor_ver,
|
|
|
|
|
);
|
2017-04-22 13:52:35 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-29 07:48:47 -04:00
|
|
|
xconn.update_cached_wm_info(root);
|
2017-04-22 13:52:35 -07:00
|
|
|
|
2019-04-27 18:06:51 +02:00
|
|
|
let target = Rc::new(RootELW{
|
|
|
|
|
p: super::EventLoopWindowTarget::X(EventLoopWindowTarget {
|
|
|
|
|
ime,
|
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
|
|
|
root,
|
2019-04-27 18:06:51 +02:00
|
|
|
windows: Default::default(),
|
|
|
|
|
_marker: ::std::marker::PhantomData,
|
|
|
|
|
ime_sender,
|
|
|
|
|
xconn,
|
|
|
|
|
wm_delete_window,
|
|
|
|
|
pending_redraws: Default::default(),
|
|
|
|
|
}),
|
|
|
|
|
_marker: ::std::marker::PhantomData
|
|
|
|
|
});
|
2017-06-17 22:59:56 +10:00
|
|
|
|
2019-04-27 18:06:51 +02:00
|
|
|
// A calloop event loop to drive us
|
|
|
|
|
let inner_loop = ::calloop::EventLoop::new().unwrap();
|
|
|
|
|
|
|
|
|
|
// Handle user events
|
|
|
|
|
let pending_user_events = Rc::new(RefCell::new(VecDeque::new()));
|
|
|
|
|
let pending_user_events2 = pending_user_events.clone();
|
|
|
|
|
|
|
|
|
|
let (user_sender, user_channel) = ::calloop::channel::channel();
|
|
|
|
|
|
|
|
|
|
let _user_source = inner_loop.handle().insert_source(user_channel, move |evt, &mut()| {
|
|
|
|
|
if let ::calloop::channel::Event::Msg(msg) = evt {
|
|
|
|
|
pending_user_events2.borrow_mut().push_back(msg);
|
|
|
|
|
}
|
|
|
|
|
}).unwrap();
|
|
|
|
|
|
|
|
|
|
// Handle X11 events
|
|
|
|
|
let pending_events: Rc<RefCell<VecDeque<_>>> = Default::default();
|
|
|
|
|
|
|
|
|
|
let mut processor = EventProcessor {
|
|
|
|
|
target: target.clone(),
|
2017-12-13 06:22:03 -05:00
|
|
|
dnd,
|
2018-05-29 07:48:47 -04:00
|
|
|
devices: Default::default(),
|
2019-04-27 18:06:51 +02:00
|
|
|
randr_event_offset,
|
|
|
|
|
ime_receiver,
|
2017-12-13 06:22:03 -05:00
|
|
|
xi2ext,
|
2017-04-22 13:52:35 -07: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
|
|
|
// Register for device hotplug events
|
2018-05-27 08:49:35 -04:00
|
|
|
// (The request buffer is flushed during `init_device`)
|
2019-04-27 18:06:51 +02:00
|
|
|
get_xtarget(&target).xconn.select_xinput_events(
|
2018-05-29 07:48:47 -04:00
|
|
|
root,
|
|
|
|
|
ffi::XIAllDevices,
|
|
|
|
|
ffi::XI_HierarchyChangedMask,
|
|
|
|
|
).queue();
|
2017-04-22 13:52:35 -07:00
|
|
|
|
2019-04-27 18:06:51 +02:00
|
|
|
processor.init_device(ffi::XIAllDevices);
|
|
|
|
|
|
|
|
|
|
// Setup the X11 event source
|
|
|
|
|
let mut x11_events = ::calloop::generic::Generic::from_raw_fd(get_xtarget(&target).xconn.x11_fd);
|
|
|
|
|
x11_events.set_interest(::calloop::mio::Ready::readable());
|
|
|
|
|
let _x11_source = inner_loop.handle().insert_source(
|
|
|
|
|
x11_events,
|
|
|
|
|
{
|
|
|
|
|
let pending_events = pending_events.clone();
|
|
|
|
|
let mut callback = move |event| {
|
|
|
|
|
pending_events.borrow_mut().push_back(event);
|
|
|
|
|
};
|
|
|
|
|
move |evt, &mut ()| {
|
|
|
|
|
if evt.readiness.is_readable() {
|
|
|
|
|
// process all pending events
|
|
|
|
|
let mut xev = unsafe { mem::uninitialized() };
|
|
|
|
|
while unsafe { processor.poll_one_event(&mut xev) } {
|
|
|
|
|
processor.process_event(&mut xev, &mut callback);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
).unwrap();
|
|
|
|
|
|
|
|
|
|
let result = EventLoop {
|
|
|
|
|
inner_loop,
|
|
|
|
|
pending_events,
|
|
|
|
|
_x11_source,
|
|
|
|
|
_user_source,
|
|
|
|
|
user_sender,
|
|
|
|
|
pending_user_events,
|
|
|
|
|
target
|
|
|
|
|
};
|
2017-04-22 13:52:35 -07:00
|
|
|
|
|
|
|
|
result
|
2017-03-03 21:41:51 +01:00
|
|
|
}
|
|
|
|
|
|
2017-09-01 11:04:57 +02:00
|
|
|
/// Returns the `XConnection` of this events loop.
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn x_connection(&self) -> &Arc<XConnection> {
|
2019-04-27 18:06:51 +02:00
|
|
|
&get_xtarget(&self.target).xconn
|
2017-09-01 11:04:57 +02:00
|
|
|
}
|
|
|
|
|
|
2019-04-27 18:06:51 +02:00
|
|
|
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
2019-02-05 10:30:33 -05:00
|
|
|
EventLoopProxy {
|
2019-04-27 18:06:51 +02:00
|
|
|
user_sender: self.user_sender.clone(),
|
2017-05-25 23:19:13 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-27 18:06:51 +02:00
|
|
|
pub(crate) fn window_target(&self) -> &RootELW<T> {
|
|
|
|
|
&self.target
|
2019-02-25 01:02:55 +02:00
|
|
|
}
|
|
|
|
|
|
2019-04-27 18:06:51 +02:00
|
|
|
pub fn run_return<F>(&mut self, mut callback: F)
|
|
|
|
|
where F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow)
|
2017-03-03 21:41:51 +01:00
|
|
|
{
|
2019-04-27 18:06:51 +02:00
|
|
|
let mut control_flow = ControlFlow::default();
|
|
|
|
|
let wt = get_xtarget(&self.target);
|
2017-04-22 13:52:35 -07:00
|
|
|
|
2017-03-03 21:41:51 +01:00
|
|
|
loop {
|
2019-04-27 18:06:51 +02:00
|
|
|
// Empty the event buffer
|
2017-06-02 21:19:45 +10:00
|
|
|
{
|
2019-04-27 18:06:51 +02:00
|
|
|
let mut guard = self.pending_events.borrow_mut();
|
|
|
|
|
for evt in guard.drain(..) {
|
|
|
|
|
sticky_exit_callback(evt, &self.target, &mut control_flow, &mut callback);
|
2017-12-13 06:22:03 -05:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-27 18:06:51 +02:00
|
|
|
// Empty the user event buffer
|
|
|
|
|
{
|
|
|
|
|
let mut guard = self.pending_user_events.borrow_mut();
|
|
|
|
|
for evt in guard.drain(..) {
|
|
|
|
|
sticky_exit_callback(
|
|
|
|
|
::event::Event::UserEvent(evt),
|
|
|
|
|
&self.target,
|
|
|
|
|
&mut control_flow,
|
|
|
|
|
&mut callback
|
|
|
|
|
);
|
2017-04-22 13:52:35 -07:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-27 18:06:51 +02:00
|
|
|
// Empty the redraw requests
|
|
|
|
|
{
|
|
|
|
|
let mut guard = wt.pending_redraws.lock().unwrap();
|
|
|
|
|
for wid in guard.drain() {
|
|
|
|
|
sticky_exit_callback(
|
|
|
|
|
Event::WindowEvent {
|
|
|
|
|
window_id: ::window::WindowId(super::WindowId::X(wid)),
|
|
|
|
|
event: WindowEvent::RedrawRequested
|
|
|
|
|
},
|
|
|
|
|
&self.target,
|
|
|
|
|
&mut control_flow,
|
|
|
|
|
&mut callback
|
|
|
|
|
);
|
2017-04-22 13:52:35 -07:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-27 18:06:51 +02:00
|
|
|
// send Events cleared
|
|
|
|
|
{
|
|
|
|
|
sticky_exit_callback(
|
|
|
|
|
::event::Event::EventsCleared,
|
|
|
|
|
&self.target,
|
|
|
|
|
&mut control_flow,
|
|
|
|
|
&mut callback
|
|
|
|
|
);
|
2017-04-22 13:52:35 -07:00
|
|
|
}
|
|
|
|
|
|
2019-04-27 18:06:51 +02:00
|
|
|
// flush the X11 connection
|
|
|
|
|
unsafe { (wt.xconn.xlib.XFlush)(wt.xconn.display); }
|
|
|
|
|
|
|
|
|
|
match control_flow {
|
|
|
|
|
ControlFlow::Exit => break,
|
|
|
|
|
ControlFlow::Poll => {
|
|
|
|
|
// non-blocking dispatch
|
|
|
|
|
self.inner_loop.dispatch(Some(::std::time::Duration::from_millis(0)), &mut ()).unwrap();
|
|
|
|
|
control_flow = ControlFlow::default();
|
|
|
|
|
callback(::event::Event::NewEvents(::event::StartCause::Poll), &self.target, &mut control_flow);
|
|
|
|
|
},
|
|
|
|
|
ControlFlow::Wait => {
|
|
|
|
|
self.inner_loop.dispatch(None, &mut ()).unwrap();
|
|
|
|
|
control_flow = ControlFlow::default();
|
|
|
|
|
callback(
|
|
|
|
|
::event::Event::NewEvents(::event::StartCause::WaitCancelled {
|
|
|
|
|
start: ::std::time::Instant::now(),
|
|
|
|
|
requested_resume: None
|
|
|
|
|
}),
|
|
|
|
|
&self.target,
|
|
|
|
|
&mut control_flow
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
ControlFlow::WaitUntil(deadline) => {
|
|
|
|
|
let start = ::std::time::Instant::now();
|
|
|
|
|
// compute the blocking duration
|
|
|
|
|
let duration = deadline.duration_since(::std::cmp::max(deadline, start));
|
|
|
|
|
self.inner_loop.dispatch(Some(duration), &mut ()).unwrap();
|
|
|
|
|
control_flow = ControlFlow::default();
|
|
|
|
|
let now = std::time::Instant::now();
|
|
|
|
|
if now < deadline {
|
|
|
|
|
callback(
|
|
|
|
|
::event::Event::NewEvents(::event::StartCause::WaitCancelled {
|
|
|
|
|
start,
|
|
|
|
|
requested_resume: Some(deadline)
|
|
|
|
|
}),
|
|
|
|
|
&self.target,
|
|
|
|
|
&mut control_flow
|
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-04-05 14:58:10 -04:00
|
|
|
} else {
|
2019-04-27 18:06:51 +02:00
|
|
|
callback(
|
|
|
|
|
::event::Event::NewEvents(::event::StartCause::ResumeTimeReached {
|
|
|
|
|
start,
|
|
|
|
|
requested_resume: deadline
|
2018-01-08 05:06:02 -05:00
|
|
|
}),
|
2019-04-27 18:06:51 +02:00
|
|
|
&self.target,
|
|
|
|
|
&mut control_flow
|
2018-06-14 19:42:18 -04:00
|
|
|
);
|
2017-04-22 13:52:35 -07: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
|
|
|
}
|
2017-04-22 13:52:35 -07:00
|
|
|
}
|
2018-05-29 07:48:47 -04:00
|
|
|
|
2019-04-27 18:06:51 +02:00
|
|
|
callback(::event::Event::LoopDestroyed, &self.target, &mut control_flow);
|
2018-05-29 07:48:47 -04:00
|
|
|
}
|
|
|
|
|
|
2019-04-27 18:06:51 +02:00
|
|
|
pub fn run<F>(mut self, callback: F) -> !
|
|
|
|
|
where F: 'static + FnMut(Event<T>, &RootELW<T>, &mut ControlFlow)
|
|
|
|
|
{
|
|
|
|
|
self.run_return(callback);
|
|
|
|
|
::std::process::exit(0);
|
2018-05-29 07:48:47 -04:00
|
|
|
}
|
2017-04-22 13:52:35 -07:00
|
|
|
}
|
|
|
|
|
|
2019-04-27 18:06:51 +02:00
|
|
|
fn get_xtarget<T>(rt: &RootELW<T>) -> &EventLoopWindowTarget<T> {
|
|
|
|
|
if let super::EventLoopWindowTarget::X(ref target) = rt.p {
|
|
|
|
|
target
|
|
|
|
|
} else {
|
|
|
|
|
unreachable!();
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-05-25 23:19:13 +10:00
|
|
|
|
2019-04-27 18:06:51 +02:00
|
|
|
impl<T: 'static> EventLoopProxy<T> {
|
|
|
|
|
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
|
|
|
|
|
self.user_sender.send(event).map_err(|_| EventLoopClosed)
|
2017-05-25 23:19:13 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-22 13:52:35 -07:00
|
|
|
struct DeviceInfo<'a> {
|
2018-05-29 07:48:47 -04:00
|
|
|
xconn: &'a XConnection,
|
2017-04-22 13:52:35 -07:00
|
|
|
info: *const ffi::XIDeviceInfo,
|
|
|
|
|
count: usize,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> DeviceInfo<'a> {
|
2018-05-29 07:48:47 -04:00
|
|
|
fn get(xconn: &'a XConnection, device: c_int) -> Option<Self> {
|
2017-04-22 13:52:35 -07:00
|
|
|
unsafe {
|
|
|
|
|
let mut count = mem::uninitialized();
|
2018-05-29 07:48:47 -04:00
|
|
|
let info = (xconn.xinput2.XIQueryDevice)(xconn.display, device, &mut count);
|
|
|
|
|
xconn.check_errors()
|
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
|
|
|
.ok()
|
|
|
|
|
.and_then(|_| {
|
|
|
|
|
if info.is_null() || count == 0 {
|
|
|
|
|
None
|
|
|
|
|
} else {
|
|
|
|
|
Some(DeviceInfo {
|
2018-05-29 07:48:47 -04:00
|
|
|
xconn,
|
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
|
|
|
info,
|
|
|
|
|
count: count as usize,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
2017-04-22 13:52:35 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> Drop for DeviceInfo<'a> {
|
|
|
|
|
fn drop(&mut 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 09:15:49 -04:00
|
|
|
assert!(!self.info.is_null());
|
2018-05-29 07:48:47 -04:00
|
|
|
unsafe { (self.xconn.xinput2.XIFreeDeviceInfo)(self.info as *mut _) };
|
2017-04-22 13:52:35 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-29 07:48:47 -04:00
|
|
|
impl<'a> Deref for DeviceInfo<'a> {
|
2017-04-22 13:52:35 -07:00
|
|
|
type Target = [ffi::XIDeviceInfo];
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
|
unsafe { slice::from_raw_parts(self.info, self.count) }
|
|
|
|
|
}
|
2017-03-03 21:41:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
2017-04-22 13:52:35 -07:00
|
|
|
pub struct WindowId(ffi::Window);
|
|
|
|
|
|
2018-12-21 09:51:48 -07:00
|
|
|
impl WindowId {
|
|
|
|
|
pub unsafe fn dummy() -> Self {
|
|
|
|
|
WindowId(0)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-22 13:52:35 -07:00
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
|
|
|
pub struct DeviceId(c_int);
|
2017-03-03 21:41:51 +01:00
|
|
|
|
2018-12-21 09:51:48 -07:00
|
|
|
impl DeviceId {
|
|
|
|
|
pub unsafe fn dummy() -> Self {
|
|
|
|
|
DeviceId(0)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-14 19:42:18 -04:00
|
|
|
pub struct Window(Arc<UnownedWindow>);
|
2017-03-03 21:41:51 +01:00
|
|
|
|
2018-05-29 07:48:47 -04:00
|
|
|
impl Deref for Window {
|
|
|
|
|
type Target = UnownedWindow;
|
2017-03-03 21:41:51 +01:00
|
|
|
#[inline]
|
2018-05-29 07:48:47 -04:00
|
|
|
fn deref(&self) -> &UnownedWindow {
|
2018-06-14 19:42:18 -04:00
|
|
|
&*self.0
|
2017-03-03 21:41:51 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-06 17:32:24 +02:00
|
|
|
impl Window {
|
2019-04-27 18:06:51 +02:00
|
|
|
pub fn new<T>(
|
|
|
|
|
event_loop: &EventLoopWindowTarget<T>,
|
2018-05-07 17:36:21 -04:00
|
|
|
attribs: WindowAttributes,
|
|
|
|
|
pl_attribs: PlatformSpecificWindowBuilderAttributes
|
2018-04-05 14:58:10 -04:00
|
|
|
) -> Result<Self, CreationError> {
|
2018-05-29 07:48:47 -04:00
|
|
|
let window = Arc::new(UnownedWindow::new(&event_loop, attribs, pl_attribs)?);
|
|
|
|
|
event_loop.windows
|
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
|
|
|
.borrow_mut()
|
2018-05-29 07:48:47 -04:00
|
|
|
.insert(window.id(), Arc::downgrade(&window));
|
2018-06-14 19:42:18 -04:00
|
|
|
Ok(Window(window))
|
2017-07-12 00:00:51 -04:00
|
|
|
}
|
2017-03-03 21:41:51 +01:00
|
|
|
}
|
|
|
|
|
|
2017-09-06 17:32:24 +02:00
|
|
|
impl Drop for Window {
|
2017-03-03 21:41:51 +01:00
|
|
|
fn drop(&mut self) {
|
2018-06-14 19:42:18 -04:00
|
|
|
let window = self.deref();
|
|
|
|
|
let xconn = &window.xconn;
|
2018-05-29 07:48:47 -04:00
|
|
|
unsafe {
|
2018-06-14 19:42:18 -04:00
|
|
|
(xconn.xlib.XDestroyWindow)(xconn.display, window.id().0);
|
2018-05-29 07:48:47 -04:00
|
|
|
// If the window was somehow already destroyed, we'll get a `BadWindow` error, which we don't care about.
|
|
|
|
|
let _ = xconn.check_errors();
|
2017-03-03 21:41:51 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-22 13:52:35 -07:00
|
|
|
|
|
|
|
|
/// XEvents of type GenericEvent store their actual data in an XGenericEventCookie data structure. This is a wrapper to
|
|
|
|
|
/// extract the cookie from a GenericEvent XEvent and release the cookie data once it has been processed
|
|
|
|
|
struct GenericEventCookie<'a> {
|
2018-05-29 07:48:47 -04:00
|
|
|
xconn: &'a XConnection,
|
2017-04-22 13:52:35 -07:00
|
|
|
cookie: ffi::XGenericEventCookie
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> GenericEventCookie<'a> {
|
2018-05-29 07:48:47 -04:00
|
|
|
fn from_event<'b>(xconn: &'b XConnection, event: ffi::XEvent) -> Option<GenericEventCookie<'b>> {
|
2017-04-22 13:52:35 -07:00
|
|
|
unsafe {
|
|
|
|
|
let mut cookie: ffi::XGenericEventCookie = From::from(event);
|
2018-05-29 07:48:47 -04:00
|
|
|
if (xconn.xlib.XGetEventData)(xconn.display, &mut cookie) == ffi::True {
|
|
|
|
|
Some(GenericEventCookie { xconn, cookie })
|
2017-04-22 13:52:35 -07:00
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> Drop for GenericEventCookie<'a> {
|
|
|
|
|
fn drop(&mut self) {
|
|
|
|
|
unsafe {
|
2018-05-29 07:48:47 -04:00
|
|
|
(self.xconn.xlib.XFreeEventData)(self.xconn.display, &mut self.cookie);
|
2017-04-22 13:52:35 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
|
struct XExtension {
|
|
|
|
|
opcode: c_int,
|
|
|
|
|
first_event_id: c_int,
|
|
|
|
|
first_error_id: c_int,
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-27 18:06:51 +02:00
|
|
|
fn mkwid(w: ffi::Window) -> ::window::WindowId { ::window::WindowId(::platform_impl::WindowId::X(WindowId(w))) }
|
|
|
|
|
fn mkdid(w: c_int) -> ::event::DeviceId { ::event::DeviceId(::platform_impl::DeviceId::X(DeviceId(w))) }
|
2017-04-22 13:52:35 -07:00
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
struct Device {
|
|
|
|
|
name: String,
|
|
|
|
|
scroll_axes: Vec<(i32, ScrollAxis)>,
|
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
|
|
|
// For master devices, this is the paired device (pointer <-> keyboard).
|
|
|
|
|
// For slave devices, this is the master.
|
|
|
|
|
attachment: c_int,
|
2017-04-22 13:52:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
|
struct ScrollAxis {
|
|
|
|
|
increment: f64,
|
|
|
|
|
orientation: ScrollOrientation,
|
|
|
|
|
position: f64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
|
enum ScrollOrientation {
|
|
|
|
|
Vertical,
|
|
|
|
|
Horizontal,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Device {
|
2019-04-27 18:06:51 +02:00
|
|
|
fn new<T: 'static>(el: &EventProcessor<T>, info: &ffi::XIDeviceInfo) -> Self {
|
2017-04-22 13:52:35 -07:00
|
|
|
let name = unsafe { CStr::from_ptr(info.name).to_string_lossy() };
|
2017-07-09 17:47:52 +10:00
|
|
|
let mut scroll_axes = Vec::new();
|
2017-04-22 13:52:35 -07:00
|
|
|
|
2019-04-27 18:06:51 +02:00
|
|
|
let wt = get_xtarget(&el.target);
|
|
|
|
|
|
2017-07-09 17:47:52 +10:00
|
|
|
if Device::physical_device(info) {
|
2017-04-22 13:52:35 -07:00
|
|
|
// Register for global raw events
|
|
|
|
|
let mask = ffi::XI_RawMotionMask
|
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
|
|
|
| ffi::XI_RawButtonPressMask
|
|
|
|
|
| ffi::XI_RawButtonReleaseMask
|
|
|
|
|
| ffi::XI_RawKeyPressMask
|
|
|
|
|
| ffi::XI_RawKeyReleaseMask;
|
2018-05-27 08:49:35 -04:00
|
|
|
// The request buffer is flushed when we poll for events
|
2019-04-27 18:06:51 +02:00
|
|
|
wt.xconn.select_xinput_events(wt.root, info.deviceid, mask).queue();
|
2017-04-22 13:52:35 -07:00
|
|
|
|
|
|
|
|
// Identify scroll axes
|
2017-07-09 17:47:52 +10:00
|
|
|
for class_ptr in Device::classes(info) {
|
2017-04-22 13:52:35 -07:00
|
|
|
let class = unsafe { &**class_ptr };
|
|
|
|
|
match class._type {
|
|
|
|
|
ffi::XIScrollClass => {
|
|
|
|
|
let info = unsafe { mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIScrollClassInfo>(class) };
|
|
|
|
|
scroll_axes.push((info.number, ScrollAxis {
|
|
|
|
|
increment: info.increment,
|
|
|
|
|
orientation: match info.scroll_type {
|
|
|
|
|
ffi::XIScrollTypeHorizontal => ScrollOrientation::Horizontal,
|
|
|
|
|
ffi::XIScrollTypeVertical => ScrollOrientation::Vertical,
|
|
|
|
|
_ => { unreachable!() }
|
|
|
|
|
},
|
|
|
|
|
position: 0.0,
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-07-09 17:47:52 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut device = Device {
|
|
|
|
|
name: name.into_owned(),
|
|
|
|
|
scroll_axes: scroll_axes,
|
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
|
|
|
attachment: info.attachment,
|
2017-07-09 17:47:52 +10:00
|
|
|
};
|
|
|
|
|
device.reset_scroll_position(info);
|
|
|
|
|
device
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn reset_scroll_position(&mut self, info: &ffi::XIDeviceInfo) {
|
|
|
|
|
if Device::physical_device(info) {
|
|
|
|
|
for class_ptr in Device::classes(info) {
|
2017-04-22 13:52:35 -07:00
|
|
|
let class = unsafe { &**class_ptr };
|
|
|
|
|
match class._type {
|
|
|
|
|
ffi::XIValuatorClass => {
|
|
|
|
|
let info = unsafe { mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIValuatorClassInfo>(class) };
|
2017-07-09 17:47:52 +10:00
|
|
|
if let Some(&mut (_, ref mut axis)) = self.scroll_axes.iter_mut().find(|&&mut (axis, _)| axis == info.number) {
|
2017-04-22 13:52:35 -07:00
|
|
|
axis.position = info.value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-07-09 17:47:52 +10:00
|
|
|
}
|
2017-04-22 13:52:35 -07:00
|
|
|
|
2017-07-09 17:47:52 +10:00
|
|
|
#[inline]
|
|
|
|
|
fn physical_device(info: &ffi::XIDeviceInfo) -> bool {
|
|
|
|
|
info._use == ffi::XISlaveKeyboard || info._use == ffi::XISlavePointer || info._use == ffi::XIFloatingSlave
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
fn classes(info: &ffi::XIDeviceInfo) -> &[*const ffi::XIAnyClassInfo] {
|
|
|
|
|
unsafe { slice::from_raw_parts(info.classes as *const *const ffi::XIAnyClassInfo, info.num_classes as usize) }
|
2017-04-22 13:52:35 -07:00
|
|
|
}
|
|
|
|
|
}
|