mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23c384bd30 | ||
|
|
ced1616e51 | ||
|
|
233ac4aed2 | ||
|
|
be5a2b0e87 | ||
|
|
2b4b64f499 | ||
|
|
262490d074 | ||
|
|
8891cfd85e | ||
|
|
2cc8fa1eac | ||
|
|
79aebf06dc | ||
|
|
19dd961752 | ||
|
|
bf413ecb83 | ||
|
|
fd1a3eda1c | ||
|
|
0e2488db32 | ||
|
|
58a00bffbb | ||
|
|
bbfe57400d | ||
|
|
4372f6fdac | ||
|
|
30f798b246 |
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
- [ ] Tested on all platforms changed
|
||||
- [ ] Added an entry to `CHANGELOG.md` if knowledge of this change could be valuable to users
|
||||
- [ ] Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
|
||||
- [ ] Created an example program if it would help users understand this functionality
|
||||
15
.travis.yml
15
.travis.yml
@@ -19,6 +19,12 @@ matrix:
|
||||
addons:
|
||||
apt:
|
||||
packages: *i686_packages
|
||||
- env: TARGET=i686-unknown-linux-gnu
|
||||
os: linux
|
||||
rust: 1.24.1
|
||||
addons:
|
||||
apt:
|
||||
packages: *i686_packages
|
||||
|
||||
# Linux 64bit
|
||||
- env: TARGET=x86_64-unknown-linux-gnu
|
||||
@@ -27,6 +33,9 @@ matrix:
|
||||
- env: TARGET=x86_64-unknown-linux-gnu
|
||||
os: linux
|
||||
rust: stable
|
||||
- env: TARGET=x86_64-unknown-linux-gnu
|
||||
os: linux
|
||||
rust: 1.24.1
|
||||
|
||||
# macOS
|
||||
- env: TARGET=x86_64-apple-darwin
|
||||
@@ -35,6 +44,9 @@ matrix:
|
||||
- env: TARGET=x86_64-apple-darwin
|
||||
os: osx
|
||||
rust: stable
|
||||
- env: TARGET=x86_64-apple-darwin
|
||||
os: osx
|
||||
rust: 1.24.1
|
||||
|
||||
# iOS
|
||||
- env: TARGET=x86_64-apple-ios
|
||||
@@ -43,6 +55,9 @@ matrix:
|
||||
- env: TARGET=x86_64-apple-ios
|
||||
os: osx
|
||||
rust: stable
|
||||
- env: TARGET=x86_64-apple-ios
|
||||
os: osx
|
||||
rust: 1.24.1
|
||||
|
||||
install:
|
||||
- rustup self update
|
||||
|
||||
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,5 +1,22 @@
|
||||
# Unreleased
|
||||
|
||||
# Version 0.15.1 (2018-06-13)
|
||||
|
||||
- On X11, the `Moved` event is no longer sent when the window is resized without changing position.
|
||||
- `MouseCursor` and `CursorState` now implement `Default`.
|
||||
- `WindowBuilder::with_resizable` implemented for Windows, X11, Wayland, and macOS.
|
||||
- `Window::set_resizable` implemented for Windows, X11, Wayland, and macOS.
|
||||
- On X11, if the monitor's width or height in millimeters is reported as 0, the DPI is now 1.0 instead of +inf.
|
||||
- On X11, the environment variable `WINIT_HIDPI_FACTOR` has been added for overriding DPI factor.
|
||||
- On X11, enabling transparency no longer causes the window contents to flicker when resizing.
|
||||
- On X11, `with_override_redirect` now actually enables override redirect.
|
||||
- macOS now generates `VirtualKeyCode::LAlt` and `VirtualKeyCode::RAlt` instead of `None` for both.
|
||||
- On macOS, `VirtualKeyCode::RWin` and `VirtualKeyCode::LWin` are no longer switched.
|
||||
- On macOS, windows without decorations can once again be resized.
|
||||
- Fixed race conditions when creating an `EventsLoop` on X11, most commonly manifesting as "[xcb] Unknown sequence number while processing queue".
|
||||
- On macOS, `CursorMoved` and `MouseInput` events are only generated if they occurs within the window's client area.
|
||||
- On macOS, resizing the window no longer generates a spurious `MouseInput` event.
|
||||
|
||||
# Version 0.15.0 (2018-05-22)
|
||||
|
||||
- `Icon::to_cardinals` is no longer public, since it was never supposed to be.
|
||||
|
||||
27
Cargo.toml
27
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.15.0"
|
||||
version = "0.15.1"
|
||||
authors = ["The winit contributors, Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
keywords = ["windowing"]
|
||||
@@ -34,25 +34,26 @@ core-foundation = "0.6"
|
||||
core-graphics = "0.14"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies.winapi]
|
||||
version = "0.3"
|
||||
version = "0.3.5"
|
||||
features = [
|
||||
"combaseapi",
|
||||
"dwmapi",
|
||||
"hidusage",
|
||||
"libloaderapi",
|
||||
"objbase",
|
||||
"processthreadsapi",
|
||||
"shellapi",
|
||||
"shobjidl_core",
|
||||
"unknwnbase",
|
||||
"windowsx",
|
||||
"wingdi",
|
||||
"winnt",
|
||||
"winuser",
|
||||
"wingdi",
|
||||
"shellapi",
|
||||
"dwmapi",
|
||||
"processthreadsapi",
|
||||
"libloaderapi",
|
||||
"windowsx",
|
||||
"hidusage",
|
||||
"combaseapi",
|
||||
"objbase",
|
||||
"unknwnbase",
|
||||
]
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies]
|
||||
wayland-client = { version = "0.20.6", features = [ "dlopen", "egl", "cursor"] }
|
||||
smithay-client-toolkit = "0.2.1"
|
||||
smithay-client-toolkit = "0.2.2"
|
||||
x11-dl = "2.17.5"
|
||||
parking_lot = "0.5"
|
||||
percent-encoding = "1.0"
|
||||
|
||||
13
appveyor.yml
13
appveyor.yml
@@ -1,12 +1,19 @@
|
||||
environment:
|
||||
matrix:
|
||||
- TARGET: x86_64-pc-windows-msvc
|
||||
CHANNEL: nightly
|
||||
- TARGET: x86_64-pc-windows-msvc
|
||||
CHANNEL: stable
|
||||
- TARGET: x86_64-pc-windows-msvc
|
||||
CHANNEL: 1.24.1
|
||||
- TARGET: i686-pc-windows-msvc
|
||||
CHANNEL: nightly
|
||||
- TARGET: i686-pc-windows-gnu
|
||||
CHANNEL: nightly
|
||||
install:
|
||||
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:TARGET}.exe"
|
||||
- rust-nightly-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
|
||||
- SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin
|
||||
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
||||
- rustup-init -yv --default-toolchain %CHANNEL% --default-host %TARGET%
|
||||
- SET PATH=%PATH%;%USERPROFILE%\.cargo\bin
|
||||
- SET PATH=%PATH%;C:\MinGW\bin
|
||||
- rustc -V
|
||||
- cargo -V
|
||||
|
||||
38
examples/resizable.rs
Normal file
38
examples/resizable.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
extern crate winit;
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
let mut resizable = false;
|
||||
|
||||
let window = winit::WindowBuilder::new()
|
||||
.with_title("Hit space to toggle resizability.")
|
||||
.with_dimensions(400, 200)
|
||||
.with_resizable(resizable)
|
||||
.build(&events_loop)
|
||||
.unwrap();
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
match event {
|
||||
winit::Event::WindowEvent { event, .. } => match event {
|
||||
winit::WindowEvent::CloseRequested => return winit::ControlFlow::Break,
|
||||
winit::WindowEvent::KeyboardInput {
|
||||
input:
|
||||
winit::KeyboardInput {
|
||||
virtual_keycode: Some(winit::VirtualKeyCode::Space),
|
||||
state: winit::ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
resizable = !resizable;
|
||||
println!("Resizable: {}", resizable);
|
||||
window.set_resizable(resizable);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
};
|
||||
winit::ControlFlow::Continue
|
||||
});
|
||||
}
|
||||
19
src/lib.rs
19
src/lib.rs
@@ -87,7 +87,6 @@ extern crate libc;
|
||||
extern crate image;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[macro_use]
|
||||
extern crate winapi;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
#[macro_use]
|
||||
@@ -374,6 +373,12 @@ pub enum MouseCursor {
|
||||
RowResize,
|
||||
}
|
||||
|
||||
impl Default for MouseCursor {
|
||||
fn default() -> Self {
|
||||
MouseCursor::Default
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes how winit handles the cursor.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum CursorState {
|
||||
@@ -391,6 +396,12 @@ pub enum CursorState {
|
||||
Grab,
|
||||
}
|
||||
|
||||
impl Default for CursorState {
|
||||
fn default() -> Self {
|
||||
CursorState::Normal
|
||||
}
|
||||
}
|
||||
|
||||
/// Attributes to use when creating a window.
|
||||
#[derive(Clone)]
|
||||
pub struct WindowAttributes {
|
||||
@@ -410,6 +421,11 @@ pub struct WindowAttributes {
|
||||
/// The default is `None`.
|
||||
pub max_dimensions: Option<(u32, u32)>,
|
||||
|
||||
/// Whether the window is resizable or not.
|
||||
///
|
||||
/// The default is `true`.
|
||||
pub resizable: bool,
|
||||
|
||||
/// Whether the window should be set as fullscreen upon creation.
|
||||
///
|
||||
/// The default is `None`.
|
||||
@@ -463,6 +479,7 @@ impl Default for WindowAttributes {
|
||||
dimensions: None,
|
||||
min_dimensions: None,
|
||||
max_dimensions: None,
|
||||
resizable: true,
|
||||
title: "winit window".to_owned(),
|
||||
maximized: false,
|
||||
fullscreen: None,
|
||||
|
||||
@@ -258,6 +258,11 @@ impl Window {
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, _resizable: bool) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
if self.native_window.is_null() {
|
||||
|
||||
@@ -462,6 +462,11 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, _resizable: bool) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show(&self) {}
|
||||
|
||||
@@ -317,6 +317,11 @@ impl Window {
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, _resizable: bool) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
unimplemented!();
|
||||
|
||||
@@ -49,8 +49,8 @@ pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
pub x11_window_type: x11::util::WindowType,
|
||||
}
|
||||
|
||||
lazy_static!(
|
||||
pub static ref X11_BACKEND: Result<Arc<XConnection>, XNotSupported> = {
|
||||
thread_local!(
|
||||
pub static X11_BACKEND: Result<Arc<XConnection>, XNotSupported> = {
|
||||
XConnection::new(Some(x_error_callback)).map(Arc::new)
|
||||
};
|
||||
);
|
||||
@@ -232,6 +232,14 @@ impl Window {
|
||||
&Window::Wayland(ref w) => w.set_max_dimensions(dimensions)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, resizable: bool) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_resizable(resizable),
|
||||
&Window::Wayland(ref w) => w.set_resizable(resizable),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, cursor: MouseCursor) {
|
||||
@@ -342,27 +350,29 @@ unsafe extern "C" fn x_error_callback(
|
||||
display: *mut x11::ffi::Display,
|
||||
event: *mut x11::ffi::XErrorEvent,
|
||||
) -> c_int {
|
||||
if let Ok(ref xconn) = *X11_BACKEND {
|
||||
let mut buf: [c_char; 1024] = mem::uninitialized();
|
||||
(xconn.xlib.XGetErrorText)(
|
||||
display,
|
||||
(*event).error_code as c_int,
|
||||
buf.as_mut_ptr(),
|
||||
buf.len() as c_int,
|
||||
);
|
||||
let description = CStr::from_ptr(buf.as_ptr()).to_string_lossy();
|
||||
X11_BACKEND.with(|result| {
|
||||
if let &Ok(ref xconn) = result {
|
||||
let mut buf: [c_char; 1024] = mem::uninitialized();
|
||||
(xconn.xlib.XGetErrorText)(
|
||||
display,
|
||||
(*event).error_code as c_int,
|
||||
buf.as_mut_ptr(),
|
||||
buf.len() as c_int,
|
||||
);
|
||||
let description = CStr::from_ptr(buf.as_ptr()).to_string_lossy();
|
||||
|
||||
let error = XError {
|
||||
description: description.into_owned(),
|
||||
error_code: (*event).error_code,
|
||||
request_code: (*event).request_code,
|
||||
minor_code: (*event).minor_code,
|
||||
};
|
||||
let error = XError {
|
||||
description: description.into_owned(),
|
||||
error_code: (*event).error_code,
|
||||
request_code: (*event).request_code,
|
||||
minor_code: (*event).minor_code,
|
||||
};
|
||||
|
||||
eprintln!("[winit X11 error] {:#?}", error);
|
||||
eprintln!("[winit X11 error] {:#?}", error);
|
||||
|
||||
*xconn.latest_error.lock() = Some(error);
|
||||
}
|
||||
*xconn.latest_error.lock() = Some(error);
|
||||
}
|
||||
});
|
||||
// Fun fact: this return value is completely ignored.
|
||||
0
|
||||
}
|
||||
@@ -424,10 +434,14 @@ r#"Failed to initialize any backend!
|
||||
}
|
||||
|
||||
pub fn new_x11() -> Result<EventsLoop, XNotSupported> {
|
||||
match *X11_BACKEND {
|
||||
Ok(ref x) => Ok(EventsLoop::X(x11::EventsLoop::new(x.clone()))),
|
||||
Err(ref err) => Err(err.clone()),
|
||||
}
|
||||
X11_BACKEND.with(|result| {
|
||||
result
|
||||
.as_ref()
|
||||
.map(Arc::clone)
|
||||
.map(x11::EventsLoop::new)
|
||||
.map(EventsLoop::X)
|
||||
.map_err(|err| err.clone())
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -101,6 +101,8 @@ impl Window {
|
||||
frame.set_maximized();
|
||||
}
|
||||
|
||||
frame.set_resizable(attributes.resizable);
|
||||
|
||||
// set decorations
|
||||
frame.set_decorate(attributes.decorations);
|
||||
|
||||
@@ -198,6 +200,11 @@ impl Window {
|
||||
self.frame.lock().unwrap().set_max_size(dimensions);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, resizable: bool) {
|
||||
self.frame.lock().unwrap().set_resizable(resizable);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, _cursor: MouseCursor) {
|
||||
// TODO
|
||||
|
||||
@@ -40,7 +40,7 @@ impl DndAtoms {
|
||||
b"text/uri-list\0".as_ptr() as *mut c_char,
|
||||
b"None\0".as_ptr() as *mut c_char,
|
||||
];
|
||||
let atoms = unsafe { util::get_atoms(xconn, &names) }?;
|
||||
let atoms = unsafe { xconn.get_atoms(&names) }?;
|
||||
Ok(DndAtoms {
|
||||
aware: atoms[0],
|
||||
enter: atoms[1],
|
||||
@@ -127,13 +127,12 @@ impl Dnd {
|
||||
DndState::Accepted => (1, self.atoms.action_private as c_long),
|
||||
DndState::Rejected => (0, self.atoms.none as c_long),
|
||||
};
|
||||
util::send_client_msg(
|
||||
&self.xconn,
|
||||
self.xconn.send_client_msg(
|
||||
target_window,
|
||||
target_window,
|
||||
self.atoms.status,
|
||||
None,
|
||||
(this_window as c_long, accepted, 0, 0, action),
|
||||
[this_window as c_long, accepted, 0, 0, action],
|
||||
).flush()
|
||||
}
|
||||
|
||||
@@ -147,13 +146,12 @@ impl Dnd {
|
||||
DndState::Accepted => (1, self.atoms.action_private as c_long),
|
||||
DndState::Rejected => (0, self.atoms.none as c_long),
|
||||
};
|
||||
util::send_client_msg(
|
||||
&self.xconn,
|
||||
self.xconn.send_client_msg(
|
||||
target_window,
|
||||
target_window,
|
||||
self.atoms.finished,
|
||||
None,
|
||||
(this_window as c_long, accepted, action, 0, 0),
|
||||
[this_window as c_long, accepted, action, 0, 0],
|
||||
).flush()
|
||||
}
|
||||
|
||||
@@ -161,8 +159,7 @@ impl Dnd {
|
||||
&self,
|
||||
source_window: c_ulong,
|
||||
) -> Result<Vec<ffi::Atom>, util::GetPropertyError> {
|
||||
util::get_property(
|
||||
&self.xconn,
|
||||
self.xconn.get_property(
|
||||
source_window,
|
||||
self.atoms.type_list,
|
||||
ffi::XA_ATOM,
|
||||
@@ -184,8 +181,7 @@ impl Dnd {
|
||||
&self,
|
||||
window: c_ulong,
|
||||
) -> Result<Vec<c_uchar>, util::GetPropertyError> {
|
||||
util::get_property(
|
||||
&self.xconn,
|
||||
self.xconn.get_property(
|
||||
window,
|
||||
self.atoms.selection,
|
||||
self.atoms.uri_list,
|
||||
|
||||
@@ -5,12 +5,20 @@ use std::sync::Arc;
|
||||
use std::os::raw::c_char;
|
||||
use std::ffi::{CStr, CString, IntoStringError};
|
||||
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use super::{ffi, util, XConnection, XError};
|
||||
|
||||
lazy_static! {
|
||||
static ref GLOBAL_LOCK: Mutex<()> = Default::default();
|
||||
}
|
||||
|
||||
unsafe fn open_im(
|
||||
xconn: &Arc<XConnection>,
|
||||
locale_modifiers: &CStr,
|
||||
) -> Option<ffi::XIM> {
|
||||
let _lock = GLOBAL_LOCK.lock();
|
||||
|
||||
// XSetLocaleModifiers returns...
|
||||
// * The current locale modifiers if it's given a NULL pointer.
|
||||
// * The new locale modifiers if we succeeded in setting them.
|
||||
@@ -85,13 +93,11 @@ enum GetXimServersError {
|
||||
// modifiers, since we don't want a user who's looking at logs to ask "am I supposed to set
|
||||
// XMODIFIERS to `@server=ibus`?!?"
|
||||
unsafe fn get_xim_servers(xconn: &Arc<XConnection>) -> Result<Vec<String>, GetXimServersError> {
|
||||
let servers_atom = util::get_atom(&xconn, b"XIM_SERVERS\0")
|
||||
.map_err(GetXimServersError::XError)?;
|
||||
let servers_atom = xconn.get_atom_unchecked(b"XIM_SERVERS\0");
|
||||
|
||||
let root = (xconn.xlib.XDefaultRootWindow)(xconn.display);
|
||||
|
||||
let mut atoms: Vec<ffi::Atom> = util::get_property(
|
||||
&xconn,
|
||||
let mut atoms: Vec<ffi::Atom> = xconn.get_property(
|
||||
root,
|
||||
servers_atom,
|
||||
ffi::XA_ATOM,
|
||||
|
||||
@@ -14,17 +14,19 @@ pub use self::monitor::{
|
||||
get_available_monitors,
|
||||
get_monitor_for_window,
|
||||
get_primary_monitor,
|
||||
invalidate_cached_monitor_list,
|
||||
};
|
||||
pub use self::window::{Window2, XWindow};
|
||||
pub use self::window::UnownedWindow;
|
||||
pub use self::xdisplay::{XConnection, XNotSupported, XError};
|
||||
|
||||
use std::{mem, ptr, slice};
|
||||
use std::sync::{Arc, mpsc, Weak};
|
||||
use std::sync::atomic::{self, AtomicBool};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CStr;
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::*;
|
||||
use std::sync::{Arc, mpsc, Weak};
|
||||
use std::sync::atomic::{self, AtomicBool};
|
||||
|
||||
use libc::{self, setlocale, LC_CTYPE};
|
||||
use parking_lot::Mutex;
|
||||
@@ -45,16 +47,14 @@ use self::dnd::{Dnd, DndState};
|
||||
use self::ime::{ImeReceiver, ImeSender, ImeCreationError, Ime};
|
||||
|
||||
pub struct EventsLoop {
|
||||
display: Arc<XConnection>,
|
||||
xconn: Arc<XConnection>,
|
||||
wm_delete_window: ffi::Atom,
|
||||
dnd: Dnd,
|
||||
ime_receiver: ImeReceiver,
|
||||
ime_sender: ImeSender,
|
||||
ime: RefCell<Ime>,
|
||||
randr_event_offset: c_int,
|
||||
windows: Arc<Mutex<HashMap<WindowId, WindowData>>>,
|
||||
// Please don't laugh at this type signature
|
||||
shared_state: RefCell<HashMap<WindowId, Weak<Mutex<window::SharedState>>>>,
|
||||
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
|
||||
devices: RefCell<HashMap<DeviceId, Device>>,
|
||||
xi2ext: XExtension,
|
||||
pending_wakeup: Arc<AtomicBool>,
|
||||
@@ -67,18 +67,17 @@ pub struct EventsLoop {
|
||||
#[derive(Clone)]
|
||||
pub struct EventsLoopProxy {
|
||||
pending_wakeup: Weak<AtomicBool>,
|
||||
display: Weak<XConnection>,
|
||||
xconn: Weak<XConnection>,
|
||||
wakeup_dummy_window: ffi::Window,
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn new(display: Arc<XConnection>) -> EventsLoop {
|
||||
let root = unsafe { (display.xlib.XDefaultRootWindow)(display.display) };
|
||||
pub fn new(xconn: Arc<XConnection>) -> EventsLoop {
|
||||
let root = unsafe { (xconn.xlib.XDefaultRootWindow)(xconn.display) };
|
||||
|
||||
let wm_delete_window = unsafe { util::get_atom(&display, b"WM_DELETE_WINDOW\0") }
|
||||
.expect("Failed to call XInternAtom (WM_DELETE_WINDOW)");
|
||||
let wm_delete_window = unsafe { xconn.get_atom_unchecked(b"WM_DELETE_WINDOW\0") };
|
||||
|
||||
let dnd = Dnd::new(Arc::clone(&display))
|
||||
let dnd = Dnd::new(Arc::clone(&xconn))
|
||||
.expect("Failed to call XInternAtoms when initializing drag and drop");
|
||||
|
||||
let (ime_sender, ime_receiver) = mpsc::channel();
|
||||
@@ -86,14 +85,14 @@ impl EventsLoop {
|
||||
// possible to actually commit pre-edit sequences.
|
||||
unsafe { setlocale(LC_CTYPE, b"\0".as_ptr() as *const _); }
|
||||
let ime = RefCell::new({
|
||||
let result = Ime::new(Arc::clone(&display));
|
||||
let result = Ime::new(Arc::clone(&xconn));
|
||||
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")
|
||||
});
|
||||
|
||||
let randr_event_offset = monitor::select_input(&display, root)
|
||||
let randr_event_offset = monitor::select_input(&xconn, root)
|
||||
.expect("Failed to query XRandR extension");
|
||||
|
||||
let xi2ext = unsafe {
|
||||
@@ -102,8 +101,8 @@ impl EventsLoop {
|
||||
first_event_id: mem::uninitialized(),
|
||||
first_error_id: mem::uninitialized(),
|
||||
};
|
||||
let res = (display.xlib.XQueryExtension)(
|
||||
display.display,
|
||||
let res = (xconn.xlib.XQueryExtension)(
|
||||
xconn.display,
|
||||
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,
|
||||
@@ -117,8 +116,8 @@ impl EventsLoop {
|
||||
unsafe {
|
||||
let mut xinput_major_ver = ffi::XI_2_Major;
|
||||
let mut xinput_minor_ver = ffi::XI_2_Minor;
|
||||
if (display.xinput2.XIQueryVersion)(
|
||||
display.display,
|
||||
if (xconn.xinput2.XIQueryVersion)(
|
||||
xconn.display,
|
||||
&mut xinput_major_ver,
|
||||
&mut xinput_minor_ver,
|
||||
) != ffi::Success as libc::c_int {
|
||||
@@ -130,13 +129,13 @@ impl EventsLoop {
|
||||
}
|
||||
}
|
||||
|
||||
util::update_cached_wm_info(&display, root);
|
||||
xconn.update_cached_wm_info(root);
|
||||
|
||||
let wakeup_dummy_window = unsafe {
|
||||
let (x, y, w, h) = (10, 10, 10, 10);
|
||||
let (border_w, border_px, background_px) = (0, 0, 0);
|
||||
(display.xlib.XCreateSimpleWindow)(
|
||||
display.display,
|
||||
(xconn.xlib.XCreateSimpleWindow)(
|
||||
xconn.display,
|
||||
root,
|
||||
x,
|
||||
y,
|
||||
@@ -149,31 +148,28 @@ impl EventsLoop {
|
||||
};
|
||||
|
||||
let result = EventsLoop {
|
||||
pending_wakeup: Arc::new(AtomicBool::new(false)),
|
||||
display,
|
||||
xconn,
|
||||
wm_delete_window,
|
||||
dnd,
|
||||
ime_receiver,
|
||||
ime_sender,
|
||||
ime,
|
||||
randr_event_offset,
|
||||
windows: Arc::new(Mutex::new(HashMap::new())),
|
||||
shared_state: RefCell::new(HashMap::new()),
|
||||
devices: RefCell::new(HashMap::new()),
|
||||
windows: Default::default(),
|
||||
devices: Default::default(),
|
||||
xi2ext,
|
||||
pending_wakeup: Default::default(),
|
||||
root,
|
||||
wakeup_dummy_window,
|
||||
};
|
||||
|
||||
// Register for device hotplug events
|
||||
unsafe {
|
||||
util::select_xinput_events(
|
||||
&result.display,
|
||||
root,
|
||||
ffi::XIAllDevices,
|
||||
ffi::XI_HierarchyChangedMask,
|
||||
)
|
||||
}.queue(); // The request buffer is flushed during init_device
|
||||
// (The request buffer is flushed during `init_device`)
|
||||
result.xconn.select_xinput_events(
|
||||
root,
|
||||
ffi::XIAllDevices,
|
||||
ffi::XI_HierarchyChangedMask,
|
||||
).queue();
|
||||
|
||||
result.init_device(ffi::XIAllDevices);
|
||||
|
||||
@@ -183,13 +179,13 @@ impl EventsLoop {
|
||||
/// Returns the `XConnection` of this events loop.
|
||||
#[inline]
|
||||
pub fn x_connection(&self) -> &Arc<XConnection> {
|
||||
&self.display
|
||||
&self.xconn
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventsLoopProxy {
|
||||
EventsLoopProxy {
|
||||
pending_wakeup: Arc::downgrade(&self.pending_wakeup),
|
||||
display: Arc::downgrade(&self.display),
|
||||
xconn: Arc::downgrade(&self.xconn),
|
||||
wakeup_dummy_window: self.wakeup_dummy_window,
|
||||
}
|
||||
}
|
||||
@@ -202,12 +198,12 @@ impl EventsLoop {
|
||||
// Get next event
|
||||
unsafe {
|
||||
// Ensure XNextEvent won't block
|
||||
let count = (self.display.xlib.XPending)(self.display.display);
|
||||
let count = (self.xconn.xlib.XPending)(self.xconn.display);
|
||||
if count == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
(self.display.xlib.XNextEvent)(self.display.display, &mut xev);
|
||||
(self.xconn.xlib.XNextEvent)(self.xconn.display, &mut xev);
|
||||
}
|
||||
self.process_event(&mut xev, &mut callback);
|
||||
}
|
||||
@@ -219,7 +215,7 @@ impl EventsLoop {
|
||||
let mut xev = unsafe { mem::uninitialized() };
|
||||
|
||||
loop {
|
||||
unsafe { (self.display.xlib.XNextEvent)(self.display.display, &mut xev) }; // Blocks as necessary
|
||||
unsafe { (self.xconn.xlib.XNextEvent)(self.xconn.display, &mut xev) }; // Blocks as necessary
|
||||
|
||||
let mut control_flow = ControlFlow::Continue;
|
||||
|
||||
@@ -243,13 +239,11 @@ impl EventsLoop {
|
||||
fn process_event<F>(&mut self, xev: &mut ffi::XEvent, mut callback: F)
|
||||
where F: FnMut(Event)
|
||||
{
|
||||
let xlib = &self.display.xlib;
|
||||
|
||||
// XFilterEvent tells us when an event has been discarded by the input method.
|
||||
// Specifically, this involves all of the KeyPress events in compose/pre-edit sequences,
|
||||
// along with an extra copy of the KeyRelease events. This also prevents backspace and
|
||||
// arrow keys from being detected twice.
|
||||
if ffi::True == unsafe { (self.display.xlib.XFilterEvent)(
|
||||
if ffi::True == unsafe { (self.xconn.xlib.XFilterEvent)(
|
||||
xev,
|
||||
{ let xev: &ffi::XAnyEvent = xev.as_ref(); xev.window }
|
||||
) } {
|
||||
@@ -259,8 +253,8 @@ impl EventsLoop {
|
||||
let event_type = xev.get_type();
|
||||
match event_type {
|
||||
ffi::MappingNotify => {
|
||||
unsafe { (xlib.XRefreshKeyboardMapping)(xev.as_mut()); }
|
||||
self.display.check_errors().expect("Failed to call XRefreshKeyboardMapping");
|
||||
unsafe { (self.xconn.xlib.XRefreshKeyboardMapping)(xev.as_mut()); }
|
||||
self.xconn.check_errors().expect("Failed to call XRefreshKeyboardMapping");
|
||||
}
|
||||
|
||||
ffi::ClientMessage => {
|
||||
@@ -401,128 +395,92 @@ impl EventsLoop {
|
||||
|
||||
ffi::ConfigureNotify => {
|
||||
let xev: &ffi::XConfigureEvent = xev.as_ref();
|
||||
let xwindow = xev.window;
|
||||
let events = self.with_window(xwindow, |window| {
|
||||
// So apparently...
|
||||
// `XSendEvent` (synthetic `ConfigureNotify`) -> position relative to root
|
||||
// `XConfigureNotify` (real `ConfigureNotify`) -> position relative to parent
|
||||
// https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.5
|
||||
// We don't want to send `Moved` when this is false, since then every `Resized`
|
||||
// (whether the window moved or not) is accompanied by an extraneous `Moved` event
|
||||
// that has a position relative to the parent window.
|
||||
let is_synthetic = xev.send_event == ffi::True;
|
||||
|
||||
// So apparently...
|
||||
// XSendEvent (synthetic ConfigureNotify) -> position relative to root
|
||||
// XConfigureNotify (real ConfigureNotify) -> position relative to parent
|
||||
// https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.5
|
||||
// We don't want to send Moved when this is true, since then every Resized
|
||||
// (whether the window moved or not) is accompanied by an extraneous Moved event
|
||||
// that has a position relative to the parent window.
|
||||
let is_synthetic = xev.send_event == ffi::True;
|
||||
let new_inner_size = (xev.width as u32, xev.height as u32);
|
||||
let new_inner_position = (xev.x as i32, xev.y as i32);
|
||||
|
||||
let window = xev.window;
|
||||
let window_id = mkwid(window);
|
||||
let mut shared_state_lock = window.shared_state.lock();
|
||||
|
||||
let new_size = (xev.width, xev.height);
|
||||
let new_position = (xev.x, xev.y);
|
||||
|
||||
let (resized, moved) = {
|
||||
let mut windows = self.windows.lock();
|
||||
if let Some(window_data) = windows.get_mut(&WindowId(window)) {
|
||||
let (mut resized, mut moved) = (false, false);
|
||||
|
||||
if window_data.config.size.is_none() {
|
||||
window_data.config.size = Some(new_size);
|
||||
resized = true;
|
||||
}
|
||||
if window_data.config.size.is_none() && is_synthetic {
|
||||
window_data.config.position = Some(new_position);
|
||||
moved = true;
|
||||
}
|
||||
|
||||
if !resized {
|
||||
if window_data.config.size != Some(new_size) {
|
||||
window_data.config.size = Some(new_size);
|
||||
resized = true;
|
||||
let (resized, moved) = {
|
||||
let resized = util::maybe_change(&mut shared_state_lock.size, new_inner_size);
|
||||
let moved = if is_synthetic {
|
||||
util::maybe_change(&mut shared_state_lock.inner_position, new_inner_position)
|
||||
} else {
|
||||
// Detect when frame extents change.
|
||||
// Since this isn't synthetic, as per the notes above, this position is relative to the
|
||||
// parent window.
|
||||
let rel_parent = new_inner_position;
|
||||
if util::maybe_change(&mut shared_state_lock.inner_position_rel_parent, rel_parent) {
|
||||
// This ensures we process the next `Moved`.
|
||||
shared_state_lock.inner_position = None;
|
||||
// Extra insurance against stale frame extents.
|
||||
shared_state_lock.frame_extents = None;
|
||||
}
|
||||
}
|
||||
if !moved && is_synthetic {
|
||||
if window_data.config.position != Some(new_position) {
|
||||
window_data.config.position = Some(new_position);
|
||||
moved = true;
|
||||
}
|
||||
}
|
||||
|
||||
if !is_synthetic
|
||||
&& window_data.config.inner_position != Some(new_position) {
|
||||
window_data.config.inner_position = Some(new_position);
|
||||
// This way, we get sent Moved when the decorations are toggled.
|
||||
window_data.config.position = None;
|
||||
self.shared_state.borrow().get(&WindowId(window)).map(|window_state| {
|
||||
if let Some(window_state) = window_state.upgrade() {
|
||||
// Extra insurance against stale frame extents
|
||||
(*window_state.lock()).frame_extents.take();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
false
|
||||
};
|
||||
(resized, moved)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let capacity = resized as usize + moved as usize;
|
||||
let mut events = Vec::with_capacity(capacity);
|
||||
|
||||
if resized {
|
||||
events.push(WindowEvent::Resized(new_inner_size.0, new_inner_size.1));
|
||||
}
|
||||
};
|
||||
|
||||
if resized {
|
||||
let (width, height) = (xev.width as u32, xev.height as u32);
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Resized(width, height),
|
||||
});
|
||||
}
|
||||
|
||||
if moved {
|
||||
// We need to convert client area position to window position.
|
||||
self.shared_state.borrow().get(&WindowId(window)).map(|window_state| {
|
||||
if let Some(window_state) = window_state.upgrade() {
|
||||
let (x, y) = {
|
||||
let (inner_x, inner_y) = (xev.x as i32, xev.y as i32);
|
||||
let mut window_state_lock = window_state.lock();
|
||||
if (*window_state_lock).frame_extents.is_some() {
|
||||
(*window_state_lock).frame_extents
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.inner_pos_to_outer(inner_x, inner_y)
|
||||
} else {
|
||||
let extents = util::get_frame_extents_heuristic(
|
||||
&self.display,
|
||||
window,
|
||||
self.root,
|
||||
);
|
||||
let outer_pos = extents.inner_pos_to_outer(inner_x, inner_y);
|
||||
(*window_state_lock).frame_extents = Some(extents);
|
||||
outer_pos
|
||||
}
|
||||
};
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Moved(x, y),
|
||||
if moved || shared_state_lock.position.is_none() {
|
||||
// We need to convert client area position to window position.
|
||||
let frame_extents = shared_state_lock.frame_extents
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| {
|
||||
let frame_extents = self.xconn.get_frame_extents_heuristic(xwindow, self.root);
|
||||
shared_state_lock.frame_extents = Some(frame_extents.clone());
|
||||
frame_extents
|
||||
});
|
||||
let outer = frame_extents.inner_pos_to_outer(new_inner_position.0, new_inner_position.1);
|
||||
shared_state_lock.position = Some(outer);
|
||||
if moved {
|
||||
events.push(WindowEvent::Moved(outer.0, outer.1));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
events
|
||||
});
|
||||
|
||||
if let Some(events) = events {
|
||||
for event in events {
|
||||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(xwindow),
|
||||
event,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ffi::ReparentNotify => {
|
||||
let xev: &ffi::XReparentEvent = xev.as_ref();
|
||||
|
||||
let window = xev.window;
|
||||
|
||||
// This is generally a reliable way to detect when the window manager's been
|
||||
// replaced, though this event is only fired by reparenting window managers
|
||||
// (which is almost all of them). Failing to correctly update WM info doesn't
|
||||
// really have much impact, since on the WMs affected (xmonad, dwm, etc.) the only
|
||||
// effect is that we waste some time trying to query unsupported properties.
|
||||
util::update_cached_wm_info(&self.display, self.root);
|
||||
self.xconn.update_cached_wm_info(self.root);
|
||||
|
||||
self.shared_state
|
||||
.borrow()
|
||||
.get(&WindowId(window))
|
||||
.map(|window_state| {
|
||||
if let Some(window_state) = window_state.upgrade() {
|
||||
(*window_state.lock()).frame_extents.take();
|
||||
}
|
||||
});
|
||||
self.with_window(xev.window, |window| {
|
||||
window.invalidate_cached_frame_extents();
|
||||
});
|
||||
}
|
||||
|
||||
ffi::DestroyNotify => {
|
||||
@@ -533,7 +491,7 @@ impl EventsLoop {
|
||||
|
||||
// In the event that the window's been destroyed without being dropped first, we
|
||||
// cleanup again here.
|
||||
self.windows.lock().remove(&WindowId(window));
|
||||
self.windows.borrow_mut().remove(&WindowId(window));
|
||||
|
||||
// Since all XIM stuff needs to happen from the same thread, we destroy the input
|
||||
// context here instead of when dropping the window.
|
||||
@@ -586,14 +544,14 @@ impl EventsLoop {
|
||||
|
||||
let keysym = unsafe {
|
||||
let mut keysym = 0;
|
||||
(self.display.xlib.XLookupString)(
|
||||
(self.xconn.xlib.XLookupString)(
|
||||
xkev,
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
&mut keysym,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
self.display.check_errors().expect("Failed to lookup keysym");
|
||||
self.xconn.check_errors().expect("Failed to lookup keysym");
|
||||
keysym
|
||||
};
|
||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||
@@ -614,7 +572,7 @@ impl EventsLoop {
|
||||
|
||||
if state == Pressed {
|
||||
let written = if let Some(ic) = self.ime.borrow().get_context(window) {
|
||||
unsafe { util::lookup_utf8(&self.display, ic, xkev) }
|
||||
self.xconn.lookup_utf8(ic, xkev)
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
@@ -630,7 +588,7 @@ impl EventsLoop {
|
||||
}
|
||||
|
||||
ffi::GenericEvent => {
|
||||
let guard = if let Some(e) = GenericEventCookie::from_event(&self.display, *xev) { e } else { return };
|
||||
let guard = if let Some(e) = GenericEventCookie::from_event(&self.xconn, *xev) { e } else { return };
|
||||
let xev = &guard.cookie;
|
||||
if self.xi2ext.opcode != xev.extension {
|
||||
return;
|
||||
@@ -648,15 +606,11 @@ impl EventsLoop {
|
||||
let window_id = mkwid(xev.event);
|
||||
let device_id = mkdid(xev.deviceid);
|
||||
if (xev.flags & ffi::XIPointerEmulated) != 0 {
|
||||
let windows = self.windows.lock();
|
||||
if let Some(window_data) = windows.get(&WindowId(xev.event)) {
|
||||
if window_data.multitouch {
|
||||
// Deliver multi-touch events instead of emulated mouse events.
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
// Deliver multi-touch events instead of emulated mouse events.
|
||||
let return_now = self
|
||||
.with_window(xev.event, |window| window.multitouch)
|
||||
.unwrap_or(true);
|
||||
if return_now { return; }
|
||||
}
|
||||
|
||||
let modifiers = ModifiersState::from(xev.mods);
|
||||
@@ -735,21 +689,11 @@ impl EventsLoop {
|
||||
|
||||
let modifiers = ModifiersState::from(xev.mods);
|
||||
|
||||
// Gymnastics to ensure self.windows isn't locked when we invoke callback
|
||||
if {
|
||||
let mut windows = self.windows.lock();
|
||||
let window_data = {
|
||||
if let Some(window_data) = windows.get_mut(&WindowId(xev.event)) {
|
||||
window_data
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
if Some(new_cursor_pos) != window_data.cursor_pos {
|
||||
window_data.cursor_pos = Some(new_cursor_pos);
|
||||
true
|
||||
} else { false }
|
||||
} {
|
||||
let cursor_moved = self.with_window(xev.event, |window| {
|
||||
let mut shared_state_lock = window.shared_state.lock();
|
||||
util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos)
|
||||
});
|
||||
if cursor_moved == Some(true) {
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: CursorMoved {
|
||||
@@ -758,6 +702,8 @@ impl EventsLoop {
|
||||
modifiers,
|
||||
},
|
||||
});
|
||||
} else if cursor_moved.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
// More gymnastics, for self.devices
|
||||
@@ -815,7 +761,7 @@ impl EventsLoop {
|
||||
let window_id = mkwid(xev.event);
|
||||
let device_id = mkdid(xev.deviceid);
|
||||
|
||||
if let Some(all_info) = DeviceInfo::get(&self.display, ffi::XIAllDevices) {
|
||||
if let Some(all_info) = DeviceInfo::get(&self.xconn, ffi::XIAllDevices) {
|
||||
let mut devices = self.devices.borrow_mut();
|
||||
for device_info in all_info.iter() {
|
||||
if device_info.deviceid == xev.sourceid
|
||||
@@ -841,13 +787,8 @@ impl EventsLoop {
|
||||
// The mods field on this event isn't actually populated, so query the
|
||||
// pointer device. In the future, we can likely remove this round-trip by
|
||||
// relying on Xkb for modifier values.
|
||||
let modifiers = unsafe {
|
||||
util::query_pointer(
|
||||
&self.display,
|
||||
xev.event,
|
||||
xev.deviceid,
|
||||
)
|
||||
}.expect("Failed to query pointer device").get_modifier_state();
|
||||
let modifiers = self.xconn.query_pointer(xev.event, xev.deviceid)
|
||||
.expect("Failed to query pointer device").get_modifier_state();
|
||||
|
||||
callback(Event::WindowEvent { window_id, event: CursorMoved {
|
||||
device_id,
|
||||
@@ -860,11 +801,7 @@ impl EventsLoop {
|
||||
|
||||
// Leave, FocusIn, and FocusOut can be received by a window that's already
|
||||
// been destroyed, which the user presumably doesn't want to deal with.
|
||||
let window_closed = self.windows
|
||||
.lock()
|
||||
.get(&WindowId(xev.event))
|
||||
.is_none();
|
||||
|
||||
let window_closed = !self.window_exists(xev.event);
|
||||
if !window_closed {
|
||||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(xev.event),
|
||||
@@ -875,11 +812,9 @@ impl EventsLoop {
|
||||
ffi::XI_FocusIn => {
|
||||
let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) };
|
||||
|
||||
if !self.window_exists(xev.event) { return; }
|
||||
let window_id = mkwid(xev.event);
|
||||
|
||||
if let None = self.windows.lock().get(&WindowId(xev.event)) {
|
||||
return;
|
||||
}
|
||||
self.ime
|
||||
.borrow_mut()
|
||||
.focus(xev.event)
|
||||
@@ -906,15 +841,11 @@ impl EventsLoop {
|
||||
}
|
||||
ffi::XI_FocusOut => {
|
||||
let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
|
||||
|
||||
if let None = self.windows.lock().get(&WindowId(xev.event)) {
|
||||
return;
|
||||
}
|
||||
if !self.window_exists(xev.event) { return; }
|
||||
self.ime
|
||||
.borrow_mut()
|
||||
.unfocus(xev.event)
|
||||
.expect("Failed to unfocus input context");
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id: mkwid(xev.event),
|
||||
event: Focused(false),
|
||||
@@ -1009,13 +940,13 @@ impl EventsLoop {
|
||||
let scancode = (keycode - 8) as u32;
|
||||
|
||||
let keysym = unsafe {
|
||||
(self.display.xlib.XKeycodeToKeysym)(
|
||||
self.display.display,
|
||||
(self.xconn.xlib.XKeycodeToKeysym)(
|
||||
self.xconn.display,
|
||||
xev.detail as ffi::KeyCode,
|
||||
0,
|
||||
)
|
||||
};
|
||||
self.display.check_errors().expect("Failed to lookup raw keysym");
|
||||
self.xconn.check_errors().expect("Failed to lookup raw keysym");
|
||||
|
||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||
|
||||
@@ -1070,18 +1001,43 @@ impl EventsLoop {
|
||||
|
||||
fn init_device(&self, device: c_int) {
|
||||
let mut devices = self.devices.borrow_mut();
|
||||
if let Some(info) = DeviceInfo::get(&self.display, device) {
|
||||
if let Some(info) = DeviceInfo::get(&self.xconn, device) {
|
||||
for info in info.iter() {
|
||||
devices.insert(DeviceId(info.deviceid), Device::new(&self, info));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn with_window<F, T>(&self, window_id: ffi::Window, callback: F) -> Option<T>
|
||||
where F: Fn(&UnownedWindow) -> T
|
||||
{
|
||||
let mut deleted = false;
|
||||
let window_id = WindowId(window_id);
|
||||
let result = self.windows
|
||||
.borrow()
|
||||
.get(&window_id)
|
||||
.and_then(|window| {
|
||||
let arc = window.upgrade();
|
||||
deleted = arc.is_none();
|
||||
arc
|
||||
})
|
||||
.map(|window| callback(&*window));
|
||||
if deleted {
|
||||
// Garbage collection
|
||||
self.windows.borrow_mut().remove(&window_id);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn window_exists(&self, window_id: ffi::Window) -> bool {
|
||||
self.with_window(window_id, |_| ()).is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl EventsLoopProxy {
|
||||
pub fn wakeup(&self) -> Result<(), EventsLoopClosed> {
|
||||
// Update the `EventsLoop`'s `pending_wakeup` flag.
|
||||
let display = match (self.pending_wakeup.upgrade(), self.display.upgrade()) {
|
||||
let display = match (self.pending_wakeup.upgrade(), self.xconn.upgrade()) {
|
||||
(Some(wakeup), Some(display)) => {
|
||||
wakeup.store(true, atomic::Ordering::Relaxed);
|
||||
display
|
||||
@@ -1094,40 +1050,37 @@ impl EventsLoopProxy {
|
||||
// NOTE: This design is taken from the old `WindowProxy::wakeup` implementation. It
|
||||
// assumes that X11 is thread safe. Is this true?
|
||||
// (WARNING: it's probably not true)
|
||||
unsafe {
|
||||
util::send_client_msg(
|
||||
&display,
|
||||
self.wakeup_dummy_window,
|
||||
self.wakeup_dummy_window,
|
||||
0,
|
||||
None,
|
||||
(0, 0, 0, 0, 0),
|
||||
)
|
||||
}.flush().expect("Failed to call XSendEvent after wakeup");
|
||||
display.send_client_msg(
|
||||
self.wakeup_dummy_window,
|
||||
self.wakeup_dummy_window,
|
||||
0,
|
||||
None,
|
||||
[0, 0, 0, 0, 0],
|
||||
).flush().expect("Failed to call XSendEvent after wakeup");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct DeviceInfo<'a> {
|
||||
display: &'a XConnection,
|
||||
xconn: &'a XConnection,
|
||||
info: *const ffi::XIDeviceInfo,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl<'a> DeviceInfo<'a> {
|
||||
fn get(display: &'a XConnection, device: c_int) -> Option<Self> {
|
||||
fn get(xconn: &'a XConnection, device: c_int) -> Option<Self> {
|
||||
unsafe {
|
||||
let mut count = mem::uninitialized();
|
||||
let info = (display.xinput2.XIQueryDevice)(display.display, device, &mut count);
|
||||
display.check_errors()
|
||||
let info = (xconn.xinput2.XIQueryDevice)(xconn.display, device, &mut count);
|
||||
xconn.check_errors()
|
||||
.ok()
|
||||
.and_then(|_| {
|
||||
if info.is_null() || count == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(DeviceInfo {
|
||||
display,
|
||||
xconn,
|
||||
info,
|
||||
count: count as usize,
|
||||
})
|
||||
@@ -1140,11 +1093,11 @@ impl<'a> DeviceInfo<'a> {
|
||||
impl<'a> Drop for DeviceInfo<'a> {
|
||||
fn drop(&mut self) {
|
||||
assert!(!self.info.is_null());
|
||||
unsafe { (self.display.xinput2.XIFreeDeviceInfo)(self.info as *mut _) };
|
||||
unsafe { (self.xconn.xinput2.XIFreeDeviceInfo)(self.info as *mut _) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ::std::ops::Deref for DeviceInfo<'a> {
|
||||
impl<'a> Deref for DeviceInfo<'a> {
|
||||
type Target = [ffi::XIDeviceInfo];
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { slice::from_raw_parts(self.info, self.count) }
|
||||
@@ -1158,57 +1111,41 @@ pub struct WindowId(ffi::Window);
|
||||
pub struct DeviceId(c_int);
|
||||
|
||||
pub struct Window {
|
||||
pub window: Arc<Window2>,
|
||||
display: Weak<XConnection>,
|
||||
windows: Weak<Mutex<HashMap<WindowId, WindowData>>>,
|
||||
pub window: Arc<UnownedWindow>,
|
||||
ime_sender: Mutex<ImeSender>,
|
||||
}
|
||||
|
||||
impl ::std::ops::Deref for Window {
|
||||
type Target = Window2;
|
||||
impl Deref for Window {
|
||||
type Target = UnownedWindow;
|
||||
#[inline]
|
||||
fn deref(&self) -> &Window2 {
|
||||
fn deref(&self) -> &UnownedWindow {
|
||||
&*self.window
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(
|
||||
x_events_loop: &EventsLoop,
|
||||
event_loop: &EventsLoop,
|
||||
attribs: WindowAttributes,
|
||||
pl_attribs: PlatformSpecificWindowBuilderAttributes
|
||||
) -> Result<Self, CreationError> {
|
||||
let multitouch = attribs.multitouch;
|
||||
let win = Arc::new(Window2::new(&x_events_loop, attribs, pl_attribs)?);
|
||||
let window = Arc::new(UnownedWindow::new(&event_loop, attribs, pl_attribs)?);
|
||||
|
||||
x_events_loop.shared_state
|
||||
event_loop.windows
|
||||
.borrow_mut()
|
||||
.insert(win.id(), Arc::downgrade(&win.shared_state));
|
||||
.insert(window.id(), Arc::downgrade(&window));
|
||||
|
||||
x_events_loop.ime
|
||||
event_loop.ime
|
||||
.borrow_mut()
|
||||
.create_context(win.id().0)
|
||||
.create_context(window.id().0)
|
||||
.expect("Failed to create input context");
|
||||
|
||||
x_events_loop.windows.lock().insert(win.id(), WindowData {
|
||||
config: Default::default(),
|
||||
multitouch,
|
||||
cursor_pos: None,
|
||||
});
|
||||
|
||||
Ok(Window {
|
||||
window: win,
|
||||
windows: Arc::downgrade(&x_events_loop.windows),
|
||||
display: Arc::downgrade(&x_events_loop.display),
|
||||
ime_sender: Mutex::new(x_events_loop.ime_sender.clone()),
|
||||
window,
|
||||
ime_sender: Mutex::new(event_loop.ime_sender.clone()),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
self.window.id()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn send_xim_spot(&self, x: i16, y: i16) {
|
||||
let _ = self.ime_sender
|
||||
@@ -1219,47 +1156,28 @@ impl Window {
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
if let (Some(windows), Some(display)) = (self.windows.upgrade(), self.display.upgrade()) {
|
||||
if let Some(_) = windows.lock().remove(&self.window.id()) {
|
||||
unsafe {
|
||||
(display.xlib.XDestroyWindow)(display.display, self.window.id().0);
|
||||
}
|
||||
}
|
||||
let xconn = &self.window.xconn;
|
||||
unsafe {
|
||||
(xconn.xlib.XDestroyWindow)(xconn.display, self.window.id().0);
|
||||
// If the window was somehow already destroyed, we'll get a `BadWindow` error, which we don't care about.
|
||||
let _ = xconn.check_errors();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// State maintained for translating window-related events
|
||||
#[derive(Debug)]
|
||||
struct WindowData {
|
||||
config: WindowConfig,
|
||||
multitouch: bool,
|
||||
cursor_pos: Option<(f64, f64)>,
|
||||
}
|
||||
|
||||
// Required by ffi members
|
||||
unsafe impl Send for WindowData {}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct WindowConfig {
|
||||
pub size: Option<(c_int, c_int)>,
|
||||
pub position: Option<(c_int, c_int)>,
|
||||
pub inner_position: Option<(c_int, c_int)>,
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
display: &'a XConnection,
|
||||
xconn: &'a XConnection,
|
||||
cookie: ffi::XGenericEventCookie
|
||||
}
|
||||
|
||||
impl<'a> GenericEventCookie<'a> {
|
||||
fn from_event<'b>(display: &'b XConnection, event: ffi::XEvent) -> Option<GenericEventCookie<'b>> {
|
||||
fn from_event<'b>(xconn: &'b XConnection, event: ffi::XEvent) -> Option<GenericEventCookie<'b>> {
|
||||
unsafe {
|
||||
let mut cookie: ffi::XGenericEventCookie = From::from(event);
|
||||
if (display.xlib.XGetEventData)(display.display, &mut cookie) == ffi::True {
|
||||
Some(GenericEventCookie{display: display, cookie: cookie})
|
||||
if (xconn.xlib.XGetEventData)(xconn.display, &mut cookie) == ffi::True {
|
||||
Some(GenericEventCookie { xconn, cookie })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -1270,8 +1188,7 @@ impl<'a> GenericEventCookie<'a> {
|
||||
impl<'a> Drop for GenericEventCookie<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let xlib = &self.display.xlib;
|
||||
(xlib.XFreeEventData)(self.display.display, &mut self.cookie);
|
||||
(self.xconn.xlib.XFreeEventData)(self.xconn.display, &mut self.cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1320,14 +1237,8 @@ impl Device {
|
||||
| ffi::XI_RawButtonReleaseMask
|
||||
| ffi::XI_RawKeyPressMask
|
||||
| ffi::XI_RawKeyReleaseMask;
|
||||
unsafe {
|
||||
util::select_xinput_events(
|
||||
&el.display,
|
||||
el.root,
|
||||
info.deviceid,
|
||||
mask,
|
||||
)
|
||||
}.queue(); // The request buffer is flushed when we poll for events
|
||||
// The request buffer is flushed when we poll for events
|
||||
el.xconn.select_xinput_events(el.root, info.deviceid, mask).queue();
|
||||
|
||||
// Identify scroll axes
|
||||
for class_ptr in Device::classes(info) {
|
||||
|
||||
@@ -66,7 +66,7 @@ impl MonitorId {
|
||||
repr: util::MonitorRepr,
|
||||
primary: bool,
|
||||
) -> Self {
|
||||
let (name, hidpi_factor) = unsafe { util::get_output_info(xconn, resources, &repr) };
|
||||
let (name, hidpi_factor) = unsafe { xconn.get_output_info(resources, &repr) };
|
||||
let (dimensions, position) = unsafe { (repr.get_dimensions(), repr.get_position()) };
|
||||
let rect = util::Rect::new(position, dimensions);
|
||||
MonitorId {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt::Debug;
|
||||
use std::os::raw::*;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
@@ -12,48 +13,60 @@ lazy_static! {
|
||||
static ref ATOM_CACHE: Mutex<AtomCache> = Mutex::new(HashMap::with_capacity(2048));
|
||||
}
|
||||
|
||||
pub unsafe fn get_atom(xconn: &Arc<XConnection>, name: &[u8]) -> Result<ffi::Atom, XError> {
|
||||
let name = CStr::from_bytes_with_nul_unchecked(name); // I trust you. Don't let me down.
|
||||
let mut atom_cache_lock = ATOM_CACHE.lock();
|
||||
let cached_atom = (*atom_cache_lock).get(name).cloned();
|
||||
if let Some(atom) = cached_atom {
|
||||
Ok(atom)
|
||||
} else {
|
||||
let atom = (xconn.xlib.XInternAtom)(
|
||||
xconn.display,
|
||||
name.as_ptr() as *const c_char,
|
||||
impl XConnection {
|
||||
pub fn get_atom<T: AsRef<CStr> + Debug>(&self, name: T) -> ffi::Atom {
|
||||
let name = name.as_ref();
|
||||
let mut atom_cache_lock = ATOM_CACHE.lock();
|
||||
let cached_atom = (*atom_cache_lock).get(name).cloned();
|
||||
if let Some(atom) = cached_atom {
|
||||
atom
|
||||
} else {
|
||||
let atom = unsafe { (self.xlib.XInternAtom)(
|
||||
self.display,
|
||||
name.as_ptr() as *const c_char,
|
||||
ffi::False,
|
||||
) };
|
||||
if atom == 0 {
|
||||
let msg = format!(
|
||||
"`XInternAtom` failed, which really shouldn't happen. Atom: {:?}, Error: {:#?}",
|
||||
name,
|
||||
self.check_errors(),
|
||||
);
|
||||
panic!(msg);
|
||||
}
|
||||
/*println!(
|
||||
"XInternAtom name:{:?} atom:{:?}",
|
||||
name,
|
||||
atom,
|
||||
);*/
|
||||
(*atom_cache_lock).insert(name.to_owned(), atom);
|
||||
atom
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn get_atom_unchecked(&self, name: &[u8]) -> ffi::Atom {
|
||||
debug_assert!(CStr::from_bytes_with_nul(name).is_ok());
|
||||
let name = CStr::from_bytes_with_nul_unchecked(name);
|
||||
self.get_atom(name)
|
||||
}
|
||||
|
||||
// Note: this doesn't use caching, for the sake of simplicity.
|
||||
// If you're dealing with this many atoms, you'll usually want to cache them locally anyway.
|
||||
pub unsafe fn get_atoms(&self, names: &[*mut c_char]) -> Result<Vec<ffi::Atom>, XError> {
|
||||
let mut atoms = Vec::with_capacity(names.len());
|
||||
(self.xlib.XInternAtoms)(
|
||||
self.display,
|
||||
names.as_ptr() as *mut _,
|
||||
names.len() as c_int,
|
||||
ffi::False,
|
||||
atoms.as_mut_ptr(),
|
||||
);
|
||||
self.check_errors()?;
|
||||
atoms.set_len(names.len());
|
||||
/*println!(
|
||||
"XInternAtom name:{:?} atom:{:?}",
|
||||
name,
|
||||
atom,
|
||||
"XInternAtoms atoms:{:?}",
|
||||
atoms,
|
||||
);*/
|
||||
xconn.check_errors()?;
|
||||
(*atom_cache_lock).insert(name.to_owned(), atom);
|
||||
Ok(atom)
|
||||
Ok(atoms)
|
||||
}
|
||||
}
|
||||
|
||||
// Note: this doesn't use caching, for the sake of simplicity.
|
||||
// If you're dealing with this many atoms, you'll usually want to cache them locally anyway.
|
||||
pub unsafe fn get_atoms(
|
||||
xconn: &Arc<XConnection>,
|
||||
names: &[*mut c_char],
|
||||
) -> Result<Vec<ffi::Atom>, XError> {
|
||||
let mut atoms = Vec::with_capacity(names.len());
|
||||
(xconn.xlib.XInternAtoms)(
|
||||
xconn.display,
|
||||
names.as_ptr() as *mut _,
|
||||
names.len() as c_int,
|
||||
ffi::False,
|
||||
atoms.as_mut_ptr(),
|
||||
);
|
||||
xconn.check_errors()?;
|
||||
atoms.set_len(names.len());
|
||||
/*println!(
|
||||
"XInternAtoms atoms:{:?}",
|
||||
atoms,
|
||||
);*/
|
||||
Ok(atoms)
|
||||
}
|
||||
|
||||
95
src/platform/linux/x11/util/client_msg.rs
Normal file
95
src/platform/linux/x11/util/client_msg.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
use super::*;
|
||||
|
||||
pub type ClientMsgPayload = [c_long; 5];
|
||||
|
||||
impl XConnection {
|
||||
pub fn send_event<T: Into<ffi::XEvent>>(
|
||||
&self,
|
||||
target_window: c_ulong,
|
||||
event_mask: Option<c_long>,
|
||||
event: T,
|
||||
) -> Flusher {
|
||||
let event_mask = event_mask.unwrap_or(ffi::NoEventMask);
|
||||
unsafe {
|
||||
(self.xlib.XSendEvent)(
|
||||
self.display,
|
||||
target_window,
|
||||
ffi::False,
|
||||
event_mask,
|
||||
&mut event.into(),
|
||||
);
|
||||
}
|
||||
Flusher::new(self)
|
||||
}
|
||||
|
||||
pub fn send_client_msg(
|
||||
&self,
|
||||
window: c_ulong, // The window this is "about"; not necessarily this window
|
||||
target_window: c_ulong, // The window we're sending to
|
||||
message_type: ffi::Atom,
|
||||
event_mask: Option<c_long>,
|
||||
data: ClientMsgPayload,
|
||||
) -> Flusher {
|
||||
let mut event: ffi::XClientMessageEvent = unsafe { mem::uninitialized() };
|
||||
event.type_ = ffi::ClientMessage;
|
||||
event.display = self.display;
|
||||
event.window = window;
|
||||
event.message_type = message_type;
|
||||
event.format = c_long::FORMAT as c_int;
|
||||
event.data = unsafe { mem::transmute(data) };
|
||||
self.send_event(target_window, event_mask, event)
|
||||
}
|
||||
|
||||
// Prepare yourself for the ultimate in unsafety!
|
||||
// You should favor `send_client_msg` whenever possible, but some protocols (i.e. startup notification) require you
|
||||
// to send more than one message worth of data.
|
||||
pub fn send_client_msg_multi<T: Formattable>(
|
||||
&self,
|
||||
window: c_ulong, // The window this is "about"; not necessarily this window
|
||||
target_window: c_ulong, // The window we're sending to
|
||||
message_type: ffi::Atom,
|
||||
event_mask: Option<c_long>,
|
||||
data: &[T],
|
||||
) -> Flusher {
|
||||
let format = T::FORMAT;
|
||||
let size_of_t = mem::size_of::<T>();
|
||||
debug_assert_eq!(size_of_t, format.get_actual_size());
|
||||
let mut event: ffi::XClientMessageEvent = unsafe { mem::uninitialized() };
|
||||
event.type_ = ffi::ClientMessage;
|
||||
event.display = self.display;
|
||||
event.window = window;
|
||||
event.message_type = message_type;
|
||||
event.format = format as c_int;
|
||||
|
||||
let t_per_payload = format.get_payload_size() / size_of_t;
|
||||
assert!(t_per_payload > 0);
|
||||
let payload_count = data.len() / t_per_payload;
|
||||
let payload_remainder = data.len() % t_per_payload;
|
||||
let payload_ptr = data.as_ptr() as *const ClientMsgPayload;
|
||||
|
||||
let mut payload_index = 0;
|
||||
while payload_index < payload_count {
|
||||
let payload = unsafe { payload_ptr.offset(payload_index as isize) };
|
||||
payload_index += 1;
|
||||
event.data = unsafe { mem::transmute(*payload) };
|
||||
self.send_event(target_window, event_mask, &event).queue();
|
||||
}
|
||||
|
||||
if payload_remainder > 0 {
|
||||
let mut payload: ClientMsgPayload = [0; 5];
|
||||
let t_payload = payload.as_mut_ptr() as *mut T;
|
||||
let invalid_payload = unsafe { payload_ptr.offset(payload_index as isize) };
|
||||
let invalid_t_payload = invalid_payload as *const T;
|
||||
let mut t_index = 0;
|
||||
while t_index < payload_remainder {
|
||||
let valid_t = unsafe { invalid_t_payload.offset(t_index as isize) };
|
||||
unsafe { (*t_payload.offset(t_index as isize)) = (*valid_t).clone() };
|
||||
t_index += 1;
|
||||
}
|
||||
event.data = unsafe { mem::transmute(payload) };
|
||||
self.send_event(target_window, event_mask, &event).queue();
|
||||
}
|
||||
|
||||
Flusher::new(self)
|
||||
}
|
||||
}
|
||||
58
src/platform/linux/x11/util/format.rs
Normal file
58
src/platform/linux/x11/util/format.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use std::fmt::Debug;
|
||||
use std::mem;
|
||||
use std::os::raw::*;
|
||||
|
||||
// This isn't actually the number of the bits in the format.
|
||||
// X11 does a match on this value to determine which type to call sizeof on.
|
||||
// Thus, we use 32 for c_long, since 32 maps to c_long which maps to 64.
|
||||
// ...if that sounds confusing, then you know why this enum is here.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Format {
|
||||
Char = 8,
|
||||
Short = 16,
|
||||
Long = 32,
|
||||
}
|
||||
|
||||
impl Format {
|
||||
pub fn from_format(format: usize) -> Option<Self> {
|
||||
match format {
|
||||
8 => Some(Format::Char),
|
||||
16 => Some(Format::Short),
|
||||
32 => Some(Format::Long),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_same_size_as<T>(&self) -> bool {
|
||||
mem::size_of::<T>() == self.get_actual_size()
|
||||
}
|
||||
|
||||
pub fn get_actual_size(&self) -> usize {
|
||||
match self {
|
||||
&Format::Char => mem::size_of::<c_char>(),
|
||||
&Format::Short => mem::size_of::<c_short>(),
|
||||
&Format::Long => mem::size_of::<c_long>(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_payload_size(&self) -> usize {
|
||||
match self {
|
||||
// Due to the wonders of X11, half the space goes unused if you're not using longs (on 64-bit).
|
||||
&Format::Char => mem::size_of::<c_char>() * 20,
|
||||
&Format::Short => mem::size_of::<c_short>() * 10,
|
||||
&Format::Long => mem::size_of::<c_long>() * 5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Formattable: Debug + Clone + Copy + PartialEq + PartialOrd {
|
||||
const FORMAT: Format;
|
||||
}
|
||||
|
||||
// You might be surprised by the absence of c_int, but not as surprised as X11 would be by the presence of it.
|
||||
impl Formattable for c_char { const FORMAT: Format = Format::Char; }
|
||||
impl Formattable for c_uchar { const FORMAT: Format = Format::Char; }
|
||||
impl Formattable for c_short { const FORMAT: Format = Format::Short; }
|
||||
impl Formattable for c_ushort { const FORMAT: Format = Format::Short; }
|
||||
impl Formattable for c_long { const FORMAT: Format = Format::Long; }
|
||||
impl Formattable for c_ulong { const FORMAT: Format = Format::Long; }
|
||||
@@ -42,30 +42,6 @@ pub struct TranslatedCoords {
|
||||
pub child: ffi::Window,
|
||||
}
|
||||
|
||||
// This is adequate for get_inner_position
|
||||
pub unsafe fn translate_coords(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
root: ffi::Window,
|
||||
) -> Result<TranslatedCoords, XError> {
|
||||
let mut translated_coords: TranslatedCoords = mem::uninitialized();
|
||||
|
||||
(xconn.xlib.XTranslateCoordinates)(
|
||||
xconn.display,
|
||||
window,
|
||||
root,
|
||||
0,
|
||||
0,
|
||||
&mut translated_coords.x_rel_root,
|
||||
&mut translated_coords.y_rel_root,
|
||||
&mut translated_coords.child,
|
||||
);
|
||||
|
||||
//println!("XTranslateCoordinates coords:{:?}", translated_coords);
|
||||
|
||||
xconn.check_errors().map(|_| translated_coords)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Geometry {
|
||||
pub root: ffi::Window,
|
||||
@@ -86,30 +62,6 @@ pub struct Geometry {
|
||||
pub depth: c_uint,
|
||||
}
|
||||
|
||||
// This is adequate for get_inner_size
|
||||
pub unsafe fn get_geometry(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
) -> Result<Geometry, XError> {
|
||||
let mut geometry: Geometry = mem::uninitialized();
|
||||
|
||||
let _status = (xconn.xlib.XGetGeometry)(
|
||||
xconn.display,
|
||||
window,
|
||||
&mut geometry.root,
|
||||
&mut geometry.x_rel_parent,
|
||||
&mut geometry.y_rel_parent,
|
||||
&mut geometry.width,
|
||||
&mut geometry.height,
|
||||
&mut geometry.border,
|
||||
&mut geometry.depth,
|
||||
);
|
||||
|
||||
//println!("XGetGeometry geo:{:?}", geometry);
|
||||
|
||||
xconn.check_errors().map(|_| geometry)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FrameExtents {
|
||||
pub left: c_ulong,
|
||||
@@ -128,109 +80,6 @@ impl FrameExtents {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_frame_extents(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
) -> Option<self::FrameExtents> {
|
||||
let extents_atom = unsafe { self::get_atom(xconn, b"_NET_FRAME_EXTENTS\0") }
|
||||
.expect("Failed to call XInternAtom (_NET_FRAME_EXTENTS)");
|
||||
|
||||
if !self::hint_is_supported(extents_atom) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Of the WMs tested, xmonad, i3, dwm, IceWM (1.3.x and earlier), and blackbox don't
|
||||
// support this. As this is part of EWMH (Extended Window Manager Hints), it's likely to
|
||||
// be unsupported by many smaller WMs.
|
||||
let extents: Option<Vec<c_ulong>> = unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
window,
|
||||
extents_atom,
|
||||
ffi::XA_CARDINAL,
|
||||
)
|
||||
}.ok();
|
||||
|
||||
extents.and_then(|extents| {
|
||||
if extents.len() >= 4 {
|
||||
Some(self::FrameExtents {
|
||||
left: extents[0],
|
||||
right: extents[1],
|
||||
top: extents[2],
|
||||
bottom: extents[3],
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_top_level(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
root: ffi::Window,
|
||||
) -> Option<bool> {
|
||||
let client_list_atom = unsafe { self::get_atom(xconn, b"_NET_CLIENT_LIST\0") }
|
||||
.expect("Failed to call XInternAtom (_NET_CLIENT_LIST)");
|
||||
|
||||
if !self::hint_is_supported(client_list_atom) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let client_list: Option<Vec<ffi::Window>> = unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
root,
|
||||
client_list_atom,
|
||||
ffi::XA_WINDOW,
|
||||
)
|
||||
}.ok();
|
||||
|
||||
client_list.map(|client_list| client_list.contains(&window))
|
||||
}
|
||||
|
||||
unsafe fn get_parent_window(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
) -> Result<ffi::Window, XError> {
|
||||
let mut root: ffi::Window = mem::uninitialized();
|
||||
let mut parent: ffi::Window = mem::uninitialized();
|
||||
let mut children: *mut ffi::Window = ptr::null_mut();
|
||||
let mut nchildren: c_uint = mem::uninitialized();
|
||||
|
||||
let _status = (xconn.xlib.XQueryTree)(
|
||||
xconn.display,
|
||||
window,
|
||||
&mut root,
|
||||
&mut parent,
|
||||
&mut children,
|
||||
&mut nchildren,
|
||||
);
|
||||
|
||||
// The list of children isn't used
|
||||
if children != ptr::null_mut() {
|
||||
(xconn.xlib.XFree)(children as *mut _);
|
||||
}
|
||||
|
||||
xconn.check_errors().map(|_| parent)
|
||||
}
|
||||
|
||||
fn climb_hierarchy(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
root: ffi::Window,
|
||||
) -> Result<ffi::Window, XError> {
|
||||
let mut outer_window = window;
|
||||
loop {
|
||||
let candidate = unsafe { get_parent_window(xconn, outer_window) }?;
|
||||
if candidate == root {
|
||||
break;
|
||||
}
|
||||
outer_window = candidate;
|
||||
}
|
||||
Ok(outer_window)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum FrameExtentsHeuristicPath {
|
||||
Supported,
|
||||
@@ -266,120 +115,237 @@ impl FrameExtentsHeuristic {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_frame_extents_heuristic(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
root: ffi::Window,
|
||||
) -> FrameExtentsHeuristic {
|
||||
use self::FrameExtentsHeuristicPath::*;
|
||||
impl XConnection {
|
||||
// This is adequate for get_inner_position
|
||||
pub fn translate_coords(&self, window: ffi::Window, root: ffi::Window) -> Result<TranslatedCoords, XError> {
|
||||
let mut translated_coords: TranslatedCoords = unsafe { mem::uninitialized() };
|
||||
unsafe {
|
||||
(self.xlib.XTranslateCoordinates)(
|
||||
self.display,
|
||||
window,
|
||||
root,
|
||||
0,
|
||||
0,
|
||||
&mut translated_coords.x_rel_root,
|
||||
&mut translated_coords.y_rel_root,
|
||||
&mut translated_coords.child,
|
||||
);
|
||||
}
|
||||
//println!("XTranslateCoordinates coords:{:?}", translated_coords);
|
||||
self.check_errors().map(|_| translated_coords)
|
||||
}
|
||||
|
||||
// Position relative to root window.
|
||||
// With rare exceptions, this is the position of a nested window. Cases where the window
|
||||
// isn't nested are outlined in the comments throghout this function, but in addition to
|
||||
// that, fullscreen windows often aren't nested.
|
||||
let (inner_y_rel_root, child) = {
|
||||
let coords = unsafe { translate_coords(xconn, window, root) }
|
||||
.expect("Failed to translate window coordinates");
|
||||
(
|
||||
coords.y_rel_root,
|
||||
coords.child,
|
||||
)
|
||||
};
|
||||
// This is adequate for get_inner_size
|
||||
pub fn get_geometry(&self, window: ffi::Window) -> Result<Geometry, XError> {
|
||||
let mut geometry: Geometry = unsafe { mem::uninitialized() };
|
||||
let _status = unsafe {
|
||||
(self.xlib.XGetGeometry)(
|
||||
self.display,
|
||||
window,
|
||||
&mut geometry.root,
|
||||
&mut geometry.x_rel_parent,
|
||||
&mut geometry.y_rel_parent,
|
||||
&mut geometry.width,
|
||||
&mut geometry.height,
|
||||
&mut geometry.border,
|
||||
&mut geometry.depth,
|
||||
)
|
||||
};
|
||||
//println!("XGetGeometry geo:{:?}", geometry);
|
||||
self.check_errors().map(|_| geometry)
|
||||
}
|
||||
|
||||
let (width, height, border) = {
|
||||
let inner_geometry = unsafe { get_geometry(xconn, window) }
|
||||
.expect("Failed to get inner window geometry");
|
||||
(
|
||||
inner_geometry.width,
|
||||
inner_geometry.height,
|
||||
inner_geometry.border,
|
||||
)
|
||||
};
|
||||
fn get_frame_extents(&self, window: ffi::Window) -> Option<FrameExtents> {
|
||||
let extents_atom = unsafe { self.get_atom_unchecked(b"_NET_FRAME_EXTENTS\0") };
|
||||
|
||||
// The first condition is only false for un-nested windows, but isn't always false for
|
||||
// un-nested windows. Mutter/Muffin/Budgie and Marco present a mysterious discrepancy:
|
||||
// when y is on the range [0, 2] and if the window has been unfocused since being
|
||||
// undecorated (or was undecorated upon construction), the first condition is true,
|
||||
// requiring us to rely on the second condition.
|
||||
let nested = !(window == child || is_top_level(xconn, child, root) == Some(true));
|
||||
|
||||
// Hopefully the WM supports EWMH, allowing us to get exact info on the window frames.
|
||||
if let Some(mut frame_extents) = get_frame_extents(xconn, window) {
|
||||
// Mutter/Muffin/Budgie and Marco preserve their decorated frame extents when
|
||||
// decorations are disabled, but since the window becomes un-nested, it's easy to
|
||||
// catch.
|
||||
if !nested {
|
||||
frame_extents = FrameExtents::new(0, 0, 0, 0);
|
||||
if !hint_is_supported(extents_atom) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// The difference between the nested window's position and the outermost window's
|
||||
// position is equivalent to the frame size. In most scenarios, this is equivalent to
|
||||
// manually climbing the hierarchy as is done in the case below. Here's a list of
|
||||
// known discrepancies:
|
||||
// * Mutter/Muffin/Budgie gives decorated windows a margin of 9px (only 7px on top) in
|
||||
// addition to a 1px semi-transparent border. The margin can be easily observed by
|
||||
// using a screenshot tool to get a screenshot of a selected window, and is
|
||||
// presumably used for drawing drop shadows. Getting window geometry information
|
||||
// via hierarchy-climbing results in this margin being included in both the
|
||||
// position and outer size, so a window positioned at (0, 0) would be reported as
|
||||
// having a position (-10, -8).
|
||||
// * Compiz has a drop shadow margin just like Mutter/Muffin/Budgie, though it's 10px
|
||||
// on all sides, and there's no additional border.
|
||||
// * Enlightenment otherwise gets a y position equivalent to inner_y_rel_root.
|
||||
// Without decorations, there's no difference. This is presumably related to
|
||||
// Enlightenment's fairly unique concept of window position; it interprets
|
||||
// positions given to XMoveWindow as a client area position rather than a position
|
||||
// of the overall window.
|
||||
// Of the WMs tested, xmonad, i3, dwm, IceWM (1.3.x and earlier), and blackbox don't
|
||||
// support this. As this is part of EWMH (Extended Window Manager Hints), it's likely to
|
||||
// be unsupported by many smaller WMs.
|
||||
let extents: Option<Vec<c_ulong>> = self.get_property(
|
||||
window,
|
||||
extents_atom,
|
||||
ffi::XA_CARDINAL,
|
||||
).ok();
|
||||
|
||||
FrameExtentsHeuristic {
|
||||
frame_extents,
|
||||
heuristic_path: Supported,
|
||||
extents.and_then(|extents| {
|
||||
if extents.len() >= 4 {
|
||||
Some(FrameExtents {
|
||||
left: extents[0],
|
||||
right: extents[1],
|
||||
top: extents[2],
|
||||
bottom: extents[3],
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_top_level(&self, window: ffi::Window, root: ffi::Window) -> Option<bool> {
|
||||
let client_list_atom = unsafe { self.get_atom_unchecked(b"_NET_CLIENT_LIST\0") };
|
||||
|
||||
if !hint_is_supported(client_list_atom) {
|
||||
return None;
|
||||
}
|
||||
} else if nested {
|
||||
// If the position value we have is for a nested window used as the client area, we'll
|
||||
// just climb up the hierarchy and get the geometry of the outermost window we're
|
||||
// nested in.
|
||||
let outer_window = climb_hierarchy(xconn, window, root)
|
||||
.expect("Failed to climb window hierarchy");
|
||||
|
||||
let (outer_y, outer_width, outer_height) = {
|
||||
let outer_geometry = unsafe { get_geometry(xconn, outer_window) }
|
||||
.expect("Failed to get outer window geometry");
|
||||
let client_list: Option<Vec<ffi::Window>> = self.get_property(
|
||||
root,
|
||||
client_list_atom,
|
||||
ffi::XA_WINDOW,
|
||||
).ok();
|
||||
|
||||
client_list.map(|client_list| client_list.contains(&window))
|
||||
}
|
||||
|
||||
fn get_parent_window(&self, window: ffi::Window) -> Result<ffi::Window, XError> {
|
||||
let parent = unsafe {
|
||||
let mut root: ffi::Window = mem::uninitialized();
|
||||
let mut parent: ffi::Window = mem::uninitialized();
|
||||
let mut children: *mut ffi::Window = ptr::null_mut();
|
||||
let mut nchildren: c_uint = mem::uninitialized();
|
||||
|
||||
// What's filled into `parent` if `window` is the root window?
|
||||
let _status = (self.xlib.XQueryTree)(
|
||||
self.display,
|
||||
window,
|
||||
&mut root,
|
||||
&mut parent,
|
||||
&mut children,
|
||||
&mut nchildren,
|
||||
);
|
||||
|
||||
// The list of children isn't used
|
||||
if children != ptr::null_mut() {
|
||||
(self.xlib.XFree)(children as *mut _);
|
||||
}
|
||||
|
||||
parent
|
||||
};
|
||||
self.check_errors().map(|_| parent)
|
||||
}
|
||||
|
||||
fn climb_hierarchy(&self, window: ffi::Window, root: ffi::Window) -> Result<ffi::Window, XError> {
|
||||
let mut outer_window = window;
|
||||
loop {
|
||||
let candidate = self.get_parent_window(outer_window)?;
|
||||
if candidate == root {
|
||||
break;
|
||||
}
|
||||
outer_window = candidate;
|
||||
}
|
||||
Ok(outer_window)
|
||||
}
|
||||
|
||||
pub fn get_frame_extents_heuristic(&self, window: ffi::Window, root: ffi::Window) -> FrameExtentsHeuristic {
|
||||
use self::FrameExtentsHeuristicPath::*;
|
||||
|
||||
// Position relative to root window.
|
||||
// With rare exceptions, this is the position of a nested window. Cases where the window
|
||||
// isn't nested are outlined in the comments throghout this function, but in addition to
|
||||
// that, fullscreen windows often aren't nested.
|
||||
let (inner_y_rel_root, child) = {
|
||||
let coords = self.translate_coords(window, root).expect("Failed to translate window coordinates");
|
||||
(
|
||||
outer_geometry.y_rel_parent,
|
||||
outer_geometry.width,
|
||||
outer_geometry.height,
|
||||
coords.y_rel_root,
|
||||
coords.child,
|
||||
)
|
||||
};
|
||||
|
||||
// Since we have the geometry of the outermost window and the geometry of the client
|
||||
// area, we can figure out what's in between.
|
||||
let diff_x = outer_width.saturating_sub(width);
|
||||
let diff_y = outer_height.saturating_sub(height);
|
||||
let offset_y = inner_y_rel_root.saturating_sub(outer_y) as c_uint;
|
||||
let (width, height, border) = {
|
||||
let inner_geometry = self.get_geometry(window).expect("Failed to get inner window geometry");
|
||||
(
|
||||
inner_geometry.width,
|
||||
inner_geometry.height,
|
||||
inner_geometry.border,
|
||||
)
|
||||
};
|
||||
|
||||
let left = diff_x / 2;
|
||||
let right = left;
|
||||
let top = offset_y;
|
||||
let bottom = diff_y.saturating_sub(offset_y);
|
||||
// The first condition is only false for un-nested windows, but isn't always false for
|
||||
// un-nested windows. Mutter/Muffin/Budgie and Marco present a mysterious discrepancy:
|
||||
// when y is on the range [0, 2] and if the window has been unfocused since being
|
||||
// undecorated (or was undecorated upon construction), the first condition is true,
|
||||
// requiring us to rely on the second condition.
|
||||
let nested = !(window == child || self.is_top_level(child, root) == Some(true));
|
||||
|
||||
let frame_extents = FrameExtents::new(
|
||||
left.into(),
|
||||
right.into(),
|
||||
top.into(),
|
||||
bottom.into(),
|
||||
);
|
||||
FrameExtentsHeuristic {
|
||||
frame_extents,
|
||||
heuristic_path: UnsupportedNested,
|
||||
}
|
||||
} else {
|
||||
// This is the case for xmonad and dwm, AKA the only WMs tested that supplied a
|
||||
// border value. This is convenient, since we can use it to get an accurate frame.
|
||||
let frame_extents = FrameExtents::from_border(border.into());
|
||||
FrameExtentsHeuristic {
|
||||
frame_extents,
|
||||
heuristic_path: UnsupportedBordered,
|
||||
// Hopefully the WM supports EWMH, allowing us to get exact info on the window frames.
|
||||
if let Some(mut frame_extents) = self.get_frame_extents(window) {
|
||||
// Mutter/Muffin/Budgie and Marco preserve their decorated frame extents when
|
||||
// decorations are disabled, but since the window becomes un-nested, it's easy to
|
||||
// catch.
|
||||
if !nested {
|
||||
frame_extents = FrameExtents::new(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
// The difference between the nested window's position and the outermost window's
|
||||
// position is equivalent to the frame size. In most scenarios, this is equivalent to
|
||||
// manually climbing the hierarchy as is done in the case below. Here's a list of
|
||||
// known discrepancies:
|
||||
// * Mutter/Muffin/Budgie gives decorated windows a margin of 9px (only 7px on top) in
|
||||
// addition to a 1px semi-transparent border. The margin can be easily observed by
|
||||
// using a screenshot tool to get a screenshot of a selected window, and is
|
||||
// presumably used for drawing drop shadows. Getting window geometry information
|
||||
// via hierarchy-climbing results in this margin being included in both the
|
||||
// position and outer size, so a window positioned at (0, 0) would be reported as
|
||||
// having a position (-10, -8).
|
||||
// * Compiz has a drop shadow margin just like Mutter/Muffin/Budgie, though it's 10px
|
||||
// on all sides, and there's no additional border.
|
||||
// * Enlightenment otherwise gets a y position equivalent to inner_y_rel_root.
|
||||
// Without decorations, there's no difference. This is presumably related to
|
||||
// Enlightenment's fairly unique concept of window position; it interprets
|
||||
// positions given to XMoveWindow as a client area position rather than a position
|
||||
// of the overall window.
|
||||
|
||||
FrameExtentsHeuristic {
|
||||
frame_extents,
|
||||
heuristic_path: Supported,
|
||||
}
|
||||
} else if nested {
|
||||
// If the position value we have is for a nested window used as the client area, we'll
|
||||
// just climb up the hierarchy and get the geometry of the outermost window we're
|
||||
// nested in.
|
||||
let outer_window = self.climb_hierarchy(window, root).expect("Failed to climb window hierarchy");
|
||||
let (outer_y, outer_width, outer_height) = {
|
||||
let outer_geometry = self.get_geometry(outer_window).expect("Failed to get outer window geometry");
|
||||
(
|
||||
outer_geometry.y_rel_parent,
|
||||
outer_geometry.width,
|
||||
outer_geometry.height,
|
||||
)
|
||||
};
|
||||
|
||||
// Since we have the geometry of the outermost window and the geometry of the client
|
||||
// area, we can figure out what's in between.
|
||||
let diff_x = outer_width.saturating_sub(width);
|
||||
let diff_y = outer_height.saturating_sub(height);
|
||||
let offset_y = inner_y_rel_root.saturating_sub(outer_y) as c_uint;
|
||||
|
||||
let left = diff_x / 2;
|
||||
let right = left;
|
||||
let top = offset_y;
|
||||
let bottom = diff_y.saturating_sub(offset_y);
|
||||
|
||||
let frame_extents = FrameExtents::new(
|
||||
left.into(),
|
||||
right.into(),
|
||||
top.into(),
|
||||
bottom.into(),
|
||||
);
|
||||
FrameExtentsHeuristic {
|
||||
frame_extents,
|
||||
heuristic_path: UnsupportedNested,
|
||||
}
|
||||
} else {
|
||||
// This is the case for xmonad and dwm, AKA the only WMs tested that supplied a
|
||||
// border value. This is convenient, since we can use it to get an accurate frame.
|
||||
let frame_extents = FrameExtents::from_border(border.into());
|
||||
FrameExtentsHeuristic {
|
||||
frame_extents,
|
||||
heuristic_path: UnsupportedBordered,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub const MWM_HINTS_DECORATIONS: c_ulong = 2;
|
||||
@@ -6,12 +8,12 @@ pub const MWM_HINTS_DECORATIONS: c_ulong = 2;
|
||||
pub enum StateOperation {
|
||||
Remove = 0, // _NET_WM_STATE_REMOVE
|
||||
Add = 1, // _NET_WM_STATE_ADD
|
||||
_Toggle = 2, // _NET_WM_STATE_TOGGLE
|
||||
Toggle = 2, // _NET_WM_STATE_TOGGLE
|
||||
}
|
||||
|
||||
impl From<bool> for StateOperation {
|
||||
fn from(b: bool) -> Self {
|
||||
if b {
|
||||
fn from(op: bool) -> Self {
|
||||
if op {
|
||||
StateOperation::Add
|
||||
} else {
|
||||
StateOperation::Remove
|
||||
@@ -62,7 +64,30 @@ impl WindowType {
|
||||
&Dialog => b"_NET_WM_WINDOW_TYPE_DIALOG\0",
|
||||
&Normal => b"_NET_WM_WINDOW_TYPE_NORMAL\0",
|
||||
};
|
||||
unsafe { get_atom(xconn, atom_name) }
|
||||
.expect("Failed to get atom for `WindowType`")
|
||||
unsafe { xconn.get_atom_unchecked(atom_name) }
|
||||
}
|
||||
}
|
||||
|
||||
impl XConnection {
|
||||
pub fn get_wm_hints(&self, window: ffi::Window) -> Result<XSmartPointer<ffi::XWMHints>, XError> {
|
||||
let wm_hints = unsafe { (self.xlib.XGetWMHints)(self.display, window) };
|
||||
self.check_errors()?;
|
||||
let wm_hints = if wm_hints.is_null() {
|
||||
self.alloc_wm_hints()
|
||||
} else {
|
||||
XSmartPointer::new(self, wm_hints).unwrap()
|
||||
};
|
||||
Ok(wm_hints)
|
||||
}
|
||||
|
||||
pub fn set_wm_hints(&self, window: ffi::Window, wm_hints: XSmartPointer<ffi::XWMHints>) -> Flusher {
|
||||
unsafe {
|
||||
(self.xlib.XSetWMHints)(
|
||||
self.display,
|
||||
window,
|
||||
wm_hints.ptr,
|
||||
);
|
||||
}
|
||||
Flusher::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +1,12 @@
|
||||
use std::str;
|
||||
|
||||
use super::*;
|
||||
use events::ModifiersState;
|
||||
|
||||
pub unsafe fn select_xinput_events(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: c_ulong,
|
||||
device_id: c_int,
|
||||
mask: i32,
|
||||
) -> Flusher {
|
||||
let mut event_mask = ffi::XIEventMask {
|
||||
deviceid: device_id,
|
||||
mask: &mask as *const _ as *mut c_uchar,
|
||||
mask_len: mem::size_of_val(&mask) as c_int,
|
||||
};
|
||||
(xconn.xinput2.XISelectEvents)(
|
||||
xconn.display,
|
||||
window,
|
||||
&mut event_mask as *mut ffi::XIEventMask,
|
||||
1, // number of masks to read from pointer above
|
||||
);
|
||||
Flusher::new(xconn)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub unsafe fn select_xkb_events(
|
||||
xconn: &Arc<XConnection>,
|
||||
device_id: c_uint,
|
||||
mask: c_ulong,
|
||||
) -> Option<Flusher> {
|
||||
let status = (xconn.xlib.XkbSelectEvents)(
|
||||
xconn.display,
|
||||
device_id,
|
||||
mask,
|
||||
mask,
|
||||
);
|
||||
if status == ffi::True {
|
||||
Some(Flusher::new(xconn))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
// A base buffer size of 1kB uses a negligible amount of RAM while preventing us from having to
|
||||
// re-allocate (and make another round-trip) in the *vast* majority of cases.
|
||||
// To test if `lookup_utf8` works correctly, set this to 1.
|
||||
const TEXT_BUFFER_SIZE: usize = 1024;
|
||||
|
||||
impl From<ffi::XIModifierState> for ModifiersState {
|
||||
fn from(mods: ffi::XIModifierState) -> Self {
|
||||
@@ -53,17 +21,17 @@ impl From<ffi::XIModifierState> for ModifiersState {
|
||||
}
|
||||
|
||||
pub struct PointerState<'a> {
|
||||
xconn: &'a Arc<XConnection>,
|
||||
_root: ffi::Window,
|
||||
_child: ffi::Window,
|
||||
_root_x: c_double,
|
||||
_root_y: c_double,
|
||||
_win_x: c_double,
|
||||
_win_y: c_double,
|
||||
_buttons: ffi::XIButtonState,
|
||||
xconn: &'a XConnection,
|
||||
root: ffi::Window,
|
||||
child: ffi::Window,
|
||||
root_x: c_double,
|
||||
root_y: c_double,
|
||||
win_x: c_double,
|
||||
win_y: c_double,
|
||||
buttons: ffi::XIButtonState,
|
||||
modifiers: ffi::XIModifierState,
|
||||
_group: ffi::XIGroupState,
|
||||
_relative_to_window: bool,
|
||||
group: ffi::XIGroupState,
|
||||
relative_to_window: bool,
|
||||
}
|
||||
|
||||
impl<'a> PointerState<'a> {
|
||||
@@ -74,114 +42,115 @@ impl<'a> PointerState<'a> {
|
||||
|
||||
impl<'a> Drop for PointerState<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// This is why you need to read the docs carefully...
|
||||
(self.xconn.xlib.XFree)(self._buttons.mask as _);
|
||||
if !self.buttons.mask.is_null() {
|
||||
unsafe {
|
||||
// This is why you need to read the docs carefully...
|
||||
(self.xconn.xlib.XFree)(self.buttons.mask as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn query_pointer(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
device_id: c_int,
|
||||
) -> Result<PointerState, XError> {
|
||||
let mut root_return = mem::uninitialized();
|
||||
let mut child_return = mem::uninitialized();
|
||||
let mut root_x_return = mem::uninitialized();
|
||||
let mut root_y_return = mem::uninitialized();
|
||||
let mut win_x_return = mem::uninitialized();
|
||||
let mut win_y_return = mem::uninitialized();
|
||||
let mut buttons_return = mem::uninitialized();
|
||||
let mut modifiers_return = mem::uninitialized();
|
||||
let mut group_return = mem::uninitialized();
|
||||
impl XConnection {
|
||||
pub fn select_xinput_events(&self, window: c_ulong, device_id: c_int, mask: i32) -> Flusher {
|
||||
let mut event_mask = ffi::XIEventMask {
|
||||
deviceid: device_id,
|
||||
mask: &mask as *const _ as *mut c_uchar,
|
||||
mask_len: mem::size_of_val(&mask) as c_int,
|
||||
};
|
||||
unsafe {
|
||||
(self.xinput2.XISelectEvents)(
|
||||
self.display,
|
||||
window,
|
||||
&mut event_mask as *mut ffi::XIEventMask,
|
||||
1, // number of masks to read from pointer above
|
||||
);
|
||||
}
|
||||
Flusher::new(self)
|
||||
}
|
||||
|
||||
let relative_to_window = (xconn.xinput2.XIQueryPointer)(
|
||||
xconn.display,
|
||||
device_id,
|
||||
window,
|
||||
&mut root_return,
|
||||
&mut child_return,
|
||||
&mut root_x_return,
|
||||
&mut root_y_return,
|
||||
&mut win_x_return,
|
||||
&mut win_y_return,
|
||||
&mut buttons_return,
|
||||
&mut modifiers_return,
|
||||
&mut group_return,
|
||||
) == ffi::True;
|
||||
#[allow(dead_code)]
|
||||
pub fn select_xkb_events(&self, device_id: c_uint, mask: c_ulong) -> Option<Flusher> {
|
||||
let status = unsafe {
|
||||
(self.xlib.XkbSelectEvents)(
|
||||
self.display,
|
||||
device_id,
|
||||
mask,
|
||||
mask,
|
||||
)
|
||||
};
|
||||
if status == ffi::True {
|
||||
Some(Flusher::new(self))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
xconn.check_errors()?;
|
||||
pub fn query_pointer(&self, window: ffi::Window, device_id: c_int) -> Result<PointerState, XError> {
|
||||
unsafe {
|
||||
let mut pointer_state: PointerState = mem::uninitialized();
|
||||
pointer_state.xconn = self;
|
||||
pointer_state.relative_to_window = (self.xinput2.XIQueryPointer)(
|
||||
self.display,
|
||||
device_id,
|
||||
window,
|
||||
&mut pointer_state.root,
|
||||
&mut pointer_state.child,
|
||||
&mut pointer_state.root_x,
|
||||
&mut pointer_state.root_y,
|
||||
&mut pointer_state.win_x,
|
||||
&mut pointer_state.win_y,
|
||||
&mut pointer_state.buttons,
|
||||
&mut pointer_state.modifiers,
|
||||
&mut pointer_state.group,
|
||||
) == ffi::True;
|
||||
if let Err(err) = self.check_errors() {
|
||||
// Running the destrutor would be bad news for us...
|
||||
mem::forget(pointer_state);
|
||||
Err(err)
|
||||
} else {
|
||||
Ok(pointer_state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(PointerState {
|
||||
xconn,
|
||||
_root: root_return,
|
||||
_child: child_return,
|
||||
_root_x: root_x_return,
|
||||
_root_y: root_y_return,
|
||||
_win_x: win_x_return,
|
||||
_win_y: win_y_return,
|
||||
_buttons: buttons_return,
|
||||
modifiers: modifiers_return,
|
||||
_group: group_return,
|
||||
_relative_to_window: relative_to_window,
|
||||
})
|
||||
}
|
||||
fn lookup_utf8_inner(
|
||||
&self,
|
||||
ic: ffi::XIC,
|
||||
key_event: &mut ffi::XKeyEvent,
|
||||
buffer: &mut [u8],
|
||||
) -> (ffi::KeySym, ffi::Status, c_int) {
|
||||
let mut keysym: ffi::KeySym = 0;
|
||||
let mut status: ffi::Status = 0;
|
||||
let count = unsafe {
|
||||
(self.xlib.Xutf8LookupString)(
|
||||
ic,
|
||||
key_event,
|
||||
buffer.as_mut_ptr() as *mut c_char,
|
||||
buffer.len() as c_int,
|
||||
&mut keysym,
|
||||
&mut status,
|
||||
)
|
||||
};
|
||||
(keysym, status, count)
|
||||
}
|
||||
|
||||
unsafe fn lookup_utf8_inner(
|
||||
xconn: &Arc<XConnection>,
|
||||
ic: ffi::XIC,
|
||||
key_event: &mut ffi::XKeyEvent,
|
||||
buffer: &mut [u8],
|
||||
) -> (ffi::KeySym, ffi::Status, c_int) {
|
||||
let mut keysym: ffi::KeySym = 0;
|
||||
let mut status: ffi::Status = 0;
|
||||
let count = (xconn.xlib.Xutf8LookupString)(
|
||||
ic,
|
||||
key_event,
|
||||
buffer.as_mut_ptr() as *mut c_char,
|
||||
buffer.len() as c_int,
|
||||
&mut keysym,
|
||||
&mut status,
|
||||
);
|
||||
(keysym, status, count)
|
||||
}
|
||||
|
||||
// A base buffer size of 1kB uses a negligible amount of RAM while preventing us from having to
|
||||
// re-allocate (and make another round-trip) in the *vast* majority of cases.
|
||||
// To test if lookup_utf8 works correctly, set this to 1.
|
||||
const TEXT_BUFFER_SIZE: usize = 1024;
|
||||
|
||||
pub unsafe fn lookup_utf8(
|
||||
xconn: &Arc<XConnection>,
|
||||
ic: ffi::XIC,
|
||||
key_event: &mut ffi::XKeyEvent,
|
||||
) -> String {
|
||||
let mut buffer: [u8; TEXT_BUFFER_SIZE] = mem::uninitialized();
|
||||
let (_, status, count) = lookup_utf8_inner(
|
||||
xconn,
|
||||
ic,
|
||||
key_event,
|
||||
&mut buffer,
|
||||
);
|
||||
|
||||
// The buffer overflowed, so we'll make a new one on the heap.
|
||||
if status == ffi::XBufferOverflow {
|
||||
let mut buffer = Vec::with_capacity(count as usize);
|
||||
buffer.set_len(count as usize);
|
||||
let (_, _, new_count) = lookup_utf8_inner(
|
||||
xconn,
|
||||
ic,
|
||||
key_event,
|
||||
&mut buffer,
|
||||
);
|
||||
debug_assert_eq!(count, new_count);
|
||||
str::from_utf8(&buffer[..count as usize])
|
||||
.unwrap_or("")
|
||||
.to_string()
|
||||
} else {
|
||||
str::from_utf8(&buffer[..count as usize])
|
||||
.unwrap_or("")
|
||||
.to_string()
|
||||
pub fn lookup_utf8(&self, ic: ffi::XIC, key_event: &mut ffi::XKeyEvent) -> String {
|
||||
let mut buffer: [u8; TEXT_BUFFER_SIZE] = unsafe { mem::uninitialized() };
|
||||
let (_, status, count) = self.lookup_utf8_inner(ic, key_event, &mut buffer);
|
||||
// The buffer overflowed, so we'll make a new one on the heap.
|
||||
if status == ffi::XBufferOverflow {
|
||||
let mut buffer = Vec::with_capacity(count as usize);
|
||||
unsafe { buffer.set_len(count as usize) };
|
||||
let (_, _, new_count) = self.lookup_utf8_inner(ic, key_event, &mut buffer);
|
||||
debug_assert_eq!(count, new_count);
|
||||
str::from_utf8(&buffer[..count as usize])
|
||||
.unwrap_or("")
|
||||
.to_string()
|
||||
} else {
|
||||
str::from_utf8(&buffer[..count as usize])
|
||||
.unwrap_or("")
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
62
src/platform/linux/x11/util/memory.rs
Normal file
62
src/platform/linux/x11/util/memory.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct XSmartPointer<'a, T> {
|
||||
xconn: &'a XConnection,
|
||||
pub ptr: *mut T,
|
||||
}
|
||||
|
||||
impl<'a, T> XSmartPointer<'a, T> {
|
||||
// You're responsible for only passing things to this that should be XFree'd.
|
||||
// Returns None if ptr is null.
|
||||
pub fn new(xconn: &'a XConnection, ptr: *mut T) -> Option<Self> {
|
||||
if !ptr.is_null() {
|
||||
Some(XSmartPointer {
|
||||
xconn,
|
||||
ptr,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for XSmartPointer<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for XSmartPointer<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Drop for XSmartPointer<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XFree)(self.ptr as *mut _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl XConnection {
|
||||
pub fn alloc_class_hint(&self) -> XSmartPointer<ffi::XClassHint> {
|
||||
XSmartPointer::new(self, unsafe { (self.xlib.XAllocClassHint)() })
|
||||
.expect("`XAllocClassHint` returned null; out of memory")
|
||||
}
|
||||
|
||||
pub fn alloc_size_hints(&self) -> XSmartPointer<ffi::XSizeHints> {
|
||||
XSmartPointer::new(self, unsafe { (self.xlib.XAllocSizeHints)() })
|
||||
.expect("`XAllocSizeHints` returned null; out of memory")
|
||||
}
|
||||
|
||||
pub fn alloc_wm_hints(&self) -> XSmartPointer<ffi::XWMHints> {
|
||||
XSmartPointer::new(self, unsafe { (self.xlib.XAllocWMHints)() })
|
||||
.expect("`XAllocWMHints` returned null; out of memory")
|
||||
}
|
||||
}
|
||||
@@ -2,180 +2,90 @@
|
||||
// *results may vary
|
||||
|
||||
mod atom;
|
||||
mod client_msg;
|
||||
mod format;
|
||||
mod geometry;
|
||||
mod hint;
|
||||
mod icon;
|
||||
mod input;
|
||||
mod memory;
|
||||
mod randr;
|
||||
mod window_property;
|
||||
mod wm;
|
||||
|
||||
pub use self::atom::*;
|
||||
pub use self::client_msg::*;
|
||||
pub use self::format::*;
|
||||
pub use self::geometry::*;
|
||||
pub use self::hint::*;
|
||||
pub use self::icon::*;
|
||||
pub use self::input::*;
|
||||
pub use self::memory::*;
|
||||
pub use self::randr::*;
|
||||
pub use self::window_property::*;
|
||||
pub use self::wm::*;
|
||||
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::*;
|
||||
|
||||
use super::{ffi, XConnection, XError};
|
||||
|
||||
// This isn't actually the number of the bits in the format.
|
||||
// X11 does a match on this value to determine which type to call sizeof on.
|
||||
// Thus, we use 32 for c_long, since 32 maps to c_long which maps to 64.
|
||||
// ...if that sounds confusing, then you know why this enum is here.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Format {
|
||||
Char = 8,
|
||||
Short = 16,
|
||||
Long = 32,
|
||||
}
|
||||
|
||||
impl Format {
|
||||
pub fn from_format(format: usize) -> Option<Self> {
|
||||
match format {
|
||||
8 => Some(Format::Char),
|
||||
16 => Some(Format::Short),
|
||||
32 => Some(Format::Long),
|
||||
_ => None,
|
||||
}
|
||||
pub fn maybe_change<T: PartialEq>(field: &mut Option<T>, value: T) -> bool {
|
||||
let wrapped = Some(value);
|
||||
if *field != wrapped {
|
||||
*field = wrapped;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn is_same_size_as<T>(&self) -> bool {
|
||||
mem::size_of::<T>() == self.get_actual_size()
|
||||
}
|
||||
|
||||
pub fn get_actual_size(&self) -> usize {
|
||||
match self {
|
||||
&Format::Char => mem::size_of::<c_char>(),
|
||||
&Format::Short => mem::size_of::<c_short>(),
|
||||
&Format::Long => mem::size_of::<c_long>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct XSmartPointer<'a, T> {
|
||||
xconn: &'a Arc<XConnection>,
|
||||
pub ptr: *mut T,
|
||||
}
|
||||
|
||||
impl<'a, T> XSmartPointer<'a, T> {
|
||||
// You're responsible for only passing things to this that should be XFree'd.
|
||||
// Returns None if ptr is null.
|
||||
pub fn new(xconn: &'a Arc<XConnection>, ptr: *mut T) -> Option<Self> {
|
||||
if !ptr.is_null() {
|
||||
Some(XSmartPointer {
|
||||
xconn,
|
||||
ptr,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for XSmartPointer<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for XSmartPointer<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Drop for XSmartPointer<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XFree)(self.ptr as *mut _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is impoartant, so pay attention!
|
||||
// Xlib has an output buffer, and tries to hide the async nature of X from you.
|
||||
// This buffer contains the requests you make, and is flushed under various circumstances:
|
||||
// 1. XPending, XNextEvent, and XWindowEvent flush "as needed"
|
||||
// 2. XFlush explicitly flushes
|
||||
// 3. XSync flushes and blocks until all requests are responded to
|
||||
// 4. Calls that have a return dependent on a response (i.e. XGetWindowProperty) sync internally.
|
||||
// When in doubt, check the X11 source; if a function calls _XReply, it flushes and waits.
|
||||
// All util functions that abstract an async function will return a Flusher.
|
||||
pub unsafe fn flush_requests(xconn: &Arc<XConnection>) -> Result<(), XError> {
|
||||
(xconn.xlib.XFlush)(xconn.display);
|
||||
//println!("XFlush");
|
||||
// This isn't necessarily a useful time to check for errors (since our request hasn't
|
||||
// necessarily been processed yet)
|
||||
xconn.check_errors()
|
||||
}
|
||||
|
||||
pub unsafe fn sync_with_server(xconn: &Arc<XConnection>) -> Result<(), XError> {
|
||||
(xconn.xlib.XSync)(xconn.display, ffi::False);
|
||||
//println!("XSync");
|
||||
xconn.check_errors()
|
||||
}
|
||||
|
||||
#[must_use = "This request was made asynchronously, and is still in the output buffer. You must explicitly choose to either `.flush()` (empty the output buffer, sending the request now) or `.queue()` (wait to send the request, allowing you to continue to add more requests without additional round-trips). For more information, see the documentation for `util::flush_requests`."]
|
||||
pub struct Flusher<'a> {
|
||||
xconn: &'a Arc<XConnection>,
|
||||
xconn: &'a XConnection,
|
||||
}
|
||||
|
||||
impl<'a> Flusher<'a> {
|
||||
pub fn new(xconn: &'a Arc<XConnection>) -> Self {
|
||||
pub fn new(xconn: &'a XConnection) -> Self {
|
||||
Flusher { xconn }
|
||||
}
|
||||
|
||||
// "I want this request sent now!"
|
||||
pub fn flush(self) -> Result<(), XError> {
|
||||
unsafe { flush_requests(self.xconn) }
|
||||
self.xconn.flush_requests()
|
||||
}
|
||||
|
||||
// "I want the response now too!"
|
||||
pub fn sync(self) -> Result<(), XError> {
|
||||
self.xconn.sync_with_server()
|
||||
}
|
||||
|
||||
// "I'm aware that this request hasn't been sent, and I'm okay with waiting."
|
||||
pub fn queue(self) {}
|
||||
}
|
||||
|
||||
pub unsafe fn send_client_msg(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: c_ulong, // The window this is "about"; not necessarily this window
|
||||
target_window: c_ulong, // The window we're sending to
|
||||
message_type: ffi::Atom,
|
||||
event_mask: Option<c_long>,
|
||||
data: (c_long, c_long, c_long, c_long, c_long),
|
||||
) -> Flusher {
|
||||
let mut event: ffi::XClientMessageEvent = mem::uninitialized();
|
||||
event.type_ = ffi::ClientMessage;
|
||||
event.display = xconn.display;
|
||||
event.window = window;
|
||||
event.message_type = message_type;
|
||||
event.format = Format::Long as c_int;
|
||||
event.data = ffi::ClientMessageData::new();
|
||||
event.data.set_long(0, data.0);
|
||||
event.data.set_long(1, data.1);
|
||||
event.data.set_long(2, data.2);
|
||||
event.data.set_long(3, data.3);
|
||||
event.data.set_long(4, data.4);
|
||||
impl XConnection {
|
||||
// This is impoartant, so pay attention!
|
||||
// Xlib has an output buffer, and tries to hide the async nature of X from you.
|
||||
// This buffer contains the requests you make, and is flushed under various circumstances:
|
||||
// 1. `XPending`, `XNextEvent`, and `XWindowEvent` flush "as needed"
|
||||
// 2. `XFlush` explicitly flushes
|
||||
// 3. `XSync` flushes and blocks until all requests are responded to
|
||||
// 4. Calls that have a return dependent on a response (i.e. `XGetWindowProperty`) sync internally.
|
||||
// When in doubt, check the X11 source; if a function calls `_XReply`, it flushes and waits.
|
||||
// All util functions that abstract an async function will return a `Flusher`.
|
||||
pub fn flush_requests(&self) -> Result<(), XError> {
|
||||
unsafe { (self.xlib.XFlush)(self.display) };
|
||||
//println!("XFlush");
|
||||
// This isn't necessarily a useful time to check for errors (since our request hasn't
|
||||
// necessarily been processed yet)
|
||||
self.check_errors()
|
||||
}
|
||||
|
||||
let event_mask = event_mask.unwrap_or(ffi::NoEventMask);
|
||||
|
||||
(xconn.xlib.XSendEvent)(
|
||||
xconn.display,
|
||||
target_window,
|
||||
ffi::False,
|
||||
event_mask,
|
||||
&mut event.into(),
|
||||
);
|
||||
|
||||
Flusher::new(xconn)
|
||||
pub fn sync_with_server(&self) -> Result<(), XError> {
|
||||
unsafe { (self.xlib.XSync)(self.display, ffi::False) };
|
||||
//println!("XSync");
|
||||
self.check_errors()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::slice;
|
||||
use std::{env, slice};
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::*;
|
||||
use super::ffi::{
|
||||
@@ -53,6 +54,22 @@ pub fn calc_dpi_factor(
|
||||
(width_px, height_px): (u32, u32),
|
||||
(width_mm, height_mm): (u64, u64),
|
||||
) -> f64 {
|
||||
// Override DPI if `WINIT_HIDPI_FACTOR` variable is set
|
||||
if let Ok(dpi_factor_str) = env::var("WINIT_HIDPI_FACTOR") {
|
||||
if let Ok(dpi_factor) = f64::from_str(&dpi_factor_str) {
|
||||
if dpi_factor <= 0. {
|
||||
panic!("Expected `WINIT_HIDPI_FACTOR` to be bigger than 0, got '{}'", dpi_factor);
|
||||
}
|
||||
|
||||
return dpi_factor;
|
||||
}
|
||||
}
|
||||
|
||||
// See http://xpra.org/trac/ticket/728 for more information
|
||||
if width_mm == 0 || width_mm == 0 {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
let ppmm = (
|
||||
(width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)
|
||||
).sqrt();
|
||||
@@ -60,25 +77,23 @@ pub fn calc_dpi_factor(
|
||||
((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0)
|
||||
}
|
||||
|
||||
pub unsafe fn get_output_info(
|
||||
xconn: &Arc<XConnection>,
|
||||
resources: *mut XRRScreenResources,
|
||||
repr: &MonitorRepr,
|
||||
) -> (String, f32) {
|
||||
let output_info = (xconn.xrandr.XRRGetOutputInfo)(
|
||||
xconn.display,
|
||||
resources,
|
||||
repr.get_output(),
|
||||
);
|
||||
let name_slice = slice::from_raw_parts(
|
||||
(*output_info).name as *mut u8,
|
||||
(*output_info).nameLen as usize,
|
||||
);
|
||||
let name = String::from_utf8_lossy(name_slice).into();
|
||||
let hidpi_factor = calc_dpi_factor(
|
||||
repr.get_dimensions(),
|
||||
((*output_info).mm_width as u64, (*output_info).mm_height as u64),
|
||||
) as f32;
|
||||
(xconn.xrandr.XRRFreeOutputInfo)(output_info);
|
||||
(name, hidpi_factor)
|
||||
impl XConnection {
|
||||
pub unsafe fn get_output_info(&self, resources: *mut XRRScreenResources, repr: &MonitorRepr) -> (String, f32) {
|
||||
let output_info = (self.xrandr.XRRGetOutputInfo)(
|
||||
self.display,
|
||||
resources,
|
||||
repr.get_output(),
|
||||
);
|
||||
let name_slice = slice::from_raw_parts(
|
||||
(*output_info).name as *mut u8,
|
||||
(*output_info).nameLen as usize,
|
||||
);
|
||||
let name = String::from_utf8_lossy(name_slice).into();
|
||||
let hidpi_factor = calc_dpi_factor(
|
||||
repr.get_dimensions(),
|
||||
((*output_info).mm_width as u64, (*output_info).mm_height as u64),
|
||||
) as f32;
|
||||
(self.xrandr.XRRFreeOutputInfo)(output_info);
|
||||
(name, hidpi_factor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -25,139 +24,121 @@ impl GetPropertyError {
|
||||
}
|
||||
|
||||
// Number of 32-bit chunks to retrieve per iteration of get_property's inner loop.
|
||||
// To test if get_property works correctly, set this to 1.
|
||||
// To test if `get_property` works correctly, set this to 1.
|
||||
const PROPERTY_BUFFER_SIZE: c_long = 1024; // 4k of RAM ought to be enough for anyone!
|
||||
|
||||
pub unsafe fn get_property<T: Debug + Clone>(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: c_ulong,
|
||||
property: ffi::Atom,
|
||||
property_type: ffi::Atom,
|
||||
) -> Result<Vec<T>, GetPropertyError> {
|
||||
let mut data = Vec::new();
|
||||
let mut offset = 0;
|
||||
|
||||
let mut done = false;
|
||||
while !done {
|
||||
let mut actual_type: ffi::Atom = mem::uninitialized();
|
||||
let mut actual_format: c_int = mem::uninitialized();
|
||||
let mut quantity_returned: c_ulong = mem::uninitialized();
|
||||
let mut bytes_after: c_ulong = mem::uninitialized();
|
||||
let mut buf: *mut c_uchar = ptr::null_mut();
|
||||
(xconn.xlib.XGetWindowProperty)(
|
||||
xconn.display,
|
||||
window,
|
||||
property,
|
||||
// This offset is in terms of 32-bit chunks.
|
||||
offset,
|
||||
// This is the quanity of 32-bit chunks to receive at once.
|
||||
PROPERTY_BUFFER_SIZE,
|
||||
ffi::False,
|
||||
property_type,
|
||||
&mut actual_type,
|
||||
&mut actual_format,
|
||||
// This is the quantity of items we retrieved in our format, NOT of 32-bit chunks!
|
||||
&mut quantity_returned,
|
||||
// ...and this is a quantity of bytes. So, this function deals in 3 different units.
|
||||
&mut bytes_after,
|
||||
&mut buf,
|
||||
);
|
||||
|
||||
if let Err(e) = xconn.check_errors() {
|
||||
return Err(GetPropertyError::XError(e));
|
||||
}
|
||||
|
||||
if actual_type != property_type {
|
||||
return Err(GetPropertyError::TypeMismatch(actual_type));
|
||||
}
|
||||
|
||||
let format_mismatch = Format::from_format(actual_format as _)
|
||||
.map(|actual_format| !actual_format.is_same_size_as::<T>())
|
||||
// This won't actually be reached; the XError condition above is triggered first.
|
||||
.unwrap_or(true);
|
||||
|
||||
if format_mismatch {
|
||||
return Err(GetPropertyError::FormatMismatch(actual_format));
|
||||
}
|
||||
|
||||
if !buf.is_null() {
|
||||
offset += PROPERTY_BUFFER_SIZE;
|
||||
let new_data = std::slice::from_raw_parts(
|
||||
buf as *mut T,
|
||||
quantity_returned as usize,
|
||||
);
|
||||
/*println!(
|
||||
"XGetWindowProperty prop:{:?} fmt:{:02} len:{:02} off:{:02} out:{:02}, buf:{:?}",
|
||||
property,
|
||||
mem::size_of::<T>() * 8,
|
||||
data.len(),
|
||||
offset,
|
||||
quantity_returned,
|
||||
new_data,
|
||||
);*/
|
||||
data.extend_from_slice(&new_data);
|
||||
// Fun fact: XGetWindowProperty allocates one extra byte at the end.
|
||||
(xconn.xlib.XFree)(buf as _); // Don't try to access new_data after this.
|
||||
} else {
|
||||
return Err(GetPropertyError::NothingAllocated);
|
||||
}
|
||||
|
||||
done = bytes_after == 0;
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PropMode {
|
||||
Replace = ffi::PropModeReplace as isize,
|
||||
_Prepend = ffi::PropModePrepend as isize,
|
||||
_Append = ffi::PropModeAppend as isize,
|
||||
Prepend = ffi::PropModePrepend as isize,
|
||||
Append = ffi::PropModeAppend as isize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InvalidFormat {
|
||||
format_used: Format,
|
||||
size_passed: usize,
|
||||
size_expected: usize,
|
||||
}
|
||||
impl XConnection {
|
||||
pub fn get_property<T: Formattable>(
|
||||
&self,
|
||||
window: c_ulong,
|
||||
property: ffi::Atom,
|
||||
property_type: ffi::Atom,
|
||||
) -> Result<Vec<T>, GetPropertyError> {
|
||||
let mut data = Vec::new();
|
||||
let mut offset = 0;
|
||||
|
||||
pub unsafe fn change_property<'a, T: Debug>(
|
||||
xconn: &'a Arc<XConnection>,
|
||||
window: c_ulong,
|
||||
property: ffi::Atom,
|
||||
property_type: ffi::Atom,
|
||||
format: Format,
|
||||
mode: PropMode,
|
||||
new_value: &[T],
|
||||
) -> Flusher<'a> {
|
||||
if !format.is_same_size_as::<T>() {
|
||||
panic!(format!(
|
||||
"[winit developer error] Incorrect usage of `util::change_property`: {:#?}",
|
||||
InvalidFormat {
|
||||
format_used: format,
|
||||
size_passed: mem::size_of::<T>() * 8,
|
||||
size_expected: format.get_actual_size() * 8,
|
||||
},
|
||||
));
|
||||
let mut done = false;
|
||||
while !done {
|
||||
unsafe {
|
||||
let mut actual_type: ffi::Atom = mem::uninitialized();
|
||||
let mut actual_format: c_int = mem::uninitialized();
|
||||
let mut quantity_returned: c_ulong = mem::uninitialized();
|
||||
let mut bytes_after: c_ulong = mem::uninitialized();
|
||||
let mut buf: *mut c_uchar = ptr::null_mut();
|
||||
(self.xlib.XGetWindowProperty)(
|
||||
self.display,
|
||||
window,
|
||||
property,
|
||||
// This offset is in terms of 32-bit chunks.
|
||||
offset,
|
||||
// This is the quanity of 32-bit chunks to receive at once.
|
||||
PROPERTY_BUFFER_SIZE,
|
||||
ffi::False,
|
||||
property_type,
|
||||
&mut actual_type,
|
||||
&mut actual_format,
|
||||
// This is the quantity of items we retrieved in our format, NOT of 32-bit chunks!
|
||||
&mut quantity_returned,
|
||||
// ...and this is a quantity of bytes. So, this function deals in 3 different units.
|
||||
&mut bytes_after,
|
||||
&mut buf,
|
||||
);
|
||||
|
||||
if let Err(e) = self.check_errors() {
|
||||
return Err(GetPropertyError::XError(e));
|
||||
}
|
||||
|
||||
if actual_type != property_type {
|
||||
return Err(GetPropertyError::TypeMismatch(actual_type));
|
||||
}
|
||||
|
||||
let format_mismatch = Format::from_format(actual_format as _) != Some(T::FORMAT);
|
||||
if format_mismatch {
|
||||
return Err(GetPropertyError::FormatMismatch(actual_format));
|
||||
}
|
||||
|
||||
if !buf.is_null() {
|
||||
offset += PROPERTY_BUFFER_SIZE;
|
||||
let new_data = std::slice::from_raw_parts(
|
||||
buf as *mut T,
|
||||
quantity_returned as usize,
|
||||
);
|
||||
/*println!(
|
||||
"XGetWindowProperty prop:{:?} fmt:{:02} len:{:02} off:{:02} out:{:02}, buf:{:?}",
|
||||
property,
|
||||
mem::size_of::<T>() * 8,
|
||||
data.len(),
|
||||
offset,
|
||||
quantity_returned,
|
||||
new_data,
|
||||
);*/
|
||||
data.extend_from_slice(&new_data);
|
||||
// Fun fact: XGetWindowProperty allocates one extra byte at the end.
|
||||
(self.xlib.XFree)(buf as _); // Don't try to access new_data after this.
|
||||
} else {
|
||||
return Err(GetPropertyError::NothingAllocated);
|
||||
}
|
||||
|
||||
done = bytes_after == 0;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
(xconn.xlib.XChangeProperty)(
|
||||
xconn.display,
|
||||
window,
|
||||
property,
|
||||
property_type,
|
||||
format as c_int,
|
||||
mode as c_int,
|
||||
new_value.as_ptr() as *const c_uchar,
|
||||
new_value.len() as c_int,
|
||||
);
|
||||
|
||||
/*println!(
|
||||
"XChangeProperty prop:{:?} val:{:?}",
|
||||
property,
|
||||
new_value,
|
||||
);*/
|
||||
|
||||
Flusher::new(xconn)
|
||||
pub fn change_property<'a, T: Formattable>(
|
||||
&'a self,
|
||||
window: c_ulong,
|
||||
property: ffi::Atom,
|
||||
property_type: ffi::Atom,
|
||||
mode: PropMode,
|
||||
new_value: &[T],
|
||||
) -> Flusher<'a> {
|
||||
debug_assert_eq!(mem::size_of::<T>(), T::FORMAT.get_actual_size());
|
||||
unsafe {
|
||||
(self.xlib.XChangeProperty)(
|
||||
self.display,
|
||||
window,
|
||||
property,
|
||||
property_type,
|
||||
T::FORMAT as c_int,
|
||||
mode as c_int,
|
||||
new_value.as_ptr() as *const c_uchar,
|
||||
new_value.len() as c_int,
|
||||
);
|
||||
}
|
||||
/*println!(
|
||||
"XChangeProperty prop:{:?} val:{:?}",
|
||||
property,
|
||||
new_value,
|
||||
);*/
|
||||
Flusher::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,139 +20,122 @@ pub fn wm_name_is_one_of(names: &[&str]) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_cached_wm_info(xconn: &Arc<XConnection>, root: ffi::Window) {
|
||||
*SUPPORTED_HINTS.lock() = self::get_supported_hints(xconn, root);
|
||||
*WM_NAME.lock() = self::get_wm_name(xconn, root);
|
||||
}
|
||||
impl XConnection {
|
||||
pub fn update_cached_wm_info(&self, root: ffi::Window) {
|
||||
*SUPPORTED_HINTS.lock() = self.get_supported_hints(root);
|
||||
*WM_NAME.lock() = self.get_wm_name(root);
|
||||
}
|
||||
|
||||
fn get_supported_hints(xconn: &Arc<XConnection>, root: ffi::Window) -> Vec<ffi::Atom> {
|
||||
let supported_atom = unsafe { self::get_atom(xconn, b"_NET_SUPPORTED\0") }
|
||||
.expect("Failed to call XInternAtom (_NET_SUPPORTED)");
|
||||
unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
fn get_supported_hints(&self, root: ffi::Window) -> Vec<ffi::Atom> {
|
||||
let supported_atom = unsafe { self.get_atom_unchecked(b"_NET_SUPPORTED\0") };
|
||||
self.get_property(
|
||||
root,
|
||||
supported_atom,
|
||||
ffi::XA_ATOM,
|
||||
)
|
||||
}.unwrap_or_else(|_| Vec::with_capacity(0))
|
||||
}
|
||||
).unwrap_or_else(|_| Vec::with_capacity(0))
|
||||
}
|
||||
|
||||
fn get_wm_name(xconn: &Arc<XConnection>, root: ffi::Window) -> Option<String> {
|
||||
let check_atom = unsafe { self::get_atom(xconn, b"_NET_SUPPORTING_WM_CHECK\0") }
|
||||
.expect("Failed to call XInternAtom (_NET_SUPPORTING_WM_CHECK)");
|
||||
let wm_name_atom = unsafe { self::get_atom(xconn, b"_NET_WM_NAME\0") }
|
||||
.expect("Failed to call XInternAtom (_NET_WM_NAME)");
|
||||
fn get_wm_name(&self, root: ffi::Window) -> Option<String> {
|
||||
let check_atom = unsafe { self.get_atom_unchecked(b"_NET_SUPPORTING_WM_CHECK\0") };
|
||||
let wm_name_atom = unsafe { self.get_atom_unchecked(b"_NET_WM_NAME\0") };
|
||||
|
||||
// Mutter/Muffin/Budgie doesn't have _NET_SUPPORTING_WM_CHECK in its _NET_SUPPORTED, despite
|
||||
// it working and being supported. This has been reported upstream, but due to the
|
||||
// inavailability of time machines, we'll just try to get _NET_SUPPORTING_WM_CHECK
|
||||
// regardless of whether or not the WM claims to support it.
|
||||
//
|
||||
// Blackbox 0.70 also incorrectly reports not supporting this, though that appears to be fixed
|
||||
// in 0.72.
|
||||
/*if !supported_hints.contains(&check_atom) {
|
||||
return None;
|
||||
}*/
|
||||
// Mutter/Muffin/Budgie doesn't have _NET_SUPPORTING_WM_CHECK in its _NET_SUPPORTED, despite
|
||||
// it working and being supported. This has been reported upstream, but due to the
|
||||
// inavailability of time machines, we'll just try to get _NET_SUPPORTING_WM_CHECK
|
||||
// regardless of whether or not the WM claims to support it.
|
||||
//
|
||||
// Blackbox 0.70 also incorrectly reports not supporting this, though that appears to be fixed
|
||||
// in 0.72.
|
||||
/*if !supported_hints.contains(&check_atom) {
|
||||
return None;
|
||||
}*/
|
||||
|
||||
// IceWM (1.3.x and earlier) doesn't report supporting _NET_WM_NAME, but will nonetheless
|
||||
// provide us with a value for it. Note that the unofficial 1.4 fork of IceWM works fine.
|
||||
/*if !supported_hints.contains(&wm_name_atom) {
|
||||
return None;
|
||||
}*/
|
||||
// IceWM (1.3.x and earlier) doesn't report supporting _NET_WM_NAME, but will nonetheless
|
||||
// provide us with a value for it. Note that the unofficial 1.4 fork of IceWM works fine.
|
||||
/*if !supported_hints.contains(&wm_name_atom) {
|
||||
return None;
|
||||
}*/
|
||||
|
||||
// Of the WMs tested, only xmonad and dwm fail to provide a WM name.
|
||||
// Of the WMs tested, only xmonad and dwm fail to provide a WM name.
|
||||
|
||||
// Querying this property on the root window will give us the ID of a child window created by
|
||||
// the WM.
|
||||
let root_window_wm_check = {
|
||||
let result = unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
// Querying this property on the root window will give us the ID of a child window created by
|
||||
// the WM.
|
||||
let root_window_wm_check = {
|
||||
let result = self.get_property(
|
||||
root,
|
||||
check_atom,
|
||||
ffi::XA_WINDOW,
|
||||
)
|
||||
);
|
||||
|
||||
let wm_check = result
|
||||
.ok()
|
||||
.and_then(|wm_check| wm_check.get(0).cloned());
|
||||
|
||||
if let Some(wm_check) = wm_check {
|
||||
wm_check
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let wm_check = result
|
||||
.ok()
|
||||
.and_then(|wm_check| wm_check.get(0).cloned());
|
||||
|
||||
if let Some(wm_check) = wm_check {
|
||||
wm_check
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// Querying the same property on the child window we were given, we should get this child
|
||||
// window's ID again.
|
||||
let child_window_wm_check = {
|
||||
let result = unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
// Querying the same property on the child window we were given, we should get this child
|
||||
// window's ID again.
|
||||
let child_window_wm_check = {
|
||||
let result = self.get_property(
|
||||
root_window_wm_check,
|
||||
check_atom,
|
||||
ffi::XA_WINDOW,
|
||||
)
|
||||
);
|
||||
|
||||
let wm_check = result
|
||||
.ok()
|
||||
.and_then(|wm_check| wm_check.get(0).cloned());
|
||||
|
||||
if let Some(wm_check) = wm_check {
|
||||
wm_check
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let wm_check = result
|
||||
.ok()
|
||||
.and_then(|wm_check| wm_check.get(0).cloned());
|
||||
|
||||
if let Some(wm_check) = wm_check {
|
||||
wm_check
|
||||
} else {
|
||||
// These values should be the same.
|
||||
if root_window_wm_check != child_window_wm_check {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// These values should be the same.
|
||||
if root_window_wm_check != child_window_wm_check {
|
||||
return None;
|
||||
}
|
||||
// All of that work gives us a window ID that we can get the WM name from.
|
||||
let wm_name = {
|
||||
let utf8_string_atom = unsafe { self.get_atom_unchecked(b"UTF8_STRING\0") };
|
||||
|
||||
// All of that work gives us a window ID that we can get the WM name from.
|
||||
let wm_name = {
|
||||
let utf8_string_atom = unsafe { self::get_atom(xconn, b"UTF8_STRING\0") }
|
||||
.expect("Failed to call XInternAtom (UTF8_STRING)");
|
||||
|
||||
let result = unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
let result = self.get_property(
|
||||
root_window_wm_check,
|
||||
wm_name_atom,
|
||||
utf8_string_atom,
|
||||
)
|
||||
};
|
||||
);
|
||||
|
||||
// IceWM requires this. IceWM was also the only WM tested that returns a null-terminated
|
||||
// string. For more fun trivia, IceWM is also unique in including version and uname
|
||||
// information in this string (this means you'll have to be careful if you want to match
|
||||
// against it, though).
|
||||
// The unofficial 1.4 fork of IceWM still includes the extra details, but properly
|
||||
// returns a UTF8 string that isn't null-terminated.
|
||||
let no_utf8 = if let Err(ref err) = result {
|
||||
err.is_actual_property_type(ffi::XA_STRING)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
// IceWM requires this. IceWM was also the only WM tested that returns a null-terminated
|
||||
// string. For more fun trivia, IceWM is also unique in including version and uname
|
||||
// information in this string (this means you'll have to be careful if you want to match
|
||||
// against it, though).
|
||||
// The unofficial 1.4 fork of IceWM still includes the extra details, but properly
|
||||
// returns a UTF8 string that isn't null-terminated.
|
||||
let no_utf8 = if let Err(ref err) = result {
|
||||
err.is_actual_property_type(ffi::XA_STRING)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if no_utf8 {
|
||||
unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
if no_utf8 {
|
||||
self.get_property(
|
||||
root_window_wm_check,
|
||||
wm_name_atom,
|
||||
ffi::XA_STRING,
|
||||
)
|
||||
} else {
|
||||
result
|
||||
}
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}.ok();
|
||||
}.ok();
|
||||
|
||||
wm_name.and_then(|wm_name| String::from_utf8(wm_name).ok())
|
||||
wm_name.and_then(|wm_name| String::from_utf8(wm_name).ok())
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
use {ControlFlow, EventsLoopClosed};
|
||||
use cocoa::{self, appkit, foundation};
|
||||
use cocoa::appkit::{NSApplication, NSEvent, NSEventMask, NSEventModifierFlags, NSEventPhase, NSView, NSWindow};
|
||||
use events::{self, ElementState, Event, MouseButton, TouchPhase, WindowEvent, DeviceEvent, ModifiersState, KeyboardInput};
|
||||
use events::{self, ElementState, Event, TouchPhase, WindowEvent, DeviceEvent, ModifiersState, KeyboardInput};
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
use super::window::Window2;
|
||||
@@ -9,7 +9,6 @@ use std;
|
||||
use std::os::raw::*;
|
||||
use super::DeviceId;
|
||||
|
||||
|
||||
pub struct EventsLoop {
|
||||
modifiers: Modifiers,
|
||||
pub shared: Arc<Shared>,
|
||||
@@ -363,13 +362,6 @@ impl EventsLoop {
|
||||
event
|
||||
},
|
||||
|
||||
appkit::NSLeftMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Left, modifiers: event_mods(ns_event) })) },
|
||||
appkit::NSLeftMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Left, modifiers: event_mods(ns_event) })) },
|
||||
appkit::NSRightMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Right, modifiers: event_mods(ns_event) })) },
|
||||
appkit::NSRightMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Right, modifiers: event_mods(ns_event) })) },
|
||||
appkit::NSOtherMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Middle, modifiers: event_mods(ns_event) })) },
|
||||
appkit::NSOtherMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Middle, modifiers: event_mods(ns_event) })) },
|
||||
|
||||
appkit::NSMouseEntered => {
|
||||
let window = match maybe_window.or_else(maybe_key_window) {
|
||||
Some(window) => window,
|
||||
@@ -410,27 +402,9 @@ impl EventsLoop {
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let window_point = ns_event.locationInWindow();
|
||||
let view_point = if ns_window == cocoa::base::nil {
|
||||
let ns_size = foundation::NSSize::new(0.0, 0.0);
|
||||
let ns_rect = foundation::NSRect::new(window_point, ns_size);
|
||||
let window_rect = window.window.convertRectFromScreen_(ns_rect);
|
||||
window.view.convertPoint_fromView_(window_rect.origin, cocoa::base::nil)
|
||||
} else {
|
||||
window.view.convertPoint_fromView_(window_point, cocoa::base::nil)
|
||||
};
|
||||
let view_rect = NSView::frame(*window.view);
|
||||
let scale_factor = window.hidpi_factor();
|
||||
|
||||
let mut events = std::collections::VecDeque::new();
|
||||
|
||||
{
|
||||
let x = (scale_factor * view_point.x as f32) as f64;
|
||||
let y = (scale_factor * (view_rect.size.height - view_point.y) as f32) as f64;
|
||||
let window_event = WindowEvent::CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event_mods(ns_event) };
|
||||
let event = Event::WindowEvent { window_id: ::WindowId(window.id()), event: window_event };
|
||||
events.push_back(event);
|
||||
}
|
||||
let mut events = std::collections::VecDeque::with_capacity(3);
|
||||
|
||||
let delta_x = (scale_factor * ns_event.deltaX() as f32) as f64;
|
||||
if delta_x != 0.0 {
|
||||
@@ -598,14 +572,14 @@ pub fn to_virtual_key_code(code: c_ushort) -> Option<events::VirtualKeyCode> {
|
||||
0x33 => events::VirtualKeyCode::Back,
|
||||
//0x34 => unkown,
|
||||
0x35 => events::VirtualKeyCode::Escape,
|
||||
0x36 => events::VirtualKeyCode::RWin,
|
||||
0x37 => events::VirtualKeyCode::LWin,
|
||||
0x36 => events::VirtualKeyCode::LWin,
|
||||
0x37 => events::VirtualKeyCode::RWin,
|
||||
0x38 => events::VirtualKeyCode::LShift,
|
||||
//0x39 => Caps lock,
|
||||
//0x3a => Left alt,
|
||||
0x3a => events::VirtualKeyCode::LAlt,
|
||||
0x3b => events::VirtualKeyCode::LControl,
|
||||
0x3c => events::VirtualKeyCode::RShift,
|
||||
//0x3d => Right alt,
|
||||
0x3d => events::VirtualKeyCode::RAlt,
|
||||
0x3e => events::VirtualKeyCode::RControl,
|
||||
//0x3f => Fn key,
|
||||
//0x40 => F17 Key,
|
||||
@@ -695,7 +669,7 @@ unsafe fn modifier_event(
|
||||
keymask: NSEventModifierFlags,
|
||||
key_pressed: bool,
|
||||
) -> Option<WindowEvent> {
|
||||
if !key_pressed && NSEvent::modifierFlags(ns_event).contains(keymask)
|
||||
if !key_pressed && NSEvent::modifierFlags(ns_event).contains(keymask)
|
||||
|| key_pressed && !NSEvent::modifierFlags(ns_event).contains(keymask) {
|
||||
let state = ElementState::Released;
|
||||
let keycode = NSEvent::keyCode(ns_event);
|
||||
|
||||
@@ -8,12 +8,12 @@ use std::os::raw::*;
|
||||
use std::sync::Weak;
|
||||
|
||||
use cocoa::base::{class, id, nil};
|
||||
use cocoa::appkit::NSWindow;
|
||||
use cocoa::appkit::{NSEvent, NSView, NSWindow};
|
||||
use cocoa::foundation::{NSPoint, NSRect, NSSize, NSString, NSUInteger};
|
||||
use objc::declare::ClassDecl;
|
||||
use objc::runtime::{Class, Object, Protocol, Sel, BOOL};
|
||||
|
||||
use {ElementState, Event, KeyboardInput, WindowEvent, WindowId};
|
||||
use {ElementState, Event, KeyboardInput, MouseButton, WindowEvent, WindowId};
|
||||
use platform::platform::events_loop::{DEVICE_ID, event_mods, Shared, to_virtual_key_code};
|
||||
use platform::platform::util;
|
||||
use platform::platform::ffi::*;
|
||||
@@ -112,6 +112,16 @@ lazy_static! {
|
||||
decl.add_method(sel!(keyUp:), key_up as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(insertTab:), insert_tab as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(insertBackTab:), insert_back_tab as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(mouseDown:), mouse_down as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(mouseUp:), mouse_up as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(rightMouseDown:), right_mouse_down as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(rightMouseUp:), right_mouse_up as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(otherMouseDown:), other_mouse_down as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(otherMouseUp:), other_mouse_up as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(mouseMoved:), mouse_moved as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(mouseDragged:), mouse_dragged as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(rightMouseDragged:), right_mouse_dragged as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(otherMouseDragged:), other_mouse_dragged as extern fn(&Object, Sel, id));
|
||||
decl.add_ivar::<*mut c_void>("winitState");
|
||||
decl.add_ivar::<id>("markedText");
|
||||
let protocol = Protocol::get("NSTextInputClient").unwrap();
|
||||
@@ -448,3 +458,109 @@ extern fn insert_back_tab(this: &Object, _sel: Sel, _sender: id) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: ElementState) {
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.window)),
|
||||
event: WindowEvent::MouseInput {
|
||||
device_id: DEVICE_ID,
|
||||
state: button_state,
|
||||
button,
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(shared) = state.shared.upgrade() {
|
||||
shared.pending_events
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push_back(window_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern fn mouse_down(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_click(this, event, MouseButton::Left, ElementState::Pressed);
|
||||
}
|
||||
|
||||
extern fn mouse_up(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_click(this, event, MouseButton::Left, ElementState::Released);
|
||||
}
|
||||
|
||||
extern fn right_mouse_down(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_click(this, event, MouseButton::Right, ElementState::Pressed);
|
||||
}
|
||||
|
||||
extern fn right_mouse_up(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_click(this, event, MouseButton::Right, ElementState::Released);
|
||||
}
|
||||
|
||||
extern fn other_mouse_down(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_click(this, event, MouseButton::Middle, ElementState::Pressed);
|
||||
}
|
||||
|
||||
extern fn other_mouse_up(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_click(this, event, MouseButton::Middle, ElementState::Released);
|
||||
}
|
||||
|
||||
fn mouse_motion(this: &Object, event: id) {
|
||||
unsafe {
|
||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state_ptr as *mut ViewState);
|
||||
|
||||
// We have to do this to have access to the `NSView` trait...
|
||||
let view: id = this as *const _ as *mut _;
|
||||
|
||||
let window_point = event.locationInWindow();
|
||||
let view_point = view.convertPoint_fromView_(window_point, nil);
|
||||
let view_rect = NSView::frame(view);
|
||||
|
||||
if view_point.x.is_sign_negative()
|
||||
|| view_point.y.is_sign_negative()
|
||||
|| view_point.x > view_rect.size.width
|
||||
|| view_point.y > view_rect.size.height {
|
||||
// Point is outside of the client area (view)
|
||||
return;
|
||||
}
|
||||
|
||||
let scale_factor = NSWindow::backingScaleFactor(state.window) as f64;
|
||||
let x = scale_factor * view_point.x as f64;
|
||||
let y = scale_factor * (view_rect.size.height as f64 - view_point.y as f64);
|
||||
|
||||
let window_event = Event::WindowEvent {
|
||||
window_id: WindowId(get_window_id(state.window)),
|
||||
event: WindowEvent::CursorMoved {
|
||||
device_id: DEVICE_ID,
|
||||
position: (x, y),
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(shared) = state.shared.upgrade() {
|
||||
shared.pending_events
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push_back(window_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern fn mouse_moved(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_motion(this, event);
|
||||
}
|
||||
|
||||
extern fn mouse_dragged(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_motion(this, event);
|
||||
}
|
||||
|
||||
extern fn right_mouse_dragged(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_motion(this, event);
|
||||
}
|
||||
|
||||
extern fn other_mouse_dragged(this: &Object, _sel: Sel, event: id) {
|
||||
mouse_motion(this, event);
|
||||
}
|
||||
|
||||
@@ -58,14 +58,16 @@ impl DelegateState {
|
||||
// resizable temporality
|
||||
let curr_mask = self.window.styleMask();
|
||||
|
||||
if !curr_mask.contains(NSWindowStyleMask::NSTitledWindowMask) {
|
||||
util::set_style_mask(*self.window, *self.view, NSWindowStyleMask::NSResizableWindowMask);
|
||||
let required = NSWindowStyleMask::NSTitledWindowMask | NSWindowStyleMask::NSResizableWindowMask;
|
||||
let needs_temp_mask = !curr_mask.contains(required);
|
||||
if needs_temp_mask {
|
||||
util::set_style_mask(*self.window, *self.view, required);
|
||||
}
|
||||
|
||||
let is_zoomed: BOOL = msg_send![*self.window, isZoomed];
|
||||
|
||||
// Roll back temp styles
|
||||
if !curr_mask.contains(NSWindowStyleMask::NSTitledWindowMask) {
|
||||
if needs_temp_mask {
|
||||
util::set_style_mask(*self.window, *self.view, curr_mask);
|
||||
}
|
||||
|
||||
@@ -76,13 +78,20 @@ impl DelegateState {
|
||||
fn restore_state_from_fullscreen(&mut self) {
|
||||
let maximized = unsafe {
|
||||
let mut win_attribs = self.win_attribs.borrow_mut();
|
||||
|
||||
win_attribs.fullscreen = None;
|
||||
let save_style_opt = self.save_style_mask.take();
|
||||
|
||||
if let Some(save_style) = save_style_opt {
|
||||
util::set_style_mask(*self.window, *self.view, save_style);
|
||||
}
|
||||
let mask = {
|
||||
let base_mask = self.save_style_mask
|
||||
.take()
|
||||
.unwrap_or_else(|| self.window.styleMask());
|
||||
if win_attribs.resizable {
|
||||
base_mask | NSWindowStyleMask::NSResizableWindowMask
|
||||
} else {
|
||||
base_mask & !NSWindowStyleMask::NSResizableWindowMask
|
||||
}
|
||||
};
|
||||
|
||||
util::set_style_mask(*self.window, *self.view, mask);
|
||||
|
||||
win_attribs.maximized
|
||||
};
|
||||
@@ -107,16 +116,17 @@ impl DelegateState {
|
||||
let mut win_attribs = self.win_attribs.borrow_mut();
|
||||
win_attribs.maximized = maximized;
|
||||
|
||||
let curr_mask = unsafe { self.window.styleMask() };
|
||||
if win_attribs.fullscreen.is_some() {
|
||||
// Handle it in window_did_exit_fullscreen
|
||||
return;
|
||||
} else if win_attribs.decorations {
|
||||
// Just use the native zoom if not borderless
|
||||
} else if curr_mask.contains(NSWindowStyleMask::NSResizableWindowMask) {
|
||||
// Just use the native zoom if resizable
|
||||
unsafe {
|
||||
self.window.zoom_(nil);
|
||||
}
|
||||
} else {
|
||||
// if it is borderless, we set the frame directly
|
||||
// if it's not resizable, we set the frame directly
|
||||
unsafe {
|
||||
let new_rect = if maximized {
|
||||
let screen = NSScreen::mainScreen(nil);
|
||||
@@ -558,7 +568,7 @@ impl WindowExt for Window2 {
|
||||
impl Window2 {
|
||||
pub fn new(
|
||||
shared: Weak<Shared>,
|
||||
win_attribs: WindowAttributes,
|
||||
mut win_attribs: WindowAttributes,
|
||||
pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
||||
) -> Result<Window2, CreationError> {
|
||||
unsafe {
|
||||
@@ -566,6 +576,10 @@ impl Window2 {
|
||||
panic!("Windows can only be created on the main thread on macOS");
|
||||
}
|
||||
}
|
||||
|
||||
// Might as well save some RAM...
|
||||
win_attribs.window_icon.take();
|
||||
|
||||
let autoreleasepool = unsafe {
|
||||
NSAutoreleasePool::new(nil)
|
||||
};
|
||||
@@ -721,9 +735,10 @@ impl Window2 {
|
||||
};
|
||||
|
||||
let mut masks = if !attrs.decorations && !screen.is_some() {
|
||||
// unresizable Window2 without a titlebar or borders
|
||||
// Resizable Window2 without a titlebar or borders
|
||||
// if decorations is set to false, ignore pl_attrs
|
||||
NSWindowStyleMask::NSBorderlessWindowMask
|
||||
| NSWindowStyleMask::NSResizableWindowMask
|
||||
} else if pl_attrs.titlebar_hidden {
|
||||
// if the titlebar is hidden, ignore other pl_attrs
|
||||
NSWindowStyleMask::NSBorderlessWindowMask |
|
||||
@@ -736,8 +751,12 @@ impl Window2 {
|
||||
NSWindowStyleMask::NSTitledWindowMask
|
||||
};
|
||||
|
||||
if !attrs.resizable {
|
||||
masks &= !NSWindowStyleMask::NSResizableWindowMask;
|
||||
}
|
||||
|
||||
if pl_attrs.fullsize_content_view {
|
||||
masks = masks | NSWindowStyleMask::NSFullSizeContentViewWindowMask;
|
||||
masks |= NSWindowStyleMask::NSFullSizeContentViewWindowMask;
|
||||
}
|
||||
|
||||
let winit_window = Window2::class();
|
||||
@@ -779,7 +798,7 @@ impl Window2 {
|
||||
if attrs.always_on_top {
|
||||
let _: () = msg_send![*window, setLevel:ffi::NSWindowLevel::NSFloatingWindowLevel];
|
||||
}
|
||||
|
||||
|
||||
if let Some((x, y)) = pl_attrs.resize_increments {
|
||||
if x >= 1 && y >= 1 {
|
||||
let size = NSSize::new(x as _, y as _);
|
||||
@@ -900,6 +919,21 @@ impl Window2 {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, resizable: bool) {
|
||||
let mut win_attribs = self.delegate.state.win_attribs.borrow_mut();
|
||||
win_attribs.resizable = resizable;
|
||||
if win_attribs.fullscreen.is_none() {
|
||||
let mut mask = unsafe { self.window.styleMask() };
|
||||
if resizable {
|
||||
mask |= NSWindowStyleMask::NSResizableWindowMask;
|
||||
} else {
|
||||
mask &= !NSWindowStyleMask::NSResizableWindowMask;
|
||||
}
|
||||
unsafe { util::set_style_mask(*self.window, *self.view, mask) };
|
||||
} // Otherwise, we don't change the mask until we exit fullscreen.
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
unimplemented!()
|
||||
@@ -1028,11 +1062,9 @@ impl Window2 {
|
||||
// It will clean up at window_did_exit_fullscreen.
|
||||
if current.is_none() {
|
||||
let curr_mask = state.window.styleMask();
|
||||
|
||||
if !curr_mask.contains(NSWindowStyleMask::NSTitledWindowMask) {
|
||||
let mask = NSWindowStyleMask::NSTitledWindowMask
|
||||
| NSWindowStyleMask::NSResizableWindowMask;
|
||||
util::set_style_mask(*self.window, *self.view, mask);
|
||||
let required = NSWindowStyleMask::NSTitledWindowMask | NSWindowStyleMask::NSResizableWindowMask;
|
||||
if !curr_mask.contains(required) {
|
||||
util::set_style_mask(*self.window, *self.view, required);
|
||||
state.save_style_mask.set(Some(curr_mask));
|
||||
}
|
||||
}
|
||||
@@ -1059,14 +1091,18 @@ impl Window2 {
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let new_mask = if decorations {
|
||||
let mut new_mask = if decorations {
|
||||
NSWindowStyleMask::NSClosableWindowMask
|
||||
| NSWindowStyleMask::NSMiniaturizableWindowMask
|
||||
| NSWindowStyleMask::NSResizableWindowMask
|
||||
| NSWindowStyleMask::NSTitledWindowMask
|
||||
} else {
|
||||
NSWindowStyleMask::NSBorderlessWindowMask
|
||||
| NSWindowStyleMask::NSResizableWindowMask
|
||||
};
|
||||
if !win_attribs.resizable {
|
||||
new_mask &= !NSWindowStyleMask::NSResizableWindowMask;
|
||||
}
|
||||
util::set_style_mask(*state.window, *state.view, new_mask);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ use winapi::shared::minwindef::{BOOL, DWORD, FALSE, TRUE, UINT};
|
||||
use winapi::shared::windef::{HDC, HWND, LPPOINT, POINT, RECT};
|
||||
use winapi::um::{combaseapi, dwmapi, libloaderapi, winuser};
|
||||
use winapi::um::objbase::{COINIT_MULTITHREADED};
|
||||
use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl};
|
||||
use winapi::um::winnt::{HRESULT, LONG, LPCWSTR};
|
||||
use winapi::um::shobjidl_core::{CLSID_TaskbarList, ITaskbarList2};
|
||||
use winapi::um::winnt::{LONG, LPCWSTR};
|
||||
|
||||
use CreationError;
|
||||
use CursorState;
|
||||
@@ -240,6 +240,36 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, resizable: bool) {
|
||||
if let Ok(mut window_state) = self.window_state.lock() {
|
||||
if window_state.attributes.resizable == resizable {
|
||||
return;
|
||||
}
|
||||
if window_state.attributes.fullscreen.is_some() {
|
||||
window_state.attributes.resizable = resizable;
|
||||
return;
|
||||
}
|
||||
let window = self.window.clone();
|
||||
let mut style = unsafe {
|
||||
winuser::GetWindowLongW(self.window.0, winuser::GWL_STYLE)
|
||||
};
|
||||
if resizable {
|
||||
style |= winuser::WS_SIZEBOX as LONG;
|
||||
} else {
|
||||
style &= !winuser::WS_SIZEBOX as LONG;
|
||||
}
|
||||
unsafe {
|
||||
winuser::SetWindowLongW(
|
||||
window.0,
|
||||
winuser::GWL_STYLE,
|
||||
style as _,
|
||||
);
|
||||
};
|
||||
window_state.attributes.resizable = resizable;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove
|
||||
pub fn platform_display(&self) -> *mut ::libc::c_void {
|
||||
panic!() // Deprecated function ; we don't care anymore
|
||||
@@ -478,14 +508,20 @@ impl Window {
|
||||
|
||||
let rect = saved_window_info.rect.clone();
|
||||
let window = self.window.clone();
|
||||
let (style, ex_style) = (saved_window_info.style, saved_window_info.ex_style);
|
||||
let (mut style, ex_style) = (saved_window_info.style, saved_window_info.ex_style);
|
||||
|
||||
let maximized = window_state.attributes.maximized;
|
||||
let resizable = window_state.attributes.resizable;
|
||||
|
||||
// On restore, resize to the previous saved rect size.
|
||||
// And because SetWindowPos will resize the window
|
||||
// We call it in the main thread
|
||||
self.events_loop_proxy.execute_in_thread(move |_| {
|
||||
if resizable {
|
||||
style |= winuser::WS_SIZEBOX as LONG;
|
||||
} else {
|
||||
style &= !winuser::WS_SIZEBOX as LONG;
|
||||
}
|
||||
winuser::SetWindowLongW(window.0, winuser::GWL_STYLE, style);
|
||||
winuser::SetWindowLongW(window.0, winuser::GWL_EXSTYLE, ex_style);
|
||||
|
||||
@@ -843,6 +879,10 @@ unsafe fn init(
|
||||
style | winuser::WS_VISIBLE
|
||||
};
|
||||
|
||||
if !window.resizable {
|
||||
style &= !winuser::WS_SIZEBOX;
|
||||
}
|
||||
|
||||
if pl_attribs.parent.is_some() {
|
||||
style |= winuser::WS_CHILD;
|
||||
}
|
||||
@@ -984,49 +1024,13 @@ thread_local!{
|
||||
}
|
||||
};
|
||||
|
||||
static TASKBAR_LIST: Cell<*mut taskbar::ITaskbarList2> = Cell::new(ptr::null_mut());
|
||||
static TASKBAR_LIST: Cell<*mut ITaskbarList2> = Cell::new(ptr::null_mut());
|
||||
}
|
||||
|
||||
pub fn com_initialized() {
|
||||
COM_INITIALIZED.with(|_| {});
|
||||
}
|
||||
|
||||
// TODO: remove these when they get added to winapi
|
||||
// https://github.com/retep998/winapi-rs/pull/592
|
||||
#[allow(non_upper_case_globals)]
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(dead_code)]
|
||||
mod taskbar {
|
||||
use super::{IUnknown,IUnknownVtbl,HRESULT, HWND,BOOL};
|
||||
DEFINE_GUID!{CLSID_TaskbarList,
|
||||
0x56fdf344, 0xfd6d, 0x11d0, 0x95, 0x8a, 0x00, 0x60, 0x97, 0xc9, 0xa0, 0x90}
|
||||
|
||||
RIDL!(#[uuid(0x56fdf342, 0xfd6d, 0x11d0, 0x95, 0x8a, 0x00, 0x60, 0x97, 0xc9, 0xa0, 0x90)]
|
||||
interface ITaskbarList(ITaskbarListVtbl): IUnknown(IUnknownVtbl) {
|
||||
fn HrInit() -> HRESULT,
|
||||
fn AddTab(
|
||||
hwnd: HWND,
|
||||
) -> HRESULT,
|
||||
fn DeleteTab(
|
||||
hwnd: HWND,
|
||||
) -> HRESULT,
|
||||
fn ActivateTab(
|
||||
hwnd: HWND,
|
||||
) -> HRESULT,
|
||||
fn SetActiveAlt(
|
||||
hwnd: HWND,
|
||||
) -> HRESULT,
|
||||
});
|
||||
|
||||
RIDL!(#[uuid(0x602d4995, 0xb13a, 0x429b, 0xa6, 0x6e, 0x19, 0x35, 0xe4, 0x4f, 0x43, 0x17)]
|
||||
interface ITaskbarList2(ITaskbarList2Vtbl): ITaskbarList(ITaskbarListVtbl) {
|
||||
fn MarkFullscreenWindow(
|
||||
hwnd: HWND,
|
||||
fFullscreen: BOOL,
|
||||
) -> HRESULT,
|
||||
});
|
||||
}
|
||||
|
||||
// Reference Implementation:
|
||||
// https://github.com/chromium/chromium/blob/f18e79d901f56154f80eea1e2218544285e62623/ui/views/win/fullscreen_handler.cc
|
||||
//
|
||||
@@ -1046,10 +1050,10 @@ unsafe fn mark_fullscreen(handle: HWND, fullscreen: bool) {
|
||||
use winapi::Interface;
|
||||
|
||||
let hr = combaseapi::CoCreateInstance(
|
||||
&taskbar::CLSID_TaskbarList,
|
||||
&CLSID_TaskbarList,
|
||||
ptr::null_mut(),
|
||||
combaseapi::CLSCTX_ALL,
|
||||
&taskbar::ITaskbarList2::uuidof(),
|
||||
&ITaskbarList2::uuidof(),
|
||||
&mut task_bar_list as *mut _ as *mut _,
|
||||
);
|
||||
|
||||
|
||||
@@ -49,6 +49,22 @@ impl WindowBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether the window is resizable or not
|
||||
///
|
||||
/// Note that making the window unresizable doesn't exempt you from handling `Resized`, as that event can still be
|
||||
/// triggered by DPI scaling, entering fullscreen mode, etc.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// This only has an effect on desktop platforms.
|
||||
///
|
||||
/// Due to a bug in XFCE, this has no effect on Xfwm.
|
||||
#[inline]
|
||||
pub fn with_resizable(mut self, resizable: bool) -> WindowBuilder {
|
||||
self.window.resizable = resizable;
|
||||
self
|
||||
}
|
||||
|
||||
/// Requests a specific title for the window.
|
||||
#[inline]
|
||||
pub fn with_title<T: Into<String>>(mut self, title: T) -> WindowBuilder {
|
||||
@@ -306,6 +322,21 @@ impl Window {
|
||||
self.window.set_max_dimensions(dimensions)
|
||||
}
|
||||
|
||||
/// Sets whether the window is resizable or not.
|
||||
///
|
||||
/// Note that making the window unresizable doesn't exempt you from handling `Resized`, as that event can still be
|
||||
/// triggered by DPI scaling, entering fullscreen mode, etc.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// This only has an effect on desktop platforms.
|
||||
///
|
||||
/// Due to a bug in XFCE, this has no effect on Xfwm.
|
||||
#[inline]
|
||||
pub fn set_resizable(&self, resizable: bool) {
|
||||
self.window.set_resizable(resizable)
|
||||
}
|
||||
|
||||
/// DEPRECATED. Gets the native platform specific display for this window.
|
||||
/// This is typically only required when integrating with
|
||||
/// other libraries that need this information.
|
||||
@@ -333,6 +364,10 @@ impl Window {
|
||||
/// Returns the ratio between the backing framebuffer resolution and the
|
||||
/// window size in screen pixels. This is typically one for a normal display
|
||||
/// and two for a retina display.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
/// On X11 the DPI factor can be overridden using the `WINIT_HIDPI_FACTOR` environment
|
||||
/// variable.
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
self.window.hidpi_factor()
|
||||
@@ -456,6 +491,10 @@ impl MonitorId {
|
||||
}
|
||||
|
||||
/// Returns the ratio between the monitor's physical pixels and logical pixels.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
/// On X11 the DPI factor can be overridden using the `WINIT_HIDPI_FACTOR` environment
|
||||
/// variable.
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
self.inner.get_hidpi_factor()
|
||||
|
||||
Reference in New Issue
Block a user