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 | |
|---|---|---|---|
|
|
e48f1fc5f1 | ||
|
|
374f131f1e | ||
|
|
363261077f | ||
|
|
102dd07456 | ||
|
|
1e97103094 | ||
|
|
cc8907b956 | ||
|
|
c4b92ebd45 | ||
|
|
fee874b5b7 | ||
|
|
eba888207e | ||
|
|
ea28791da6 | ||
|
|
fe2d37fcdc | ||
|
|
3407a8dd78 | ||
|
|
5761fb6b30 | ||
|
|
7aeb2c083b | ||
|
|
8f47fdbe67 | ||
|
|
2ea42b3947 | ||
|
|
7510b95d8c |
26
CHANGELOG.md
26
CHANGELOG.md
@@ -1,5 +1,31 @@
|
||||
# Unreleased
|
||||
|
||||
# Version 0.14.0 (2018-05-09)
|
||||
|
||||
- Created the `Copy`, `Paste` and `Cut` `VirtualKeyCode`s and added support for them on X11 and Wayland
|
||||
- Fix `.with_decorations(false)` in macOS
|
||||
- On Mac, `NSWindow` and supporting objects might be alive long after they were `closed` which resulted in apps consuming more heap then needed. Mainly it was affecting multi window applications. Not expecting any user visible change of behaviour after the fix.
|
||||
- Fix regression of Window platform extensions for macOS where `NSFullSizeContentViewWindowMask` was not being correctly applied to `.fullsize_content_view`.
|
||||
- Corrected `get_position` on Windows to be relative to the screen rather than to the taskbar.
|
||||
- Corrected `Moved` event on Windows to use position values equivalent to those returned by `get_position`. It previously supplied client area positions instead of window positions, and would additionally interpret negative values as being very large (around `u16::MAX`).
|
||||
- Implemented `Moved` event on macOS.
|
||||
- On X11, the `Moved` event correctly use window positions rather than client area positions. Additionally, a stray `Moved` that unconditionally accompanied `Resized` with the client area position relative to the parent has been eliminated; `Moved` is still received alongside `Resized`, but now only once and always correctly.
|
||||
- On Windows, implemented all variants of `DeviceEvent` other than `Text`. Mouse `DeviceEvent`s are now received even if the window isn't in the foreground.
|
||||
- `DeviceId` on Windows is no longer a unit struct, and now contains a `u32`. For `WindowEvent`s, this will always be 0, but on `DeviceEvent`s it will be the handle to that device. `DeviceIdExt::get_persistent_identifier` can be used to acquire a unique identifier for that device that persists across replugs/reboots/etc.
|
||||
- Corrected `run_forever` on X11 to stop discarding `Awakened` events.
|
||||
- Various safety and correctness improvements to the X11 backend internals.
|
||||
- Fixed memory leak on X11 every time the mouse entered the window.
|
||||
- On X11, drag and drop now works reliably in release mode.
|
||||
- Added `WindowBuilderExt::with_resize_increments` and `WindowBuilderExt::with_base_size` to X11, allowing for more optional hints to be set.
|
||||
- Rework of the wayland backend, migrating it to use [Smithay's Client Toolkit](https://github.com/Smithay/client-toolkit).
|
||||
- Added `WindowBuilder::with_window_icon` and `Window::set_window_icon`, finally making it possible to set the window icon on Windows and X11. The `icon_loading` feature can be enabled to allow for icons to be easily loaded; see example program `window_icon.rs` for usage.
|
||||
- Windows additionally has `WindowBuilderExt::with_taskbar_icon` and `WindowExt::set_taskbar_icon`.
|
||||
- On Windows, fix panic when trying to call `set_fullscreen(None)` on a window that has not been fullscreened prior.
|
||||
|
||||
# Version 0.13.1 (2018-04-26)
|
||||
|
||||
- Ensure necessary `x11-dl` version is used.
|
||||
|
||||
# Version 0.13.0 (2018-04-25)
|
||||
|
||||
- Implement `WindowBuilder::with_maximized`, `Window::set_fullscreen`, `Window::set_maximized` and `Window::set_decorations` for MacOS.
|
||||
|
||||
15
Cargo.toml
15
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
authors = ["The winit contributors, Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
keywords = ["windowing"]
|
||||
@@ -10,9 +10,13 @@ repository = "https://github.com/tomaka/winit"
|
||||
documentation = "https://docs.rs/winit"
|
||||
categories = ["gui"]
|
||||
|
||||
[features]
|
||||
icon_loading = ["image"]
|
||||
|
||||
[dependencies]
|
||||
lazy_static = "1"
|
||||
libc = "0.2"
|
||||
image = { version = "0.19", optional = true }
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies.android_glue]
|
||||
version = "0.2"
|
||||
@@ -44,9 +48,8 @@ features = [
|
||||
]
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies]
|
||||
wayland-client = { version = "0.12.0", features = ["dlopen"] }
|
||||
wayland-protocols = { version = "0.12.0", features = ["unstable_protocols"] }
|
||||
wayland-kbd = "0.13.0"
|
||||
wayland-window = "0.13.0"
|
||||
x11-dl = "2.17"
|
||||
wayland-client = { version = "0.20.4", features = [ "dlopen", "egl", "cursor"] }
|
||||
smithay-client-toolkit = "0.2.1"
|
||||
x11-dl = "2.17.5"
|
||||
parking_lot = "0.5"
|
||||
percent-encoding = "1.0"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.13"
|
||||
winit = "0.14"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
|
||||
BIN
examples/icon.png
Normal file
BIN
examples/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
95
examples/window_icon.rs
Normal file
95
examples/window_icon.rs
Normal file
@@ -0,0 +1,95 @@
|
||||
// Heads up: you need to compile this example with `--features icon_loading`.
|
||||
// `Icon::from_path` won't be available otherwise, though for your own applications, you could use
|
||||
// `Icon::from_rgba` if you don't want to depend on the `image` crate.
|
||||
|
||||
extern crate winit;
|
||||
#[cfg(feature = "icon_loading")]
|
||||
extern crate image;
|
||||
|
||||
use winit::Icon;
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
fn main() {
|
||||
// You'll have to choose an icon size at your own discretion. On X11, the desired size varies
|
||||
// by WM, and on Windows, you still have to account for screen scaling. Here we use 32px,
|
||||
// since it seems to work well enough in most cases. Be careful about going too high, or
|
||||
// you'll be bitten by the low-quality downscaling built into the WM.
|
||||
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/icon.png");
|
||||
// While `Icon::from_path` is the most straightforward, you have a few other options. If you
|
||||
// want to use the `include_bytes` macro, then pass the result to `Icon::from_bytes`. See the
|
||||
// docs for the full list of options (you'll have to generate the docs with the `icon_loading`
|
||||
// feature enabled).
|
||||
let icon = Icon::from_path(path).expect("Failed to open icon");
|
||||
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
let window = winit::WindowBuilder::new()
|
||||
.with_title("An iconic window!")
|
||||
// At present, this only does anything on Windows and X11, so if you want to save load
|
||||
// time, you can put icon loading behind a function that returns `None` on other platforms.
|
||||
.with_window_icon(Some(icon))
|
||||
.build(&events_loop)
|
||||
.unwrap();
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
if let winit::Event::WindowEvent { event, .. } = event {
|
||||
use winit::WindowEvent::*;
|
||||
match event {
|
||||
CloseRequested => return winit::ControlFlow::Break,
|
||||
DroppedFile(path) => {
|
||||
use image::GenericImage;
|
||||
|
||||
let icon_image = image::open(path).expect("Failed to open window icon");
|
||||
|
||||
let (width, height) = icon_image.dimensions();
|
||||
const DESIRED_SIZE: u32 = 32;
|
||||
let (new_width, new_height) = if width == height {
|
||||
(DESIRED_SIZE, DESIRED_SIZE)
|
||||
} else {
|
||||
// Note that this will never divide by zero, due to the previous condition.
|
||||
let aspect_adjustment = DESIRED_SIZE as f64
|
||||
/ std::cmp::max(width, height) as f64;
|
||||
(
|
||||
(width as f64 * aspect_adjustment) as u32,
|
||||
(height as f64 * aspect_adjustment) as u32,
|
||||
)
|
||||
};
|
||||
|
||||
// By scaling the icon ourselves, we get higher-quality filtering and save
|
||||
// some memory.
|
||||
let icon = image::imageops::resize(
|
||||
&icon_image,
|
||||
new_width,
|
||||
new_height,
|
||||
image::FilterType::Lanczos3,
|
||||
);
|
||||
|
||||
let (offset_x, offset_y) = (
|
||||
(DESIRED_SIZE - new_width) / 2,
|
||||
(DESIRED_SIZE - new_height) / 2,
|
||||
);
|
||||
|
||||
let mut canvas = image::ImageBuffer::new(DESIRED_SIZE, DESIRED_SIZE);
|
||||
image::imageops::replace(
|
||||
&mut canvas,
|
||||
&icon,
|
||||
offset_x,
|
||||
offset_y,
|
||||
);
|
||||
|
||||
window.set_window_icon(Some(canvas.into()));
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
winit::ControlFlow::Continue
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "icon_loading"))]
|
||||
fn main() {
|
||||
print!(
|
||||
r#"This example requires the `icon_loading` feature:
|
||||
cargo run --example window_icon --features icon_loading
|
||||
"#);
|
||||
}
|
||||
@@ -24,10 +24,10 @@ pub enum Event {
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum WindowEvent {
|
||||
|
||||
/// The size of the window has changed.
|
||||
/// The size of the window has changed. Contains the client area's new dimensions.
|
||||
Resized(u32, u32),
|
||||
|
||||
/// The position of the window has changed.
|
||||
/// The position of the window has changed. Contains the window's new position.
|
||||
Moved(i32, i32),
|
||||
|
||||
/// The window has been requested to close.
|
||||
@@ -431,6 +431,9 @@ pub enum VirtualKeyCode {
|
||||
WebSearch,
|
||||
WebStop,
|
||||
Yen,
|
||||
Copy,
|
||||
Paste,
|
||||
Cut,
|
||||
}
|
||||
|
||||
/// Represents the current state of the keyboard modifiers
|
||||
|
||||
160
src/icon.rs
Normal file
160
src/icon.rs
Normal file
@@ -0,0 +1,160 @@
|
||||
use std::{fmt, mem};
|
||||
use std::error::Error;
|
||||
#[cfg(feature = "icon_loading")]
|
||||
use std::io::{BufRead, Seek};
|
||||
#[cfg(feature = "icon_loading")]
|
||||
use std::path::Path;
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
use image;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Pixel {
|
||||
pub(crate) r: u8,
|
||||
pub(crate) g: u8,
|
||||
pub(crate) b: u8,
|
||||
pub(crate) a: u8,
|
||||
}
|
||||
|
||||
pub(crate) const PIXEL_SIZE: usize = mem::size_of::<Pixel>();
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
/// An error produced when using `Icon::from_rgba` with invalid arguments.
|
||||
pub enum BadIcon {
|
||||
/// Produced when the length of the `rgba` argument isn't divisible by 4, thus `rgba` can't be
|
||||
/// safely interpreted as 32bpp RGBA pixels.
|
||||
ByteCountNotDivisibleBy4 {
|
||||
byte_count: usize,
|
||||
},
|
||||
/// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`.
|
||||
/// At least one of your arguments is incorrect.
|
||||
DimensionsVsPixelCount {
|
||||
width: u32,
|
||||
height: u32,
|
||||
width_x_height: usize,
|
||||
pixel_count: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl fmt::Display for BadIcon {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
let msg = match self {
|
||||
&BadIcon::ByteCountNotDivisibleBy4 { byte_count } => format!(
|
||||
"The length of the `rgba` argument ({:?}) isn't divisible by 4, making it impossible to interpret as 32bpp RGBA pixels.",
|
||||
byte_count,
|
||||
),
|
||||
&BadIcon::DimensionsVsPixelCount {
|
||||
width,
|
||||
height,
|
||||
width_x_height,
|
||||
pixel_count,
|
||||
} => format!(
|
||||
"The specified dimensions ({:?}x{:?}) don't match the number of pixels supplied by the `rgba` argument ({:?}). For those dimensions, the expected pixel count is {:?}.",
|
||||
width, height, pixel_count, width_x_height,
|
||||
),
|
||||
};
|
||||
write!(formatter, "{}", msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for BadIcon {
|
||||
fn description(&self) -> &str {
|
||||
"A valid icon cannot be created from these arguments"
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&Error> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
/// An icon used for the window titlebar, taskbar, etc.
|
||||
///
|
||||
/// Enabling the `icon_loading` feature provides you with several convenience methods for creating
|
||||
/// an `Icon` from any format supported by the [image](https://github.com/PistonDevelopers/image)
|
||||
/// crate.
|
||||
pub struct Icon {
|
||||
pub(crate) rgba: Vec<u8>,
|
||||
pub(crate) width: u32,
|
||||
pub(crate) height: u32,
|
||||
}
|
||||
|
||||
impl Icon {
|
||||
/// Creates an `Icon` from 32bpp RGBA data.
|
||||
///
|
||||
/// The length of `rgba` must be divisible by 4, and `width * height` must equal
|
||||
/// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error.
|
||||
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
|
||||
if rgba.len() % PIXEL_SIZE != 0 {
|
||||
return Err(BadIcon::ByteCountNotDivisibleBy4 { byte_count: rgba.len() });
|
||||
}
|
||||
let pixel_count = rgba.len() / PIXEL_SIZE;
|
||||
if pixel_count != (width * height) as usize {
|
||||
Err(BadIcon::DimensionsVsPixelCount {
|
||||
width,
|
||||
height,
|
||||
width_x_height: (width * height) as usize,
|
||||
pixel_count,
|
||||
})
|
||||
} else {
|
||||
Ok(Icon { rgba, width, height })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
/// Loads an `Icon` from the path of an image on the filesystem.
|
||||
pub fn from_path<P: AsRef<Path>>(path: P) -> image::ImageResult<Self> {
|
||||
image::open(path).map(Into::into)
|
||||
}
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
/// Loads an `Icon` from anything implementing `BufRead` and `Seek`.
|
||||
pub fn from_reader<R: BufRead + Seek>(
|
||||
reader: R,
|
||||
format: image::ImageFormat,
|
||||
) -> image::ImageResult<Self> {
|
||||
image::load(reader, format).map(Into::into)
|
||||
}
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
/// Loads an `Icon` from the unprocessed bytes of an image file.
|
||||
/// Uses heuristics to determine format.
|
||||
pub fn from_bytes(bytes: &[u8]) -> image::ImageResult<Self> {
|
||||
image::load_from_memory(bytes).map(Into::into)
|
||||
}
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
/// Loads an `Icon` from the unprocessed bytes of an image.
|
||||
pub fn from_bytes_with_format(
|
||||
bytes: &[u8],
|
||||
format: image::ImageFormat,
|
||||
) -> image::ImageResult<Self> {
|
||||
image::load_from_memory_with_format(bytes, format).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
impl From<image::DynamicImage> for Icon {
|
||||
fn from(image: image::DynamicImage) -> Self {
|
||||
use image::{GenericImage, Pixel};
|
||||
let (width, height) = image.dimensions();
|
||||
let mut rgba = Vec::with_capacity((width * height) as usize * PIXEL_SIZE);
|
||||
for (_, _, pixel) in image.pixels() {
|
||||
rgba.extend_from_slice(&pixel.to_rgba().data);
|
||||
}
|
||||
Icon { rgba, width, height }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
impl From<image::RgbaImage> for Icon {
|
||||
fn from(buf: image::RgbaImage) -> Self {
|
||||
let (width, height) = buf.dimensions();
|
||||
let mut rgba = Vec::with_capacity((width * height) as usize * PIXEL_SIZE);
|
||||
for (_, _, pixel) in buf.enumerate_pixels() {
|
||||
rgba.extend_from_slice(&pixel.data);
|
||||
}
|
||||
Icon { rgba, width, height }
|
||||
}
|
||||
}
|
||||
16
src/lib.rs
16
src/lib.rs
@@ -83,8 +83,9 @@
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "windows"))]
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
extern crate libc;
|
||||
#[cfg(feature = "icon_loading")]
|
||||
extern crate image;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[macro_use]
|
||||
@@ -101,17 +102,20 @@ extern crate core_graphics;
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
extern crate x11_dl;
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
extern crate parking_lot;
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
extern crate percent_encoding;
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))]
|
||||
#[macro_use]
|
||||
extern crate wayland_client;
|
||||
extern crate smithay_client_toolkit as sctk;
|
||||
|
||||
pub use events::*;
|
||||
pub use window::{AvailableMonitorsIter, MonitorId};
|
||||
pub use icon::*;
|
||||
|
||||
mod platform;
|
||||
mod events;
|
||||
mod window;
|
||||
mod icon;
|
||||
|
||||
pub mod os;
|
||||
|
||||
@@ -438,6 +442,11 @@ pub struct WindowAttributes {
|
||||
/// The default is `true`.
|
||||
pub decorations: bool,
|
||||
|
||||
/// The window icon.
|
||||
///
|
||||
/// The default is `None`.
|
||||
pub window_icon: Option<Icon>,
|
||||
|
||||
/// [iOS only] Enable multitouch,
|
||||
/// see [multipleTouchEnabled](https://developer.apple.com/documentation/uikit/uiview/1622519-multipletouchenabled)
|
||||
pub multitouch: bool,
|
||||
@@ -456,6 +465,7 @@ impl Default for WindowAttributes {
|
||||
visible: true,
|
||||
transparent: false,
|
||||
decorations: true,
|
||||
window_icon: None,
|
||||
multitouch: false,
|
||||
}
|
||||
}
|
||||
|
||||
0
src/os/macos.rs
Executable file → Normal file
0
src/os/macos.rs
Executable file → Normal file
@@ -173,18 +173,16 @@ impl WindowExt for Window {
|
||||
|
||||
#[inline]
|
||||
fn get_wayland_surface(&self) -> Option<*mut raw::c_void> {
|
||||
use wayland_client::Proxy;
|
||||
match self.window {
|
||||
LinuxWindow::Wayland(ref w) => Some(w.get_surface().ptr() as *mut _),
|
||||
LinuxWindow::Wayland(ref w) => Some(w.get_surface().c_ptr() as *mut _),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_wayland_display(&self) -> Option<*mut raw::c_void> {
|
||||
use wayland_client::Proxy;
|
||||
match self.window {
|
||||
LinuxWindow::Wayland(ref w) => Some(w.get_display().ptr() as *mut _),
|
||||
LinuxWindow::Wayland(ref w) => Some(w.get_display().c_ptr() as *mut _),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
@@ -199,6 +197,11 @@ impl WindowExt for Window {
|
||||
pub trait WindowBuilderExt {
|
||||
fn with_x11_visual<T>(self, visual_infos: *const T) -> WindowBuilder;
|
||||
fn with_x11_screen(self, screen_id: i32) -> WindowBuilder;
|
||||
|
||||
/// Build window with resize increment hint. Only implemented on X11.
|
||||
fn with_resize_increments(self, width_inc: i32, height_inc: i32) -> WindowBuilder;
|
||||
/// Build window with base size hint. Only implemented on X11.
|
||||
fn with_base_size(self, base_width: i32, base_height: i32) -> WindowBuilder;
|
||||
}
|
||||
|
||||
impl WindowBuilderExt for WindowBuilder {
|
||||
@@ -215,6 +218,18 @@ impl WindowBuilderExt for WindowBuilder {
|
||||
self.platform_specific.screen_id = Some(screen_id);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_resize_increments(mut self, width_inc: i32, height_inc: i32) -> WindowBuilder {
|
||||
self.platform_specific.resize_increments = Some((width_inc, height_inc));
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_base_size(mut self, base_width: i32, base_height: i32) -> WindowBuilder {
|
||||
self.platform_specific.base_size = Some((base_width, base_height));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `MonitorId` that are specific to Linux.
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
#![cfg(target_os = "windows")]
|
||||
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use libc;
|
||||
use MonitorId;
|
||||
use Window;
|
||||
use WindowBuilder;
|
||||
use winapi::shared::windef::HWND;
|
||||
|
||||
use {DeviceId, Icon, MonitorId, Window, WindowBuilder};
|
||||
|
||||
/// Additional methods on `Window` that are specific to Windows.
|
||||
pub trait WindowExt {
|
||||
/// Returns the native handle that is used by this window.
|
||||
///
|
||||
/// The pointer will become invalid when the native window was destroyed.
|
||||
fn get_hwnd(&self) -> *mut libc::c_void;
|
||||
|
||||
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
|
||||
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>);
|
||||
}
|
||||
|
||||
impl WindowExt for Window {
|
||||
@@ -20,20 +23,34 @@ impl WindowExt for Window {
|
||||
fn get_hwnd(&self) -> *mut libc::c_void {
|
||||
self.window.hwnd() as *mut _
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>) {
|
||||
self.window.set_taskbar_icon(taskbar_icon)
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `WindowBuilder` that are specific to Windows.
|
||||
pub trait WindowBuilderExt {
|
||||
/// Sets a parent to the window to be created.
|
||||
fn with_parent_window(self, parent: HWND) -> WindowBuilder;
|
||||
|
||||
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
|
||||
fn with_taskbar_icon(self, taskbar_icon: Option<Icon>) -> WindowBuilder;
|
||||
}
|
||||
|
||||
impl WindowBuilderExt for WindowBuilder {
|
||||
/// Sets a parent to the window to be created.
|
||||
#[inline]
|
||||
fn with_parent_window(mut self, parent: HWND) -> WindowBuilder {
|
||||
self.platform_specific.parent = Some(parent);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_taskbar_icon(mut self, taskbar_icon: Option<Icon>) -> WindowBuilder {
|
||||
self.platform_specific.taskbar_icon = taskbar_icon;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `MonitorId` that are specific to Windows.
|
||||
@@ -56,3 +73,18 @@ impl MonitorIdExt for MonitorId {
|
||||
self.inner.get_hmonitor() as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `DeviceId` that are specific to Windows.
|
||||
pub trait DeviceIdExt {
|
||||
/// Returns an identifier that persistently refers to this specific device.
|
||||
///
|
||||
/// Will return `None` if the device is no longer available.
|
||||
fn get_persistent_identifier(&self) -> Option<String>;
|
||||
}
|
||||
|
||||
impl DeviceIdExt for DeviceId {
|
||||
#[inline]
|
||||
fn get_persistent_identifier(&self) -> Option<String> {
|
||||
self.0.get_persistent_identifier()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,8 +201,8 @@ pub struct PlatformSpecificWindowBuilderAttributes;
|
||||
pub struct PlatformSpecificHeadlessBuilderAttributes;
|
||||
|
||||
impl Window {
|
||||
pub fn new(_: &EventsLoop, win_attribs: &WindowAttributes,
|
||||
_: &PlatformSpecificWindowBuilderAttributes)
|
||||
pub fn new(_: &EventsLoop, win_attribs: WindowAttributes,
|
||||
_: PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window, CreationError>
|
||||
{
|
||||
// not implemented
|
||||
@@ -323,6 +323,11 @@ impl Window {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, _icon: Option<::Icon>) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||
RootMonitorId{inner: MonitorId}
|
||||
|
||||
@@ -347,8 +347,8 @@ fn em_try(res: ffi::EMSCRIPTEN_RESULT) -> Result<(), String> {
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(events_loop: &EventsLoop, attribs: &::WindowAttributes,
|
||||
_pl_attribs: &PlatformSpecificWindowBuilderAttributes)
|
||||
pub fn new(events_loop: &EventsLoop, attribs: ::WindowAttributes,
|
||||
_pl_attribs: PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window, ::CreationError>
|
||||
{
|
||||
if events_loop.window.lock().unwrap().is_some() {
|
||||
@@ -543,6 +543,11 @@ impl Window {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, _icon: Option<::Icon>) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> ::MonitorId {
|
||||
::MonitorId{inner: MonitorId}
|
||||
|
||||
@@ -263,7 +263,7 @@ pub struct DeviceId;
|
||||
pub struct PlatformSpecificWindowBuilderAttributes;
|
||||
|
||||
impl Window {
|
||||
pub fn new(ev: &EventsLoop, _: &WindowAttributes, _: &PlatformSpecificWindowBuilderAttributes)
|
||||
pub fn new(ev: &EventsLoop, _: WindowAttributes, _: PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window, CreationError>
|
||||
{
|
||||
Ok(Window {
|
||||
@@ -370,6 +370,11 @@ impl Window {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, _icon: Option<::Icon>) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||
RootMonitorId{inner: MonitorId}
|
||||
|
||||
@@ -1,18 +1,27 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::{env, mem};
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::*;
|
||||
use std::sync::Arc;
|
||||
use std::env;
|
||||
|
||||
use {CreationError, CursorState, EventsLoopClosed, MouseCursor, ControlFlow};
|
||||
// `std::os::raw::c_void` and `libc::c_void` are NOT interchangeable!
|
||||
use libc;
|
||||
|
||||
use self::x11::XConnection;
|
||||
use self::x11::XError;
|
||||
use self::x11::ffi::XVisualInfo;
|
||||
|
||||
pub use self::x11::XNotSupported;
|
||||
use {
|
||||
CreationError,
|
||||
CursorState,
|
||||
EventsLoopClosed,
|
||||
Icon,
|
||||
MouseCursor,
|
||||
ControlFlow,
|
||||
WindowAttributes,
|
||||
};
|
||||
use window::MonitorId as RootMonitorId;
|
||||
use self::x11::{XConnection, XError};
|
||||
use self::x11::ffi::XVisualInfo;
|
||||
pub use self::x11::XNotSupported;
|
||||
|
||||
mod dlopen;
|
||||
pub mod wayland;
|
||||
@@ -31,6 +40,8 @@ const BACKEND_PREFERENCE_ENV_VAR: &str = "WINIT_UNIX_BACKEND";
|
||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
pub visual_infos: Option<XVisualInfo>,
|
||||
pub screen_id: Option<i32>,
|
||||
pub resize_increments: Option<(i32, i32)>,
|
||||
pub base_size: Option<(i32, i32)>,
|
||||
}
|
||||
|
||||
lazy_static!(
|
||||
@@ -106,18 +117,17 @@ impl MonitorId {
|
||||
|
||||
impl Window {
|
||||
#[inline]
|
||||
pub fn new(events_loop: &EventsLoop,
|
||||
window: &::WindowAttributes,
|
||||
pl_attribs: &PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Self, CreationError>
|
||||
{
|
||||
pub fn new(
|
||||
events_loop: &EventsLoop,
|
||||
attribs: WindowAttributes,
|
||||
pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
||||
) -> Result<Self, CreationError> {
|
||||
match *events_loop {
|
||||
EventsLoop::Wayland(ref evlp) => {
|
||||
wayland::Window::new(evlp, window).map(Window::Wayland)
|
||||
EventsLoop::Wayland(ref events_loop) => {
|
||||
wayland::Window::new(events_loop, attribs).map(Window::Wayland)
|
||||
},
|
||||
|
||||
EventsLoop::X(ref el) => {
|
||||
x11::Window::new(el, window, pl_attribs).map(Window::X)
|
||||
EventsLoop::X(ref events_loop) => {
|
||||
x11::Window::new(events_loop, attribs, pl_attribs).map(Window::X)
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -252,19 +262,17 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
use wayland_client::Proxy;
|
||||
match self {
|
||||
&Window::X(ref w) => w.platform_display(),
|
||||
&Window::Wayland(ref w) => w.get_display().ptr() as *mut _
|
||||
&Window::Wayland(ref w) => w.get_display().c_ptr() as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
||||
use wayland_client::Proxy;
|
||||
match self {
|
||||
&Window::X(ref w) => w.platform_window(),
|
||||
&Window::Wayland(ref w) => w.get_surface().ptr() as *mut _
|
||||
&Window::Wayland(ref w) => w.get_surface().c_ptr() as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,6 +300,14 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, window_icon: Option<Icon>) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_window_icon(window_icon),
|
||||
&Window::Wayland(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||
match self {
|
||||
@@ -301,15 +317,19 @@ impl Window {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn x_error_callback(dpy: *mut x11::ffi::Display, event: *mut x11::ffi::XErrorEvent)
|
||||
-> libc::c_int
|
||||
{
|
||||
use std::ffi::CStr;
|
||||
|
||||
if let Ok(ref x) = *X11_BACKEND {
|
||||
let mut buff: Vec<u8> = Vec::with_capacity(1024);
|
||||
(x.xlib.XGetErrorText)(dpy, (*event).error_code as i32, buff.as_mut_ptr() as *mut libc::c_char, buff.capacity() as i32);
|
||||
let description = CStr::from_ptr(buff.as_mut_ptr() as *const libc::c_char).to_string_lossy();
|
||||
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();
|
||||
|
||||
let error = XError {
|
||||
description: description.into_owned(),
|
||||
@@ -318,9 +338,9 @@ unsafe extern "C" fn x_error_callback(dpy: *mut x11::ffi::Display, event: *mut x
|
||||
minor_code: (*event).minor_code,
|
||||
};
|
||||
|
||||
*x.latest_error.lock().unwrap() = Some(error);
|
||||
*xconn.latest_error.lock() = Some(error);
|
||||
}
|
||||
|
||||
// Fun fact: this return value is completely ignored.
|
||||
0
|
||||
}
|
||||
|
||||
@@ -340,28 +360,39 @@ impl EventsLoop {
|
||||
if let Ok(env_var) = env::var(BACKEND_PREFERENCE_ENV_VAR) {
|
||||
match env_var.as_str() {
|
||||
"x11" => {
|
||||
return EventsLoop::new_x11().unwrap(); // TODO: propagate
|
||||
// TODO: propagate
|
||||
return EventsLoop::new_x11().expect("Failed to initialize X11 backend");
|
||||
},
|
||||
"wayland" => {
|
||||
match EventsLoop::new_wayland() {
|
||||
Ok(e) => return e,
|
||||
Err(_) => panic!() // TODO: propagate
|
||||
}
|
||||
return EventsLoop::new_wayland()
|
||||
.expect("Failed to initialize Wayland backend");
|
||||
},
|
||||
_ => panic!("Unknown environment variable value for {}, try one of `x11`,`wayland`",
|
||||
BACKEND_PREFERENCE_ENV_VAR),
|
||||
_ => panic!(
|
||||
"Unknown environment variable value for {}, try one of `x11`,`wayland`",
|
||||
BACKEND_PREFERENCE_ENV_VAR,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(el) = EventsLoop::new_wayland() {
|
||||
return el;
|
||||
}
|
||||
let wayland_err = match EventsLoop::new_wayland() {
|
||||
Ok(event_loop) => return event_loop,
|
||||
Err(err) => err,
|
||||
};
|
||||
|
||||
if let Ok(el) = EventsLoop::new_x11() {
|
||||
return el;
|
||||
}
|
||||
let x11_err = match EventsLoop::new_x11() {
|
||||
Ok(event_loop) => return event_loop,
|
||||
Err(err) => err,
|
||||
};
|
||||
|
||||
panic!("No backend is available")
|
||||
let err_string = format!(
|
||||
r#"Failed to initialize any backend!
|
||||
Wayland status: {:#?}
|
||||
X11 status: {:#?}
|
||||
"#,
|
||||
wayland_err,
|
||||
x11_err,
|
||||
);
|
||||
panic!(err_string);
|
||||
}
|
||||
|
||||
pub fn new_wayland() -> Result<EventsLoop, ()> {
|
||||
|
||||
@@ -3,37 +3,35 @@ use std::collections::VecDeque;
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use {EventsLoopClosed, ControlFlow};
|
||||
use {ControlFlow, EventsLoopClosed};
|
||||
|
||||
use super::WindowId;
|
||||
use super::window::WindowStore;
|
||||
use super::keyboard::init_keyboard;
|
||||
|
||||
use wayland_client::{EnvHandler, EnvNotify, default_connect, EventQueue, EventQueueHandle, Proxy, StateToken};
|
||||
use wayland_client::protocol::{wl_compositor, wl_seat, wl_shell, wl_shm, wl_subcompositor,
|
||||
wl_display, wl_registry, wl_output, wl_surface,
|
||||
wl_pointer, wl_keyboard, wl_touch};
|
||||
use sctk::Environment;
|
||||
use sctk::output::OutputMgr;
|
||||
use sctk::reexports::client::{Display, EventQueue, GlobalEvent, Proxy};
|
||||
use sctk::reexports::client::commons::Implementation;
|
||||
use sctk::reexports::client::protocol::{wl_keyboard, wl_output, wl_pointer, wl_registry, wl_seat,
|
||||
wl_touch};
|
||||
|
||||
use super::wayland_window::{Frame, Shell, create_frame, FrameImplementation};
|
||||
use super::wayland_protocols::unstable::xdg_shell::v6::client::zxdg_shell_v6;
|
||||
use sctk::reexports::client::protocol::wl_display::RequestsTrait as DisplayRequests;
|
||||
|
||||
pub struct EventsLoopSink {
|
||||
buffer: VecDeque<::Event>
|
||||
buffer: VecDeque<::Event>,
|
||||
}
|
||||
|
||||
unsafe impl Send for EventsLoopSink { }
|
||||
|
||||
impl EventsLoopSink {
|
||||
pub fn new() -> EventsLoopSink{
|
||||
pub fn new() -> EventsLoopSink {
|
||||
EventsLoopSink {
|
||||
buffer: VecDeque::new()
|
||||
buffer: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_event(&mut self, evt: ::WindowEvent, wid: WindowId) {
|
||||
let evt = ::Event::WindowEvent {
|
||||
event: evt,
|
||||
window_id: ::WindowId(::platform::WindowId::Wayland(wid))
|
||||
window_id: ::WindowId(::platform::WindowId::Wayland(wid)),
|
||||
};
|
||||
self.buffer.push_back(evt);
|
||||
}
|
||||
@@ -42,7 +40,10 @@ impl EventsLoopSink {
|
||||
self.buffer.push_back(evt);
|
||||
}
|
||||
|
||||
fn empty_with<F>(&mut self, callback: &mut F) where F: FnMut(::Event) {
|
||||
fn empty_with<F>(&mut self, callback: &mut F)
|
||||
where
|
||||
F: FnMut(::Event),
|
||||
{
|
||||
for evt in self.buffer.drain(..) {
|
||||
callback(evt)
|
||||
}
|
||||
@@ -57,15 +58,15 @@ pub struct EventsLoop {
|
||||
// Whether or not there is a pending `Awakened` event to be emitted.
|
||||
pending_wakeup: Arc<AtomicBool>,
|
||||
// The window store
|
||||
pub store: StateToken<WindowStore>,
|
||||
pub store: Arc<Mutex<WindowStore>>,
|
||||
// the env
|
||||
env_token: StateToken<EnvHandler<InnerEnv>>,
|
||||
// the ctxt
|
||||
pub ctxt_token: StateToken<StateContext>,
|
||||
pub env: Environment,
|
||||
// a cleanup switch to prune dead windows
|
||||
pub cleanup_needed: Arc<Mutex<bool>>,
|
||||
// The wayland display
|
||||
pub display: Arc<wl_display::WlDisplay>,
|
||||
pub display: Arc<Display>,
|
||||
// The list of seats
|
||||
pub seats: Arc<Mutex<Vec<(u32, Proxy<wl_seat::WlSeat>)>>>,
|
||||
}
|
||||
|
||||
// A handle that can be sent across threads and used to wake up the `EventsLoop`.
|
||||
@@ -73,7 +74,7 @@ pub struct EventsLoop {
|
||||
// We should only try and wake up the `EventsLoop` if it still exists, so we hold Weak ptrs.
|
||||
#[derive(Clone)]
|
||||
pub struct EventsLoopProxy {
|
||||
display: Weak<wl_display::WlDisplay>,
|
||||
display: Weak<Display>,
|
||||
pending_wakeup: Weak<AtomicBool>,
|
||||
}
|
||||
|
||||
@@ -89,10 +90,10 @@ impl EventsLoopProxy {
|
||||
// Update the `EventsLoop`'s `pending_wakeup` flag.
|
||||
wakeup.store(true, Ordering::Relaxed);
|
||||
// Cause the `EventsLoop` to break from `dispatch` if it is currently blocked.
|
||||
display.sync();
|
||||
let _ = display.sync();
|
||||
display.flush().map_err(|_| EventsLoopClosed)?;
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
_ => Err(EventsLoopClosed),
|
||||
}
|
||||
}
|
||||
@@ -100,58 +101,35 @@ impl EventsLoopProxy {
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn new() -> Option<EventsLoop> {
|
||||
let (display, mut event_queue) = match default_connect() {
|
||||
let (display, mut event_queue) = match Display::connect_to_env() {
|
||||
Ok(ret) => ret,
|
||||
Err(_) => return None
|
||||
Err(_) => return None,
|
||||
};
|
||||
|
||||
let registry = display.get_registry();
|
||||
let ctxt_token = event_queue.state().insert(
|
||||
StateContext::new(registry.clone().unwrap())
|
||||
);
|
||||
let env_token = EnvHandler::init_with_notify(
|
||||
&mut event_queue,
|
||||
®istry,
|
||||
env_notify(),
|
||||
ctxt_token.clone()
|
||||
);
|
||||
|
||||
// two round trips to fully initialize
|
||||
event_queue.sync_roundtrip().expect("Wayland connection unexpectedly lost");
|
||||
event_queue.sync_roundtrip().expect("Wayland connection unexpectedly lost");
|
||||
|
||||
event_queue.state().with_value(&ctxt_token, |proxy, ctxt| {
|
||||
ctxt.ensure_shell(proxy.get_mut(&env_token))
|
||||
});
|
||||
|
||||
let sink = Arc::new(Mutex::new(EventsLoopSink::new()));
|
||||
let store = Arc::new(Mutex::new(WindowStore::new()));
|
||||
let seats = Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
let store = event_queue.state().insert(WindowStore::new());
|
||||
let env = Environment::from_registry_with_cb(
|
||||
display.get_registry().unwrap(),
|
||||
&mut event_queue,
|
||||
SeatManager {
|
||||
sink: sink.clone(),
|
||||
store: store.clone(),
|
||||
seats: seats.clone(),
|
||||
},
|
||||
).unwrap();
|
||||
|
||||
let seat_idata = SeatIData {
|
||||
sink: sink.clone(),
|
||||
keyboard: None,
|
||||
pointer: None,
|
||||
touch: None,
|
||||
windows_token: store.clone()
|
||||
};
|
||||
|
||||
let mut me = EventsLoop {
|
||||
Some(EventsLoop {
|
||||
display: Arc::new(display),
|
||||
evq: RefCell::new(event_queue),
|
||||
sink: sink,
|
||||
pending_wakeup: Arc::new(AtomicBool::new(false)),
|
||||
store: store,
|
||||
ctxt_token: ctxt_token,
|
||||
env_token: env_token,
|
||||
cleanup_needed: Arc::new(Mutex::new(false))
|
||||
};
|
||||
|
||||
me.init_seat(|evqh, seat| {
|
||||
evqh.register(seat, seat_implementation(), seat_idata);
|
||||
});
|
||||
|
||||
Some(me)
|
||||
env: env,
|
||||
cleanup_needed: Arc::new(Mutex::new(false)),
|
||||
seats: seats,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventsLoopProxy {
|
||||
@@ -162,7 +140,8 @@ impl EventsLoop {
|
||||
}
|
||||
|
||||
pub fn poll_events<F>(&mut self, mut callback: F)
|
||||
where F: FnMut(::Event)
|
||||
where
|
||||
F: FnMut(::Event),
|
||||
{
|
||||
// send pending events to the server
|
||||
self.display.flush().expect("Wayland connection lost.");
|
||||
@@ -175,7 +154,10 @@ impl EventsLoop {
|
||||
h.read_events().expect("Wayland connection lost.");
|
||||
}
|
||||
// dispatch wayland events
|
||||
self.evq.get_mut().dispatch_pending().expect("Wayland connection lost.");
|
||||
self.evq
|
||||
.get_mut()
|
||||
.dispatch_pending()
|
||||
.expect("Wayland connection lost.");
|
||||
self.post_dispatch_triggers();
|
||||
|
||||
// dispatch buffered events to client
|
||||
@@ -183,15 +165,18 @@ impl EventsLoop {
|
||||
}
|
||||
|
||||
pub fn run_forever<F>(&mut self, mut callback: F)
|
||||
where F: FnMut(::Event) -> ControlFlow,
|
||||
where
|
||||
F: FnMut(::Event) -> ControlFlow,
|
||||
{
|
||||
// send pending events to the server
|
||||
self.display.flush().expect("Wayland connection lost.");
|
||||
|
||||
// Check for control flow by wrapping the callback.
|
||||
let control_flow = ::std::cell::Cell::new(ControlFlow::Continue);
|
||||
let mut callback = |event| if let ControlFlow::Break = callback(event) {
|
||||
control_flow.set(ControlFlow::Break);
|
||||
let mut callback = |event| {
|
||||
if let ControlFlow::Break = callback(event) {
|
||||
control_flow.set(ControlFlow::Break);
|
||||
}
|
||||
};
|
||||
|
||||
// dispatch any pre-buffered events
|
||||
@@ -200,7 +185,10 @@ impl EventsLoop {
|
||||
|
||||
loop {
|
||||
// dispatch events blocking if needed
|
||||
self.evq.get_mut().dispatch().expect("Wayland connection lost.");
|
||||
self.evq
|
||||
.get_mut()
|
||||
.dispatch()
|
||||
.expect("Wayland connection lost.");
|
||||
self.post_dispatch_triggers();
|
||||
|
||||
// empty buffer of events
|
||||
@@ -213,25 +201,27 @@ impl EventsLoop {
|
||||
}
|
||||
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
let mut guard = self.evq.borrow_mut();
|
||||
let state = guard.state();
|
||||
let state_ctxt = state.get(&self.ctxt_token);
|
||||
if let Some(info) = state_ctxt.monitors.iter().next() {
|
||||
MonitorId {
|
||||
info: info.clone()
|
||||
self.env.outputs.with_all(|list| {
|
||||
if let Some(&(_, ref proxy, _)) = list.first() {
|
||||
MonitorId {
|
||||
proxy: proxy.clone(),
|
||||
mgr: self.env.outputs.clone(),
|
||||
}
|
||||
} else {
|
||||
panic!("No monitor is available.")
|
||||
}
|
||||
} else {
|
||||
panic!("No monitor is available.")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
let mut guard = self.evq.borrow_mut();
|
||||
let state = guard.state();
|
||||
let state_ctxt = state.get(&self.ctxt_token);
|
||||
state_ctxt.monitors.iter()
|
||||
.map(|m| MonitorId { info: m.clone() })
|
||||
.collect()
|
||||
self.env.outputs.with_all(|list| {
|
||||
list.iter()
|
||||
.map(|&(_, ref proxy, _)| MonitorId {
|
||||
proxy: proxy.clone(),
|
||||
mgr: self.env.outputs.clone(),
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,93 +229,9 @@ impl EventsLoop {
|
||||
* Private EventsLoop Internals
|
||||
*/
|
||||
|
||||
wayland_env!(InnerEnv,
|
||||
compositor: wl_compositor::WlCompositor,
|
||||
shm: wl_shm::WlShm,
|
||||
subcompositor: wl_subcompositor::WlSubcompositor
|
||||
);
|
||||
|
||||
pub struct StateContext {
|
||||
registry: wl_registry::WlRegistry,
|
||||
seat: Option<wl_seat::WlSeat>,
|
||||
shell: Option<Shell>,
|
||||
monitors: Vec<Arc<Mutex<OutputInfo>>>
|
||||
}
|
||||
|
||||
impl StateContext {
|
||||
fn new(registry: wl_registry::WlRegistry) -> StateContext {
|
||||
StateContext {
|
||||
registry: registry,
|
||||
seat: None,
|
||||
shell: None,
|
||||
monitors: Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures a shell is available
|
||||
///
|
||||
/// If a shell is already bound, do nothing. Otherwise,
|
||||
/// try to bind wl_shell as a fallback. If this fails,
|
||||
/// panic, as this is a bug from the compositor.
|
||||
fn ensure_shell(&mut self, env: &mut EnvHandler<InnerEnv>) {
|
||||
if self.shell.is_some() {
|
||||
return;
|
||||
}
|
||||
// xdg_shell is not available, so initialize wl_shell
|
||||
for &(name, ref interface, _) in env.globals() {
|
||||
if interface == "wl_shell" {
|
||||
self.shell = Some(Shell::Wl(self.registry.bind::<wl_shell::WlShell>(1, name)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
// This is a compositor bug, it _must_ at least support wl_shell
|
||||
panic!("Compositor didi not advertize xdg_shell not wl_shell.");
|
||||
}
|
||||
|
||||
pub fn monitor_id_for(&self, output: &wl_output::WlOutput) -> MonitorId {
|
||||
for info in &self.monitors {
|
||||
let guard = info.lock().unwrap();
|
||||
if guard.output.equals(output) {
|
||||
return MonitorId {
|
||||
info: info.clone()
|
||||
};
|
||||
}
|
||||
}
|
||||
panic!("Received an inexistent wl_output?!");
|
||||
}
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn init_seat<F>(&mut self, f: F)
|
||||
where F: FnOnce(&mut EventQueueHandle, &wl_seat::WlSeat)
|
||||
{
|
||||
let mut guard = self.evq.borrow_mut();
|
||||
if guard.state().get(&self.ctxt_token).seat.is_some() {
|
||||
// seat has already been init
|
||||
return;
|
||||
}
|
||||
|
||||
// clone the token to make borrow checker happy
|
||||
let ctxt_token = self.ctxt_token.clone();
|
||||
let seat = guard.state().with_value(&self.env_token, |proxy, env| {
|
||||
let ctxt = proxy.get(&ctxt_token);
|
||||
for &(name, ref interface, _) in env.globals() {
|
||||
if interface == wl_seat::WlSeat::interface_name() {
|
||||
return Some(ctxt.registry.bind::<wl_seat::WlSeat>(5, name));
|
||||
}
|
||||
}
|
||||
None
|
||||
});
|
||||
|
||||
if let Some(seat) = seat {
|
||||
f(&mut *guard, &seat);
|
||||
guard.state().get_mut(&self.ctxt_token).seat = Some(seat)
|
||||
}
|
||||
}
|
||||
|
||||
fn post_dispatch_triggers(&mut self) {
|
||||
let mut sink = self.sink.lock().unwrap();
|
||||
let evq = self.evq.get_mut();
|
||||
// process a possible pending wakeup call
|
||||
if self.pending_wakeup.load(Ordering::Relaxed) {
|
||||
sink.send_raw_event(::Event::Awakened);
|
||||
@@ -335,7 +241,7 @@ impl EventsLoop {
|
||||
{
|
||||
let mut cleanup_needed = self.cleanup_needed.lock().unwrap();
|
||||
if *cleanup_needed {
|
||||
let pruned = evq.state().get_mut(&self.store).cleanup();
|
||||
let pruned = self.store.lock().unwrap().cleanup();
|
||||
*cleanup_needed = false;
|
||||
for wid in pruned {
|
||||
sink.send_event(::WindowEvent::Destroyed, wid);
|
||||
@@ -343,11 +249,11 @@ impl EventsLoop {
|
||||
}
|
||||
}
|
||||
// process pending resize/refresh
|
||||
evq.state().get_mut(&self.store).for_each(
|
||||
self.store.lock().unwrap().for_each(
|
||||
|newsize, refresh, frame_refresh, closed, wid, frame| {
|
||||
if let Some(frame) = frame {
|
||||
if let Some((w, h)) = newsize {
|
||||
frame.resize(w as i32, h as i32);
|
||||
frame.resize(w as u32, h as u32);
|
||||
frame.refresh();
|
||||
sink.send_event(::WindowEvent::Resized(w as u32, h as u32), wid);
|
||||
} else if frame_refresh {
|
||||
@@ -360,140 +266,147 @@ impl EventsLoop {
|
||||
if closed {
|
||||
sink.send_event(::WindowEvent::CloseRequested, wid);
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a new window with given dimensions
|
||||
///
|
||||
/// Grabs a lock on the event queue in the process
|
||||
pub fn create_window<ID: 'static, F>(&self, width: u32, height: u32, implem: FrameImplementation<ID>, idata: F)
|
||||
-> (wl_surface::WlSurface, Frame)
|
||||
where F: FnOnce(&wl_surface::WlSurface) -> ID
|
||||
{
|
||||
let (surface, frame) = {
|
||||
let mut guard = self.evq.borrow_mut();
|
||||
let env = guard.state().get(&self.env_token).clone_inner().unwrap();
|
||||
let shell = match guard.state().get(&self.ctxt_token).shell {
|
||||
Some(Shell::Wl(ref wl_shell)) => Shell::Wl(wl_shell.clone().unwrap()),
|
||||
Some(Shell::Xdg(ref xdg_shell)) => Shell::Xdg(xdg_shell.clone().unwrap()),
|
||||
None => unreachable!()
|
||||
};
|
||||
let seat = guard.state().get(&self.ctxt_token).seat.as_ref().and_then(|s| s.clone());
|
||||
let surface = env.compositor.create_surface();
|
||||
let frame = create_frame(
|
||||
&mut guard,
|
||||
implem,
|
||||
idata(&surface),
|
||||
&surface, width as i32, height as i32,
|
||||
&env.compositor,
|
||||
&env.subcompositor,
|
||||
&env.shm,
|
||||
&shell,
|
||||
seat
|
||||
).expect("Failed to create a tmpfile buffer.");
|
||||
(surface, frame)
|
||||
};
|
||||
|
||||
(surface, frame)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Wayland protocol implementations
|
||||
*/
|
||||
|
||||
fn env_notify() -> EnvNotify<StateToken<StateContext>> {
|
||||
EnvNotify {
|
||||
new_global: |evqh, token, registry, id, interface, version| {
|
||||
use std::cmp::min;
|
||||
if interface == wl_output::WlOutput::interface_name() {
|
||||
// a new output is available
|
||||
let output = registry.bind::<wl_output::WlOutput>(min(version, 3), id);
|
||||
evqh.register(&output, output_impl(), token.clone());
|
||||
evqh.state().get_mut(&token).monitors.push(
|
||||
Arc::new(Mutex::new(OutputInfo::new(output, id)))
|
||||
);
|
||||
} else if interface == zxdg_shell_v6::ZxdgShellV6::interface_name() {
|
||||
// We have an xdg_shell, bind it
|
||||
let xdg_shell = registry.bind::<zxdg_shell_v6::ZxdgShellV6>(1, id);
|
||||
evqh.register(&xdg_shell, xdg_ping_implementation(), ());
|
||||
evqh.state().get_mut(&token).shell = Some(Shell::Xdg(xdg_shell));
|
||||
}
|
||||
},
|
||||
del_global: |evqh, token, _, id| {
|
||||
// maybe this was a monitor, cleanup
|
||||
evqh.state().get_mut(&token).monitors.retain(
|
||||
|m| m.lock().unwrap().id != id
|
||||
);
|
||||
},
|
||||
ready: |_, _, _| {}
|
||||
}
|
||||
struct SeatManager {
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
store: Arc<Mutex<WindowStore>>,
|
||||
seats: Arc<Mutex<Vec<(u32, Proxy<wl_seat::WlSeat>)>>>,
|
||||
}
|
||||
|
||||
fn xdg_ping_implementation() -> zxdg_shell_v6::Implementation<()> {
|
||||
zxdg_shell_v6::Implementation {
|
||||
ping: |_, _, shell, serial| {
|
||||
shell.pong(serial);
|
||||
impl Implementation<Proxy<wl_registry::WlRegistry>, GlobalEvent> for SeatManager {
|
||||
fn receive(&mut self, evt: GlobalEvent, registry: Proxy<wl_registry::WlRegistry>) {
|
||||
use self::wl_registry::RequestsTrait as RegistryRequests;
|
||||
use self::wl_seat::RequestsTrait as SeatRequests;
|
||||
match evt {
|
||||
GlobalEvent::New {
|
||||
id,
|
||||
ref interface,
|
||||
version,
|
||||
} if interface == "wl_seat" =>
|
||||
{
|
||||
use std::cmp::min;
|
||||
let seat = registry
|
||||
.bind::<wl_seat::WlSeat>(min(version, 5), id)
|
||||
.unwrap()
|
||||
.implement(SeatData {
|
||||
sink: self.sink.clone(),
|
||||
store: self.store.clone(),
|
||||
pointer: None,
|
||||
keyboard: None,
|
||||
touch: None,
|
||||
});
|
||||
self.store.lock().unwrap().new_seat(&seat);
|
||||
self.seats.lock().unwrap().push((id, seat));
|
||||
}
|
||||
GlobalEvent::Removed { id, ref interface } if interface == "wl_seat" => {
|
||||
let mut seats = self.seats.lock().unwrap();
|
||||
if let Some(idx) = seats.iter().position(|&(i, _)| i == id) {
|
||||
let (_, seat) = seats.swap_remove(idx);
|
||||
if seat.version() >= 5 {
|
||||
seat.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SeatIData {
|
||||
struct SeatData {
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
pointer: Option<wl_pointer::WlPointer>,
|
||||
keyboard: Option<wl_keyboard::WlKeyboard>,
|
||||
touch: Option<wl_touch::WlTouch>,
|
||||
windows_token: StateToken<WindowStore>
|
||||
store: Arc<Mutex<WindowStore>>,
|
||||
pointer: Option<Proxy<wl_pointer::WlPointer>>,
|
||||
keyboard: Option<Proxy<wl_keyboard::WlKeyboard>>,
|
||||
touch: Option<Proxy<wl_touch::WlTouch>>,
|
||||
}
|
||||
|
||||
fn seat_implementation() -> wl_seat::Implementation<SeatIData> {
|
||||
wl_seat::Implementation {
|
||||
name: |_, _, _, _| {},
|
||||
capabilities: |evqh, idata, seat, capabilities| {
|
||||
// create pointer if applicable
|
||||
if capabilities.contains(wl_seat::Capability::Pointer) && idata.pointer.is_none() {
|
||||
let pointer = seat.get_pointer().expect("Seat is not dead");
|
||||
let p_idata = super::pointer::PointerIData::new(
|
||||
&idata.sink,
|
||||
idata.windows_token.clone()
|
||||
);
|
||||
evqh.register(&pointer, super::pointer::pointer_implementation(), p_idata);
|
||||
idata.pointer = Some(pointer);
|
||||
}
|
||||
// destroy pointer if applicable
|
||||
if !capabilities.contains(wl_seat::Capability::Pointer) {
|
||||
if let Some(pointer) = idata.pointer.take() {
|
||||
pointer.release();
|
||||
impl Implementation<Proxy<wl_seat::WlSeat>, wl_seat::Event> for SeatData {
|
||||
fn receive(&mut self, evt: wl_seat::Event, seat: Proxy<wl_seat::WlSeat>) {
|
||||
use self::wl_seat::RequestsTrait as SeatRequests;
|
||||
match evt {
|
||||
wl_seat::Event::Name { .. } => (),
|
||||
wl_seat::Event::Capabilities { capabilities } => {
|
||||
// create pointer if applicable
|
||||
if capabilities.contains(wl_seat::Capability::Pointer) && self.pointer.is_none() {
|
||||
self.pointer = Some(super::pointer::implement_pointer(
|
||||
seat.get_pointer().unwrap(),
|
||||
self.sink.clone(),
|
||||
self.store.clone(),
|
||||
))
|
||||
}
|
||||
// destroy pointer if applicable
|
||||
if !capabilities.contains(wl_seat::Capability::Pointer) {
|
||||
if let Some(pointer) = self.pointer.take() {
|
||||
if pointer.version() >= 3 {
|
||||
use self::wl_pointer::RequestsTrait;
|
||||
pointer.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
// create keyboard if applicable
|
||||
if capabilities.contains(wl_seat::Capability::Keyboard) && self.keyboard.is_none() {
|
||||
self.keyboard = Some(super::keyboard::init_keyboard(
|
||||
seat.get_keyboard().unwrap(),
|
||||
self.sink.clone(),
|
||||
))
|
||||
}
|
||||
// destroy keyboard if applicable
|
||||
if !capabilities.contains(wl_seat::Capability::Keyboard) {
|
||||
if let Some(kbd) = self.keyboard.take() {
|
||||
if kbd.version() >= 3 {
|
||||
use self::wl_keyboard::RequestsTrait;
|
||||
kbd.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
// create touch if applicable
|
||||
if capabilities.contains(wl_seat::Capability::Touch) && self.touch.is_none() {
|
||||
self.touch = Some(super::touch::implement_touch(
|
||||
seat.get_touch().unwrap(),
|
||||
self.sink.clone(),
|
||||
self.store.clone(),
|
||||
))
|
||||
}
|
||||
// destroy touch if applicable
|
||||
if !capabilities.contains(wl_seat::Capability::Touch) {
|
||||
if let Some(touch) = self.touch.take() {
|
||||
if touch.version() >= 3 {
|
||||
use self::wl_touch::RequestsTrait;
|
||||
touch.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// create keyboard if applicable
|
||||
if capabilities.contains(wl_seat::Capability::Keyboard) && idata.keyboard.is_none() {
|
||||
let kbd = seat.get_keyboard().expect("Seat is not dead");
|
||||
init_keyboard(evqh, &kbd, &idata.sink);
|
||||
idata.keyboard = Some(kbd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SeatData {
|
||||
fn drop(&mut self) {
|
||||
if let Some(pointer) = self.pointer.take() {
|
||||
if pointer.version() >= 3 {
|
||||
use self::wl_pointer::RequestsTrait;
|
||||
pointer.release();
|
||||
}
|
||||
// destroy keyboard if applicable
|
||||
if !capabilities.contains(wl_seat::Capability::Keyboard) {
|
||||
if let Some(kbd) = idata.keyboard.take() {
|
||||
kbd.release();
|
||||
}
|
||||
}
|
||||
if let Some(kbd) = self.keyboard.take() {
|
||||
if kbd.version() >= 3 {
|
||||
use self::wl_keyboard::RequestsTrait;
|
||||
kbd.release();
|
||||
}
|
||||
// create touch if applicable
|
||||
if capabilities.contains(wl_seat::Capability::Touch) && idata.touch.is_none() {
|
||||
let touch = seat.get_touch().expect("Seat is not dead");
|
||||
let t_idata = super::touch::TouchIData::new(
|
||||
&idata.sink,
|
||||
idata.windows_token.clone()
|
||||
);
|
||||
evqh.register(&touch, super::touch::touch_implementation(), t_idata);
|
||||
idata.touch = Some(touch);
|
||||
}
|
||||
// destroy touch if applicable
|
||||
if !capabilities.contains(wl_seat::Capability::Touch) {
|
||||
if let Some(touch) = idata.touch.take() {
|
||||
touch.release();
|
||||
}
|
||||
}
|
||||
if let Some(touch) = self.touch.take() {
|
||||
if touch.version() >= 3 {
|
||||
use self::wl_touch::RequestsTrait;
|
||||
touch.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -503,92 +416,54 @@ fn seat_implementation() -> wl_seat::Implementation<SeatIData> {
|
||||
* Monitor stuff
|
||||
*/
|
||||
|
||||
fn output_impl() -> wl_output::Implementation<StateToken<StateContext>> {
|
||||
wl_output::Implementation {
|
||||
geometry: |evqh, token, output, x, y, _, _, _, make, model, _| {
|
||||
let ctxt = evqh.state().get_mut(token);
|
||||
for info in &ctxt.monitors {
|
||||
let mut guard = info.lock().unwrap();
|
||||
if guard.output.equals(output) {
|
||||
guard.pix_pos = (x, y);
|
||||
guard.name = format!("{} - {}", make, model);
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
mode: |evqh, token, output, flags, w, h, _refresh| {
|
||||
if flags.contains(wl_output::Mode::Current) {
|
||||
let ctxt = evqh.state().get_mut(token);
|
||||
for info in &ctxt.monitors {
|
||||
let mut guard = info.lock().unwrap();
|
||||
if guard.output.equals(output) {
|
||||
guard.pix_size = (w as u32, h as u32);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
done: |_, _, _| {},
|
||||
scale: |evqh, token, output, scale| {
|
||||
let ctxt = evqh.state().get_mut(token);
|
||||
for info in &ctxt.monitors {
|
||||
let mut guard = info.lock().unwrap();
|
||||
if guard.output.equals(output) {
|
||||
guard.scale = scale as f32;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct OutputInfo {
|
||||
pub output: wl_output::WlOutput,
|
||||
pub id: u32,
|
||||
pub scale: f32,
|
||||
pub pix_size: (u32, u32),
|
||||
pub pix_pos: (i32, i32),
|
||||
pub name: String
|
||||
}
|
||||
|
||||
impl OutputInfo {
|
||||
fn new(output: wl_output::WlOutput, id: u32) -> OutputInfo {
|
||||
OutputInfo {
|
||||
output: output,
|
||||
id: id,
|
||||
scale: 1.0,
|
||||
pix_size: (0, 0),
|
||||
pix_pos: (0, 0),
|
||||
name: "".into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MonitorId {
|
||||
pub info: Arc<Mutex<OutputInfo>>
|
||||
pub(crate) proxy: Proxy<wl_output::WlOutput>,
|
||||
pub(crate) mgr: OutputMgr,
|
||||
}
|
||||
|
||||
impl Clone for MonitorId {
|
||||
fn clone(&self) -> MonitorId {
|
||||
MonitorId {
|
||||
proxy: self.proxy.clone(),
|
||||
mgr: self.mgr.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
Some(self.info.lock().unwrap().name.clone())
|
||||
self.mgr.with_info(&self.proxy, |_, info| {
|
||||
format!("{} ({})", info.model, info.make)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> u32 {
|
||||
self.info.lock().unwrap().id
|
||||
self.mgr.with_info(&self.proxy, |id, _| id).unwrap_or(0)
|
||||
}
|
||||
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
self.info.lock().unwrap().pix_size
|
||||
match self.mgr.with_info(&self.proxy, |_, info| {
|
||||
info.modes
|
||||
.iter()
|
||||
.find(|m| m.is_current)
|
||||
.map(|m| m.dimensions)
|
||||
}) {
|
||||
Some(Some((w, h))) => (w as u32, h as u32),
|
||||
_ => (0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
self.info.lock().unwrap().pix_pos
|
||||
self.mgr
|
||||
.with_info(&self.proxy, |_, info| info.location)
|
||||
.unwrap_or((0, 0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
self.info.lock().unwrap().scale
|
||||
self.mgr
|
||||
.with_info(&self.proxy, |_, info| info.scale_factor as f32)
|
||||
.unwrap_or(1.0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,150 +1,154 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use {VirtualKeyCode, ElementState, WindowEvent as Event, KeyboardInput, ModifiersState};
|
||||
use {ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent};
|
||||
|
||||
use super::{EventsLoopSink, WindowId, make_wid, DeviceId};
|
||||
use super::wayland_kbd::{MappedKeyboardImplementation, register_kbd};
|
||||
use wayland_client::protocol::wl_keyboard;
|
||||
use wayland_client::EventQueueHandle;
|
||||
use super::{make_wid, DeviceId, EventsLoopSink};
|
||||
use sctk::keyboard::{self, map_keyboard_auto, Event as KbEvent};
|
||||
use sctk::reexports::client::{NewProxy, Proxy};
|
||||
use sctk::reexports::client::protocol::wl_keyboard;
|
||||
|
||||
pub fn init_keyboard(evq: &mut EventQueueHandle, keyboard: &wl_keyboard::WlKeyboard, sink: &Arc<Mutex<EventsLoopSink>>) {
|
||||
let idata = KeyboardIData {
|
||||
sink: sink.clone(),
|
||||
target: None
|
||||
};
|
||||
|
||||
if register_kbd(evq, keyboard, mapped_keyboard_impl(), idata).is_err() {
|
||||
// initializing libxkbcommon failed :(
|
||||
// fallback implementation
|
||||
let idata = KeyboardIData {
|
||||
sink: sink.clone(),
|
||||
target: None
|
||||
};
|
||||
evq.register(keyboard, raw_keyboard_impl(), idata);
|
||||
}
|
||||
}
|
||||
|
||||
struct KeyboardIData {
|
||||
pub fn init_keyboard(
|
||||
keyboard: NewProxy<wl_keyboard::WlKeyboard>,
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
target: Option<WindowId>
|
||||
}
|
||||
|
||||
fn mapped_keyboard_impl() -> MappedKeyboardImplementation<KeyboardIData> {
|
||||
MappedKeyboardImplementation {
|
||||
enter: |_, idata, _, _, surface, _, _, _| {
|
||||
let wid = make_wid(surface);
|
||||
idata.sink.lock().unwrap().send_event(Event::Focused(true), wid);
|
||||
idata.target = Some(wid);
|
||||
},
|
||||
leave: |_, idata, _, _, surface| {
|
||||
let wid = make_wid(surface);
|
||||
idata.sink.lock().unwrap().send_event(Event::Focused(false), wid);
|
||||
idata.target = None;
|
||||
},
|
||||
key: |_, idata, _, _, _, mods, rawkey, keysym, state, utf8| {
|
||||
if let Some(wid) = idata.target {
|
||||
) -> Proxy<wl_keyboard::WlKeyboard> {
|
||||
// { variables to be captured by the closure
|
||||
let mut target = None;
|
||||
let my_sink = sink.clone();
|
||||
// }
|
||||
let ret = map_keyboard_auto(keyboard, move |evt: KbEvent, _| match evt {
|
||||
KbEvent::Enter { surface, .. } => {
|
||||
let wid = make_wid(&surface);
|
||||
my_sink
|
||||
.lock()
|
||||
.unwrap()
|
||||
.send_event(WindowEvent::Focused(true), wid);
|
||||
target = Some(wid);
|
||||
}
|
||||
KbEvent::Leave { surface, .. } => {
|
||||
let wid = make_wid(&surface);
|
||||
my_sink
|
||||
.lock()
|
||||
.unwrap()
|
||||
.send_event(WindowEvent::Focused(false), wid);
|
||||
target = None;
|
||||
}
|
||||
KbEvent::Key {
|
||||
modifiers,
|
||||
rawkey,
|
||||
keysym,
|
||||
state,
|
||||
utf8,
|
||||
..
|
||||
} => {
|
||||
if let Some(wid) = target {
|
||||
let state = match state {
|
||||
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
|
||||
wl_keyboard::KeyState::Released => ElementState::Released,
|
||||
};
|
||||
let vkcode = key_to_vkey(rawkey, keysym);
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
let mut guard = my_sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
Event::KeyboardInput {
|
||||
WindowEvent::KeyboardInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: rawkey,
|
||||
virtual_keycode: vkcode,
|
||||
modifiers: ModifiersState {
|
||||
shift: mods.shift,
|
||||
ctrl: mods.ctrl,
|
||||
alt: mods.alt,
|
||||
logo: mods.logo
|
||||
},
|
||||
modifiers: modifiers.into(),
|
||||
},
|
||||
},
|
||||
wid
|
||||
wid,
|
||||
);
|
||||
// send char event only on key press, not release
|
||||
if let ElementState::Released = state { return }
|
||||
if let ElementState::Released = state {
|
||||
return;
|
||||
}
|
||||
if let Some(txt) = utf8 {
|
||||
for chr in txt.chars() {
|
||||
guard.send_event(Event::ReceivedCharacter(chr), wid);
|
||||
guard.send_event(WindowEvent::ReceivedCharacter(chr), wid);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
repeat_info: |_, _idata, _, _rate, _delay| {
|
||||
// TODO: handle repeat info
|
||||
}
|
||||
}
|
||||
}
|
||||
KbEvent::RepeatInfo { .. } => { /* TODO: handle repeat info */ }
|
||||
});
|
||||
|
||||
match ret {
|
||||
Ok(keyboard) => keyboard,
|
||||
Err((_, keyboard)) => {
|
||||
// This is a fallback impl if libxkbcommon was not available
|
||||
// This case should probably never happen, as most wayland
|
||||
// compositors _need_ libxkbcommon anyway...
|
||||
//
|
||||
// In this case, we don't have the keymap information (it is
|
||||
// supposed to be serialized by the compositor using libxkbcommon)
|
||||
|
||||
// This is fallback impl if libxkbcommon was not available
|
||||
// This case should probably never happen, as most wayland
|
||||
// compositors _need_ libxkbcommon anyway...
|
||||
//
|
||||
// In this case, we don't have the keymap information (it is
|
||||
// supposed to be serialized by the compositor using libxkbcommon)
|
||||
fn raw_keyboard_impl() -> wl_keyboard::Implementation<KeyboardIData> {
|
||||
wl_keyboard::Implementation {
|
||||
enter: |_, idata, _, _, surface, _| {
|
||||
let wid = make_wid(surface);
|
||||
idata.sink.lock().unwrap().send_event(Event::Focused(true), wid);
|
||||
idata.target = Some(wid);
|
||||
},
|
||||
leave: |_, idata, _, _, surface| {
|
||||
let wid = make_wid(surface);
|
||||
idata.sink.lock().unwrap().send_event(Event::Focused(false), wid);
|
||||
idata.target = None;
|
||||
},
|
||||
key: |_, idata, _, _, _, key, state| {
|
||||
if let Some(wid) = idata.target {
|
||||
let state = match state {
|
||||
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
|
||||
wl_keyboard::KeyState::Released => ElementState::Released,
|
||||
};
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::KeyboardInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: key,
|
||||
virtual_keycode: None,
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
},
|
||||
wid
|
||||
);
|
||||
}
|
||||
},
|
||||
repeat_info: |_, _idata, _, _rate, _delay| {},
|
||||
keymap: |_, _, _, _, _, _| {},
|
||||
modifiers: |_, _, _, _, _, _, _, _| {}
|
||||
// { variables to be captured by the closure
|
||||
let mut target = None;
|
||||
let my_sink = sink;
|
||||
// }
|
||||
keyboard.implement(move |evt, _| match evt {
|
||||
wl_keyboard::Event::Enter { surface, .. } => {
|
||||
let wid = make_wid(&surface);
|
||||
my_sink
|
||||
.lock()
|
||||
.unwrap()
|
||||
.send_event(WindowEvent::Focused(true), wid);
|
||||
target = Some(wid);
|
||||
}
|
||||
wl_keyboard::Event::Leave { surface, .. } => {
|
||||
let wid = make_wid(&surface);
|
||||
my_sink
|
||||
.lock()
|
||||
.unwrap()
|
||||
.send_event(WindowEvent::Focused(false), wid);
|
||||
target = None;
|
||||
}
|
||||
wl_keyboard::Event::Key { key, state, .. } => {
|
||||
if let Some(wid) = target {
|
||||
let state = match state {
|
||||
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
|
||||
wl_keyboard::KeyState::Released => ElementState::Released,
|
||||
};
|
||||
my_sink.lock().unwrap().send_event(
|
||||
WindowEvent::KeyboardInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: key,
|
||||
virtual_keycode: None,
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn key_to_vkey(rawkey: u32, keysym: u32) -> Option<VirtualKeyCode> {
|
||||
match rawkey {
|
||||
1 => Some(VirtualKeyCode::Escape),
|
||||
2 => Some(VirtualKeyCode::Key1),
|
||||
3 => Some(VirtualKeyCode::Key2),
|
||||
4 => Some(VirtualKeyCode::Key3),
|
||||
5 => Some(VirtualKeyCode::Key4),
|
||||
6 => Some(VirtualKeyCode::Key5),
|
||||
7 => Some(VirtualKeyCode::Key6),
|
||||
8 => Some(VirtualKeyCode::Key7),
|
||||
9 => Some(VirtualKeyCode::Key8),
|
||||
1 => Some(VirtualKeyCode::Escape),
|
||||
2 => Some(VirtualKeyCode::Key1),
|
||||
3 => Some(VirtualKeyCode::Key2),
|
||||
4 => Some(VirtualKeyCode::Key3),
|
||||
5 => Some(VirtualKeyCode::Key4),
|
||||
6 => Some(VirtualKeyCode::Key5),
|
||||
7 => Some(VirtualKeyCode::Key6),
|
||||
8 => Some(VirtualKeyCode::Key7),
|
||||
9 => Some(VirtualKeyCode::Key8),
|
||||
10 => Some(VirtualKeyCode::Key9),
|
||||
11 => Some(VirtualKeyCode::Key0),
|
||||
_ => keysym_to_vkey(keysym)
|
||||
_ => keysym_to_vkey(keysym),
|
||||
}
|
||||
}
|
||||
|
||||
fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
|
||||
use super::wayland_kbd::keysyms;
|
||||
use sctk::keyboard::keysyms;
|
||||
match keysym {
|
||||
// letters
|
||||
keysyms::XKB_KEY_A | keysyms::XKB_KEY_a => Some(VirtualKeyCode::A),
|
||||
@@ -174,15 +178,15 @@ fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
|
||||
keysyms::XKB_KEY_Y | keysyms::XKB_KEY_y => Some(VirtualKeyCode::Y),
|
||||
keysyms::XKB_KEY_Z | keysyms::XKB_KEY_z => Some(VirtualKeyCode::Z),
|
||||
// F--
|
||||
keysyms::XKB_KEY_F1 => Some(VirtualKeyCode::F1),
|
||||
keysyms::XKB_KEY_F2 => Some(VirtualKeyCode::F2),
|
||||
keysyms::XKB_KEY_F3 => Some(VirtualKeyCode::F3),
|
||||
keysyms::XKB_KEY_F4 => Some(VirtualKeyCode::F4),
|
||||
keysyms::XKB_KEY_F5 => Some(VirtualKeyCode::F5),
|
||||
keysyms::XKB_KEY_F6 => Some(VirtualKeyCode::F6),
|
||||
keysyms::XKB_KEY_F7 => Some(VirtualKeyCode::F7),
|
||||
keysyms::XKB_KEY_F8 => Some(VirtualKeyCode::F8),
|
||||
keysyms::XKB_KEY_F9 => Some(VirtualKeyCode::F9),
|
||||
keysyms::XKB_KEY_F1 => Some(VirtualKeyCode::F1),
|
||||
keysyms::XKB_KEY_F2 => Some(VirtualKeyCode::F2),
|
||||
keysyms::XKB_KEY_F3 => Some(VirtualKeyCode::F3),
|
||||
keysyms::XKB_KEY_F4 => Some(VirtualKeyCode::F4),
|
||||
keysyms::XKB_KEY_F5 => Some(VirtualKeyCode::F5),
|
||||
keysyms::XKB_KEY_F6 => Some(VirtualKeyCode::F6),
|
||||
keysyms::XKB_KEY_F7 => Some(VirtualKeyCode::F7),
|
||||
keysyms::XKB_KEY_F8 => Some(VirtualKeyCode::F8),
|
||||
keysyms::XKB_KEY_F9 => Some(VirtualKeyCode::F9),
|
||||
keysyms::XKB_KEY_F10 => Some(VirtualKeyCode::F10),
|
||||
keysyms::XKB_KEY_F11 => Some(VirtualKeyCode::F11),
|
||||
keysyms::XKB_KEY_F12 => Some(VirtualKeyCode::F12),
|
||||
@@ -290,7 +294,21 @@ fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
|
||||
// => Some(VirtualKeyCode::WebSearch),
|
||||
// => Some(VirtualKeyCode::WebStop),
|
||||
// => Some(VirtualKeyCode::Yen),
|
||||
keysyms::XKB_KEY_XF86Copy => Some(VirtualKeyCode::Copy),
|
||||
keysyms::XKB_KEY_XF86Paste => Some(VirtualKeyCode::Paste),
|
||||
keysyms::XKB_KEY_XF86Cut => Some(VirtualKeyCode::Cut),
|
||||
// fallback
|
||||
_ => None
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
impl From<keyboard::ModifiersState> for ModifiersState {
|
||||
fn from(mods: keyboard::ModifiersState) -> ModifiersState {
|
||||
ModifiersState {
|
||||
shift: mods.shift,
|
||||
ctrl: mods.ctrl,
|
||||
alt: mods.alt,
|
||||
logo: mods.logo,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd",
|
||||
target_os = "openbsd"))]
|
||||
|
||||
pub use self::window::Window;
|
||||
pub use self::event_loop::{EventsLoop, EventsLoopProxy, EventsLoopSink, MonitorId};
|
||||
|
||||
extern crate wayland_kbd;
|
||||
extern crate wayland_window;
|
||||
extern crate wayland_protocols;
|
||||
|
||||
use wayland_client::protocol::wl_surface;
|
||||
use wayland_client::Proxy;
|
||||
use sctk::reexports::client::protocol::wl_surface;
|
||||
use sctk::reexports::client::Proxy;
|
||||
|
||||
mod event_loop;
|
||||
mod pointer;
|
||||
@@ -23,6 +20,6 @@ pub struct DeviceId;
|
||||
pub struct WindowId(usize);
|
||||
|
||||
#[inline]
|
||||
fn make_wid(s: &wl_surface::WlSurface) -> WindowId {
|
||||
WindowId(s.ptr() as usize)
|
||||
}
|
||||
fn make_wid(s: &Proxy<wl_surface::WlSurface>) -> WindowId {
|
||||
WindowId(s.c_ptr() as usize)
|
||||
}
|
||||
|
||||
@@ -1,194 +1,190 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase};
|
||||
use {ElementState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent};
|
||||
use events::ModifiersState;
|
||||
|
||||
use super::{WindowId, DeviceId};
|
||||
use super::DeviceId;
|
||||
use super::event_loop::EventsLoopSink;
|
||||
use super::window::WindowStore;
|
||||
|
||||
use wayland_client::{Proxy, StateToken};
|
||||
use wayland_client::protocol::wl_pointer;
|
||||
use sctk::reexports::client::{NewProxy, Proxy};
|
||||
use sctk::reexports::client::protocol::wl_pointer::{self, Event as PtrEvent, WlPointer};
|
||||
|
||||
pub struct PointerIData {
|
||||
pub fn implement_pointer(
|
||||
pointer: NewProxy<WlPointer>,
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
windows_token: StateToken<WindowStore>,
|
||||
mouse_focus: Option<WindowId>,
|
||||
axis_buffer: Option<(f32, f32)>,
|
||||
axis_discrete_buffer: Option<(i32, i32)>,
|
||||
axis_state: TouchPhase,
|
||||
}
|
||||
store: Arc<Mutex<WindowStore>>,
|
||||
) -> Proxy<WlPointer> {
|
||||
let mut mouse_focus = None;
|
||||
let mut axis_buffer = None;
|
||||
let mut axis_discrete_buffer = None;
|
||||
let mut axis_state = TouchPhase::Ended;
|
||||
|
||||
impl PointerIData {
|
||||
pub fn new(sink: &Arc<Mutex<EventsLoopSink>>, token: StateToken<WindowStore>)
|
||||
-> PointerIData
|
||||
{
|
||||
PointerIData {
|
||||
sink: sink.clone(),
|
||||
windows_token: token,
|
||||
mouse_focus: None,
|
||||
axis_buffer: None,
|
||||
axis_discrete_buffer: None,
|
||||
axis_state: TouchPhase::Cancelled
|
||||
pointer.implement(move |evt, pointer: Proxy<_>| {
|
||||
let mut sink = sink.lock().unwrap();
|
||||
let store = store.lock().unwrap();
|
||||
match evt {
|
||||
PtrEvent::Enter {
|
||||
surface,
|
||||
surface_x,
|
||||
surface_y,
|
||||
..
|
||||
} => {
|
||||
let wid = store.find_wid(&surface);
|
||||
if let Some(wid) = wid {
|
||||
mouse_focus = Some(wid);
|
||||
sink.send_event(
|
||||
WindowEvent::CursorEntered {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
sink.send_event(
|
||||
WindowEvent::CursorMoved {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
position: (surface_x, surface_y),
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
PtrEvent::Leave { surface, .. } => {
|
||||
mouse_focus = None;
|
||||
let wid = store.find_wid(&surface);
|
||||
if let Some(wid) = wid {
|
||||
sink.send_event(
|
||||
WindowEvent::CursorLeft {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
PtrEvent::Motion {
|
||||
surface_x,
|
||||
surface_y,
|
||||
..
|
||||
} => {
|
||||
if let Some(wid) = mouse_focus {
|
||||
sink.send_event(
|
||||
WindowEvent::CursorMoved {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
position: (surface_x, surface_y),
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
PtrEvent::Button { button, state, .. } => {
|
||||
if let Some(wid) = mouse_focus {
|
||||
let state = match state {
|
||||
wl_pointer::ButtonState::Pressed => ElementState::Pressed,
|
||||
wl_pointer::ButtonState::Released => ElementState::Released,
|
||||
};
|
||||
let button = match button {
|
||||
0x110 => MouseButton::Left,
|
||||
0x111 => MouseButton::Right,
|
||||
0x112 => MouseButton::Middle,
|
||||
// TODO figure out the translation ?
|
||||
_ => return,
|
||||
};
|
||||
sink.send_event(
|
||||
WindowEvent::MouseInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
state: state,
|
||||
button: button,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
PtrEvent::Axis { axis, value, .. } => {
|
||||
if let Some(wid) = mouse_focus {
|
||||
if pointer.version() < 5 {
|
||||
let (mut x, mut y) = (0.0, 0.0);
|
||||
// old seat compatibility
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
||||
wl_pointer::Axis::HorizontalScroll => x += value as f32,
|
||||
}
|
||||
sink.send_event(
|
||||
WindowEvent::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
|
||||
phase: TouchPhase::Moved,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
} else {
|
||||
let (mut x, mut y) = axis_buffer.unwrap_or((0.0, 0.0));
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
||||
wl_pointer::Axis::HorizontalScroll => x += value as f32,
|
||||
}
|
||||
axis_buffer = Some((x, y));
|
||||
axis_state = match axis_state {
|
||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||
_ => TouchPhase::Started,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PtrEvent::Frame => {
|
||||
let axis_buffer = axis_buffer.take();
|
||||
let axis_discrete_buffer = axis_discrete_buffer.take();
|
||||
if let Some(wid) = mouse_focus {
|
||||
if let Some((x, y)) = axis_discrete_buffer {
|
||||
sink.send_event(
|
||||
WindowEvent::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::LineDelta(x as f32, y as f32),
|
||||
phase: axis_state,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
} else if let Some((x, y)) = axis_buffer {
|
||||
sink.send_event(
|
||||
WindowEvent::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
|
||||
phase: axis_state,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
PtrEvent::AxisSource { .. } => (),
|
||||
PtrEvent::AxisStop { .. } => {
|
||||
axis_state = TouchPhase::Ended;
|
||||
}
|
||||
PtrEvent::AxisDiscrete { axis, discrete } => {
|
||||
let (mut x, mut y) = axis_discrete_buffer.unwrap_or((0, 0));
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= discrete,
|
||||
wl_pointer::Axis::HorizontalScroll => x += discrete,
|
||||
}
|
||||
axis_discrete_buffer = Some((x, y));
|
||||
axis_state = match axis_state {
|
||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||
_ => TouchPhase::Started,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pointer_implementation() -> wl_pointer::Implementation<PointerIData> {
|
||||
wl_pointer::Implementation {
|
||||
enter: |evqh, idata, _, _, surface, x, y| {
|
||||
let wid = evqh.state().get(&idata.windows_token).find_wid(surface);
|
||||
if let Some(wid) = wid {
|
||||
idata.mouse_focus = Some(wid);
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
Event::CursorEntered {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
guard.send_event(
|
||||
Event::CursorMoved {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
position: (x, y),
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
},
|
||||
leave: |evqh, idata, _, _, surface| {
|
||||
idata.mouse_focus = None;
|
||||
let wid = evqh.state().get(&idata.windows_token).find_wid(surface);
|
||||
if let Some(wid) = wid {
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
Event::CursorLeft {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
},
|
||||
motion: |_, idata, _, _, x, y| {
|
||||
if let Some(wid) = idata.mouse_focus {
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::CursorMoved {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
position: (x, y),
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid
|
||||
);
|
||||
}
|
||||
},
|
||||
button: |_, idata, _, _, _, button, state| {
|
||||
if let Some(wid) = idata.mouse_focus {
|
||||
let state = match state {
|
||||
wl_pointer::ButtonState::Pressed => ElementState::Pressed,
|
||||
wl_pointer::ButtonState::Released => ElementState::Released
|
||||
};
|
||||
let button = match button {
|
||||
0x110 => MouseButton::Left,
|
||||
0x111 => MouseButton::Right,
|
||||
0x112 => MouseButton::Middle,
|
||||
// TODO figure out the translation ?
|
||||
_ => return
|
||||
};
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::MouseInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
state: state,
|
||||
button: button,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid
|
||||
);
|
||||
}
|
||||
},
|
||||
axis: |_, idata, pointer, _, axis, value| {
|
||||
if let Some(wid) = idata.mouse_focus {
|
||||
if pointer.version() < 5 {
|
||||
let (mut x, mut y) = (0.0, 0.0);
|
||||
// old seat compatibility
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
||||
wl_pointer::Axis::HorizontalScroll => x += value as f32
|
||||
}
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
|
||||
phase: TouchPhase::Moved,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid
|
||||
);
|
||||
} else {
|
||||
let (mut x, mut y) = idata.axis_buffer.unwrap_or((0.0, 0.0));
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
||||
wl_pointer::Axis::HorizontalScroll => x += value as f32
|
||||
}
|
||||
idata.axis_buffer = Some((x,y));
|
||||
idata.axis_state = match idata.axis_state {
|
||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||
_ => TouchPhase::Started
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
frame: |_, idata, _| {
|
||||
let axis_buffer = idata.axis_buffer.take();
|
||||
let axis_discrete_buffer = idata.axis_discrete_buffer.take();
|
||||
if let Some(wid) = idata.mouse_focus {
|
||||
if let Some((x, y)) = axis_discrete_buffer {
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::LineDelta(x as f32, y as f32),
|
||||
phase: idata.axis_state,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid
|
||||
);
|
||||
} else if let Some((x, y)) = axis_buffer {
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
|
||||
phase: idata.axis_state,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
axis_source: |_, _, _, _| {},
|
||||
axis_stop: |_, idata, _, _, _| {
|
||||
idata.axis_state = TouchPhase::Ended;
|
||||
},
|
||||
axis_discrete: |_, idata, _, axis, discrete| {
|
||||
let (mut x, mut y) = idata.axis_discrete_buffer.unwrap_or((0,0));
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= discrete,
|
||||
wl_pointer::Axis::HorizontalScroll => x += discrete
|
||||
}
|
||||
idata.axis_discrete_buffer = Some((x,y));
|
||||
idata.axis_state = match idata.axis_state {
|
||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||
_ => TouchPhase::Started
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,106 +1,93 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use {WindowEvent as Event, TouchPhase};
|
||||
use {TouchPhase, WindowEvent};
|
||||
|
||||
use super::{WindowId, DeviceId};
|
||||
use super::{DeviceId, WindowId};
|
||||
use super::event_loop::EventsLoopSink;
|
||||
use super::window::WindowStore;
|
||||
|
||||
use wayland_client::StateToken;
|
||||
use wayland_client::protocol::wl_touch;
|
||||
|
||||
pub struct TouchIData {
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
windows_token: StateToken<WindowStore>,
|
||||
pending_ids: Vec<TouchPoint>,
|
||||
}
|
||||
use sctk::reexports::client::{NewProxy, Proxy};
|
||||
use sctk::reexports::client::protocol::wl_touch::{Event as TouchEvent, WlTouch};
|
||||
|
||||
struct TouchPoint {
|
||||
wid: WindowId,
|
||||
location: (f64, f64),
|
||||
id: i32
|
||||
id: i32,
|
||||
}
|
||||
|
||||
impl TouchIData {
|
||||
pub fn new(sink: &Arc<Mutex<EventsLoopSink>>, token: StateToken<WindowStore>)
|
||||
-> TouchIData
|
||||
{
|
||||
TouchIData {
|
||||
sink: sink.clone(),
|
||||
windows_token: token,
|
||||
pending_ids: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn touch_implementation() -> wl_touch::Implementation<TouchIData> {
|
||||
wl_touch::Implementation {
|
||||
down: |evqh, idata, _, _serial, _time, surface, touch_id, x, y| {
|
||||
let wid = evqh.state().get(&idata.windows_token).find_wid(surface);
|
||||
if let Some(wid) = wid {
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
Event::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Started,
|
||||
pub(crate) fn implement_touch(
|
||||
touch: NewProxy<WlTouch>,
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
store: Arc<Mutex<WindowStore>>,
|
||||
) -> Proxy<WlTouch> {
|
||||
let mut pending_ids = Vec::new();
|
||||
touch.implement(move |evt, _| {
|
||||
let mut sink = sink.lock().unwrap();
|
||||
let store = store.lock().unwrap();
|
||||
match evt {
|
||||
TouchEvent::Down {
|
||||
surface, id, x, y, ..
|
||||
} => {
|
||||
let wid = store.find_wid(&surface);
|
||||
if let Some(wid) = wid {
|
||||
sink.send_event(
|
||||
WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Started,
|
||||
location: (x, y),
|
||||
id: id as u64,
|
||||
}),
|
||||
wid,
|
||||
);
|
||||
pending_ids.push(TouchPoint {
|
||||
wid: wid,
|
||||
location: (x, y),
|
||||
id: touch_id as u64
|
||||
}),
|
||||
wid,
|
||||
);
|
||||
idata.pending_ids.push(TouchPoint {
|
||||
wid: wid,
|
||||
location: (x, y),
|
||||
id: touch_id
|
||||
});
|
||||
id: id,
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
up: |_, idata, _, _serial, _time, touch_id| {
|
||||
let idx = idata.pending_ids.iter().position(|p| p.id == touch_id);
|
||||
if let Some(idx) = idx {
|
||||
let pt = idata.pending_ids.remove(idx);
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
Event::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Ended,
|
||||
location: pt.location,
|
||||
id: touch_id as u64
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
TouchEvent::Up { id, .. } => {
|
||||
let idx = pending_ids.iter().position(|p| p.id == id);
|
||||
if let Some(idx) = idx {
|
||||
let pt = pending_ids.remove(idx);
|
||||
sink.send_event(
|
||||
WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Ended,
|
||||
location: pt.location,
|
||||
id: id as u64,
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
motion: |_, idata, _, _time, touch_id, x, y| {
|
||||
let pt = idata.pending_ids.iter_mut().find(|p| p.id == touch_id);
|
||||
if let Some(pt) = pt {
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
pt.location = (x, y);
|
||||
guard.send_event(
|
||||
Event::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Moved,
|
||||
location: (x, y),
|
||||
id: touch_id as u64
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
TouchEvent::Motion { id, x, y, .. } => {
|
||||
let pt = pending_ids.iter_mut().find(|p| p.id == id);
|
||||
if let Some(pt) = pt {
|
||||
pt.location = (x, y);
|
||||
sink.send_event(
|
||||
WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Moved,
|
||||
location: (x, y),
|
||||
id: id as u64,
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
frame: |_, _, _| {},
|
||||
cancel: |_, idata, _| {
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
for pt in idata.pending_ids.drain(..) {
|
||||
guard.send_event(
|
||||
Event::Touch(::Touch {
|
||||
TouchEvent::Frame => (),
|
||||
TouchEvent::Cancel => for pt in pending_ids.drain(..) {
|
||||
sink.send_event(
|
||||
WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Cancelled,
|
||||
location: pt.location,
|
||||
id: pt.id as u64
|
||||
id: pt.id as u64,
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,81 +1,127 @@
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
|
||||
use wayland_client::protocol::{wl_display,wl_surface};
|
||||
use wayland_client::{Proxy, StateToken};
|
||||
|
||||
use {CreationError, MouseCursor, CursorState, WindowAttributes};
|
||||
use {CreationError, CursorState, MouseCursor, WindowAttributes};
|
||||
use platform::MonitorId as PlatformMonitorId;
|
||||
use window::MonitorId as RootMonitorId;
|
||||
|
||||
use super::{EventsLoop, WindowId, make_wid, MonitorId};
|
||||
use super::wayland_window::{Frame, FrameImplementation, State as FrameState};
|
||||
use super::event_loop::StateContext;
|
||||
use sctk::window::{BasicFrame, Event as WEvent, Window as SWindow};
|
||||
use sctk::reexports::client::{Display, Proxy};
|
||||
use sctk::reexports::client::protocol::{wl_seat, wl_surface};
|
||||
use sctk::reexports::client::protocol::wl_compositor::RequestsTrait as CompositorRequests;
|
||||
use sctk::reexports::client::protocol::wl_surface::RequestsTrait as SurfaceRequests;
|
||||
|
||||
use super::{make_wid, EventsLoop, MonitorId, WindowId};
|
||||
|
||||
pub struct Window {
|
||||
surface: wl_surface::WlSurface,
|
||||
frame: Arc<Mutex<Frame>>,
|
||||
monitors: Arc<Mutex<MonitorList>>,
|
||||
surface: Proxy<wl_surface::WlSurface>,
|
||||
frame: Arc<Mutex<SWindow<BasicFrame>>>,
|
||||
monitors: Arc<Mutex<Vec<MonitorId>>>,
|
||||
size: Arc<Mutex<(u32, u32)>>,
|
||||
kill_switch: (Arc<Mutex<bool>>, Arc<Mutex<bool>>),
|
||||
display: Arc<wl_display::WlDisplay>,
|
||||
need_frame_refresh: Arc<Mutex<bool>>
|
||||
display: Arc<Display>,
|
||||
need_frame_refresh: Arc<Mutex<bool>>,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(evlp: &EventsLoop, attributes: &WindowAttributes) -> Result<Window, CreationError>
|
||||
{
|
||||
let (width, height) = attributes.dimensions.unwrap_or((800,600));
|
||||
|
||||
// Create the decorated surface
|
||||
pub fn new(evlp: &EventsLoop, attributes: WindowAttributes) -> Result<Window, CreationError> {
|
||||
let (width, height) = attributes.dimensions.unwrap_or((800, 600));
|
||||
// Create the window
|
||||
let size = Arc::new(Mutex::new((width, height)));
|
||||
let store_token = evlp.store.clone();
|
||||
let (surface, mut frame) = evlp.create_window(
|
||||
width, height, decorated_impl(),
|
||||
|surface| FrameIData {
|
||||
surface: surface.clone().unwrap(),
|
||||
store_token: store_token.clone()
|
||||
|
||||
// monitor tracking
|
||||
let monitor_list = Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
let surface = evlp.env.compositor.create_surface().unwrap().implement({
|
||||
let list = monitor_list.clone();
|
||||
let omgr = evlp.env.outputs.clone();
|
||||
move |event, _| match event {
|
||||
wl_surface::Event::Enter { output } => list.lock().unwrap().push(MonitorId {
|
||||
proxy: output,
|
||||
mgr: omgr.clone(),
|
||||
}),
|
||||
wl_surface::Event::Leave { output } => {
|
||||
list.lock().unwrap().retain(|m| !m.proxy.equals(&output));
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
let window_store = evlp.store.clone();
|
||||
let my_surface = surface.clone();
|
||||
let mut frame = SWindow::<BasicFrame>::init(
|
||||
surface.clone(),
|
||||
(width, height),
|
||||
&evlp.env.compositor,
|
||||
&evlp.env.subcompositor,
|
||||
&evlp.env.shm,
|
||||
&evlp.env.shell,
|
||||
move |event, ()| match event {
|
||||
WEvent::Configure { new_size, .. } => {
|
||||
let mut store = window_store.lock().unwrap();
|
||||
for window in &mut store.windows {
|
||||
if window.surface.equals(&my_surface) {
|
||||
window.newsize = new_size.map(|(w, h)| (w as i32, h as i32));
|
||||
window.need_refresh = true;
|
||||
*(window.need_frame_refresh.lock().unwrap()) = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
WEvent::Refresh => {
|
||||
let store = window_store.lock().unwrap();
|
||||
for window in &store.windows {
|
||||
if window.surface.equals(&my_surface) {
|
||||
*(window.need_frame_refresh.lock().unwrap()) = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
WEvent::Close => {
|
||||
let mut store = window_store.lock().unwrap();
|
||||
for window in &mut store.windows {
|
||||
if window.surface.equals(&my_surface) {
|
||||
window.closed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
).unwrap();
|
||||
|
||||
for &(_, ref seat) in evlp.seats.lock().unwrap().iter() {
|
||||
frame.new_seat(seat);
|
||||
}
|
||||
|
||||
// Check for fullscreen requirements
|
||||
if let Some(RootMonitorId { inner: PlatformMonitorId::Wayland(ref monitor_id) }) = attributes.fullscreen {
|
||||
let info = monitor_id.info.lock().unwrap();
|
||||
frame.set_state(FrameState::Fullscreen(Some(&info.output)));
|
||||
if let Some(RootMonitorId {
|
||||
inner: PlatformMonitorId::Wayland(ref monitor_id),
|
||||
}) = attributes.fullscreen
|
||||
{
|
||||
frame.set_fullscreen(Some(&monitor_id.proxy));
|
||||
} else if attributes.maximized {
|
||||
frame.set_state(FrameState::Maximized);
|
||||
frame.set_maximized();
|
||||
}
|
||||
|
||||
// set decorations
|
||||
frame.set_decorate(attributes.decorations);
|
||||
|
||||
// min-max dimensions
|
||||
frame.set_min_size(attributes.min_dimensions.map(|(w, h)| (w as i32, h as i32)));
|
||||
frame.set_max_size(attributes.max_dimensions.map(|(w, h)| (w as i32, h as i32)));
|
||||
|
||||
// setup the monitor tracking
|
||||
let monitor_list = Arc::new(Mutex::new(MonitorList::default()));
|
||||
{
|
||||
let mut evq = evlp.evq.borrow_mut();
|
||||
let idata = (evlp.ctxt_token.clone(), monitor_list.clone());
|
||||
evq.register(&surface, surface_impl(), idata);
|
||||
}
|
||||
frame.set_min_size(attributes.min_dimensions);
|
||||
frame.set_max_size(attributes.max_dimensions);
|
||||
|
||||
let kill_switch = Arc::new(Mutex::new(false));
|
||||
let need_frame_refresh = Arc::new(Mutex::new(true));
|
||||
let frame = Arc::new(Mutex::new(frame));
|
||||
|
||||
{
|
||||
let mut evq = evlp.evq.borrow_mut();
|
||||
evq.state().get_mut(&store_token).windows.push(InternalWindow {
|
||||
closed: false,
|
||||
newsize: None,
|
||||
need_refresh: false,
|
||||
need_frame_refresh: need_frame_refresh.clone(),
|
||||
surface: surface.clone().unwrap(),
|
||||
kill_switch: kill_switch.clone(),
|
||||
frame: Arc::downgrade(&frame)
|
||||
});
|
||||
evq.sync_roundtrip().unwrap();
|
||||
}
|
||||
evlp.store.lock().unwrap().windows.push(InternalWindow {
|
||||
closed: false,
|
||||
newsize: None,
|
||||
need_refresh: false,
|
||||
need_frame_refresh: need_frame_refresh.clone(),
|
||||
surface: surface.clone(),
|
||||
kill_switch: kill_switch.clone(),
|
||||
frame: Arc::downgrade(&frame),
|
||||
});
|
||||
evlp.evq.borrow_mut().sync_roundtrip().unwrap();
|
||||
|
||||
Ok(Window {
|
||||
display: evlp.display.clone(),
|
||||
@@ -84,7 +130,7 @@ impl Window {
|
||||
monitors: monitor_list,
|
||||
size: size,
|
||||
kill_switch: (kill_switch, evlp.cleanup_needed.clone()),
|
||||
need_frame_refresh: need_frame_refresh
|
||||
need_frame_refresh: need_frame_refresh,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -131,25 +177,25 @@ impl Window {
|
||||
#[inline]
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
let (w, h) = self.size.lock().unwrap().clone();
|
||||
let (w, h) = super::wayland_window::add_borders(w as i32, h as i32);
|
||||
// let (w, h) = super::wayland_window::add_borders(w as i32, h as i32);
|
||||
Some((w as u32, h as u32))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
// NOTE: This will only resize the borders, the contents must be updated by the user
|
||||
pub fn set_inner_size(&self, x: u32, y: u32) {
|
||||
self.frame.lock().unwrap().resize(x as i32, y as i32);
|
||||
self.frame.lock().unwrap().resize(x, y);
|
||||
*(self.size.lock().unwrap()) = (x, y);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
self.frame.lock().unwrap().set_min_size(dimensions.map(|(w, h)| (w as i32, h as i32)));
|
||||
self.frame.lock().unwrap().set_min_size(dimensions);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
self.frame.lock().unwrap().set_max_size(dimensions.map(|(w, h)| (w as i32, h as i32)));
|
||||
self.frame.lock().unwrap().set_max_size(dimensions);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -159,22 +205,22 @@ impl Window {
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
use CursorState::{Grab, Normal, Hide};
|
||||
use CursorState::{Grab, Hide, Normal};
|
||||
// TODO : not yet possible on wayland to grab cursor
|
||||
match state {
|
||||
Grab => Err("Cursor cannot be grabbed on wayland yet.".to_string()),
|
||||
Hide => Err("Cursor cannot be hidden on wayland yet.".to_string()),
|
||||
Normal => Ok(())
|
||||
Normal => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
let mut factor = 1.0;
|
||||
let mut factor: f32 = 1.0;
|
||||
let guard = self.monitors.lock().unwrap();
|
||||
for monitor_id in &guard.monitors {
|
||||
let info = monitor_id.info.lock().unwrap();
|
||||
if info.scale > factor { factor = info.scale; }
|
||||
for monitor_id in guard.iter() {
|
||||
let hidpif = monitor_id.get_hidpi_factor();
|
||||
factor = factor.max(hidpif);
|
||||
}
|
||||
factor
|
||||
}
|
||||
@@ -186,18 +232,23 @@ impl Window {
|
||||
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
if maximized {
|
||||
self.frame.lock().unwrap().set_state(FrameState::Maximized);
|
||||
self.frame.lock().unwrap().set_maximized();
|
||||
} else {
|
||||
self.frame.lock().unwrap().set_state(FrameState::Regular);
|
||||
self.frame.lock().unwrap().unset_maximized();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) {
|
||||
if let Some(RootMonitorId { inner: PlatformMonitorId::Wayland(ref monitor_id) }) = monitor {
|
||||
let info = monitor_id.info.lock().unwrap();
|
||||
self.frame.lock().unwrap().set_state(FrameState::Fullscreen(Some(&info.output)));
|
||||
if let Some(RootMonitorId {
|
||||
inner: PlatformMonitorId::Wayland(ref monitor_id),
|
||||
}) = monitor
|
||||
{
|
||||
self.frame
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_fullscreen(Some(&monitor_id.proxy));
|
||||
} else {
|
||||
self.frame.lock().unwrap().set_state(FrameState::Regular);
|
||||
self.frame.lock().unwrap().unset_fullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,11 +258,11 @@ impl Window {
|
||||
Err(())
|
||||
}
|
||||
|
||||
pub fn get_display(&self) -> &wl_display::WlDisplay {
|
||||
pub fn get_display(&self) -> &Display {
|
||||
&*self.display
|
||||
}
|
||||
|
||||
pub fn get_surface(&self) -> &wl_surface::WlSurface {
|
||||
pub fn get_surface(&self) -> &Proxy<wl_surface::WlSurface> {
|
||||
&self.surface
|
||||
}
|
||||
|
||||
@@ -219,7 +270,7 @@ impl Window {
|
||||
// we don't know how much each monitor sees us so...
|
||||
// just return the most recent one ?
|
||||
let guard = self.monitors.lock().unwrap();
|
||||
guard.monitors.last().unwrap().clone()
|
||||
guard.last().unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,25 +286,27 @@ impl Drop for Window {
|
||||
*/
|
||||
|
||||
struct InternalWindow {
|
||||
surface: wl_surface::WlSurface,
|
||||
surface: Proxy<wl_surface::WlSurface>,
|
||||
newsize: Option<(i32, i32)>,
|
||||
need_refresh: bool,
|
||||
need_frame_refresh: Arc<Mutex<bool>>,
|
||||
closed: bool,
|
||||
kill_switch: Arc<Mutex<bool>>,
|
||||
frame: Weak<Mutex<Frame>>
|
||||
frame: Weak<Mutex<SWindow<BasicFrame>>>,
|
||||
}
|
||||
|
||||
pub struct WindowStore {
|
||||
windows: Vec<InternalWindow>
|
||||
windows: Vec<InternalWindow>,
|
||||
}
|
||||
|
||||
impl WindowStore {
|
||||
pub fn new() -> WindowStore {
|
||||
WindowStore { windows: Vec::new() }
|
||||
WindowStore {
|
||||
windows: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_wid(&self, surface: &wl_surface::WlSurface) -> Option<WindowId> {
|
||||
pub fn find_wid(&self, surface: &Proxy<wl_surface::WlSurface>) -> Option<WindowId> {
|
||||
for window in &self.windows {
|
||||
if surface.equals(&window.surface) {
|
||||
return Some(make_wid(surface));
|
||||
@@ -277,8 +330,17 @@ impl WindowStore {
|
||||
pruned
|
||||
}
|
||||
|
||||
pub fn new_seat(&self, seat: &Proxy<wl_seat::WlSeat>) {
|
||||
for window in &self.windows {
|
||||
if let Some(w) = window.frame.upgrade() {
|
||||
w.lock().unwrap().new_seat(seat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_each<F>(&mut self, mut f: F)
|
||||
where F: FnMut(Option<(i32, i32)>, bool, bool, bool, WindowId, Option<&mut Frame>)
|
||||
where
|
||||
F: FnMut(Option<(i32, i32)>, bool, bool, bool, WindowId, Option<&mut SWindow<BasicFrame>>),
|
||||
{
|
||||
for window in &mut self.windows {
|
||||
let opt_arc = window.frame.upgrade();
|
||||
@@ -289,7 +351,7 @@ impl WindowStore {
|
||||
::std::mem::replace(&mut *window.need_frame_refresh.lock().unwrap(), false),
|
||||
window.closed,
|
||||
make_wid(&window.surface),
|
||||
opt_mutex_lock.as_mut().map(|m| &mut **m)
|
||||
opt_mutex_lock.as_mut().map(|m| &mut **m),
|
||||
);
|
||||
window.need_refresh = false;
|
||||
// avoid re-spamming the event
|
||||
@@ -297,68 +359,3 @@ impl WindowStore {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Protocol implementation
|
||||
*/
|
||||
|
||||
struct FrameIData {
|
||||
store_token: StateToken<WindowStore>,
|
||||
surface: wl_surface::WlSurface
|
||||
}
|
||||
|
||||
fn decorated_impl() -> FrameImplementation<FrameIData> {
|
||||
FrameImplementation {
|
||||
configure: |evqh, idata, _, newsize| {
|
||||
let store = evqh.state().get_mut(&idata.store_token);
|
||||
for window in &mut store.windows {
|
||||
if window.surface.equals(&idata.surface) {
|
||||
window.newsize = newsize;
|
||||
window.need_refresh = true;
|
||||
*(window.need_frame_refresh.lock().unwrap()) = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
close: |evqh, idata| {
|
||||
let store = evqh.state().get_mut(&idata.store_token);
|
||||
for window in &mut store.windows {
|
||||
if window.surface.equals(&idata.surface) {
|
||||
window.closed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
refresh: |evqh, idata| {
|
||||
let store = evqh.state().get_mut(&idata.store_token);
|
||||
for window in &mut store.windows {
|
||||
if window.surface.equals(&idata.surface) {
|
||||
*(window.need_frame_refresh.lock().unwrap()) = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct MonitorList {
|
||||
monitors: Vec<MonitorId>
|
||||
}
|
||||
|
||||
fn surface_impl() -> wl_surface::Implementation<(StateToken<StateContext>, Arc<Mutex<MonitorList>>)> {
|
||||
wl_surface::Implementation {
|
||||
enter: |evqh, &mut (ref token, ref list), _, output| {
|
||||
let mut guard = list.lock().unwrap();
|
||||
let ctxt = evqh.state().get(token);
|
||||
let monitor = ctxt.monitor_id_for(output);
|
||||
guard.monitors.push(monitor);
|
||||
},
|
||||
leave: |evqh, &mut (ref token, ref list), _, output| {
|
||||
let mut guard = list.lock().unwrap();
|
||||
let ctxt = evqh.state().get(token);
|
||||
let monitor = ctxt.monitor_id_for(output);
|
||||
guard.monitors.retain(|m| !Arc::ptr_eq(&m.info, &monitor.info));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,14 +2,12 @@ use std::io;
|
||||
use std::sync::Arc;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::Utf8Error;
|
||||
use std::os::raw::*;
|
||||
|
||||
use libc::{c_char, c_int, c_long, c_uchar, c_ulong};
|
||||
use percent_encoding::percent_decode;
|
||||
|
||||
use super::{ffi, util, XConnection, XError};
|
||||
|
||||
const DND_ATOMS_LEN: usize = 12;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DndAtoms {
|
||||
pub aware: ffi::Atom,
|
||||
@@ -28,36 +26,21 @@ pub struct DndAtoms {
|
||||
|
||||
impl DndAtoms {
|
||||
pub fn new(xconn: &Arc<XConnection>) -> Result<Self, XError> {
|
||||
let mut atoms = Vec::with_capacity(DND_ATOMS_LEN);
|
||||
|
||||
let mut names = [
|
||||
b"XdndAware\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndEnter\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndLeave\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndDrop\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndPosition\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndStatus\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndActionPrivate\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndSelection\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndFinished\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"XdndTypeList\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"text/uri-list\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
b"None\0".to_owned().as_mut_ptr() as *mut c_char,
|
||||
let names = [
|
||||
b"XdndAware\0".as_ptr() as *mut c_char,
|
||||
b"XdndEnter\0".as_ptr() as *mut c_char,
|
||||
b"XdndLeave\0".as_ptr() as *mut c_char,
|
||||
b"XdndDrop\0".as_ptr() as *mut c_char,
|
||||
b"XdndPosition\0".as_ptr() as *mut c_char,
|
||||
b"XdndStatus\0".as_ptr() as *mut c_char,
|
||||
b"XdndActionPrivate\0".as_ptr() as *mut c_char,
|
||||
b"XdndSelection\0".as_ptr() as *mut c_char,
|
||||
b"XdndFinished\0".as_ptr() as *mut c_char,
|
||||
b"XdndTypeList\0".as_ptr() as *mut c_char,
|
||||
b"text/uri-list\0".as_ptr() as *mut c_char,
|
||||
b"None\0".as_ptr() as *mut c_char,
|
||||
];
|
||||
|
||||
unsafe {
|
||||
(xconn.xlib.XInternAtoms)(
|
||||
xconn.display,
|
||||
names.as_mut_ptr(),
|
||||
DND_ATOMS_LEN as c_int,
|
||||
ffi::False,
|
||||
atoms.as_mut_ptr(),
|
||||
);
|
||||
}
|
||||
xconn.check_errors()?;
|
||||
unsafe {
|
||||
atoms.set_len(DND_ATOMS_LEN);
|
||||
}
|
||||
let atoms = unsafe { util::get_atoms(xconn, &names) }?;
|
||||
Ok(DndAtoms {
|
||||
aware: atoms[0],
|
||||
enter: atoms[1],
|
||||
@@ -151,7 +134,7 @@ impl Dnd {
|
||||
self.atoms.status,
|
||||
None,
|
||||
(this_window as c_long, accepted, 0, 0, action),
|
||||
)
|
||||
).flush()
|
||||
}
|
||||
|
||||
pub unsafe fn send_finished(
|
||||
@@ -171,7 +154,7 @@ impl Dnd {
|
||||
self.atoms.finished,
|
||||
None,
|
||||
(this_window as c_long, accepted, action, 0, 0),
|
||||
)
|
||||
).flush()
|
||||
}
|
||||
|
||||
pub unsafe fn get_type_list(
|
||||
|
||||
@@ -1000,6 +1000,9 @@ pub fn keysym_to_element(keysym: libc::c_uint) -> Option<VirtualKeyCode> {
|
||||
//ffi::XK_Hebrew_switch => events::VirtualKeyCode::Hebrew_switch,
|
||||
ffi::XF86XK_Back => VirtualKeyCode::NavigateBackward,
|
||||
ffi::XF86XK_Forward => VirtualKeyCode::NavigateForward,
|
||||
ffi::XF86XK_Copy => VirtualKeyCode::Copy,
|
||||
ffi::XF86XK_Paste => VirtualKeyCode::Paste,
|
||||
ffi::XF86XK_Cut => VirtualKeyCode::Cut,
|
||||
_ => return None
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,27 +1,6 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
|
||||
pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
|
||||
pub use self::window::{Window2, XWindow};
|
||||
pub use self::xdisplay::{XConnection, XNotSupported, XError};
|
||||
|
||||
pub mod ffi;
|
||||
|
||||
use platform::PlatformSpecificWindowBuilderAttributes;
|
||||
use {CreationError, Event, EventsLoopClosed, WindowEvent, DeviceEvent,
|
||||
KeyboardInput, ControlFlow};
|
||||
use events::ModifiersState;
|
||||
|
||||
use std::{mem, ptr, slice};
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
use std::sync::atomic::{self, AtomicBool};
|
||||
use std::sync::mpsc;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_char, c_int, c_long, c_uchar, c_uint, c_ulong};
|
||||
|
||||
use libc::{self, setlocale, LC_CTYPE};
|
||||
|
||||
mod events;
|
||||
mod monitor;
|
||||
mod window;
|
||||
@@ -30,16 +9,36 @@ mod dnd;
|
||||
mod ime;
|
||||
mod util;
|
||||
|
||||
pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
|
||||
pub use self::window::{Window2, XWindow};
|
||||
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::os::raw::*;
|
||||
|
||||
use libc::{self, setlocale, LC_CTYPE};
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use {
|
||||
ControlFlow,
|
||||
CreationError,
|
||||
DeviceEvent,
|
||||
Event,
|
||||
EventsLoopClosed,
|
||||
KeyboardInput,
|
||||
WindowAttributes,
|
||||
WindowEvent,
|
||||
};
|
||||
use events::ModifiersState;
|
||||
use platform::PlatformSpecificWindowBuilderAttributes;
|
||||
use self::dnd::{Dnd, DndState};
|
||||
use self::ime::{ImeReceiver, ImeSender, ImeCreationError, Ime};
|
||||
|
||||
// API TRANSITION
|
||||
//
|
||||
// We don't use the gen_api_transistion!() macro but rather do the expansion manually:
|
||||
//
|
||||
// As this module is nested into platform/linux, its code is not _exactly_ the same as
|
||||
// the one generated by the macro.
|
||||
|
||||
pub struct EventsLoop {
|
||||
display: Arc<XConnection>,
|
||||
wm_delete_window: ffi::Atom,
|
||||
@@ -48,7 +47,9 @@ pub struct EventsLoop {
|
||||
ime_sender: ImeSender,
|
||||
ime: RefCell<Ime>,
|
||||
windows: Arc<Mutex<HashMap<WindowId, WindowData>>>,
|
||||
devices: Mutex<HashMap<DeviceId, Device>>,
|
||||
// Please don't laugh at this type signature
|
||||
shared_state: RefCell<HashMap<WindowId, Weak<Mutex<window::SharedState>>>>,
|
||||
devices: RefCell<HashMap<DeviceId, Device>>,
|
||||
xi2ext: XExtension,
|
||||
pending_wakeup: Arc<AtomicBool>,
|
||||
root: ffi::Window,
|
||||
@@ -66,8 +67,8 @@ pub struct EventsLoopProxy {
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn new(display: Arc<XConnection>) -> EventsLoop {
|
||||
let wm_delete_window = unsafe { (display.xlib.XInternAtom)(display.display, b"WM_DELETE_WINDOW\0".as_ptr() as *const c_char, 0) };
|
||||
display.check_errors().expect("Failed to call XInternAtom");
|
||||
let wm_delete_window = unsafe { util::get_atom(&display, b"WM_DELETE_WINDOW\0") }
|
||||
.expect("Failed to call XInternAtom (WM_DELETE_WINDOW)");
|
||||
|
||||
let dnd = Dnd::new(Arc::clone(&display))
|
||||
.expect("Failed to call XInternAtoms when initializing drag and drop");
|
||||
@@ -105,19 +106,36 @@ 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, &mut xinput_major_ver, &mut xinput_minor_ver) != ffi::Success as libc::c_int {
|
||||
panic!("X server has XInput extension {}.{} but does not support XInput2", xinput_major_ver, xinput_minor_ver);
|
||||
if (display.xinput2.XIQueryVersion)(
|
||||
display.display,
|
||||
&mut xinput_major_ver,
|
||||
&mut xinput_minor_ver,
|
||||
) != ffi::Success as libc::c_int {
|
||||
panic!(
|
||||
"X server has XInput extension {}.{} but does not support XInput2",
|
||||
xinput_major_ver,
|
||||
xinput_minor_ver,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let root = unsafe { (display.xlib.XDefaultRootWindow)(display.display) };
|
||||
util::update_cached_wm_info(&display, 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, root, x, y, w, h,
|
||||
border_w, border_px, background_px)
|
||||
(display.xlib.XCreateSimpleWindow)(
|
||||
display.display,
|
||||
root,
|
||||
x,
|
||||
y,
|
||||
w,
|
||||
h,
|
||||
border_w,
|
||||
border_px,
|
||||
background_px,
|
||||
)
|
||||
};
|
||||
|
||||
let result = EventsLoop {
|
||||
@@ -129,27 +147,24 @@ impl EventsLoop {
|
||||
ime_sender,
|
||||
ime,
|
||||
windows: Arc::new(Mutex::new(HashMap::new())),
|
||||
devices: Mutex::new(HashMap::new()),
|
||||
shared_state: RefCell::new(HashMap::new()),
|
||||
devices: RefCell::new(HashMap::new()),
|
||||
xi2ext,
|
||||
root,
|
||||
wakeup_dummy_window,
|
||||
};
|
||||
|
||||
{
|
||||
// Register for device hotplug events
|
||||
let mask = ffi::XI_HierarchyChangedMask;
|
||||
unsafe {
|
||||
let mut event_mask = ffi::XIEventMask{
|
||||
deviceid: ffi::XIAllDevices,
|
||||
mask: &mask as *const _ as *mut c_uchar,
|
||||
mask_len: mem::size_of_val(&mask) as c_int,
|
||||
};
|
||||
(result.display.xinput2.XISelectEvents)(result.display.display, root,
|
||||
&mut event_mask as *mut ffi::XIEventMask, 1);
|
||||
}
|
||||
// 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
|
||||
|
||||
result.init_device(ffi::XIAllDevices);
|
||||
}
|
||||
result.init_device(ffi::XIAllDevices);
|
||||
|
||||
result
|
||||
}
|
||||
@@ -190,8 +205,6 @@ impl EventsLoop {
|
||||
pub fn run_forever<F>(&mut self, mut callback: F)
|
||||
where F: FnMut(Event) -> ControlFlow
|
||||
{
|
||||
self.pending_wakeup.store(false, atomic::Ordering::Relaxed);
|
||||
|
||||
let mut xev = unsafe { mem::uninitialized() };
|
||||
|
||||
loop {
|
||||
@@ -232,7 +245,8 @@ impl EventsLoop {
|
||||
return;
|
||||
}
|
||||
|
||||
match xev.get_type() {
|
||||
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");
|
||||
@@ -377,47 +391,129 @@ impl EventsLoop {
|
||||
ffi::ConfigureNotify => {
|
||||
let xev: &ffi::XConfigureEvent = xev.as_ref();
|
||||
|
||||
// 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 window = xev.window;
|
||||
let window_id = mkwid(window);
|
||||
|
||||
let new_size = (xev.width, xev.height);
|
||||
let new_position = (xev.x, xev.y);
|
||||
// Gymnastics to ensure self.windows isn't locked when we invoke callback
|
||||
|
||||
let (resized, moved) = {
|
||||
let mut windows = self.windows.lock().unwrap();
|
||||
let mut windows = self.windows.lock();
|
||||
if let Some(window_data) = windows.get_mut(&WindowId(window)) {
|
||||
if window_data.config.is_none() {
|
||||
window_data.config = Some(WindowConfig::new(xev));
|
||||
(true, true)
|
||||
} else {
|
||||
let window_state = window_data.config.as_mut().unwrap();
|
||||
(if window_state.size != new_size {
|
||||
window_state.size = new_size;
|
||||
true
|
||||
} else { false },
|
||||
if window_state.position != new_position {
|
||||
window_state.position = new_position;
|
||||
true
|
||||
} else { false })
|
||||
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;
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
(resized, moved)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if resized {
|
||||
let (width, height) = (xev.width as u32, xev.height as u32);
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Resized(xev.width as u32, xev.height as u32),
|
||||
event: WindowEvent::Resized(width, height),
|
||||
});
|
||||
}
|
||||
|
||||
if moved {
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::Moved(xev.x as i32, xev.y as i32),
|
||||
// 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),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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.shared_state
|
||||
.borrow()
|
||||
.get(&WindowId(window))
|
||||
.map(|window_state| {
|
||||
if let Some(window_state) = window_state.upgrade() {
|
||||
(*window_state.lock()).frame_extents.take();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ffi::DestroyNotify => {
|
||||
let xev: &ffi::XDestroyWindowEvent = xev.as_ref();
|
||||
|
||||
@@ -426,7 +522,7 @@ impl EventsLoop {
|
||||
|
||||
// In the event that the window's been destroyed without being dropped first, we
|
||||
// cleanup again here.
|
||||
self.windows.lock().unwrap().remove(&WindowId(window));
|
||||
self.windows.lock().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.
|
||||
@@ -462,42 +558,47 @@ impl EventsLoop {
|
||||
let window = xkev.window;
|
||||
let window_id = mkwid(window);
|
||||
|
||||
let modifiers = ModifiersState {
|
||||
alt: xkev.state & ffi::Mod1Mask != 0,
|
||||
shift: xkev.state & ffi::ShiftMask != 0,
|
||||
ctrl: xkev.state & ffi::ControlMask != 0,
|
||||
logo: xkev.state & ffi::Mod4Mask != 0,
|
||||
};
|
||||
|
||||
let keysym = unsafe {
|
||||
let mut keysym = 0;
|
||||
(self.display.xlib.XLookupString)(
|
||||
xkev,
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
&mut keysym,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
keysym
|
||||
};
|
||||
|
||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||
// Standard virtual core keyboard ID. XInput2 needs to be used to get a reliable
|
||||
// value, though this should only be an issue under multiseat configurations.
|
||||
let device = 3;
|
||||
let device_id = mkdid(device);
|
||||
|
||||
// When a compose sequence or IME pre-edit is finished, it ends in a KeyPress with
|
||||
// a keycode of 0.
|
||||
if xkev.keycode != 0 {
|
||||
callback(Event::WindowEvent { window_id, event: WindowEvent::KeyboardInput {
|
||||
// Standard virtual core keyboard ID. XInput2 needs to be used to get a
|
||||
// reliable value, though this should only be an issue under multiseat
|
||||
// configurations.
|
||||
device_id: mkdid(3),
|
||||
input: KeyboardInput {
|
||||
state,
|
||||
scancode: xkev.keycode - 8,
|
||||
virtual_keycode,
|
||||
modifiers,
|
||||
},
|
||||
}});
|
||||
let modifiers = ModifiersState {
|
||||
alt: xkev.state & ffi::Mod1Mask != 0,
|
||||
shift: xkev.state & ffi::ShiftMask != 0,
|
||||
ctrl: xkev.state & ffi::ControlMask != 0,
|
||||
logo: xkev.state & ffi::Mod4Mask != 0,
|
||||
};
|
||||
|
||||
let keysym = unsafe {
|
||||
let mut keysym = 0;
|
||||
(self.display.xlib.XLookupString)(
|
||||
xkev,
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
&mut keysym,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
self.display.check_errors().expect("Failed to lookup keysym");
|
||||
keysym
|
||||
};
|
||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
event: WindowEvent::KeyboardInput {
|
||||
device_id,
|
||||
input: KeyboardInput {
|
||||
state,
|
||||
scancode: xkev.keycode - 8,
|
||||
virtual_keycode,
|
||||
modifiers,
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if state == Pressed {
|
||||
@@ -536,7 +637,7 @@ 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().unwrap();
|
||||
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.
|
||||
@@ -625,7 +726,7 @@ impl EventsLoop {
|
||||
|
||||
// Gymnastics to ensure self.windows isn't locked when we invoke callback
|
||||
if {
|
||||
let mut windows = self.windows.lock().unwrap();
|
||||
let mut windows = self.windows.lock();
|
||||
let window_data = {
|
||||
if let Some(window_data) = windows.get_mut(&WindowId(xev.event)) {
|
||||
window_data
|
||||
@@ -652,8 +753,11 @@ impl EventsLoop {
|
||||
let mut events = Vec::new();
|
||||
{
|
||||
let mask = unsafe { slice::from_raw_parts(xev.valuators.mask, xev.valuators.mask_len as usize) };
|
||||
let mut devices = self.devices.lock().unwrap();
|
||||
let physical_device = devices.get_mut(&DeviceId(xev.sourceid)).unwrap();
|
||||
let mut devices = self.devices.borrow_mut();
|
||||
let physical_device = match devices.get_mut(&DeviceId(xev.sourceid)) {
|
||||
Some(device) => device,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let mut value = xev.valuators.values;
|
||||
for i in 0..xev.valuators.mask_len*8 {
|
||||
@@ -700,11 +804,16 @@ impl EventsLoop {
|
||||
let window_id = mkwid(xev.event);
|
||||
let device_id = mkdid(xev.deviceid);
|
||||
|
||||
let mut devices = self.devices.lock().unwrap();
|
||||
let physical_device = devices.get_mut(&DeviceId(xev.sourceid)).unwrap();
|
||||
for info in DeviceInfo::get(&self.display, ffi::XIAllDevices).iter() {
|
||||
if info.deviceid == xev.sourceid {
|
||||
physical_device.reset_scroll_position(info);
|
||||
let mut devices = self.devices.borrow_mut();
|
||||
let physical_device = match devices.get_mut(&DeviceId(xev.sourceid)) {
|
||||
Some(device) => device,
|
||||
None => return,
|
||||
};
|
||||
if let Some(all_info) = DeviceInfo::get(&self.display, ffi::XIAllDevices) {
|
||||
for device_info in all_info.iter() {
|
||||
if device_info.deviceid == xev.sourceid {
|
||||
physical_device.reset_scroll_position(device_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
callback(Event::WindowEvent {
|
||||
@@ -713,15 +822,17 @@ impl EventsLoop {
|
||||
});
|
||||
|
||||
let new_cursor_pos = (xev.event_x, xev.event_y);
|
||||
// The mods field on this event isn't actually useful, so we have to
|
||||
// query the pointer device.
|
||||
|
||||
// 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();
|
||||
)
|
||||
}.expect("Failed to query pointer device").get_modifier_state();
|
||||
|
||||
callback(Event::WindowEvent { window_id, event: CursorMoved {
|
||||
device_id,
|
||||
@@ -736,7 +847,6 @@ impl EventsLoop {
|
||||
// been destroyed, which the user presumably doesn't want to deal with.
|
||||
let window_closed = self.windows
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get(&WindowId(xev.event))
|
||||
.is_none();
|
||||
|
||||
@@ -752,7 +862,7 @@ impl EventsLoop {
|
||||
|
||||
let window_id = mkwid(xev.event);
|
||||
|
||||
if let None = self.windows.lock().unwrap().get(&WindowId(xev.event)) {
|
||||
if let None = self.windows.lock().get(&WindowId(xev.event)) {
|
||||
return;
|
||||
}
|
||||
self.ime
|
||||
@@ -764,11 +874,11 @@ impl EventsLoop {
|
||||
|
||||
// The deviceid for this event is for a keyboard instead of a pointer,
|
||||
// so we have to do a little extra work.
|
||||
let device_info = DeviceInfo::get(&self.display, xev.deviceid);
|
||||
// For master devices, the attachment field contains the ID of the
|
||||
// paired master device; for the master keyboard, the attachment is
|
||||
// the master pointer, and vice versa.
|
||||
let pointer_id = unsafe { (*device_info.info) }.attachment;
|
||||
let pointer_id = self.devices
|
||||
.borrow()
|
||||
.get(&DeviceId(xev.deviceid))
|
||||
.map(|device| device.attachment)
|
||||
.unwrap_or(2);
|
||||
|
||||
callback(Event::WindowEvent {
|
||||
window_id,
|
||||
@@ -782,7 +892,7 @@ impl EventsLoop {
|
||||
ffi::XI_FocusOut => {
|
||||
let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
|
||||
|
||||
if let None = self.windows.lock().unwrap().get(&WindowId(xev.event)) {
|
||||
if let None = self.windows.lock().get(&WindowId(xev.event)) {
|
||||
return;
|
||||
}
|
||||
self.ime
|
||||
@@ -870,19 +980,44 @@ impl EventsLoop {
|
||||
}
|
||||
|
||||
ffi::XI_RawKeyPress | ffi::XI_RawKeyRelease => {
|
||||
// TODO: Use xkbcommon for keysym and text decoding
|
||||
let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
|
||||
let xkeysym = unsafe { (self.display.xlib.XKeycodeToKeysym)(self.display.display, xev.detail as ffi::KeyCode, 0) };
|
||||
callback(Event::DeviceEvent { device_id: mkdid(xev.deviceid), event: DeviceEvent::Key(KeyboardInput {
|
||||
scancode: (xev.detail - 8) as u32,
|
||||
virtual_keycode: events::keysym_to_element(xkeysym as libc::c_uint),
|
||||
state: match xev.evtype {
|
||||
ffi::XI_RawKeyPress => Pressed,
|
||||
ffi::XI_RawKeyRelease => Released,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
modifiers: ModifiersState::default(),
|
||||
})});
|
||||
|
||||
let state = match xev.evtype {
|
||||
ffi::XI_RawKeyPress => Pressed,
|
||||
ffi::XI_RawKeyRelease => Released,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let device_id = xev.sourceid;
|
||||
let keycode = xev.detail;
|
||||
if keycode < 8 { return; }
|
||||
let scancode = (keycode - 8) as u32;
|
||||
|
||||
let keysym = unsafe {
|
||||
(self.display.xlib.XKeycodeToKeysym)(
|
||||
self.display.display,
|
||||
xev.detail as ffi::KeyCode,
|
||||
0,
|
||||
)
|
||||
};
|
||||
self.display.check_errors().expect("Failed to lookup raw keysym");
|
||||
|
||||
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
|
||||
|
||||
callback(Event::DeviceEvent {
|
||||
device_id: mkdid(device_id),
|
||||
event: DeviceEvent::Key(KeyboardInput {
|
||||
scancode,
|
||||
virtual_keycode,
|
||||
state,
|
||||
// So, in an ideal world we can use libxkbcommon to get modifiers.
|
||||
// However, libxkbcommon-x11 isn't as commonly installed as one
|
||||
// would hope. We can still use the Xkb extension to get
|
||||
// comprehensive keyboard state updates, but interpreting that
|
||||
// info manually is going to be involved.
|
||||
modifiers: ModifiersState::default(),
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
ffi::XI_HierarchyChanged => {
|
||||
@@ -893,7 +1028,7 @@ impl EventsLoop {
|
||||
callback(Event::DeviceEvent { device_id: mkdid(info.deviceid), event: DeviceEvent::Added });
|
||||
} else if 0 != info.flags & (ffi::XISlaveRemoved | ffi::XIMasterRemoved) {
|
||||
callback(Event::DeviceEvent { device_id: mkdid(info.deviceid), event: DeviceEvent::Removed });
|
||||
let mut devices = self.devices.lock().unwrap();
|
||||
let mut devices = self.devices.borrow_mut();
|
||||
devices.remove(&DeviceId(info.deviceid));
|
||||
}
|
||||
}
|
||||
@@ -901,23 +1036,24 @@ impl EventsLoop {
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
match self.ime_receiver.try_recv() {
|
||||
Ok((window_id, x, y)) => {
|
||||
self.ime.borrow_mut().send_xim_spot(window_id, x, y);
|
||||
}
|
||||
Err(_) => ()
|
||||
},
|
||||
Err(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn init_device(&self, device: c_int) {
|
||||
let mut devices = self.devices.lock().unwrap();
|
||||
for info in DeviceInfo::get(&self.display, device).iter() {
|
||||
devices.insert(DeviceId(info.deviceid), Device::new(&self, info));
|
||||
let mut devices = self.devices.borrow_mut();
|
||||
if let Some(info) = DeviceInfo::get(&self.display, device) {
|
||||
for info in info.iter() {
|
||||
devices.insert(DeviceId(info.deviceid), Device::new(&self, info));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -935,28 +1071,19 @@ impl EventsLoopProxy {
|
||||
|
||||
// Push an event on the X event queue so that methods run_forever will advance.
|
||||
//
|
||||
// NOTE: This code (and the following `XSendEvent` code) is taken from the old
|
||||
// `WindowProxy::wakeup` implementation. The code assumes that X11 is thread safe. Is this
|
||||
// true?
|
||||
let mut xev = ffi::XClientMessageEvent {
|
||||
type_: ffi::ClientMessage,
|
||||
window: self.wakeup_dummy_window,
|
||||
format: 32,
|
||||
message_type: 0,
|
||||
serial: 0,
|
||||
send_event: 0,
|
||||
display: display.display,
|
||||
data: unsafe { mem::zeroed() },
|
||||
};
|
||||
|
||||
// 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 {
|
||||
let propagate = false as i32;
|
||||
let event_mask = 0;
|
||||
let xevent = &mut xev as *mut ffi::XClientMessageEvent as *mut ffi::XEvent;
|
||||
(display.xlib.XSendEvent)(display.display, self.wakeup_dummy_window, propagate, event_mask, xevent);
|
||||
(display.xlib.XFlush)(display.display);
|
||||
display.check_errors().expect("Failed to call XSendEvent after wakeup");
|
||||
}
|
||||
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");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -969,21 +1096,30 @@ struct DeviceInfo<'a> {
|
||||
}
|
||||
|
||||
impl<'a> DeviceInfo<'a> {
|
||||
fn get(display: &'a XConnection, device: c_int) -> Self {
|
||||
fn get(display: &'a XConnection, device: c_int) -> Option<Self> {
|
||||
unsafe {
|
||||
let mut count = mem::uninitialized();
|
||||
let info = (display.xinput2.XIQueryDevice)(display.display, device, &mut count);
|
||||
DeviceInfo {
|
||||
display: display,
|
||||
info: info,
|
||||
count: count as usize,
|
||||
}
|
||||
display.check_errors()
|
||||
.ok()
|
||||
.and_then(|_| {
|
||||
if info.is_null() || count == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(DeviceInfo {
|
||||
display,
|
||||
info,
|
||||
count: count as usize,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for DeviceInfo<'a> {
|
||||
fn drop(&mut self) {
|
||||
assert!(!self.info.is_null());
|
||||
unsafe { (self.display.xinput2.XIFreeDeviceInfo)(self.info as *mut _) };
|
||||
}
|
||||
}
|
||||
@@ -1019,19 +1155,24 @@ impl ::std::ops::Deref for Window {
|
||||
impl Window {
|
||||
pub fn new(
|
||||
x_events_loop: &EventsLoop,
|
||||
window: &::WindowAttributes,
|
||||
pl_attribs: &PlatformSpecificWindowBuilderAttributes
|
||||
attribs: WindowAttributes,
|
||||
pl_attribs: PlatformSpecificWindowBuilderAttributes
|
||||
) -> Result<Self, CreationError> {
|
||||
let win = Arc::new(try!(Window2::new(&x_events_loop, window, pl_attribs)));
|
||||
let multitouch = attribs.multitouch;
|
||||
let win = Arc::new(Window2::new(&x_events_loop, attribs, pl_attribs)?);
|
||||
|
||||
x_events_loop.shared_state
|
||||
.borrow_mut()
|
||||
.insert(win.id(), Arc::downgrade(&win.shared_state));
|
||||
|
||||
x_events_loop.ime
|
||||
.borrow_mut()
|
||||
.create_context(win.id().0)
|
||||
.expect("Failed to create input context");
|
||||
|
||||
x_events_loop.windows.lock().unwrap().insert(win.id(), WindowData {
|
||||
config: None,
|
||||
multitouch: window.multitouch,
|
||||
x_events_loop.windows.lock().insert(win.id(), WindowData {
|
||||
config: Default::default(),
|
||||
multitouch,
|
||||
cursor_pos: None,
|
||||
});
|
||||
|
||||
@@ -1052,7 +1193,6 @@ impl Window {
|
||||
pub fn send_xim_spot(&self, x: i16, y: i16) {
|
||||
let _ = self.ime_sender
|
||||
.lock()
|
||||
.unwrap()
|
||||
.send((self.window.id().0, x, y));
|
||||
}
|
||||
}
|
||||
@@ -1060,10 +1200,9 @@ 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().unwrap().remove(&self.window.id()) {
|
||||
if let Some(_) = windows.lock().remove(&self.window.id()) {
|
||||
unsafe {
|
||||
(display.xlib.XDestroyWindow)(display.display, self.window.id().0);
|
||||
display.check_errors().expect("Failed to destroy window");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1071,8 +1210,9 @@ impl Drop for Window {
|
||||
}
|
||||
|
||||
/// State maintained for translating window-related events
|
||||
#[derive(Debug)]
|
||||
struct WindowData {
|
||||
config: Option<WindowConfig>,
|
||||
config: WindowConfig,
|
||||
multitouch: bool,
|
||||
cursor_pos: Option<(f64, f64)>,
|
||||
}
|
||||
@@ -1080,21 +1220,13 @@ struct WindowData {
|
||||
// Required by ffi members
|
||||
unsafe impl Send for WindowData {}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct WindowConfig {
|
||||
size: (c_int, c_int),
|
||||
position: (c_int, c_int),
|
||||
pub size: Option<(c_int, c_int)>,
|
||||
pub position: Option<(c_int, c_int)>,
|
||||
pub inner_position: Option<(c_int, c_int)>,
|
||||
}
|
||||
|
||||
impl WindowConfig {
|
||||
fn new(event: &ffi::XConfigureEvent) -> Self {
|
||||
WindowConfig {
|
||||
size: (event.width, event.height),
|
||||
position: (event.x, event.y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 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> {
|
||||
@@ -1138,6 +1270,9 @@ fn mkdid(w: c_int) -> ::DeviceId { ::DeviceId(::platform::DeviceId::X(DeviceId(w
|
||||
struct Device {
|
||||
name: String,
|
||||
scroll_axes: Vec<(i32, ScrollAxis)>,
|
||||
// For master devices, this is the paired device (pointer <-> keyboard).
|
||||
// For slave devices, this is the master.
|
||||
attachment: c_int,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
@@ -1154,24 +1289,25 @@ enum ScrollOrientation {
|
||||
}
|
||||
|
||||
impl Device {
|
||||
fn new(el: &EventsLoop, info: &ffi::XIDeviceInfo) -> Self
|
||||
{
|
||||
fn new(el: &EventsLoop, info: &ffi::XIDeviceInfo) -> Self {
|
||||
let name = unsafe { CStr::from_ptr(info.name).to_string_lossy() };
|
||||
let mut scroll_axes = Vec::new();
|
||||
|
||||
if Device::physical_device(info) {
|
||||
// Register for global raw events
|
||||
let mask = ffi::XI_RawMotionMask
|
||||
| ffi::XI_RawButtonPressMask | ffi::XI_RawButtonReleaseMask
|
||||
| ffi::XI_RawKeyPressMask | ffi::XI_RawKeyReleaseMask;
|
||||
| ffi::XI_RawButtonPressMask
|
||||
| ffi::XI_RawButtonReleaseMask
|
||||
| ffi::XI_RawKeyPressMask
|
||||
| ffi::XI_RawKeyReleaseMask;
|
||||
unsafe {
|
||||
let mut event_mask = ffi::XIEventMask{
|
||||
deviceid: info.deviceid,
|
||||
mask: &mask as *const _ as *mut c_uchar,
|
||||
mask_len: mem::size_of_val(&mask) as c_int,
|
||||
};
|
||||
(el.display.xinput2.XISelectEvents)(el.display.display, el.root, &mut event_mask as *mut ffi::XIEventMask, 1);
|
||||
}
|
||||
util::select_xinput_events(
|
||||
&el.display,
|
||||
el.root,
|
||||
info.deviceid,
|
||||
mask,
|
||||
)
|
||||
}.queue(); // The request buffer is flushed when we poll for events
|
||||
|
||||
// Identify scroll axes
|
||||
for class_ptr in Device::classes(info) {
|
||||
@@ -1197,6 +1333,7 @@ impl Device {
|
||||
let mut device = Device {
|
||||
name: name.into_owned(),
|
||||
scroll_axes: scroll_axes,
|
||||
attachment: info.attachment,
|
||||
};
|
||||
device.reset_scroll_position(info);
|
||||
device
|
||||
|
||||
@@ -1,365 +0,0 @@
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::{c_char, c_double, c_int, c_long, c_short, c_uchar, c_uint, c_ulong};
|
||||
|
||||
use super::{ffi, XConnection, XError};
|
||||
use events::ModifiersState;
|
||||
|
||||
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 _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn get_atom(xconn: &Arc<XConnection>, name: &[u8]) -> Result<ffi::Atom, XError> {
|
||||
let atom_name: *const c_char = name.as_ptr() as _;
|
||||
let atom = (xconn.xlib.XInternAtom)(xconn.display, atom_name, ffi::False);
|
||||
xconn.check_errors().map(|_| atom)
|
||||
}
|
||||
|
||||
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),
|
||||
) -> Result<(), XError> {
|
||||
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 = 32;
|
||||
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);
|
||||
|
||||
let event_mask = event_mask.unwrap_or(ffi::NoEventMask);
|
||||
|
||||
(xconn.xlib.XSendEvent)(
|
||||
xconn.display,
|
||||
target_window,
|
||||
ffi::False,
|
||||
event_mask,
|
||||
&mut event.into(),
|
||||
);
|
||||
|
||||
xconn.check_errors().map(|_| ())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum GetPropertyError {
|
||||
XError(XError),
|
||||
TypeMismatch(ffi::Atom),
|
||||
FormatMismatch(c_int),
|
||||
NothingAllocated,
|
||||
}
|
||||
|
||||
impl GetPropertyError {
|
||||
pub fn is_actual_property_type(&self, t: ffi::Atom) -> bool {
|
||||
if let GetPropertyError::TypeMismatch(actual_type) = *self {
|
||||
actual_type == t
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn get_property<T>(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: c_ulong,
|
||||
property: ffi::Atom,
|
||||
property_type: ffi::Atom,
|
||||
) -> Result<Vec<T>, GetPropertyError> {
|
||||
let mut data = Vec::new();
|
||||
|
||||
let mut done = false;
|
||||
while !done {
|
||||
let mut actual_type: ffi::Atom = mem::uninitialized();
|
||||
let mut actual_format: c_int = mem::uninitialized();
|
||||
let mut byte_count: 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,
|
||||
(data.len() / 4) as c_long,
|
||||
1024,
|
||||
ffi::False,
|
||||
property_type,
|
||||
&mut actual_type,
|
||||
&mut actual_format,
|
||||
&mut byte_count,
|
||||
&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));
|
||||
}
|
||||
|
||||
// Fun fact: actual_format ISN'T the size of the type; it's more like a really bad enum
|
||||
let format_mismatch = match actual_format as usize {
|
||||
8 => mem::size_of::<T>() != mem::size_of::<c_char>(),
|
||||
16 => mem::size_of::<T>() != mem::size_of::<c_short>(),
|
||||
32 => mem::size_of::<T>() != mem::size_of::<c_long>(),
|
||||
_ => true, // this won't actually be reached; the XError condition above is triggered
|
||||
};
|
||||
|
||||
if format_mismatch {
|
||||
return Err(GetPropertyError::FormatMismatch(actual_format));
|
||||
}
|
||||
|
||||
if !buf.is_null() {
|
||||
let mut buf =
|
||||
Vec::from_raw_parts(buf as *mut T, byte_count as usize, byte_count as usize);
|
||||
data.append(&mut buf);
|
||||
} else {
|
||||
return Err(GetPropertyError::NothingAllocated);
|
||||
}
|
||||
|
||||
done = bytes_after == 0;
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
impl From<ffi::XIModifierState> for ModifiersState {
|
||||
fn from(mods: ffi::XIModifierState) -> Self {
|
||||
let state = mods.effective as c_uint;
|
||||
ModifiersState {
|
||||
alt: state & ffi::Mod1Mask != 0,
|
||||
shift: state & ffi::ShiftMask != 0,
|
||||
ctrl: state & ffi::ControlMask != 0,
|
||||
logo: state & ffi::Mod4Mask != 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PointerState {
|
||||
#[allow(dead_code)]
|
||||
root: ffi::Window,
|
||||
#[allow(dead_code)]
|
||||
child: ffi::Window,
|
||||
#[allow(dead_code)]
|
||||
root_x: c_double,
|
||||
#[allow(dead_code)]
|
||||
root_y: c_double,
|
||||
#[allow(dead_code)]
|
||||
win_x: c_double,
|
||||
#[allow(dead_code)]
|
||||
win_y: c_double,
|
||||
#[allow(dead_code)]
|
||||
buttons: ffi::XIButtonState,
|
||||
modifiers: ffi::XIModifierState,
|
||||
#[allow(dead_code)]
|
||||
group: ffi::XIGroupState,
|
||||
#[allow(dead_code)]
|
||||
relative_to_window: bool,
|
||||
}
|
||||
|
||||
impl PointerState {
|
||||
pub fn get_modifier_state(&self) -> ModifiersState {
|
||||
self.modifiers.into()
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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;
|
||||
|
||||
xconn.check_errors()?;
|
||||
|
||||
Ok(PointerState {
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
pub unsafe fn lookup_utf8(
|
||||
xconn: &Arc<XConnection>,
|
||||
ic: ffi::XIC,
|
||||
key_event: &mut ffi::XKeyEvent,
|
||||
) -> String {
|
||||
const INIT_BUFF_SIZE: usize = 16;
|
||||
|
||||
// Buffer allocated on heap instead of stack, due to the possible reallocation
|
||||
let mut buffer: Vec<u8> = vec![mem::uninitialized(); INIT_BUFF_SIZE];
|
||||
let (_, status, mut count) = lookup_utf8_inner(
|
||||
xconn,
|
||||
ic,
|
||||
key_event,
|
||||
&mut buffer,
|
||||
);
|
||||
|
||||
// Buffer overflowed, dynamically reallocate
|
||||
if status == ffi::XBufferOverflow {
|
||||
buffer = vec![mem::uninitialized(); count as usize];
|
||||
let (_, _, new_count) = lookup_utf8_inner(
|
||||
xconn,
|
||||
ic,
|
||||
key_event,
|
||||
&mut buffer,
|
||||
);
|
||||
count = new_count;
|
||||
}
|
||||
|
||||
str::from_utf8(&buffer[..count as usize]).unwrap_or("").to_string()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FrameExtents {
|
||||
pub left: c_ulong,
|
||||
pub right: c_ulong,
|
||||
pub top: c_ulong,
|
||||
pub bottom: c_ulong,
|
||||
}
|
||||
|
||||
impl FrameExtents {
|
||||
pub fn new(left: c_ulong, right: c_ulong, top: c_ulong, bottom: c_ulong) -> Self {
|
||||
FrameExtents { left, right, top, bottom }
|
||||
}
|
||||
|
||||
pub fn from_border(border: c_ulong) -> Self {
|
||||
Self::new(border, border, border, border)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WindowGeometry {
|
||||
pub x: c_int,
|
||||
pub y: c_int,
|
||||
pub width: c_uint,
|
||||
pub height: c_uint,
|
||||
pub frame: FrameExtents,
|
||||
}
|
||||
|
||||
impl WindowGeometry {
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
(self.x as _, self.y as _)
|
||||
}
|
||||
|
||||
pub fn get_inner_position(&self) -> (i32, i32) {
|
||||
(
|
||||
self.x.saturating_add(self.frame.left as c_int) as _,
|
||||
self.y.saturating_add(self.frame.top as c_int) as _,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_inner_size(&self) -> (u32, u32) {
|
||||
(self.width as _, self.height as _)
|
||||
}
|
||||
|
||||
pub fn get_outer_size(&self) -> (u32, u32) {
|
||||
(
|
||||
self.width.saturating_add(
|
||||
self.frame.left.saturating_add(self.frame.right) as c_uint
|
||||
) as _,
|
||||
self.height.saturating_add(
|
||||
self.frame.top.saturating_add(self.frame.bottom) as c_uint
|
||||
) as _,
|
||||
)
|
||||
}
|
||||
}
|
||||
59
src/platform/linux/x11/util/atom.rs
Normal file
59
src/platform/linux/x11/util/atom.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::*;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use super::*;
|
||||
|
||||
type AtomCache = HashMap<CString, ffi::Atom>;
|
||||
|
||||
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,
|
||||
ffi::False,
|
||||
);
|
||||
/*println!(
|
||||
"XInternAtom name:{:?} atom:{:?}",
|
||||
name,
|
||||
atom,
|
||||
);*/
|
||||
xconn.check_errors()?;
|
||||
(*atom_cache_lock).insert(name.to_owned(), atom);
|
||||
Ok(atom)
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
350
src/platform/linux/x11/util/geometry.rs
Normal file
350
src/platform/linux/x11/util/geometry.rs
Normal file
@@ -0,0 +1,350 @@
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TranslatedCoords {
|
||||
pub x_rel_root: c_int,
|
||||
pub y_rel_root: c_int,
|
||||
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,
|
||||
// If you want positions relative to the root window, use translate_coords.
|
||||
// Note that the overwhelming majority of window managers are reparenting WMs, thus the window
|
||||
// ID we get from window creation is for a nested window used as the window's client area. If
|
||||
// you call get_geometry with that window ID, then you'll get the position of that client area
|
||||
// window relative to the parent it's nested in (the frame), which isn't helpful if you want
|
||||
// to know the frame position.
|
||||
pub x_rel_parent: c_int,
|
||||
pub y_rel_parent: c_int,
|
||||
// In that same case, this will give you client area size.
|
||||
pub width: c_uint,
|
||||
pub height: c_uint,
|
||||
// xmonad and dwm were the only WMs tested that use the border return at all.
|
||||
// The majority of WMs seem to simply fill it with 0 unconditionally.
|
||||
pub border: c_uint,
|
||||
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,
|
||||
pub right: c_ulong,
|
||||
pub top: c_ulong,
|
||||
pub bottom: c_ulong,
|
||||
}
|
||||
|
||||
impl FrameExtents {
|
||||
pub fn new(left: c_ulong, right: c_ulong, top: c_ulong, bottom: c_ulong) -> Self {
|
||||
FrameExtents { left, right, top, bottom }
|
||||
}
|
||||
|
||||
pub fn from_border(border: c_ulong) -> Self {
|
||||
Self::new(border, border, border, border)
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
UnsupportedNested,
|
||||
UnsupportedBordered,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FrameExtentsHeuristic {
|
||||
pub frame_extents: FrameExtents,
|
||||
pub heuristic_path: FrameExtentsHeuristicPath,
|
||||
}
|
||||
|
||||
impl FrameExtentsHeuristic {
|
||||
pub fn inner_pos_to_outer(&self, x: i32, y: i32) -> (i32, i32) {
|
||||
use self::FrameExtentsHeuristicPath::*;
|
||||
if self.heuristic_path != UnsupportedBordered {
|
||||
(x - self.frame_extents.left as i32, y - self.frame_extents.top as i32)
|
||||
} else {
|
||||
(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inner_size_to_outer(&self, width: u32, height: u32) -> (u32, u32) {
|
||||
(
|
||||
width.saturating_add(
|
||||
self.frame_extents.left.saturating_add(self.frame_extents.right) as u32
|
||||
),
|
||||
height.saturating_add(
|
||||
self.frame_extents.top.saturating_add(self.frame_extents.bottom) as u32
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_frame_extents_heuristic(
|
||||
xconn: &Arc<XConnection>,
|
||||
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 = unsafe { translate_coords(xconn, window, root) }
|
||||
.expect("Failed to translate window coordinates");
|
||||
(
|
||||
coords.y_rel_root,
|
||||
coords.child,
|
||||
)
|
||||
};
|
||||
|
||||
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,
|
||||
)
|
||||
};
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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 = 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");
|
||||
(
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/platform/linux/x11/util/hint.rs
Normal file
20
src/platform/linux/x11/util/hint.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use super::*;
|
||||
|
||||
pub const MWM_HINTS_DECORATIONS: c_ulong = 2;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum StateOperation {
|
||||
Remove = 0, // _NET_WM_STATE_REMOVE
|
||||
Add = 1, // _NET_WM_STATE_ADD
|
||||
_Toggle = 2, // _NET_WM_STATE_TOGGLE
|
||||
}
|
||||
|
||||
impl From<bool> for StateOperation {
|
||||
fn from(b: bool) -> Self {
|
||||
if b {
|
||||
StateOperation::Add
|
||||
} else {
|
||||
StateOperation::Remove
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/platform/linux/x11/util/icon.rs
Normal file
34
src/platform/linux/x11/util/icon.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use {Icon, Pixel, PIXEL_SIZE};
|
||||
use super::*;
|
||||
|
||||
impl Pixel {
|
||||
pub fn to_packed_argb(&self) -> Cardinal {
|
||||
let mut cardinal = 0;
|
||||
assert!(CARDINAL_SIZE >= PIXEL_SIZE);
|
||||
let as_bytes = &mut cardinal as *mut _ as *mut u8;
|
||||
unsafe {
|
||||
*as_bytes.offset(0) = self.b;
|
||||
*as_bytes.offset(1) = self.g;
|
||||
*as_bytes.offset(2) = self.r;
|
||||
*as_bytes.offset(3) = self.a;
|
||||
}
|
||||
cardinal
|
||||
}
|
||||
}
|
||||
|
||||
impl Icon {
|
||||
pub fn to_cardinals(&self) -> Vec<Cardinal> {
|
||||
assert_eq!(self.rgba.len() % PIXEL_SIZE, 0);
|
||||
let pixel_count = self.rgba.len() / PIXEL_SIZE;
|
||||
assert_eq!(pixel_count, (self.width * self.height) as usize);
|
||||
let mut data = Vec::with_capacity(pixel_count);
|
||||
data.push(self.width as Cardinal);
|
||||
data.push(self.height as Cardinal);
|
||||
let pixels = self.rgba.as_ptr() as *const Pixel;
|
||||
for pixel_index in 0..pixel_count {
|
||||
let pixel = unsafe { &*pixels.offset(pixel_index as isize) };
|
||||
data.push(pixel.to_packed_argb());
|
||||
}
|
||||
data
|
||||
}
|
||||
}
|
||||
187
src/platform/linux/x11/util/input.rs
Normal file
187
src/platform/linux/x11/util/input.rs
Normal file
@@ -0,0 +1,187 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ffi::XIModifierState> for ModifiersState {
|
||||
fn from(mods: ffi::XIModifierState) -> Self {
|
||||
let state = mods.effective as c_uint;
|
||||
ModifiersState {
|
||||
alt: state & ffi::Mod1Mask != 0,
|
||||
shift: state & ffi::ShiftMask != 0,
|
||||
ctrl: state & ffi::ControlMask != 0,
|
||||
logo: state & ffi::Mod4Mask != 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
modifiers: ffi::XIModifierState,
|
||||
_group: ffi::XIGroupState,
|
||||
_relative_to_window: bool,
|
||||
}
|
||||
|
||||
impl<'a> PointerState<'a> {
|
||||
pub fn get_modifier_state(&self) -> ModifiersState {
|
||||
self.modifiers.into()
|
||||
}
|
||||
}
|
||||
|
||||
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 _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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;
|
||||
|
||||
xconn.check_errors()?;
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
179
src/platform/linux/x11/util/mod.rs
Normal file
179
src/platform/linux/x11/util/mod.rs
Normal file
@@ -0,0 +1,179 @@
|
||||
// Welcome to the util module, where we try to keep you from shooting yourself in the foot.
|
||||
// *results may vary
|
||||
|
||||
mod atom;
|
||||
mod geometry;
|
||||
mod hint;
|
||||
mod icon;
|
||||
mod input;
|
||||
mod window_property;
|
||||
mod wm;
|
||||
|
||||
pub use self::atom::*;
|
||||
pub use self::geometry::*;
|
||||
pub use self::hint::*;
|
||||
pub use self::icon::*;
|
||||
pub use self::input::*;
|
||||
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 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>,
|
||||
}
|
||||
|
||||
impl<'a> Flusher<'a> {
|
||||
pub fn new(xconn: &'a Arc<XConnection>) -> Self {
|
||||
Flusher { xconn }
|
||||
}
|
||||
|
||||
// "I want this request sent now!"
|
||||
pub fn flush(self) -> Result<(), XError> {
|
||||
unsafe { flush_requests(self.xconn) }
|
||||
}
|
||||
|
||||
// "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);
|
||||
|
||||
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)
|
||||
}
|
||||
163
src/platform/linux/x11/util/window_property.rs
Normal file
163
src/platform/linux/x11/util/window_property.rs
Normal file
@@ -0,0 +1,163 @@
|
||||
use std;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub type Cardinal = c_long;
|
||||
pub const CARDINAL_SIZE: usize = mem::size_of::<c_long>();
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum GetPropertyError {
|
||||
XError(XError),
|
||||
TypeMismatch(ffi::Atom),
|
||||
FormatMismatch(c_int),
|
||||
NothingAllocated,
|
||||
}
|
||||
|
||||
impl GetPropertyError {
|
||||
pub fn is_actual_property_type(&self, t: ffi::Atom) -> bool {
|
||||
if let GetPropertyError::TypeMismatch(actual_type) = *self {
|
||||
actual_type == t
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
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,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InvalidFormat {
|
||||
format_used: Format,
|
||||
size_passed: usize,
|
||||
size_expected: usize,
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
(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)
|
||||
}
|
||||
158
src/platform/linux/x11/util/wm.rs
Normal file
158
src/platform/linux/x11/util/wm.rs
Normal file
@@ -0,0 +1,158 @@
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use super::*;
|
||||
|
||||
// This info is global to the window manager.
|
||||
lazy_static! {
|
||||
static ref SUPPORTED_HINTS: Mutex<Vec<ffi::Atom>> = Mutex::new(Vec::with_capacity(0));
|
||||
static ref WM_NAME: Mutex<Option<String>> = Mutex::new(None);
|
||||
}
|
||||
|
||||
pub fn hint_is_supported(hint: ffi::Atom) -> bool {
|
||||
(*SUPPORTED_HINTS.lock()).contains(&hint)
|
||||
}
|
||||
|
||||
pub fn wm_name_is_one_of(names: &[&str]) -> bool {
|
||||
if let Some(ref name) = *WM_NAME.lock() {
|
||||
names.contains(&name.as_str())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
root,
|
||||
supported_atom,
|
||||
ffi::XA_ATOM,
|
||||
)
|
||||
}.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)");
|
||||
|
||||
// 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;
|
||||
}*/
|
||||
|
||||
// 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,
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
// 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,
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
// 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(xconn, b"UTF8_STRING\0") }
|
||||
.expect("Failed to call XInternAtom (UTF8_STRING)");
|
||||
|
||||
let result = unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
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
|
||||
};
|
||||
|
||||
if no_utf8 {
|
||||
unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
root_window_wm_check,
|
||||
wm_name_atom,
|
||||
ffi::XA_STRING,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}.ok();
|
||||
|
||||
wm_name.and_then(|wm_name| String::from_utf8(wm_name).ok())
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
||||
use std::ptr;
|
||||
use std::fmt;
|
||||
use std::error::Error;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use libc;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use super::ffi;
|
||||
|
||||
@@ -29,12 +29,12 @@ pub type XErrorHandler = Option<unsafe extern fn(*mut ffi::Display, *mut ffi::XE
|
||||
impl XConnection {
|
||||
pub fn new(error_handler: XErrorHandler) -> Result<XConnection, XNotSupported> {
|
||||
// opening the libraries
|
||||
let xlib = try!(ffi::Xlib::open());
|
||||
let xcursor = try!(ffi::Xcursor::open());
|
||||
let xrandr = try!(ffi::Xrandr_2_2_0::open());
|
||||
let xlib = ffi::Xlib::open()?;
|
||||
let xcursor = ffi::Xcursor::open()?;
|
||||
let xrandr = ffi::Xrandr_2_2_0::open()?;
|
||||
let xrandr_1_5 = ffi::Xrandr::open().ok();
|
||||
let xinput2 = try!(ffi::XInput2::open());
|
||||
let xlib_xcb = try!(ffi::Xlib_xcb::open());
|
||||
let xinput2 = ffi::XInput2::open()?;
|
||||
let xlib_xcb = ffi::Xlib_xcb::open()?;
|
||||
|
||||
unsafe { (xlib.XInitThreads)() };
|
||||
unsafe { (xlib.XSetErrorHandler)(error_handler) };
|
||||
@@ -49,13 +49,13 @@ impl XConnection {
|
||||
};
|
||||
|
||||
Ok(XConnection {
|
||||
xlib: xlib,
|
||||
xrandr: xrandr,
|
||||
xrandr_1_5: xrandr_1_5,
|
||||
xcursor: xcursor,
|
||||
xinput2: xinput2,
|
||||
xlib_xcb: xlib_xcb,
|
||||
display: display,
|
||||
xlib,
|
||||
xrandr,
|
||||
xrandr_1_5,
|
||||
xcursor,
|
||||
xinput2,
|
||||
xlib_xcb,
|
||||
display,
|
||||
latest_error: Mutex::new(None),
|
||||
})
|
||||
}
|
||||
@@ -63,8 +63,7 @@ impl XConnection {
|
||||
/// Checks whether an error has been triggered by the previous function calls.
|
||||
#[inline]
|
||||
pub fn check_errors(&self) -> Result<(), XError> {
|
||||
let error = self.latest_error.lock().unwrap().take();
|
||||
|
||||
let error = self.latest_error.lock().take();
|
||||
if let Some(error) = error {
|
||||
Err(error)
|
||||
} else {
|
||||
@@ -75,7 +74,13 @@ impl XConnection {
|
||||
/// Ignores any previous error.
|
||||
#[inline]
|
||||
pub fn ignore_error(&self) {
|
||||
*self.latest_error.lock().unwrap() = None;
|
||||
*self.latest_error.lock() = None;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for XConnection {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.display.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ impl Shared {
|
||||
if let Ok(mut windows) = self.windows.lock() {
|
||||
windows.retain(|w| match w.upgrade() {
|
||||
Some(w) => w.id() != id,
|
||||
None => true,
|
||||
None => false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ impl ::std::ops::Deref for Window {
|
||||
impl Window {
|
||||
|
||||
pub fn new(events_loop: &EventsLoop,
|
||||
attributes: &::WindowAttributes,
|
||||
pl_attribs: &PlatformSpecificWindowBuilderAttributes) -> Result<Self, CreationError>
|
||||
attributes: ::WindowAttributes,
|
||||
pl_attribs: PlatformSpecificWindowBuilderAttributes) -> Result<Self, CreationError>
|
||||
{
|
||||
let weak_shared = Arc::downgrade(&events_loop.shared);
|
||||
let window = Arc::new(try!(Window2::new(weak_shared, attributes, pl_attribs)));
|
||||
|
||||
@@ -14,7 +14,7 @@ use cocoa;
|
||||
use cocoa::appkit::{self, NSApplication, NSColor, NSScreen, NSView, NSWindow, NSWindowButton,
|
||||
NSWindowStyleMask};
|
||||
use cocoa::base::{id, nil};
|
||||
use cocoa::foundation::{NSDictionary, NSPoint, NSRect, NSSize, NSString};
|
||||
use cocoa::foundation::{NSDictionary, NSPoint, NSRect, NSSize, NSString, NSAutoreleasePool};
|
||||
|
||||
use core_graphics::display::CGDisplay;
|
||||
|
||||
@@ -22,7 +22,7 @@ use std;
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::c_void;
|
||||
use std::sync::Weak;
|
||||
use std::cell::{Cell,RefCell};
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
use super::events_loop::{EventsLoop, Shared};
|
||||
|
||||
@@ -43,6 +43,9 @@ struct DelegateState {
|
||||
// This is set when WindowBuilder::with_fullscreen was set,
|
||||
// see comments of `window_did_fail_to_enter_fullscreen`
|
||||
handle_with_fullscreen: bool,
|
||||
|
||||
// During windowDidResize, we use this to only send Moved if the position changed.
|
||||
previous_position: Option<(i32, i32)>,
|
||||
}
|
||||
|
||||
impl DelegateState {
|
||||
@@ -161,6 +164,17 @@ impl WindowDelegate {
|
||||
emit_event(state, WindowEvent::Resized(width, height));
|
||||
}
|
||||
|
||||
unsafe fn emit_move_event(state: &mut DelegateState) {
|
||||
let frame_rect = NSWindow::frame(*state.window);
|
||||
let x = frame_rect.origin.x as _;
|
||||
let y = Window2::bottom_left_to_top_left(frame_rect);
|
||||
let moved = state.previous_position != Some((x, y));
|
||||
if moved {
|
||||
state.previous_position = Some((x, y));
|
||||
emit_event(state, WindowEvent::Moved(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
extern fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
@@ -190,6 +204,16 @@ impl WindowDelegate {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
emit_resize_event(state);
|
||||
emit_move_event(state);
|
||||
}
|
||||
}
|
||||
|
||||
// This won't be triggered if the move was part of a resize.
|
||||
extern fn window_did_move(this: &Object, _: Sel, _: id) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("winitState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
emit_move_event(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,6 +401,8 @@ impl WindowDelegate {
|
||||
window_will_close as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(windowDidResize:),
|
||||
window_did_resize as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(windowDidMove:),
|
||||
window_did_move as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(windowDidChangeScreen:),
|
||||
window_did_change_screen as extern fn(&Object, Sel, id));
|
||||
decl.add_method(sel!(windowDidChangeBackingProperties:),
|
||||
@@ -426,9 +452,15 @@ impl WindowDelegate {
|
||||
unsafe {
|
||||
let delegate = IdRef::new(msg_send![WindowDelegate::class(), new]);
|
||||
|
||||
// setDelegate uses autorelease on objects,
|
||||
// so need autorelease
|
||||
let autoreleasepool = NSAutoreleasePool::new(nil);
|
||||
|
||||
(&mut **delegate).set_ivar("winitState", state_ptr as *mut ::std::os::raw::c_void);
|
||||
let _: () = msg_send![*state.window, setDelegate:*delegate];
|
||||
|
||||
let _: () = msg_send![autoreleasepool, drain];
|
||||
|
||||
WindowDelegate { state: state, _this: delegate }
|
||||
}
|
||||
}
|
||||
@@ -438,7 +470,11 @@ impl Drop for WindowDelegate {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// Nil the window's delegate so it doesn't still reference us
|
||||
// NOTE: setDelegate:nil at first retains the previous value,
|
||||
// and then autoreleases it, so autorelease pool is needed
|
||||
let autoreleasepool = NSAutoreleasePool::new(nil);
|
||||
let _: () = msg_send![*self.state.window, setDelegate:nil];
|
||||
let _: () = msg_send![autoreleasepool, drain];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -479,6 +515,23 @@ unsafe fn get_current_monitor() -> RootMonitorId {
|
||||
|
||||
impl Drop for Window2 {
|
||||
fn drop(&mut self) {
|
||||
// Remove this window from the `EventLoop`s list of windows.
|
||||
// The destructor order is:
|
||||
// Window ->
|
||||
// Rc<Window2> (makes Weak<..> in shared.windows None) ->
|
||||
// Window2
|
||||
// needed to remove the element from array
|
||||
let id = self.id();
|
||||
if let Some(shared) = self.delegate.state.shared.upgrade() {
|
||||
shared.find_and_remove_window(id);
|
||||
}
|
||||
|
||||
// nswindow::close uses autorelease
|
||||
// so autorelease pool
|
||||
let autoreleasepool = unsafe {
|
||||
NSAutoreleasePool::new(nil)
|
||||
};
|
||||
|
||||
// Close the window if it has not yet been closed.
|
||||
let nswindow = *self.window;
|
||||
if nswindow != nil {
|
||||
@@ -486,6 +539,8 @@ impl Drop for Window2 {
|
||||
let () = msg_send![nswindow, close];
|
||||
}
|
||||
}
|
||||
|
||||
let _: () = unsafe { msg_send![autoreleasepool, drain] };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,28 +559,40 @@ impl WindowExt for Window2 {
|
||||
impl Window2 {
|
||||
pub fn new(
|
||||
shared: Weak<Shared>,
|
||||
win_attribs: &WindowAttributes,
|
||||
pl_attribs: &PlatformSpecificWindowBuilderAttributes,
|
||||
win_attribs: WindowAttributes,
|
||||
pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
||||
) -> Result<Window2, CreationError> {
|
||||
unsafe {
|
||||
if !msg_send![cocoa::base::class("NSThread"), isMainThread] {
|
||||
panic!("Windows can only be created on the main thread on macOS");
|
||||
}
|
||||
}
|
||||
let autoreleasepool = unsafe {
|
||||
NSAutoreleasePool::new(nil)
|
||||
};
|
||||
|
||||
let app = match Window2::create_app(pl_attribs.activation_policy) {
|
||||
Some(app) => app,
|
||||
None => { return Err(OsError(format!("Couldn't create NSApplication"))); },
|
||||
None => {
|
||||
let _: () = unsafe { msg_send![autoreleasepool, drain] };
|
||||
return Err(OsError(format!("Couldn't create NSApplication")));
|
||||
},
|
||||
};
|
||||
|
||||
let window = match Window2::create_window(win_attribs, pl_attribs)
|
||||
let window = match Window2::create_window(&win_attribs, &pl_attribs)
|
||||
{
|
||||
Some(window) => window,
|
||||
None => { return Err(OsError(format!("Couldn't create NSWindow"))); },
|
||||
None => {
|
||||
let _: () = unsafe { msg_send![autoreleasepool, drain] };
|
||||
return Err(OsError(format!("Couldn't create NSWindow")));
|
||||
},
|
||||
};
|
||||
let view = match Window2::create_view(*window) {
|
||||
Some(view) => view,
|
||||
None => { return Err(OsError(format!("Couldn't create NSView"))); },
|
||||
None => {
|
||||
let _: () = unsafe { msg_send![autoreleasepool, drain] };
|
||||
return Err(OsError(format!("Couldn't create NSView")));
|
||||
},
|
||||
};
|
||||
|
||||
unsafe {
|
||||
@@ -553,11 +620,12 @@ impl Window2 {
|
||||
let ds = DelegateState {
|
||||
view: view.clone(),
|
||||
window: window.clone(),
|
||||
shared,
|
||||
win_attribs: RefCell::new(win_attribs.clone()),
|
||||
standard_frame: Cell::new(None),
|
||||
save_style_mask: Cell::new(None),
|
||||
handle_with_fullscreen: win_attribs.fullscreen.is_some(),
|
||||
shared: shared,
|
||||
previous_position: None,
|
||||
};
|
||||
ds.win_attribs.borrow_mut().fullscreen = None;
|
||||
|
||||
@@ -591,6 +659,8 @@ impl Window2 {
|
||||
window.delegate.state.perform_maximized(win_attribs.maximized);
|
||||
}
|
||||
|
||||
let _: () = unsafe { msg_send![autoreleasepool, drain] };
|
||||
|
||||
Ok(window)
|
||||
}
|
||||
|
||||
@@ -611,11 +681,29 @@ impl Window2 {
|
||||
}
|
||||
}
|
||||
|
||||
fn class() -> *const Class {
|
||||
static mut WINDOW2_CLASS: *const Class = 0 as *const Class;
|
||||
static INIT: std::sync::Once = std::sync::ONCE_INIT;
|
||||
|
||||
INIT.call_once(|| unsafe {
|
||||
let window_superclass = Class::get("NSWindow").unwrap();
|
||||
let mut decl = ClassDecl::new("WinitWindow", window_superclass).unwrap();
|
||||
decl.add_method(sel!(canBecomeMainWindow), yes as extern fn(&Object, Sel) -> BOOL);
|
||||
decl.add_method(sel!(canBecomeKeyWindow), yes as extern fn(&Object, Sel) -> BOOL);
|
||||
WINDOW2_CLASS = decl.register();
|
||||
});
|
||||
|
||||
unsafe {
|
||||
WINDOW2_CLASS
|
||||
}
|
||||
}
|
||||
|
||||
fn create_window(
|
||||
attrs: &WindowAttributes,
|
||||
pl_attrs: &PlatformSpecificWindowBuilderAttributes)
|
||||
-> Option<IdRef> {
|
||||
pl_attrs: &PlatformSpecificWindowBuilderAttributes
|
||||
) -> Option<IdRef> {
|
||||
unsafe {
|
||||
let autoreleasepool = NSAutoreleasePool::new(nil);
|
||||
let screen = match attrs.fullscreen {
|
||||
Some(ref monitor_id) => {
|
||||
let monitor_screen = monitor_id.inner.get_nsscreen();
|
||||
@@ -631,43 +719,27 @@ impl Window2 {
|
||||
}
|
||||
};
|
||||
|
||||
let masks = if pl_attrs.titlebar_hidden {
|
||||
let mut masks = if !attrs.decorations && !screen.is_some() {
|
||||
// unresizable Window2 without a titlebar or borders
|
||||
// if decorations is set to false, ignore pl_attrs
|
||||
NSWindowStyleMask::NSBorderlessWindowMask
|
||||
} else if pl_attrs.titlebar_hidden {
|
||||
// if the titlebar is hidden, ignore other pl_attrs
|
||||
NSWindowStyleMask::NSBorderlessWindowMask |
|
||||
NSWindowStyleMask::NSResizableWindowMask
|
||||
} else if pl_attrs.titlebar_transparent {
|
||||
// Window2 with a transparent titlebar and regular content view
|
||||
} else {
|
||||
// default case, resizable window with titlebar and titlebar buttons
|
||||
NSWindowStyleMask::NSClosableWindowMask |
|
||||
NSWindowStyleMask::NSMiniaturizableWindowMask |
|
||||
NSWindowStyleMask::NSResizableWindowMask |
|
||||
NSWindowStyleMask::NSTitledWindowMask
|
||||
} else if pl_attrs.fullsize_content_view {
|
||||
// Window2 with a transparent titlebar and fullsize content view
|
||||
NSWindowStyleMask::NSClosableWindowMask |
|
||||
NSWindowStyleMask::NSMiniaturizableWindowMask |
|
||||
NSWindowStyleMask::NSResizableWindowMask |
|
||||
NSWindowStyleMask::NSTitledWindowMask |
|
||||
NSWindowStyleMask::NSFullSizeContentViewWindowMask
|
||||
} else {
|
||||
if !attrs.decorations && !screen.is_some() {
|
||||
// Window2 without a titlebar
|
||||
NSWindowStyleMask::NSBorderlessWindowMask
|
||||
} else {
|
||||
// Window2 with a titlebar
|
||||
NSWindowStyleMask::NSClosableWindowMask |
|
||||
NSWindowStyleMask::NSMiniaturizableWindowMask |
|
||||
NSWindowStyleMask::NSResizableWindowMask |
|
||||
NSWindowStyleMask::NSTitledWindowMask
|
||||
}
|
||||
};
|
||||
|
||||
let winit_window = Class::get("WinitWindow").unwrap_or_else(|| {
|
||||
let window_superclass = Class::get("NSWindow").unwrap();
|
||||
let mut decl = ClassDecl::new("WinitWindow", window_superclass).unwrap();
|
||||
decl.add_method(sel!(canBecomeMainWindow), yes as extern fn(&Object, Sel) -> BOOL);
|
||||
decl.add_method(sel!(canBecomeKeyWindow), yes as extern fn(&Object, Sel) -> BOOL);
|
||||
decl.register();
|
||||
Class::get("WinitWindow").unwrap()
|
||||
});
|
||||
if pl_attrs.fullsize_content_view {
|
||||
masks = masks | NSWindowStyleMask::NSFullSizeContentViewWindowMask;
|
||||
}
|
||||
|
||||
let winit_window = Window2::class();
|
||||
|
||||
let window: id = msg_send![winit_window, alloc];
|
||||
|
||||
@@ -677,7 +749,7 @@ impl Window2 {
|
||||
appkit::NSBackingStoreBuffered,
|
||||
NO,
|
||||
));
|
||||
window.non_nil().map(|window| {
|
||||
let res = window.non_nil().map(|window| {
|
||||
let title = IdRef::new(NSString::alloc(nil).init_str(&attrs.title));
|
||||
window.setReleasedWhenClosed_(NO);
|
||||
window.setTitle_(*title);
|
||||
@@ -710,7 +782,9 @@ impl Window2 {
|
||||
|
||||
window.center();
|
||||
window
|
||||
})
|
||||
});
|
||||
let _: () = msg_send![autoreleasepool, drain];
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
@@ -998,6 +1072,16 @@ impl Window2 {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, _icon: Option<::Icon>) {
|
||||
// macOS doesn't have window icons. Though, there is `setRepresentedFilename`, but that's
|
||||
// semantically distinct and should only be used when the window is in some representing a
|
||||
// specific file/directory. For instance, Terminal.app uses this for the CWD. Anyway, that
|
||||
// should eventually be implemented as `WindowBuilderExt::with_represented_file` or
|
||||
// something, and doesn't have anything to do with this.
|
||||
// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/WinPanel/Tasks/SettingWindowTitle.html
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||
unsafe {
|
||||
@@ -1008,7 +1092,7 @@ impl Window2 {
|
||||
|
||||
// Convert the `cocoa::base::id` associated with a window to a usize to use as a unique identifier
|
||||
// for the window.
|
||||
pub fn get_window_id(window_cocoa_id: cocoa::base::id) -> Id {
|
||||
pub fn get_window_id(window_cocoa_id: id) -> Id {
|
||||
Id(window_cocoa_id as *const objc::runtime::Object as usize)
|
||||
}
|
||||
|
||||
@@ -1084,7 +1168,12 @@ impl IdRef {
|
||||
impl Drop for IdRef {
|
||||
fn drop(&mut self) {
|
||||
if self.0 != nil {
|
||||
let _: () = unsafe { msg_send![self.0, release] };
|
||||
unsafe {
|
||||
let autoreleasepool =
|
||||
NSAutoreleasePool::new(nil);
|
||||
let _ : () = msg_send![self.0, release];
|
||||
let _ : () = msg_send![autoreleasepool, release];
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
use std::char;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use events::VirtualKeyCode;
|
||||
use events::ModifiersState;
|
||||
|
||||
use winapi::shared::minwindef::{WPARAM, LPARAM};
|
||||
use winapi::shared::minwindef::{WPARAM, LPARAM, UINT};
|
||||
use winapi::um::winuser;
|
||||
|
||||
use ScanCode;
|
||||
use std::char;
|
||||
|
||||
const MAPVK_VK_TO_CHAR: u32 = 2;
|
||||
const MAPVK_VSC_TO_VK_EX: u32 = 3;
|
||||
|
||||
pub fn get_key_mods() -> ModifiersState {
|
||||
let mut mods = ModifiersState::default();
|
||||
@@ -29,18 +28,9 @@ pub fn get_key_mods() -> ModifiersState {
|
||||
mods
|
||||
}
|
||||
|
||||
pub fn vkeycode_to_element(wparam: WPARAM, lparam: LPARAM) -> (ScanCode, Option<VirtualKeyCode>) {
|
||||
let scancode = ((lparam >> 16) & 0xff) as u32;
|
||||
let extended = (lparam & 0x01000000) != 0;
|
||||
let vk = match wparam as i32 {
|
||||
winuser::VK_SHIFT => unsafe { winuser::MapVirtualKeyA(scancode, MAPVK_VSC_TO_VK_EX) as i32 },
|
||||
winuser::VK_CONTROL => if extended { winuser::VK_RCONTROL } else { winuser::VK_LCONTROL },
|
||||
winuser::VK_MENU => if extended { winuser::VK_RMENU } else { winuser::VK_LMENU },
|
||||
other => other
|
||||
};
|
||||
|
||||
pub fn vkey_to_winit_vkey(vkey: c_int) -> Option<VirtualKeyCode> {
|
||||
// VK_* codes are documented here https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
|
||||
(scancode, match vk {
|
||||
match vkey {
|
||||
//winuser::VK_LBUTTON => Some(VirtualKeyCode::Lbutton),
|
||||
//winuser::VK_RBUTTON => Some(VirtualKeyCode::Rbutton),
|
||||
//winuser::VK_CANCEL => Some(VirtualKeyCode::Cancel),
|
||||
@@ -191,13 +181,13 @@ pub fn vkeycode_to_element(wparam: WPARAM, lparam: LPARAM) -> (ScanCode, Option<
|
||||
winuser::VK_OEM_COMMA => Some(VirtualKeyCode::Comma),
|
||||
winuser::VK_OEM_MINUS => Some(VirtualKeyCode::Minus),
|
||||
winuser::VK_OEM_PERIOD => Some(VirtualKeyCode::Period),
|
||||
winuser::VK_OEM_1 => map_text_keys(vk),
|
||||
winuser::VK_OEM_2 => map_text_keys(vk),
|
||||
winuser::VK_OEM_3 => map_text_keys(vk),
|
||||
winuser::VK_OEM_4 => map_text_keys(vk),
|
||||
winuser::VK_OEM_5 => map_text_keys(vk),
|
||||
winuser::VK_OEM_6 => map_text_keys(vk),
|
||||
winuser::VK_OEM_7 => map_text_keys(vk),
|
||||
winuser::VK_OEM_1 => map_text_keys(vkey),
|
||||
winuser::VK_OEM_2 => map_text_keys(vkey),
|
||||
winuser::VK_OEM_3 => map_text_keys(vkey),
|
||||
winuser::VK_OEM_4 => map_text_keys(vkey),
|
||||
winuser::VK_OEM_5 => map_text_keys(vkey),
|
||||
winuser::VK_OEM_6 => map_text_keys(vkey),
|
||||
winuser::VK_OEM_7 => map_text_keys(vkey),
|
||||
/*winuser::VK_OEM_8 => Some(VirtualKeyCode::Oem_8), */
|
||||
winuser::VK_OEM_102 => Some(VirtualKeyCode::OEM102),
|
||||
/*winuser::VK_PROCESSKEY => Some(VirtualKeyCode::Processkey),
|
||||
@@ -212,13 +202,40 @@ pub fn vkeycode_to_element(wparam: WPARAM, lparam: LPARAM) -> (ScanCode, Option<
|
||||
winuser::VK_PA1 => Some(VirtualKeyCode::Pa1),
|
||||
winuser::VK_OEM_CLEAR => Some(VirtualKeyCode::Oem_clear),*/
|
||||
_ => None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vkey_left_right(vkey: c_int, scancode: UINT, extended: bool) -> c_int {
|
||||
match vkey {
|
||||
winuser::VK_SHIFT => unsafe { winuser::MapVirtualKeyA(
|
||||
scancode,
|
||||
winuser::MAPVK_VSC_TO_VK_EX,
|
||||
) as _ },
|
||||
winuser::VK_CONTROL => if extended {
|
||||
winuser::VK_RCONTROL
|
||||
} else {
|
||||
winuser::VK_LCONTROL
|
||||
},
|
||||
winuser::VK_MENU => if extended {
|
||||
winuser::VK_RMENU
|
||||
} else {
|
||||
winuser::VK_LMENU
|
||||
},
|
||||
_ => vkey,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vkeycode_to_element(wparam: WPARAM, lparam: LPARAM) -> (ScanCode, Option<VirtualKeyCode>) {
|
||||
let scancode = ((lparam >> 16) & 0xff) as UINT;
|
||||
let extended = (lparam & 0x01000000) != 0;
|
||||
let vkey = vkey_left_right(wparam as _, scancode, extended);
|
||||
(scancode, vkey_to_winit_vkey(vkey))
|
||||
}
|
||||
|
||||
// This is needed as windows doesn't properly distinguish
|
||||
// some virtual key codes for different keyboard layouts
|
||||
fn map_text_keys(win_virtual_key: i32) -> Option<VirtualKeyCode> {
|
||||
let char_key = unsafe { winuser::MapVirtualKeyA(win_virtual_key as u32, MAPVK_VK_TO_CHAR) } & 0x7FFF;
|
||||
let char_key = unsafe { winuser::MapVirtualKeyA(win_virtual_key as u32, winuser::MAPVK_VK_TO_CHAR) } & 0x7FFF;
|
||||
match char::from_u32(char_key) {
|
||||
Some(';') => Some(VirtualKeyCode::Semicolon),
|
||||
Some('/') => Some(VirtualKeyCode::Slash),
|
||||
|
||||
@@ -30,13 +30,13 @@ use winapi::shared::minwindef::{LOWORD, HIWORD, DWORD, WPARAM, LPARAM, INT, UINT
|
||||
use winapi::shared::windef::{HWND, POINT, RECT};
|
||||
use winapi::shared::windowsx;
|
||||
use winapi::um::{winuser, shellapi, processthreadsapi};
|
||||
use winapi::um::winnt::LONG;
|
||||
use winapi::um::winnt::{LONG, SHORT};
|
||||
|
||||
use platform::platform::event;
|
||||
use events::DeviceEvent;
|
||||
use platform::platform::{event, Cursor, WindowId, DEVICE_ID, wrap_device_id, util};
|
||||
use platform::platform::event::{vkey_to_winit_vkey, vkey_left_right};
|
||||
use platform::platform::raw_input::*;
|
||||
use platform::platform::window::adjust_size;
|
||||
use platform::platform::Cursor;
|
||||
use platform::platform::WindowId;
|
||||
use platform::platform::DEVICE_ID;
|
||||
|
||||
use ControlFlow;
|
||||
use CursorState;
|
||||
@@ -279,8 +279,9 @@ impl EventsLoopProxy {
|
||||
|
||||
/// Executes a function in the background thread.
|
||||
///
|
||||
/// Note that we use a FnMut instead of a FnOnce because we're too lazy to create an equivalent
|
||||
/// to the unstable FnBox.
|
||||
/// Note that we use FnMut instead of FnOnce because boxing FnOnce won't work on stable Rust
|
||||
/// until 2030 when the design of Box is finally complete.
|
||||
/// https://github.com/rust-lang/rust/issues/28796
|
||||
///
|
||||
/// The `Inserted` can be used to inject a `WindowState` for the callback to use. The state is
|
||||
/// removed automatically if the callback receives a `WM_CLOSE` message for the window.
|
||||
@@ -302,10 +303,7 @@ impl EventsLoopProxy {
|
||||
);
|
||||
// PostThreadMessage can only fail if the thread ID is invalid (which shouldn't happen
|
||||
// as the events loop is still alive) or if the queue is full.
|
||||
assert!(
|
||||
res != 0,
|
||||
"PostThreadMessage failed ; is the messages queue full?"
|
||||
);
|
||||
assert!(res != 0, "PostThreadMessage failed; is the messages queue full?");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -423,6 +421,22 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||
},
|
||||
|
||||
// WM_MOVE supplies client area positions, so we send Moved here instead.
|
||||
winuser::WM_WINDOWPOSCHANGED => {
|
||||
use events::WindowEvent::Moved;
|
||||
|
||||
let windowpos = lparam as *const winuser::WINDOWPOS;
|
||||
if (*windowpos).flags & winuser::SWP_NOMOVE != winuser::SWP_NOMOVE {
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: Moved((*windowpos).x, (*windowpos).y),
|
||||
});
|
||||
}
|
||||
|
||||
// This is necessary for us to still get sent WM_SIZE.
|
||||
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||
},
|
||||
|
||||
winuser::WM_SIZE => {
|
||||
use events::WindowEvent::Resized;
|
||||
let w = LOWORD(lparam as DWORD) as u32;
|
||||
@@ -461,17 +475,6 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_MOVE => {
|
||||
use events::WindowEvent::Moved;
|
||||
let x = LOWORD(lparam as DWORD) as i32;
|
||||
let y = HIWORD(lparam as DWORD) as i32;
|
||||
send_event(Event::WindowEvent {
|
||||
window_id: SuperWindowId(WindowId(window)),
|
||||
event: Moved(x, y),
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_CHAR => {
|
||||
use std::mem;
|
||||
use events::WindowEvent::ReceivedCharacter;
|
||||
@@ -562,7 +565,6 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
},
|
||||
|
||||
winuser::WM_MOUSEWHEEL => {
|
||||
use events::{DeviceEvent, WindowEvent};
|
||||
use events::MouseScrollDelta::LineDelta;
|
||||
use events::TouchPhase;
|
||||
|
||||
@@ -575,11 +577,6 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
event: WindowEvent::MouseWheel { device_id: DEVICE_ID, delta: LineDelta(0.0, value), phase: TouchPhase::Moved, modifiers: event::get_key_mods() },
|
||||
});
|
||||
|
||||
send_event(Event::DeviceEvent {
|
||||
device_id: DEVICE_ID,
|
||||
event: DeviceEvent::MouseWheel { delta: LineDelta(0.0, value) },
|
||||
});
|
||||
|
||||
0
|
||||
},
|
||||
|
||||
@@ -746,46 +743,121 @@ pub unsafe extern "system" fn callback(window: HWND, msg: UINT,
|
||||
0
|
||||
},
|
||||
|
||||
winuser::WM_INPUT_DEVICE_CHANGE => {
|
||||
let event = match wparam as _ {
|
||||
winuser::GIDC_ARRIVAL => DeviceEvent::Added,
|
||||
winuser::GIDC_REMOVAL => DeviceEvent::Removed,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
send_event(Event::DeviceEvent {
|
||||
device_id: wrap_device_id(lparam as _),
|
||||
event,
|
||||
});
|
||||
|
||||
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||
},
|
||||
|
||||
winuser::WM_INPUT => {
|
||||
use events::DeviceEvent::{Motion, MouseMotion};
|
||||
let mut data: winuser::RAWINPUT = mem::uninitialized();
|
||||
let mut data_size = mem::size_of::<winuser::RAWINPUT>() as UINT;
|
||||
winuser::GetRawInputData(mem::transmute(lparam), winuser::RID_INPUT,
|
||||
mem::transmute(&mut data), &mut data_size,
|
||||
mem::size_of::<winuser::RAWINPUTHEADER>() as UINT);
|
||||
use events::DeviceEvent::{Motion, MouseMotion, MouseWheel, Button, Key};
|
||||
use events::MouseScrollDelta::LineDelta;
|
||||
use events::ElementState::{Pressed, Released};
|
||||
|
||||
if data.header.dwType == winuser::RIM_TYPEMOUSE {
|
||||
let mouse = data.data.mouse();
|
||||
if mouse.usFlags & winuser::MOUSE_MOVE_RELATIVE == winuser::MOUSE_MOVE_RELATIVE {
|
||||
let x = mouse.lLastX as f64;
|
||||
let y = mouse.lLastY as f64;
|
||||
if let Some(data) = get_raw_input_data(lparam as _) {
|
||||
let device_id = wrap_device_id(data.header.hDevice as _);
|
||||
|
||||
if x != 0.0 {
|
||||
if data.header.dwType == winuser::RIM_TYPEMOUSE {
|
||||
let mouse = data.data.mouse();
|
||||
|
||||
if util::has_flag(mouse.usFlags, winuser::MOUSE_MOVE_RELATIVE) {
|
||||
let x = mouse.lLastX as f64;
|
||||
let y = mouse.lLastY as f64;
|
||||
|
||||
if x != 0.0 {
|
||||
send_event(Event::DeviceEvent {
|
||||
device_id,
|
||||
event: Motion { axis: 0, value: x }
|
||||
});
|
||||
}
|
||||
|
||||
if y != 0.0 {
|
||||
send_event(Event::DeviceEvent {
|
||||
device_id,
|
||||
event: Motion { axis: 1, value: y }
|
||||
});
|
||||
}
|
||||
|
||||
if x != 0.0 || y != 0.0 {
|
||||
send_event(Event::DeviceEvent {
|
||||
device_id,
|
||||
event: MouseMotion { delta: (x, y) }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if util::has_flag(mouse.usButtonFlags, winuser::RI_MOUSE_WHEEL) {
|
||||
let delta = mouse.usButtonData as SHORT / winuser::WHEEL_DELTA;
|
||||
send_event(Event::DeviceEvent {
|
||||
device_id: DEVICE_ID,
|
||||
event: Motion { axis: 0, value: x }
|
||||
device_id,
|
||||
event: MouseWheel { delta: LineDelta(0.0, delta as f32) }
|
||||
});
|
||||
}
|
||||
|
||||
if y != 0.0 {
|
||||
send_event(Event::DeviceEvent {
|
||||
device_id: DEVICE_ID,
|
||||
event: Motion { axis: 1, value: y }
|
||||
});
|
||||
let button_state = get_raw_mouse_button_state(mouse.usButtonFlags);
|
||||
// Left, middle, and right, respectively.
|
||||
for (index, state) in button_state.iter().enumerate() {
|
||||
if let Some(state) = *state {
|
||||
// This gives us consistency with X11, since there doesn't
|
||||
// seem to be anything else reasonable to do for a mouse
|
||||
// button ID.
|
||||
let button = (index + 1) as _;
|
||||
send_event(Event::DeviceEvent {
|
||||
device_id,
|
||||
event: Button {
|
||||
button,
|
||||
state,
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if data.header.dwType == winuser::RIM_TYPEKEYBOARD {
|
||||
let keyboard = data.data.keyboard();
|
||||
|
||||
let pressed = keyboard.Message == winuser::WM_KEYDOWN
|
||||
|| keyboard.Message == winuser::WM_SYSKEYDOWN;
|
||||
let released = keyboard.Message == winuser::WM_KEYUP
|
||||
|| keyboard.Message == winuser::WM_SYSKEYUP;
|
||||
|
||||
if pressed || released {
|
||||
let state = if pressed {
|
||||
Pressed
|
||||
} else {
|
||||
Released
|
||||
};
|
||||
|
||||
let scancode = keyboard.MakeCode as _;
|
||||
let extended = util::has_flag(keyboard.Flags, winuser::RI_KEY_E0 as _);
|
||||
let vkey = vkey_left_right(
|
||||
keyboard.VKey as _,
|
||||
scancode,
|
||||
extended,
|
||||
);
|
||||
let virtual_keycode = vkey_to_winit_vkey(vkey);
|
||||
|
||||
if x != 0.0 || y != 0.0 {
|
||||
send_event(Event::DeviceEvent {
|
||||
device_id: DEVICE_ID,
|
||||
event: MouseMotion { delta: (x, y) }
|
||||
device_id,
|
||||
event: Key(KeyboardInput {
|
||||
scancode,
|
||||
state,
|
||||
virtual_keycode,
|
||||
modifiers: event::get_key_mods(),
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
} else {
|
||||
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||
}
|
||||
|
||||
winuser::DefWindowProcW(window, msg, wparam, lparam)
|
||||
},
|
||||
|
||||
winuser::WM_TOUCH => {
|
||||
|
||||
112
src/platform/windows/icon.rs
Normal file
112
src/platform/windows/icon.rs
Normal file
@@ -0,0 +1,112 @@
|
||||
use std::{self, mem, ptr};
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::path::Path;
|
||||
|
||||
use winapi::ctypes::{c_int, wchar_t};
|
||||
use winapi::shared::minwindef::{BYTE, LPARAM, WPARAM};
|
||||
use winapi::shared::windef::{HICON, HWND};
|
||||
use winapi::um::winuser;
|
||||
|
||||
use {Pixel, PIXEL_SIZE, Icon};
|
||||
use platform::platform::util;
|
||||
|
||||
impl Pixel {
|
||||
fn to_bgra(&mut self) {
|
||||
mem::swap(&mut self.r, &mut self.b);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum IconType {
|
||||
Small = winuser::ICON_SMALL as isize,
|
||||
Big = winuser::ICON_BIG as isize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WinIcon {
|
||||
pub handle: HICON,
|
||||
}
|
||||
|
||||
impl WinIcon {
|
||||
#[allow(dead_code)]
|
||||
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, util::WinError> {
|
||||
let wide_path: Vec<u16> = path.as_ref().as_os_str().encode_wide().collect();
|
||||
let handle = unsafe {
|
||||
winuser::LoadImageW(
|
||||
ptr::null_mut(),
|
||||
wide_path.as_ptr() as *const wchar_t,
|
||||
winuser::IMAGE_ICON,
|
||||
0, // 0 indicates that we want to use the actual width
|
||||
0, // and height
|
||||
winuser::LR_LOADFROMFILE,
|
||||
) as HICON
|
||||
};
|
||||
if !handle.is_null() {
|
||||
Ok(WinIcon { handle })
|
||||
} else {
|
||||
Err(util::WinError::from_last_error())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_icon(icon: Icon) -> Result<Self, util::WinError> {
|
||||
Self::from_rgba(icon.rgba, icon.width, icon.height)
|
||||
}
|
||||
|
||||
pub fn from_rgba(mut rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, util::WinError> {
|
||||
assert_eq!(rgba.len() % PIXEL_SIZE, 0);
|
||||
let pixel_count = rgba.len() / PIXEL_SIZE;
|
||||
assert_eq!(pixel_count, (width * height) as usize);
|
||||
let mut and_mask = Vec::with_capacity(pixel_count);
|
||||
let pixels = rgba.as_mut_ptr() as *mut Pixel; // how not to write idiomatic Rust
|
||||
for pixel_index in 0..pixel_count {
|
||||
let pixel = unsafe { &mut *pixels.offset(pixel_index as isize) };
|
||||
and_mask.push(pixel.a.wrapping_sub(std::u8::MAX)); // invert alpha channel
|
||||
pixel.to_bgra();
|
||||
}
|
||||
assert_eq!(and_mask.len(), pixel_count);
|
||||
let handle = unsafe {
|
||||
winuser::CreateIcon(
|
||||
ptr::null_mut(),
|
||||
width as c_int,
|
||||
height as c_int,
|
||||
1,
|
||||
(PIXEL_SIZE * 8) as BYTE,
|
||||
and_mask.as_ptr() as *const BYTE,
|
||||
rgba.as_ptr() as *const BYTE,
|
||||
) as HICON
|
||||
};
|
||||
if !handle.is_null() {
|
||||
Ok(WinIcon { handle })
|
||||
} else {
|
||||
Err(util::WinError::from_last_error())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_for_window(&self, hwnd: HWND, icon_type: IconType) {
|
||||
unsafe {
|
||||
winuser::SendMessageW(
|
||||
hwnd,
|
||||
winuser::WM_SETICON,
|
||||
icon_type as WPARAM,
|
||||
self.handle as LPARAM,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WinIcon {
|
||||
fn drop(&mut self) {
|
||||
unsafe { winuser::DestroyIcon(self.handle) };
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unset_for_window(hwnd: HWND, icon_type: IconType) {
|
||||
unsafe {
|
||||
winuser::SendMessageW(
|
||||
hwnd,
|
||||
winuser::WM_SETICON,
|
||||
icon_type as WPARAM,
|
||||
0 as LPARAM,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ pub use self::window::Window;
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
pub parent: Option<HWND>,
|
||||
pub taskbar_icon: Option<::Icon>,
|
||||
}
|
||||
|
||||
unsafe impl Send for PlatformSpecificWindowBuilderAttributes {}
|
||||
@@ -18,10 +19,25 @@ unsafe impl Sync for PlatformSpecificWindowBuilderAttributes {}
|
||||
// TODO: document what this means
|
||||
pub type Cursor = *const winapi::ctypes::wchar_t;
|
||||
|
||||
// Constant device ID, to be removed when this backend is updated to report real device IDs.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId;
|
||||
const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);
|
||||
pub struct DeviceId(u32);
|
||||
|
||||
impl DeviceId {
|
||||
pub fn get_persistent_identifier(&self) -> Option<String> {
|
||||
if self.0 != 0 {
|
||||
raw_input::get_raw_input_device_name(self.0 as _)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Constant device ID, to be removed when this backend is updated to report real device IDs.
|
||||
const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId(0));
|
||||
|
||||
fn wrap_device_id(id: u32) -> ::DeviceId {
|
||||
::DeviceId(DeviceId(id))
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(HWND);
|
||||
@@ -30,5 +46,8 @@ unsafe impl Sync for WindowId {}
|
||||
|
||||
mod event;
|
||||
mod events_loop;
|
||||
mod icon;
|
||||
mod monitor;
|
||||
mod raw_input;
|
||||
mod util;
|
||||
mod window;
|
||||
|
||||
@@ -6,7 +6,7 @@ use winapi::um::winuser;
|
||||
use std::collections::VecDeque;
|
||||
use std::{mem, ptr};
|
||||
|
||||
use super::EventsLoop;
|
||||
use super::{EventsLoop, util};
|
||||
|
||||
/// Win32 implementation of the main `MonitorId` object.
|
||||
#[derive(Clone)]
|
||||
@@ -44,12 +44,6 @@ struct HMonitor(HMONITOR);
|
||||
|
||||
unsafe impl Send for HMonitor {}
|
||||
|
||||
fn wchar_as_string(wchar: &[wchar_t]) -> String {
|
||||
String::from_utf16_lossy(wchar)
|
||||
.trim_right_matches(0 as char)
|
||||
.to_string()
|
||||
}
|
||||
|
||||
unsafe extern "system" fn monitor_enum_proc(hmonitor: HMONITOR, _: HDC, place: LPRECT, data: LPARAM) -> BOOL {
|
||||
let monitors = data as *mut VecDeque<MonitorId>;
|
||||
|
||||
@@ -67,7 +61,7 @@ unsafe extern "system" fn monitor_enum_proc(hmonitor: HMONITOR, _: HDC, place: L
|
||||
(*monitors).push_back(MonitorId {
|
||||
adapter_name: monitor_info.szDevice,
|
||||
hmonitor: HMonitor(hmonitor),
|
||||
monitor_name: wchar_as_string(&monitor_info.szDevice),
|
||||
monitor_name: util::wchar_to_string(&monitor_info.szDevice),
|
||||
primary: monitor_info.dwFlags & winuser::MONITORINFOF_PRIMARY != 0,
|
||||
position,
|
||||
dimensions,
|
||||
@@ -109,7 +103,7 @@ impl EventsLoop {
|
||||
MonitorId {
|
||||
adapter_name: monitor_info.szDevice,
|
||||
hmonitor: super::monitor::HMonitor(hmonitor),
|
||||
monitor_name: wchar_as_string(&monitor_info.szDevice),
|
||||
monitor_name: util::wchar_to_string(&monitor_info.szDevice),
|
||||
primary: monitor_info.dwFlags & winuser::MONITORINFOF_PRIMARY != 0,
|
||||
position,
|
||||
dimensions,
|
||||
|
||||
235
src/platform/windows/raw_input.rs
Normal file
235
src/platform/windows/raw_input.rs
Normal file
@@ -0,0 +1,235 @@
|
||||
use std::mem::{self, size_of};
|
||||
use std::ptr;
|
||||
|
||||
use winapi::ctypes::wchar_t;
|
||||
use winapi::shared::minwindef::{UINT, USHORT, TRUE};
|
||||
use winapi::shared::hidusage::{
|
||||
HID_USAGE_PAGE_GENERIC,
|
||||
HID_USAGE_GENERIC_MOUSE,
|
||||
HID_USAGE_GENERIC_KEYBOARD,
|
||||
};
|
||||
use winapi::shared::windef::HWND;
|
||||
use winapi::um::winnt::HANDLE;
|
||||
use winapi::um::winuser::{
|
||||
self,
|
||||
RAWINPUTDEVICELIST,
|
||||
RID_DEVICE_INFO,
|
||||
RID_DEVICE_INFO_MOUSE,
|
||||
RID_DEVICE_INFO_KEYBOARD,
|
||||
RID_DEVICE_INFO_HID,
|
||||
RIM_TYPEMOUSE,
|
||||
RIM_TYPEKEYBOARD,
|
||||
RIM_TYPEHID,
|
||||
RIDI_DEVICEINFO,
|
||||
RIDI_DEVICENAME,
|
||||
RAWINPUTDEVICE,
|
||||
RIDEV_DEVNOTIFY,
|
||||
RIDEV_INPUTSINK,
|
||||
HRAWINPUT,
|
||||
RAWINPUT,
|
||||
RAWINPUTHEADER,
|
||||
RID_INPUT,
|
||||
};
|
||||
|
||||
use platform::platform::util;
|
||||
use events::ElementState;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_raw_input_device_list() -> Option<Vec<RAWINPUTDEVICELIST>> {
|
||||
let list_size = size_of::<RAWINPUTDEVICELIST>() as UINT;
|
||||
|
||||
let mut num_devices = 0;
|
||||
let status = unsafe { winuser::GetRawInputDeviceList(
|
||||
ptr::null_mut(),
|
||||
&mut num_devices,
|
||||
list_size,
|
||||
) };
|
||||
|
||||
if status == UINT::max_value() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut buffer = Vec::with_capacity(num_devices as _);
|
||||
|
||||
let num_stored = unsafe { winuser::GetRawInputDeviceList(
|
||||
buffer.as_ptr() as _,
|
||||
&mut num_devices,
|
||||
list_size,
|
||||
) };
|
||||
|
||||
if num_stored == UINT::max_value() {
|
||||
return None;
|
||||
}
|
||||
|
||||
debug_assert_eq!(num_devices, num_stored);
|
||||
|
||||
unsafe { buffer.set_len(num_devices as _) };
|
||||
|
||||
Some(buffer)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub enum RawDeviceInfo {
|
||||
Mouse(RID_DEVICE_INFO_MOUSE),
|
||||
Keyboard(RID_DEVICE_INFO_KEYBOARD),
|
||||
Hid(RID_DEVICE_INFO_HID),
|
||||
}
|
||||
|
||||
impl From<RID_DEVICE_INFO> for RawDeviceInfo {
|
||||
fn from(info: RID_DEVICE_INFO) -> Self {
|
||||
unsafe {
|
||||
match info.dwType {
|
||||
RIM_TYPEMOUSE => RawDeviceInfo::Mouse(*info.u.mouse()),
|
||||
RIM_TYPEKEYBOARD => RawDeviceInfo::Keyboard(*info.u.keyboard()),
|
||||
RIM_TYPEHID => RawDeviceInfo::Hid(*info.u.hid()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_raw_input_device_info(handle: HANDLE) -> Option<RawDeviceInfo> {
|
||||
let mut info: RID_DEVICE_INFO = unsafe { mem::uninitialized() };
|
||||
let info_size = size_of::<RID_DEVICE_INFO>() as UINT;
|
||||
|
||||
info.cbSize = info_size;
|
||||
|
||||
let mut minimum_size = 0;
|
||||
let status = unsafe { winuser::GetRawInputDeviceInfoW(
|
||||
handle,
|
||||
RIDI_DEVICEINFO,
|
||||
&mut info as *mut _ as _,
|
||||
&mut minimum_size,
|
||||
) };
|
||||
|
||||
if status == UINT::max_value() || status == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
debug_assert_eq!(info_size, status);
|
||||
|
||||
Some(info.into())
|
||||
}
|
||||
|
||||
pub fn get_raw_input_device_name(handle: HANDLE) -> Option<String> {
|
||||
let mut minimum_size = 0;
|
||||
let status = unsafe { winuser::GetRawInputDeviceInfoW(
|
||||
handle,
|
||||
RIDI_DEVICENAME,
|
||||
ptr::null_mut(),
|
||||
&mut minimum_size,
|
||||
) };
|
||||
|
||||
if status != 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut name: Vec<wchar_t> = Vec::with_capacity(minimum_size as _);
|
||||
|
||||
let status = unsafe { winuser::GetRawInputDeviceInfoW(
|
||||
handle,
|
||||
RIDI_DEVICENAME,
|
||||
name.as_ptr() as _,
|
||||
&mut minimum_size,
|
||||
) };
|
||||
|
||||
if status == UINT::max_value() || status == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
debug_assert_eq!(minimum_size, status);
|
||||
|
||||
unsafe { name.set_len(minimum_size as _) };
|
||||
|
||||
Some(util::wchar_to_string(&name))
|
||||
}
|
||||
|
||||
pub fn register_raw_input_devices(devices: &[RAWINPUTDEVICE]) -> bool {
|
||||
let device_size = size_of::<RAWINPUTDEVICE>() as UINT;
|
||||
|
||||
let success = unsafe { winuser::RegisterRawInputDevices(
|
||||
devices.as_ptr() as _,
|
||||
devices.len() as _,
|
||||
device_size,
|
||||
) };
|
||||
|
||||
success == TRUE
|
||||
}
|
||||
|
||||
pub fn register_all_mice_and_keyboards_for_raw_input(window_handle: HWND) -> bool {
|
||||
// RIDEV_DEVNOTIFY: receive hotplug events
|
||||
// RIDEV_INPUTSINK: receive events even if we're not in the foreground
|
||||
let flags = RIDEV_DEVNOTIFY | RIDEV_INPUTSINK;
|
||||
|
||||
let devices: [RAWINPUTDEVICE; 2] = [
|
||||
RAWINPUTDEVICE {
|
||||
usUsagePage: HID_USAGE_PAGE_GENERIC,
|
||||
usUsage: HID_USAGE_GENERIC_MOUSE,
|
||||
dwFlags: flags,
|
||||
hwndTarget: window_handle,
|
||||
},
|
||||
RAWINPUTDEVICE {
|
||||
usUsagePage: HID_USAGE_PAGE_GENERIC,
|
||||
usUsage: HID_USAGE_GENERIC_KEYBOARD,
|
||||
dwFlags: flags,
|
||||
hwndTarget: window_handle,
|
||||
},
|
||||
];
|
||||
|
||||
register_raw_input_devices(&devices)
|
||||
}
|
||||
|
||||
pub fn get_raw_input_data(handle: HRAWINPUT) -> Option<RAWINPUT> {
|
||||
let mut data: RAWINPUT = unsafe { mem::uninitialized() };
|
||||
let mut data_size = size_of::<RAWINPUT>() as UINT;
|
||||
let header_size = size_of::<RAWINPUTHEADER>() as UINT;
|
||||
|
||||
let status = unsafe { winuser::GetRawInputData(
|
||||
handle,
|
||||
RID_INPUT,
|
||||
&mut data as *mut _ as _,
|
||||
&mut data_size,
|
||||
header_size,
|
||||
) };
|
||||
|
||||
if status == UINT::max_value() || status == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(data)
|
||||
}
|
||||
|
||||
|
||||
fn button_flags_to_element_state(button_flags: USHORT, down_flag: USHORT, up_flag: USHORT)
|
||||
-> Option<ElementState>
|
||||
{
|
||||
// We assume the same button won't be simultaneously pressed and released.
|
||||
if util::has_flag(button_flags, down_flag) {
|
||||
Some(ElementState::Pressed)
|
||||
} else if util::has_flag(button_flags, up_flag) {
|
||||
Some(ElementState::Released)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_raw_mouse_button_state(button_flags: USHORT) -> [Option<ElementState>; 3] {
|
||||
[
|
||||
button_flags_to_element_state(
|
||||
button_flags,
|
||||
winuser::RI_MOUSE_LEFT_BUTTON_DOWN,
|
||||
winuser::RI_MOUSE_LEFT_BUTTON_UP,
|
||||
),
|
||||
button_flags_to_element_state(
|
||||
button_flags,
|
||||
winuser::RI_MOUSE_MIDDLE_BUTTON_DOWN,
|
||||
winuser::RI_MOUSE_MIDDLE_BUTTON_UP,
|
||||
),
|
||||
button_flags_to_element_state(
|
||||
button_flags,
|
||||
winuser::RI_MOUSE_RIGHT_BUTTON_DOWN,
|
||||
winuser::RI_MOUSE_RIGHT_BUTTON_UP,
|
||||
),
|
||||
]
|
||||
}
|
||||
72
src/platform/windows/util.rs
Normal file
72
src/platform/windows/util.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
use std::{self, mem, ptr};
|
||||
use std::ops::BitAnd;
|
||||
|
||||
use winapi::ctypes::wchar_t;
|
||||
use winapi::shared::minwindef::DWORD;
|
||||
use winapi::um::errhandlingapi::GetLastError;
|
||||
use winapi::um::winbase::{
|
||||
FormatMessageW,
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
||||
FORMAT_MESSAGE_FROM_SYSTEM,
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
lstrlenW,
|
||||
LocalFree,
|
||||
};
|
||||
use winapi::um::winnt::{
|
||||
LPCWSTR,
|
||||
MAKELANGID,
|
||||
LANG_NEUTRAL,
|
||||
SUBLANG_DEFAULT,
|
||||
};
|
||||
|
||||
pub fn has_flag<T>(bitset: T, flag: T) -> bool
|
||||
where T:
|
||||
Copy + PartialEq + BitAnd<T, Output = T>
|
||||
{
|
||||
bitset & flag == flag
|
||||
}
|
||||
|
||||
pub fn wchar_to_string(wchar: &[wchar_t]) -> String {
|
||||
String::from_utf16_lossy(wchar)
|
||||
.trim_right_matches(0 as char)
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub struct WinError(Option<String>);
|
||||
|
||||
impl WinError {
|
||||
pub fn from_last_error() -> Self {
|
||||
WinError(unsafe { get_last_error() })
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn get_last_error() -> Option<String> {
|
||||
let err = GetLastError();
|
||||
if err != 0 {
|
||||
let buf_addr: LPCWSTR = {
|
||||
let mut buf_addr: LPCWSTR = mem::uninitialized();
|
||||
FormatMessageW(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER
|
||||
| FORMAT_MESSAGE_FROM_SYSTEM
|
||||
| FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
ptr::null(),
|
||||
err,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) as DWORD,
|
||||
// This is a pointer to a pointer
|
||||
&mut buf_addr as *mut LPCWSTR as *mut _,
|
||||
0,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
buf_addr
|
||||
};
|
||||
if !buf_addr.is_null() {
|
||||
let buf_len = lstrlenW(buf_addr) as usize;
|
||||
let buf_slice = std::slice::from_raw_parts(buf_addr, buf_len);
|
||||
let string = wchar_to_string(buf_slice);
|
||||
LocalFree(buf_addr as *mut _);
|
||||
return Some(string);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -1,35 +1,31 @@
|
||||
#![cfg(target_os = "windows")]
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::ffi::OsStr;
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::{io, mem, ptr};
|
||||
use std::os::raw;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::mpsc::channel;
|
||||
use std::cell::Cell;
|
||||
|
||||
use platform::platform::events_loop::{self, DESTROY_MSG_ID};
|
||||
use platform::platform::EventsLoop;
|
||||
use platform::platform::PlatformSpecificWindowBuilderAttributes;
|
||||
use platform::platform::WindowId;
|
||||
use winapi::shared::minwindef::{BOOL, DWORD, UINT};
|
||||
use winapi::shared::windef::{HDC, HWND, POINT, RECT};
|
||||
use winapi::um::{combaseapi, dwmapi, libloaderapi, processthreadsapi, winuser};
|
||||
use winapi::um::objbase::{COINIT_MULTITHREADED};
|
||||
use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl};
|
||||
use winapi::um::winnt::{HRESULT, LONG, LPCWSTR};
|
||||
|
||||
use CreationError;
|
||||
use CursorState;
|
||||
use Icon;
|
||||
use MonitorId as RootMonitorId;
|
||||
use MouseCursor;
|
||||
use WindowAttributes;
|
||||
use MonitorId as RootMonitorId;
|
||||
|
||||
use winapi::shared::minwindef::{UINT, DWORD, BOOL};
|
||||
use winapi::shared::windef::{HWND, HDC, RECT, POINT};
|
||||
use winapi::shared::hidusage;
|
||||
use winapi::um::{winuser, dwmapi, libloaderapi, processthreadsapi};
|
||||
use winapi::um::winnt::{LPCWSTR, LONG, HRESULT};
|
||||
use winapi::um::combaseapi;
|
||||
use winapi::um::objbase::{COINIT_MULTITHREADED};
|
||||
use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl};
|
||||
use platform::platform::{EventsLoop, PlatformSpecificWindowBuilderAttributes, WindowId};
|
||||
use platform::platform::events_loop::{self, DESTROY_MSG_ID};
|
||||
use platform::platform::icon::{self, IconType, WinIcon};
|
||||
use platform::platform::raw_input::register_all_mice_and_keyboards_for_raw_input;
|
||||
|
||||
/// The Win32 implementation of the main `Window` object.
|
||||
pub struct Window {
|
||||
@@ -39,6 +35,9 @@ pub struct Window {
|
||||
/// The current window state.
|
||||
window_state: Arc<Mutex<events_loop::WindowState>>,
|
||||
|
||||
window_icon: Cell<Option<WinIcon>>,
|
||||
taskbar_icon: Cell<Option<WinIcon>>,
|
||||
|
||||
// The events loop proxy.
|
||||
events_loop_proxy: events_loop::EventsLoopProxy,
|
||||
}
|
||||
@@ -72,19 +71,19 @@ unsafe fn unjust_window_rect(prc: &mut RECT, style: DWORD, ex_style: DWORD) -> B
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(events_loop: &EventsLoop, w_attr: &WindowAttributes,
|
||||
pl_attr: &PlatformSpecificWindowBuilderAttributes) -> Result<Window, CreationError>
|
||||
{
|
||||
let mut w_attr = Some(w_attr.clone());
|
||||
let mut pl_attr = Some(pl_attr.clone());
|
||||
|
||||
pub fn new(
|
||||
events_loop: &EventsLoop,
|
||||
w_attr: WindowAttributes,
|
||||
pl_attr: PlatformSpecificWindowBuilderAttributes,
|
||||
) -> Result<Window, CreationError> {
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let proxy = events_loop.create_proxy();
|
||||
|
||||
events_loop.execute_in_thread(move |inserter| {
|
||||
// We dispatch an `init` function because of code style.
|
||||
let win = unsafe { init(w_attr.take().unwrap(), pl_attr.take().unwrap(), inserter, proxy.clone()) };
|
||||
// First person to remove the need for cloning here gets a cookie!
|
||||
let win = unsafe { init(w_attr.clone(), pl_attr.clone(), inserter, proxy.clone()) };
|
||||
let _ = tx.send(win);
|
||||
});
|
||||
|
||||
@@ -92,10 +91,11 @@ impl Window {
|
||||
}
|
||||
|
||||
pub fn set_title(&self, text: &str) {
|
||||
let text = OsStr::new(text)
|
||||
.encode_wide()
|
||||
.chain(Some(0).into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
unsafe {
|
||||
let text = OsStr::new(text).encode_wide().chain(Some(0).into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
winuser::SetWindowTextW(self.window.0, text.as_ptr() as LPCWSTR);
|
||||
}
|
||||
}
|
||||
@@ -116,24 +116,20 @@ impl Window {
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
use std::mem;
|
||||
let mut rect: RECT = unsafe { mem::uninitialized() };
|
||||
|
||||
let mut placement: winuser::WINDOWPLACEMENT = unsafe { mem::zeroed() };
|
||||
placement.length = mem::size_of::<winuser::WINDOWPLACEMENT>() as UINT;
|
||||
|
||||
if unsafe { winuser::GetWindowPlacement(self.window.0, &mut placement) } == 0 {
|
||||
return None
|
||||
if unsafe { winuser::GetWindowRect(self.window.0, &mut rect) } != 0 {
|
||||
Some((rect.left as i32, rect.top as i32))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
let ref rect = placement.rcNormalPosition;
|
||||
Some((rect.left as i32, rect.top as i32))
|
||||
}
|
||||
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
use std::mem;
|
||||
|
||||
let mut position: POINT = unsafe{ mem::zeroed() };
|
||||
if unsafe{ winuser::ClientToScreen(self.window.0, &mut position) } == 0 {
|
||||
let mut position: POINT = unsafe { mem::zeroed() };
|
||||
if unsafe { winuser::ClientToScreen(self.window.0, &mut position) } == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -429,6 +425,13 @@ impl Window {
|
||||
|
||||
unsafe fn restore_saved_window(&self) {
|
||||
let window_state = self.window_state.lock().unwrap();
|
||||
|
||||
// 'saved_window_info' can be None if the window has never been
|
||||
// in fullscreen mode before this method gets called.
|
||||
if window_state.saved_window_info.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset original window style and size. The multiple window size/moves
|
||||
// here are ugly, but if SetWindowPos() doesn't redraw, the taskbar won't be
|
||||
// repainted. Better-looking methods welcome.
|
||||
@@ -614,6 +617,32 @@ impl Window {
|
||||
inner: EventsLoop::get_current_monitor(self.window.0),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, mut window_icon: Option<Icon>) {
|
||||
let window_icon = window_icon
|
||||
.take()
|
||||
.map(|icon| WinIcon::from_icon(icon).expect("Failed to create `ICON_SMALL`"));
|
||||
if let Some(ref window_icon) = window_icon {
|
||||
window_icon.set_for_window(self.window.0, IconType::Small);
|
||||
} else {
|
||||
icon::unset_for_window(self.window.0, IconType::Small);
|
||||
}
|
||||
self.window_icon.replace(window_icon);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_taskbar_icon(&self, mut taskbar_icon: Option<Icon>) {
|
||||
let taskbar_icon = taskbar_icon
|
||||
.take()
|
||||
.map(|icon| WinIcon::from_icon(icon).expect("Failed to create `ICON_BIG`"));
|
||||
if let Some(ref taskbar_icon) = taskbar_icon {
|
||||
taskbar_icon.set_for_window(self.window.0, IconType::Big);
|
||||
} else {
|
||||
icon::unset_for_window(self.window.0, IconType::Big);
|
||||
}
|
||||
self.taskbar_icon.replace(taskbar_icon);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
@@ -646,13 +675,44 @@ pub unsafe fn adjust_size(
|
||||
(rect.right - rect.left, rect.bottom - rect.top)
|
||||
}
|
||||
|
||||
unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
||||
inserter: events_loop::Inserter, events_loop_proxy: events_loop::EventsLoopProxy) -> Result<Window, CreationError> {
|
||||
let title = OsStr::new(&window.title).encode_wide().chain(Some(0).into_iter())
|
||||
unsafe fn init(
|
||||
mut window: WindowAttributes,
|
||||
mut pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
||||
inserter: events_loop::Inserter,
|
||||
events_loop_proxy: events_loop::EventsLoopProxy,
|
||||
) -> Result<Window, CreationError> {
|
||||
let title = OsStr::new(&window.title)
|
||||
.encode_wide()
|
||||
.chain(Some(0).into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let window_icon = {
|
||||
let icon = window.window_icon
|
||||
.take()
|
||||
.map(WinIcon::from_icon);
|
||||
if icon.is_some() {
|
||||
Some(icon.unwrap().map_err(|err| {
|
||||
CreationError::OsError(format!("Failed to create `ICON_SMALL`: {:?}", err))
|
||||
})?)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
let taskbar_icon = {
|
||||
let icon = pl_attribs.taskbar_icon
|
||||
.take()
|
||||
.map(WinIcon::from_icon);
|
||||
if icon.is_some() {
|
||||
Some(icon.unwrap().map_err(|err| {
|
||||
CreationError::OsError(format!("Failed to create `ICON_BIG`: {:?}", err))
|
||||
})?)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
// registering the window class
|
||||
let class_name = register_window_class();
|
||||
let class_name = register_window_class(&window_icon, &taskbar_icon);
|
||||
|
||||
// building a RECT object with coordinates
|
||||
let mut rect = RECT {
|
||||
@@ -731,16 +791,8 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild
|
||||
WindowWrapper(handle, hdc)
|
||||
};
|
||||
|
||||
// Set up raw mouse input
|
||||
{
|
||||
let mut rid: winuser::RAWINPUTDEVICE = mem::uninitialized();
|
||||
rid.usUsagePage = hidusage::HID_USAGE_PAGE_GENERIC;
|
||||
rid.usUsage = hidusage::HID_USAGE_GENERIC_MOUSE;
|
||||
rid.dwFlags = 0;
|
||||
rid.hwndTarget = real_window.0;
|
||||
|
||||
winuser::RegisterRawInputDevices(&rid, 1, mem::size_of::<winuser::RAWINPUTDEVICE>() as u32);
|
||||
}
|
||||
// Set up raw input
|
||||
register_all_mice_and_keyboards_for_raw_input(real_window.0);
|
||||
|
||||
// Register for touch events if applicable
|
||||
{
|
||||
@@ -749,18 +801,22 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild
|
||||
winuser::RegisterTouchWindow( real_window.0, winuser::TWF_WANTPALM );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let (transparent, maximized, fullscreen) = (
|
||||
window.transparent.clone(), window.maximized.clone(), window.fullscreen.clone()
|
||||
);
|
||||
|
||||
// Creating a mutex to track the current window state
|
||||
let window_state = Arc::new(Mutex::new(events_loop::WindowState {
|
||||
cursor: winuser::IDC_ARROW, // use arrow by default
|
||||
cursor_state: CursorState::Normal,
|
||||
attributes: window.clone(),
|
||||
attributes: window,
|
||||
mouse_in_window: false,
|
||||
saved_window_info: None,
|
||||
}));
|
||||
|
||||
// making the window transparent
|
||||
if window.transparent {
|
||||
if transparent {
|
||||
let bb = dwmapi::DWM_BLURBEHIND {
|
||||
dwFlags: 0x1, // FIXME: DWM_BB_ENABLE;
|
||||
fEnable: 1,
|
||||
@@ -774,12 +830,14 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild
|
||||
let win = Window {
|
||||
window: real_window,
|
||||
window_state: window_state,
|
||||
events_loop_proxy
|
||||
window_icon: Cell::new(window_icon),
|
||||
taskbar_icon: Cell::new(taskbar_icon),
|
||||
events_loop_proxy,
|
||||
};
|
||||
|
||||
win.set_maximized(window.maximized);
|
||||
if let Some(_) = window.fullscreen {
|
||||
win.set_fullscreen(window.fullscreen);
|
||||
win.set_maximized(maximized);
|
||||
if let Some(_) = fullscreen {
|
||||
win.set_fullscreen(fullscreen);
|
||||
force_window_active(win.window.0);
|
||||
}
|
||||
|
||||
@@ -788,9 +846,23 @@ unsafe fn init(window: WindowAttributes, pl_attribs: PlatformSpecificWindowBuild
|
||||
Ok(win)
|
||||
}
|
||||
|
||||
unsafe fn register_window_class() -> Vec<u16> {
|
||||
let class_name = OsStr::new("Window Class").encode_wide().chain(Some(0).into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
unsafe fn register_window_class(
|
||||
window_icon: &Option<WinIcon>,
|
||||
taskbar_icon: &Option<WinIcon>,
|
||||
) -> Vec<u16> {
|
||||
let class_name: Vec<_> = OsStr::new("Window Class")
|
||||
.encode_wide()
|
||||
.chain(Some(0).into_iter())
|
||||
.collect();
|
||||
|
||||
let h_icon = taskbar_icon
|
||||
.as_ref()
|
||||
.map(|icon| icon.handle)
|
||||
.unwrap_or(ptr::null_mut());
|
||||
let h_icon_small = window_icon
|
||||
.as_ref()
|
||||
.map(|icon| icon.handle)
|
||||
.unwrap_or(ptr::null_mut());
|
||||
|
||||
let class = winuser::WNDCLASSEXW {
|
||||
cbSize: mem::size_of::<winuser::WNDCLASSEXW>() as UINT,
|
||||
@@ -799,12 +871,12 @@ unsafe fn register_window_class() -> Vec<u16> {
|
||||
cbClsExtra: 0,
|
||||
cbWndExtra: 0,
|
||||
hInstance: libloaderapi::GetModuleHandleW(ptr::null()),
|
||||
hIcon: ptr::null_mut(),
|
||||
hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly
|
||||
hIcon: h_icon,
|
||||
hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly
|
||||
hbrBackground: ptr::null_mut(),
|
||||
lpszMenuName: ptr::null(),
|
||||
lpszClassName: class_name.as_ptr(),
|
||||
hIconSm: ptr::null_mut(),
|
||||
hIconSm: h_icon_small,
|
||||
};
|
||||
|
||||
// We ignore errors because registering the same window class twice would trigger
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::collections::vec_deque::IntoIter as VecDequeIter;
|
||||
use CreationError;
|
||||
use CursorState;
|
||||
use EventsLoop;
|
||||
use Icon;
|
||||
use MouseCursor;
|
||||
use Window;
|
||||
use WindowBuilder;
|
||||
@@ -91,6 +92,24 @@ impl WindowBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the window icon. On Windows and X11, this is typically the small icon in the top-left
|
||||
/// corner of the titlebar.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// This only has an effect on Windows and X11.
|
||||
///
|
||||
/// On Windows, this sets `ICON_SMALL`. The base size for a window icon is 16x16, but it's
|
||||
/// recommended to account for screen scaling and pick a multiple of that, i.e. 32x32.
|
||||
///
|
||||
/// X11 has no universal guidelines for icon sizes, so you're at the whims of the WM. That
|
||||
/// said, it's usually in the same ballpark as on Windows.
|
||||
#[inline]
|
||||
pub fn with_window_icon(mut self, window_icon: Option<Icon>) -> WindowBuilder {
|
||||
self.window.window_icon = window_icon;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables multitouch.
|
||||
#[inline]
|
||||
pub fn with_multitouch(mut self) -> WindowBuilder {
|
||||
@@ -103,22 +122,22 @@ impl WindowBuilder {
|
||||
/// Error should be very rare and only occur in case of permission denied, incompatible system,
|
||||
/// out of memory, etc.
|
||||
pub fn build(mut self, events_loop: &EventsLoop) -> Result<Window, CreationError> {
|
||||
// resizing the window to the dimensions of the monitor when fullscreen
|
||||
if self.window.dimensions.is_none() {
|
||||
self.window.dimensions = Some(self.window.dimensions.unwrap_or_else(|| {
|
||||
if let Some(ref monitor) = self.window.fullscreen {
|
||||
self.window.dimensions = Some(monitor.get_dimensions());
|
||||
// resizing the window to the dimensions of the monitor when fullscreen
|
||||
monitor.get_dimensions()
|
||||
} else {
|
||||
// default dimensions
|
||||
(1024, 768)
|
||||
}
|
||||
}
|
||||
|
||||
// default dimensions
|
||||
if self.window.dimensions.is_none() {
|
||||
self.window.dimensions = Some((1024, 768));
|
||||
}
|
||||
}));
|
||||
|
||||
// building
|
||||
let w = try!(platform::Window::new(&events_loop.events_loop, &self.window, &self.platform_specific));
|
||||
|
||||
Ok(Window { window: w })
|
||||
platform::Window::new(
|
||||
&events_loop.events_loop,
|
||||
self.window,
|
||||
self.platform_specific,
|
||||
).map(|window| Window { window })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,6 +363,19 @@ impl Window {
|
||||
self.window.set_decorations(decorations)
|
||||
}
|
||||
|
||||
/// Sets the window icon. On Windows and X11, this is typically the small icon in the top-left
|
||||
/// corner of the titlebar.
|
||||
///
|
||||
/// For more usage notes, see `WindowBuilder::with_window_icon`.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// This only has an effect on Windows and X11.
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, window_icon: Option<Icon>) {
|
||||
self.window.set_window_icon(window_icon)
|
||||
}
|
||||
|
||||
/// Returns the monitor on which the window currently resides
|
||||
pub fn get_current_monitor(&self) -> MonitorId {
|
||||
self.window.get_current_monitor()
|
||||
|
||||
Reference in New Issue
Block a user