Merge branch 'web' into webmerge2

This commit is contained in:
Hal Gentz 2019-07-09 22:43:15 -06:00
commit 613fafdfdf
No known key found for this signature in database
GPG key ID: FFA703792A952E2E
117 changed files with 5740 additions and 4274 deletions

View file

@ -9,32 +9,32 @@ use std::os::raw;
#[link(name = "android")]
#[link(name = "EGL")]
#[link(name = "GLESv2")]
extern {}
extern "C" {}
/**
* asset_manager.h
*/
** asset_manager.h
**/
pub type AAssetManager = raw::c_void;
/**
* native_window.h
*/
** native_window.h
**/
pub type ANativeWindow = raw::c_void;
extern {
extern "C" {
pub fn ANativeWindow_getHeight(window: *const ANativeWindow) -> libc::int32_t;
pub fn ANativeWindow_getWidth(window: *const ANativeWindow) -> libc::int32_t;
}
/**
* native_activity.h
*/
** native_activity.h
**/
pub type JavaVM = ();
pub type JNIEnv = ();
pub type jobject = *const libc::c_void;
pub type AInputQueue = (); // FIXME: wrong
pub type ARect = (); // FIXME: wrong
pub type AInputQueue = (); // FIXME: wrong
pub type ARect = (); // FIXME: wrong
#[repr(C)]
pub struct ANativeActivity {
@ -52,43 +52,56 @@ pub struct ANativeActivity {
#[repr(C)]
pub struct ANativeActivityCallbacks {
pub onStart: extern fn(*mut ANativeActivity),
pub onResume: extern fn(*mut ANativeActivity),
pub onSaveInstanceState: extern fn(*mut ANativeActivity, *mut libc::size_t),
pub onPause: extern fn(*mut ANativeActivity),
pub onStop: extern fn(*mut ANativeActivity),
pub onDestroy: extern fn(*mut ANativeActivity),
pub onWindowFocusChanged: extern fn(*mut ANativeActivity, libc::c_int),
pub onNativeWindowCreated: extern fn(*mut ANativeActivity, *const ANativeWindow),
pub onNativeWindowResized: extern fn(*mut ANativeActivity, *const ANativeWindow),
pub onNativeWindowRedrawNeeded: extern fn(*mut ANativeActivity, *const ANativeWindow),
pub onNativeWindowDestroyed: extern fn(*mut ANativeActivity, *const ANativeWindow),
pub onInputQueueCreated: extern fn(*mut ANativeActivity, *mut AInputQueue),
pub onInputQueueDestroyed: extern fn(*mut ANativeActivity, *mut AInputQueue),
pub onContentRectChanged: extern fn(*mut ANativeActivity, *const ARect),
pub onConfigurationChanged: extern fn(*mut ANativeActivity),
pub onLowMemory: extern fn(*mut ANativeActivity),
pub onStart: extern "C" fn(*mut ANativeActivity),
pub onResume: extern "C" fn(*mut ANativeActivity),
pub onSaveInstanceState: extern "C" fn(*mut ANativeActivity, *mut libc::size_t),
pub onPause: extern "C" fn(*mut ANativeActivity),
pub onStop: extern "C" fn(*mut ANativeActivity),
pub onDestroy: extern "C" fn(*mut ANativeActivity),
pub onWindowFocusChanged: extern "C" fn(*mut ANativeActivity, libc::c_int),
pub onNativeWindowCreated: extern "C" fn(*mut ANativeActivity, *const ANativeWindow),
pub onNativeWindowResized: extern "C" fn(*mut ANativeActivity, *const ANativeWindow),
pub onNativeWindowRedrawNeeded: extern "C" fn(*mut ANativeActivity, *const ANativeWindow),
pub onNativeWindowDestroyed: extern "C" fn(*mut ANativeActivity, *const ANativeWindow),
pub onInputQueueCreated: extern "C" fn(*mut ANativeActivity, *mut AInputQueue),
pub onInputQueueDestroyed: extern "C" fn(*mut ANativeActivity, *mut AInputQueue),
pub onContentRectChanged: extern "C" fn(*mut ANativeActivity, *const ARect),
pub onConfigurationChanged: extern "C" fn(*mut ANativeActivity),
pub onLowMemory: extern "C" fn(*mut ANativeActivity),
}
/**
* looper.h
*/
** looper.h
**/
pub type ALooper = ();
#[link(name = "android")]
extern {
extern "C" {
pub fn ALooper_forThread() -> *const ALooper;
pub fn ALooper_acquire(looper: *const ALooper);
pub fn ALooper_release(looper: *const ALooper);
pub fn ALooper_prepare(opts: libc::c_int) -> *const ALooper;
pub fn ALooper_pollOnce(timeoutMillis: libc::c_int, outFd: *mut libc::c_int,
outEvents: *mut libc::c_int, outData: *mut *mut libc::c_void) -> libc::c_int;
pub fn ALooper_pollAll(timeoutMillis: libc::c_int, outFd: *mut libc::c_int,
outEvents: *mut libc::c_int, outData: *mut *mut libc::c_void) -> libc::c_int;
pub fn ALooper_pollOnce(
timeoutMillis: libc::c_int,
outFd: *mut libc::c_int,
outEvents: *mut libc::c_int,
outData: *mut *mut libc::c_void,
) -> libc::c_int;
pub fn ALooper_pollAll(
timeoutMillis: libc::c_int,
outFd: *mut libc::c_int,
outEvents: *mut libc::c_int,
outData: *mut *mut libc::c_void,
) -> libc::c_int;
pub fn ALooper_wake(looper: *const ALooper);
pub fn ALooper_addFd(looper: *const ALooper, fd: libc::c_int, ident: libc::c_int,
events: libc::c_int, callback: ALooper_callbackFunc, data: *mut libc::c_void)
-> libc::c_int;
pub fn ALooper_addFd(
looper: *const ALooper,
fd: libc::c_int,
ident: libc::c_int,
events: libc::c_int,
callback: ALooper_callbackFunc,
data: *mut libc::c_void,
) -> libc::c_int;
pub fn ALooper_removeFd(looper: *const ALooper, fd: libc::c_int) -> libc::c_int;
}
@ -105,4 +118,5 @@ pub const ALOOPER_EVENT_ERROR: libc::c_int = 1 << 2;
pub const ALOOPER_EVENT_HANGUP: libc::c_int = 1 << 3;
pub const ALOOPER_EVENT_INVALID: libc::c_int = 1 << 4;
pub type ALooper_callbackFunc = extern fn(libc::c_int, libc::c_int, *mut libc::c_void) -> libc::c_int;
pub type ALooper_callbackFunc =
extern "C" fn(libc::c_int, libc::c_int, *mut libc::c_void) -> libc::c_int;

View file

@ -4,34 +4,28 @@ extern crate android_glue;
mod ffi;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::fmt;
use std::os::raw::c_void;
use std::sync::mpsc::{Receiver, channel};
use std::{
cell::RefCell,
collections::VecDeque,
fmt,
os::raw::c_void,
sync::mpsc::{channel, Receiver},
};
use {
CreationError,
Event,
LogicalPosition,
LogicalSize,
CursorIcon,
PhysicalPosition,
PhysicalSize,
WindowAttributes,
WindowEvent,
WindowId as RootWindowId,
use crate::{
error::{ExternalError, NotSupportedError},
events::{Touch, TouchPhase},
window::MonitorHandle as RootMonitorHandle,
CreationError, CursorIcon, Event, LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize,
WindowAttributes, WindowEvent, WindowId as RootWindowId,
};
use CreationError::OsError;
use error::{ExternalError, NotSupportedError};
use events::{Touch, TouchPhase};
use window::MonitorHandle as RootMonitorHandle;
pub type OsError = std::io::Error;
pub struct EventLoop {
event_rx: Receiver<android_glue::Event>,
suspend_callback: RefCell<Option<Box<Fn(bool) -> ()>>>,
suspend_callback: RefCell<Option<Box<dyn Fn(bool) -> ()>>>,
}
#[derive(Clone)]
@ -60,10 +54,11 @@ impl EventLoop {
}
pub fn poll_events<F>(&mut self, mut callback: F)
where F: FnMut(::Event)
where
F: FnMut(::Event),
{
while let Ok(event) = self.event_rx.try_recv() {
let e = match event{
let e = match event {
android_glue::Event::EventMotion(motion) => {
let dpi_factor = MonitorHandle.hidpi_factor();
let location = LogicalPosition::from_physical(
@ -84,37 +79,36 @@ impl EventLoop {
device_id: DEVICE_ID,
}),
})
},
}
android_glue::Event::InitWindow => {
// The activity went to foreground.
if let Some(cb) = self.suspend_callback.borrow().as_ref() {
(*cb)(false);
}
Some(Event::Suspended(false))
},
Some(Event::Resumed)
}
android_glue::Event::TermWindow => {
// The activity went to background.
if let Some(cb) = self.suspend_callback.borrow().as_ref() {
(*cb)(true);
}
Some(Event::Suspended(true))
},
android_glue::Event::WindowResized |
android_glue::Event::ConfigChanged => {
Some(Event::Suspended)
}
android_glue::Event::WindowResized | android_glue::Event::ConfigChanged => {
// Activity Orientation changed or resized.
let native_window = unsafe { android_glue::native_window() };
if native_window.is_null() {
None
} else {
let dpi_factor = MonitorHandle.hidpi_factor();
let physical_size = MonitorHandle.dimensions();
let physical_size = MonitorHandle.size();
let size = LogicalSize::from_physical(physical_size, dpi_factor);
Some(Event::WindowEvent {
window_id: RootWindowId(WindowId),
event: WindowEvent::Resized(size),
})
}
},
}
android_glue::Event::WindowRedrawNeeded => {
// The activity needs to be redrawn.
Some(Event::WindowEvent {
@ -122,26 +116,23 @@ impl EventLoop {
event: WindowEvent::Redraw,
})
}
android_glue::Event::Wake => {
Some(Event::Awakened)
}
_ => {
None
}
android_glue::Event::Wake => Some(Event::Awakened),
_ => None,
};
if let Some(event) = e {
callback(event);
}
};
}
}
pub fn set_suspend_callback(&self, cb: Option<Box<Fn(bool) -> ()>>) {
pub fn set_suspend_callback(&self, cb: Option<Box<dyn Fn(bool) -> ()>>) {
*self.suspend_callback.borrow_mut() = cb;
}
pub fn run_forever<F>(&mut self, mut callback: F)
where F: FnMut(::Event) -> ::ControlFlow,
where
F: FnMut(::Event) -> ::ControlFlow,
{
// Yeah that's a very bad implementation.
loop {
@ -196,7 +187,7 @@ pub struct Window {
pub struct MonitorHandle;
impl fmt::Debug for MonitorHandle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[derive(Debug)]
struct MonitorHandle {
name: Option<String>,
@ -207,7 +198,7 @@ impl fmt::Debug for MonitorHandle {
let monitor_id_proxy = MonitorHandle {
name: self.name(),
dimensions: self.dimensions(),
dimensions: self.size(),
position: self.outer_position(),
hidpi_factor: self.hidpi_factor(),
};
@ -223,13 +214,14 @@ impl MonitorHandle {
}
#[inline]
pub fn dimensions(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize {
unsafe {
let window = android_glue::native_window();
(
ffi::ANativeWindow_getWidth(window) as f64,
ffi::ANativeWindow_getHeight(window) as f64,
).into()
)
.into()
}
}
@ -251,10 +243,11 @@ pub struct PlatformSpecificWindowBuilderAttributes;
pub struct PlatformSpecificHeadlessBuilderAttributes;
impl Window {
pub fn new(_: &EventLoop, win_attribs: WindowAttributes,
_: PlatformSpecificWindowBuilderAttributes)
-> Result<Window, CreationError>
{
pub fn new(
_: &EventLoop,
win_attribs: WindowAttributes,
_: PlatformSpecificWindowBuilderAttributes,
) -> Result<Window, CreationError> {
let native_window = unsafe { android_glue::native_window() };
if native_window.is_null() {
return Err(OsError(format!("Android's native window is null")));
@ -325,7 +318,7 @@ impl Window {
None
} else {
let dpi_factor = self.hidpi_factor();
let physical_size = self.current_monitor().dimensions();
let physical_size = self.current_monitor().size();
Some(LogicalSize::from_physical(physical_size, dpi_factor))
}
}
@ -406,7 +399,9 @@ impl Window {
#[inline]
pub fn current_monitor(&self) -> RootMonitorHandle {
RootMonitorHandle { inner: MonitorHandle }
RootMonitorHandle {
inner: MonitorHandle,
}
}
#[inline]

View file

@ -1,8 +1,8 @@
#![allow(dead_code, non_camel_case_types, non_snake_case)]
use std::os::raw::{c_int, c_char, c_void, c_ulong, c_double, c_long, c_ushort};
#[cfg(test)]
use std::mem;
use std::os::raw::{c_char, c_double, c_int, c_long, c_ulong, c_ushort, c_void};
pub type EM_BOOL = c_int;
pub type EM_UTF8 = c_char;
@ -71,30 +71,45 @@ pub const DOM_KEY_LOCATION_NUMPAD: c_ulong = 0x03;
pub type em_callback_func = Option<unsafe extern "C" fn()>;
pub type em_key_callback_func = Option<unsafe extern "C" fn(
eventType: c_int,
keyEvent: *const EmscriptenKeyboardEvent,
userData: *mut c_void) -> EM_BOOL>;
pub type em_key_callback_func = Option<
unsafe extern "C" fn(
eventType: c_int,
keyEvent: *const EmscriptenKeyboardEvent,
userData: *mut c_void,
) -> EM_BOOL,
>;
pub type em_mouse_callback_func = Option<unsafe extern "C" fn(
eventType: c_int,
mouseEvent: *const EmscriptenMouseEvent,
userData: *mut c_void) -> EM_BOOL>;
pub type em_mouse_callback_func = Option<
unsafe extern "C" fn(
eventType: c_int,
mouseEvent: *const EmscriptenMouseEvent,
userData: *mut c_void,
) -> EM_BOOL,
>;
pub type em_pointerlockchange_callback_func = Option<unsafe extern "C" fn(
eventType: c_int,
pointerlockChangeEvent: *const EmscriptenPointerlockChangeEvent,
userData: *mut c_void) -> EM_BOOL>;
pub type em_pointerlockchange_callback_func = Option<
unsafe extern "C" fn(
eventType: c_int,
pointerlockChangeEvent: *const EmscriptenPointerlockChangeEvent,
userData: *mut c_void,
) -> EM_BOOL,
>;
pub type em_fullscreenchange_callback_func = Option<unsafe extern "C" fn(
eventType: c_int,
fullscreenChangeEvent: *const EmscriptenFullscreenChangeEvent,
userData: *mut c_void) -> EM_BOOL>;
pub type em_fullscreenchange_callback_func = Option<
unsafe extern "C" fn(
eventType: c_int,
fullscreenChangeEvent: *const EmscriptenFullscreenChangeEvent,
userData: *mut c_void,
) -> EM_BOOL,
>;
pub type em_touch_callback_func = Option<unsafe extern "C" fn(
eventType: c_int,
touchEvent: *const EmscriptenTouchEvent,
userData: *mut c_void) -> EM_BOOL>;
pub type em_touch_callback_func = Option<
unsafe extern "C" fn(
eventType: c_int,
touchEvent: *const EmscriptenTouchEvent,
userData: *mut c_void,
) -> EM_BOOL,
>;
#[repr(C)]
pub struct EmscriptenFullscreenChangeEvent {
@ -136,7 +151,9 @@ fn bindgen_test_layout_EmscriptenKeyboardEvent() {
assert_eq!(mem::align_of::<EmscriptenKeyboardEvent>(), 8usize);
}
impl Clone for EmscriptenKeyboardEvent {
fn clone(&self) -> Self { *self }
fn clone(&self) -> Self {
*self
}
}
#[repr(C)]
@ -219,96 +236,128 @@ fn bindgen_test_layout_EmscriptenPointerlockChangeEvent() {
}
extern "C" {
pub fn emscripten_set_canvas_size(
width: c_int, height: c_int)
-> EMSCRIPTEN_RESULT;
pub fn emscripten_set_canvas_size(width: c_int, height: c_int) -> EMSCRIPTEN_RESULT;
pub fn emscripten_get_canvas_size(
width: *mut c_int, height: *mut c_int,
is_fullscreen: *mut c_int)
-> EMSCRIPTEN_RESULT;
width: *mut c_int,
height: *mut c_int,
is_fullscreen: *mut c_int,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_element_css_size(
target: *const c_char, width: c_double,
height: c_double) -> EMSCRIPTEN_RESULT;
target: *const c_char,
width: c_double,
height: c_double,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_get_element_css_size(
target: *const c_char, width: *mut c_double,
height: *mut c_double) -> EMSCRIPTEN_RESULT;
target: *const c_char,
width: *mut c_double,
height: *mut c_double,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_request_pointerlock(
target: *const c_char, deferUntilInEventHandler: EM_BOOL)
-> EMSCRIPTEN_RESULT;
target: *const c_char,
deferUntilInEventHandler: EM_BOOL,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_exit_pointerlock() -> EMSCRIPTEN_RESULT;
pub fn emscripten_request_fullscreen(
target: *const c_char, deferUntilInEventHandler: EM_BOOL)
-> EMSCRIPTEN_RESULT;
target: *const c_char,
deferUntilInEventHandler: EM_BOOL,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_exit_fullscreen() -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_keydown_callback(
target: *const c_char, userData: *mut c_void,
useCapture: EM_BOOL, callback: em_key_callback_func)
-> EMSCRIPTEN_RESULT;
target: *const c_char,
userData: *mut c_void,
useCapture: EM_BOOL,
callback: em_key_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_keyup_callback(
target: *const c_char, userData: *mut c_void,
useCapture: EM_BOOL, callback: em_key_callback_func)
-> EMSCRIPTEN_RESULT;
target: *const c_char,
userData: *mut c_void,
useCapture: EM_BOOL,
callback: em_key_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_mousemove_callback(
target: *const c_char, user_data: *mut c_void,
use_capture: EM_BOOL, callback: em_mouse_callback_func)
-> EMSCRIPTEN_RESULT;
target: *const c_char,
user_data: *mut c_void,
use_capture: EM_BOOL,
callback: em_mouse_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_mousedown_callback(
target: *const c_char, user_data: *mut c_void,
use_capture: EM_BOOL, callback: em_mouse_callback_func)
-> EMSCRIPTEN_RESULT;
target: *const c_char,
user_data: *mut c_void,
use_capture: EM_BOOL,
callback: em_mouse_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_mouseup_callback(
target: *const c_char, user_data: *mut c_void,
use_capture: EM_BOOL, callback: em_mouse_callback_func)
-> EMSCRIPTEN_RESULT;
target: *const c_char,
user_data: *mut c_void,
use_capture: EM_BOOL,
callback: em_mouse_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_hide_mouse();
pub fn emscripten_get_device_pixel_ratio() -> f64;
pub fn emscripten_set_pointerlockchange_callback(
target: *const c_char, userData: *mut c_void, useCapture: EM_BOOL,
callback: em_pointerlockchange_callback_func) -> EMSCRIPTEN_RESULT;
target: *const c_char,
userData: *mut c_void,
useCapture: EM_BOOL,
callback: em_pointerlockchange_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_fullscreenchange_callback(
target: *const c_char, userData: *mut c_void, useCapture: EM_BOOL,
callback: em_fullscreenchange_callback_func) -> EMSCRIPTEN_RESULT;
target: *const c_char,
userData: *mut c_void,
useCapture: EM_BOOL,
callback: em_fullscreenchange_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_asm_const(code: *const c_char);
pub fn emscripten_set_main_loop(
func: em_callback_func, fps: c_int, simulate_infinite_loop: EM_BOOL);
func: em_callback_func,
fps: c_int,
simulate_infinite_loop: EM_BOOL,
);
pub fn emscripten_cancel_main_loop();
pub fn emscripten_set_touchstart_callback(
target: *const c_char, userData: *mut c_void,
useCapture: c_int, callback: em_touch_callback_func)
-> EMSCRIPTEN_RESULT;
target: *const c_char,
userData: *mut c_void,
useCapture: c_int,
callback: em_touch_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_touchend_callback(
target: *const c_char, userData: *mut c_void,
useCapture: c_int, callback: em_touch_callback_func)
-> EMSCRIPTEN_RESULT;
target: *const c_char,
userData: *mut c_void,
useCapture: c_int,
callback: em_touch_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_touchmove_callback(
target: *const c_char, userData: *mut c_void,
useCapture: c_int, callback: em_touch_callback_func)
-> EMSCRIPTEN_RESULT;
target: *const c_char,
userData: *mut c_void,
useCapture: c_int,
callback: em_touch_callback_func,
) -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_touchcancel_callback(
target: *const c_char, userData: *mut c_void,
useCapture: c_int, callback: em_touch_callback_func)
-> EMSCRIPTEN_RESULT;
target: *const c_char,
userData: *mut c_void,
useCapture: c_int,
callback: em_touch_callback_func,
) -> EMSCRIPTEN_RESULT;
}

View file

@ -2,16 +2,23 @@
mod ffi;
use std::{mem, ptr, str};
use std::cell::RefCell;
use std::collections::VecDeque;
use std::os::raw::{c_char, c_void, c_double, c_ulong, c_int};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Mutex, Arc};
use std::{
cell::RefCell,
collections::VecDeque,
mem,
os::raw::{c_char, c_double, c_int, c_ulong, c_void},
ptr, str,
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
},
};
use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize};
use error::{ExternalError, NotSupportedError};
use window::MonitorHandle as RootMonitorHandle;
use crate::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize},
error::{ExternalError, NotSupportedError},
window::MonitorHandle as RootMonitorHandle,
};
const DOCUMENT_NAME: &'static str = "#document\0";
@ -54,7 +61,7 @@ impl MonitorHandle {
}
#[inline]
pub fn dimensions(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize {
(0, 0).into()
}
@ -68,14 +75,22 @@ impl MonitorHandle {
thread_local!(static MAIN_LOOP_CALLBACK: RefCell<*mut c_void> = RefCell::new(ptr::null_mut()));
// Used to assign a callback to emscripten main loop
pub fn set_main_loop_callback<F>(callback : F) where F : FnMut() {
pub fn set_main_loop_callback<F>(callback: F)
where
F: FnMut(),
{
MAIN_LOOP_CALLBACK.with(|log| {
*log.borrow_mut() = &callback as *const _ as *mut c_void;
});
unsafe { ffi::emscripten_set_main_loop(Some(wrapper::<F>), 0, 1); }
unsafe {
ffi::emscripten_set_main_loop(Some(wrapper::<F>), 0, 1);
}
unsafe extern "C" fn wrapper<F>() where F : FnMut() {
unsafe extern "C" fn wrapper<F>()
where
F: FnMut(),
{
MAIN_LOOP_CALLBACK.with(|z| {
let closure = *z.borrow_mut() as *mut F;
(*closure)();
@ -128,7 +143,8 @@ impl EventLoop {
}
pub fn poll_events<F>(&self, mut callback: F)
where F: FnMut(::Event)
where
F: FnMut(::Event),
{
let ref mut window = *self.window.lock().unwrap();
if let &mut Some(ref mut window) = window {
@ -139,17 +155,22 @@ impl EventLoop {
}
pub fn run_forever<F>(&self, mut callback: F)
where F: FnMut(::Event) -> ::ControlFlow
where
F: FnMut(::Event) -> ::ControlFlow,
{
self.interrupted.store(false, Ordering::Relaxed);
// TODO: handle control flow
set_main_loop_callback(|| {
self.poll_events(|e| { callback(e); });
self.poll_events(|e| {
callback(e);
});
::std::thread::sleep(::std::time::Duration::from_millis(5));
if self.interrupted.load(Ordering::Relaxed) {
unsafe { ffi::emscripten_cancel_main_loop(); }
unsafe {
ffi::emscripten_cancel_main_loop();
}
}
});
}
@ -190,15 +211,15 @@ fn show_mouse() {
// }
// styleSheet.insertRule('canvas.emscripten { border: none; cursor: auto; }', 0);
unsafe {
ffi::emscripten_asm_const(b"var styleSheet = document.styleSheets[0]; var rules = styleSheet.cssRules; for (var i = 0; i < rules.length; i++) { if (rules[i].cssText.substr(0, 6) == 'canvas') { styleSheet.deleteRule(i); i--; } } styleSheet.insertRule('canvas.emscripten { border: none; cursor: auto; }', 0);\0".as_ptr() as *const c_char);
ffi::emscripten_asm_const(b"var styleSheet = document.styleSheets[0]; var rules = styleSheet.cssRules; for (var i = 0; i < rules.length; i++) { if (rules[i].cssText.substr(0, 6) == 'canvas') { styleSheet.deleteRule(i); i--; } } styleSheet.insertRule('canvas.emscripten { border: none; cursor: auto; }', 0);\0".as_ptr() as *const c_char);
}
}
extern "C" fn mouse_callback(
event_type: c_int,
event: *const ffi::EmscriptenMouseEvent,
event_queue: *mut c_void) -> ffi::EM_BOOL
{
event_queue: *mut c_void,
) -> ffi::EM_BOOL {
unsafe {
let queue: &Mutex<VecDeque<::Event>> = mem::transmute(event_queue);
@ -221,18 +242,18 @@ extern "C" fn mouse_callback(
event: ::WindowEvent::CursorMoved {
device_id: ::DeviceId(DeviceId),
position,
modifiers: modifiers,
}
modifiers,
},
});
queue.lock().unwrap().push_back(::Event::DeviceEvent {
device_id: ::DeviceId(DeviceId),
event: ::DeviceEvent::MouseMotion {
delta: ((*event).movementX as f64, (*event).movementY as f64),
}
},
});
},
mouse_input @ ffi::EMSCRIPTEN_EVENT_MOUSEDOWN |
mouse_input @ ffi::EMSCRIPTEN_EVENT_MOUSEUP => {
}
mouse_input @ ffi::EMSCRIPTEN_EVENT_MOUSEDOWN
| mouse_input @ ffi::EMSCRIPTEN_EVENT_MOUSEUP => {
let button = match (*event).button {
0 => ::MouseButton::Left,
1 => ::MouseButton::Middle,
@ -248,14 +269,13 @@ extern "C" fn mouse_callback(
window_id: ::WindowId(WindowId(0)),
event: ::WindowEvent::MouseInput {
device_id: ::DeviceId(DeviceId),
state: state,
button: button,
modifiers: modifiers,
}
state,
button,
modifiers,
},
})
},
_ => {
}
_ => {}
}
}
ffi::EM_FALSE
@ -264,8 +284,8 @@ extern "C" fn mouse_callback(
extern "C" fn keyboard_callback(
event_type: c_int,
event: *const ffi::EmscriptenKeyboardEvent,
event_queue: *mut c_void) -> ffi::EM_BOOL
{
event_queue: *mut c_void,
) -> ffi::EM_BOOL {
unsafe {
let queue: &Mutex<VecDeque<::Event>> = mem::transmute(event_queue);
@ -290,7 +310,7 @@ extern "C" fn keyboard_callback(
},
},
});
},
}
ffi::EMSCRIPTEN_EVENT_KEYUP => {
queue.lock().unwrap().push_back(::Event::WindowEvent {
window_id: ::WindowId(WindowId(0)),
@ -304,19 +324,18 @@ extern "C" fn keyboard_callback(
},
},
});
},
_ => {
}
_ => {}
}
}
ffi::EM_FALSE
}
extern fn touch_callback(
extern "C" fn touch_callback(
event_type: c_int,
event: *const ffi::EmscriptenTouchEvent,
event_queue: *mut c_void) -> ffi::EM_BOOL
{
event_queue: *mut c_void,
) -> ffi::EM_BOOL {
unsafe {
let queue: &Mutex<VecDeque<::Event>> = mem::transmute(event_queue);
@ -356,8 +375,8 @@ extern fn touch_callback(
unsafe extern "C" fn fullscreen_callback(
_eventType: c_int,
_fullscreenChangeEvent: *const ffi::EmscriptenFullscreenChangeEvent,
_userData: *mut c_void) -> ffi::EM_BOOL
{
_userData: *mut c_void,
) -> ffi::EM_BOOL {
ffi::emscripten_request_fullscreen(ptr::null(), ffi::EM_TRUE);
ffi::EM_FALSE
}
@ -367,8 +386,8 @@ unsafe extern "C" fn fullscreen_callback(
unsafe extern "C" fn pointerlockchange_callback(
_eventType: c_int,
_pointerlockChangeEvent: *const ffi::EmscriptenPointerlockChangeEvent,
_userData: *mut c_void) -> ffi::EM_BOOL
{
_userData: *mut c_void,
) -> ffi::EM_BOOL {
ffi::emscripten_request_pointerlock(ptr::null(), ffi::EM_TRUE);
ffi::EM_FALSE
}
@ -381,12 +400,15 @@ fn em_try(res: ffi::EMSCRIPTEN_RESULT) -> Result<(), String> {
}
impl Window {
pub fn new(event_loop: &EventLoop, attribs: ::WindowAttributes,
_pl_attribs: PlatformSpecificWindowBuilderAttributes)
-> Result<Window, ::CreationError>
{
pub fn new(
event_loop: &EventLoop,
attribs: ::WindowAttributes,
_pl_attribs: PlatformSpecificWindowBuilderAttributes,
) -> Result<Window, ::CreationError> {
if event_loop.window.lock().unwrap().is_some() {
return Err(::CreationError::OsError("Cannot create another window".to_owned()));
return Err(::CreationError::OsError(
"Cannot create another window".to_owned(),
));
}
let w = Window2 {
@ -400,35 +422,87 @@ impl Window {
window: Arc::new(w),
};
// TODO: set up more event callbacks
unsafe {
em_try(ffi::emscripten_set_mousemove_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(mouse_callback)))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_mousedown_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(mouse_callback)))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_mouseup_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(mouse_callback)))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_keydown_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(keyboard_callback)))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_keyup_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(keyboard_callback)))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_touchstart_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(touch_callback)))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_touchend_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(touch_callback)))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_touchmove_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(touch_callback)))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_touchcancel_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(touch_callback)))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_mousemove_callback(
DOCUMENT_NAME.as_ptr() as *const c_char,
mem::transmute(&*window.window.events),
ffi::EM_FALSE,
Some(mouse_callback),
))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_mousedown_callback(
DOCUMENT_NAME.as_ptr() as *const c_char,
mem::transmute(&*window.window.events),
ffi::EM_FALSE,
Some(mouse_callback),
))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_mouseup_callback(
DOCUMENT_NAME.as_ptr() as *const c_char,
mem::transmute(&*window.window.events),
ffi::EM_FALSE,
Some(mouse_callback),
))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_keydown_callback(
DOCUMENT_NAME.as_ptr() as *const c_char,
mem::transmute(&*window.window.events),
ffi::EM_FALSE,
Some(keyboard_callback),
))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_keyup_callback(
DOCUMENT_NAME.as_ptr() as *const c_char,
mem::transmute(&*window.window.events),
ffi::EM_FALSE,
Some(keyboard_callback),
))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_touchstart_callback(
DOCUMENT_NAME.as_ptr() as *const c_char,
mem::transmute(&*window.window.events),
ffi::EM_FALSE,
Some(touch_callback),
))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_touchend_callback(
DOCUMENT_NAME.as_ptr() as *const c_char,
mem::transmute(&*window.window.events),
ffi::EM_FALSE,
Some(touch_callback),
))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_touchmove_callback(
DOCUMENT_NAME.as_ptr() as *const c_char,
mem::transmute(&*window.window.events),
ffi::EM_FALSE,
Some(touch_callback),
))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_touchcancel_callback(
DOCUMENT_NAME.as_ptr() as *const c_char,
mem::transmute(&*window.window.events),
ffi::EM_FALSE,
Some(touch_callback),
))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
}
if attribs.fullscreen.is_some() {
unsafe {
em_try(ffi::emscripten_request_fullscreen(ptr::null(), ffi::EM_TRUE))
.map_err(|e| ::CreationError::OsError(e))?;
em_try(ffi::emscripten_set_fullscreenchange_callback(ptr::null(), 0 as *mut c_void, ffi::EM_FALSE, Some(fullscreen_callback)))
.map_err(|e| ::CreationError::OsError(e))?;
em_try(ffi::emscripten_request_fullscreen(
ptr::null(),
ffi::EM_TRUE,
))
.map_err(|e| ::CreationError::OsError(e))?;
em_try(ffi::emscripten_set_fullscreenchange_callback(
ptr::null(),
0 as *mut c_void,
ffi::EM_FALSE,
Some(fullscreen_callback),
))
.map_err(|e| ::CreationError::OsError(e))?;
}
} else if let Some(size) = attribs.inner_size {
window.set_inner_size(size);
@ -444,8 +518,7 @@ impl Window {
}
#[inline]
pub fn set_title(&self, _title: &str) {
}
pub fn set_title(&self, _title: &str) {}
#[inline]
pub fn outer_position(&self) -> Option<LogicalPosition> {
@ -458,8 +531,7 @@ impl Window {
}
#[inline]
pub fn set_outer_position(&self, _: LogicalPosition) {
}
pub fn set_outer_position(&self, _: LogicalPosition) {}
#[inline]
pub fn inner_size(&self) -> Option<LogicalSize> {
@ -532,7 +604,9 @@ impl Window {
#[inline]
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
let mut grabbed_lock = self.window.cursor_grabbed.lock().unwrap();
if grab == *grabbed_lock { return Ok(()); }
if grab == *grabbed_lock {
return Ok(());
}
unsafe {
if grab {
em_try(ffi::emscripten_set_pointerlockchange_callback(
@ -541,7 +615,10 @@ impl Window {
ffi::EM_FALSE,
Some(pointerlockchange_callback),
))?;
em_try(ffi::emscripten_request_pointerlock(ptr::null(), ffi::EM_TRUE))?;
em_try(ffi::emscripten_request_pointerlock(
ptr::null(),
ffi::EM_TRUE,
))?;
} else {
em_try(ffi::emscripten_set_pointerlockchange_callback(
ptr::null(),
@ -559,7 +636,9 @@ impl Window {
#[inline]
pub fn set_cursor_visible(&self, visible: bool) {
let mut visible_lock = self.window.cursor_visible.lock().unwrap();
if visible == *visible_lock { return; }
if visible == *visible_lock {
return;
}
if visible {
show_mouse();
} else {
@ -615,7 +694,9 @@ impl Window {
#[inline]
pub fn current_monitor(&self) -> RootMonitorHandle {
RootMonitorHandle { inner: MonitorHandle }
RootMonitorHandle {
inner: MonitorHandle,
}
}
#[inline]
@ -646,21 +727,37 @@ impl Drop for Window {
// Exit fullscreen if on
if self.window.is_fullscreen {
ffi::emscripten_set_fullscreenchange_callback(ptr::null(), 0 as *mut c_void, ffi::EM_FALSE, None);
ffi::emscripten_set_fullscreenchange_callback(
ptr::null(),
0 as *mut c_void,
ffi::EM_FALSE,
None,
);
ffi::emscripten_exit_fullscreen();
}
// Delete callbacks
ffi::emscripten_set_keydown_callback(DOCUMENT_NAME.as_ptr() as *const c_char, 0 as *mut c_void, ffi::EM_FALSE,None);
ffi::emscripten_set_keyup_callback(DOCUMENT_NAME.as_ptr() as *const c_char, 0 as *mut c_void, ffi::EM_FALSE,None);
ffi::emscripten_set_keydown_callback(
DOCUMENT_NAME.as_ptr() as *const c_char,
0 as *mut c_void,
ffi::EM_FALSE,
None,
);
ffi::emscripten_set_keyup_callback(
DOCUMENT_NAME.as_ptr() as *const c_char,
0 as *mut c_void,
ffi::EM_FALSE,
None,
);
}
}
}
fn error_to_str(code: ffi::EMSCRIPTEN_RESULT) -> &'static str {
match code {
ffi::EMSCRIPTEN_RESULT_SUCCESS | ffi::EMSCRIPTEN_RESULT_DEFERRED
=> "Internal error in the library (success detected as failure)",
ffi::EMSCRIPTEN_RESULT_SUCCESS | ffi::EMSCRIPTEN_RESULT_DEFERRED => {
"Internal error in the library (success detected as failure)"
}
ffi::EMSCRIPTEN_RESULT_NOT_SUPPORTED => "Not supported",
ffi::EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED => "Failed not deferred",
@ -670,7 +767,7 @@ fn error_to_str(code: ffi::EMSCRIPTEN_RESULT) -> &'static str {
ffi::EMSCRIPTEN_RESULT_FAILED => "Failed",
ffi::EMSCRIPTEN_RESULT_NO_DATA => "No data",
_ => "Undocumented error"
_ => "Undocumented error",
}
}
@ -679,7 +776,9 @@ fn key_translate(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES]) ->
let maybe_key = unsafe { str::from_utf8(mem::transmute::<_, &[u8]>(slice)) };
let key = match maybe_key {
Ok(key) => key,
Err(_) => { return 0; },
Err(_) => {
return 0;
}
};
if key.chars().count() == 1 {
key.as_bytes()[0]
@ -688,14 +787,17 @@ fn key_translate(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES]) ->
}
}
fn key_translate_virt(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES],
location: c_ulong) -> Option<::VirtualKeyCode>
{
fn key_translate_virt(
input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES],
location: c_ulong,
) -> Option<::VirtualKeyCode> {
let slice = &input[0..input.iter().take_while(|x| **x != 0).count()];
let maybe_key = unsafe { str::from_utf8(mem::transmute::<_, &[u8]>(slice)) };
let key = match maybe_key {
Ok(key) => key,
Err(_) => { return None; },
Err(_) => {
return None;
}
};
use VirtualKeyCode::*;
match key {

View file

@ -1,26 +1,23 @@
use std::{mem, ptr};
use std::cell::{RefCell, RefMut};
use std::mem::ManuallyDrop;
use std::os::raw::c_void;
use std::time::Instant;
use std::{
cell::{RefCell, RefMut},
mem::{self, ManuallyDrop},
os::raw::c_void,
ptr,
time::Instant,
};
use event::{Event, StartCause};
use event_loop::ControlFlow;
use crate::{
event::{Event, StartCause},
event_loop::ControlFlow,
};
use platform_impl::platform::event_loop::{EventHandler, Never};
use platform_impl::platform::ffi::{
id,
CFAbsoluteTimeGetCurrent,
CFRelease,
CFRunLoopAddTimer,
CFRunLoopGetMain,
CFRunLoopRef,
CFRunLoopTimerCreate,
CFRunLoopTimerInvalidate,
CFRunLoopTimerRef,
CFRunLoopTimerSetNextFireDate,
kCFRunLoopCommonModes,
NSUInteger,
use crate::platform_impl::platform::{
event_loop::{EventHandler, Never},
ffi::{
id, kCFRunLoopCommonModes, CFAbsoluteTimeGetCurrent, CFRelease, CFRunLoopAddTimer,
CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate, CFRunLoopTimerInvalidate,
CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate, NSUInteger,
},
};
macro_rules! bug {
@ -39,10 +36,10 @@ enum AppStateImpl {
Launching {
queued_windows: Vec<id>,
queued_events: Vec<Event<Never>>,
queued_event_handler: Box<EventHandler>,
queued_event_handler: Box<dyn EventHandler>,
},
ProcessingEvents {
event_handler: Box<EventHandler>,
event_handler: Box<dyn EventHandler>,
active_control_flow: ControlFlow,
},
// special state to deal with reentrancy and prevent mutable aliasing.
@ -50,11 +47,11 @@ enum AppStateImpl {
queued_events: Vec<Event<Never>>,
},
Waiting {
waiting_event_handler: Box<EventHandler>,
waiting_event_handler: Box<dyn EventHandler>,
start: Instant,
},
PollFinished {
waiting_event_handler: Box<EventHandler>,
waiting_event_handler: Box<dyn EventHandler>,
},
Terminated,
}
@ -62,12 +59,18 @@ enum AppStateImpl {
impl Drop for AppStateImpl {
fn drop(&mut self) {
match self {
&mut AppStateImpl::NotLaunched { ref mut queued_windows, .. } |
&mut AppStateImpl::Launching { ref mut queued_windows, .. } => unsafe {
&mut AppStateImpl::NotLaunched {
ref mut queued_windows,
..
}
| &mut AppStateImpl::Launching {
ref mut queued_windows,
..
} => unsafe {
for &mut window in queued_windows {
let () = msg_send![window, release];
}
}
},
_ => {}
}
}
@ -88,7 +91,9 @@ impl AppState {
static mut APP_STATE: RefCell<Option<AppState>> = RefCell::new(None);
if cfg!(debug_assertions) {
assert_main_thread!("bug in winit: `AppState::get_mut()` can only be called on the main thread");
assert_main_thread!(
"bug in winit: `AppState::get_mut()` can only be called on the main thread"
);
}
let mut guard = APP_STATE.borrow_mut();
@ -108,33 +113,36 @@ impl AppState {
}
init_guard(&mut guard)
}
RefMut::map(guard, |state| {
state.as_mut().unwrap()
})
RefMut::map(guard, |state| state.as_mut().unwrap())
}
// requires main thread and window is a UIWindow
// retains window
pub unsafe fn set_key_window(window: id) {
let mut this = AppState::get_mut();
match &mut this.app_state {
&mut AppStateImpl::NotLaunched { ref mut queued_windows, .. } => {
&mut AppStateImpl::NotLaunched {
ref mut queued_windows,
..
} => {
queued_windows.push(window);
msg_send![window, retain];
return;
}
&mut AppStateImpl::ProcessingEvents { .. } => {},
&mut AppStateImpl::InUserCallback { .. } => {},
&mut AppStateImpl::Terminated => panic!("Attempt to create a `Window` \
after the app has terminated"),
app_state => unreachable!("unexpected state: {:#?}", app_state), // all other cases should be impossible
&mut AppStateImpl::ProcessingEvents { .. } => {}
&mut AppStateImpl::InUserCallback { .. } => {}
&mut AppStateImpl::Terminated => panic!(
"Attempt to create a `Window` \
after the app has terminated"
),
app_state => unreachable!("unexpected state: {:#?}", app_state), /* all other cases should be impossible */
}
drop(this);
msg_send![window, makeKeyAndVisible]
}
// requires main thread
pub unsafe fn will_launch(queued_event_handler: Box<EventHandler>) {
pub unsafe fn will_launch(queued_event_handler: Box<dyn EventHandler>) {
let mut this = AppState::get_mut();
let (queued_windows, queued_events) = match &mut this.app_state {
&mut AppStateImpl::NotLaunched {
@ -145,14 +153,19 @@ impl AppState {
let events = ptr::read(queued_events);
(windows, events)
}
_ => panic!("winit iOS expected the app to be in a `NotLaunched` \
state, but was not - please file an issue"),
_ => panic!(
"winit iOS expected the app to be in a `NotLaunched` \
state, but was not - please file an issue"
),
};
ptr::write(&mut this.app_state, AppStateImpl::Launching {
queued_windows,
queued_events,
queued_event_handler,
});
ptr::write(
&mut this.app_state,
AppStateImpl::Launching {
queued_windows,
queued_events,
queued_event_handler,
},
);
}
// requires main thread
@ -188,11 +201,11 @@ impl AppState {
let screen: id = msg_send![window, screen];
let () = msg_send![screen, retain];
let () = msg_send![window, setScreen:0 as id];
let () = msg_send![window, setScreen:screen];
let () = msg_send![window, setScreen: screen];
let () = msg_send![screen, release];
let controller: id = msg_send![window, rootViewController];
let () = msg_send![window, setRootViewController:ptr::null::<()>()];
let () = msg_send![window, setRootViewController:controller];
let () = msg_send![window, setRootViewController: controller];
let () = msg_send![window, makeKeyAndVisible];
}
let () = msg_send![window, release];
@ -210,15 +223,20 @@ impl AppState {
let event_handler = ptr::read(queued_event_handler);
(windows, events, event_handler)
}
_ => panic!("winit iOS expected the app to be in a `Launching` \
state, but was not - please file an issue"),
_ => panic!(
"winit iOS expected the app to be in a `Launching` \
state, but was not - please file an issue"
),
};
ptr::write(&mut this.app_state, AppStateImpl::ProcessingEvents {
event_handler,
active_control_flow: ControlFlow::Poll,
});
ptr::write(
&mut this.app_state,
AppStateImpl::ProcessingEvents {
event_handler,
active_control_flow: ControlFlow::Poll,
},
);
drop(this);
let events = std::iter::once(Event::NewEvents(StartCause::Init)).chain(events);
AppState::handle_nonuser_events(events);
@ -238,69 +256,79 @@ impl AppState {
// AppState::did_finish_launching handles the special transition `Init`
pub unsafe fn handle_wakeup_transition() {
let mut this = AppState::get_mut();
let event = match this.control_flow {
ControlFlow::Poll => {
let event_handler = match &mut this.app_state {
&mut AppStateImpl::NotLaunched { .. } |
&mut AppStateImpl::Launching { .. } => return,
&mut AppStateImpl::PollFinished {
ref mut waiting_event_handler,
} => ptr::read(waiting_event_handler),
_ => bug!("`EventHandler` unexpectedly started polling"),
};
ptr::write(&mut this.app_state, AppStateImpl::ProcessingEvents {
event_handler,
active_control_flow: ControlFlow::Poll,
});
Event::NewEvents(StartCause::Poll)
}
ControlFlow::Wait => {
let (event_handler, start) = match &mut this.app_state {
&mut AppStateImpl::NotLaunched { .. } |
&mut AppStateImpl::Launching { .. } => return,
&mut AppStateImpl::Waiting {
ref mut waiting_event_handler,
ref mut start,
} => (ptr::read(waiting_event_handler), *start),
_ => bug!("`EventHandler` unexpectedly woke up"),
};
ptr::write(&mut this.app_state, AppStateImpl::ProcessingEvents {
event_handler,
active_control_flow: ControlFlow::Wait,
});
Event::NewEvents(StartCause::WaitCancelled {
start,
requested_resume: None,
})
}
ControlFlow::WaitUntil(requested_resume) => {
let (event_handler, start) = match &mut this.app_state {
&mut AppStateImpl::NotLaunched { .. } |
&mut AppStateImpl::Launching { .. } => return,
&mut AppStateImpl::Waiting {
ref mut waiting_event_handler,
ref mut start,
} => (ptr::read(waiting_event_handler), *start),
_ => bug!("`EventHandler` unexpectedly woke up"),
};
ptr::write(&mut this.app_state, AppStateImpl::ProcessingEvents {
event_handler,
active_control_flow: ControlFlow::WaitUntil(requested_resume),
});
if Instant::now() >= requested_resume {
Event::NewEvents(StartCause::ResumeTimeReached {
start,
requested_resume,
})
} else {
let event =
match this.control_flow {
ControlFlow::Poll => {
let event_handler = match &mut this.app_state {
&mut AppStateImpl::NotLaunched { .. }
| &mut AppStateImpl::Launching { .. } => return,
&mut AppStateImpl::PollFinished {
ref mut waiting_event_handler,
} => ptr::read(waiting_event_handler),
_ => bug!("`EventHandler` unexpectedly started polling"),
};
ptr::write(
&mut this.app_state,
AppStateImpl::ProcessingEvents {
event_handler,
active_control_flow: ControlFlow::Poll,
},
);
Event::NewEvents(StartCause::Poll)
}
ControlFlow::Wait => {
let (event_handler, start) = match &mut this.app_state {
&mut AppStateImpl::NotLaunched { .. }
| &mut AppStateImpl::Launching { .. } => return,
&mut AppStateImpl::Waiting {
ref mut waiting_event_handler,
ref mut start,
} => (ptr::read(waiting_event_handler), *start),
_ => bug!("`EventHandler` unexpectedly woke up"),
};
ptr::write(
&mut this.app_state,
AppStateImpl::ProcessingEvents {
event_handler,
active_control_flow: ControlFlow::Wait,
},
);
Event::NewEvents(StartCause::WaitCancelled {
start,
requested_resume: Some(requested_resume),
requested_resume: None,
})
}
}
ControlFlow::Exit => bug!("unexpected controlflow `Exit`"),
};
ControlFlow::WaitUntil(requested_resume) => {
let (event_handler, start) = match &mut this.app_state {
&mut AppStateImpl::NotLaunched { .. }
| &mut AppStateImpl::Launching { .. } => return,
&mut AppStateImpl::Waiting {
ref mut waiting_event_handler,
ref mut start,
} => (ptr::read(waiting_event_handler), *start),
_ => bug!("`EventHandler` unexpectedly woke up"),
};
ptr::write(
&mut this.app_state,
AppStateImpl::ProcessingEvents {
event_handler,
active_control_flow: ControlFlow::WaitUntil(requested_resume),
},
);
if Instant::now() >= requested_resume {
Event::NewEvents(StartCause::ResumeTimeReached {
start,
requested_resume,
})
} else {
Event::NewEvents(StartCause::WaitCancelled {
start,
requested_resume: Some(requested_resume),
})
}
}
ControlFlow::Exit => bug!("unexpected controlflow `Exit`"),
};
drop(this);
AppState::handle_nonuser_event(event)
}
@ -328,7 +356,7 @@ impl AppState {
..
} => {
queued_events.extend(events);
return
return;
}
&mut AppStateImpl::ProcessingEvents {
ref mut event_handler,
@ -338,9 +366,12 @@ impl AppState {
| &mut AppStateImpl::Waiting { .. }
| &mut AppStateImpl::Terminated => bug!("unexpected attempted to process an event"),
};
ptr::write(&mut this.app_state, AppStateImpl::InUserCallback {
queued_events: Vec::new(),
});
ptr::write(
&mut this.app_state,
AppStateImpl::InUserCallback {
queued_events: Vec::new(),
},
);
drop(this);
for event in events {
@ -360,7 +391,7 @@ impl AppState {
active_control_flow,
};
this.control_flow = control_flow;
break
break;
}
drop(this);
for event in queued_events {
@ -384,9 +415,12 @@ impl AppState {
| &mut AppStateImpl::Waiting { .. }
| &mut AppStateImpl::Terminated => bug!("unexpected attempted to process an event"),
};
ptr::write(&mut this.app_state, AppStateImpl::InUserCallback {
queued_events: Vec::new(),
});
ptr::write(
&mut this.app_state,
AppStateImpl::InUserCallback {
queued_events: Vec::new(),
},
);
drop(this);
event_handler.handle_user_events(&mut control_flow);
@ -404,7 +438,7 @@ impl AppState {
active_control_flow,
};
this.control_flow = control_flow;
break
break;
}
drop(this);
for event in queued_events {
@ -432,20 +466,21 @@ impl AppState {
&mut AppStateImpl::ProcessingEvents {
ref mut event_handler,
ref mut active_control_flow,
} => (ManuallyDrop::new(ptr::read(event_handler)), *active_control_flow),
} => (
ManuallyDrop::new(ptr::read(event_handler)),
*active_control_flow,
),
_ => unreachable!(),
};
let new = this.control_flow;
match (old, new) {
(ControlFlow::Poll, ControlFlow::Poll) => {
ptr::write(
&mut this.app_state,
AppStateImpl::PollFinished {
waiting_event_handler: ManuallyDrop::into_inner(event_handler),
},
)
},
(ControlFlow::Poll, ControlFlow::Poll) => ptr::write(
&mut this.app_state,
AppStateImpl::PollFinished {
waiting_event_handler: ManuallyDrop::into_inner(event_handler),
},
),
(ControlFlow::Wait, ControlFlow::Wait) => {
let start = Instant::now();
ptr::write(
@ -455,9 +490,10 @@ impl AppState {
start,
},
)
},
}
(ControlFlow::WaitUntil(old_instant), ControlFlow::WaitUntil(new_instant))
if old_instant == new_instant => {
if old_instant == new_instant =>
{
let start = Instant::now();
ptr::write(
&mut this.app_state,
@ -477,7 +513,7 @@ impl AppState {
},
);
this.waker.stop()
},
}
(_, ControlFlow::WaitUntil(new_instant)) => {
let start = Instant::now();
ptr::write(
@ -488,7 +524,7 @@ impl AppState {
},
);
this.waker.start_at(new_instant)
},
}
(_, ControlFlow::Poll) => {
ptr::write(
&mut this.app_state,
@ -497,7 +533,7 @@ impl AppState {
},
);
this.waker.start()
},
}
(_, ControlFlow::Exit) => {
// https://developer.apple.com/library/archive/qa/qa1561/_index.html
// it is not possible to quit an iOS app gracefully and programatically
@ -511,7 +547,11 @@ impl AppState {
let mut this = unsafe { AppState::get_mut() };
let mut old = mem::replace(&mut this.app_state, AppStateImpl::Terminated);
let mut control_flow = this.control_flow;
if let AppStateImpl::ProcessingEvents { ref mut event_handler, .. } = old {
if let AppStateImpl::ProcessingEvents {
ref mut event_handler,
..
} = old
{
drop(this);
event_handler.handle_nonuser_event(Event::LoopDestroyed, &mut control_flow)
} else {
@ -535,7 +575,7 @@ impl Drop for EventLoopWaker {
impl EventLoopWaker {
fn new(rl: CFRunLoopRef) -> EventLoopWaker {
extern fn wakeup_main_loop(_timer: CFRunLoopTimerRef, _info: *mut c_void) {}
extern "C" fn wakeup_main_loop(_timer: CFRunLoopTimerRef, _info: *mut c_void) {}
unsafe {
// create a timer with a 1microsec interval (1ns does not work) to mimic polling.
// it is initially setup with a first fire time really far into the
@ -577,4 +617,4 @@ impl EventLoopWaker {
}
}
}
}
}

View file

@ -1,50 +1,33 @@
use std::{mem, ptr};
use std::collections::VecDeque;
use std::ffi::c_void;
use std::fmt::{self, Debug, Formatter};
use std::marker::PhantomData;
use std::sync::mpsc::{self, Sender, Receiver};
use event::Event;
use event_loop::{
ControlFlow,
EventLoopWindowTarget as RootEventLoopWindowTarget,
EventLoopClosed,
use std::{
collections::VecDeque,
ffi::c_void,
fmt::{self, Debug},
marker::PhantomData,
mem, ptr,
sync::mpsc::{self, Receiver, Sender},
};
use platform::ios::Idiom;
use platform_impl::platform::app_state::AppState;
use platform_impl::platform::ffi::{
id,
nil,
CFIndex,
CFRelease,
CFRunLoopActivity,
CFRunLoopAddObserver,
CFRunLoopAddSource,
CFRunLoopGetMain,
CFRunLoopObserverCreate,
CFRunLoopObserverRef,
CFRunLoopSourceContext,
CFRunLoopSourceCreate,
CFRunLoopSourceInvalidate,
CFRunLoopSourceRef,
CFRunLoopSourceSignal,
CFRunLoopWakeUp,
kCFRunLoopCommonModes,
kCFRunLoopDefaultMode,
kCFRunLoopEntry,
kCFRunLoopBeforeWaiting,
kCFRunLoopAfterWaiting,
kCFRunLoopExit,
NSOperatingSystemVersion,
NSString,
UIApplicationMain,
UIUserInterfaceIdiom,
use crate::{
event::Event,
event_loop::{
ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootEventLoopWindowTarget,
},
platform::ios::Idiom,
};
use crate::platform_impl::platform::{
app_state::AppState,
ffi::{
id, kCFRunLoopAfterWaiting, kCFRunLoopBeforeWaiting, kCFRunLoopCommonModes,
kCFRunLoopDefaultMode, kCFRunLoopEntry, kCFRunLoopExit, nil, CFIndex, CFRelease,
CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopAddSource, CFRunLoopGetMain,
CFRunLoopObserverCreate, CFRunLoopObserverRef, CFRunLoopSourceContext,
CFRunLoopSourceCreate, CFRunLoopSourceInvalidate, CFRunLoopSourceRef,
CFRunLoopSourceSignal, CFRunLoopWakeUp, NSOperatingSystemVersion, NSString,
UIApplicationMain, UIUserInterfaceIdiom,
},
monitor, view, MonitorHandle,
};
use platform_impl::platform::monitor;
use platform_impl::platform::MonitorHandle;
use platform_impl::platform::view;
pub struct EventLoopWindowTarget<T: 'static> {
receiver: Receiver<T>,
@ -67,8 +50,11 @@ impl<T: 'static> EventLoop<T> {
static mut SINGLETON_INIT: bool = false;
unsafe {
assert_main_thread!("`EventLoop` can only be created on the main thread on iOS");
assert!(!SINGLETON_INIT, "Only one `EventLoop` is supported on iOS. \
`EventLoopProxy` might be helpful");
assert!(
!SINGLETON_INIT,
"Only one `EventLoop` is supported on iOS. \
`EventLoopProxy` might be helpful"
);
SINGLETON_INIT = true;
view::create_delegate_class();
}
@ -92,25 +78,34 @@ impl<T: 'static> EventLoop<T> {
capabilities,
},
_marker: PhantomData,
}
},
}
}
pub fn run<F>(self, event_handler: F) -> !
where
F: 'static + FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow)
F: 'static + FnMut(Event<T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
{
unsafe {
let application: *mut c_void = msg_send![class!(UIApplication), sharedApplication];
assert_eq!(application, ptr::null_mut(), "\
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\
Note: `EventLoop::run` calls `UIApplicationMain` on iOS");
assert_eq!(
application,
ptr::null_mut(),
"\
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\
Note: `EventLoop::run` calls `UIApplicationMain` on iOS"
);
AppState::will_launch(Box::new(EventLoopHandler {
f: event_handler,
event_loop: self.window_target,
}));
UIApplicationMain(0, ptr::null(), nil, NSString::alloc(nil).init_str("AppDelegate"));
UIApplicationMain(
0,
ptr::null(),
nil,
NSString::alloc(nil).init_str("AppDelegate"),
);
unreachable!()
}
}
@ -121,16 +116,12 @@ impl<T: 'static> EventLoop<T> {
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
// guaranteed to be on main thread
unsafe {
monitor::uiscreens()
}
unsafe { monitor::uiscreens() }
}
pub fn primary_monitor(&self) -> MonitorHandle {
// guaranteed to be on main thread
unsafe {
monitor::main_uiscreen()
}
unsafe { monitor::main_uiscreen() }
}
pub fn window_target(&self) -> &RootEventLoopWindowTarget<T> {
@ -142,9 +133,7 @@ impl<T: 'static> EventLoop<T> {
impl<T: 'static> EventLoop<T> {
pub fn idiom(&self) -> Idiom {
// guaranteed to be on main thread
unsafe {
self::get_idiom()
}
unsafe { self::get_idiom() }
}
}
@ -183,18 +172,12 @@ impl<T> EventLoopProxy<T> {
// we want all the members of context to be zero/null, except one
let mut context: CFRunLoopSourceContext = mem::zeroed();
context.perform = event_loop_proxy_handler;
let source = CFRunLoopSourceCreate(
ptr::null_mut(),
CFIndex::max_value() - 1,
&mut context,
);
let source =
CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::max_value() - 1, &mut context);
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
CFRunLoopWakeUp(rl);
EventLoopProxy {
sender,
source,
}
EventLoopProxy { sender, source }
}
}
@ -213,7 +196,7 @@ impl<T> EventLoopProxy<T> {
fn setup_control_flow_observers() {
unsafe {
// begin is queued with the highest priority to ensure it is processed before other observers
extern fn control_flow_begin_handler(
extern "C" fn control_flow_begin_handler(
_: CFRunLoopObserverRef,
activity: CFRunLoopActivity,
_: *mut c_void,
@ -230,7 +213,7 @@ fn setup_control_flow_observers() {
// end is queued with the lowest priority to ensure it is processed after other observers
// without that, LoopDestroyed will get sent after EventsCleared
extern fn control_flow_end_handler(
extern "C" fn control_flow_end_handler(
_: CFRunLoopObserverRef,
activity: CFRunLoopActivity,
_: *mut c_void,
@ -281,8 +264,8 @@ struct EventLoopHandler<F, T: 'static> {
}
impl<F, T: 'static> Debug for EventLoopHandler<F, T> {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.debug_struct("EventLoopHandler")
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EventLoopHandler")
.field("event_loop", &self.event_loop)
.finish()
}
@ -303,11 +286,7 @@ where
fn handle_user_events(&mut self, control_flow: &mut ControlFlow) {
for event in self.event_loop.p.receiver.try_iter() {
(self.f)(
Event::UserEvent(event),
&self.event_loop,
control_flow,
);
(self.f)(Event::UserEvent(event), &self.event_loop, control_flow);
}
}
}
@ -325,7 +304,10 @@ pub struct Capabilities {
impl From<NSOperatingSystemVersion> for Capabilities {
fn from(os_version: NSOperatingSystemVersion) -> Capabilities {
assert!(os_version.major >= 8, "`winit` current requires iOS version 8 or greater");
assert!(
os_version.major >= 8,
"`winit` current requires iOS version 8 or greater"
);
let supports_safe_area = os_version.major >= 11;

View file

@ -1,13 +1,10 @@
#![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)]
use std::ffi::CString;
use std::ops::BitOr;
use std::os::raw::*;
use std::{ffi::CString, ops::BitOr, os::raw::*};
use objc::{Encode, Encoding};
use objc::runtime::Object;
use objc::{runtime::Object, Encode, Encoding};
use platform::ios::{Idiom, ValidOrientations};
use crate::platform::ios::{Idiom, ValidOrientations};
pub type id = *mut Object;
pub const nil: id = 0 as id;
@ -87,7 +84,9 @@ pub struct UIEdgeInsets {
pub struct UIUserInterfaceIdiom(NSInteger);
unsafe impl Encode for UIUserInterfaceIdiom {
fn encode() -> Encoding { NSInteger::encode() }
fn encode() -> Encoding {
NSInteger::encode()
}
}
impl UIUserInterfaceIdiom {
@ -128,7 +127,9 @@ impl Into<Idiom> for UIUserInterfaceIdiom {
pub struct UIInterfaceOrientationMask(NSUInteger);
unsafe impl Encode for UIInterfaceOrientationMask {
fn encode() -> Encoding { NSUInteger::encode() }
fn encode() -> Encoding {
NSUInteger::encode()
}
}
impl UIInterfaceOrientationMask {
@ -136,9 +137,12 @@ impl UIInterfaceOrientationMask {
pub const PortraitUpsideDown: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 2);
pub const LandscapeLeft: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 4);
pub const LandscapeRight: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 3);
pub const Landscape: UIInterfaceOrientationMask = UIInterfaceOrientationMask(Self::LandscapeLeft.0 | Self::LandscapeRight.0);
pub const AllButUpsideDown: UIInterfaceOrientationMask = UIInterfaceOrientationMask(Self::Landscape.0 | Self::Portrait.0);
pub const All: UIInterfaceOrientationMask = UIInterfaceOrientationMask(Self::AllButUpsideDown.0 | Self::PortraitUpsideDown.0);
pub const Landscape: UIInterfaceOrientationMask =
UIInterfaceOrientationMask(Self::LandscapeLeft.0 | Self::LandscapeRight.0);
pub const AllButUpsideDown: UIInterfaceOrientationMask =
UIInterfaceOrientationMask(Self::Landscape.0 | Self::Portrait.0);
pub const All: UIInterfaceOrientationMask =
UIInterfaceOrientationMask(Self::AllButUpsideDown.0 | Self::PortraitUpsideDown.0);
}
impl BitOr for UIInterfaceOrientationMask {
@ -155,18 +159,23 @@ impl UIInterfaceOrientationMask {
idiom: Idiom,
) -> UIInterfaceOrientationMask {
match (valid_orientations, idiom) {
(ValidOrientations::LandscapeAndPortrait, Idiom::Phone) => UIInterfaceOrientationMask::AllButUpsideDown,
(ValidOrientations::LandscapeAndPortrait, Idiom::Phone) => {
UIInterfaceOrientationMask::AllButUpsideDown
}
(ValidOrientations::LandscapeAndPortrait, _) => UIInterfaceOrientationMask::All,
(ValidOrientations::Landscape, _) => UIInterfaceOrientationMask::Landscape,
(ValidOrientations::Portrait, Idiom::Phone) => UIInterfaceOrientationMask::Portrait,
(ValidOrientations::Portrait, _) => UIInterfaceOrientationMask::Portrait | UIInterfaceOrientationMask::PortraitUpsideDown,
(ValidOrientations::Portrait, _) => {
UIInterfaceOrientationMask::Portrait
| UIInterfaceOrientationMask::PortraitUpsideDown
}
}
}
}
#[link(name = "UIKit", kind = "framework")]
#[link(name = "CoreFoundation", kind = "framework")]
extern {
extern "C" {
pub static kCFRunLoopDefaultMode: CFRunLoopMode;
pub static kCFRunLoopCommonModes: CFRunLoopMode;
@ -203,15 +212,8 @@ extern {
callout: CFRunLoopTimerCallBack,
context: *mut CFRunLoopTimerContext,
) -> CFRunLoopTimerRef;
pub fn CFRunLoopAddTimer(
rl: CFRunLoopRef,
timer: CFRunLoopTimerRef,
mode: CFRunLoopMode,
);
pub fn CFRunLoopTimerSetNextFireDate(
timer: CFRunLoopTimerRef,
fireDate: CFAbsoluteTime,
);
pub fn CFRunLoopAddTimer(rl: CFRunLoopRef, timer: CFRunLoopTimerRef, mode: CFRunLoopMode);
pub fn CFRunLoopTimerSetNextFireDate(timer: CFRunLoopTimerRef, fireDate: CFAbsoluteTime);
pub fn CFRunLoopTimerInvalidate(time: CFRunLoopTimerRef);
pub fn CFRunLoopSourceCreate(
@ -219,11 +221,7 @@ extern {
order: CFIndex,
context: *mut CFRunLoopSourceContext,
) -> CFRunLoopSourceRef;
pub fn CFRunLoopAddSource(
rl: CFRunLoopRef,
source: CFRunLoopSourceRef,
mode: CFRunLoopMode,
);
pub fn CFRunLoopAddSource(rl: CFRunLoopRef, source: CFRunLoopSourceRef, mode: CFRunLoopMode);
pub fn CFRunLoopSourceInvalidate(source: CFRunLoopSourceRef);
pub fn CFRunLoopSourceSignal(source: CFRunLoopSourceRef);
@ -259,15 +257,9 @@ pub const kCFRunLoopBeforeWaiting: CFRunLoopActivity = 1 << 5;
pub const kCFRunLoopAfterWaiting: CFRunLoopActivity = 1 << 6;
pub const kCFRunLoopExit: CFRunLoopActivity = 1 << 7;
pub type CFRunLoopObserverCallBack = extern "C" fn(
observer: CFRunLoopObserverRef,
activity: CFRunLoopActivity,
info: *mut c_void,
);
pub type CFRunLoopTimerCallBack = extern "C" fn(
timer: CFRunLoopTimerRef,
info: *mut c_void,
);
pub type CFRunLoopObserverCallBack =
extern "C" fn(observer: CFRunLoopObserverRef, activity: CFRunLoopActivity, info: *mut c_void);
pub type CFRunLoopTimerCallBack = extern "C" fn(timer: CFRunLoopTimerRef, info: *mut c_void);
pub enum CFRunLoopObserverContext {}
pub enum CFRunLoopTimerContext {}
@ -299,11 +291,11 @@ pub trait NSString: Sized {
impl NSString for id {
unsafe fn initWithUTF8String_(self, c_string: *const c_char) -> id {
msg_send![self, initWithUTF8String:c_string as id]
msg_send![self, initWithUTF8String: c_string as id]
}
unsafe fn stringByAppendingString_(self, other: id) -> id {
msg_send![self, stringByAppendingString:other]
msg_send![self, stringByAppendingString: other]
}
unsafe fn init_str(self, string: &str) -> id {

View file

@ -26,7 +26,6 @@
//! fn start_inner() {
//! ...
//! }
//!
//! ```
//!
//! Compile project and then drag resulting .a into Xcode project. Add winit.h to xcode.
@ -47,8 +46,8 @@
//!
//! This is how those event are represented in winit:
//!
//! - applicationDidBecomeActive is Suspended(false)
//! - applicationWillResignActive is Suspended(true)
//! - applicationDidBecomeActive is Resumed
//! - applicationWillResignActive is Suspended
//! - applicationWillTerminate is LoopDestroyed
//!
//! Keep in mind that after LoopDestroyed event is received every attempt to draw with
@ -78,12 +77,10 @@ mod window;
use std::fmt;
pub use self::event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget};
pub use self::monitor::MonitorHandle;
pub use self::window::{
PlatformSpecificWindowBuilderAttributes,
Window,
WindowId,
pub use self::{
event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget},
monitor::MonitorHandle,
window::{PlatformSpecificWindowBuilderAttributes, Window, WindowId},
};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -106,9 +103,9 @@ unsafe impl Sync for DeviceId {}
pub enum OsError {}
impl fmt::Display for OsError {
fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
_ => unreachable!()
_ => unreachable!(),
}
}
}

View file

@ -1,17 +1,16 @@
use std::{
collections::VecDeque,
collections::{HashSet, VecDeque},
fmt,
ops::{Deref, DerefMut},
};
use dpi::{PhysicalPosition, PhysicalSize};
use crate::{
dpi::{PhysicalPosition, PhysicalSize},
monitor::VideoMode,
};
use platform_impl::platform::ffi::{
id,
nil,
CGFloat,
CGRect,
NSUInteger,
use crate::platform_impl::platform::ffi::{
id, nil, CGFloat, CGRect, CGSize, NSInteger, NSUInteger,
};
pub struct Inner {
@ -35,7 +34,9 @@ impl Deref for MonitorHandle {
fn deref(&self) -> &Inner {
unsafe {
assert_main_thread!("`MonitorHandle` methods can only be run on the main thread on iOS");
assert_main_thread!(
"`MonitorHandle` methods can only be run on the main thread on iOS"
);
}
&self.inner
}
@ -44,7 +45,9 @@ impl Deref for MonitorHandle {
impl DerefMut for MonitorHandle {
fn deref_mut(&mut self) -> &mut Inner {
unsafe {
assert_main_thread!("`MonitorHandle` methods can only be run on the main thread on iOS");
assert_main_thread!(
"`MonitorHandle` methods can only be run on the main thread on iOS"
);
}
&mut self.inner
}
@ -68,18 +71,18 @@ impl Drop for MonitorHandle {
}
impl fmt::Debug for MonitorHandle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[derive(Debug)]
struct MonitorHandle {
name: Option<String>,
dimensions: PhysicalSize,
size: PhysicalSize,
position: PhysicalPosition,
hidpi_factor: f64,
}
let monitor_id_proxy = MonitorHandle {
name: self.name(),
dimensions: self.dimensions(),
size: self.size(),
position: self.position(),
hidpi_factor: self.hidpi_factor(),
};
@ -94,7 +97,9 @@ impl MonitorHandle {
assert_main_thread!("`MonitorHandle` can only be cloned on the main thread on iOS");
let () = msg_send![uiscreen, retain];
}
MonitorHandle { inner: Inner { uiscreen } }
MonitorHandle {
inner: Inner { uiscreen },
}
}
}
@ -114,7 +119,7 @@ impl Inner {
}
}
pub fn dimensions(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize {
unsafe {
let bounds: CGRect = msg_send![self.ui_screen(), nativeBounds];
(bounds.size.width as f64, bounds.size.height as f64).into()
@ -134,6 +139,27 @@ impl Inner {
scale as f64
}
}
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
let refresh_rate: NSInteger = unsafe { msg_send![self.uiscreen, maximumFramesPerSecond] };
let available_modes: id = unsafe { msg_send![self.uiscreen, availableModes] };
let available_mode_count: NSUInteger = unsafe { msg_send![available_modes, count] };
let mut modes = HashSet::with_capacity(available_mode_count);
for i in 0..available_mode_count {
let mode: id = unsafe { msg_send![available_modes, objectAtIndex: i] };
let size: CGSize = unsafe { msg_send![mode, size] };
modes.insert(VideoMode {
size: (size.width as u32, size.height as u32),
bit_depth: 32,
refresh_rate: refresh_rate as u16,
});
}
modes.into_iter()
}
}
// MonitorHandleExtIOS
@ -164,7 +190,7 @@ pub unsafe fn uiscreens() -> VecDeque<MonitorHandle> {
loop {
let screen: id = msg_send![screens_enum, nextObject];
if screen == nil {
break result
break result;
}
result.push_back(MonitorHandle::retained_new(screen));
}

View file

@ -1,31 +1,23 @@
use std::collections::HashMap;
use objc::declare::ClassDecl;
use objc::runtime::{BOOL, Class, NO, Object, Sel, YES};
use event::{
DeviceId as RootDeviceId,
Event,
Touch,
TouchPhase,
WindowEvent
use objc::{
declare::ClassDecl,
runtime::{Class, Object, Sel, BOOL, NO, YES},
};
use platform::ios::MonitorHandleExtIOS;
use window::{WindowAttributes, WindowId as RootWindowId};
use platform_impl::platform::app_state::AppState;
use platform_impl::platform::DeviceId;
use platform_impl::platform::event_loop;
use platform_impl::platform::ffi::{
id,
nil,
CGFloat,
CGPoint,
CGRect,
UIInterfaceOrientationMask,
UITouchPhase,
use crate::{
event::{DeviceId as RootDeviceId, Event, Touch, TouchPhase, WindowEvent},
platform::ios::MonitorHandleExtIOS,
window::{WindowAttributes, WindowId as RootWindowId},
};
use crate::platform_impl::platform::{
app_state::AppState,
event_loop,
ffi::{id, nil, CGFloat, CGPoint, CGRect, UIInterfaceOrientationMask, UITouchPhase},
window::PlatformSpecificWindowBuilderAttributes,
DeviceId,
};
use platform_impl::platform::window::{PlatformSpecificWindowBuilderAttributes};
// requires main thread
unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
@ -40,10 +32,13 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
classes.entry(root_view_class).or_insert_with(move || {
let uiview_class = class!(UIView);
let is_uiview: BOOL = msg_send![root_view_class, isSubclassOfClass:uiview_class];
assert_eq!(is_uiview, YES, "`root_view_class` must inherit from `UIView`");
let is_uiview: BOOL = msg_send![root_view_class, isSubclassOfClass: uiview_class];
assert_eq!(
is_uiview, YES,
"`root_view_class` must inherit from `UIView`"
);
extern fn draw_rect(object: &Object, _: Sel, rect: CGRect) {
extern "C" fn draw_rect(object: &Object, _: Sel, rect: CGRect) {
unsafe {
let window: id = msg_send![object, window];
AppState::handle_nonuser_event(Event::WindowEvent {
@ -55,13 +50,14 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
}
}
extern fn layout_subviews(object: &Object, _: Sel) {
extern "C" fn layout_subviews(object: &Object, _: Sel) {
unsafe {
let window: id = msg_send![object, window];
let bounds: CGRect = msg_send![window, bounds];
let screen: id = msg_send![window, screen];
let screen_space: id = msg_send![screen, coordinateSpace];
let screen_frame: CGRect = msg_send![object, convertRect:bounds toCoordinateSpace:screen_space];
let screen_frame: CGRect =
msg_send![object, convertRect:bounds toCoordinateSpace:screen_space];
let size = crate::dpi::LogicalSize {
width: screen_frame.size.width,
height: screen_frame.size.height,
@ -78,10 +74,14 @@ unsafe fn get_view_class(root_view_class: &'static Class) -> &'static Class {
let mut decl = ClassDecl::new(&format!("WinitUIView{}", ID), root_view_class)
.expect("Failed to declare class `WinitUIView`");
ID += 1;
decl.add_method(sel!(drawRect:),
draw_rect as extern fn(&Object, Sel, CGRect));
decl.add_method(sel!(layoutSubviews),
layout_subviews as extern fn(&Object, Sel));
decl.add_method(
sel!(drawRect:),
draw_rect as extern "C" fn(&Object, Sel, CGRect),
);
decl.add_method(
sel!(layoutSubviews),
layout_subviews as extern "C" fn(&Object, Sel),
);
decl.register()
})
}
@ -92,33 +92,39 @@ unsafe fn get_view_controller_class() -> &'static Class {
if CLASS.is_none() {
let uiviewcontroller_class = class!(UIViewController);
extern fn set_prefers_status_bar_hidden(object: &mut Object, _: Sel, hidden: BOOL) {
extern "C" fn set_prefers_status_bar_hidden(object: &mut Object, _: Sel, hidden: BOOL) {
unsafe {
object.set_ivar::<BOOL>("_prefers_status_bar_hidden", hidden);
let () = msg_send![object, setNeedsStatusBarAppearanceUpdate];
}
}
extern fn prefers_status_bar_hidden(object: &Object, _: Sel) -> BOOL {
unsafe {
*object.get_ivar::<BOOL>("_prefers_status_bar_hidden")
}
extern "C" fn prefers_status_bar_hidden(object: &Object, _: Sel) -> BOOL {
unsafe { *object.get_ivar::<BOOL>("_prefers_status_bar_hidden") }
}
extern fn set_supported_orientations(object: &mut Object, _: Sel, orientations: UIInterfaceOrientationMask) {
extern "C" fn set_supported_orientations(
object: &mut Object,
_: Sel,
orientations: UIInterfaceOrientationMask,
) {
unsafe {
object.set_ivar::<UIInterfaceOrientationMask>("_supported_orientations", orientations);
object.set_ivar::<UIInterfaceOrientationMask>(
"_supported_orientations",
orientations,
);
let () = msg_send![class!(UIViewController), attemptRotationToDeviceOrientation];
}
}
extern fn supported_orientations(object: &Object, _: Sel) -> UIInterfaceOrientationMask {
unsafe {
*object.get_ivar::<UIInterfaceOrientationMask>("_supported_orientations")
}
extern "C" fn supported_orientations(
object: &Object,
_: Sel,
) -> UIInterfaceOrientationMask {
unsafe { *object.get_ivar::<UIInterfaceOrientationMask>("_supported_orientations") }
}
extern fn should_autorotate(_: &Object, _: Sel) -> BOOL {
extern "C" fn should_autorotate(_: &Object, _: Sel) -> BOOL {
YES
}
@ -126,16 +132,27 @@ unsafe fn get_view_controller_class() -> &'static Class {
.expect("Failed to declare class `WinitUIViewController`");
decl.add_ivar::<BOOL>("_prefers_status_bar_hidden");
decl.add_ivar::<UIInterfaceOrientationMask>("_supported_orientations");
decl.add_method(sel!(setPrefersStatusBarHidden:),
set_prefers_status_bar_hidden as extern fn(&mut Object, Sel, BOOL));
decl.add_method(sel!(prefersStatusBarHidden),
prefers_status_bar_hidden as extern fn(&Object, Sel) -> BOOL);
decl.add_method(sel!(setSupportedInterfaceOrientations:),
set_supported_orientations as extern fn(&mut Object, Sel, UIInterfaceOrientationMask));
decl.add_method(sel!(supportedInterfaceOrientations),
supported_orientations as extern fn(&Object, Sel) -> UIInterfaceOrientationMask);
decl.add_method(sel!(shouldAutorotate),
should_autorotate as extern fn(&Object, Sel) -> BOOL);
decl.add_method(
sel!(setPrefersStatusBarHidden:),
set_prefers_status_bar_hidden as extern "C" fn(&mut Object, Sel, BOOL),
);
decl.add_method(
sel!(prefersStatusBarHidden),
prefers_status_bar_hidden as extern "C" fn(&Object, Sel) -> BOOL,
);
decl.add_method(
sel!(setSupportedInterfaceOrientations:),
set_supported_orientations
as extern "C" fn(&mut Object, Sel, UIInterfaceOrientationMask),
);
decl.add_method(
sel!(supportedInterfaceOrientations),
supported_orientations as extern "C" fn(&Object, Sel) -> UIInterfaceOrientationMask,
);
decl.add_method(
sel!(shouldAutorotate),
should_autorotate as extern "C" fn(&Object, Sel) -> BOOL,
);
CLASS = Some(decl.register());
}
CLASS.unwrap()
@ -147,7 +164,7 @@ unsafe fn get_window_class() -> &'static Class {
if CLASS.is_none() {
let uiwindow_class = class!(UIWindow);
extern fn become_key_window(object: &Object, _: Sel) {
extern "C" fn become_key_window(object: &Object, _: Sel) {
unsafe {
AppState::handle_nonuser_event(Event::WindowEvent {
window_id: RootWindowId(object.into()),
@ -157,7 +174,7 @@ unsafe fn get_window_class() -> &'static Class {
}
}
extern fn resign_key_window(object: &Object, _: Sel) {
extern "C" fn resign_key_window(object: &Object, _: Sel) {
unsafe {
AppState::handle_nonuser_event(Event::WindowEvent {
window_id: RootWindowId(object.into()),
@ -167,7 +184,7 @@ unsafe fn get_window_class() -> &'static Class {
}
}
extern fn handle_touches(object: &Object, _: Sel, touches: id, _:id) {
extern "C" fn handle_touches(object: &Object, _: Sel, touches: id, _: id) {
unsafe {
let uiscreen = msg_send![object, screen];
let touches_enum: id = msg_send![touches, objectEnumerator];
@ -175,9 +192,9 @@ unsafe fn get_window_class() -> &'static Class {
loop {
let touch: id = msg_send![touches_enum, nextObject];
if touch == nil {
break
break;
}
let location: CGPoint = msg_send![touch, locationInView:nil];
let location: CGPoint = msg_send![touch, locationInView: nil];
let touch_id = touch as u64;
let phase: UITouchPhase = msg_send![touch, phase];
let phase = match phase {
@ -203,16 +220,20 @@ unsafe fn get_window_class() -> &'static Class {
}
}
extern fn set_content_scale_factor(object: &mut Object, _: Sel, hidpi_factor: CGFloat) {
extern "C" fn set_content_scale_factor(object: &mut Object, _: Sel, hidpi_factor: CGFloat) {
unsafe {
let () = msg_send![super(object, class!(UIWindow)), setContentScaleFactor:hidpi_factor];
let () = msg_send![
super(object, class!(UIWindow)),
setContentScaleFactor: hidpi_factor
];
let view_controller: id = msg_send![object, rootViewController];
let view: id = msg_send![view_controller, view];
let () = msg_send![view, setContentScaleFactor:hidpi_factor];
let () = msg_send![view, setContentScaleFactor: hidpi_factor];
let bounds: CGRect = msg_send![object, bounds];
let screen: id = msg_send![object, screen];
let screen_space: id = msg_send![screen, coordinateSpace];
let screen_frame: CGRect = msg_send![object, convertRect:bounds toCoordinateSpace:screen_space];
let screen_frame: CGRect =
msg_send![object, convertRect:bounds toCoordinateSpace:screen_space];
let size = crate::dpi::LogicalSize {
width: screen_frame.size.width,
height: screen_frame.size.height,
@ -221,32 +242,47 @@ unsafe fn get_window_class() -> &'static Class {
std::iter::once(Event::WindowEvent {
window_id: RootWindowId(object.into()),
event: WindowEvent::HiDpiFactorChanged(hidpi_factor as _),
}).chain(std::iter::once(Event::WindowEvent {
})
.chain(std::iter::once(Event::WindowEvent {
window_id: RootWindowId(object.into()),
event: WindowEvent::Resized(size),
}))
})),
);
}
}
let mut decl = ClassDecl::new("WinitUIWindow", uiwindow_class)
.expect("Failed to declare class `WinitUIWindow`");
decl.add_method(sel!(becomeKeyWindow),
become_key_window as extern fn(&Object, Sel));
decl.add_method(sel!(resignKeyWindow),
resign_key_window as extern fn(&Object, Sel));
decl.add_method(
sel!(becomeKeyWindow),
become_key_window as extern "C" fn(&Object, Sel),
);
decl.add_method(
sel!(resignKeyWindow),
resign_key_window as extern "C" fn(&Object, Sel),
);
decl.add_method(sel!(touchesBegan:withEvent:),
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
decl.add_method(sel!(touchesMoved:withEvent:),
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
decl.add_method(sel!(touchesEnded:withEvent:),
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
decl.add_method(sel!(touchesCancelled:withEvent:),
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
decl.add_method(
sel!(touchesBegan:withEvent:),
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
);
decl.add_method(
sel!(touchesMoved:withEvent:),
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
);
decl.add_method(
sel!(touchesEnded:withEvent:),
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
);
decl.add_method(
sel!(touchesCancelled:withEvent:),
handle_touches as extern "C" fn(this: &Object, _: Sel, _: id, _: id),
);
decl.add_method(sel!(setContentScaleFactor:),
set_content_scale_factor as extern fn(&mut Object, Sel, CGFloat));
decl.add_method(
sel!(setContentScaleFactor:),
set_content_scale_factor as extern "C" fn(&mut Object, Sel, CGFloat),
);
CLASS = Some(decl.register());
}
@ -263,9 +299,9 @@ pub unsafe fn create_view(
let view: id = msg_send![class, alloc];
assert!(!view.is_null(), "Failed to create `UIView` instance");
let view: id = msg_send![view, initWithFrame:frame];
let view: id = msg_send![view, initWithFrame: frame];
assert!(!view.is_null(), "Failed to initialize `UIView` instance");
let () = msg_send![view, setMultipleTouchEnabled:YES];
let () = msg_send![view, setMultipleTouchEnabled: YES];
view
}
@ -279,9 +315,15 @@ pub unsafe fn create_view_controller(
let class = get_view_controller_class();
let view_controller: id = msg_send![class, alloc];
assert!(!view_controller.is_null(), "Failed to create `UIViewController` instance");
assert!(
!view_controller.is_null(),
"Failed to create `UIViewController` instance"
);
let view_controller: id = msg_send![view_controller, init];
assert!(!view_controller.is_null(), "Failed to initialize `UIViewController` instance");
assert!(
!view_controller.is_null(),
"Failed to initialize `UIViewController` instance"
);
let status_bar_hidden = if window_attributes.decorations {
NO
} else {
@ -292,9 +334,15 @@ pub unsafe fn create_view_controller(
platform_attributes.valid_orientations,
idiom,
);
let () = msg_send![view_controller, setPrefersStatusBarHidden:status_bar_hidden];
let () = msg_send![view_controller, setSupportedInterfaceOrientations:supported_orientations];
let () = msg_send![view_controller, setView:view];
let () = msg_send![
view_controller,
setPrefersStatusBarHidden: status_bar_hidden
];
let () = msg_send![
view_controller,
setSupportedInterfaceOrientations: supported_orientations
];
let () = msg_send![view_controller, setView: view];
view_controller
}
@ -309,11 +357,14 @@ pub unsafe fn create_window(
let window: id = msg_send![class, alloc];
assert!(!window.is_null(), "Failed to create `UIWindow` instance");
let window: id = msg_send![window, initWithFrame:frame];
assert!(!window.is_null(), "Failed to initialize `UIWindow` instance");
let () = msg_send![window, setRootViewController:view_controller];
let window: id = msg_send![window, initWithFrame: frame];
assert!(
!window.is_null(),
"Failed to initialize `UIWindow` instance"
);
let () = msg_send![window, setRootViewController: view_controller];
if let Some(hidpi_factor) = platform_attributes.hidpi_factor {
let () = msg_send![window, setContentScaleFactor:hidpi_factor as CGFloat];
let () = msg_send![window, setContentScaleFactor: hidpi_factor as CGFloat];
}
if let &Some(ref monitor) = &window_attributes.fullscreen {
let () = msg_send![window, setScreen:monitor.ui_screen()];
@ -323,29 +374,25 @@ pub unsafe fn create_window(
}
pub fn create_delegate_class() {
extern fn did_finish_launching(_: &mut Object, _: Sel, _: id, _: id) -> BOOL {
extern "C" fn did_finish_launching(_: &mut Object, _: Sel, _: id, _: id) -> BOOL {
unsafe {
AppState::did_finish_launching();
}
YES
}
extern fn did_become_active(_: &Object, _: Sel, _: id) {
unsafe {
AppState::handle_nonuser_event(Event::Suspended(false))
}
extern "C" fn did_become_active(_: &Object, _: Sel, _: id) {
unsafe { AppState::handle_nonuser_event(Event::Resumed) }
}
extern fn will_resign_active(_: &Object, _: Sel, _: id) {
unsafe {
AppState::handle_nonuser_event(Event::Suspended(true))
}
extern "C" fn will_resign_active(_: &Object, _: Sel, _: id) {
unsafe { AppState::handle_nonuser_event(Event::Suspended) }
}
extern fn will_enter_foreground(_: &Object, _: Sel, _: id) {}
extern fn did_enter_background(_: &Object, _: Sel, _: id) {}
extern "C" fn will_enter_foreground(_: &Object, _: Sel, _: id) {}
extern "C" fn did_enter_background(_: &Object, _: Sel, _: id) {}
extern fn will_terminate(_: &Object, _: Sel, _: id) {
extern "C" fn will_terminate(_: &Object, _: Sel, _: id) {
unsafe {
let app: id = msg_send![class!(UIApplication), sharedApplication];
let windows: id = msg_send![app, windows];
@ -354,9 +401,9 @@ pub fn create_delegate_class() {
loop {
let window: id = msg_send![windows_enum, nextObject];
if window == nil {
break
break;
}
let is_winit_window: BOOL = msg_send![window, isKindOfClass:class!(WinitUIWindow)];
let is_winit_window: BOOL = msg_send![window, isKindOfClass: class!(WinitUIWindow)];
if is_winit_window == YES {
events.push(Event::WindowEvent {
window_id: RootWindowId(window.into()),
@ -370,23 +417,36 @@ pub fn create_delegate_class() {
}
let ui_responder = class!(UIResponder);
let mut decl = ClassDecl::new("AppDelegate", ui_responder).expect("Failed to declare class `AppDelegate`");
let mut decl =
ClassDecl::new("AppDelegate", ui_responder).expect("Failed to declare class `AppDelegate`");
unsafe {
decl.add_method(sel!(application:didFinishLaunchingWithOptions:),
did_finish_launching as extern fn(&mut Object, Sel, id, id) -> BOOL);
decl.add_method(
sel!(application:didFinishLaunchingWithOptions:),
did_finish_launching as extern "C" fn(&mut Object, Sel, id, id) -> BOOL,
);
decl.add_method(sel!(applicationDidBecomeActive:),
did_become_active as extern fn(&Object, Sel, id));
decl.add_method(sel!(applicationWillResignActive:),
will_resign_active as extern fn(&Object, Sel, id));
decl.add_method(sel!(applicationWillEnterForeground:),
will_enter_foreground as extern fn(&Object, Sel, id));
decl.add_method(sel!(applicationDidEnterBackground:),
did_enter_background as extern fn(&Object, Sel, id));
decl.add_method(
sel!(applicationDidBecomeActive:),
did_become_active as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(applicationWillResignActive:),
will_resign_active as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(applicationWillEnterForeground:),
will_enter_foreground as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(applicationDidEnterBackground:),
did_enter_background as extern "C" fn(&Object, Sel, id),
);
decl.add_method(sel!(applicationWillTerminate:),
will_terminate as extern fn(&Object, Sel, id));
decl.add_method(
sel!(applicationWillTerminate:),
will_terminate as extern "C" fn(&Object, Sel, id),
);
decl.register();
}

View file

@ -3,35 +3,21 @@ use std::{
ops::{Deref, DerefMut},
};
use objc::runtime::{Class, NO, Object, YES};
use objc::runtime::{Class, Object, NO, YES};
use dpi::{self, LogicalPosition, LogicalSize};
use error::{ExternalError, NotSupportedError, OsError as RootOsError};
use icon::Icon;
use monitor::MonitorHandle as RootMonitorHandle;
use platform::ios::{MonitorHandleExtIOS, ValidOrientations};
use window::{
CursorIcon,
WindowAttributes,
};
use platform_impl::{
platform::{
use crate::{
dpi::{self, LogicalPosition, LogicalSize},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
icon::Icon,
monitor::MonitorHandle as RootMonitorHandle,
platform::ios::{MonitorHandleExtIOS, ValidOrientations},
platform_impl::platform::{
app_state::AppState,
event_loop,
ffi::{
id,
CGFloat,
CGPoint,
CGRect,
CGSize,
UIEdgeInsets,
UIInterfaceOrientationMask,
},
monitor,
view,
EventLoopWindowTarget,
MonitorHandle
ffi::{id, CGFloat, CGPoint, CGRect, CGSize, UIEdgeInsets, UIInterfaceOrientationMask},
monitor, view, EventLoopWindowTarget, MonitorHandle,
},
window::{CursorIcon, WindowAttributes},
};
pub struct Inner {
@ -59,10 +45,10 @@ impl Inner {
pub fn set_visible(&self, visible: bool) {
match visible {
true => unsafe {
let () = msg_send![self.window, setHidden:NO];
let () = msg_send![self.window, setHidden: NO];
},
false => unsafe {
let () = msg_send![self.window, setHidden:YES];
let () = msg_send![self.window, setHidden: YES];
},
}
}
@ -104,7 +90,7 @@ impl Inner {
size: screen_frame.size,
};
let bounds = self.from_screen_space(new_screen_frame);
let () = msg_send![self.window, setBounds:bounds];
let () = msg_send![self.window, setBounds: bounds];
}
}
@ -181,9 +167,9 @@ impl Inner {
// this is pretty slow on iOS, so avoid doing it if we can
if uiscreen != current {
let () = msg_send![self.window, setScreen:uiscreen];
let () = msg_send![self.window, setScreen: uiscreen];
}
let () = msg_send![self.window, setFrame:bounds];
let () = msg_send![self.window, setFrame: bounds];
}
None => warn!("`Window::set_fullscreen(None)` ignored on iOS"),
}
@ -213,7 +199,10 @@ impl Inner {
pub fn set_decorations(&self, decorations: bool) {
unsafe {
let status_bar_hidden = if decorations { NO } else { YES };
let () = msg_send![self.view_controller, setPrefersStatusBarHidden:status_bar_hidden];
let () = msg_send![
self.view_controller,
setPrefersStatusBarHidden: status_bar_hidden
];
}
}
@ -232,20 +221,18 @@ impl Inner {
pub fn current_monitor(&self) -> RootMonitorHandle {
unsafe {
let uiscreen: id = msg_send![self.window, screen];
RootMonitorHandle { inner: MonitorHandle::retained_new(uiscreen) }
RootMonitorHandle {
inner: MonitorHandle::retained_new(uiscreen),
}
}
}
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
unsafe {
monitor::uiscreens()
}
unsafe { monitor::uiscreens() }
}
pub fn primary_monitor(&self) -> MonitorHandle {
unsafe {
monitor::main_uiscreen()
}
unsafe { monitor::main_uiscreen() }
}
pub fn id(&self) -> WindowId {
@ -306,7 +293,8 @@ impl Window {
// TODO: transparency, visible
unsafe {
let screen = window_attributes.fullscreen
let screen = window_attributes
.fullscreen
.as_ref()
.map(|screen| screen.ui_screen() as _)
.unwrap_or_else(|| monitor::main_uiscreen().ui_screen());
@ -315,14 +303,23 @@ impl Window {
let frame = match window_attributes.inner_size {
Some(dim) => CGRect {
origin: screen_bounds.origin,
size: CGSize { width: dim.width, height: dim.height },
size: CGSize {
width: dim.width,
height: dim.height,
},
},
None => screen_bounds,
};
let view = view::create_view(&window_attributes, &platform_attributes, frame.clone());
let view_controller = view::create_view_controller(&window_attributes, &platform_attributes, view);
let window = view::create_window(&window_attributes, &platform_attributes, frame, view_controller);
let view_controller =
view::create_view_controller(&window_attributes, &platform_attributes, view);
let window = view::create_window(
&window_attributes,
&platform_attributes,
frame,
view_controller,
);
let supports_safe_area = event_loop.capabilities().supports_safe_area;
@ -342,23 +339,38 @@ impl Window {
// WindowExtIOS
impl Inner {
pub fn ui_window(&self) -> id { self.window }
pub fn ui_view_controller(&self) -> id { self.view_controller }
pub fn ui_view(&self) -> id { self.view }
pub fn ui_window(&self) -> id {
self.window
}
pub fn ui_view_controller(&self) -> id {
self.view_controller
}
pub fn ui_view(&self) -> id {
self.view
}
pub fn set_hidpi_factor(&self, hidpi_factor: f64) {
unsafe {
assert!(dpi::validate_hidpi_factor(hidpi_factor), "`WindowExtIOS::set_hidpi_factor` received an invalid hidpi factor");
assert!(
dpi::validate_hidpi_factor(hidpi_factor),
"`WindowExtIOS::set_hidpi_factor` received an invalid hidpi factor"
);
let hidpi_factor = hidpi_factor as CGFloat;
let () = msg_send![self.view, setContentScaleFactor:hidpi_factor];
let () = msg_send![self.view, setContentScaleFactor: hidpi_factor];
}
}
pub fn set_valid_orientations(&self, valid_orientations: ValidOrientations) {
unsafe {
let idiom = event_loop::get_idiom();
let supported_orientations = UIInterfaceOrientationMask::from_valid_orientations_idiom(valid_orientations, idiom);
msg_send![self.view_controller, setSupportedInterfaceOrientations:supported_orientations];
let supported_orientations = UIInterfaceOrientationMask::from_valid_orientations_idiom(
valid_orientations,
idiom,
);
msg_send![
self.view_controller,
setSupportedInterfaceOrientations: supported_orientations
];
}
}
}
@ -411,14 +423,18 @@ impl Inner {
let screen_frame = self.to_screen_space(bounds);
let status_bar_frame: CGRect = {
let app: id = msg_send![class!(UIApplication), sharedApplication];
assert!(!app.is_null(), "`Window::get_inner_position` cannot be called before `EventLoop::run` on iOS");
assert!(
!app.is_null(),
"`Window::get_inner_position` cannot be called before `EventLoop::run` on iOS"
);
msg_send![app, statusBarFrame]
};
let (y, height) = if screen_frame.origin.y > status_bar_frame.size.height {
(screen_frame.origin.y, screen_frame.size.height)
} else {
let y = status_bar_frame.size.height;
let height = screen_frame.size.height - (status_bar_frame.size.height - screen_frame.origin.y);
let height = screen_frame.size.height
- (status_bar_frame.size.height - screen_frame.origin.y);
(y, height)
};
CGRect {
@ -429,7 +445,7 @@ impl Inner {
size: CGSize {
width: screen_frame.size.width,
height,
}
},
}
}
}
@ -453,13 +469,17 @@ unsafe impl Sync for WindowId {}
impl From<&Object> for WindowId {
fn from(window: &Object) -> WindowId {
WindowId { window: window as *const _ as _ }
WindowId {
window: window as *const _ as _,
}
}
}
impl From<&mut Object> for WindowId {
fn from(window: &mut Object) -> WindowId {
WindowId { window: window as _ }
WindowId {
window: window as _,
}
}
}

View file

@ -1,13 +1,13 @@
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
#![allow(dead_code)]
use std::os::raw::{c_void, c_char, c_int};
use std::os::raw::{c_char, c_int, c_void};
pub const RTLD_LAZY: c_int = 0x001;
pub const RTLD_NOW: c_int = 0x002;
#[link(name ="dl")]
extern {
#[link(name = "dl")]
extern "C" {
pub fn dlopen(filename: *const c_char, flag: c_int) -> *mut c_void;
pub fn dlerror() -> *mut c_char;
pub fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;

View file

@ -1,24 +1,21 @@
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
use std::collections::VecDeque;
use std::{env, mem, fmt};
use std::ffi::CStr;
use std::os::raw::*;
use std::sync::Arc;
use std::{collections::VecDeque, env, ffi::CStr, fmt, mem, os::raw::*, sync::Arc};
use parking_lot::Mutex;
use sctk::reexports::client::ConnectError;
use smithay_client_toolkit::reexports::client::ConnectError;
use dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize};
use icon::Icon;
use error::{ExternalError, NotSupportedError, OsError as RootOsError};
use event::Event;
use event_loop::{EventLoopClosed, ControlFlow, EventLoopWindowTarget as RootELW};
use monitor::MonitorHandle as RootMonitorHandle;
use window::{WindowAttributes, CursorIcon};
use self::x11::{XConnection, XError};
use self::x11::ffi::XVisualInfo;
pub use self::x11::XNotSupported;
use self::x11::{ffi::XVisualInfo, XConnection, XError};
use crate::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
event::Event,
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
icon::Icon,
monitor::{MonitorHandle as RootMonitorHandle, VideoMode},
window::{CursorIcon, WindowAttributes},
};
mod dlopen;
pub mod wayland;
@ -43,14 +40,13 @@ pub struct PlatformSpecificWindowBuilderAttributes {
pub override_redirect: bool,
pub x11_window_type: x11::util::WindowType,
pub gtk_theme_variant: Option<String>,
pub app_id: Option<String>
pub app_id: Option<String>,
}
lazy_static!(
pub static ref X11_BACKEND: Mutex<Result<Arc<XConnection>, XNotSupported>> = {
Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new))
};
);
lazy_static! {
pub static ref X11_BACKEND: Mutex<Result<Arc<XConnection>, XNotSupported>> =
{ Mutex::new(XConnection::new(Some(x_error_callback)).map(Arc::new)) };
}
#[derive(Debug, Clone)]
pub enum OsError {
@ -59,10 +55,10 @@ pub enum OsError {
}
impl fmt::Display for OsError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match self {
OsError::XError(e) => formatter.pad(&e.description),
OsError::XMisc(e) => formatter.pad(e),
OsError::XError(e) => f.pad(&e.description),
OsError::XMisc(e) => f.pad(e),
}
}
}
@ -120,10 +116,10 @@ impl MonitorHandle {
}
#[inline]
pub fn dimensions(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize {
match self {
&MonitorHandle::X(ref m) => m.dimensions(),
&MonitorHandle::Wayland(ref m) => m.dimensions(),
&MonitorHandle::X(ref m) => m.size(),
&MonitorHandle::Wayland(ref m) => m.size(),
}
}
@ -142,6 +138,14 @@ impl MonitorHandle {
&MonitorHandle::Wayland(ref m) => m.hidpi_factor() as f64,
}
}
#[inline]
pub fn video_modes(&self) -> Box<dyn Iterator<Item = VideoMode>> {
match self {
MonitorHandle::X(m) => Box::new(m.video_modes()),
MonitorHandle::Wayland(m) => Box::new(m.video_modes()),
}
}
}
impl Window {
@ -154,10 +158,10 @@ impl Window {
match *window_target {
EventLoopWindowTarget::Wayland(ref window_target) => {
wayland::Window::new(window_target, attribs, pl_attribs).map(Window::Wayland)
},
}
EventLoopWindowTarget::X(ref window_target) => {
x11::Window::new(window_target, attribs, pl_attribs).map(Window::X)
},
}
}
}
@ -261,7 +265,7 @@ impl Window {
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
match self {
&Window::X(ref w) => w.set_cursor_icon(cursor),
&Window::Wayland(ref w) => w.set_cursor_icon(cursor)
&Window::Wayland(ref w) => w.set_cursor_icon(cursor),
}
}
@ -283,7 +287,7 @@ impl Window {
#[inline]
pub fn hidpi_factor(&self) -> f64 {
match self {
match self {
&Window::X(ref w) => w.hidpi_factor(),
&Window::Wayland(ref w) => w.hidpi_factor() as f64,
}
@ -309,8 +313,9 @@ impl Window {
pub fn fullscreen(&self) -> Option<RootMonitorHandle> {
match self {
&Window::X(ref w) => w.fullscreen(),
&Window::Wayland(ref w) => w.fullscreen()
.map(|monitor_id| RootMonitorHandle { inner: MonitorHandle::Wayland(monitor_id) })
&Window::Wayland(ref w) => w.fullscreen().map(|monitor_id| RootMonitorHandle {
inner: MonitorHandle::Wayland(monitor_id),
}),
}
}
@ -318,7 +323,7 @@ impl Window {
pub fn set_fullscreen(&self, monitor: Option<RootMonitorHandle>) {
match self {
&Window::X(ref w) => w.set_fullscreen(monitor),
&Window::Wayland(ref w) => w.set_fullscreen(monitor)
&Window::Wayland(ref w) => w.set_fullscreen(monitor),
}
}
@ -326,7 +331,7 @@ impl Window {
pub fn set_decorations(&self, decorations: bool) {
match self {
&Window::X(ref w) => w.set_decorations(decorations),
&Window::Wayland(ref w) => w.set_decorations(decorations)
&Window::Wayland(ref w) => w.set_decorations(decorations),
}
}
@ -365,19 +370,25 @@ impl Window {
#[inline]
pub fn current_monitor(&self) -> RootMonitorHandle {
match self {
&Window::X(ref window) => RootMonitorHandle { inner: MonitorHandle::X(window.current_monitor()) },
&Window::Wayland(ref window) => RootMonitorHandle { inner: MonitorHandle::Wayland(window.current_monitor()) },
&Window::X(ref window) => RootMonitorHandle {
inner: MonitorHandle::X(window.current_monitor()),
},
&Window::Wayland(ref window) => RootMonitorHandle {
inner: MonitorHandle::Wayland(window.current_monitor()),
},
}
}
#[inline]
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
match self {
&Window::X(ref window) => window.available_monitors()
&Window::X(ref window) => window
.available_monitors()
.into_iter()
.map(MonitorHandle::X)
.collect(),
&Window::Wayland(ref window) => window.available_monitors()
&Window::Wayland(ref window) => window
.available_monitors()
.into_iter()
.map(MonitorHandle::Wayland)
.collect(),
@ -393,7 +404,6 @@ impl Window {
}
}
unsafe extern "C" fn x_error_callback(
display: *mut x11::ffi::Display,
event: *mut x11::ffi::XErrorEvent,
@ -424,10 +434,9 @@ unsafe extern "C" fn x_error_callback(
0
}
pub enum EventLoop<T: 'static> {
Wayland(wayland::EventLoop<T>),
X(x11::EventLoop<T>)
X(x11::EventLoop<T>),
}
#[derive(Clone)]
@ -436,18 +445,17 @@ pub enum EventLoopProxy<T: 'static> {
Wayland(wayland::EventLoopProxy<T>),
}
impl<T:'static> EventLoop<T> {
impl<T: 'static> EventLoop<T> {
pub fn new() -> EventLoop<T> {
if let Ok(env_var) = env::var(BACKEND_PREFERENCE_ENV_VAR) {
match env_var.as_str() {
"x11" => {
// TODO: propagate
return EventLoop::new_x11().expect("Failed to initialize X11 backend");
},
}
"wayland" => {
return EventLoop::new_wayland()
.expect("Failed to initialize Wayland backend");
},
return EventLoop::new_wayland().expect("Failed to initialize Wayland backend");
}
_ => panic!(
"Unknown environment variable value for {}, try one of `x11`,`wayland`",
BACKEND_PREFERENCE_ENV_VAR,
@ -467,15 +475,13 @@ impl<T:'static> EventLoop<T> {
let err_string = format!(
"Failed to initialize any backend! Wayland status: {:?} X11 status: {:?}",
wayland_err,
x11_err,
wayland_err, x11_err,
);
panic!(err_string);
}
pub fn new_wayland() -> Result<EventLoop<T>, ConnectError> {
wayland::EventLoop::new()
.map(EventLoop::Wayland)
wayland::EventLoop::new().map(EventLoop::Wayland)
}
pub fn new_x11() -> Result<EventLoop<T>, XNotSupported> {
@ -521,20 +527,22 @@ impl<T:'static> EventLoop<T> {
}
pub fn run_return<F>(&mut self, callback: F)
where F: FnMut(::event::Event<T>, &RootELW<T>, &mut ControlFlow)
where
F: FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
{
match *self {
EventLoop::Wayland(ref mut evlp) => evlp.run_return(callback),
EventLoop::X(ref mut evlp) => evlp.run_return(callback)
EventLoop::X(ref mut evlp) => evlp.run_return(callback),
}
}
pub fn run<F>(self, callback: F) -> !
where F: 'static + FnMut(::event::Event<T>, &RootELW<T>, &mut ControlFlow)
where
F: 'static + FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
{
match self {
EventLoop::Wayland(evlp) => evlp.run(callback),
EventLoop::X(evlp) => evlp.run(callback)
EventLoop::X(evlp) => evlp.run(callback),
}
}
@ -546,10 +554,10 @@ impl<T:'static> EventLoop<T> {
}
}
pub fn window_target(&self) -> &::event_loop::EventLoopWindowTarget<T> {
pub fn window_target(&self) -> &crate::event_loop::EventLoopWindowTarget<T> {
match *self {
EventLoop::Wayland(ref evl) => evl.window_target(),
EventLoop::X(ref evl) => evl.window_target()
EventLoop::X(ref evl) => evl.window_target(),
}
}
}
@ -565,12 +573,16 @@ impl<T: 'static> EventLoopProxy<T> {
pub enum EventLoopWindowTarget<T> {
Wayland(wayland::EventLoopWindowTarget<T>),
X(x11::EventLoopWindowTarget<T>)
X(x11::EventLoopWindowTarget<T>),
}
fn sticky_exit_callback<T, F>(
evt: Event<T>, target: &RootELW<T>, control_flow: &mut ControlFlow, callback: &mut F
) where F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow)
evt: Event<T>,
target: &RootELW<T>,
control_flow: &mut ControlFlow,
callback: &mut F,
) where
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
{
// make ControlFlow::Exit sticky by providing a dummy
// control flow reference if it is already Exit.

View file

@ -1,46 +1,67 @@
use std::cell::RefCell;
use std::collections::VecDeque;
use std::fmt;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::time::Instant;
use event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW};
use event::ModifiersState;
use dpi::{PhysicalPosition, PhysicalSize};
use platform_impl::platform::sticky_exit_callback;
use super::window::WindowStore;
use super::WindowId;
use sctk::output::OutputMgr;
use sctk::reexports::client::protocol::{
wl_keyboard, wl_output, wl_pointer, wl_registry, wl_seat, wl_touch,
use std::{
cell::RefCell,
collections::VecDeque,
fmt,
rc::Rc,
sync::{Arc, Mutex},
time::Instant,
};
use sctk::reexports::client::{ConnectError, Display, EventQueue, GlobalEvent};
use sctk::Environment;
pub struct WindowEventsSink {
buffer: VecDeque<(::event::WindowEvent, ::window::WindowId)>,
use smithay_client_toolkit::reexports::protocols::unstable::relative_pointer::v1::client::{
zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1,
zwp_relative_pointer_v1::ZwpRelativePointerV1,
};
use crate::{
dpi::{PhysicalPosition, PhysicalSize},
event::ModifiersState,
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
monitor::VideoMode,
platform_impl::platform::sticky_exit_callback,
};
use super::{window::WindowStore, DeviceId, WindowId};
use smithay_client_toolkit::{
output::OutputMgr,
reexports::client::{
protocol::{wl_keyboard, wl_output, wl_pointer, wl_registry, wl_seat, wl_touch},
ConnectError, Display, EventQueue, GlobalEvent,
},
Environment,
};
pub struct WindowEventsSink<T> {
buffer: VecDeque<crate::event::Event<T>>,
}
impl WindowEventsSink {
pub fn new() -> WindowEventsSink {
impl<T> WindowEventsSink<T> {
pub fn new() -> WindowEventsSink<T> {
WindowEventsSink {
buffer: VecDeque::new(),
}
}
pub fn send_event(&mut self, evt: ::event::WindowEvent, wid: WindowId) {
self.buffer.push_back((evt, ::window::WindowId(::platform_impl::WindowId::Wayland(wid))));
pub fn send_window_event(&mut self, evt: crate::event::WindowEvent, wid: WindowId) {
self.buffer.push_back(crate::event::Event::WindowEvent {
event: evt,
window_id: crate::window::WindowId(crate::platform_impl::WindowId::Wayland(wid)),
});
}
fn empty_with<F, T>(&mut self, mut callback: F)
pub fn send_device_event(&mut self, evt: crate::event::DeviceEvent, dev_id: DeviceId) {
self.buffer.push_back(crate::event::Event::DeviceEvent {
event: evt,
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(dev_id)),
});
}
fn empty_with<F>(&mut self, mut callback: F)
where
F: FnMut(::event::Event<T>),
F: FnMut(crate::event::Event<T>),
{
for (evt, wid) in self.buffer.drain(..) {
callback(::event::Event::WindowEvent { event: evt, window_id: wid})
for evt in self.buffer.drain(..) {
callback(evt)
}
}
}
@ -53,12 +74,14 @@ pub struct EventLoop<T: 'static> {
// the output manager
pub outputs: OutputMgr,
// our sink, shared with some handlers, buffering the events
sink: Arc<Mutex<WindowEventsSink>>,
sink: Arc<Mutex<WindowEventsSink<T>>>,
pending_user_events: Rc<RefCell<VecDeque<T>>>,
_user_source: ::calloop::Source<::calloop::channel::Channel<T>>,
user_sender: ::calloop::channel::Sender<T>,
_kbd_source: ::calloop::Source<::calloop::channel::Channel<(::event::WindowEvent, super::WindowId)>>,
window_target: RootELW<T>
_kbd_source: ::calloop::Source<
::calloop::channel::Channel<(crate::event::WindowEvent, super::WindowId)>,
>,
window_target: RootELW<T>,
}
// A handle that can be sent across threads and used to wake up the `EventLoop`.
@ -66,7 +89,7 @@ pub struct EventLoop<T: 'static> {
// We should only try and wake up the `EventLoop` if it still exists, so we hold Weak ptrs.
#[derive(Clone)]
pub struct EventLoopProxy<T: 'static> {
user_sender: ::calloop::channel::Sender<T>
user_sender: ::calloop::channel::Sender<T>,
}
pub struct EventLoopWindowTarget<T> {
@ -82,7 +105,7 @@ pub struct EventLoopWindowTarget<T> {
pub display: Arc<Display>,
// The list of seats
pub seats: Arc<Mutex<Vec<(u32, wl_seat::WlSeat)>>>,
_marker: ::std::marker::PhantomData<T>
_marker: ::std::marker::PhantomData<T>,
}
impl<T: 'static> EventLoopProxy<T> {
@ -104,14 +127,18 @@ impl<T: 'static> EventLoop<T> {
let (kbd_sender, kbd_channel) = ::calloop::channel::channel();
let kbd_sink = sink.clone();
let kbd_source = inner_loop.handle().insert_source(kbd_channel, move |evt, &mut()| {
if let ::calloop::channel::Event::Msg((evt, wid)) = evt {
kbd_sink.lock().unwrap().send_event(evt, wid);
}
}).unwrap();
let kbd_source = inner_loop
.handle()
.insert_source(kbd_channel, move |evt, &mut ()| {
if let ::calloop::channel::Event::Msg((evt, wid)) = evt {
kbd_sink.lock().unwrap().send_window_event(evt, wid);
}
})
.unwrap();
let mut seat_manager = SeatManager {
sink: sink.clone(),
relative_pointer_manager_proxy: None,
store: store.clone(),
seats: seats.clone(),
kbd_sender,
@ -120,34 +147,52 @@ impl<T: 'static> EventLoop<T> {
let env = Environment::from_display_with_cb(
&display,
&mut event_queue,
move |event, registry| {
match event {
GlobalEvent::New { id, ref interface, version } => {
if interface == "wl_seat" {
seat_manager.add_seat(id, version, registry)
}
},
GlobalEvent::Removed { id, ref interface } => {
if interface == "wl_seat" {
seat_manager.remove_seat(id)
}
},
move |event, registry| match event {
GlobalEvent::New {
id,
ref interface,
version,
} => {
if interface == "zwp_relative_pointer_manager_v1" {
seat_manager.relative_pointer_manager_proxy = Some(
registry
.bind(version, id, move |pointer_manager| {
pointer_manager.implement_closure(|_, _| (), ())
})
.unwrap(),
)
}
if interface == "wl_seat" {
seat_manager.add_seat(id, version, registry)
}
}
GlobalEvent::Removed { id, ref interface } => {
if interface == "wl_seat" {
seat_manager.remove_seat(id)
}
}
},
).unwrap();
)
.unwrap();
let source = inner_loop.handle().insert_source(event_queue, |(), &mut ()| {}).unwrap();
let source = inner_loop
.handle()
.insert_source(event_queue, |(), &mut ()| {})
.unwrap();
let pending_user_events = Rc::new(RefCell::new(VecDeque::new()));
let pending_user_events2 = pending_user_events.clone();
let (user_sender, user_channel) = ::calloop::channel::channel();
let user_source = inner_loop.handle().insert_source(user_channel, move |evt, &mut()| {
if let ::calloop::channel::Event::Msg(msg) = evt {
pending_user_events2.borrow_mut().push_back(msg);
}
}).unwrap();
let user_source = inner_loop
.handle()
.insert_source(user_channel, move |evt, &mut ()| {
if let ::calloop::channel::Event::Msg(msg) = evt {
pending_user_events2.borrow_mut().push_back(msg);
}
})
.unwrap();
Ok(EventLoop {
inner_loop,
@ -159,35 +204,37 @@ impl<T: 'static> EventLoop<T> {
user_sender,
_kbd_source: kbd_source,
window_target: RootELW {
p: ::platform_impl::EventLoopWindowTarget::Wayland(EventLoopWindowTarget {
p: crate::platform_impl::EventLoopWindowTarget::Wayland(EventLoopWindowTarget {
evq: RefCell::new(source),
store,
env,
cleanup_needed: Arc::new(Mutex::new(false)),
seats,
display,
_marker: ::std::marker::PhantomData
_marker: ::std::marker::PhantomData,
}),
_marker: ::std::marker::PhantomData
}
_marker: ::std::marker::PhantomData,
},
})
}
pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy {
user_sender: self.user_sender.clone()
user_sender: self.user_sender.clone(),
}
}
pub fn run<F>(mut self, callback: F) -> !
where F: 'static + FnMut(::event::Event<T>, &RootELW<T>, &mut ControlFlow)
where
F: 'static + FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
{
self.run_return(callback);
::std::process::exit(0);
}
pub fn run_return<F>(&mut self, mut callback: F)
where F: FnMut(::event::Event<T>, &RootELW<T>, &mut ControlFlow)
where
F: FnMut(crate::event::Event<T>, &RootELW<T>, &mut ControlFlow),
{
// send pending events to the server
self.display.flush().expect("Wayland connection lost.");
@ -197,7 +244,11 @@ impl<T: 'static> EventLoop<T> {
let sink = self.sink.clone();
let user_events = self.pending_user_events.clone();
callback(::event::Event::NewEvents(::event::StartCause::Init), &self.window_target, &mut control_flow);
callback(
crate::event::Event::NewEvents(crate::event::StartCause::Init),
&self.window_target,
&mut control_flow,
);
loop {
self.post_dispatch_triggers();
@ -206,7 +257,12 @@ impl<T: 'static> EventLoop<T> {
{
let mut guard = sink.lock().unwrap();
guard.empty_with(|evt| {
sticky_exit_callback(evt, &self.window_target, &mut control_flow, &mut callback);
sticky_exit_callback(
evt,
&self.window_target,
&mut control_flow,
&mut callback,
);
});
}
// empty user events
@ -214,10 +270,10 @@ impl<T: 'static> EventLoop<T> {
let mut guard = user_events.borrow_mut();
for evt in guard.drain(..) {
sticky_exit_callback(
::event::Event::UserEvent(evt),
crate::event::Event::UserEvent(evt),
&self.window_target,
&mut control_flow,
&mut callback
&mut callback,
);
}
}
@ -227,16 +283,21 @@ impl<T: 'static> EventLoop<T> {
{
let mut guard = sink.lock().unwrap();
guard.empty_with(|evt| {
sticky_exit_callback(evt, &self.window_target, &mut control_flow, &mut callback);
sticky_exit_callback(
evt,
&self.window_target,
&mut control_flow,
&mut callback,
);
});
}
// send Events cleared
{
sticky_exit_callback(
::event::Event::EventsCleared,
crate::event::Event::EventsCleared,
&self.window_target,
&mut control_flow,
&mut callback
&mut callback,
);
}
@ -247,22 +308,26 @@ impl<T: 'static> EventLoop<T> {
ControlFlow::Exit => break,
ControlFlow::Poll => {
// non-blocking dispatch
self.inner_loop.dispatch(Some(::std::time::Duration::from_millis(0)), &mut ()).unwrap();
control_flow = ControlFlow::default();
callback(::event::Event::NewEvents(::event::StartCause::Poll), &self.window_target, &mut control_flow);
},
self.inner_loop
.dispatch(Some(::std::time::Duration::from_millis(0)), &mut ())
.unwrap();
callback(
crate::event::Event::NewEvents(crate::event::StartCause::Poll),
&self.window_target,
&mut control_flow,
);
}
ControlFlow::Wait => {
self.inner_loop.dispatch(None, &mut ()).unwrap();
control_flow = ControlFlow::default();
callback(
::event::Event::NewEvents(::event::StartCause::WaitCancelled {
crate::event::Event::NewEvents(crate::event::StartCause::WaitCancelled {
start: Instant::now(),
requested_resume: None
requested_resume: None,
}),
&self.window_target,
&mut control_flow
&mut control_flow,
);
},
}
ControlFlow::WaitUntil(deadline) => {
let start = Instant::now();
// compute the blocking duration
@ -272,32 +337,39 @@ impl<T: 'static> EventLoop<T> {
::std::time::Duration::from_millis(0)
};
self.inner_loop.dispatch(Some(duration), &mut ()).unwrap();
control_flow = ControlFlow::default();
let now = Instant::now();
if now < deadline {
callback(
::event::Event::NewEvents(::event::StartCause::WaitCancelled {
start,
requested_resume: Some(deadline)
}),
crate::event::Event::NewEvents(
crate::event::StartCause::WaitCancelled {
start,
requested_resume: Some(deadline),
},
),
&self.window_target,
&mut control_flow
&mut control_flow,
);
} else {
callback(
::event::Event::NewEvents(::event::StartCause::ResumeTimeReached {
start,
requested_resume: deadline
}),
crate::event::Event::NewEvents(
crate::event::StartCause::ResumeTimeReached {
start,
requested_resume: deadline,
},
),
&self.window_target,
&mut control_flow
&mut control_flow,
);
}
},
}
}
}
callback(::event::Event::LoopDestroyed, &self.window_target, &mut control_flow);
callback(
crate::event::Event::LoopDestroyed,
&self.window_target,
&mut control_flow,
);
}
pub fn primary_monitor(&self) -> MonitorHandle {
@ -325,8 +397,8 @@ impl<T> EventLoop<T> {
fn post_dispatch_triggers(&mut self) {
let mut sink = self.sink.lock().unwrap();
let window_target = match self.window_target.p {
::platform_impl::EventLoopWindowTarget::Wayland(ref wt) => wt,
_ => unreachable!()
crate::platform_impl::EventLoopWindowTarget::Wayland(ref wt) => wt,
_ => unreachable!(),
};
// prune possible dead windows
{
@ -335,7 +407,7 @@ impl<T> EventLoop<T> {
let pruned = window_target.store.lock().unwrap().cleanup();
*cleanup_needed = false;
for wid in pruned {
sink.send_event(::event::WindowEvent::Destroyed, wid);
sink.send_window_event(crate::event::WindowEvent::Destroyed, wid);
}
}
}
@ -346,8 +418,11 @@ impl<T> EventLoop<T> {
if let Some((w, h)) = newsize {
frame.resize(w, h);
frame.refresh();
let logical_size = ::dpi::LogicalSize::new(w as f64, h as f64);
sink.send_event(::event::WindowEvent::Resized(logical_size), wid);
let logical_size = crate::dpi::LogicalSize::new(w as f64, h as f64);
sink.send_window_event(
crate::event::WindowEvent::Resized(logical_size),
wid,
);
*size = (w, h);
} else if frame_refresh {
frame.refresh();
@ -357,13 +432,16 @@ impl<T> EventLoop<T> {
}
}
if let Some(dpi) = new_dpi {
sink.send_event(::event::WindowEvent::HiDpiFactorChanged(dpi as f64), wid);
sink.send_window_event(
crate::event::WindowEvent::HiDpiFactorChanged(dpi as f64),
wid,
);
}
if refresh {
sink.send_event(::event::WindowEvent::RedrawRequested, wid);
sink.send_window_event(crate::event::WindowEvent::RedrawRequested, wid);
}
if closed {
sink.send_event(::event::WindowEvent::CloseRequested, wid);
sink.send_window_event(crate::event::WindowEvent::CloseRequested, wid);
}
},
)
@ -374,14 +452,15 @@ impl<T> EventLoop<T> {
* Wayland protocol implementations
*/
struct SeatManager {
sink: Arc<Mutex<WindowEventsSink>>,
struct SeatManager<T: 'static> {
sink: Arc<Mutex<WindowEventsSink<T>>>,
store: Arc<Mutex<WindowStore>>,
seats: Arc<Mutex<Vec<(u32, wl_seat::WlSeat)>>>,
kbd_sender: ::calloop::channel::Sender<(::event::WindowEvent, super::WindowId)>
kbd_sender: ::calloop::channel::Sender<(crate::event::WindowEvent, super::WindowId)>,
relative_pointer_manager_proxy: Option<ZwpRelativePointerManagerV1>,
}
impl SeatManager {
impl<T: 'static> SeatManager<T> {
fn add_seat(&mut self, id: u32, version: u32, registry: wl_registry::WlRegistry) {
use std::cmp::min;
@ -389,6 +468,8 @@ impl SeatManager {
sink: self.sink.clone(),
store: self.store.clone(),
pointer: None,
relative_pointer: None,
relative_pointer_manager_proxy: self.relative_pointer_manager_proxy.as_ref().cloned(),
keyboard: None,
touch: None,
kbd_sender: self.kbd_sender.clone(),
@ -396,9 +477,7 @@ impl SeatManager {
};
let seat = registry
.bind(min(version, 5), id, move |seat| {
seat.implement_closure(move |event, seat| {
seat_data.receive(event, seat)
}, ())
seat.implement_closure(move |event, seat| seat_data.receive(event, seat), ())
})
.unwrap();
self.store.lock().unwrap().new_seat(&seat);
@ -416,17 +495,19 @@ impl SeatManager {
}
}
struct SeatData {
sink: Arc<Mutex<WindowEventsSink>>,
struct SeatData<T> {
sink: Arc<Mutex<WindowEventsSink<T>>>,
store: Arc<Mutex<WindowStore>>,
kbd_sender: ::calloop::channel::Sender<(::event::WindowEvent, super::WindowId)>,
kbd_sender: ::calloop::channel::Sender<(crate::event::WindowEvent, super::WindowId)>,
pointer: Option<wl_pointer::WlPointer>,
relative_pointer: Option<ZwpRelativePointerV1>,
relative_pointer_manager_proxy: Option<ZwpRelativePointerManagerV1>,
keyboard: Option<wl_keyboard::WlKeyboard>,
touch: Option<wl_touch::WlTouch>,
modifiers_tracker: Arc<Mutex<ModifiersState>>,
}
impl SeatData {
impl<T: 'static> SeatData<T> {
fn receive(&mut self, evt: wl_seat::Event, seat: wl_seat::WlSeat) {
match evt {
wl_seat::Event::Name { .. } => (),
@ -438,7 +519,19 @@ impl SeatData {
self.sink.clone(),
self.store.clone(),
self.modifiers_tracker.clone(),
))
));
self.relative_pointer =
self.relative_pointer_manager_proxy
.as_ref()
.and_then(|manager| {
super::pointer::implement_relative_pointer(
self.sink.clone(),
self.pointer.as_ref().unwrap(),
manager,
)
.ok()
})
}
// destroy pointer if applicable
if !capabilities.contains(wl_seat::Capability::Pointer) {
@ -480,13 +573,13 @@ impl SeatData {
}
}
}
},
_ => unreachable!()
}
_ => unreachable!(),
}
}
}
impl Drop for SeatData {
impl<T> Drop for SeatData<T> {
fn drop(&mut self) {
if let Some(pointer) = self.pointer.take() {
if pointer.as_ref().version() >= 3 {
@ -525,12 +618,12 @@ impl Clone for MonitorHandle {
}
impl fmt::Debug for MonitorHandle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[derive(Debug)]
struct MonitorHandle {
name: Option<String>,
native_identifier: u32,
dimensions: PhysicalSize,
size: PhysicalSize,
position: PhysicalPosition,
hidpi_factor: i32,
}
@ -538,7 +631,7 @@ impl fmt::Debug for MonitorHandle {
let monitor_id_proxy = MonitorHandle {
name: self.name(),
native_identifier: self.native_identifier(),
dimensions: self.dimensions(),
size: self.size(),
position: self.position(),
hidpi_factor: self.hidpi_factor(),
};
@ -559,7 +652,7 @@ impl MonitorHandle {
self.mgr.with_info(&self.proxy, |id, _| id).unwrap_or(0)
}
pub fn dimensions(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize {
match self.mgr.with_info(&self.proxy, |_, info| {
info.modes
.iter()
@ -568,7 +661,8 @@ impl MonitorHandle {
}) {
Some(Some((w, h))) => (w as u32, h as u32),
_ => (0, 0),
}.into()
}
.into()
}
pub fn position(&self) -> PhysicalPosition {
@ -584,6 +678,19 @@ impl MonitorHandle {
.with_info(&self.proxy, |_, info| info.scale_factor)
.unwrap_or(1)
}
#[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.mgr
.with_info(&self.proxy, |_, info| info.modes.clone())
.unwrap_or(vec![])
.into_iter()
.map(|x| VideoMode {
size: (x.dimensions.0 as u32, x.dimensions.1 as u32),
refresh_rate: (x.refresh_rate as f32 / 1000.0).round() as u16,
bit_depth: 32,
})
}
}
pub fn primary_monitor(outputs: &OutputMgr) -> MonitorHandle {

View file

@ -1,16 +1,18 @@
use std::sync::{Arc, Mutex};
use super::{make_wid, DeviceId};
use sctk::keyboard::{
self, map_keyboard_auto_with_repeat, Event as KbEvent, KeyRepeatEvent, KeyRepeatKind,
use smithay_client_toolkit::{
keyboard::{
self, map_keyboard_auto_with_repeat, Event as KbEvent, KeyRepeatEvent, KeyRepeatKind,
},
reexports::client::protocol::{wl_keyboard, wl_seat},
};
use sctk::reexports::client::protocol::{wl_keyboard, wl_seat};
use event::{ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent};
use crate::event::{ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent};
pub fn init_keyboard(
seat: &wl_seat::WlSeat,
sink: ::calloop::channel::Sender<(::event::WindowEvent, super::WindowId)>,
sink: ::calloop::channel::Sender<(crate::event::WindowEvent, super::WindowId)>,
modifiers_tracker: Arc<Mutex<ModifiersState>>,
) -> wl_keyboard::WlKeyboard {
// { variables to be captured by the closures
@ -23,78 +25,92 @@ pub fn init_keyboard(
let ret = map_keyboard_auto_with_repeat(
seat,
KeyRepeatKind::System,
move |evt: KbEvent, _| match evt {
KbEvent::Enter { surface, .. } => {
let wid = make_wid(&surface);
my_sink.send((WindowEvent::Focused(true), wid)).unwrap();
*target.lock().unwrap() = Some(wid);
}
KbEvent::Leave { surface, .. } => {
let wid = make_wid(&surface);
my_sink.send((WindowEvent::Focused(false), wid)).unwrap();
*target.lock().unwrap() = None;
}
KbEvent::Key {
rawkey,
keysym,
state,
utf8,
..
} => {
if let Some(wid) = *target.lock().unwrap() {
let state = match state {
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
wl_keyboard::KeyState::Released => ElementState::Released,
_ => unreachable!(),
};
let vkcode = key_to_vkey(rawkey, keysym);
my_sink.send(
(WindowEvent::KeyboardInput {
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
input: KeyboardInput {
state: state,
scancode: rawkey,
virtual_keycode: vkcode,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
},
wid)
).unwrap();
// send char event only on key press, not release
if let ElementState::Released = state {
return;
}
if let Some(txt) = utf8 {
for chr in txt.chars() {
my_sink.send((WindowEvent::ReceivedCharacter(chr), wid)).unwrap();
move |evt: KbEvent<'_>, _| {
match evt {
KbEvent::Enter { surface, .. } => {
let wid = make_wid(&surface);
my_sink.send((WindowEvent::Focused(true), wid)).unwrap();
*target.lock().unwrap() = Some(wid);
}
KbEvent::Leave { surface, .. } => {
let wid = make_wid(&surface);
my_sink.send((WindowEvent::Focused(false), wid)).unwrap();
*target.lock().unwrap() = None;
}
KbEvent::Key {
rawkey,
keysym,
state,
utf8,
..
} => {
if let Some(wid) = *target.lock().unwrap() {
let state = match state {
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
wl_keyboard::KeyState::Released => ElementState::Released,
_ => unreachable!(),
};
let vkcode = key_to_vkey(rawkey, keysym);
my_sink
.send((
WindowEvent::KeyboardInput {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
input: KeyboardInput {
state,
scancode: rawkey,
virtual_keycode: vkcode,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
},
wid,
))
.unwrap();
// send char event only on key press, not release
if let ElementState::Released = state {
return;
}
if let Some(txt) = utf8 {
for chr in txt.chars() {
my_sink
.send((WindowEvent::ReceivedCharacter(chr), wid))
.unwrap();
}
}
}
}
KbEvent::RepeatInfo { .. } => { /* Handled by smithay client toolkit */ }
KbEvent::Modifiers {
modifiers: event_modifiers,
} => *modifiers_tracker.lock().unwrap() = event_modifiers.into(),
}
KbEvent::RepeatInfo { .. } => { /* Handled by smithay client toolkit */ }
KbEvent::Modifiers { modifiers: event_modifiers } => {
*modifiers_tracker.lock().unwrap() = event_modifiers.into()
},
},
move |repeat_event: KeyRepeatEvent, _| {
if let Some(wid) = *repeat_target.lock().unwrap() {
let state = ElementState::Pressed;
let vkcode = key_to_vkey(repeat_event.rawkey, repeat_event.keysym);
repeat_sink.send((
WindowEvent::KeyboardInput {
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
input: KeyboardInput {
state: state,
scancode: repeat_event.rawkey,
virtual_keycode: vkcode,
modifiers: my_modifiers.lock().unwrap().clone(),
repeat_sink
.send((
WindowEvent::KeyboardInput {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
input: KeyboardInput {
state,
scancode: repeat_event.rawkey,
virtual_keycode: vkcode,
modifiers: my_modifiers.lock().unwrap().clone(),
},
},
},
wid)
).unwrap();
wid,
))
.unwrap();
if let Some(txt) = repeat_event.utf8 {
for chr in txt.chars() {
repeat_sink.send((WindowEvent::ReceivedCharacter(chr), wid)).unwrap();
repeat_sink
.send((WindowEvent::ReceivedCharacter(chr), wid))
.unwrap();
}
}
}
@ -111,46 +127,55 @@ pub fn init_keyboard(
// In this case, we don't have the keymap information (it is
// supposed to be serialized by the compositor using libxkbcommon)
// { variables to be captured by the closure
let mut target = None;
let my_sink = sink;
// }
seat.get_keyboard(|keyboard| {
keyboard.implement_closure(move |evt, _| match evt {
wl_keyboard::Event::Enter { surface, .. } => {
let wid = make_wid(&surface);
my_sink.send((WindowEvent::Focused(true), wid)).unwrap();
target = Some(wid);
}
wl_keyboard::Event::Leave { surface, .. } => {
let wid = make_wid(&surface);
my_sink.send((WindowEvent::Focused(false), wid)).unwrap();
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,
_ => unreachable!()
};
my_sink.send((
WindowEvent::KeyboardInput {
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
input: KeyboardInput {
state: state,
scancode: key,
virtual_keycode: None,
modifiers: ModifiersState::default(),
},
},
wid,
)).unwrap();
// { variables to be captured by the closure
let mut target = None;
let my_sink = sink;
// }
keyboard.implement_closure(
move |evt, _| match evt {
wl_keyboard::Event::Enter { surface, .. } => {
let wid = make_wid(&surface);
my_sink.send((WindowEvent::Focused(true), wid)).unwrap();
target = Some(wid);
}
}
_ => (),
}, ())
}).unwrap()
wl_keyboard::Event::Leave { surface, .. } => {
let wid = make_wid(&surface);
my_sink.send((WindowEvent::Focused(false), wid)).unwrap();
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,
_ => unreachable!(),
};
my_sink
.send((
WindowEvent::KeyboardInput {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
input: KeyboardInput {
state,
scancode: key,
virtual_keycode: None,
modifiers: ModifiersState::default(),
},
},
wid,
))
.unwrap();
}
}
_ => (),
},
(),
)
})
.unwrap()
}
}
}
@ -173,7 +198,7 @@ fn key_to_vkey(rawkey: u32, keysym: u32) -> Option<VirtualKeyCode> {
}
fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
use sctk::keyboard::keysyms;
use smithay_client_toolkit::keyboard::keysyms;
match keysym {
// letters
keysyms::XKB_KEY_A | keysyms::XKB_KEY_a => Some(VirtualKeyCode::A),

View file

@ -1,15 +1,19 @@
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd",
target_os = "netbsd", target_os = "openbsd"))]
pub use self::window::Window;
pub use self::event_loop::{EventLoop, EventLoopWindowTarget, EventLoopProxy, WindowEventsSink, MonitorHandle};
pub use self::{
event_loop::{
EventLoop, EventLoopProxy, EventLoopWindowTarget, MonitorHandle, WindowEventsSink,
},
window::Window,
};
use sctk::reexports::client::protocol::wl_surface;
use smithay_client_toolkit::reexports::client::protocol::wl_surface;
mod event_loop;
mod keyboard;
mod pointer;
mod touch;
mod keyboard;
mod window;
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]

View file

@ -1,191 +1,243 @@
use std::sync::{Arc, Mutex};
use event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent, ModifiersState};
use crate::event::{
DeviceEvent, ElementState, ModifiersState, MouseButton, MouseScrollDelta, TouchPhase,
WindowEvent,
};
use super::DeviceId;
use super::event_loop::WindowEventsSink;
use super::window::WindowStore;
use super::{event_loop::WindowEventsSink, window::WindowStore, DeviceId};
use sctk::reexports::client::protocol::wl_pointer::{self, Event as PtrEvent, WlPointer};
use sctk::reexports::client::protocol::wl_seat;
use smithay_client_toolkit::reexports::client::protocol::{
wl_pointer::{self, Event as PtrEvent, WlPointer},
wl_seat,
};
pub fn implement_pointer(
use smithay_client_toolkit::reexports::protocols::unstable::relative_pointer::v1::client::{
zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1, zwp_relative_pointer_v1::Event,
zwp_relative_pointer_v1::ZwpRelativePointerV1,
};
pub fn implement_pointer<T: 'static>(
seat: &wl_seat::WlSeat,
sink: Arc<Mutex<WindowEventsSink>>,
sink: Arc<Mutex<WindowEventsSink<T>>>,
store: Arc<Mutex<WindowStore>>,
modifiers_tracker: Arc<Mutex<ModifiersState>>,
) -> WlPointer {
let mut mouse_focus = None;
let mut axis_buffer = None;
let mut axis_discrete_buffer = None;
let mut axis_state = TouchPhase::Ended;
seat.get_pointer(|pointer| {
pointer.implement_closure(move |evt, pointer| {
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: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
},
wid,
);
sink.send_event(
WindowEvent::CursorMoved {
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
position: (surface_x, surface_y).into(),
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
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: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
},
wid,
);
}
}
PtrEvent::Motion {
surface_x,
surface_y,
..
} => {
if let Some(wid) = mouse_focus {
sink.send_event(
WindowEvent::CursorMoved {
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
position: (surface_x, surface_y).into(),
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
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,
_ => unreachable!()
};
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: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
state: state,
button: button,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
wid,
);
}
}
PtrEvent::Axis { axis, value, .. } => {
if let Some(wid) = mouse_focus {
if pointer.as_ref().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,
_ => unreachable!()
}
sink.send_event(
WindowEvent::MouseWheel {
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
phase: TouchPhase::Moved,
modifiers: modifiers_tracker.lock().unwrap().clone(),
let mut mouse_focus = None;
let mut axis_buffer = None;
let mut axis_discrete_buffer = None;
let mut axis_state = TouchPhase::Ended;
pointer.implement_closure(
move |evt, pointer| {
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_window_event(
WindowEvent::CursorEntered {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
},
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,
_ => unreachable!()
}
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: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
delta: MouseScrollDelta::LineDelta(x as f32, y as f32),
phase: axis_state,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
wid,
);
} else if let Some((x, y)) = axis_buffer {
sink.send_event(
WindowEvent::MouseWheel {
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
delta: MouseScrollDelta::PixelDelta((x as f64, y as f64).into()),
phase: axis_state,
sink.send_window_event(
WindowEvent::CursorMoved {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
position: (surface_x, surface_y).into(),
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
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,
_ => unreachable!()
PtrEvent::Leave { surface, .. } => {
mouse_focus = None;
let wid = store.find_wid(&surface);
if let Some(wid) = wid {
sink.send_window_event(
WindowEvent::CursorLeft {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
},
wid,
);
}
}
axis_discrete_buffer = Some((x, y));
axis_state = match axis_state {
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
_ => TouchPhase::Started,
PtrEvent::Motion {
surface_x,
surface_y,
..
} => {
if let Some(wid) = mouse_focus {
sink.send_window_event(
WindowEvent::CursorMoved {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
position: (surface_x, surface_y).into(),
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
wid,
);
}
}
},
_ => unreachable!()
}
}, ())
}).unwrap()
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,
_ => unreachable!(),
};
let button = match button {
0x110 => MouseButton::Left,
0x111 => MouseButton::Right,
0x112 => MouseButton::Middle,
// TODO figure out the translation ?
_ => return,
};
sink.send_window_event(
WindowEvent::MouseInput {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
state,
button,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
wid,
);
}
}
PtrEvent::Axis { axis, value, .. } => {
if let Some(wid) = mouse_focus {
if pointer.as_ref().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,
_ => unreachable!(),
}
sink.send_window_event(
WindowEvent::MouseWheel {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
delta: MouseScrollDelta::PixelDelta(
(x as f64, y as f64).into(),
),
phase: TouchPhase::Moved,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
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,
_ => unreachable!(),
}
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_window_event(
WindowEvent::MouseWheel {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
delta: MouseScrollDelta::LineDelta(x as f32, y as f32),
phase: axis_state,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
wid,
);
} else if let Some((x, y)) = axis_buffer {
sink.send_window_event(
WindowEvent::MouseWheel {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
delta: MouseScrollDelta::PixelDelta(
(x as f64, y as f64).into(),
),
phase: axis_state,
modifiers: modifiers_tracker.lock().unwrap().clone(),
},
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,
_ => unreachable!(),
}
axis_discrete_buffer = Some((x, y));
axis_state = match axis_state {
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
_ => TouchPhase::Started,
}
}
_ => unreachable!(),
}
},
(),
)
})
.unwrap()
}
pub fn implement_relative_pointer<T: 'static>(
sink: Arc<Mutex<WindowEventsSink<T>>>,
pointer: &WlPointer,
manager: &ZwpRelativePointerManagerV1,
) -> Result<ZwpRelativePointerV1, ()> {
manager.get_relative_pointer(pointer, |rel_pointer| {
rel_pointer.implement_closure(
move |evt, _rel_pointer| {
let mut sink = sink.lock().unwrap();
match evt {
Event::RelativeMotion { dx, dy, .. } => sink
.send_device_event(DeviceEvent::MouseMotion { delta: (dx, dy) }, DeviceId),
_ => unreachable!(),
}
},
(),
)
})
}

View file

@ -1,13 +1,13 @@
use std::sync::{Arc, Mutex};
use event::{TouchPhase, WindowEvent};
use crate::event::{TouchPhase, WindowEvent};
use super::{DeviceId, WindowId};
use super::event_loop::WindowEventsSink;
use super::window::WindowStore;
use super::{event_loop::WindowEventsSink, window::WindowStore, DeviceId, WindowId};
use sctk::reexports::client::protocol::wl_touch::{Event as TouchEvent, WlTouch};
use sctk::reexports::client::protocol::wl_seat;
use smithay_client_toolkit::reexports::client::protocol::{
wl_seat,
wl_touch::{Event as TouchEvent, WlTouch},
};
struct TouchPoint {
wid: WindowId,
@ -15,82 +15,96 @@ struct TouchPoint {
id: i32,
}
pub(crate) fn implement_touch(
pub(crate) fn implement_touch<T: 'static>(
seat: &wl_seat::WlSeat,
sink: Arc<Mutex<WindowEventsSink>>,
sink: Arc<Mutex<WindowEventsSink<T>>>,
store: Arc<Mutex<WindowStore>>,
) -> WlTouch {
let mut pending_ids = Vec::new();
seat.get_touch(|touch| {
touch.implement_closure(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(::event::Touch {
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
phase: TouchPhase::Started,
location: (x, y).into(),
id: id as u64,
}),
wid,
);
pending_ids.push(TouchPoint {
wid: wid,
location: (x, y),
id: id,
});
touch.implement_closure(
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_window_event(
WindowEvent::Touch(crate::event::Touch {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
phase: TouchPhase::Started,
location: (x, y).into(),
id: id as u64,
}),
wid,
);
pending_ids.push(TouchPoint {
wid,
location: (x, y),
id,
});
}
}
}
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(::event::Touch {
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
phase: TouchPhase::Ended,
location: pt.location.into(),
id: 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_window_event(
WindowEvent::Touch(crate::event::Touch {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
phase: TouchPhase::Ended,
location: pt.location.into(),
id: 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(::event::Touch {
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
phase: TouchPhase::Moved,
location: (x, y).into(),
id: 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_window_event(
WindowEvent::Touch(crate::event::Touch {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
phase: TouchPhase::Moved,
location: (x, y).into(),
id: id as u64,
}),
pt.wid,
);
}
}
TouchEvent::Frame => (),
TouchEvent::Cancel => {
for pt in pending_ids.drain(..) {
sink.send_window_event(
WindowEvent::Touch(crate::event::Touch {
device_id: crate::event::DeviceId(
crate::platform_impl::DeviceId::Wayland(DeviceId),
),
phase: TouchPhase::Cancelled,
location: pt.location.into(),
id: pt.id as u64,
}),
pt.wid,
);
}
}
_ => unreachable!(),
}
TouchEvent::Frame => (),
TouchEvent::Cancel => for pt in pending_ids.drain(..) {
sink.send_event(
WindowEvent::Touch(::event::Touch {
device_id: ::event::DeviceId(::platform_impl::DeviceId::Wayland(DeviceId)),
phase: TouchPhase::Cancelled,
location: pt.location.into(),
id: pt.id as u64,
}),
pt.wid,
);
},
_ => unreachable!()
}
}, ())
}).unwrap()
},
(),
)
})
.unwrap()
}

View file

@ -1,20 +1,31 @@
use std::collections::VecDeque;
use std::sync::{Arc, Mutex, Weak};
use std::{
collections::VecDeque,
sync::{Arc, Mutex, Weak},
};
use dpi::{LogicalPosition, LogicalSize};
use error::{ExternalError, NotSupportedError, OsError as RootOsError};
use platform_impl::{MonitorHandle as PlatformMonitorHandle, PlatformSpecificWindowBuilderAttributes as PlAttributes};
use monitor::MonitorHandle as RootMonitorHandle;
use window::{WindowAttributes, CursorIcon};
use crate::{
dpi::{LogicalPosition, LogicalSize},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
monitor::MonitorHandle as RootMonitorHandle,
platform_impl::{
MonitorHandle as PlatformMonitorHandle,
PlatformSpecificWindowBuilderAttributes as PlAttributes,
},
window::{CursorIcon, WindowAttributes},
};
use sctk::surface::{get_dpi_factor, get_outputs};
use sctk::window::{ConceptFrame, Event as WEvent, State as WState, Window as SWindow, Theme};
use sctk::reexports::client::Display;
use sctk::reexports::client::protocol::{wl_seat, wl_surface};
use sctk::output::OutputMgr;
use smithay_client_toolkit::{
output::OutputMgr,
reexports::client::{
protocol::{wl_seat, wl_surface},
Display,
},
surface::{get_dpi_factor, get_outputs},
window::{ConceptFrame, Event as WEvent, State as WState, Theme, Window as SWindow},
};
use super::{make_wid, EventLoopWindowTarget, MonitorHandle, WindowId};
use platform_impl::platform::wayland::event_loop::{available_monitors, primary_monitor};
use crate::platform_impl::platform::wayland::event_loop::{available_monitors, primary_monitor};
pub struct Window {
surface: wl_surface::WlSurface,
@ -29,7 +40,11 @@ pub struct Window {
}
impl Window {
pub fn new<T>(evlp: &EventLoopWindowTarget<T>, attributes: WindowAttributes, pl_attribs: PlAttributes) -> Result<Window, RootOsError> {
pub fn new<T>(
evlp: &EventLoopWindowTarget<T>,
attributes: WindowAttributes,
pl_attribs: PlAttributes,
) -> Result<Window, RootOsError> {
let (width, height) = attributes.inner_size.map(Into::into).unwrap_or((800, 600));
// Create the window
let size = Arc::new(Mutex::new((width, height)));
@ -81,7 +96,8 @@ impl Window {
}
}
},
).unwrap();
)
.unwrap();
if let Some(app_id) = pl_attribs.app_id {
frame.set_app_id(app_id);
@ -135,10 +151,10 @@ impl Window {
Ok(Window {
display: evlp.display.clone(),
surface: surface,
frame: frame,
surface,
frame,
outputs: evlp.env.outputs.clone(),
size: size,
size,
kill_switch: (kill_switch, evlp.cleanup_needed.clone()),
need_frame_refresh,
need_refresh,
@ -199,12 +215,18 @@ impl Window {
#[inline]
pub fn set_min_inner_size(&self, dimensions: Option<LogicalSize>) {
self.frame.lock().unwrap().set_min_size(dimensions.map(Into::into));
self.frame
.lock()
.unwrap()
.set_min_size(dimensions.map(Into::into));
}
#[inline]
pub fn set_max_inner_size(&self, dimensions: Option<LogicalSize>) {
self.frame.lock().unwrap().set_max_size(dimensions.map(Into::into));
self.frame
.lock()
.unwrap()
.set_max_size(dimensions.map(Into::into));
}
#[inline]
@ -252,7 +274,6 @@ impl Window {
}
}
pub fn set_theme<T: Theme>(&self, theme: T) {
self.frame.lock().unwrap().set_theme(theme)
}
@ -380,7 +401,16 @@ impl WindowStore {
pub fn for_each<F>(&mut self, mut f: F)
where
F: FnMut(Option<(u32, u32)>, &mut (u32, u32), Option<i32>, bool, bool, bool, WindowId, Option<&mut SWindow<ConceptFrame>>),
F: FnMut(
Option<(u32, u32)>,
&mut (u32, u32),
Option<i32>,
bool,
bool,
bool,
WindowId,
Option<&mut SWindow<ConceptFrame>>,
),
{
for window in &mut self.windows {
let opt_arc = window.frame.upgrade();
@ -403,4 +433,3 @@ impl WindowStore {
}
}
}

View file

@ -1,8 +1,10 @@
use std::io;
use std::sync::Arc;
use std::path::{Path, PathBuf};
use std::str::Utf8Error;
use std::os::raw::*;
use std::{
io,
os::raw::*,
path::{Path, PathBuf},
str::Utf8Error,
sync::Arc,
};
use percent_encoding::percent_decode;
@ -127,13 +129,15 @@ impl Dnd {
DndState::Accepted => (1, self.atoms.action_private as c_long),
DndState::Rejected => (0, self.atoms.none as c_long),
};
self.xconn.send_client_msg(
target_window,
target_window,
self.atoms.status,
None,
[this_window as c_long, accepted, 0, 0, action],
).flush()
self.xconn
.send_client_msg(
target_window,
target_window,
self.atoms.status,
None,
[this_window as c_long, accepted, 0, 0, action],
)
.flush()
}
pub unsafe fn send_finished(
@ -146,24 +150,23 @@ impl Dnd {
DndState::Accepted => (1, self.atoms.action_private as c_long),
DndState::Rejected => (0, self.atoms.none as c_long),
};
self.xconn.send_client_msg(
target_window,
target_window,
self.atoms.finished,
None,
[this_window as c_long, accepted, action, 0, 0],
).flush()
self.xconn
.send_client_msg(
target_window,
target_window,
self.atoms.finished,
None,
[this_window as c_long, accepted, action, 0, 0],
)
.flush()
}
pub unsafe fn get_type_list(
&self,
source_window: c_ulong,
) -> Result<Vec<ffi::Atom>, util::GetPropertyError> {
self.xconn.get_property(
source_window,
self.atoms.type_list,
ffi::XA_ATOM,
)
self.xconn
.get_property(source_window, self.atoms.type_list, ffi::XA_ATOM)
}
pub unsafe fn convert_selection(&self, window: c_ulong, time: c_ulong) {
@ -181,11 +184,8 @@ impl Dnd {
&self,
window: c_ulong,
) -> Result<Vec<c_uchar>, util::GetPropertyError> {
self.xconn.get_property(
window,
self.atoms.selection,
self.atoms.uri_list,
)
self.xconn
.get_property(window, self.atoms.selection, self.atoms.uri_list)
}
pub fn parse_data(&self, data: &mut Vec<c_uchar>) -> Result<Vec<PathBuf>, DndDataParseError> {

View file

@ -1,20 +1,18 @@
use std::cell::RefCell;
use std::collections::HashMap;
use std::ptr;
use std::rc::Rc;
use std::slice;
use std::{cell::RefCell, collections::HashMap, ptr, rc::Rc, slice};
use libc::{c_int, c_uint, c_ulong, c_char, c_long};
use libc::{c_char, c_int, c_long, c_uint, c_ulong};
use super::{
mkdid, mkwid, get_xtarget, DeviceId, WindowId, Device, ImeReceiver, XExtension,
monitor, ffi, UnownedWindow, ScrollOrientation, GenericEventCookie,
events, util, DndState, Dnd, DeviceInfo
events, ffi, get_xtarget, mkdid, mkwid, monitor, util, Device, DeviceId, DeviceInfo, Dnd,
DndState, GenericEventCookie, ImeReceiver, ScrollOrientation, UnownedWindow, WindowId,
XExtension,
};
use event_loop::EventLoopWindowTarget as RootELW;
use event::{DeviceEvent, Event, KeyboardInput, ModifiersState, WindowEvent};
use dpi::{LogicalPosition,LogicalSize};
use crate::{
dpi::{LogicalPosition, LogicalSize},
event::{DeviceEvent, Event, KeyboardInput, ModifiersState, WindowEvent},
event_loop::EventLoopWindowTarget as RootELW,
};
pub(super) struct EventProcessor<T: 'static> {
pub(super) dnd: Dnd,
@ -22,7 +20,7 @@ pub(super) struct EventProcessor<T: 'static> {
pub(super) randr_event_offset: c_int,
pub(super) devices: RefCell<HashMap<DeviceId, Device>>,
pub(super) xi2ext: XExtension,
pub(super) target: Rc<RootELW<T>>
pub(super) target: Rc<RootELW<T>>,
}
impl<T: 'static> EventProcessor<T> {
@ -37,12 +35,14 @@ impl<T: 'static> EventProcessor<T> {
}
fn with_window<F, Ret>(&self, window_id: ffi::Window, callback: F) -> Option<Ret>
where F: Fn(&UnownedWindow) -> Ret
where
F: Fn(&UnownedWindow) -> Ret,
{
let mut deleted = false;
let window_id = WindowId(window_id);
let wt = get_xtarget(&self.target);
let result = wt.windows
let result = wt
.windows
.borrow()
.get(&window_id)
.and_then(|window| {
@ -62,7 +62,7 @@ impl<T: 'static> EventProcessor<T> {
self.with_window(window_id, |_| ()).is_some()
}
pub(super) unsafe fn poll_one_event(&mut self, event_ptr : *mut ffi::XEvent) -> bool {
pub(super) unsafe fn poll_one_event(&mut self, event_ptr: *mut ffi::XEvent) -> bool {
let wt = get_xtarget(&self.target);
// This function is used to poll and remove a single event
// from the Xlib event queue in a non-blocking, atomic way.
@ -73,7 +73,8 @@ impl<T: 'static> EventProcessor<T> {
unsafe extern "C" fn predicate(
_display: *mut ffi::Display,
_event: *mut ffi::XEvent,
_arg : *mut c_char) -> c_int {
_arg: *mut c_char,
) -> c_int {
// This predicate always returns "true" (1) to accept all events
1
}
@ -82,31 +83,41 @@ impl<T: 'static> EventProcessor<T> {
wt.xconn.display,
event_ptr,
Some(predicate),
std::ptr::null_mut());
std::ptr::null_mut(),
);
result != 0
}
pub(super) fn process_event<F>(&mut self, xev: &mut ffi::XEvent, mut callback: F)
where F: FnMut(Event<T>)
where
F: FnMut(Event<T>),
{
let wt = get_xtarget(&self.target);
// XFilterEvent tells us when an event has been discarded by the input method.
// Specifically, this involves all of the KeyPress events in compose/pre-edit sequences,
// along with an extra copy of the KeyRelease events. This also prevents backspace and
// arrow keys from being detected twice.
if ffi::True == unsafe { (wt.xconn.xlib.XFilterEvent)(
xev,
{ let xev: &ffi::XAnyEvent = xev.as_ref(); xev.window }
) } {
if ffi::True
== unsafe {
(wt.xconn.xlib.XFilterEvent)(xev, {
let xev: &ffi::XAnyEvent = xev.as_ref();
xev.window
})
}
{
return;
}
let event_type = xev.get_type();
match event_type {
ffi::MappingNotify => {
unsafe { (wt.xconn.xlib.XRefreshKeyboardMapping)(xev.as_mut()); }
wt.xconn.check_errors().expect("Failed to call XRefreshKeyboardMapping");
unsafe {
(wt.xconn.xlib.XRefreshKeyboardMapping)(xev.as_mut());
}
wt.xconn
.check_errors()
.expect("Failed to call XRefreshKeyboardMapping");
}
ffi::ClientMessage => {
@ -116,7 +127,20 @@ impl<T: 'static> EventProcessor<T> {
let window_id = mkwid(window);
if client_msg.data.get_long(0) as ffi::Atom == wt.wm_delete_window {
callback(Event::WindowEvent { window_id, event: WindowEvent::CloseRequested });
callback(Event::WindowEvent {
window_id,
event: WindowEvent::CloseRequested,
});
} else if client_msg.data.get_long(0) as ffi::Atom == wt.net_wm_ping {
let response_msg: &mut ffi::XClientMessageEvent = xev.as_mut();
response_msg.window = wt.root;
wt.xconn
.send_event(
wt.root,
Some(ffi::SubstructureNotifyMask | ffi::SubstructureRedirectMask),
*response_msg,
)
.queue();
} else if client_msg.message_type == self.dnd.atoms.enter {
let source_window = client_msg.data.get_long(0) as c_ulong;
let flags = client_msg.data.get_long(1);
@ -127,10 +151,11 @@ impl<T: 'static> EventProcessor<T> {
let type_list = vec![
client_msg.data.get_long(2) as c_ulong,
client_msg.data.get_long(3) as c_ulong,
client_msg.data.get_long(4) as c_ulong
client_msg.data.get_long(4) as c_ulong,
];
self.dnd.type_list = Some(type_list);
} else if let Ok(more_types) = unsafe { self.dnd.get_type_list(source_window) } {
} else if let Ok(more_types) = unsafe { self.dnd.get_type_list(source_window) }
{
self.dnd.type_list = Some(more_types);
}
} else if client_msg.message_type == self.dnd.atoms.position {
@ -178,18 +203,21 @@ impl<T: 'static> EventProcessor<T> {
// This results in the `SelectionNotify` event below
self.dnd.convert_selection(window, time);
}
self.dnd.send_status(window, source_window, DndState::Accepted)
self.dnd
.send_status(window, source_window, DndState::Accepted)
.expect("Failed to send `XdndStatus` message.");
}
} else {
unsafe {
self.dnd.send_status(window, source_window, DndState::Rejected)
self.dnd
.send_status(window, source_window, DndState::Rejected)
.expect("Failed to send `XdndStatus` message.");
}
self.dnd.reset();
}
} else if client_msg.message_type == self.dnd.atoms.drop {
let (source_window, state) = if let Some(source_window) = self.dnd.source_window {
let (source_window, state) = if let Some(source_window) = self.dnd.source_window
{
if let Some(Ok(ref path_list)) = self.dnd.result {
for path in path_list {
callback(Event::WindowEvent {
@ -206,7 +234,8 @@ impl<T: 'static> EventProcessor<T> {
(source_window, DndState::Rejected)
};
unsafe {
self.dnd.send_finished(window, source_window, state)
self.dnd
.send_finished(window, source_window, state)
.expect("Failed to send `XdndFinished` message.");
}
self.dnd.reset();
@ -274,15 +303,22 @@ impl<T: 'static> EventProcessor<T> {
let mut shared_state_lock = window.shared_state.lock();
let (mut resized, moved) = {
let resized = util::maybe_change(&mut shared_state_lock.size, new_inner_size);
let resized =
util::maybe_change(&mut shared_state_lock.size, new_inner_size);
let moved = if is_synthetic {
util::maybe_change(&mut shared_state_lock.inner_position, new_inner_position)
util::maybe_change(
&mut shared_state_lock.inner_position,
new_inner_position,
)
} else {
// Detect when frame extents change.
// Since this isn't synthetic, as per the notes above, this position is relative to the
// parent window.
let rel_parent = new_inner_position;
if util::maybe_change(&mut shared_state_lock.inner_position_rel_parent, rel_parent) {
if util::maybe_change(
&mut shared_state_lock.inner_position_rel_parent,
rel_parent,
) {
// This ensures we process the next `Moved`.
shared_state_lock.inner_position = None;
// Extra insurance against stale frame extents.
@ -297,18 +333,22 @@ impl<T: 'static> EventProcessor<T> {
let new_outer_position = if moved || shared_state_lock.position.is_none() {
// We need to convert client area position to window position.
let frame_extents = shared_state_lock.frame_extents
let frame_extents = shared_state_lock
.frame_extents
.as_ref()
.cloned()
.unwrap_or_else(|| {
let frame_extents = wt.xconn.get_frame_extents_heuristic(xwindow, wt.root);
let frame_extents =
wt.xconn.get_frame_extents_heuristic(xwindow, wt.root);
shared_state_lock.frame_extents = Some(frame_extents.clone());
frame_extents
});
let outer = frame_extents.inner_pos_to_outer(new_inner_position.0, new_inner_position.1);
let outer = frame_extents
.inner_pos_to_outer(new_inner_position.0, new_inner_position.1);
shared_state_lock.position = Some(outer);
if moved {
let logical_position = LogicalPosition::from_physical(outer, monitor.hidpi_factor);
let logical_position =
LogicalPosition::from_physical(outer, monitor.hidpi_factor);
events.moved = Some(WindowEvent::Moved(logical_position));
}
outer
@ -319,12 +359,13 @@ impl<T: 'static> EventProcessor<T> {
if is_synthetic {
// If we don't use the existing adjusted value when available, then the user can screw up the
// resizing by dragging across monitors *without* dropping the window.
let (width, height) = shared_state_lock.dpi_adjusted
let (width, height) = shared_state_lock
.dpi_adjusted
.unwrap_or_else(|| (xev.width as f64, xev.height as f64));
let last_hidpi_factor = shared_state_lock.guessed_dpi
.take()
.unwrap_or_else(|| {
shared_state_lock.last_monitor
let last_hidpi_factor =
shared_state_lock.guessed_dpi.take().unwrap_or_else(|| {
shared_state_lock
.last_monitor
.as_ref()
.map(|last_monitor| last_monitor.hidpi_factor)
.unwrap_or(1.0)
@ -337,7 +378,8 @@ impl<T: 'static> EventProcessor<T> {
new_hidpi_factor
};
if last_hidpi_factor != new_hidpi_factor {
events.dpi_changed = Some(WindowEvent::HiDpiFactorChanged(new_hidpi_factor));
events.dpi_changed =
Some(WindowEvent::HiDpiFactorChanged(new_hidpi_factor));
let (new_width, new_height, flusher) = window.adjust_for_dpi(
last_hidpi_factor,
new_hidpi_factor,
@ -357,7 +399,10 @@ impl<T: 'static> EventProcessor<T> {
// WMs constrain the window size, making the resize fail. This would cause an endless stream of
// XResizeWindow requests, making Xorg, the winit client, and the WM consume 100% of CPU.
if let Some(adjusted_size) = shared_state_lock.dpi_adjusted {
let rounded_size = (adjusted_size.0.round() as u32, adjusted_size.1.round() as u32);
let rounded_size = (
adjusted_size.0.round() as u32,
adjusted_size.1.round() as u32,
);
if new_inner_size == rounded_size || !util::wm_name_is_one_of(&["Xfwm4"]) {
// When this finally happens, the event will not be synthetic.
shared_state_lock.dpi_adjusted = None;
@ -374,7 +419,8 @@ impl<T: 'static> EventProcessor<T> {
}
if resized {
let logical_size = LogicalSize::from_physical(new_inner_size, monitor.hidpi_factor);
let logical_size =
LogicalSize::from_physical(new_inner_size, monitor.hidpi_factor);
events.resized = Some(WindowEvent::Resized(logical_size));
}
@ -427,7 +473,10 @@ impl<T: 'static> EventProcessor<T> {
.remove_context(window)
.expect("Failed to destroy input context");
callback(Event::WindowEvent { window_id, event: WindowEvent::Destroyed });
callback(Event::WindowEvent {
window_id,
event: WindowEvent::Destroyed,
});
}
ffi::Expose => {
@ -436,11 +485,14 @@ impl<T: 'static> EventProcessor<T> {
let window = xev.window;
let window_id = mkwid(window);
callback(Event::WindowEvent { window_id, event: WindowEvent::RedrawRequested });
callback(Event::WindowEvent {
window_id,
event: WindowEvent::RedrawRequested,
});
}
ffi::KeyPress | ffi::KeyRelease => {
use event::ElementState::{Pressed, Released};
use crate::event::ElementState::{Pressed, Released};
// Note that in compose/pre-edit sequences, this will always be Released.
let state = if xev.get_type() == ffi::KeyPress {
@ -493,7 +545,7 @@ impl<T: 'static> EventProcessor<T> {
virtual_keycode,
modifiers,
},
}
},
});
}
@ -515,17 +567,26 @@ impl<T: 'static> EventProcessor<T> {
}
ffi::GenericEvent => {
let guard = if let Some(e) = GenericEventCookie::from_event(&wt.xconn, *xev) { e } else { return };
let guard = if let Some(e) = GenericEventCookie::from_event(&wt.xconn, *xev) {
e
} else {
return;
};
let xev = &guard.cookie;
if self.xi2ext.opcode != xev.extension {
return;
}
use event::WindowEvent::{Focused, CursorEntered, MouseInput, CursorLeft, CursorMoved, MouseWheel, AxisMotion};
use event::ElementState::{Pressed, Released};
use event::MouseButton::{Left, Right, Middle, Other};
use event::MouseScrollDelta::LineDelta;
use event::{Touch, TouchPhase};
use crate::event::{
ElementState::{Pressed, Released},
MouseButton::{Left, Middle, Other, Right},
MouseScrollDelta::LineDelta,
Touch, TouchPhase,
WindowEvent::{
AxisMotion, CursorEntered, CursorLeft, CursorMoved, Focused, MouseInput,
MouseWheel,
},
};
match xev.evtype {
ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
@ -576,23 +637,25 @@ impl<T: 'static> EventProcessor<T> {
// Suppress emulated scroll wheel clicks, since we handle the real motion events for those.
// In practice, even clicky scroll wheels appear to be reported by evdev (and XInput2 in
// turn) as axis motion, so we don't otherwise special-case these button presses.
4 | 5 | 6 | 7 => if xev.flags & ffi::XIPointerEmulated == 0 {
callback(Event::WindowEvent {
window_id,
event: MouseWheel {
device_id,
delta: match xev.detail {
4 => LineDelta(0.0, 1.0),
5 => LineDelta(0.0, -1.0),
6 => LineDelta(-1.0, 0.0),
7 => LineDelta(1.0, 0.0),
_ => unreachable!(),
4 | 5 | 6 | 7 => {
if xev.flags & ffi::XIPointerEmulated == 0 {
callback(Event::WindowEvent {
window_id,
event: MouseWheel {
device_id,
delta: match xev.detail {
4 => LineDelta(0.0, 1.0),
5 => LineDelta(0.0, -1.0),
6 => LineDelta(-1.0, 0.0),
7 => LineDelta(1.0, 0.0),
_ => unreachable!(),
},
phase: TouchPhase::Moved,
modifiers,
},
phase: TouchPhase::Moved,
modifiers,
},
});
},
});
}
}
x => callback(Event::WindowEvent {
window_id,
@ -618,9 +681,8 @@ impl<T: 'static> EventProcessor<T> {
util::maybe_change(&mut shared_state_lock.cursor_pos, new_cursor_pos)
});
if cursor_moved == Some(true) {
let dpi_factor = self.with_window(xev.event, |window| {
window.hidpi_factor()
});
let dpi_factor =
self.with_window(xev.event, |window| window.hidpi_factor());
if let Some(dpi_factor) = dpi_factor {
let position = LogicalPosition::from_physical(
(xev.event_x as f64, xev.event_y as f64),
@ -644,7 +706,12 @@ impl<T: 'static> EventProcessor<T> {
// More gymnastics, for self.devices
let mut events = Vec::new();
{
let mask = unsafe { slice::from_raw_parts(xev.valuators.mask, xev.valuators.mask_len as usize) };
let mask = unsafe {
slice::from_raw_parts(
xev.valuators.mask,
xev.valuators.mask_len as usize,
)
};
let mut devices = self.devices.borrow_mut();
let physical_device = match devices.get_mut(&DeviceId(xev.sourceid)) {
Some(device) => device,
@ -652,10 +719,14 @@ impl<T: 'static> EventProcessor<T> {
};
let mut value = xev.valuators.values;
for i in 0..xev.valuators.mask_len*8 {
for i in 0..xev.valuators.mask_len * 8 {
if ffi::XIMaskIsSet(mask, i) {
let x = unsafe { *value };
if let Some(&mut (_, ref mut info)) = physical_device.scroll_axes.iter_mut().find(|&&mut (axis, _)| axis == i) {
if let Some(&mut (_, ref mut info)) = physical_device
.scroll_axes
.iter_mut()
.find(|&&mut (axis, _)| axis == i)
{
let delta = (x - info.position) / info.increment;
info.position = x;
events.push(Event::WindowEvent {
@ -663,9 +734,13 @@ impl<T: 'static> EventProcessor<T> {
event: MouseWheel {
device_id,
delta: match info.orientation {
ScrollOrientation::Horizontal => LineDelta(delta as f32, 0.0),
ScrollOrientation::Horizontal => {
LineDelta(delta as f32, 0.0)
}
// X11 vertical scroll coordinates are opposite to winit's
ScrollOrientation::Vertical => LineDelta(0.0, -delta as f32),
ScrollOrientation::Vertical => {
LineDelta(0.0, -delta as f32)
}
},
phase: TouchPhase::Moved,
modifiers,
@ -704,7 +779,8 @@ impl<T: 'static> EventProcessor<T> {
// presumably some other WMs. On those, `XI_Enter` doesn't include
// the physical device ID, so both `sourceid` and `deviceid` are
// the virtual device.
|| device_info.attachment == xev.sourceid {
|| device_info.attachment == xev.sourceid
{
let device_id = DeviceId(device_info.deviceid);
if let Some(device) = devices.get_mut(&device_id) {
device.reset_scroll_position(device_info);
@ -717,9 +793,9 @@ impl<T: 'static> EventProcessor<T> {
event: CursorEntered { device_id },
});
if let Some(dpi_factor) = self.with_window(xev.event, |window| {
window.hidpi_factor()
}) {
if let Some(dpi_factor) =
self.with_window(xev.event, |window| window.hidpi_factor())
{
let position = LogicalPosition::from_physical(
(xev.event_x as f64, xev.event_y as f64),
dpi_factor,
@ -732,7 +808,8 @@ impl<T: 'static> EventProcessor<T> {
// This needs to only be done after confirming the window still exists,
// since otherwise we risk getting a `BadWindow` error if the window was
// dropped with queued events.
let modifiers = wt.xconn
let modifiers = wt
.xconn
.query_pointer(xev.event, xev.deviceid)
.expect("Failed to query pointer device")
.get_modifier_state();
@ -756,19 +833,20 @@ impl<T: 'static> EventProcessor<T> {
if !window_closed {
callback(Event::WindowEvent {
window_id: mkwid(xev.event),
event: CursorLeft { device_id: mkdid(xev.deviceid) },
event: CursorLeft {
device_id: mkdid(xev.deviceid),
},
});
}
}
ffi::XI_FocusIn => {
let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) };
let dpi_factor = match self.with_window(xev.event, |window| {
window.hidpi_factor()
}) {
Some(dpi_factor) => dpi_factor,
None => return,
};
let dpi_factor =
match self.with_window(xev.event, |window| window.hidpi_factor()) {
Some(dpi_factor) => dpi_factor,
None => return,
};
let window_id = mkwid(xev.event);
wt.ime
@ -776,11 +854,15 @@ impl<T: 'static> EventProcessor<T> {
.focus(xev.event)
.expect("Failed to focus input context");
callback(Event::WindowEvent { window_id, event: Focused(true) });
callback(Event::WindowEvent {
window_id,
event: Focused(true),
});
// The deviceid for this event is for a keyboard instead of a pointer,
// so we have to do a little extra work.
let pointer_id = self.devices
let pointer_id = self
.devices
.borrow()
.get(&DeviceId(xev.deviceid))
.map(|device| device.attachment)
@ -796,12 +878,14 @@ impl<T: 'static> EventProcessor<T> {
device_id: mkdid(pointer_id),
position,
modifiers: ModifiersState::from(xev.mods),
}
},
});
}
ffi::XI_FocusOut => {
let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
if !self.window_exists(xev.event) { return; }
if !self.window_exists(xev.event) {
return;
}
wt.ime
.borrow_mut()
.unfocus(xev.event)
@ -819,11 +903,10 @@ impl<T: 'static> EventProcessor<T> {
ffi::XI_TouchBegin => TouchPhase::Started,
ffi::XI_TouchUpdate => TouchPhase::Moved,
ffi::XI_TouchEnd => TouchPhase::Ended,
_ => unreachable!()
_ => unreachable!(),
};
let dpi_factor = self.with_window(xev.event, |window| {
window.hidpi_factor()
});
let dpi_factor =
self.with_window(xev.event, |window| window.hidpi_factor());
if let Some(dpi_factor) = dpi_factor {
let location = LogicalPosition::from_physical(
(xev.event_x as f64, xev.event_y as f64),
@ -844,14 +927,17 @@ impl<T: 'static> EventProcessor<T> {
ffi::XI_RawButtonPress | ffi::XI_RawButtonRelease => {
let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
if xev.flags & ffi::XIPointerEmulated == 0 {
callback(Event::DeviceEvent { device_id: mkdid(xev.deviceid), event: DeviceEvent::Button {
button: xev.detail as u32,
state: match xev.evtype {
ffi::XI_RawButtonPress => Pressed,
ffi::XI_RawButtonRelease => Released,
_ => unreachable!(),
callback(Event::DeviceEvent {
device_id: mkdid(xev.deviceid),
event: DeviceEvent::Button {
button: xev.detail as u32,
state: match xev.evtype {
ffi::XI_RawButtonPress => Pressed,
ffi::XI_RawButtonRelease => Released,
_ => unreachable!(),
},
},
}});
});
}
}
@ -859,11 +945,16 @@ impl<T: 'static> EventProcessor<T> {
let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
let did = mkdid(xev.deviceid);
let mask = unsafe { slice::from_raw_parts(xev.valuators.mask, xev.valuators.mask_len as usize) };
let mask = unsafe {
slice::from_raw_parts(
xev.valuators.mask,
xev.valuators.mask_len as usize,
)
};
let mut value = xev.raw_values;
let mut mouse_delta = (0.0, 0.0);
let mut scroll_delta = (0.0, 0.0);
for i in 0..xev.valuators.mask_len*8 {
for i in 0..xev.valuators.mask_len * 8 {
if ffi::XIMaskIsSet(mask, i) {
let x = unsafe { *value };
// We assume that every XInput2 device with analog axes is a pointing device emitting
@ -873,24 +964,31 @@ impl<T: 'static> EventProcessor<T> {
1 => mouse_delta.1 = x,
2 => scroll_delta.0 = x as f32,
3 => scroll_delta.1 = x as f32,
_ => {},
_ => {}
}
callback(Event::DeviceEvent { device_id: did, event: DeviceEvent::Motion {
axis: i as u32,
value: x,
}});
callback(Event::DeviceEvent {
device_id: did,
event: DeviceEvent::Motion {
axis: i as u32,
value: x,
},
});
value = unsafe { value.offset(1) };
}
}
if mouse_delta != (0.0, 0.0) {
callback(Event::DeviceEvent { device_id: did, event: DeviceEvent::MouseMotion {
delta: mouse_delta,
}});
callback(Event::DeviceEvent {
device_id: did,
event: DeviceEvent::MouseMotion { delta: mouse_delta },
});
}
if scroll_delta != (0.0, 0.0) {
callback(Event::DeviceEvent { device_id: did, event: DeviceEvent::MouseWheel {
delta: LineDelta(scroll_delta.0, scroll_delta.1),
}});
callback(Event::DeviceEvent {
device_id: did,
event: DeviceEvent::MouseWheel {
delta: LineDelta(scroll_delta.0, scroll_delta.1),
},
});
}
}
@ -905,7 +1003,9 @@ impl<T: 'static> EventProcessor<T> {
let device_id = xev.sourceid;
let keycode = xev.detail;
if keycode < 8 { return; }
if keycode < 8 {
return;
}
let scancode = (keycode - 8) as u32;
let keysym = unsafe {
@ -915,7 +1015,9 @@ impl<T: 'static> EventProcessor<T> {
0,
)
};
wt.xconn.check_errors().expect("Failed to lookup raw keysym");
wt.xconn
.check_errors()
.expect("Failed to lookup raw keysym");
let virtual_keycode = events::keysym_to_element(keysym as c_uint);
@ -937,12 +1039,21 @@ impl<T: 'static> EventProcessor<T> {
ffi::XI_HierarchyChanged => {
let xev: &ffi::XIHierarchyEvent = unsafe { &*(xev.data as *const _) };
for info in unsafe { slice::from_raw_parts(xev.info, xev.num_info as usize) } {
for info in
unsafe { slice::from_raw_parts(xev.info, xev.num_info as usize) }
{
if 0 != info.flags & (ffi::XISlaveAdded | ffi::XIMasterAdded) {
self.init_device(info.deviceid);
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 });
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.borrow_mut();
devices.remove(&DeviceId(info.deviceid));
}
@ -951,7 +1062,7 @@ impl<T: 'static> EventProcessor<T> {
_ => {}
}
},
}
_ => {
if event_type == self.randr_event_offset {
// In the future, it would be quite easy to emit monitor hotplug events.
@ -972,10 +1083,11 @@ impl<T: 'static> EventProcessor<T> {
callback(Event::WindowEvent {
window_id: mkwid(window_id.0),
event: WindowEvent::HiDpiFactorChanged(
new_monitor.hidpi_factor
new_monitor.hidpi_factor,
),
});
let (width, height) = window.inner_size_physical();
let (width, height) =
window.inner_size_physical();
let (_, _, flusher) = window.adjust_for_dpi(
prev_monitor.hidpi_factor,
new_monitor.hidpi_factor,
@ -991,13 +1103,13 @@ impl<T: 'static> EventProcessor<T> {
}
}
}
},
}
}
match self.ime_receiver.try_recv() {
Ok((window_id, x, y)) => {
wt.ime.borrow_mut().send_xim_spot(window_id, x, y);
},
}
Err(_) => (),
}
}

View file

@ -1,6 +1,6 @@
use libc;
use super::ffi;
use event::VirtualKeyCode;
use crate::event::VirtualKeyCode;
use libc;
pub fn keysym_to_element(keysym: libc::c_uint) -> Option<VirtualKeyCode> {
Some(match keysym {
@ -1003,6 +1003,6 @@ pub fn keysym_to_element(keysym: libc::c_uint) -> Option<VirtualKeyCode> {
ffi::XF86XK_Copy => VirtualKeyCode::Copy,
ffi::XF86XK_Paste => VirtualKeyCode::Paste,
ffi::XF86XK_Cut => VirtualKeyCode::Cut,
_ => return None
_ => return None,
})
}

View file

@ -1,9 +1,4 @@
pub use x11_dl::keysym::*;
pub use x11_dl::xcursor::*;
pub use x11_dl::xlib::*;
pub use x11_dl::xinput::*;
pub use x11_dl::xinput2::*;
pub use x11_dl::xlib_xcb::*;
pub use x11_dl::error::OpenError;
pub use x11_dl::xrandr::*;
pub use x11_dl::xrender::*;
pub use x11_dl::{
error::OpenError, keysym::*, xcursor::*, xinput::*, xinput2::*, xlib::*, xlib_xcb::*,
xrandr::*, xrender::*,
};

View file

@ -1,13 +1,12 @@
use std::ptr;
use std::sync::Arc;
use std::collections::HashMap;
use std::os::raw::c_char;
use std::{collections::HashMap, os::raw::c_char, ptr, sync::Arc};
use super::{ffi, XConnection, XError};
use super::inner::{close_im, ImeInner};
use super::input_method::PotentialInputMethods;
use super::context::{ImeContextCreationError, ImeContext};
use super::{
context::{ImeContext, ImeContextCreationError},
inner::{close_im, ImeInner},
input_method::PotentialInputMethods,
};
pub unsafe fn xim_set_callback(
xconn: &Arc<XConnection>,
@ -17,12 +16,7 @@ pub unsafe fn xim_set_callback(
) -> Result<(), XError> {
// It's advisable to wrap variadic FFI functions in our own functions, as we want to minimize
// access that isn't type-checked.
(xconn.xlib.XSetIMValues)(
xim,
field,
callback,
ptr::null_mut::<()>(),
);
(xconn.xlib.XSetIMValues)(xim, field, callback, ptr::null_mut::<()>());
xconn.check_errors()
}
@ -107,18 +101,14 @@ unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> {
let _ = close_im(xconn, new_im.im);
}
result
}.map_err(ReplaceImError::SetDestroyCallbackFailed)?;
}
.map_err(ReplaceImError::SetDestroyCallbackFailed)?;
let mut new_contexts = HashMap::new();
for (window, old_context) in (*inner).contexts.iter() {
let spot = old_context.as_ref().map(|old_context| old_context.ic_spot);
let new_context = {
let result = ImeContext::new(
xconn,
new_im.im,
*window,
spot,
);
let result = ImeContext::new(xconn, new_im.im, *window, spot);
if result.is_err() {
let _ = close_im(xconn, new_im.im);
}
@ -137,7 +127,7 @@ unsafe fn replace_im(inner: *mut ImeInner) -> Result<(), ReplaceImError> {
Ok(())
}
pub unsafe extern fn xim_instantiate_callback(
pub unsafe extern "C" fn xim_instantiate_callback(
_display: *mut ffi::Display,
client_data: ffi::XPointer,
// This field is unsupplied.
@ -160,7 +150,7 @@ pub unsafe extern fn xim_instantiate_callback(
// This callback is triggered when the input method is closed on the server end. When this
// happens, XCloseIM/XDestroyIC doesn't need to be called, as the resources have already been
// free'd (attempting to do so causes our connection to freeze).
pub unsafe extern fn xim_destroy_callback(
pub unsafe extern "C" fn xim_destroy_callback(
_xim: ffi::XIM,
client_data: ffi::XPointer,
// This field is unsupplied.

View file

@ -1,6 +1,8 @@
use std::ptr;
use std::sync::Arc;
use std::os::raw::{c_short, c_void};
use std::{
os::raw::{c_short, c_void},
ptr,
sync::Arc,
};
use super::{ffi, util, XConnection, XError};
@ -22,7 +24,8 @@ unsafe fn create_pre_edit_attr<'a>(
ic_spot,
ptr::null_mut::<()>(),
),
).expect("XVaCreateNestedList returned NULL")
)
.expect("XVaCreateNestedList returned NULL")
}
// WARNING: this struct doesn't destroy its XIC resource when dropped.
@ -49,7 +52,9 @@ impl ImeContext {
};
let ic = ic.ok_or(ImeContextCreationError::Null)?;
xconn.check_errors().map_err(ImeContextCreationError::XError)?;
xconn
.check_errors()
.map_err(ImeContextCreationError::XError)?;
Ok(ImeContext {
ic,

View file

@ -1,12 +1,8 @@
use std::mem;
use std::ptr;
use std::sync::Arc;
use std::collections::HashMap;
use std::{collections::HashMap, mem, ptr, sync::Arc};
use super::{ffi, XConnection, XError};
use super::input_method::PotentialInputMethods;
use super::context::ImeContext;
use super::{context::ImeContext, input_method::PotentialInputMethods};
pub unsafe fn close_im(xconn: &Arc<XConnection>, im: ffi::XIM) -> Result<(), XError> {
(xconn.xlib.XCloseIM)(im);
@ -33,10 +29,7 @@ pub struct ImeInner {
}
impl ImeInner {
pub fn new(
xconn: Arc<XConnection>,
potential_input_methods: PotentialInputMethods,
) -> Self {
pub fn new(xconn: Arc<XConnection>, potential_input_methods: PotentialInputMethods) -> Self {
ImeInner {
xconn,
im: ptr::null_mut(),

View file

@ -1,9 +1,11 @@
use std::env;
use std::fmt;
use std::ptr;
use std::sync::Arc;
use std::os::raw::c_char;
use std::ffi::{CStr, CString, IntoStringError};
use std::{
env,
ffi::{CStr, CString, IntoStringError},
fmt,
os::raw::c_char,
ptr,
sync::Arc,
};
use parking_lot::Mutex;
@ -13,10 +15,7 @@ lazy_static! {
static ref GLOBAL_LOCK: Mutex<()> = Default::default();
}
unsafe fn open_im(
xconn: &Arc<XConnection>,
locale_modifiers: &CStr,
) -> Option<ffi::XIM> {
unsafe fn open_im(xconn: &Arc<XConnection>, locale_modifiers: &CStr) -> Option<ffi::XIM> {
let _lock = GLOBAL_LOCK.lock();
// XSetLocaleModifiers returns...
@ -97,11 +96,9 @@ unsafe fn get_xim_servers(xconn: &Arc<XConnection>) -> Result<Vec<String>, GetXi
let root = (xconn.xlib.XDefaultRootWindow)(xconn.display);
let mut atoms: Vec<ffi::Atom> = xconn.get_property(
root,
servers_atom,
ffi::XA_ATOM,
).map_err(GetXimServersError::GetPropertyError)?;
let mut atoms: Vec<ffi::Atom> = xconn
.get_property(root, servers_atom, ffi::XA_ATOM)
.map_err(GetXimServersError::GetPropertyError)?;
let mut names: Vec<*const c_char> = Vec::with_capacity(atoms.len());
(xconn.xlib.XGetAtomNames)(
@ -135,15 +132,12 @@ impl InputMethodName {
pub fn from_string(string: String) -> Self {
let c_string = CString::new(string.clone())
.expect("String used to construct CString contained null byte");
InputMethodName {
c_string,
string,
}
InputMethodName { c_string, string }
}
pub fn from_str(string: &str) -> Self {
let c_string = CString::new(string)
.expect("String used to construct CString contained null byte");
let c_string =
CString::new(string).expect("String used to construct CString contained null byte");
InputMethodName {
c_string,
string: string.to_owned(),
@ -152,7 +146,7 @@ impl InputMethodName {
}
impl fmt::Debug for InputMethodName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.string.fmt(f)
}
}
@ -254,7 +248,7 @@ impl PotentialInputMethods {
pub fn open_im(
&mut self,
xconn: &Arc<XConnection>,
callback: Option<&Fn() -> ()>,
callback: Option<&dyn Fn() -> ()>,
) -> InputMethodResult {
use self::InputMethodResult::*;

View file

@ -1,20 +1,24 @@
// Important: all XIM calls need to happen from the same thread!
mod callbacks;
mod context;
mod inner;
mod input_method;
mod context;
mod callbacks;
use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender};
use std::sync::{
mpsc::{Receiver, Sender},
Arc,
};
use super::{ffi, util, XConnection, XError};
use self::inner::{close_im, ImeInner};
use self::input_method::PotentialInputMethods;
use self::context::ImeContext;
use self::callbacks::*;
pub use self::context::ImeContextCreationError;
use self::{
callbacks::*,
context::ImeContext,
inner::{close_im, ImeInner},
input_method::PotentialInputMethods,
};
pub type ImeReceiver = Receiver<(ffi::Window, i16, i16)>;
pub type ImeSender = Sender<(ffi::Window, i16, i16)>;
@ -37,10 +41,7 @@ impl Ime {
let potential_input_methods = PotentialInputMethods::new(&xconn);
let (mut inner, client_data) = {
let mut inner = Box::new(ImeInner::new(
xconn,
potential_input_methods,
));
let mut inner = Box::new(ImeInner::new(xconn, potential_input_methods));
let inner_ptr = Box::into_raw(inner);
let client_data = inner_ptr as _;
let destroy_callback = ffi::XIMCallback {
@ -54,9 +55,12 @@ impl Ime {
let xconn = Arc::clone(&inner.xconn);
let input_method = inner.potential_input_methods.open_im(&xconn, Some(&|| {
let _ = unsafe { set_instantiate_callback(&xconn, client_data) };
}));
let input_method = inner.potential_input_methods.open_im(
&xconn,
Some(&|| {
let _ = unsafe { set_instantiate_callback(&xconn, client_data) };
}),
);
let is_fallback = input_method.is_fallback();
if let Some(input_method) = input_method.ok() {
@ -84,19 +88,12 @@ impl Ime {
// Ok(_) indicates that nothing went wrong internally
// Ok(true) indicates that the action was actually performed
// Ok(false) indicates that the action is not presently applicable
pub fn create_context(&mut self, window: ffi::Window)
-> Result<bool, ImeContextCreationError>
{
pub fn create_context(&mut self, window: ffi::Window) -> Result<bool, ImeContextCreationError> {
let context = if self.is_destroyed() {
// Create empty entry in map, so that when IME is rebuilt, this window has a context.
None
} else {
Some(unsafe { ImeContext::new(
&self.inner.xconn,
self.inner.im,
window,
None,
) }?)
Some(unsafe { ImeContext::new(&self.inner.xconn, self.inner.im, window, None) }?)
};
self.inner.contexts.insert(window, context);
Ok(!self.is_destroyed())

View file

@ -1,49 +1,58 @@
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
pub mod ffi;
mod dnd;
mod event_processor;
mod events;
pub mod ffi;
mod ime;
mod monitor;
pub mod util;
mod window;
mod xdisplay;
mod dnd;
mod ime;
pub mod util;
mod event_processor;
pub use self::monitor::MonitorHandle;
pub use self::window::UnownedWindow;
pub use self::xdisplay::{XConnection, XNotSupported, XError};
pub use self::{
monitor::MonitorHandle,
window::UnownedWindow,
xdisplay::{XConnection, XError, XNotSupported},
};
use std::{mem, slice};
use std::cell::RefCell;
use std::collections::{VecDeque, HashMap, HashSet};
use std::ffi::CStr;
use std::ops::Deref;
use std::os::raw::*;
use std::rc::Rc;
use std::sync::{Arc, mpsc, Weak, Mutex};
use std::{
cell::RefCell,
collections::{HashMap, HashSet, VecDeque},
ffi::CStr,
mem,
ops::Deref,
os::raw::*,
rc::Rc,
slice,
sync::{mpsc, Arc, Mutex, Weak},
};
use libc::{self, setlocale, LC_CTYPE};
use error::OsError as RootOsError;
use event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW};
use event::{WindowEvent, Event};
use platform_impl::PlatformSpecificWindowBuilderAttributes;
use platform_impl::platform::sticky_exit_callback;
use window::{WindowAttributes};
use self::dnd::{Dnd, DndState};
use self::ime::{ImeReceiver, ImeSender, ImeCreationError, Ime};
use self::event_processor::EventProcessor;
use self::{
dnd::{Dnd, DndState},
event_processor::EventProcessor,
ime::{Ime, ImeCreationError, ImeReceiver, ImeSender},
};
use crate::{
error::OsError as RootOsError,
event::{Event, WindowEvent},
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootELW},
platform_impl::{platform::sticky_exit_callback, PlatformSpecificWindowBuilderAttributes},
window::WindowAttributes,
};
pub struct EventLoopWindowTarget<T> {
xconn: Arc<XConnection>,
wm_delete_window: ffi::Atom,
net_wm_ping: ffi::Atom,
ime_sender: ImeSender,
root: ffi::Window,
ime: RefCell<Ime>,
windows: RefCell<HashMap<WindowId, Weak<UnownedWindow>>>,
pending_redraws: Arc<Mutex<HashSet<WindowId>>>,
_marker: ::std::marker::PhantomData<T>
_marker: ::std::marker::PhantomData<T>,
}
pub struct EventLoop<T: 'static> {
@ -53,7 +62,7 @@ pub struct EventLoop<T: 'static> {
pending_user_events: Rc<RefCell<VecDeque<T>>>,
user_sender: ::calloop::channel::Sender<T>,
pending_events: Rc<RefCell<VecDeque<Event<T>>>>,
target: Rc<RootELW<T>>
target: Rc<RootELW<T>>,
}
#[derive(Clone)]
@ -67,13 +76,17 @@ impl<T: 'static> EventLoop<T> {
let wm_delete_window = unsafe { xconn.get_atom_unchecked(b"WM_DELETE_WINDOW\0") };
let net_wm_ping = unsafe { xconn.get_atom_unchecked(b"_NET_WM_PING\0") };
let dnd = Dnd::new(Arc::clone(&xconn))
.expect("Failed to call XInternAtoms when initializing drag and drop");
let (ime_sender, ime_receiver) = mpsc::channel();
// Input methods will open successfully without setting the locale, but it won't be
// possible to actually commit pre-edit sequences.
unsafe { setlocale(LC_CTYPE, b"\0".as_ptr() as *const _); }
unsafe {
setlocale(LC_CTYPE, b"\0".as_ptr() as *const _);
}
let ime = RefCell::new({
let result = Ime::new(Arc::clone(&xconn));
if let Err(ImeCreationError::OpenFailure(ref state)) = result {
@ -82,7 +95,8 @@ impl<T: 'static> EventLoop<T> {
result.expect("Failed to set input method destruction callback")
});
let randr_event_offset = xconn.select_xrandr_input(root)
let randr_event_offset = xconn
.select_xrandr_input(root)
.expect("Failed to query XRandR extension");
let xi2ext = unsafe {
@ -96,7 +110,8 @@ impl<T: 'static> EventLoop<T> {
b"XInputExtension\0".as_ptr() as *const c_char,
&mut result.opcode as *mut c_int,
&mut result.first_event_id as *mut c_int,
&mut result.first_error_id as *mut c_int);
&mut result.first_error_id as *mut c_int,
);
if res == ffi::False {
panic!("X server missing XInput extension");
}
@ -110,18 +125,18 @@ impl<T: 'static> EventLoop<T> {
xconn.display,
&mut xinput_major_ver,
&mut xinput_minor_ver,
) != ffi::Success as libc::c_int {
) != ffi::Success as libc::c_int
{
panic!(
"X server has XInput extension {}.{} but does not support XInput2",
xinput_major_ver,
xinput_minor_ver,
xinput_major_ver, xinput_minor_ver,
);
}
}
xconn.update_cached_wm_info(root);
let target = Rc::new(RootELW{
let target = Rc::new(RootELW {
p: super::EventLoopWindowTarget::X(EventLoopWindowTarget {
ime,
root,
@ -130,9 +145,10 @@ impl<T: 'static> EventLoop<T> {
ime_sender,
xconn,
wm_delete_window,
net_wm_ping,
pending_redraws: Default::default(),
}),
_marker: ::std::marker::PhantomData
_marker: ::std::marker::PhantomData,
});
// A calloop event loop to drive us
@ -144,11 +160,14 @@ impl<T: 'static> EventLoop<T> {
let (user_sender, user_channel) = ::calloop::channel::channel();
let _user_source = inner_loop.handle().insert_source(user_channel, move |evt, &mut()| {
if let ::calloop::channel::Event::Msg(msg) = evt {
pending_user_events2.borrow_mut().push_back(msg);
}
}).unwrap();
let _user_source = inner_loop
.handle()
.insert_source(user_channel, move |evt, &mut ()| {
if let ::calloop::channel::Event::Msg(msg) = evt {
pending_user_events2.borrow_mut().push_back(msg);
}
})
.unwrap();
// Handle X11 events
let pending_events: Rc<RefCell<VecDeque<_>>> = Default::default();
@ -164,20 +183,20 @@ impl<T: 'static> EventLoop<T> {
// Register for device hotplug events
// (The request buffer is flushed during `init_device`)
get_xtarget(&target).xconn.select_xinput_events(
root,
ffi::XIAllDevices,
ffi::XI_HierarchyChangedMask,
).queue();
get_xtarget(&target)
.xconn
.select_xinput_events(root, ffi::XIAllDevices, ffi::XI_HierarchyChangedMask)
.queue();
processor.init_device(ffi::XIAllDevices);
// Setup the X11 event source
let mut x11_events = ::calloop::generic::Generic::from_raw_fd(get_xtarget(&target).xconn.x11_fd);
let mut x11_events =
::calloop::generic::Generic::from_raw_fd(get_xtarget(&target).xconn.x11_fd);
x11_events.set_interest(::calloop::mio::Ready::readable());
let _x11_source = inner_loop.handle().insert_source(
x11_events,
{
let _x11_source = inner_loop
.handle()
.insert_source(x11_events, {
let pending_events = pending_events.clone();
let mut callback = move |event| {
pending_events.borrow_mut().push_back(event);
@ -191,8 +210,8 @@ impl<T: 'static> EventLoop<T> {
}
}
}
}
).unwrap();
})
.unwrap();
let result = EventLoop {
inner_loop,
@ -201,7 +220,7 @@ impl<T: 'static> EventLoop<T> {
_user_source,
user_sender,
pending_user_events,
target
target,
};
result
@ -224,7 +243,8 @@ impl<T: 'static> EventLoop<T> {
}
pub fn run_return<F>(&mut self, mut callback: F)
where F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow)
where
F: FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
{
let mut control_flow = ControlFlow::default();
let wt = get_xtarget(&self.target);
@ -243,10 +263,10 @@ impl<T: 'static> EventLoop<T> {
let mut guard = self.pending_user_events.borrow_mut();
for evt in guard.drain(..) {
sticky_exit_callback(
::event::Event::UserEvent(evt),
crate::event::Event::UserEvent(evt),
&self.target,
&mut control_flow,
&mut callback
&mut callback,
);
}
}
@ -256,48 +276,49 @@ impl<T: 'static> EventLoop<T> {
for wid in guard.drain() {
sticky_exit_callback(
Event::WindowEvent {
window_id: ::window::WindowId(super::WindowId::X(wid)),
event: WindowEvent::RedrawRequested
window_id: crate::window::WindowId(super::WindowId::X(wid)),
event: WindowEvent::RedrawRequested,
},
&self.target,
&mut control_flow,
&mut callback
&mut callback,
);
}
}
// send Events cleared
{
sticky_exit_callback(
::event::Event::EventsCleared,
crate::event::Event::EventsCleared,
&self.target,
&mut control_flow,
&mut callback
&mut callback,
);
}
// flush the X11 connection
unsafe { (wt.xconn.xlib.XFlush)(wt.xconn.display); }
match control_flow {
ControlFlow::Exit => break,
ControlFlow::Poll => {
// non-blocking dispatch
self.inner_loop.dispatch(Some(::std::time::Duration::from_millis(0)), &mut ()).unwrap();
control_flow = ControlFlow::default();
callback(::event::Event::NewEvents(::event::StartCause::Poll), &self.target, &mut control_flow);
},
self.inner_loop
.dispatch(Some(::std::time::Duration::from_millis(0)), &mut ())
.unwrap();
callback(
crate::event::Event::NewEvents(crate::event::StartCause::Poll),
&self.target,
&mut control_flow,
);
}
ControlFlow::Wait => {
self.inner_loop.dispatch(None, &mut ()).unwrap();
control_flow = ControlFlow::default();
callback(
::event::Event::NewEvents(::event::StartCause::WaitCancelled {
crate::event::Event::NewEvents(crate::event::StartCause::WaitCancelled {
start: ::std::time::Instant::now(),
requested_resume: None
requested_resume: None,
}),
&self.target,
&mut control_flow
&mut control_flow,
);
},
}
ControlFlow::WaitUntil(deadline) => {
let start = ::std::time::Instant::now();
// compute the blocking duration
@ -307,36 +328,44 @@ impl<T: 'static> EventLoop<T> {
::std::time::Duration::from_millis(0)
};
self.inner_loop.dispatch(Some(duration), &mut ()).unwrap();
control_flow = ControlFlow::default();
let now = std::time::Instant::now();
if now < deadline {
callback(
::event::Event::NewEvents(::event::StartCause::WaitCancelled {
start,
requested_resume: Some(deadline)
}),
crate::event::Event::NewEvents(
crate::event::StartCause::WaitCancelled {
start,
requested_resume: Some(deadline),
},
),
&self.target,
&mut control_flow
&mut control_flow,
);
} else {
callback(
::event::Event::NewEvents(::event::StartCause::ResumeTimeReached {
start,
requested_resume: deadline
}),
crate::event::Event::NewEvents(
crate::event::StartCause::ResumeTimeReached {
start,
requested_resume: deadline,
},
),
&self.target,
&mut control_flow
&mut control_flow,
);
}
}
}
}
callback(::event::Event::LoopDestroyed, &self.target, &mut control_flow);
callback(
crate::event::Event::LoopDestroyed,
&self.target,
&mut control_flow,
);
}
pub fn run<F>(mut self, callback: F) -> !
where F: 'static + FnMut(Event<T>, &RootELW<T>, &mut ControlFlow)
where
F: 'static + FnMut(Event<T>, &RootELW<T>, &mut ControlFlow),
{
self.run_return(callback);
::std::process::exit(0);
@ -345,10 +374,10 @@ impl<T: 'static> EventLoop<T> {
fn get_xtarget<T>(rt: &RootELW<T>) -> &EventLoopWindowTarget<T> {
if let super::EventLoopWindowTarget::X(ref target) = rt.p {
target
} else {
unreachable!();
}
target
} else {
unreachable!();
}
}
impl<T: 'static> EventLoopProxy<T> {
@ -368,19 +397,17 @@ impl<'a> DeviceInfo<'a> {
unsafe {
let mut count = mem::uninitialized();
let info = (xconn.xinput2.XIQueryDevice)(xconn.display, device, &mut count);
xconn.check_errors()
.ok()
.and_then(|_| {
if info.is_null() || count == 0 {
None
} else {
Some(DeviceInfo {
xconn,
info,
count: count as usize,
})
}
})
xconn.check_errors().ok().and_then(|_| {
if info.is_null() || count == 0 {
None
} else {
Some(DeviceInfo {
xconn,
info,
count: count as usize,
})
}
})
}
}
}
@ -431,10 +458,11 @@ impl Window {
pub fn new<T>(
event_loop: &EventLoopWindowTarget<T>,
attribs: WindowAttributes,
pl_attribs: PlatformSpecificWindowBuilderAttributes
pl_attribs: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, RootOsError> {
let window = Arc::new(UnownedWindow::new(&event_loop, attribs, pl_attribs)?);
event_loop.windows
event_loop
.windows
.borrow_mut()
.insert(window.id(), Arc::downgrade(&window));
Ok(Window(window))
@ -457,11 +485,14 @@ impl Drop for Window {
/// extract the cookie from a GenericEvent XEvent and release the cookie data once it has been processed
struct GenericEventCookie<'a> {
xconn: &'a XConnection,
cookie: ffi::XGenericEventCookie
cookie: ffi::XGenericEventCookie,
}
impl<'a> GenericEventCookie<'a> {
fn from_event<'b>(xconn: &'b XConnection, event: ffi::XEvent) -> Option<GenericEventCookie<'b>> {
fn from_event<'b>(
xconn: &'b XConnection,
event: ffi::XEvent,
) -> Option<GenericEventCookie<'b>> {
unsafe {
let mut cookie: ffi::XGenericEventCookie = From::from(event);
if (xconn.xlib.XGetEventData)(xconn.display, &mut cookie) == ffi::True {
@ -488,8 +519,12 @@ struct XExtension {
first_error_id: c_int,
}
fn mkwid(w: ffi::Window) -> ::window::WindowId { ::window::WindowId(::platform_impl::WindowId::X(WindowId(w))) }
fn mkdid(w: c_int) -> ::event::DeviceId { ::event::DeviceId(::platform_impl::DeviceId::X(DeviceId(w))) }
fn mkwid(w: ffi::Window) -> crate::window::WindowId {
crate::window::WindowId(crate::platform_impl::WindowId::X(WindowId(w)))
}
fn mkdid(w: c_int) -> crate::event::DeviceId {
crate::event::DeviceId(crate::platform_impl::DeviceId::X(DeviceId(w)))
}
#[derive(Debug)]
struct Device {
@ -528,23 +563,30 @@ impl Device {
| ffi::XI_RawKeyPressMask
| ffi::XI_RawKeyReleaseMask;
// The request buffer is flushed when we poll for events
wt.xconn.select_xinput_events(wt.root, info.deviceid, mask).queue();
wt.xconn
.select_xinput_events(wt.root, info.deviceid, mask)
.queue();
// Identify scroll axes
for class_ptr in Device::classes(info) {
let class = unsafe { &**class_ptr };
match class._type {
ffi::XIScrollClass => {
let info = unsafe { mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIScrollClassInfo>(class) };
scroll_axes.push((info.number, ScrollAxis {
increment: info.increment,
orientation: match info.scroll_type {
ffi::XIScrollTypeHorizontal => ScrollOrientation::Horizontal,
ffi::XIScrollTypeVertical => ScrollOrientation::Vertical,
_ => { unreachable!() }
let info = unsafe {
mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIScrollClassInfo>(class)
};
scroll_axes.push((
info.number,
ScrollAxis {
increment: info.increment,
orientation: match info.scroll_type {
ffi::XIScrollTypeHorizontal => ScrollOrientation::Horizontal,
ffi::XIScrollTypeVertical => ScrollOrientation::Vertical,
_ => unreachable!(),
},
position: 0.0,
},
position: 0.0,
}));
));
}
_ => {}
}
@ -553,7 +595,7 @@ impl Device {
let mut device = Device {
name: name.into_owned(),
scroll_axes: scroll_axes,
scroll_axes,
attachment: info.attachment,
};
device.reset_scroll_position(info);
@ -566,8 +608,14 @@ impl Device {
let class = unsafe { &**class_ptr };
match class._type {
ffi::XIValuatorClass => {
let info = unsafe { mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIValuatorClassInfo>(class) };
if let Some(&mut (_, ref mut axis)) = self.scroll_axes.iter_mut().find(|&&mut (axis, _)| axis == info.number) {
let info = unsafe {
mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIValuatorClassInfo>(class)
};
if let Some(&mut (_, ref mut axis)) = self
.scroll_axes
.iter_mut()
.find(|&&mut (axis, _)| axis == info.number)
{
axis.position = info.value;
}
}
@ -579,11 +627,18 @@ impl Device {
#[inline]
fn physical_device(info: &ffi::XIDeviceInfo) -> bool {
info._use == ffi::XISlaveKeyboard || info._use == ffi::XISlavePointer || info._use == ffi::XIFloatingSlave
info._use == ffi::XISlaveKeyboard
|| info._use == ffi::XISlavePointer
|| info._use == ffi::XIFloatingSlave
}
#[inline]
fn classes(info: &ffi::XIDeviceInfo) -> &[*const ffi::XIAnyClassInfo] {
unsafe { slice::from_raw_parts(info.classes as *const *const ffi::XIAnyClassInfo, info.num_classes as usize) }
unsafe {
slice::from_raw_parts(
info.classes as *const *const ffi::XIAnyClassInfo,
info.num_classes as usize,
)
}
}
}

View file

@ -2,15 +2,16 @@ use std::os::raw::*;
use parking_lot::Mutex;
use dpi::{PhysicalPosition, PhysicalSize};
use super::{util, XConnection, XError};
use super::ffi::{
RRCrtcChangeNotifyMask,
RROutputPropertyNotifyMask,
RRScreenChangeNotifyMask,
True,
Window,
XRRScreenResources,
use super::{
ffi::{
RRCrtcChangeNotifyMask, RROutputPropertyNotifyMask, RRScreenChangeNotifyMask, True, Window,
XRRScreenResources,
},
util, XConnection, XError,
};
use crate::{
dpi::{PhysicalPosition, PhysicalSize},
monitor::VideoMode,
};
// Used to test XRandR < 1.5 code path. This should always be committed as false.
@ -56,6 +57,8 @@ pub struct MonitorHandle {
pub(crate) hidpi_factor: f64,
/// Used to determine which windows are on this monitor
pub(crate) rect: util::AaRect,
/// Supported video modes on this monitor
video_modes: Vec<VideoMode>,
}
impl MonitorHandle {
@ -66,8 +69,8 @@ impl MonitorHandle {
repr: util::MonitorRepr,
primary: bool,
) -> Option<Self> {
let (name, hidpi_factor) = unsafe { xconn.get_output_info(resources, &repr)? };
let (dimensions, position) = unsafe { (repr.dimensions(), repr.position()) };
let (name, hidpi_factor, video_modes) = unsafe { xconn.get_output_info(resources, &repr)? };
let (dimensions, position) = unsafe { (repr.size(), repr.position()) };
let rect = util::AaRect::new(position, dimensions);
Some(MonitorHandle {
id,
@ -77,6 +80,7 @@ impl MonitorHandle {
position,
primary,
rect,
video_modes,
})
}
@ -89,7 +93,7 @@ impl MonitorHandle {
self.id as u32
}
pub fn dimensions(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize {
self.dimensions.into()
}
@ -101,6 +105,11 @@ impl MonitorHandle {
pub fn hidpi_factor(&self) -> f64 {
self.hidpi_factor
}
#[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
self.video_modes.clone().into_iter()
}
}
impl XConnection {
@ -151,7 +160,8 @@ impl XConnection {
// videowalls.
let xrandr_1_5 = self.xrandr_1_5.as_ref().unwrap();
let mut monitor_count = 0;
let monitors = (xrandr_1_5.XRRGetMonitors)(self.display, root, 1, &mut monitor_count);
let monitors =
(xrandr_1_5.XRRGetMonitors)(self.display, root, 1, &mut monitor_count);
assert!(monitor_count >= 0);
available = Vec::with_capacity(monitor_count as usize);
for monitor_index in 0..monitor_count {
@ -164,7 +174,8 @@ impl XConnection {
monitor_index as u32,
monitor.into(),
is_primary,
).map(|monitor_id| available.push(monitor_id));
)
.map(|monitor_id| available.push(monitor_id));
}
(xrandr_1_5.XRRFreeMonitors)(monitors);
} else {
@ -181,13 +192,8 @@ impl XConnection {
let crtc = util::MonitorRepr::from(crtc);
let is_primary = crtc.get_output() == primary;
has_primary |= is_primary;
MonitorHandle::from_repr(
self,
resources,
crtc_id as u32,
crtc,
is_primary,
).map(|monitor_id| available.push(monitor_id));
MonitorHandle::from_repr(self, resources, crtc_id as u32, crtc, is_primary)
.map(|monitor_id| available.push(monitor_id));
}
(self.xrandr.XRRFreeCrtcInfo)(crtc);
}
@ -235,13 +241,8 @@ impl XConnection {
if version_lock.is_none() {
let mut major = 0;
let mut minor = 0;
let has_extension = unsafe {
(self.xrandr.XRRQueryVersion)(
self.display,
&mut major,
&mut minor,
)
};
let has_extension =
unsafe { (self.xrandr.XRRQueryVersion)(self.display, &mut major, &mut minor) };
if has_extension != True {
panic!("[winit] XRandR extension not available.");
}
@ -252,11 +253,7 @@ impl XConnection {
let mut event_offset = 0;
let mut error_offset = 0;
let status = unsafe {
(self.xrandr.XRRQueryExtension)(
self.display,
&mut event_offset,
&mut error_offset,
)
(self.xrandr.XRRQueryExtension)(self.display, &mut event_offset, &mut error_offset)
};
if status != True {
@ -264,9 +261,7 @@ impl XConnection {
unreachable!("[winit] `XRRQueryExtension` failed but no error was received.");
}
let mask = RRCrtcChangeNotifyMask
| RROutputPropertyNotifyMask
| RRScreenChangeNotifyMask;
let mask = RRCrtcChangeNotifyMask | RROutputPropertyNotifyMask | RRScreenChangeNotifyMask;
unsafe { (self.xrandr.XRRSelectInput)(self.display, root, mask) };
Ok(event_offset)

View file

@ -1,7 +1,9 @@
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use std::fmt::Debug;
use std::os::raw::*;
use std::{
collections::HashMap,
ffi::{CStr, CString},
fmt::Debug,
os::raw::*,
};
use parking_lot::Mutex;
@ -21,11 +23,9 @@ impl XConnection {
if let Some(atom) = cached_atom {
atom
} else {
let atom = unsafe { (self.xlib.XInternAtom)(
self.display,
name.as_ptr() as *const c_char,
ffi::False,
) };
let atom = unsafe {
(self.xlib.XInternAtom)(self.display, name.as_ptr() as *const c_char, ffi::False)
};
if atom == 0 {
let msg = format!(
"`XInternAtom` failed, which really shouldn't happen. Atom: {:?}, Error: {:#?}",
@ -52,7 +52,7 @@ impl XConnection {
// Note: this doesn't use caching, for the sake of simplicity.
// If you're dealing with this many atoms, you'll usually want to cache them locally anyway.
pub unsafe fn get_atoms(&self, names: &[*mut c_char]) -> Result<Vec<ffi::Atom>, XError> {
pub unsafe fn get_atoms(&self, names: &[*mut c_char]) -> Result<Vec<ffi::Atom>, XError> {
let mut atoms = Vec::with_capacity(names.len());
(self.xlib.XInternAtoms)(
self.display,

View file

@ -8,7 +8,7 @@ impl XConnection {
target_window: c_ulong,
event_mask: Option<c_long>,
event: T,
) -> Flusher {
) -> Flusher<'_> {
let event_mask = event_mask.unwrap_or(ffi::NoEventMask);
unsafe {
(self.xlib.XSendEvent)(
@ -24,12 +24,12 @@ impl XConnection {
pub fn send_client_msg(
&self,
window: c_ulong, // The window this is "about"; not necessarily this window
window: c_ulong, // The window this is "about"; not necessarily this window
target_window: c_ulong, // The window we're sending to
message_type: ffi::Atom,
event_mask: Option<c_long>,
data: ClientMsgPayload,
) -> Flusher {
) -> Flusher<'_> {
let mut event: ffi::XClientMessageEvent = unsafe { mem::uninitialized() };
event.type_ = ffi::ClientMessage;
event.display = self.display;
@ -45,12 +45,12 @@ impl XConnection {
// to send more than one message worth of data.
pub fn send_client_msg_multi<T: Formattable>(
&self,
window: c_ulong, // The window this is "about"; not necessarily this window
window: c_ulong, // The window this is "about"; not necessarily this window
target_window: c_ulong, // The window we're sending to
message_type: ffi::Atom,
event_mask: Option<c_long>,
data: &[T],
) -> Flusher {
) -> Flusher<'_> {
let format = T::FORMAT;
let size_of_t = mem::size_of::<T>();
debug_assert_eq!(size_of_t, format.get_actual_size());

View file

@ -1,6 +1,4 @@
use std::fmt::Debug;
use std::mem;
use std::os::raw::*;
use std::{fmt::Debug, mem, os::raw::*};
// This isn't actually the number of the bits in the format.
// X11 does a match on this value to determine which type to call sizeof on.
@ -50,9 +48,21 @@ pub trait Formattable: Debug + Clone + Copy + PartialEq + PartialOrd {
}
// You might be surprised by the absence of c_int, but not as surprised as X11 would be by the presence of it.
impl Formattable for c_schar { const FORMAT: Format = Format::Char; }
impl Formattable for c_uchar { const FORMAT: Format = Format::Char; }
impl Formattable for c_short { const FORMAT: Format = Format::Short; }
impl Formattable for c_ushort { const FORMAT: Format = Format::Short; }
impl Formattable for c_long { const FORMAT: Format = Format::Long; }
impl Formattable for c_ulong { const FORMAT: Format = Format::Long; }
impl Formattable for c_schar {
const FORMAT: Format = Format::Char;
}
impl Formattable for c_uchar {
const FORMAT: Format = Format::Char;
}
impl Formattable for c_short {
const FORMAT: Format = Format::Short;
}
impl Formattable for c_ushort {
const FORMAT: Format = Format::Short;
}
impl Formattable for c_long {
const FORMAT: Format = Format::Long;
}
impl Formattable for c_ulong {
const FORMAT: Format = Format::Long;
}

View file

@ -1,7 +1,7 @@
use std::cmp;
use super::*;
use dpi::{LogicalPosition, LogicalSize};
use crate::dpi::{LogicalPosition, LogicalSize};
// Friendly neighborhood axis-aligned rectangle
#[derive(Debug, Clone, PartialEq, Eq)]
@ -16,7 +16,12 @@ impl AaRect {
pub fn new((x, y): (i32, i32), (width, height): (u32, u32)) -> Self {
let (x, y) = (x as i64, y as i64);
let (width, height) = (width as i64, height as i64);
AaRect { x, y, width, height }
AaRect {
x,
y,
width,
height,
}
}
pub fn contains_point(&self, x: i64, y: i64) -> bool {
@ -73,7 +78,12 @@ pub struct FrameExtents {
impl FrameExtents {
pub fn new(left: c_ulong, right: c_ulong, top: c_ulong, bottom: c_ulong) -> Self {
FrameExtents { left, right, top, bottom }
FrameExtents {
left,
right,
top,
bottom,
}
}
pub fn from_border(border: c_ulong) -> Self {
@ -116,13 +126,20 @@ 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)
(
x - self.frame_extents.left as i32,
y - self.frame_extents.top as i32,
)
} else {
(x, y)
}
}
pub fn inner_pos_to_outer_logical(&self, mut logical: LogicalPosition, factor: f64) -> LogicalPosition {
pub fn inner_pos_to_outer_logical(
&self,
mut logical: LogicalPosition,
factor: f64,
) -> LogicalPosition {
use self::FrameExtentsHeuristicPath::*;
if self.heuristic_path != UnsupportedBordered {
let frame_extents = self.frame_extents.as_logical(factor);
@ -135,15 +152,23 @@ impl FrameExtentsHeuristic {
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
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
self.frame_extents
.top
.saturating_add(self.frame_extents.bottom) as u32,
),
)
}
pub fn inner_size_to_outer_logical(&self, mut logical: LogicalSize, factor: f64) -> LogicalSize {
pub fn inner_size_to_outer_logical(
&self,
mut logical: LogicalSize,
factor: f64,
) -> LogicalSize {
let frame_extents = self.frame_extents.as_logical(factor);
logical.width += frame_extents.left + frame_extents.right;
logical.height += frame_extents.top + frame_extents.bottom;
@ -153,7 +178,11 @@ impl FrameExtentsHeuristic {
impl XConnection {
// This is adequate for inner_position
pub fn translate_coords(&self, window: ffi::Window, root: ffi::Window) -> Result<TranslatedCoords, XError> {
pub fn translate_coords(
&self,
window: ffi::Window,
root: ffi::Window,
) -> Result<TranslatedCoords, XError> {
let mut translated_coords: TranslatedCoords = unsafe { mem::uninitialized() };
unsafe {
(self.xlib.XTranslateCoordinates)(
@ -201,11 +230,9 @@ impl XConnection {
// Of the WMs tested, xmonad, i3, dwm, IceWM (1.3.x and earlier), and blackbox don't
// support this. As this is part of EWMH (Extended Window Manager Hints), it's likely to
// be unsupported by many smaller WMs.
let extents: Option<Vec<c_ulong>> = self.get_property(
window,
extents_atom,
ffi::XA_CARDINAL,
).ok();
let extents: Option<Vec<c_ulong>> = self
.get_property(window, extents_atom, ffi::XA_CARDINAL)
.ok();
extents.and_then(|extents| {
if extents.len() >= 4 {
@ -228,11 +255,9 @@ impl XConnection {
return None;
}
let client_list: Option<Vec<ffi::Window>> = self.get_property(
root,
client_list_atom,
ffi::XA_WINDOW,
).ok();
let client_list: Option<Vec<ffi::Window>> = self
.get_property(root, client_list_atom, ffi::XA_WINDOW)
.ok();
client_list.map(|client_list| client_list.contains(&window))
}
@ -264,7 +289,11 @@ impl XConnection {
self.check_errors().map(|_| parent)
}
fn climb_hierarchy(&self, window: ffi::Window, root: ffi::Window) -> Result<ffi::Window, XError> {
fn climb_hierarchy(
&self,
window: ffi::Window,
root: ffi::Window,
) -> Result<ffi::Window, XError> {
let mut outer_window = window;
loop {
let candidate = self.get_parent_window(outer_window)?;
@ -276,7 +305,11 @@ impl XConnection {
Ok(outer_window)
}
pub fn get_frame_extents_heuristic(&self, window: ffi::Window, root: ffi::Window) -> FrameExtentsHeuristic {
pub fn get_frame_extents_heuristic(
&self,
window: ffi::Window,
root: ffi::Window,
) -> FrameExtentsHeuristic {
use self::FrameExtentsHeuristicPath::*;
// Position relative to root window.
@ -284,15 +317,16 @@ impl XConnection {
// isn't nested are outlined in the comments throghout this function, but in addition to
// that, fullscreen windows often aren't nested.
let (inner_y_rel_root, child) = {
let coords = self.translate_coords(window, root).expect("Failed to translate window coordinates");
(
coords.y_rel_root,
coords.child,
)
let coords = self
.translate_coords(window, root)
.expect("Failed to translate window coordinates");
(coords.y_rel_root, coords.child)
};
let (width, height, border) = {
let inner_geometry = self.get_geometry(window).expect("Failed to get inner window geometry");
let inner_geometry = self
.get_geometry(window)
.expect("Failed to get inner window geometry");
(
inner_geometry.width,
inner_geometry.height,
@ -343,9 +377,13 @@ impl XConnection {
// If the position value we have is for a nested window used as the client area, we'll
// just climb up the hierarchy and get the geometry of the outermost window we're
// nested in.
let outer_window = self.climb_hierarchy(window, root).expect("Failed to climb window hierarchy");
let outer_window = self
.climb_hierarchy(window, root)
.expect("Failed to climb window hierarchy");
let (outer_y, outer_width, outer_height) = {
let outer_geometry = self.get_geometry(outer_window).expect("Failed to get outer window geometry");
let outer_geometry = self
.get_geometry(outer_window)
.expect("Failed to get outer window geometry");
(
outer_geometry.y_rel_parent,
outer_geometry.width,
@ -364,12 +402,8 @@ impl XConnection {
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(),
);
let frame_extents =
FrameExtents::new(left.into(), right.into(), top.into(), bottom.into());
FrameExtentsHeuristic {
frame_extents,
heuristic_path: UnsupportedNested,

View file

@ -1,9 +1,8 @@
use std::slice;
use std::sync::Arc;
use super::*;
pub const MWM_HINTS_DECORATIONS: c_ulong = 2;
#[derive(Debug)]
pub enum StateOperation {
Remove = 0, // _NET_WM_STATE_REMOVE
@ -93,13 +92,101 @@ impl WindowType {
}
}
pub struct MotifHints {
hints: MwmHints,
}
#[repr(C)]
struct MwmHints {
flags: c_ulong,
functions: c_ulong,
decorations: c_ulong,
input_mode: c_long,
status: c_ulong,
}
#[allow(dead_code)]
mod mwm {
use libc::c_ulong;
// Motif WM hints are obsolete, but still widely supported.
// https://stackoverflow.com/a/1909708
pub const MWM_HINTS_FUNCTIONS: c_ulong = 1 << 0;
pub const MWM_HINTS_DECORATIONS: c_ulong = 1 << 1;
pub const MWM_FUNC_ALL: c_ulong = 1 << 0;
pub const MWM_FUNC_RESIZE: c_ulong = 1 << 1;
pub const MWM_FUNC_MOVE: c_ulong = 1 << 2;
pub const MWM_FUNC_MINIMIZE: c_ulong = 1 << 3;
pub const MWM_FUNC_MAXIMIZE: c_ulong = 1 << 4;
pub const MWM_FUNC_CLOSE: c_ulong = 1 << 5;
}
impl MotifHints {
pub fn new() -> MotifHints {
MotifHints {
hints: MwmHints {
flags: 0,
functions: 0,
decorations: 0,
input_mode: 0,
status: 0,
},
}
}
pub fn set_decorations(&mut self, decorations: bool) {
self.hints.flags |= mwm::MWM_HINTS_DECORATIONS;
self.hints.decorations = decorations as c_ulong;
}
pub fn set_maximizable(&mut self, maximizable: bool) {
if maximizable {
self.add_func(mwm::MWM_FUNC_MAXIMIZE);
} else {
self.remove_func(mwm::MWM_FUNC_MAXIMIZE);
}
}
fn add_func(&mut self, func: c_ulong) {
if self.hints.flags & mwm::MWM_HINTS_FUNCTIONS != 0 {
if self.hints.functions & mwm::MWM_FUNC_ALL != 0 {
self.hints.functions &= !func;
} else {
self.hints.functions |= func;
}
}
}
fn remove_func(&mut self, func: c_ulong) {
if self.hints.flags & mwm::MWM_HINTS_FUNCTIONS == 0 {
self.hints.flags |= mwm::MWM_HINTS_FUNCTIONS;
self.hints.functions = mwm::MWM_FUNC_ALL;
}
if self.hints.functions & mwm::MWM_FUNC_ALL != 0 {
self.hints.functions |= func;
} else {
self.hints.functions &= !func;
}
}
}
impl MwmHints {
fn as_slice(&self) -> &[c_ulong] {
unsafe { slice::from_raw_parts(self as *const _ as *const c_ulong, 5) }
}
}
pub struct NormalHints<'a> {
size_hints: XSmartPointer<'a, ffi::XSizeHints>,
}
impl<'a> NormalHints<'a> {
pub fn new(xconn: &'a XConnection) -> Self {
NormalHints { size_hints: xconn.alloc_size_hints() }
NormalHints {
size_hints: xconn.alloc_size_hints(),
}
}
pub fn has_flag(&self, flag: c_long) -> bool {
@ -130,7 +217,11 @@ impl<'a> NormalHints<'a> {
}
pub fn get_max_size(&self) -> Option<(u32, u32)> {
self.getter(ffi::PMaxSize, &self.size_hints.max_width, &self.size_hints.max_height)
self.getter(
ffi::PMaxSize,
&self.size_hints.max_width,
&self.size_hints.max_height,
)
}
pub fn set_max_size(&mut self, max_size: Option<(u32, u32)>) {
@ -144,7 +235,11 @@ impl<'a> NormalHints<'a> {
}
pub fn get_min_size(&self) -> Option<(u32, u32)> {
self.getter(ffi::PMinSize, &self.size_hints.min_width, &self.size_hints.min_height)
self.getter(
ffi::PMinSize,
&self.size_hints.min_width,
&self.size_hints.min_height,
)
}
pub fn set_min_size(&mut self, min_size: Option<(u32, u32)>) {
@ -158,7 +253,11 @@ impl<'a> NormalHints<'a> {
}
pub fn get_resize_increments(&self) -> Option<(u32, u32)> {
self.getter(ffi::PResizeInc, &self.size_hints.width_inc, &self.size_hints.height_inc)
self.getter(
ffi::PResizeInc,
&self.size_hints.width_inc,
&self.size_hints.height_inc,
)
}
pub fn set_resize_increments(&mut self, resize_increments: Option<(u32, u32)>) {
@ -172,7 +271,11 @@ impl<'a> NormalHints<'a> {
}
pub fn get_base_size(&self) -> Option<(u32, u32)> {
self.getter(ffi::PBaseSize, &self.size_hints.base_width, &self.size_hints.base_height)
self.getter(
ffi::PBaseSize,
&self.size_hints.base_width,
&self.size_hints.base_height,
)
}
pub fn set_base_size(&mut self, base_size: Option<(u32, u32)>) {
@ -187,7 +290,10 @@ impl<'a> NormalHints<'a> {
}
impl XConnection {
pub fn get_wm_hints(&self, window: ffi::Window) -> Result<XSmartPointer<ffi::XWMHints>, XError> {
pub fn get_wm_hints(
&self,
window: ffi::Window,
) -> Result<XSmartPointer<'_, ffi::XWMHints>, XError> {
let wm_hints = unsafe { (self.xlib.XGetWMHints)(self.display, window) };
self.check_errors()?;
let wm_hints = if wm_hints.is_null() {
@ -198,18 +304,18 @@ impl XConnection {
Ok(wm_hints)
}
pub fn set_wm_hints(&self, window: ffi::Window, wm_hints: XSmartPointer<ffi::XWMHints>) -> Flusher {
pub fn set_wm_hints(
&self,
window: ffi::Window,
wm_hints: XSmartPointer<'_, ffi::XWMHints>,
) -> Flusher<'_> {
unsafe {
(self.xlib.XSetWMHints)(
self.display,
window,
wm_hints.ptr,
);
(self.xlib.XSetWMHints)(self.display, window, wm_hints.ptr);
}
Flusher::new(self)
}
pub fn get_normal_hints(&self, window: ffi::Window) -> Result<NormalHints, XError> {
pub fn get_normal_hints(&self, window: ffi::Window) -> Result<NormalHints<'_>, XError> {
let size_hints = self.alloc_size_hints();
let mut supplied_by_user: c_long = unsafe { mem::uninitialized() };
unsafe {
@ -223,14 +329,42 @@ impl XConnection {
self.check_errors().map(|_| NormalHints { size_hints })
}
pub fn set_normal_hints(&self, window: ffi::Window, normal_hints: NormalHints) -> Flusher {
pub fn set_normal_hints(
&self,
window: ffi::Window,
normal_hints: NormalHints<'_>,
) -> Flusher<'_> {
unsafe {
(self.xlib.XSetWMNormalHints)(
self.display,
window,
normal_hints.size_hints.ptr,
);
(self.xlib.XSetWMNormalHints)(self.display, window, normal_hints.size_hints.ptr);
}
Flusher::new(self)
}
pub fn get_motif_hints(&self, window: ffi::Window) -> MotifHints {
let motif_hints = unsafe { self.get_atom_unchecked(b"_MOTIF_WM_HINTS\0") };
let mut hints = MotifHints::new();
if let Ok(props) = self.get_property::<c_ulong>(window, motif_hints, motif_hints) {
hints.hints.flags = props.get(0).cloned().unwrap_or(0);
hints.hints.functions = props.get(1).cloned().unwrap_or(0);
hints.hints.decorations = props.get(2).cloned().unwrap_or(0);
hints.hints.input_mode = props.get(3).cloned().unwrap_or(0) as c_long;
hints.hints.status = props.get(4).cloned().unwrap_or(0);
}
hints
}
pub fn set_motif_hints(&self, window: ffi::Window, hints: &MotifHints) -> Flusher<'_> {
let motif_hints = unsafe { self.get_atom_unchecked(b"_MOTIF_WM_HINTS\0") };
self.change_property(
window,
motif_hints,
motif_hints,
PropMode::Replace,
hints.hints.as_slice(),
)
}
}

View file

@ -1,5 +1,5 @@
use window::{Icon, Pixel, PIXEL_SIZE};
use super::*;
use crate::window::{Icon, Pixel, PIXEL_SIZE};
impl Pixel {
pub fn to_packed_argb(&self) -> Cardinal {

View file

@ -1,7 +1,7 @@
use std::str;
use super::*;
use event::ModifiersState;
use crate::event::ModifiersState;
pub const VIRTUAL_CORE_POINTER: c_int = 2;
pub const VIRTUAL_CORE_KEYBOARD: c_int = 3;
@ -55,7 +55,12 @@ impl<'a> Drop for PointerState<'a> {
}
impl XConnection {
pub fn select_xinput_events(&self, window: c_ulong, device_id: c_int, mask: i32) -> Flusher {
pub fn select_xinput_events(
&self,
window: c_ulong,
device_id: c_int,
mask: i32,
) -> Flusher<'_> {
let mut event_mask = ffi::XIEventMask {
deviceid: device_id,
mask: &mask as *const _ as *mut c_uchar,
@ -73,15 +78,8 @@ impl XConnection {
}
#[allow(dead_code)]
pub fn select_xkb_events(&self, device_id: c_uint, mask: c_ulong) -> Option<Flusher> {
let status = unsafe {
(self.xlib.XkbSelectEvents)(
self.display,
device_id,
mask,
mask,
)
};
pub fn select_xkb_events(&self, device_id: c_uint, mask: c_ulong) -> Option<Flusher<'_>> {
let status = unsafe { (self.xlib.XkbSelectEvents)(self.display, device_id, mask, mask) };
if status == ffi::True {
Some(Flusher::new(self))
} else {
@ -89,9 +87,13 @@ impl XConnection {
}
}
pub fn query_pointer(&self, window: ffi::Window, device_id: c_int) -> Result<PointerState, XError> {
pub fn query_pointer(
&self,
window: ffi::Window,
device_id: c_int,
) -> Result<PointerState<'_>, XError> {
unsafe {
let mut pointer_state: PointerState = mem::uninitialized();
let mut pointer_state: PointerState<'_> = mem::uninitialized();
pointer_state.xconn = self;
pointer_state.relative_to_window = (self.xinput2.XIQueryPointer)(
self.display,

View file

@ -12,10 +12,7 @@ impl<'a, T> XSmartPointer<'a, T> {
// Returns None if ptr is null.
pub fn new(xconn: &'a XConnection, ptr: *mut T) -> Option<Self> {
if !ptr.is_null() {
Some(XSmartPointer {
xconn,
ptr,
})
Some(XSmartPointer { xconn, ptr })
} else {
None
}
@ -45,17 +42,17 @@ impl<'a, T> Drop for XSmartPointer<'a, T> {
}
impl XConnection {
pub fn alloc_class_hint(&self) -> XSmartPointer<ffi::XClassHint> {
pub fn alloc_class_hint(&self) -> XSmartPointer<'_, ffi::XClassHint> {
XSmartPointer::new(self, unsafe { (self.xlib.XAllocClassHint)() })
.expect("`XAllocClassHint` returned null; out of memory")
}
pub fn alloc_size_hints(&self) -> XSmartPointer<ffi::XSizeHints> {
pub fn alloc_size_hints(&self) -> XSmartPointer<'_, ffi::XSizeHints> {
XSmartPointer::new(self, unsafe { (self.xlib.XAllocSizeHints)() })
.expect("`XAllocSizeHints` returned null; out of memory")
}
pub fn alloc_wm_hints(&self) -> XSmartPointer<ffi::XWMHints> {
pub fn alloc_wm_hints(&self) -> XSmartPointer<'_, ffi::XWMHints> {
XSmartPointer::new(self, unsafe { (self.xlib.XAllocWMHints)() })
.expect("`XAllocWMHints` returned null; out of memory")
}

View file

@ -13,22 +13,12 @@ mod randr;
mod window_property;
mod wm;
pub use self::atom::*;
pub use self::client_msg::*;
pub use self::format::*;
pub use self::geometry::*;
pub use self::hint::*;
pub use self::icon::*;
pub use self::input::*;
pub use self::memory::*;
pub use self::randr::*;
pub use self::window_property::*;
pub use self::wm::*;
pub use self::{
atom::*, client_msg::*, format::*, geometry::*, hint::*, icon::*, input::*, memory::*,
randr::*, window_property::*, wm::*,
};
use std::mem;
use std::ptr;
use std::ops::BitAnd;
use std::os::raw::*;
use std::{mem, ops::BitAnd, os::raw::*, ptr};
use super::{ffi, XConnection, XError};
@ -48,8 +38,8 @@ pub fn maybe_change<T: PartialEq>(field: &mut Option<T>, value: T) -> bool {
}
pub fn has_flag<T>(bitset: T, flag: T) -> bool
where T:
Copy + PartialEq + BitAnd<T, Output = T>
where
T: Copy + PartialEq + BitAnd<T, Output = T>,
{
bitset & flag == flag
}

View file

@ -1,8 +1,7 @@
use std::{env, slice};
use std::str::FromStr;
use std::{env, slice, str::FromStr};
use dpi::validate_hidpi_factor;
use super::*;
use crate::{dpi::validate_hidpi_factor, monitor::VideoMode};
pub fn calc_dpi_factor(
(width_px, height_px): (u32, u32),
@ -23,14 +22,12 @@ pub fn calc_dpi_factor(
}
// See http://xpra.org/trac/ticket/728 for more information.
if width_mm == 0 || width_mm == 0 {
if width_mm == 0 || height_mm == 0 {
warn!("XRandR reported that the display's 0mm in size, which is certifiably insane");
return 1.0;
}
let ppmm = (
(width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)
).sqrt();
let ppmm = ((width_px as f64 * height_px as f64) / (width_mm as f64 * height_mm as f64)).sqrt();
// Quantize 1/12 step size
let dpi_factor = ((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0);
assert!(validate_hidpi_factor(dpi_factor));
@ -51,7 +48,7 @@ impl MonitorRepr {
}
}
pub unsafe fn dimensions(&self) -> (u32, u32) {
pub unsafe fn size(&self) -> (u32, u32) {
match *self {
MonitorRepr::Monitor(monitor) => ((*monitor).width as u32, (*monitor).height as u32),
MonitorRepr::Crtc(crtc) => ((*crtc).width as u32, (*crtc).height as u32),
@ -87,7 +84,7 @@ impl XConnection {
return None;
}
if let Ok(res) = ::std::ffi::CStr::from_ptr(resource_manager_str).to_str() {
let name : &str = "Xft.dpi:\t";
let name: &str = "Xft.dpi:\t";
for pair in res.split("\n") {
if pair.starts_with(&name) {
let res = &pair[name.len()..];
@ -101,12 +98,9 @@ impl XConnection {
&self,
resources: *mut ffi::XRRScreenResources,
repr: &MonitorRepr,
) -> Option<(String, f64)> {
let output_info = (self.xrandr.XRRGetOutputInfo)(
self.display,
resources,
repr.get_output(),
);
) -> Option<(String, f64, Vec<VideoMode>)> {
let output_info =
(self.xrandr.XRRGetOutputInfo)(self.display, resources, repr.get_output());
if output_info.is_null() {
// When calling `XRRGetOutputInfo` on a virtual monitor (versus a physical display)
// it's possible for it to return null.
@ -114,6 +108,33 @@ impl XConnection {
let _ = self.check_errors(); // discard `BadRROutput` error
return None;
}
let screen = (self.xlib.XDefaultScreen)(self.display);
let bit_depth = (self.xlib.XDefaultDepth)(self.display, screen);
let output_modes =
slice::from_raw_parts((*output_info).modes, (*output_info).nmode as usize);
let resource_modes = slice::from_raw_parts((*resources).modes, (*resources).nmode as usize);
let modes = resource_modes
.iter()
// XRROutputInfo contains an array of mode ids that correspond to
// modes in the array in XRRScreenResources
.filter(|x| output_modes.iter().any(|id| x.id == *id))
.map(|x| {
let refresh_rate = if x.dotClock > 0 && x.hTotal > 0 && x.vTotal > 0 {
x.dotClock as u64 * 1000 / (x.hTotal as u64 * x.vTotal as u64)
} else {
0
};
VideoMode {
size: (x.width, x.height),
refresh_rate: (refresh_rate as f32 / 1000.0).round() as u16,
bit_depth: bit_depth as u16,
}
});
let name_slice = slice::from_raw_parts(
(*output_info).name as *mut u8,
(*output_info).nameLen as usize,
@ -123,12 +144,15 @@ impl XConnection {
dpi / 96.
} else {
calc_dpi_factor(
repr.dimensions(),
((*output_info).mm_width as u64, (*output_info).mm_height as u64),
repr.size(),
(
(*output_info).mm_width as u64,
(*output_info).mm_height as u64,
),
)
};
(self.xrandr.XRRFreeOutputInfo)(output_info);
Some((name, hidpi_factor))
Some((name, hidpi_factor, modes.collect()))
}
}

View file

@ -84,10 +84,8 @@ impl XConnection {
if !buf.is_null() {
offset += PROPERTY_BUFFER_SIZE;
let new_data = std::slice::from_raw_parts(
buf as *mut T,
quantity_returned as usize,
);
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,

View file

@ -28,11 +28,8 @@ impl XConnection {
fn get_supported_hints(&self, root: ffi::Window) -> Vec<ffi::Atom> {
let supported_atom = unsafe { self.get_atom_unchecked(b"_NET_SUPPORTED\0") };
self.get_property(
root,
supported_atom,
ffi::XA_ATOM,
).unwrap_or_else(|_| Vec::with_capacity(0))
self.get_property(root, supported_atom, ffi::XA_ATOM)
.unwrap_or_else(|_| Vec::with_capacity(0))
}
fn get_wm_name(&self, root: ffi::Window) -> Option<String> {
@ -61,15 +58,9 @@ impl XConnection {
// Querying this property on the root window will give us the ID of a child window created by
// the WM.
let root_window_wm_check = {
let result = self.get_property(
root,
check_atom,
ffi::XA_WINDOW,
);
let result = self.get_property(root, check_atom, ffi::XA_WINDOW);
let wm_check = result
.ok()
.and_then(|wm_check| wm_check.get(0).cloned());
let wm_check = result.ok().and_then(|wm_check| wm_check.get(0).cloned());
if let Some(wm_check) = wm_check {
wm_check
@ -81,15 +72,9 @@ impl XConnection {
// Querying the same property on the child window we were given, we should get this child
// window's ID again.
let child_window_wm_check = {
let result = self.get_property(
root_window_wm_check,
check_atom,
ffi::XA_WINDOW,
);
let result = self.get_property(root_window_wm_check, check_atom, ffi::XA_WINDOW);
let wm_check = result
.ok()
.and_then(|wm_check| wm_check.get(0).cloned());
let wm_check = result.ok().and_then(|wm_check| wm_check.get(0).cloned());
if let Some(wm_check) = wm_check {
wm_check
@ -107,11 +92,7 @@ impl XConnection {
let wm_name = {
let utf8_string_atom = unsafe { self.get_atom_unchecked(b"UTF8_STRING\0") };
let result = self.get_property(
root_window_wm_check,
wm_name_atom,
utf8_string_atom,
);
let result = self.get_property(root_window_wm_check, wm_name_atom, utf8_string_atom);
// IceWM requires this. IceWM was also the only WM tested that returns a null-terminated
// string. For more fun trivia, IceWM is also unique in including version and uname
@ -126,15 +107,12 @@ impl XConnection {
};
if no_utf8 {
self.get_property(
root_window_wm_check,
wm_name_atom,
ffi::XA_STRING,
)
self.get_property(root_window_wm_check, wm_name_atom, ffi::XA_STRING)
} else {
result
}
}.ok();
}
.ok();
wm_name.and_then(|wm_name| String::from_utf8(wm_name).ok())
}

View file

@ -1,23 +1,20 @@
use std::{cmp, env, mem};
use std::collections::HashSet;
use std::ffi::CString;
use std::os::raw::*;
use std::path::Path;
use std::sync::Arc;
use std::{cmp, collections::HashSet, env, ffi::CString, mem, os::raw::*, path::Path, sync::Arc};
use libc;
use parking_lot::Mutex;
use error::{ExternalError, NotSupportedError, OsError as RootOsError};
use window::{Icon, CursorIcon, WindowAttributes};
use dpi::{LogicalPosition, LogicalSize};
use platform_impl::MonitorHandle as PlatformMonitorHandle;
use platform_impl::{OsError, PlatformSpecificWindowBuilderAttributes};
use platform_impl::x11::ime::ImeContextCreationError;
use platform_impl::x11::MonitorHandle as X11MonitorHandle;
use monitor::MonitorHandle as RootMonitorHandle;
use crate::{
dpi::{LogicalPosition, LogicalSize},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
monitor::MonitorHandle as RootMonitorHandle,
platform_impl::{
x11::{ime::ImeContextCreationError, MonitorHandle as X11MonitorHandle},
MonitorHandle as PlatformMonitorHandle, OsError, PlatformSpecificWindowBuilderAttributes,
},
window::{CursorIcon, Icon, WindowAttributes},
};
use super::{ffi, util, ImeSender, XConnection, XError, WindowId, EventLoopWindowTarget};
use super::{ffi, util, EventLoopWindowTarget, ImeSender, WindowId, XConnection, XError};
unsafe extern "C" fn visibility_predicate(
_display: *mut ffi::Display,
@ -60,9 +57,9 @@ unsafe impl Sync for UnownedWindow {}
pub struct UnownedWindow {
pub xconn: Arc<XConnection>, // never changes
xwindow: ffi::Window, // never changes
root: ffi::Window, // never changes
screen_id: i32, // never changes
xwindow: ffi::Window, // never changes
root: ffi::Window, // never changes
screen_id: i32, // never changes
cursor: Mutex<CursorIcon>,
cursor_grabbed: Mutex<bool>,
cursor_visible: Mutex<bool>,
@ -89,7 +86,8 @@ impl UnownedWindow {
}
}
dpi_factor.unwrap_or_else(|| {
xconn.query_pointer(root, util::VIRTUAL_CORE_POINTER)
xconn
.query_pointer(root, util::VIRTUAL_CORE_POINTER)
.ok()
.and_then(|pointer_state| {
let (x, y) = (pointer_state.root_x as i64, pointer_state.root_y as i64);
@ -110,17 +108,18 @@ impl UnownedWindow {
info!("Guessed window DPI factor: {}", dpi_factor);
let max_inner_size: Option<(u32, u32)> = window_attrs.max_inner_size.map(|size| {
size.to_physical(dpi_factor).into()
});
let min_inner_size: Option<(u32, u32)> = window_attrs.min_inner_size.map(|size| {
size.to_physical(dpi_factor).into()
});
let max_inner_size: Option<(u32, u32)> = window_attrs
.max_inner_size
.map(|size| size.to_physical(dpi_factor).into());
let min_inner_size: Option<(u32, u32)> = window_attrs
.min_inner_size
.map(|size| size.to_physical(dpi_factor).into());
let dimensions = {
// x11 only applies constraints when the window is actively resized
// by the user, so we have to manually apply the initial constraints
let mut dimensions: (u32, u32) = window_attrs.inner_size
let mut dimensions: (u32, u32) = window_attrs
.inner_size
.or_else(|| Some((800, 600).into()))
.map(|size| size.to_physical(dpi_factor))
.map(Into::into)
@ -133,7 +132,10 @@ impl UnownedWindow {
dimensions.0 = cmp::max(dimensions.0, min.0);
dimensions.1 = cmp::max(dimensions.1, min.1);
}
debug!("Calculated physical dimensions: {}x{}", dimensions.0, dimensions.1);
debug!(
"Calculated physical dimensions: {}x{}",
dimensions.0, dimensions.1
);
dimensions
};
@ -150,7 +152,9 @@ impl UnownedWindow {
let visual = vi.visual;
(xconn.xlib.XCreateColormap)(xconn.display, root, visual, ffi::AllocNone)
}
} else { 0 };
} else {
0
};
swa.event_mask = ffi::ExposureMask
| ffi::StructureNotifyMask
| ffi::VisibilityChangeMask
@ -220,7 +224,9 @@ impl UnownedWindow {
// title to determine placement/etc., so doing this after mapping would cause the WM to
// act on the wrong title state.
window.set_title_inner(&window_attrs.title).queue();
window.set_decorations_inner(window_attrs.decorations).queue();
window
.set_decorations_inner(window_attrs.decorations)
.queue();
{
// Enable drag and drop (TODO: extend API to make this toggleable)
@ -234,15 +240,16 @@ impl UnownedWindow {
util::PropMode::Replace,
version,
)
}.queue();
}
.queue();
// WM_CLASS must be set *before* mapping the window, as per ICCCM!
{
let (class, instance) = if let Some((instance, class)) = pl_attribs.class {
let instance = CString::new(instance.as_str())
.expect("`WM_CLASS` instance contained null byte");
let class = CString::new(class.as_str())
.expect("`WM_CLASS` class contained null byte");
let class =
CString::new(class.as_str()).expect("`WM_CLASS` class contained null byte");
(instance, class)
} else {
let class = env::args()
@ -269,12 +276,8 @@ impl UnownedWindow {
(*class_hint).res_class = instance.as_ptr() as *mut c_char;
unsafe {
(xconn.xlib.XSetClassHint)(
xconn.display,
window.xwindow,
class_hint.ptr,
);
}//.queue();
(xconn.xlib.XSetClassHint)(xconn.display, window.xwindow, class_hint.ptr);
} //.queue();
}
window.set_pid().map(|flusher| flusher.queue());
@ -289,9 +292,11 @@ impl UnownedWindow {
// set size hints
{
let mut min_inner_size = window_attrs.min_inner_size
let mut min_inner_size = window_attrs
.min_inner_size
.map(|size| size.to_physical(dpi_factor));
let mut max_inner_size = window_attrs.max_inner_size
let mut max_inner_size = window_attrs
.max_inner_size
.map(|size| size.to_physical(dpi_factor));
if !window_attrs.resizable {
if util::wm_name_is_one_of(&["Xfwm4"]) {
@ -325,16 +330,17 @@ impl UnownedWindow {
(xconn.xlib.XSetWMProtocols)(
xconn.display,
window.xwindow,
&event_loop.wm_delete_window as *const ffi::Atom as *mut ffi::Atom,
1,
&[event_loop.wm_delete_window, event_loop.net_wm_ping] as *const ffi::Atom
as *mut ffi::Atom,
2,
);
}//.queue();
} //.queue();
// Set visibility (map window)
if window_attrs.visible {
unsafe {
(xconn.xlib.XMapRaised)(xconn.display, window.xwindow);
}//.queue();
} //.queue();
}
// Attempt to make keyboard input repeat detectable
@ -346,7 +352,9 @@ impl UnownedWindow {
&mut supported_ptr,
);
if supported_ptr == ffi::False {
return Err(os_error!(OsError::XMisc("`XkbSetDetectableAutoRepeat` failed")));
return Err(os_error!(OsError::XMisc(
"`XkbSetDetectableAutoRepeat` failed"
)));
}
}
@ -366,16 +374,18 @@ impl UnownedWindow {
| ffi::XI_TouchEndMask;
mask
};
xconn.select_xinput_events(window.xwindow, ffi::XIAllMasterDevices, mask).queue();
xconn
.select_xinput_events(window.xwindow, ffi::XIAllMasterDevices, mask)
.queue();
{
let result = event_loop.ime
.borrow_mut()
.create_context(window.xwindow);
let result = event_loop.ime.borrow_mut().create_context(window.xwindow);
if let Err(err) = result {
let e = match err {
ImeContextCreationError::XError(err) => OsError::XError(err),
ImeContextCreationError::Null => OsError::XMisc("IME Context creation failed"),
ImeContextCreationError::Null => {
OsError::XMisc("IME Context creation failed")
}
};
return Err(os_error!(e));
}
@ -386,10 +396,14 @@ impl UnownedWindow {
window.set_maximized_inner(window_attrs.maximized).queue();
}
if window_attrs.fullscreen.is_some() {
window.set_fullscreen_inner(window_attrs.fullscreen.clone()).queue();
window
.set_fullscreen_inner(window_attrs.fullscreen.clone())
.queue();
}
if window_attrs.always_on_top {
window.set_always_on_top_inner(window_attrs.always_on_top).queue();
window
.set_always_on_top_inner(window_attrs.always_on_top)
.queue();
}
if window_attrs.visible {
@ -397,7 +411,8 @@ impl UnownedWindow {
// XSetInputFocus generates an error if the window is not visible, so we wait
// until we receive VisibilityNotify.
let mut event = mem::uninitialized();
(xconn.xlib.XIfEvent)( // This will flush the request buffer IF it blocks.
(xconn.xlib.XIfEvent)(
// This will flush the request buffer IF it blocks.
xconn.display,
&mut event as *mut ffi::XEvent,
Some(visibility_predicate),
@ -414,7 +429,8 @@ impl UnownedWindow {
}
// We never want to give the user a broken window, since by then, it's too late to handle.
xconn.sync_with_server()
xconn
.sync_with_server()
.map(|_| window)
.map_err(|x_err| os_error!(OsError::XError(x_err)))
}
@ -429,7 +445,7 @@ impl UnownedWindow {
LogicalSize::from_physical((width, height), dpi)
}
fn set_pid(&self) -> Option<util::Flusher> {
fn set_pid(&self) -> Option<util::Flusher<'_>> {
let pid_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_PID\0") };
let client_machine_atom = unsafe { self.xconn.get_atom_unchecked(b"WM_CLIENT_MACHINE\0") };
unsafe {
@ -439,18 +455,22 @@ impl UnownedWindow {
const MAXHOSTNAMELEN: usize = 256;
let mut hostname: [c_char; MAXHOSTNAMELEN] = mem::uninitialized();
let status = libc::gethostname(hostname.as_mut_ptr(), hostname.len());
if status != 0 { return None; }
if status != 0 {
return None;
}
hostname[MAXHOSTNAMELEN - 1] = '\0' as c_char; // a little extra safety
let hostname_length = libc::strlen(hostname.as_ptr());
(hostname, hostname_length as usize)
};
self.xconn.change_property(
self.xwindow,
pid_atom,
ffi::XA_CARDINAL,
util::PropMode::Replace,
&[libc::getpid() as util::Cardinal],
).queue();
self.xconn
.change_property(
self.xwindow,
pid_atom,
ffi::XA_CARDINAL,
util::PropMode::Replace,
&[libc::getpid() as util::Cardinal],
)
.queue();
let flusher = self.xconn.change_property(
self.xwindow,
client_machine_atom,
@ -462,7 +482,7 @@ impl UnownedWindow {
}
}
fn set_window_type(&self, window_type: util::WindowType) -> util::Flusher {
fn set_window_type(&self, window_type: util::WindowType) -> util::Flusher<'_> {
let hint_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_WINDOW_TYPE\0") };
let window_type_atom = window_type.as_atom(&self.xconn);
self.xconn.change_property(
@ -474,7 +494,7 @@ impl UnownedWindow {
)
}
fn set_gtk_theme_variant(&self, variant: String) -> util::Flusher {
fn set_gtk_theme_variant(&self, variant: String) -> util::Flusher<'_> {
let hint_atom = unsafe { self.xconn.get_atom_unchecked(b"_GTK_THEME_VARIANT\0") };
let utf8_atom = unsafe { self.xconn.get_atom_unchecked(b"UTF8_STRING\0") };
let variant = CString::new(variant).expect("`_GTK_THEME_VARIANT` contained null byte");
@ -489,20 +509,26 @@ impl UnownedWindow {
#[inline]
pub fn set_urgent(&self, is_urgent: bool) {
let mut wm_hints = self.xconn.get_wm_hints(self.xwindow).expect("`XGetWMHints` failed");
let mut wm_hints = self
.xconn
.get_wm_hints(self.xwindow)
.expect("`XGetWMHints` failed");
if is_urgent {
(*wm_hints).flags |= ffi::XUrgencyHint;
} else {
(*wm_hints).flags &= !ffi::XUrgencyHint;
}
self.xconn.set_wm_hints(self.xwindow, wm_hints).flush().expect("Failed to set urgency hint");
self.xconn
.set_wm_hints(self.xwindow, wm_hints)
.flush()
.expect("Failed to set urgency hint");
}
fn set_netwm(
&self,
operation: util::StateOperation,
properties: (c_long, c_long, c_long, c_long),
) -> util::Flusher {
) -> util::Flusher<'_> {
let state_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE\0") };
self.xconn.send_client_msg(
self.xwindow,
@ -519,12 +545,13 @@ impl UnownedWindow {
)
}
fn set_fullscreen_hint(&self, fullscreen: bool) -> util::Flusher {
let fullscreen_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_FULLSCREEN\0") };
fn set_fullscreen_hint(&self, fullscreen: bool) -> util::Flusher<'_> {
let fullscreen_atom =
unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_FULLSCREEN\0") };
self.set_netwm(fullscreen.into(), (fullscreen_atom as c_long, 0, 0, 0))
}
fn set_fullscreen_inner(&self, monitor: Option<RootMonitorHandle>) -> util::Flusher {
fn set_fullscreen_inner(&self, monitor: Option<RootMonitorHandle>) -> util::Flusher<'_> {
match monitor {
None => {
let flusher = self.set_fullscreen_hint(false);
@ -532,12 +559,15 @@ impl UnownedWindow {
self.set_position_inner(position.0, position.1).queue();
}
flusher
},
Some(RootMonitorHandle { inner: PlatformMonitorHandle::X(monitor) }) => {
}
Some(RootMonitorHandle {
inner: PlatformMonitorHandle::X(monitor),
}) => {
let window_position = self.outer_position_physical();
self.shared_state.lock().restore_position = Some(window_position);
let monitor_origin: (i32, i32) = monitor.position().into();
self.set_position_inner(monitor_origin.0, monitor_origin.1).queue();
self.set_position_inner(monitor_origin.0, monitor_origin.1)
.queue();
self.set_fullscreen_hint(true)
}
_ => unreachable!(),
@ -567,17 +597,15 @@ impl UnownedWindow {
#[inline]
pub fn current_monitor(&self) -> X11MonitorHandle {
let monitor = self.shared_state
.lock()
.last_monitor
.as_ref()
.cloned();
monitor
.unwrap_or_else(|| {
let monitor = self.xconn.get_monitor_for_window(Some(self.get_rect())).to_owned();
self.shared_state.lock().last_monitor = Some(monitor.clone());
monitor
})
let monitor = self.shared_state.lock().last_monitor.as_ref().cloned();
monitor.unwrap_or_else(|| {
let monitor = self
.xconn
.get_monitor_for_window(Some(self.get_rect()))
.to_owned();
self.shared_state.lock().last_monitor = Some(monitor.clone());
monitor
})
}
pub fn available_monitors(&self) -> Vec<X11MonitorHandle> {
@ -588,10 +616,19 @@ impl UnownedWindow {
self.xconn.primary_monitor()
}
fn set_maximized_inner(&self, maximized: bool) -> util::Flusher {
let horz_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_HORZ\0") };
let vert_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_VERT\0") };
self.set_netwm(maximized.into(), (horz_atom as c_long, vert_atom as c_long, 0, 0))
fn set_maximized_inner(&self, maximized: bool) -> util::Flusher<'_> {
let horz_atom = unsafe {
self.xconn
.get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_HORZ\0")
};
let vert_atom = unsafe {
self.xconn
.get_atom_unchecked(b"_NET_WM_STATE_MAXIMIZED_VERT\0")
};
self.set_netwm(
maximized.into(),
(horz_atom as c_long, vert_atom as c_long, 0, 0),
)
}
#[inline]
@ -602,7 +639,7 @@ impl UnownedWindow {
self.invalidate_cached_frame_extents();
}
fn set_title_inner(&self, title: &str) -> util::Flusher {
fn set_title_inner(&self, title: &str) -> util::Flusher<'_> {
let wm_name_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_NAME\0") };
let utf8_atom = unsafe { self.xconn.get_atom_unchecked(b"UTF8_STRING\0") };
let title = CString::new(title).expect("Window title contained null byte");
@ -629,21 +666,12 @@ impl UnownedWindow {
.expect("Failed to set window title");
}
fn set_decorations_inner(&self, decorations: bool) -> util::Flusher {
let wm_hints = unsafe { self.xconn.get_atom_unchecked(b"_MOTIF_WM_HINTS\0") };
self.xconn.change_property(
self.xwindow,
wm_hints,
wm_hints,
util::PropMode::Replace,
&[
util::MWM_HINTS_DECORATIONS, // flags
0, // functions
decorations as c_ulong, // decorations
0, // input mode
0, // status
],
)
fn set_decorations_inner(&self, decorations: bool) -> util::Flusher<'_> {
let mut hints = self.xconn.get_motif_hints(self.xwindow);
hints.set_decorations(decorations);
self.xconn.set_motif_hints(self.xwindow, &hints)
}
#[inline]
@ -654,7 +682,15 @@ impl UnownedWindow {
self.invalidate_cached_frame_extents();
}
fn set_always_on_top_inner(&self, always_on_top: bool) -> util::Flusher {
fn set_maximizable_inner(&self, maximizable: bool) -> util::Flusher<'_> {
let mut hints = self.xconn.get_motif_hints(self.xwindow);
hints.set_maximizable(maximizable);
self.xconn.set_motif_hints(self.xwindow, &hints)
}
fn set_always_on_top_inner(&self, always_on_top: bool) -> util::Flusher<'_> {
let above_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_STATE_ABOVE\0") };
self.set_netwm(always_on_top.into(), (above_atom as c_long, 0, 0, 0))
}
@ -666,7 +702,7 @@ impl UnownedWindow {
.expect("Failed to set always-on-top state");
}
fn set_icon_inner(&self, icon: Icon) -> util::Flusher {
fn set_icon_inner(&self, icon: Icon) -> util::Flusher<'_> {
let icon_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_ICON\0") };
let data = icon.to_cardinals();
self.xconn.change_property(
@ -678,7 +714,7 @@ impl UnownedWindow {
)
}
fn unset_icon_inner(&self) -> util::Flusher {
fn unset_icon_inner(&self) -> util::Flusher<'_> {
let icon_atom = unsafe { self.xconn.get_atom_unchecked(b"_NET_WM_ICON\0") };
let empty_data: [util::Cardinal; 0] = [];
self.xconn.change_property(
@ -695,7 +731,9 @@ impl UnownedWindow {
match icon {
Some(icon) => self.set_icon_inner(icon),
None => self.unset_icon_inner(),
}.flush().expect("Failed to set icons");
}
.flush()
.expect("Failed to set icons");
}
#[inline]
@ -703,19 +741,23 @@ impl UnownedWindow {
match visible {
true => unsafe {
(self.xconn.xlib.XMapRaised)(self.xconn.display, self.xwindow);
self.xconn.flush_requests()
self.xconn
.flush_requests()
.expect("Failed to call XMapRaised");
},
false => unsafe {
(self.xconn.xlib.XUnmapWindow)(self.xconn.display, self.xwindow);
self.xconn.flush_requests()
self.xconn
.flush_requests()
.expect("Failed to call XUnmapWindow");
}
},
}
}
fn update_cached_frame_extents(&self) {
let extents = self.xconn.get_frame_extents_heuristic(self.xwindow, self.root);
let extents = self
.xconn
.get_frame_extents_heuristic(self.xwindow, self.root);
(*self.shared_state.lock()).frame_extents = Some(extents);
}
@ -749,7 +791,8 @@ impl UnownedWindow {
pub(crate) fn inner_position_physical(&self) -> (i32, i32) {
// This should be okay to unwrap since the only error XTranslateCoordinates can return
// is BadWindow, and if the window handle is bad we have bigger problems.
self.xconn.translate_coords(self.xwindow, self.root)
self.xconn
.translate_coords(self.xwindow, self.root)
.map(|coords| (coords.x_rel_root, coords.y_rel_root))
.unwrap()
}
@ -759,7 +802,7 @@ impl UnownedWindow {
Ok(self.logicalize_coords(self.inner_position_physical()))
}
pub(crate) fn set_position_inner(&self, mut x: i32, mut y: i32) -> util::Flusher {
pub(crate) fn set_position_inner(&self, mut x: i32, mut y: i32) -> util::Flusher<'_> {
// There are a few WMs that set client area position rather than window position, so
// we'll translate for consistency.
if util::wm_name_is_one_of(&["Enlightenment", "FVWM"]) {
@ -773,12 +816,7 @@ impl UnownedWindow {
}
}
unsafe {
(self.xconn.xlib.XMoveWindow)(
self.xconn.display,
self.xwindow,
x as c_int,
y as c_int,
);
(self.xconn.xlib.XMoveWindow)(self.xconn.display, self.xwindow, x as c_int, y as c_int);
}
util::Flusher::new(&self.xconn)
}
@ -798,7 +836,8 @@ impl UnownedWindow {
pub(crate) fn inner_size_physical(&self) -> (u32, u32) {
// This should be okay to unwrap since the only error XGetGeometry can return
// is BadWindow, and if the window handle is bad we have bigger problems.
self.xconn.get_geometry(self.xwindow)
self.xconn
.get_geometry(self.xwindow)
.map(|geo| (geo.width, geo.height))
.unwrap()
}
@ -840,7 +879,8 @@ impl UnownedWindow {
height as c_uint,
);
self.xconn.flush_requests()
}.expect("Failed to call `XResizeWindow`");
}
.expect("Failed to call `XResizeWindow`");
}
#[inline]
@ -851,11 +891,14 @@ impl UnownedWindow {
}
fn update_normal_hints<F>(&self, callback: F) -> Result<(), XError>
where F: FnOnce(&mut util::NormalHints) -> ()
where
F: FnOnce(&mut util::NormalHints<'_>) -> (),
{
let mut normal_hints = self.xconn.get_normal_hints(self.xwindow)?;
callback(&mut normal_hints);
self.xconn.set_normal_hints(self.xwindow, normal_hints).flush()
self.xconn
.set_normal_hints(self.xwindow, normal_hints)
.flush()
}
pub(crate) fn set_min_inner_size_physical(&self, dimensions: Option<(u32, u32)>) {
@ -866,9 +909,8 @@ impl UnownedWindow {
#[inline]
pub fn set_min_inner_size(&self, logical_dimensions: Option<LogicalSize>) {
self.shared_state.lock().min_inner_size = logical_dimensions;
let physical_dimensions = logical_dimensions.map(|logical_dimensions| {
logical_dimensions.to_physical(self.hidpi_factor()).into()
});
let physical_dimensions = logical_dimensions
.map(|logical_dimensions| logical_dimensions.to_physical(self.hidpi_factor()).into());
self.set_min_inner_size_physical(physical_dimensions);
}
@ -880,9 +922,8 @@ impl UnownedWindow {
#[inline]
pub fn set_max_inner_size(&self, logical_dimensions: Option<LogicalSize>) {
self.shared_state.lock().max_inner_size = logical_dimensions;
let physical_dimensions = logical_dimensions.map(|logical_dimensions| {
logical_dimensions.to_physical(self.hidpi_factor()).into()
});
let physical_dimensions = logical_dimensions
.map(|logical_dimensions| logical_dimensions.to_physical(self.hidpi_factor()).into());
self.set_max_inner_size_physical(physical_dimensions);
}
@ -892,7 +933,7 @@ impl UnownedWindow {
new_dpi_factor: f64,
width: f64,
height: f64,
) -> (f64, f64, util::Flusher) {
) -> (f64, f64, util::Flusher<'_>) {
let scale_factor = new_dpi_factor / old_dpi_factor;
let new_width = width * scale_factor;
let new_height = height * scale_factor;
@ -910,7 +951,8 @@ impl UnownedWindow {
normal_hints.set_min_size(min_size);
normal_hints.set_resize_increments(resize_increments);
normal_hints.set_base_size(base_size);
}).expect("Failed to update normal hints");
})
.expect("Failed to update normal hints");
unsafe {
(self.xconn.xlib.XResizeWindow)(
self.xconn.display,
@ -933,12 +975,17 @@ impl UnownedWindow {
let (logical_min, logical_max) = if resizable {
let shared_state_lock = self.shared_state.lock();
(shared_state_lock.min_inner_size, shared_state_lock.max_inner_size)
(
shared_state_lock.min_inner_size,
shared_state_lock.max_inner_size,
)
} else {
let window_size = Some(self.inner_size());
(window_size.clone(), window_size)
};
self.set_maximizable_inner(resizable).queue();
let dpi_factor = self.hidpi_factor();
let min_inner_size = logical_min
.map(|logical_size| logical_size.to_physical(dpi_factor))
@ -949,7 +996,8 @@ impl UnownedWindow {
self.update_normal_hints(|normal_hints| {
normal_hints.set_min_size(min_inner_size);
normal_hints.set_max_size(max_inner_size);
}).expect("Failed to call `XSetWMNormalHints`");
})
.expect("Failed to call `XSetWMNormalHints`");
}
#[inline]
@ -974,9 +1022,7 @@ impl UnownedWindow {
#[inline]
pub fn xcb_connection(&self) -> *mut c_void {
unsafe {
(self.xconn.xlib_xcb.XGetXCBConnection)(self.xconn.display) as *mut _
}
unsafe { (self.xconn.xlib_xcb.XGetXCBConnection)(self.xconn.display) as *mut _ }
}
fn load_cursor(&self, name: &[u8]) -> ffi::Cursor {
@ -999,13 +1045,9 @@ impl UnownedWindow {
}
fn get_cursor(&self, cursor: CursorIcon) -> ffi::Cursor {
let load = |name: &[u8]| {
self.load_cursor(name)
};
let load = |name: &[u8]| self.load_cursor(name);
let loadn = |names: &[&[u8]]| {
self.load_first_existing_cursor(names)
};
let loadn = |names: &[&[u8]]| self.load_first_existing_cursor(names);
// Try multiple names in some cases where the name
// differs on the desktop environments or themes.
@ -1030,7 +1072,6 @@ impl UnownedWindow {
CursorIcon::NoDrop => loadn(&[b"no-drop\0", b"circle\0"]),
CursorIcon::NotAllowed => load(b"crossed_circle\0"),
// Resize cursors
CursorIcon::EResize => load(b"right_side\0"),
CursorIcon::NResize => load(b"top_side\0"),
@ -1063,7 +1104,9 @@ impl UnownedWindow {
if cursor != 0 {
(self.xconn.xlib.XFreeCursor)(self.xconn.display, cursor);
}
self.xconn.flush_requests().expect("Failed to set or free the cursor");
self.xconn
.flush_requests()
.expect("Failed to set or free the cursor");
}
}
@ -1081,13 +1124,7 @@ impl UnownedWindow {
fn create_empty_cursor(&self) -> Option<ffi::Cursor> {
let data = 0;
let pixmap = unsafe {
(self.xconn.xlib.XCreateBitmapFromData)(
self.xconn.display,
self.xwindow,
&data,
1,
1,
)
(self.xconn.xlib.XCreateBitmapFromData)(self.xconn.display, self.xwindow, &data, 1, 1)
};
if pixmap == 0 {
// Failed to allocate
@ -1116,7 +1153,9 @@ impl UnownedWindow {
#[inline]
pub fn set_cursor_grab(&self, grab: bool) -> Result<(), ExternalError> {
let mut grabbed_lock = self.cursor_grabbed.lock();
if grab == *grabbed_lock { return Ok(()); }
if grab == *grabbed_lock {
return Ok(());
}
unsafe {
// We ungrab before grabbing to prevent passive grabs from causing `AlreadyGrabbed`.
// Therefore, this is common to both codepaths.
@ -1128,8 +1167,7 @@ impl UnownedWindow {
self.xconn.display,
self.xwindow,
ffi::True,
(
ffi::ButtonPressMask
(ffi::ButtonPressMask
| ffi::ButtonReleaseMask
| ffi::EnterWindowMask
| ffi::LeaveWindowMask
@ -1141,8 +1179,7 @@ impl UnownedWindow {
| ffi::Button4MotionMask
| ffi::Button5MotionMask
| ffi::ButtonMotionMask
| ffi::KeymapStateMask
) as c_uint,
| ffi::KeymapStateMask) as c_uint,
ffi::GrabModeAsync,
ffi::GrabModeAsync,
self.xwindow,
@ -1153,14 +1190,20 @@ impl UnownedWindow {
match result {
ffi::GrabSuccess => Ok(()),
ffi::AlreadyGrabbed => Err("Cursor could not be grabbed: already grabbed by another client"),
ffi::AlreadyGrabbed => {
Err("Cursor could not be grabbed: already grabbed by another client")
}
ffi::GrabInvalidTime => Err("Cursor could not be grabbed: invalid time"),
ffi::GrabNotViewable => Err("Cursor could not be grabbed: grab location not viewable"),
ffi::GrabNotViewable => {
Err("Cursor could not be grabbed: grab location not viewable")
}
ffi::GrabFrozen => Err("Cursor could not be grabbed: frozen by another client"),
_ => unreachable!(),
}.map_err(|err| ExternalError::Os(os_error!(OsError::XMisc(err))))
}
.map_err(|err| ExternalError::Os(os_error!(OsError::XMisc(err))))
} else {
self.xconn.flush_requests()
self.xconn
.flush_requests()
.map_err(|err| ExternalError::Os(os_error!(OsError::XError(err))))
};
if result.is_ok() {
@ -1172,11 +1215,14 @@ impl UnownedWindow {
#[inline]
pub fn set_cursor_visible(&self, visible: bool) {
let mut visible_lock = self.cursor_visible.lock();
if visible == *visible_lock {return; }
if visible == *visible_lock {
return;
}
let cursor = if visible {
self.get_cursor(*self.cursor.lock())
} else {
self.create_empty_cursor().expect("Failed to create empty cursor")
self.create_empty_cursor()
.expect("Failed to create empty cursor")
};
*visible_lock = visible;
drop(visible_lock);
@ -1190,29 +1236,25 @@ impl UnownedWindow {
pub fn set_cursor_position_physical(&self, x: i32, y: i32) -> Result<(), ExternalError> {
unsafe {
(self.xconn.xlib.XWarpPointer)(
self.xconn.display,
0,
self.xwindow,
0,
0,
0,
0,
x,
y,
);
self.xconn.flush_requests().map_err(|e| ExternalError::Os(os_error!(OsError::XError(e))))
(self.xconn.xlib.XWarpPointer)(self.xconn.display, 0, self.xwindow, 0, 0, 0, 0, x, y);
self.xconn
.flush_requests()
.map_err(|e| ExternalError::Os(os_error!(OsError::XError(e))))
}
}
#[inline]
pub fn set_cursor_position(&self, logical_position: LogicalPosition) -> Result<(), ExternalError> {
pub fn set_cursor_position(
&self,
logical_position: LogicalPosition,
) -> Result<(), ExternalError> {
let (x, y) = logical_position.to_physical(self.hidpi_factor()).into();
self.set_cursor_position_physical(x, y)
}
pub(crate) fn set_ime_position_physical(&self, x: i32, y: i32) {
let _ = self.ime_sender
let _ = self
.ime_sender
.lock()
.send((self.xwindow, x as i16, y as i16));
}
@ -1224,10 +1266,15 @@ impl UnownedWindow {
}
#[inline]
pub fn id(&self) -> WindowId { WindowId(self.xwindow) }
pub fn id(&self) -> WindowId {
WindowId(self.xwindow)
}
#[inline]
pub fn request_redraw(&self) {
self.pending_redraws.lock().unwrap().insert(WindowId(self.xwindow));
self.pending_redraws
.lock()
.unwrap()
.insert(WindowId(self.xwindow));
}
}

View file

@ -1,7 +1,4 @@
use std::ptr;
use std::fmt;
use std::error::Error;
use std::os::raw::c_int;
use std::{error::Error, fmt, os::raw::c_int, ptr};
use libc;
use parking_lot::Mutex;
@ -27,7 +24,8 @@ pub struct XConnection {
unsafe impl Send for XConnection {}
unsafe impl Sync for XConnection {}
pub type XErrorHandler = Option<unsafe extern fn(*mut ffi::Display, *mut ffi::XErrorEvent) -> libc::c_int>;
pub type XErrorHandler =
Option<unsafe extern "C" fn(*mut ffi::Display, *mut ffi::XErrorEvent) -> libc::c_int>;
impl XConnection {
pub fn new(error_handler: XErrorHandler) -> Result<XConnection, XNotSupported> {
@ -53,9 +51,7 @@ impl XConnection {
};
// Get X11 socket file descriptor
let fd = unsafe {
(xlib.XConnectionNumber)(display)
};
let fd = unsafe { (xlib.XConnectionNumber)(display) };
Ok(XConnection {
xlib,
@ -90,7 +86,7 @@ impl XConnection {
}
impl fmt::Debug for XConnection {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display.fmt(f)
}
}
@ -119,9 +115,12 @@ impl Error for XError {
}
impl fmt::Display for XError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(formatter, "X error: {} (code: {}, request code: {}, minor code: {})",
self.description, self.error_code, self.request_code, self.minor_code)
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
formatter,
"X error: {} (code: {}, request code: {}, minor code: {})",
self.description, self.error_code, self.request_code, self.minor_code
)
}
}
@ -131,7 +130,7 @@ pub enum XNotSupported {
/// Failed to load one or several shared libraries.
LibraryOpenError(ffi::OpenError),
/// Connecting to the X server with `XOpenDisplay` failed.
XOpenDisplayFailed, // TODO: add better message
XOpenDisplayFailed, // TODO: add better message
}
impl From<ffi::OpenError> for XNotSupported {
@ -151,16 +150,16 @@ impl Error for XNotSupported {
}
#[inline]
fn cause(&self) -> Option<&Error> {
fn cause(&self) -> Option<&dyn Error> {
match *self {
XNotSupported::LibraryOpenError(ref err) => Some(err),
_ => None
_ => None,
}
}
}
impl fmt::Display for XNotSupported {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
formatter.write_str(self.description())
}
}

View file

@ -1,10 +1,18 @@
use std::collections::VecDeque;
use cocoa::{appkit::{self, NSEvent}, base::id};
use objc::{declare::ClassDecl, runtime::{Class, Object, Sel}};
use cocoa::{
appkit::{self, NSEvent},
base::id,
};
use objc::{
declare::ClassDecl,
runtime::{Class, Object, Sel},
};
use event::{DeviceEvent, Event};
use platform_impl::platform::{app_state::AppState, DEVICE_ID, util};
use crate::{
event::{DeviceEvent, ElementState, Event},
platform_impl::platform::{app_state::AppState, util, DEVICE_ID},
};
pub struct AppClass(pub *const Class);
unsafe impl Send for AppClass {}
@ -17,7 +25,7 @@ lazy_static! {
decl.add_method(
sel!(sendEvent:),
send_event as extern fn(&Object, Sel, id),
send_event as extern "C" fn(&Object, Sel, id),
);
AppClass(decl.register())
@ -27,23 +35,25 @@ lazy_static! {
// Normally, holding Cmd + any key never sends us a `keyUp` event for that key.
// Overriding `sendEvent:` like this fixes that. (https://stackoverflow.com/a/15294196)
// Fun fact: Firefox still has this bug! (https://bugzilla.mozilla.org/show_bug.cgi?id=1299553)
extern fn send_event(this: &Object, _sel: Sel, event: id) {
extern "C" fn send_event(this: &Object, _sel: Sel, event: id) {
unsafe {
// For posterity, there are some undocumented event types
// (https://github.com/servo/cocoa-rs/issues/155)
// but that doesn't really matter here.
let event_type = event.eventType();
let modifier_flags = event.modifierFlags();
if event_type == appkit::NSKeyUp && util::has_flag(
modifier_flags,
appkit::NSEventModifierFlags::NSCommandKeyMask,
) {
if event_type == appkit::NSKeyUp
&& util::has_flag(
modifier_flags,
appkit::NSEventModifierFlags::NSCommandKeyMask,
)
{
let key_window: id = msg_send![this, keyWindow];
let _: () = msg_send![key_window, sendEvent:event];
let _: () = msg_send![key_window, sendEvent: event];
} else {
maybe_dispatch_device_event(event);
let superclass = util::superclass(this);
let _: () = msg_send![super(this, superclass), sendEvent:event];
let _: () = msg_send![super(this, superclass), sendEvent: event];
}
}
}
@ -51,10 +61,10 @@ extern fn send_event(this: &Object, _sel: Sel, event: id) {
unsafe fn maybe_dispatch_device_event(event: id) {
let event_type = event.eventType();
match event_type {
appkit::NSMouseMoved |
appkit::NSLeftMouseDragged |
appkit::NSOtherMouseDragged |
appkit::NSRightMouseDragged => {
appkit::NSMouseMoved
| appkit::NSLeftMouseDragged
| appkit::NSOtherMouseDragged
| appkit::NSRightMouseDragged => {
let mut events = VecDeque::with_capacity(3);
let delta_x = event.deltaX() as f64;
@ -63,26 +73,60 @@ unsafe fn maybe_dispatch_device_event(event: id) {
if delta_x != 0.0 {
events.push_back(Event::DeviceEvent {
device_id: DEVICE_ID,
event: DeviceEvent::Motion { axis: 0, value: delta_x },
event: DeviceEvent::Motion {
axis: 0,
value: delta_x,
},
});
}
if delta_y != 0.0 {
events.push_back(Event::DeviceEvent {
device_id: DEVICE_ID,
event: DeviceEvent::Motion { axis: 1, value: delta_y },
event: DeviceEvent::Motion {
axis: 1,
value: delta_y,
},
});
}
if delta_x != 0.0 || delta_y != 0.0 {
events.push_back(Event::DeviceEvent {
device_id: DEVICE_ID,
event: DeviceEvent::MouseMotion { delta: (delta_x, delta_y) },
event: DeviceEvent::MouseMotion {
delta: (delta_x, delta_y),
},
});
}
AppState::queue_events(events);
},
}
appkit::NSLeftMouseDown | appkit::NSRightMouseDown | appkit::NSOtherMouseDown => {
let mut events = VecDeque::with_capacity(1);
events.push_back(Event::DeviceEvent {
device_id: DEVICE_ID,
event: DeviceEvent::Button {
button: event.buttonNumber() as u32,
state: ElementState::Pressed,
},
});
AppState::queue_events(events);
}
appkit::NSLeftMouseUp | appkit::NSRightMouseUp | appkit::NSOtherMouseUp => {
let mut events = VecDeque::with_capacity(1);
events.push_back(Event::DeviceEvent {
device_id: DEVICE_ID,
event: DeviceEvent::Button {
button: event.buttonNumber() as u32,
state: ElementState::Released,
},
});
AppState::queue_events(events);
}
_ => (),
}
}

View file

@ -1,7 +1,10 @@
use cocoa::base::id;
use objc::{runtime::{Class, Object, Sel, BOOL, YES}, declare::ClassDecl};
use objc::{
declare::ClassDecl,
runtime::{Class, Object, Sel, BOOL, YES},
};
use platform_impl::platform::app_state::AppState;
use crate::platform_impl::platform::app_state::AppState;
pub struct AppDelegateClass(pub *const Class);
unsafe impl Send for AppDelegateClass {}
@ -14,67 +17,67 @@ lazy_static! {
decl.add_method(
sel!(applicationDidFinishLaunching:),
did_finish_launching as extern fn(&Object, Sel, id) -> BOOL,
did_finish_launching as extern "C" fn(&Object, Sel, id) -> BOOL,
);
decl.add_method(
sel!(applicationDidBecomeActive:),
did_become_active as extern fn(&Object, Sel, id),
did_become_active as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(applicationWillResignActive:),
will_resign_active as extern fn(&Object, Sel, id),
will_resign_active as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(applicationWillEnterForeground:),
will_enter_foreground as extern fn(&Object, Sel, id),
will_enter_foreground as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(applicationDidEnterBackground:),
did_enter_background as extern fn(&Object, Sel, id),
did_enter_background as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(applicationWillTerminate:),
will_terminate as extern fn(&Object, Sel, id),
will_terminate as extern "C" fn(&Object, Sel, id),
);
AppDelegateClass(decl.register())
};
}
extern fn did_finish_launching(_: &Object, _: Sel, _: id) -> BOOL {
extern "C" fn did_finish_launching(_: &Object, _: Sel, _: id) -> BOOL {
trace!("Triggered `didFinishLaunching`");
AppState::launched();
trace!("Completed `didFinishLaunching`");
YES
}
extern fn did_become_active(_: &Object, _: Sel, _: id) {
extern "C" fn did_become_active(_: &Object, _: Sel, _: id) {
trace!("Triggered `didBecomeActive`");
/*unsafe {
HANDLER.lock().unwrap().handle_nonuser_event(Event::Suspended(false))
HANDLER.lock().unwrap().handle_nonuser_event(Event::Resumed)
}*/
trace!("Completed `didBecomeActive`");
}
extern fn will_resign_active(_: &Object, _: Sel, _: id) {
extern "C" fn will_resign_active(_: &Object, _: Sel, _: id) {
trace!("Triggered `willResignActive`");
/*unsafe {
HANDLER.lock().unwrap().handle_nonuser_event(Event::Suspended(true))
HANDLER.lock().unwrap().handle_nonuser_event(Event::Suspended)
}*/
trace!("Completed `willResignActive`");
}
extern fn will_enter_foreground(_: &Object, _: Sel, _: id) {
extern "C" fn will_enter_foreground(_: &Object, _: Sel, _: id) {
trace!("Triggered `willEnterForeground`");
trace!("Completed `willEnterForeground`");
}
extern fn did_enter_background(_: &Object, _: Sel, _: id) {
extern "C" fn did_enter_background(_: &Object, _: Sel, _: id) {
trace!("Triggered `didEnterBackground`");
trace!("Completed `didEnterBackground`");
}
extern fn will_terminate(_: &Object, _: Sel, _: id) {
extern "C" fn will_terminate(_: &Object, _: Sel, _: id) {
trace!("Triggered `willTerminate`");
/*unsafe {
let app: id = msg_send![class!(UIApplication), sharedApplication];

View file

@ -1,17 +1,23 @@
use std::{
collections::VecDeque, fmt::{self, Debug, Formatter},
hint::unreachable_unchecked, mem,
sync::{atomic::{AtomicBool, Ordering}, Mutex, MutexGuard}, time::Instant,
collections::VecDeque,
fmt::{self, Debug},
hint::unreachable_unchecked,
mem,
sync::{
atomic::{AtomicBool, Ordering},
Mutex, MutexGuard,
},
time::Instant,
};
use cocoa::{appkit::NSApp, base::nil};
use {
use crate::{
event::{Event, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoopWindowTarget as RootWindowTarget},
platform_impl::platform::{observer::EventLoopWaker, util::Never},
window::WindowId,
};
use platform_impl::platform::{observer::EventLoopWaker, util::Never};
lazy_static! {
static ref HANDLER: Handler = Default::default();
@ -38,8 +44,9 @@ struct EventLoopHandler<F, T: 'static> {
}
impl<F, T> Debug for EventLoopHandler<F, T> {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.debug_struct("EventLoopHandler")
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("EventLoopHandler")
.field("window_target", &self.window_target)
.finish()
}
@ -51,11 +58,7 @@ where
T: 'static,
{
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow) {
(self.callback)(
event.userify(),
&self.window_target,
control_flow,
);
(self.callback)(event.userify(), &self.window_target, control_flow);
self.will_exit |= *control_flow == ControlFlow::Exit;
if self.will_exit {
*control_flow = ControlFlow::Exit;
@ -65,11 +68,7 @@ where
fn handle_user_events(&mut self, control_flow: &mut ControlFlow) {
let mut will_exit = self.will_exit;
for event in self.window_target.p.receiver.try_iter() {
(self.callback)(
Event::UserEvent(event),
&self.window_target,
control_flow,
);
(self.callback)(Event::UserEvent(event), &self.window_target, control_flow);
will_exit |= *control_flow == ControlFlow::Exit;
if will_exit {
*control_flow = ControlFlow::Exit;
@ -167,18 +166,13 @@ impl Handler {
fn handle_nonuser_event(&self, event: Event<Never>) {
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
callback.handle_nonuser_event(
event,
&mut *self.control_flow.lock().unwrap(),
);
callback.handle_nonuser_event(event, &mut *self.control_flow.lock().unwrap());
}
}
fn handle_user_events(&self) {
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
callback.handle_user_events(
&mut *self.control_flow.lock().unwrap(),
);
callback.handle_user_events(&mut *self.control_flow.lock().unwrap());
}
}
}
@ -213,7 +207,9 @@ impl AppState {
}
pub fn wakeup() {
if !HANDLER.is_ready() { return }
if !HANDLER.is_ready() {
return;
}
let start = HANDLER.get_start_time().unwrap();
let cause = match HANDLER.get_control_flow_and_update_prev() {
ControlFlow::Poll => StartCause::Poll,
@ -233,8 +229,8 @@ impl AppState {
requested_resume: Some(requested_resume),
}
}
},
ControlFlow::Exit => StartCause::Poll,//panic!("unexpected `ControlFlow::Exit`"),
}
ControlFlow::Exit => StartCause::Poll, //panic!("unexpected `ControlFlow::Exit`"),
};
HANDLER.set_in_callback(true);
HANDLER.handle_nonuser_event(Event::NewEvents(cause));
@ -278,7 +274,9 @@ impl AppState {
}
pub fn cleared() {
if !HANDLER.is_ready() { return }
if !HANDLER.is_ready() {
return;
}
if !HANDLER.get_in_callback() {
HANDLER.set_in_callback(true);
HANDLER.handle_user_events();
@ -295,8 +293,8 @@ impl AppState {
HANDLER.set_in_callback(false);
}
if HANDLER.should_exit() {
let _: () = unsafe { msg_send![NSApp(), stop:nil] };
return
let _: () = unsafe { msg_send![NSApp(), stop: nil] };
return;
}
HANDLER.update_start_time();
match HANDLER.get_old_and_new_control_flow() {

View file

@ -1,12 +1,14 @@
use std::os::raw::c_ushort;
use cocoa::{appkit::{NSEvent, NSEventModifierFlags}, base::id};
use event::{
ElementState, KeyboardInput,
ModifiersState, VirtualKeyCode, WindowEvent,
use cocoa::{
appkit::{NSEvent, NSEventModifierFlags},
base::id,
};
use crate::{
event::{ElementState, KeyboardInput, ModifiersState, VirtualKeyCode, WindowEvent},
platform_impl::platform::DEVICE_ID,
};
use platform_impl::platform::DEVICE_ID;
pub fn char_to_keycode(c: char) -> Option<VirtualKeyCode> {
// We only translate keys that are affected by keyboard layout.
@ -57,9 +59,9 @@ pub fn char_to_keycode(c: char) -> Option<VirtualKeyCode> {
'-' | '_' => VirtualKeyCode::Minus,
']' | '}' => VirtualKeyCode::RBracket,
'[' | '{' => VirtualKeyCode::LBracket,
'\''| '"' => VirtualKeyCode::Apostrophe,
'\'' | '"' => VirtualKeyCode::Apostrophe,
';' | ':' => VirtualKeyCode::Semicolon,
'\\'| '|' => VirtualKeyCode::Backslash,
'\\' | '|' => VirtualKeyCode::Backslash,
',' | '<' => VirtualKeyCode::Comma,
'/' | '?' => VirtualKeyCode::Slash,
'.' | '>' => VirtualKeyCode::Period,
@ -198,7 +200,6 @@ pub fn scancode_to_keycode(scancode: c_ushort) -> Option<VirtualKeyCode> {
0x7d => VirtualKeyCode::Down,
0x7e => VirtualKeyCode::Up,
//0x7f => unkown,
0xa => VirtualKeyCode::Caret,
_ => return None,
})
@ -215,16 +216,14 @@ pub fn check_function_keys(string: &String) -> Option<VirtualKeyCode> {
0xf71a => VirtualKeyCode::F23,
0xf71b => VirtualKeyCode::F24,
_ => return None,
})
});
}
None
}
pub fn event_mods(event: id) -> ModifiersState {
let flags = unsafe {
NSEvent::modifierFlags(event)
};
let flags = unsafe { NSEvent::modifierFlags(event) };
ModifiersState {
shift: flags.contains(NSEventModifierFlags::NSShiftKeyMask),
ctrl: flags.contains(NSEventModifierFlags::NSControlKeyMask),
@ -238,9 +237,7 @@ pub fn get_scancode(event: cocoa::base::id) -> c_ushort {
// and there is no easy way to navtively retrieve the layout-dependent character.
// In winit, we use keycode to refer to the key's character, and so this function aligns
// AppKit's terminology with ours.
unsafe {
msg_send![event, keyCode]
}
unsafe { msg_send![event, keyCode] }
}
pub unsafe fn modifier_event(
@ -249,7 +246,8 @@ pub unsafe fn modifier_event(
was_key_pressed: bool,
) -> Option<WindowEvent> {
if !was_key_pressed && NSEvent::modifierFlags(ns_event).contains(keymask)
|| was_key_pressed && !NSEvent::modifierFlags(ns_event).contains(keymask) {
|| was_key_pressed && !NSEvent::modifierFlags(ns_event).contains(keymask)
{
let state = if was_key_pressed {
ElementState::Released
} else {

View file

@ -1,17 +1,24 @@
use std::{
collections::VecDeque, mem, os::raw::c_void, process, ptr, sync::mpsc, marker::PhantomData
collections::VecDeque, marker::PhantomData, mem, os::raw::c_void, process, ptr, sync::mpsc,
};
use cocoa::{appkit::NSApp, base::{id, nil}, foundation::NSAutoreleasePool};
use cocoa::{
appkit::NSApp,
base::{id, nil},
foundation::NSAutoreleasePool,
};
use {
use crate::{
event::Event,
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootWindowTarget},
};
use platform_impl::platform::{
app::APP_CLASS, app_delegate::APP_DELEGATE_CLASS,
app_state::AppState, monitor::{self, MonitorHandle},
observer::*, util::IdRef,
platform_impl::platform::{
app::APP_CLASS,
app_delegate::APP_DELEGATE_CLASS,
app_state::AppState,
monitor::{self, MonitorHandle},
observer::*,
util::IdRef,
},
};
pub struct EventLoopWindowTarget<T: 'static> {
@ -75,7 +82,8 @@ impl<T> EventLoop<T> {
}
pub fn run<F>(self, callback: F) -> !
where F: 'static + FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
where
F: 'static + FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
{
unsafe {
let _pool = NSAutoreleasePool::new(nil);
@ -89,7 +97,8 @@ impl<T> EventLoop<T> {
}
pub fn run_return<F>(&mut self, _callback: F)
where F: FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
where
F: FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
{
unimplemented!();
}
@ -119,11 +128,8 @@ impl<T> Proxy<T> {
let rl = CFRunLoopGetMain();
let mut context: CFRunLoopSourceContext = mem::zeroed();
context.perform = event_loop_proxy_handler;
let source = CFRunLoopSourceCreate(
ptr::null_mut(),
CFIndex::max_value() - 1,
&mut context,
);
let source =
CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::max_value() - 1, &mut context);
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
CFRunLoopWakeUp(rl);

View file

@ -2,8 +2,10 @@
#![allow(dead_code, non_snake_case, non_upper_case_globals)]
use cocoa::base::id;
use cocoa::foundation::{NSInteger, NSUInteger};
use cocoa::{
base::id,
foundation::{NSInteger, NSUInteger},
};
use objc;
pub const NSNotFound: NSInteger = NSInteger::max_value();
@ -53,11 +55,11 @@ impl NSMutableAttributedString for id {
}
unsafe fn initWithString(self, string: id) -> id {
msg_send![self, initWithString:string]
msg_send![self, initWithString: string]
}
unsafe fn initWithAttributedString(self, string: id) -> id {
msg_send![self, initWithAttributedString:string]
msg_send![self, initWithAttributedString: string]
}
unsafe fn string(self) -> id {

View file

@ -15,16 +15,13 @@ mod window_delegate;
use std::{fmt, ops::Deref, sync::Arc};
use {
event::DeviceId as RootDeviceId, window::WindowAttributes,
error::OsError as RootOsError,
};
pub use self::{
event_loop::{EventLoop, EventLoopWindowTarget, Proxy as EventLoopProxy},
monitor::MonitorHandle,
window::{
Id as WindowId, PlatformSpecificWindowBuilderAttributes, UnownedWindow,
},
window::{Id as WindowId, PlatformSpecificWindowBuilderAttributes, UnownedWindow},
};
use crate::{
error::OsError as RootOsError, event::DeviceId as RootDeviceId, window::WindowAttributes,
};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -48,7 +45,7 @@ pub struct Window {
#[derive(Debug)]
pub enum OsError {
CGError(core_graphics::base::CGError),
CreationError(&'static str)
CreationError(&'static str),
}
unsafe impl Send for Window {}
@ -74,7 +71,7 @@ impl Window {
}
impl fmt::Display for OsError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
OsError::CGError(e) => f.pad(&format!("CGError {}", e)),
OsError::CreationError(e) => f.pad(e),

View file

@ -1,10 +1,21 @@
use std::{collections::VecDeque, fmt};
use cocoa::{appkit::NSScreen, base::{id, nil}, foundation::{NSString, NSUInteger}};
use core_graphics::display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds};
use cocoa::{
appkit::NSScreen,
base::{id, nil},
foundation::{NSString, NSUInteger},
};
use core_graphics::display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds, CGDisplayMode};
use core_video_sys::{
kCVReturnSuccess, kCVTimeIsIndefinite, CVDisplayLinkCreateWithCGDisplay,
CVDisplayLinkGetNominalOutputVideoRefreshPeriod, CVDisplayLinkRelease,
};
use dpi::{PhysicalPosition, PhysicalSize};
use platform_impl::platform::util::IdRef;
use crate::{
dpi::{PhysicalPosition, PhysicalSize},
monitor::VideoMode,
platform_impl::platform::util::IdRef,
};
#[derive(Clone, PartialEq)]
pub struct MonitorHandle(CGDirectDisplayID);
@ -26,13 +37,13 @@ pub fn primary_monitor() -> MonitorHandle {
}
impl fmt::Debug for MonitorHandle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// TODO: Do this using the proper fmt API
#[derive(Debug)]
struct MonitorHandle {
name: Option<String>,
native_identifier: u32,
dimensions: PhysicalSize,
size: PhysicalSize,
position: PhysicalPosition,
hidpi_factor: f64,
}
@ -40,7 +51,7 @@ impl fmt::Debug for MonitorHandle {
let monitor_id_proxy = MonitorHandle {
name: self.name(),
native_identifier: self.native_identifier(),
dimensions: self.dimensions(),
size: self.size(),
position: self.position(),
hidpi_factor: self.hidpi_factor(),
};
@ -65,15 +76,12 @@ impl MonitorHandle {
self.0
}
pub fn dimensions(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize {
let MonitorHandle(display_id) = *self;
let display = CGDisplay::new(display_id);
let height = display.pixels_high();
let width = display.pixels_wide();
PhysicalSize::from_logical(
(width as f64, height as f64),
self.hidpi_factor(),
)
PhysicalSize::from_logical((width as f64, height as f64), self.hidpi_factor())
}
#[inline]
@ -86,14 +94,52 @@ impl MonitorHandle {
}
pub fn hidpi_factor(&self) -> f64 {
let screen = match self.nsscreen() {
let screen = match self.ns_screen() {
Some(screen) => screen,
None => return 1.0, // default to 1.0 when we can't find the screen
};
unsafe { NSScreen::backingScaleFactor(screen) as f64 }
}
pub(crate) fn nsscreen(&self) -> Option<id> {
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
let cv_refresh_rate = unsafe {
let mut display_link = std::ptr::null_mut();
assert_eq!(
CVDisplayLinkCreateWithCGDisplay(self.0, &mut display_link),
kCVReturnSuccess
);
let time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(display_link);
CVDisplayLinkRelease(display_link);
// This value is indefinite if an invalid display link was specified
assert!(time.flags & kCVTimeIsIndefinite == 0);
time.timeScale as i64 / time.timeValue
};
CGDisplayMode::all_display_modes(self.0, std::ptr::null())
.expect("failed to obtain list of display modes")
.into_iter()
.map(move |mode| {
let cg_refresh_rate = mode.refresh_rate().round() as i64;
// CGDisplayModeGetRefreshRate returns 0.0 for any display that
// isn't a CRT
let refresh_rate = if cg_refresh_rate > 0 {
cg_refresh_rate
} else {
cv_refresh_rate
};
VideoMode {
size: (mode.width() as u32, mode.height() as u32),
refresh_rate: refresh_rate as u16,
bit_depth: mode.bit_depth() as u16,
}
})
}
pub(crate) fn ns_screen(&self) -> Option<id> {
unsafe {
let native_id = self.native_identifier();
let screens = NSScreen::screens(nil);

View file

@ -1,9 +1,9 @@
use std::{self, ptr, os::raw::*, time::Instant};
use std::{self, os::raw::*, ptr, time::Instant};
use platform_impl::platform::app_state::AppState;
use crate::platform_impl::platform::app_state::AppState;
#[link(name = "CoreFoundation", kind = "framework")]
extern {
extern "C" {
pub static kCFRunLoopDefaultMode: CFRunLoopMode;
pub static kCFRunLoopCommonModes: CFRunLoopMode;
@ -33,15 +33,8 @@ extern {
callout: CFRunLoopTimerCallBack,
context: *mut CFRunLoopTimerContext,
) -> CFRunLoopTimerRef;
pub fn CFRunLoopAddTimer(
rl: CFRunLoopRef,
timer: CFRunLoopTimerRef,
mode: CFRunLoopMode,
);
pub fn CFRunLoopTimerSetNextFireDate(
timer: CFRunLoopTimerRef,
fireDate: CFAbsoluteTime,
);
pub fn CFRunLoopAddTimer(rl: CFRunLoopRef, timer: CFRunLoopTimerRef, mode: CFRunLoopMode);
pub fn CFRunLoopTimerSetNextFireDate(timer: CFRunLoopTimerRef, fireDate: CFAbsoluteTime);
pub fn CFRunLoopTimerInvalidate(time: CFRunLoopTimerRef);
pub fn CFRunLoopSourceCreate(
@ -49,11 +42,8 @@ extern {
order: CFIndex,
context: *mut CFRunLoopSourceContext,
) -> CFRunLoopSourceRef;
pub fn CFRunLoopAddSource(
rl: CFRunLoopRef,
source: CFRunLoopSourceRef,
mode: CFRunLoopMode,
);
pub fn CFRunLoopAddSource(rl: CFRunLoopRef, source: CFRunLoopSourceRef, mode: CFRunLoopMode);
#[allow(dead_code)]
pub fn CFRunLoopSourceInvalidate(source: CFRunLoopSourceRef);
pub fn CFRunLoopSourceSignal(source: CFRunLoopSourceRef);
@ -62,6 +52,7 @@ extern {
}
pub type Boolean = u8;
#[allow(dead_code)]
const FALSE: Boolean = 0;
const TRUE: Boolean = 1;
@ -87,24 +78,23 @@ pub type CFRunLoopActivity = CFOptionFlags;
pub type CFAbsoluteTime = CFTimeInterval;
pub type CFTimeInterval = f64;
#[allow(non_upper_case_globals)]
pub const kCFRunLoopEntry: CFRunLoopActivity = 0;
#[allow(non_upper_case_globals)]
pub const kCFRunLoopBeforeWaiting: CFRunLoopActivity = 1 << 5;
#[allow(non_upper_case_globals)]
pub const kCFRunLoopAfterWaiting: CFRunLoopActivity = 1 << 6;
#[allow(non_upper_case_globals)]
pub const kCFRunLoopExit: CFRunLoopActivity = 1 << 7;
pub type CFRunLoopObserverCallBack = extern "C" fn(
observer: CFRunLoopObserverRef,
activity: CFRunLoopActivity,
info: *mut c_void,
);
pub type CFRunLoopTimerCallBack = extern "C" fn(
timer: CFRunLoopTimerRef,
info: *mut c_void
);
pub type CFRunLoopObserverCallBack =
extern "C" fn(observer: CFRunLoopObserverRef, activity: CFRunLoopActivity, info: *mut c_void);
pub type CFRunLoopTimerCallBack = extern "C" fn(timer: CFRunLoopTimerRef, info: *mut c_void);
pub enum CFRunLoopObserverContext {}
pub enum CFRunLoopTimerContext {}
#[allow(non_snake_case)]
#[repr(C)]
pub struct CFRunLoopSourceContext {
pub version: CFIndex,
@ -120,7 +110,7 @@ pub struct CFRunLoopSourceContext {
}
// begin is queued with the highest priority to ensure it is processed before other observers
extern fn control_flow_begin_handler(
extern "C" fn control_flow_begin_handler(
_: CFRunLoopObserverRef,
activity: CFRunLoopActivity,
_: *mut c_void,
@ -131,7 +121,7 @@ extern fn control_flow_begin_handler(
//trace!("Triggered `CFRunLoopAfterWaiting`");
AppState::wakeup();
//trace!("Completed `CFRunLoopAfterWaiting`");
},
}
kCFRunLoopEntry => unimplemented!(), // not expected to ever happen
_ => unreachable!(),
}
@ -139,7 +129,7 @@ extern fn control_flow_begin_handler(
// end is queued with the lowest priority to ensure it is processed after other observers
// without that, LoopDestroyed would get sent after EventsCleared
extern fn control_flow_end_handler(
extern "C" fn control_flow_end_handler(
_: CFRunLoopObserverRef,
activity: CFRunLoopActivity,
_: *mut c_void,
@ -150,8 +140,8 @@ extern fn control_flow_end_handler(
//trace!("Triggered `CFRunLoopBeforeWaiting`");
AppState::cleared();
//trace!("Completed `CFRunLoopBeforeWaiting`");
},
kCFRunLoopExit => (),//unimplemented!(), // not expected to ever happen
}
kCFRunLoopExit => (), //unimplemented!(), // not expected to ever happen
_ => unreachable!(),
}
}
@ -172,7 +162,7 @@ impl RunLoop {
let observer = CFRunLoopObserverCreate(
ptr::null_mut(),
flags,
TRUE, // Indicates we want this to run repeatedly
TRUE, // Indicates we want this to run repeatedly
priority, // The lower the value, the sooner this will run
handler,
ptr::null_mut(),
@ -197,7 +187,6 @@ pub fn setup_control_flow_observers() {
}
}
pub struct EventLoopWaker {
timer: CFRunLoopTimerRef,
}
@ -213,7 +202,7 @@ impl Drop for EventLoopWaker {
impl Default for EventLoopWaker {
fn default() -> EventLoopWaker {
extern fn wakeup_main_loop(_timer: CFRunLoopTimerRef, _info: *mut c_void) {}
extern "C" fn wakeup_main_loop(_timer: CFRunLoopTimerRef, _info: *mut c_void) {}
unsafe {
// create a timer with a 1µs interval (1ns does not work) to mimic polling.
// it is initially setup with a first fire time really far into the
@ -250,8 +239,8 @@ impl EventLoopWaker {
unsafe {
let current = CFAbsoluteTimeGetCurrent();
let duration = instant - now;
let fsecs = duration.subsec_nanos() as f64 / 1_000_000_000.0
+ duration.as_secs() as f64;
let fsecs =
duration.subsec_nanos() as f64 / 1_000_000_000.0 + duration.as_secs() as f64;
CFRunLoopTimerSetNextFireDate(self.timer, current + fsecs)
}
}

View file

@ -1,42 +1,47 @@
use std::{os::raw::c_void, sync::{Mutex, Weak}};
use std::{
os::raw::c_void,
sync::{Mutex, Weak},
};
use cocoa::{
appkit::{CGFloat, NSWindow, NSWindowStyleMask},
appkit::{CGFloat, NSScreen, NSWindow, NSWindowStyleMask},
base::{id, nil},
foundation::{NSAutoreleasePool, NSPoint, NSSize},
foundation::{NSAutoreleasePool, NSPoint, NSSize, NSString},
};
use dispatch::ffi::{dispatch_async_f, dispatch_get_main_queue, dispatch_sync_f};
use dpi::LogicalSize;
use platform_impl::platform::{ffi, window::SharedState};
use crate::{
dpi::LogicalSize,
platform_impl::platform::{ffi, util::IdRef, window::SharedState},
};
unsafe fn set_style_mask(nswindow: id, nsview: id, mask: NSWindowStyleMask) {
nswindow.setStyleMask_(mask);
unsafe fn set_style_mask(ns_window: id, ns_view: id, mask: NSWindowStyleMask) {
ns_window.setStyleMask_(mask);
// If we don't do this, key handling will break
// (at least until the window is clicked again/etc.)
nswindow.makeFirstResponder_(nsview);
ns_window.makeFirstResponder_(ns_view);
}
struct SetStyleMaskData {
nswindow: id,
nsview: id,
ns_window: id,
ns_view: id,
mask: NSWindowStyleMask,
}
impl SetStyleMaskData {
fn new_ptr(
nswindow: id,
nsview: id,
mask: NSWindowStyleMask,
) -> *mut Self {
Box::into_raw(Box::new(SetStyleMaskData { nswindow, nsview, mask }))
fn new_ptr(ns_window: id, ns_view: id, mask: NSWindowStyleMask) -> *mut Self {
Box::into_raw(Box::new(SetStyleMaskData {
ns_window,
ns_view,
mask,
}))
}
}
extern fn set_style_mask_callback(context: *mut c_void) {
extern "C" fn set_style_mask_callback(context: *mut c_void) {
unsafe {
let context_ptr = context as *mut SetStyleMaskData;
{
let context = &*context_ptr;
set_style_mask(context.nswindow, context.nsview, context.mask);
set_style_mask(context.ns_window, context.ns_view, context.mask);
}
Box::from_raw(context_ptr);
}
@ -45,16 +50,16 @@ extern fn set_style_mask_callback(context: *mut c_void) {
// `setStyleMask:` isn't thread-safe, so we have to use Grand Central Dispatch.
// Otherwise, this would vomit out errors about not being on the main thread
// and fail to do anything.
pub unsafe fn set_style_mask_async(nswindow: id, nsview: id, mask: NSWindowStyleMask) {
let context = SetStyleMaskData::new_ptr(nswindow, nsview, mask);
pub unsafe fn set_style_mask_async(ns_window: id, ns_view: id, mask: NSWindowStyleMask) {
let context = SetStyleMaskData::new_ptr(ns_window, ns_view, mask);
dispatch_async_f(
dispatch_get_main_queue(),
context as *mut _,
set_style_mask_callback,
);
}
pub unsafe fn set_style_mask_sync(nswindow: id, nsview: id, mask: NSWindowStyleMask) {
let context = SetStyleMaskData::new_ptr(nswindow, nsview, mask);
pub unsafe fn set_style_mask_sync(ns_window: id, ns_view: id, mask: NSWindowStyleMask) {
let context = SetStyleMaskData::new_ptr(ns_window, ns_view, mask);
dispatch_sync_f(
dispatch_get_main_queue(),
context as *mut _,
@ -63,24 +68,21 @@ pub unsafe fn set_style_mask_sync(nswindow: id, nsview: id, mask: NSWindowStyleM
}
struct SetContentSizeData {
nswindow: id,
ns_window: id,
size: LogicalSize,
}
impl SetContentSizeData {
fn new_ptr(
nswindow: id,
size: LogicalSize,
) -> *mut Self {
Box::into_raw(Box::new(SetContentSizeData { nswindow, size }))
fn new_ptr(ns_window: id, size: LogicalSize) -> *mut Self {
Box::into_raw(Box::new(SetContentSizeData { ns_window, size }))
}
}
extern fn set_content_size_callback(context: *mut c_void) {
extern "C" fn set_content_size_callback(context: *mut c_void) {
unsafe {
let context_ptr = context as *mut SetContentSizeData;
{
let context = &*context_ptr;
NSWindow::setContentSize_(
context.nswindow,
context.ns_window,
NSSize::new(
context.size.width as CGFloat,
context.size.height as CGFloat,
@ -92,8 +94,8 @@ extern fn set_content_size_callback(context: *mut c_void) {
}
// `setContentSize:` isn't thread-safe either, though it doesn't log any errors
// and just fails silently. Anyway, GCD to the rescue!
pub unsafe fn set_content_size_async(nswindow: id, size: LogicalSize) {
let context = SetContentSizeData::new_ptr(nswindow, size);
pub unsafe fn set_content_size_async(ns_window: id, size: LogicalSize) {
let context = SetContentSizeData::new_ptr(ns_window, size);
dispatch_async_f(
dispatch_get_main_queue(),
context as *mut _,
@ -102,31 +104,28 @@ pub unsafe fn set_content_size_async(nswindow: id, size: LogicalSize) {
}
struct SetFrameTopLeftPointData {
nswindow: id,
ns_window: id,
point: NSPoint,
}
impl SetFrameTopLeftPointData {
fn new_ptr(
nswindow: id,
point: NSPoint,
) -> *mut Self {
Box::into_raw(Box::new(SetFrameTopLeftPointData { nswindow, point }))
fn new_ptr(ns_window: id, point: NSPoint) -> *mut Self {
Box::into_raw(Box::new(SetFrameTopLeftPointData { ns_window, point }))
}
}
extern fn set_frame_top_left_point_callback(context: *mut c_void) {
extern "C" fn set_frame_top_left_point_callback(context: *mut c_void) {
unsafe {
let context_ptr = context as *mut SetFrameTopLeftPointData;
{
let context = &*context_ptr;
NSWindow::setFrameTopLeftPoint_(context.nswindow, context.point);
NSWindow::setFrameTopLeftPoint_(context.ns_window, context.point);
}
Box::from_raw(context_ptr);
}
}
// `setFrameTopLeftPoint:` isn't thread-safe, but fortunately has the courtesy
// to log errors.
pub unsafe fn set_frame_top_left_point_async(nswindow: id, point: NSPoint) {
let context = SetFrameTopLeftPointData::new_ptr(nswindow, point);
pub unsafe fn set_frame_top_left_point_async(ns_window: id, point: NSPoint) {
let context = SetFrameTopLeftPointData::new_ptr(ns_window, point);
dispatch_async_f(
dispatch_get_main_queue(),
context as *mut _,
@ -135,30 +134,27 @@ pub unsafe fn set_frame_top_left_point_async(nswindow: id, point: NSPoint) {
}
struct SetLevelData {
nswindow: id,
ns_window: id,
level: ffi::NSWindowLevel,
}
impl SetLevelData {
fn new_ptr(
nswindow: id,
level: ffi::NSWindowLevel,
) -> *mut Self {
Box::into_raw(Box::new(SetLevelData { nswindow, level }))
fn new_ptr(ns_window: id, level: ffi::NSWindowLevel) -> *mut Self {
Box::into_raw(Box::new(SetLevelData { ns_window, level }))
}
}
extern fn set_level_callback(context: *mut c_void) {
extern "C" fn set_level_callback(context: *mut c_void) {
unsafe {
let context_ptr = context as *mut SetLevelData;
{
let context = &*context_ptr;
context.nswindow.setLevel_(context.level as _);
context.ns_window.setLevel_(context.level as _);
}
Box::from_raw(context_ptr);
}
}
// `setFrameTopLeftPoint:` isn't thread-safe, and fails silently.
pub unsafe fn set_level_async(nswindow: id, level: ffi::NSWindowLevel) {
let context = SetLevelData::new_ptr(nswindow, level);
pub unsafe fn set_level_async(ns_window: id, level: ffi::NSWindowLevel) {
let context = SetLevelData::new_ptr(ns_window, level);
dispatch_async_f(
dispatch_get_main_queue(),
context as *mut _,
@ -167,27 +163,27 @@ pub unsafe fn set_level_async(nswindow: id, level: ffi::NSWindowLevel) {
}
struct ToggleFullScreenData {
nswindow: id,
nsview: id,
ns_window: id,
ns_view: id,
not_fullscreen: bool,
shared_state: Weak<Mutex<SharedState>>,
}
impl ToggleFullScreenData {
fn new_ptr(
nswindow: id,
nsview: id,
ns_window: id,
ns_view: id,
not_fullscreen: bool,
shared_state: Weak<Mutex<SharedState>>,
) -> *mut Self {
Box::into_raw(Box::new(ToggleFullScreenData {
nswindow,
nsview,
ns_window,
ns_view,
not_fullscreen,
shared_state,
}))
}
}
extern fn toggle_full_screen_callback(context: *mut c_void) {
extern "C" fn toggle_full_screen_callback(context: *mut c_void) {
unsafe {
let context_ptr = context as *mut ToggleFullScreenData;
{
@ -197,11 +193,11 @@ extern fn toggle_full_screen_callback(context: *mut c_void) {
// set a normal style temporarily. The previous state will be
// restored in `WindowDelegate::window_did_exit_fullscreen`.
if context.not_fullscreen {
let curr_mask = context.nswindow.styleMask();
let curr_mask = context.ns_window.styleMask();
let required = NSWindowStyleMask::NSTitledWindowMask
| NSWindowStyleMask::NSResizableWindowMask;
if !curr_mask.contains(required) {
set_style_mask(context.nswindow, context.nsview, required);
set_style_mask(context.ns_window, context.ns_view, required);
if let Some(shared_state) = context.shared_state.upgrade() {
trace!("Locked shared state in `toggle_full_screen_callback`");
let mut shared_state_lock = shared_state.lock().unwrap();
@ -211,7 +207,7 @@ extern fn toggle_full_screen_callback(context: *mut c_void) {
}
}
context.nswindow.toggleFullScreen_(nil);
context.ns_window.toggleFullScreen_(nil);
}
Box::from_raw(context_ptr);
}
@ -219,17 +215,12 @@ extern fn toggle_full_screen_callback(context: *mut c_void) {
// `toggleFullScreen` is thread-safe, but our additional logic to account for
// window styles isn't.
pub unsafe fn toggle_full_screen_async(
nswindow: id,
nsview: id,
ns_window: id,
ns_view: id,
not_fullscreen: bool,
shared_state: Weak<Mutex<SharedState>>,
) {
let context = ToggleFullScreenData::new_ptr(
nswindow,
nsview,
not_fullscreen,
shared_state,
);
let context = ToggleFullScreenData::new_ptr(ns_window, ns_view, not_fullscreen, shared_state);
dispatch_async_f(
dispatch_get_main_queue(),
context as *mut _,
@ -237,28 +228,105 @@ pub unsafe fn toggle_full_screen_async(
);
}
struct OrderOutData {
nswindow: id,
struct SetMaximizedData {
ns_window: id,
is_zoomed: bool,
maximized: bool,
shared_state: Weak<Mutex<SharedState>>,
}
impl OrderOutData {
fn new_ptr(nswindow: id) -> *mut Self {
Box::into_raw(Box::new(OrderOutData { nswindow }))
impl SetMaximizedData {
fn new_ptr(
ns_window: id,
is_zoomed: bool,
maximized: bool,
shared_state: Weak<Mutex<SharedState>>,
) -> *mut Self {
Box::into_raw(Box::new(SetMaximizedData {
ns_window,
is_zoomed,
maximized,
shared_state,
}))
}
}
extern fn order_out_callback(context: *mut c_void) {
extern "C" fn set_maximized_callback(context: *mut c_void) {
unsafe {
let context_ptr = context as *mut SetMaximizedData;
{
let context = &*context_ptr;
if let Some(shared_state) = context.shared_state.upgrade() {
trace!("Locked shared state in `set_maximized`");
let mut shared_state_lock = shared_state.lock().unwrap();
// Save the standard frame sized if it is not zoomed
if !context.is_zoomed {
shared_state_lock.standard_frame = Some(NSWindow::frame(context.ns_window));
}
shared_state_lock.maximized = context.maximized;
let curr_mask = context.ns_window.styleMask();
if shared_state_lock.fullscreen.is_some() {
// Handle it in window_did_exit_fullscreen
return;
} else if curr_mask.contains(NSWindowStyleMask::NSResizableWindowMask) {
// Just use the native zoom if resizable
context.ns_window.zoom_(nil);
} else {
// if it's not resizable, we set the frame directly
let new_rect = if context.maximized {
let screen = NSScreen::mainScreen(nil);
NSScreen::visibleFrame(screen)
} else {
shared_state_lock.saved_standard_frame()
};
context.ns_window.setFrame_display_(new_rect, 0);
}
trace!("Unlocked shared state in `set_maximized`");
}
}
Box::from_raw(context_ptr);
}
}
// `setMaximized` is not thread-safe
pub unsafe fn set_maximized_async(
ns_window: id,
is_zoomed: bool,
maximized: bool,
shared_state: Weak<Mutex<SharedState>>,
) {
let context = SetMaximizedData::new_ptr(ns_window, is_zoomed, maximized, shared_state);
dispatch_async_f(
dispatch_get_main_queue(),
context as *mut _,
set_maximized_callback,
);
}
struct OrderOutData {
ns_window: id,
}
impl OrderOutData {
fn new_ptr(ns_window: id) -> *mut Self {
Box::into_raw(Box::new(OrderOutData { ns_window }))
}
}
extern "C" fn order_out_callback(context: *mut c_void) {
unsafe {
let context_ptr = context as *mut OrderOutData;
{
let context = &*context_ptr;
context.nswindow.orderOut_(nil);
context.ns_window.orderOut_(nil);
}
Box::from_raw(context_ptr);
}
}
// `orderOut:` isn't thread-safe. Calling it from another thread actually works,
// but with an odd delay.
pub unsafe fn order_out_async(nswindow: id) {
let context = OrderOutData::new_ptr(nswindow);
pub unsafe fn order_out_async(ns_window: id) {
let context = OrderOutData::new_ptr(ns_window);
dispatch_async_f(
dispatch_get_main_queue(),
context as *mut _,
@ -267,27 +335,27 @@ pub unsafe fn order_out_async(nswindow: id) {
}
struct MakeKeyAndOrderFrontData {
nswindow: id,
ns_window: id,
}
impl MakeKeyAndOrderFrontData {
fn new_ptr(nswindow: id) -> *mut Self {
Box::into_raw(Box::new(MakeKeyAndOrderFrontData { nswindow }))
fn new_ptr(ns_window: id) -> *mut Self {
Box::into_raw(Box::new(MakeKeyAndOrderFrontData { ns_window }))
}
}
extern fn make_key_and_order_front_callback(context: *mut c_void) {
extern "C" fn make_key_and_order_front_callback(context: *mut c_void) {
unsafe {
let context_ptr = context as *mut MakeKeyAndOrderFrontData;
{
let context = &*context_ptr;
context.nswindow.makeKeyAndOrderFront_(nil);
context.ns_window.makeKeyAndOrderFront_(nil);
}
Box::from_raw(context_ptr);
}
}
// `makeKeyAndOrderFront:` isn't thread-safe. Calling it from another thread
// actually works, but with an odd delay.
pub unsafe fn make_key_and_order_front_async(nswindow: id) {
let context = MakeKeyAndOrderFrontData::new_ptr(nswindow);
pub unsafe fn make_key_and_order_front_async(ns_window: id) {
let context = MakeKeyAndOrderFrontData::new_ptr(ns_window);
dispatch_async_f(
dispatch_get_main_queue(),
context as *mut _,
@ -295,21 +363,53 @@ pub unsafe fn make_key_and_order_front_async(nswindow: id) {
);
}
struct CloseData {
nswindow: id,
struct SetTitleData {
ns_window: id,
title: String,
}
impl CloseData {
fn new_ptr(nswindow: id) -> *mut Self {
Box::into_raw(Box::new(CloseData { nswindow }))
impl SetTitleData {
fn new_ptr(ns_window: id, title: String) -> *mut Self {
Box::into_raw(Box::new(SetTitleData { ns_window, title }))
}
}
extern fn close_callback(context: *mut c_void) {
extern "C" fn set_title_callback(context: *mut c_void) {
unsafe {
let context_ptr = context as *mut SetTitleData;
{
let context = &*context_ptr;
let title = IdRef::new(NSString::alloc(nil).init_str(&context.title));
context.ns_window.setTitle_(*title);
}
Box::from_raw(context_ptr);
}
}
// `setTitle:` isn't thread-safe. Calling it from another thread invalidates the
// window drag regions, which throws an exception when not done in the main
// thread
pub unsafe fn set_title_async(ns_window: id, title: String) {
let context = SetTitleData::new_ptr(ns_window, title);
dispatch_async_f(
dispatch_get_main_queue(),
context as *mut _,
set_title_callback,
);
}
struct CloseData {
ns_window: id,
}
impl CloseData {
fn new_ptr(ns_window: id) -> *mut Self {
Box::into_raw(Box::new(CloseData { ns_window }))
}
}
extern "C" fn close_callback(context: *mut c_void) {
unsafe {
let context_ptr = context as *mut CloseData;
{
let context = &*context_ptr;
let pool = NSAutoreleasePool::new(nil);
context.nswindow.close();
context.ns_window.close();
pool.drain();
}
Box::from_raw(context_ptr);
@ -317,11 +417,7 @@ extern fn close_callback(context: *mut c_void) {
}
// `close:` is thread-safe, but we want the event to be triggered from the main
// thread. Though, it's a good idea to look into that more...
pub unsafe fn close_async(nswindow: id) {
let context = CloseData::new_ptr(nswindow);
dispatch_async_f(
dispatch_get_main_queue(),
context as *mut _,
close_callback,
);
pub unsafe fn close_async(ns_window: id) {
let context = CloseData::new_ptr(ns_window);
dispatch_async_f(dispatch_get_main_queue(), context as *mut _, close_callback);
}

View file

@ -1,10 +1,11 @@
use cocoa::{
appkit::NSImage, base::{id, nil},
appkit::NSImage,
base::{id, nil},
foundation::{NSDictionary, NSPoint, NSString},
};
use objc::runtime::Sel;
use window::CursorIcon;
use crate::window::CursorIcon;
pub enum Cursor {
Native(&'static str),
@ -22,7 +23,9 @@ impl From<CursorIcon> for Cursor {
CursorIcon::VerticalText => Cursor::Native("IBeamCursorForVerticalLayout"),
CursorIcon::Copy => Cursor::Native("dragCopyCursor"),
CursorIcon::Alias => Cursor::Native("dragLinkCursor"),
CursorIcon::NotAllowed | CursorIcon::NoDrop => Cursor::Native("operationNotAllowedCursor"),
CursorIcon::NotAllowed | CursorIcon::NoDrop => {
Cursor::Native("operationNotAllowedCursor")
}
CursorIcon::ContextMenu => Cursor::Native("contextualMenuCursor"),
CursorIcon::Crosshair => Cursor::Native("crosshairCursor"),
CursorIcon::EResize => Cursor::Native("resizeRightCursor"),
@ -52,7 +55,9 @@ impl From<CursorIcon> for Cursor {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=522349
// This is the wrong semantics for `Wait`, but it's the same as
// what's used in Safari and Chrome.
CursorIcon::Wait | CursorIcon::Progress => Cursor::Undocumented("busyButClickableCursor"),
CursorIcon::Wait | CursorIcon::Progress => {
Cursor::Undocumented("busyButClickableCursor")
}
// For the rest, we can just snatch the cursors from WebKit...
// They fit the style of the native cursors, and will seem
@ -75,19 +80,19 @@ impl Cursor {
match self {
Cursor::Native(cursor_name) => {
let sel = Sel::register(cursor_name);
msg_send![class!(NSCursor), performSelector:sel]
},
msg_send![class!(NSCursor), performSelector: sel]
}
Cursor::Undocumented(cursor_name) => {
let class = class!(NSCursor);
let sel = Sel::register(cursor_name);
let sel = if msg_send![class, respondsToSelector:sel] {
let sel = if msg_send![class, respondsToSelector: sel] {
sel
} else {
warn!("Cursor `{}` appears to be invalid", cursor_name);
sel!(arrowCursor)
};
msg_send![class, performSelector:sel]
},
msg_send![class, performSelector: sel]
}
Cursor::WebKit(cursor_name) => load_webkit_cursor(cursor_name),
}
}
@ -104,27 +109,15 @@ pub unsafe fn load_webkit_cursor(cursor_name: &str) -> id {
let key_x = NSString::alloc(nil).init_str("hotx");
let key_y = NSString::alloc(nil).init_str("hoty");
let cursor_path: id = msg_send![cursor_root,
stringByAppendingPathComponent:cursor_name
];
let pdf_path: id = msg_send![cursor_path,
stringByAppendingPathComponent:cursor_pdf
];
let info_path: id = msg_send![cursor_path,
stringByAppendingPathComponent:cursor_plist
];
let cursor_path: id = msg_send![cursor_root, stringByAppendingPathComponent: cursor_name];
let pdf_path: id = msg_send![cursor_path, stringByAppendingPathComponent: cursor_pdf];
let info_path: id = msg_send![cursor_path, stringByAppendingPathComponent: cursor_plist];
let image = NSImage::alloc(nil).initByReferencingFile_(pdf_path);
let info = NSDictionary::dictionaryWithContentsOfFile_(
nil,
info_path,
);
let info = NSDictionary::dictionaryWithContentsOfFile_(nil, info_path);
let x = info.valueForKey_(key_x);
let y = info.valueForKey_(key_y);
let point = NSPoint::new(
msg_send![x, doubleValue],
msg_send![y, doubleValue],
);
let point = NSPoint::new(msg_send![x, doubleValue], msg_send![y, doubleValue]);
let cursor: id = msg_send![class!(NSCursor), alloc];
msg_send![cursor,
initWithImage:image

View file

@ -1,14 +0,0 @@
use cocoa::base::{id, nil};
pub trait IntoOption: Sized {
fn into_option(self) -> Option<Self>;
}
impl IntoOption for id {
fn into_option(self) -> Option<Self> {
match self != nil {
true => Some(self),
false => None,
}
}
}

View file

@ -1,10 +1,9 @@
mod async;
mod r#async;
mod cursor;
pub use self::{async::*, cursor::*};
pub use self::{cursor::*, r#async::*};
use std::ops::Deref;
use std::ops::BitAnd;
use std::ops::{BitAnd, Deref};
use cocoa::{
appkit::{NSApp, NSWindowStyleMask},
@ -12,17 +11,17 @@ use cocoa::{
foundation::{NSAutoreleasePool, NSRect, NSUInteger},
};
use core_graphics::display::CGDisplay;
use objc::runtime::{BOOL, Class, Object, Sel, YES};
use objc::runtime::{Class, Object, Sel, BOOL, YES};
use platform_impl::platform::ffi;
use crate::platform_impl::platform::ffi;
// Replace with `!` once stable
#[derive(Debug)]
pub enum Never {}
pub fn has_flag<T>(bitset: T, flag: T) -> bool
where T:
Copy + PartialEq + BitAnd<T, Output = T>
where
T: Copy + PartialEq + BitAnd<T, Output = T>,
{
bitset & flag == flag
}
@ -48,7 +47,11 @@ impl IdRef {
}
pub fn non_nil(self) -> Option<IdRef> {
if self.0 == nil { None } else { Some(self) }
if self.0 == nil {
None
} else {
Some(self)
}
}
}
@ -94,16 +97,16 @@ pub unsafe fn superclass<'a>(this: &'a Object) -> &'a Class {
pub unsafe fn create_input_context(view: id) -> IdRef {
let input_context: id = msg_send![class!(NSTextInputContext), alloc];
let input_context: id = msg_send![input_context, initWithClient:view];
let input_context: id = msg_send![input_context, initWithClient: view];
IdRef::new(input_context)
}
#[allow(dead_code)]
pub unsafe fn open_emoji_picker() {
let () = msg_send![NSApp(), orderFrontCharacterPalette:nil];
let () = msg_send![NSApp(), orderFrontCharacterPalette: nil];
}
pub extern fn yes(_: &Object, _: Sel) -> BOOL {
pub extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
YES
}
@ -120,4 +123,3 @@ pub unsafe fn toggle_style_mask(window: id, view: id, mask: NSWindowStyleMask, o
// If we don't do this, key handling will break. Therefore, never call `setStyleMask` directly!
window.makeFirstResponder_(view);
}

View file

@ -1,26 +1,39 @@
use std::{
boxed::Box, collections::VecDeque, os::raw::*, slice, str,
boxed::Box,
collections::VecDeque,
os::raw::*,
slice, str,
sync::{Arc, Mutex, Weak},
};
use cocoa::{
appkit::{NSApp, NSEvent, NSEventModifierFlags, NSEventPhase, NSView, NSWindow},
base::{id, nil}, foundation::{NSPoint, NSRect, NSSize, NSString, NSUInteger},
base::{id, nil},
foundation::{NSPoint, NSRect, NSSize, NSString, NSUInteger},
};
use objc::{
declare::ClassDecl,
runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES},
};
use objc::{declare::ClassDecl, runtime::{BOOL, Class, NO, Object, Protocol, Sel, YES}};
use {
use crate::{
event::{
DeviceEvent, ElementState, Event, KeyboardInput, MouseButton,
MouseScrollDelta, TouchPhase, VirtualKeyCode, WindowEvent,
DeviceEvent, ElementState, Event, KeyboardInput, MouseButton, MouseScrollDelta, TouchPhase,
VirtualKeyCode, WindowEvent,
},
platform_impl::platform::{
app_state::AppState,
event::{
char_to_keycode, check_function_keys, event_mods, get_scancode, modifier_event,
scancode_to_keycode,
},
ffi::*,
util::{self, IdRef},
window::get_window_id,
DEVICE_ID,
},
window::WindowId,
};
use platform_impl::platform::{
app_state::AppState, DEVICE_ID,
event::{check_function_keys, event_mods, modifier_event, char_to_keycode, get_scancode, scancode_to_keycode},
util::{self, IdRef}, ffi::*, window::get_window_id,
};
#[derive(Default)]
struct Modifiers {
@ -31,7 +44,7 @@ struct Modifiers {
}
struct ViewState {
nswindow: id,
ns_window: id,
pub cursor: Arc<Mutex<util::Cursor>>,
ime_spot: Option<(f64, f64)>,
raw_characters: Option<String>,
@ -39,11 +52,11 @@ struct ViewState {
modifiers: Modifiers,
}
pub fn new_view(nswindow: id) -> (IdRef, Weak<Mutex<util::Cursor>>) {
pub fn new_view(ns_window: id) -> (IdRef, Weak<Mutex<util::Cursor>>) {
let cursor = Default::default();
let cursor_access = Arc::downgrade(&cursor);
let state = ViewState {
nswindow,
ns_window,
cursor,
ime_spot: None,
raw_characters: None,
@ -53,18 +66,19 @@ pub fn new_view(nswindow: id) -> (IdRef, Weak<Mutex<util::Cursor>>) {
unsafe {
// This is free'd in `dealloc`
let state_ptr = Box::into_raw(Box::new(state)) as *mut c_void;
let nsview: id = msg_send![VIEW_CLASS.0, alloc];
(IdRef::new(msg_send![nsview, initWithWinit:state_ptr]), cursor_access)
let ns_view: id = msg_send![VIEW_CLASS.0, alloc];
(
IdRef::new(msg_send![ns_view, initWithWinit: state_ptr]),
cursor_access,
)
}
}
pub unsafe fn set_ime_position(nsview: id, input_context: id, x: f64, y: f64) {
let state_ptr: *mut c_void = *(*nsview).get_mut_ivar("winitState");
pub unsafe fn set_ime_position(ns_view: id, input_context: id, x: f64, y: f64) {
let state_ptr: *mut c_void = *(*ns_view).get_mut_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
let content_rect = NSWindow::contentRectForFrameRect_(
state.nswindow,
NSWindow::frame(state.nswindow),
);
let content_rect =
NSWindow::contentRectForFrameRect_(state.ns_window, NSWindow::frame(state.ns_window));
let base_x = content_rect.origin.x as f64;
let base_y = (content_rect.origin.y + content_rect.size.height) as f64;
state.ime_spot = Some((base_x + x, base_y - y));
@ -79,161 +93,148 @@ lazy_static! {
static ref VIEW_CLASS: ViewClass = unsafe {
let superclass = class!(NSView);
let mut decl = ClassDecl::new("WinitView", superclass).unwrap();
decl.add_method(
sel!(dealloc),
dealloc as extern fn(&Object, Sel),
);
decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel));
decl.add_method(
sel!(initWithWinit:),
init_with_winit as extern fn(&Object, Sel, *mut c_void) -> id,
init_with_winit as extern "C" fn(&Object, Sel, *mut c_void) -> id,
);
decl.add_method(
sel!(viewDidMoveToWindow),
view_did_move_to_window as extern fn(&Object, Sel),
view_did_move_to_window as extern "C" fn(&Object, Sel),
);
decl.add_method(
sel!(drawRect:),
draw_rect as extern fn(&Object, Sel, id),
draw_rect as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(acceptsFirstResponder),
accepts_first_responder as extern fn(&Object, Sel) -> BOOL,
accepts_first_responder as extern "C" fn(&Object, Sel) -> BOOL,
);
decl.add_method(
sel!(touchBar),
touch_bar as extern fn(&Object, Sel) -> BOOL,
touch_bar as extern "C" fn(&Object, Sel) -> BOOL,
);
decl.add_method(
sel!(resetCursorRects),
reset_cursor_rects as extern fn(&Object, Sel),
reset_cursor_rects as extern "C" fn(&Object, Sel),
);
decl.add_method(
sel!(hasMarkedText),
has_marked_text as extern fn(&Object, Sel) -> BOOL,
has_marked_text as extern "C" fn(&Object, Sel) -> BOOL,
);
decl.add_method(
sel!(markedRange),
marked_range as extern fn(&Object, Sel) -> NSRange,
marked_range as extern "C" fn(&Object, Sel) -> NSRange,
);
decl.add_method(
sel!(selectedRange),
selected_range as extern fn(&Object, Sel) -> NSRange,
selected_range as extern "C" fn(&Object, Sel) -> NSRange,
);
decl.add_method(
sel!(setMarkedText:selectedRange:replacementRange:),
set_marked_text as extern fn(&mut Object, Sel, id, NSRange, NSRange),
);
decl.add_method(
sel!(unmarkText),
unmark_text as extern fn(&Object, Sel),
set_marked_text as extern "C" fn(&mut Object, Sel, id, NSRange, NSRange),
);
decl.add_method(sel!(unmarkText), unmark_text as extern "C" fn(&Object, Sel));
decl.add_method(
sel!(validAttributesForMarkedText),
valid_attributes_for_marked_text as extern fn(&Object, Sel) -> id,
valid_attributes_for_marked_text as extern "C" fn(&Object, Sel) -> id,
);
decl.add_method(
sel!(attributedSubstringForProposedRange:actualRange:),
attributed_substring_for_proposed_range as extern fn(&Object, Sel, NSRange, *mut c_void) -> id,
attributed_substring_for_proposed_range
as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> id,
);
decl.add_method(
sel!(insertText:replacementRange:),
insert_text as extern fn(&Object, Sel, id, NSRange),
insert_text as extern "C" fn(&Object, Sel, id, NSRange),
);
decl.add_method(
sel!(characterIndexForPoint:),
character_index_for_point as extern fn(&Object, Sel, NSPoint) -> NSUInteger,
character_index_for_point as extern "C" fn(&Object, Sel, NSPoint) -> NSUInteger,
);
decl.add_method(
sel!(firstRectForCharacterRange:actualRange:),
first_rect_for_character_range as extern fn(&Object, Sel, NSRange, *mut c_void) -> NSRect,
first_rect_for_character_range
as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> NSRect,
);
decl.add_method(
sel!(doCommandBySelector:),
do_command_by_selector as extern fn(&Object, Sel, Sel),
);
decl.add_method(
sel!(keyDown:),
key_down as extern fn(&Object, Sel, id),
);
decl.add_method(
sel!(keyUp:),
key_up as extern fn(&Object, Sel, id),
do_command_by_selector as extern "C" fn(&Object, Sel, Sel),
);
decl.add_method(sel!(keyDown:), key_down as extern "C" fn(&Object, Sel, id));
decl.add_method(sel!(keyUp:), key_up as extern "C" fn(&Object, Sel, id));
decl.add_method(
sel!(flagsChanged:),
flags_changed as extern fn(&Object, Sel, id),
flags_changed as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(insertTab:),
insert_tab as extern fn(&Object, Sel, id),
insert_tab as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(insertBackTab:),
insert_back_tab as extern fn(&Object, Sel, id),
insert_back_tab as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(mouseDown:),
mouse_down as extern fn(&Object, Sel, id),
);
decl.add_method(
sel!(mouseUp:),
mouse_up as extern fn(&Object, Sel, id),
mouse_down as extern "C" fn(&Object, Sel, id),
);
decl.add_method(sel!(mouseUp:), mouse_up as extern "C" fn(&Object, Sel, id));
decl.add_method(
sel!(rightMouseDown:),
right_mouse_down as extern fn(&Object, Sel, id),
right_mouse_down as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(rightMouseUp:),
right_mouse_up as extern fn(&Object, Sel, id),
right_mouse_up as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(otherMouseDown:),
other_mouse_down as extern fn(&Object, Sel, id),
other_mouse_down as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(otherMouseUp:),
other_mouse_up as extern fn(&Object, Sel, id),
other_mouse_up as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(mouseMoved:),
mouse_moved as extern fn(&Object, Sel, id),
mouse_moved as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(mouseDragged:),
mouse_dragged as extern fn(&Object, Sel, id),
mouse_dragged as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(rightMouseDragged:),
right_mouse_dragged as extern fn(&Object, Sel, id),
right_mouse_dragged as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(otherMouseDragged:),
other_mouse_dragged as extern fn(&Object, Sel, id),
other_mouse_dragged as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(mouseEntered:),
mouse_entered as extern fn(&Object, Sel, id),
mouse_entered as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(mouseExited:),
mouse_exited as extern fn(&Object, Sel, id),
mouse_exited as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(scrollWheel:),
scroll_wheel as extern fn(&Object, Sel, id),
scroll_wheel as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(pressureChangeWithEvent:),
pressure_change_with_event as extern fn(&Object, Sel, id),
pressure_change_with_event as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(_wantsKeyDownForEvent:),
wants_key_down_for_event as extern fn(&Object, Sel, id) -> BOOL,
wants_key_down_for_event as extern "C" fn(&Object, Sel, id) -> BOOL,
);
decl.add_method(
sel!(cancelOperation:),
cancel_operation as extern fn(&Object, Sel, id),
cancel_operation as extern "C" fn(&Object, Sel, id),
);
decl.add_ivar::<*mut c_void>("winitState");
decl.add_ivar::<id>("markedText");
@ -243,7 +244,7 @@ lazy_static! {
};
}
extern fn dealloc(this: &Object, _sel: Sel) {
extern "C" fn dealloc(this: &Object, _sel: Sel) {
unsafe {
let state: *mut c_void = *this.get_ivar("winitState");
let marked_text: id = *this.get_ivar("markedText");
@ -252,21 +253,20 @@ extern fn dealloc(this: &Object, _sel: Sel) {
}
}
extern fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> id {
extern "C" fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> id {
unsafe {
let this: id = msg_send![this, init];
if this != nil {
(*this).set_ivar("winitState", state);
let marked_text = <id as NSMutableAttributedString>::init(
NSMutableAttributedString::alloc(nil),
);
let marked_text =
<id as NSMutableAttributedString>::init(NSMutableAttributedString::alloc(nil));
(*this).set_ivar("markedText", marked_text);
}
this
}
}
extern fn view_did_move_to_window(this: &Object, _sel: Sel) {
extern "C" fn view_did_move_to_window(this: &Object, _sel: Sel) {
trace!("Triggered `viewDidMoveToWindow`");
unsafe {
let rect: NSRect = msg_send![this, visibleRect];
@ -280,30 +280,30 @@ extern fn view_did_move_to_window(this: &Object, _sel: Sel) {
trace!("Completed `viewDidMoveToWindow`");
}
extern fn draw_rect(this: &Object, _sel: Sel, rect: id) {
extern "C" fn draw_rect(this: &Object, _sel: Sel, rect: id) {
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
AppState::queue_redraw(WindowId(get_window_id(state.nswindow)));
AppState::queue_redraw(WindowId(get_window_id(state.ns_window)));
let superclass = util::superclass(this);
let () = msg_send![super(this, superclass), drawRect:rect];
let () = msg_send![super(this, superclass), drawRect: rect];
}
}
extern fn accepts_first_responder(_this: &Object, _sel: Sel) -> BOOL {
extern "C" fn accepts_first_responder(_this: &Object, _sel: Sel) -> BOOL {
YES
}
// This is necessary to prevent a beefy terminal error on MacBook Pros:
// IMKInputSession [0x7fc573576ff0 presentFunctionRowItemTextInputViewWithEndpoint:completionHandler:] : [self textInputContext]=0x7fc573558e10 *NO* NSRemoteViewController to client, NSError=Error Domain=NSCocoaErrorDomain Code=4099 "The connection from pid 0 was invalidated from this process." UserInfo={NSDebugDescription=The connection from pid 0 was invalidated from this process.}, com.apple.inputmethod.EmojiFunctionRowItem
// TODO: Add an API extension for using `NSTouchBar`
extern fn touch_bar(_this: &Object, _sel: Sel) -> BOOL {
extern "C" fn touch_bar(_this: &Object, _sel: Sel) -> BOOL {
NO
}
extern fn reset_cursor_rects(this: &Object, _sel: Sel) {
extern "C" fn reset_cursor_rects(this: &Object, _sel: Sel) {
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
@ -317,8 +317,7 @@ extern fn reset_cursor_rects(this: &Object, _sel: Sel) {
}
}
extern fn has_marked_text(this: &Object, _sel: Sel) -> BOOL {
extern "C" fn has_marked_text(this: &Object, _sel: Sel) -> BOOL {
unsafe {
trace!("Triggered `hasMarkedText`");
let marked_text: id = *this.get_ivar("markedText");
@ -327,7 +326,7 @@ extern fn has_marked_text(this: &Object, _sel: Sel) -> BOOL {
}
}
extern fn marked_range(this: &Object, _sel: Sel) -> NSRange {
extern "C" fn marked_range(this: &Object, _sel: Sel) -> NSRange {
unsafe {
trace!("Triggered `markedRange`");
let marked_text: id = *this.get_ivar("markedText");
@ -341,13 +340,13 @@ extern fn marked_range(this: &Object, _sel: Sel) -> NSRange {
}
}
extern fn selected_range(_this: &Object, _sel: Sel) -> NSRange {
extern "C" fn selected_range(_this: &Object, _sel: Sel) -> NSRange {
trace!("Triggered `selectedRange`");
trace!("Completed `selectedRange`");
util::EMPTY_RANGE
}
extern fn set_marked_text(
extern "C" fn set_marked_text(
this: &mut Object,
_sel: Sel,
string: id,
@ -359,7 +358,7 @@ extern fn set_marked_text(
let marked_text_ref: &mut id = this.get_mut_ivar("markedText");
let _: () = msg_send![(*marked_text_ref), release];
let marked_text = NSMutableAttributedString::alloc(nil);
let has_attr = msg_send![string, isKindOfClass:class!(NSAttributedString)];
let has_attr = msg_send![string, isKindOfClass: class!(NSAttributedString)];
if has_attr {
marked_text.initWithAttributedString(string);
} else {
@ -370,7 +369,7 @@ extern fn set_marked_text(
trace!("Completed `setMarkedText`");
}
extern fn unmark_text(this: &Object, _sel: Sel) {
extern "C" fn unmark_text(this: &Object, _sel: Sel) {
trace!("Triggered `unmarkText`");
unsafe {
let marked_text: id = *this.get_ivar("markedText");
@ -382,13 +381,13 @@ extern fn unmark_text(this: &Object, _sel: Sel) {
trace!("Completed `unmarkText`");
}
extern fn valid_attributes_for_marked_text(_this: &Object, _sel: Sel) -> id {
extern "C" fn valid_attributes_for_marked_text(_this: &Object, _sel: Sel) -> id {
trace!("Triggered `validAttributesForMarkedText`");
trace!("Completed `validAttributesForMarkedText`");
unsafe { msg_send![class!(NSArray), array] }
}
extern fn attributed_substring_for_proposed_range(
extern "C" fn attributed_substring_for_proposed_range(
_this: &Object,
_sel: Sel,
_range: NSRange,
@ -399,13 +398,13 @@ extern fn attributed_substring_for_proposed_range(
nil
}
extern fn character_index_for_point(_this: &Object, _sel: Sel, _point: NSPoint) -> NSUInteger {
extern "C" fn character_index_for_point(_this: &Object, _sel: Sel, _point: NSPoint) -> NSUInteger {
trace!("Triggered `characterIndexForPoint`");
trace!("Completed `characterIndexForPoint`");
0
}
extern fn first_rect_for_character_range(
extern "C" fn first_rect_for_character_range(
this: &Object,
_sel: Sel,
_range: NSRange,
@ -417,28 +416,25 @@ extern fn first_rect_for_character_range(
let state = &mut *(state_ptr as *mut ViewState);
let (x, y) = state.ime_spot.unwrap_or_else(|| {
let content_rect = NSWindow::contentRectForFrameRect_(
state.nswindow,
NSWindow::frame(state.nswindow),
state.ns_window,
NSWindow::frame(state.ns_window),
);
let x = content_rect.origin.x;
let y = util::bottom_left_to_top_left(content_rect);
(x, y)
});
trace!("Completed `firstRectForCharacterRange`");
NSRect::new(
NSPoint::new(x as _, y as _),
NSSize::new(0.0, 0.0),
)
NSRect::new(NSPoint::new(x as _, y as _), NSSize::new(0.0, 0.0))
}
}
extern fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range: NSRange) {
extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range: NSRange) {
trace!("Triggered `insertText`");
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
let has_attr = msg_send![string, isKindOfClass:class!(NSAttributedString)];
let has_attr = msg_send![string, isKindOfClass: class!(NSAttributedString)];
let characters = if has_attr {
// This is a *mut NSAttributedString
msg_send![string, string]
@ -447,10 +443,8 @@ extern fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range:
string
};
let slice = slice::from_raw_parts(
characters.UTF8String() as *const c_uchar,
characters.len(),
);
let slice =
slice::from_raw_parts(characters.UTF8String() as *const c_uchar, characters.len());
let string = str::from_utf8_unchecked(slice);
state.is_key_down = true;
@ -460,7 +454,7 @@ extern fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range:
let mut events = VecDeque::with_capacity(characters.len());
for character in string.chars() {
events.push_back(Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)),
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::ReceivedCharacter(character),
});
}
@ -470,7 +464,7 @@ extern fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range:
trace!("Completed `insertText`");
}
extern fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
extern "C" fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
trace!("Triggered `doCommandBySelector`");
// Basically, we're sent this message whenever a keyboard event that doesn't generate a "human readable" character
// happens, i.e. newlines, tabs, and Ctrl+C.
@ -484,7 +478,7 @@ extern fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
// 1) as a reminder for how `doCommandBySelector` works
// 2) to make our use of carriage return explicit
events.push_back(Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)),
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::ReceivedCharacter('\r'),
});
} else {
@ -492,7 +486,7 @@ extern fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
if let Some(raw_characters) = raw_characters {
for character in raw_characters.chars() {
events.push_back(Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)),
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::ReceivedCharacter(character),
});
}
@ -513,10 +507,8 @@ fn get_characters(event: id, ignore_modifiers: bool) -> String {
};
assert_ne!(characters, nil);
let slice = slice::from_raw_parts(
characters.UTF8String() as *const c_uchar,
characters.len(),
);
let slice =
slice::from_raw_parts(characters.UTF8String() as *const c_uchar, characters.len());
let string = str::from_utf8_unchecked(slice);
string.to_owned()
@ -528,15 +520,15 @@ fn retrieve_keycode(event: id) -> Option<VirtualKeyCode> {
#[inline]
fn get_code(ev: id, raw: bool) -> Option<VirtualKeyCode> {
let characters = get_characters(ev, raw);
characters.chars().next().map_or(None, |c| char_to_keycode(c))
characters
.chars()
.next()
.map_or(None, |c| char_to_keycode(c))
}
// Cmd switches Roman letters for Dvorak-QWERTY layout, so we try modified characters first.
// If we don't get a match, then we fall back to unmodified characters.
let code = get_code(event, false)
.or_else(|| {
get_code(event, true)
});
let code = get_code(event, false).or_else(|| get_code(event, true));
// We've checked all layout related keys, so fall through to scancode.
// Reaching this code means that the key is layout-independent (e.g. Backspace, Return).
@ -546,19 +538,16 @@ fn retrieve_keycode(event: id) -> Option<VirtualKeyCode> {
// in characters property.
code.or_else(|| {
let scancode = get_scancode(event);
scancode_to_keycode(scancode)
.or_else(|| {
check_function_keys(&get_characters(event, true))
})
scancode_to_keycode(scancode).or_else(|| check_function_keys(&get_characters(event, true)))
})
}
extern fn key_down(this: &Object, _sel: Sel, event: id) {
extern "C" fn key_down(this: &Object, _sel: Sel, event: id) {
trace!("Triggered `keyDown`");
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
let window_id = WindowId(get_window_id(state.nswindow));
let window_id = WindowId(get_window_id(state.ns_window));
let characters = get_characters(event, false);
state.raw_characters = Some(characters.clone());
@ -601,14 +590,14 @@ extern fn key_down(this: &Object, _sel: Sel, event: id) {
// Some keys (and only *some*, with no known reason) don't trigger `insertText`, while others do...
// So, we don't give repeats the opportunity to trigger that, since otherwise our hack will cause some
// keys to generate twice as many characters.
let array: id = msg_send![class!(NSArray), arrayWithObject:event];
let _: () = msg_send![this, interpretKeyEvents:array];
let array: id = msg_send![class!(NSArray), arrayWithObject: event];
let _: () = msg_send![this, interpretKeyEvents: array];
}
}
trace!("Completed `keyDown`");
}
extern fn key_up(this: &Object, _sel: Sel, event: id) {
extern "C" fn key_up(this: &Object, _sel: Sel, event: id) {
trace!("Triggered `keyUp`");
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
@ -620,7 +609,7 @@ extern fn key_up(this: &Object, _sel: Sel, event: id) {
let virtual_keycode = retrieve_keycode(event);
let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)),
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
@ -637,7 +626,7 @@ extern fn key_up(this: &Object, _sel: Sel, event: id) {
trace!("Completed `keyUp`");
}
extern fn flags_changed(this: &Object, _sel: Sel, event: id) {
extern "C" fn flags_changed(this: &Object, _sel: Sel, event: id) {
trace!("Triggered `flagsChanged`");
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
@ -683,7 +672,7 @@ extern fn flags_changed(this: &Object, _sel: Sel, event: id) {
for event in events {
AppState::queue_event(Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)),
window_id: WindowId(get_window_id(state.ns_window)),
event,
});
}
@ -691,31 +680,31 @@ extern fn flags_changed(this: &Object, _sel: Sel, event: id) {
trace!("Completed `flagsChanged`");
}
extern fn insert_tab(this: &Object, _sel: Sel, _sender: id) {
extern "C" fn insert_tab(this: &Object, _sel: Sel, _sender: id) {
unsafe {
let window: id = msg_send![this, window];
let first_responder: id = msg_send![window, firstResponder];
let this_ptr = this as *const _ as *mut _;
if first_responder == this_ptr {
let (): _ = msg_send![window, selectNextKeyView:this];
let (): _ = msg_send![window, selectNextKeyView: this];
}
}
}
extern fn insert_back_tab(this: &Object, _sel: Sel, _sender: id) {
extern "C" fn insert_back_tab(this: &Object, _sel: Sel, _sender: id) {
unsafe {
let window: id = msg_send![this, window];
let first_responder: id = msg_send![window, firstResponder];
let this_ptr = this as *const _ as *mut _;
if first_responder == this_ptr {
let (): _ = msg_send![window, selectPreviousKeyView:this];
let (): _ = msg_send![window, selectPreviousKeyView: this];
}
}
}
// Allows us to receive Cmd-. (the shortcut for closing a dialog)
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6
extern fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
trace!("Triggered `cancelOperation`");
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
@ -728,7 +717,7 @@ extern fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
let event: id = msg_send![NSApp(), currentEvent];
let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)),
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
input: KeyboardInput {
@ -751,7 +740,7 @@ fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: Elem
let state = &mut *(state_ptr as *mut ViewState);
let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)),
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::MouseInput {
device_id: DEVICE_ID,
state: button_state,
@ -764,27 +753,27 @@ fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: Elem
}
}
extern fn mouse_down(this: &Object, _sel: Sel, event: id) {
extern "C" fn mouse_down(this: &Object, _sel: Sel, event: id) {
mouse_click(this, event, MouseButton::Left, ElementState::Pressed);
}
extern fn mouse_up(this: &Object, _sel: Sel, event: id) {
extern "C" fn mouse_up(this: &Object, _sel: Sel, event: id) {
mouse_click(this, event, MouseButton::Left, ElementState::Released);
}
extern fn right_mouse_down(this: &Object, _sel: Sel, event: id) {
extern "C" fn right_mouse_down(this: &Object, _sel: Sel, event: id) {
mouse_click(this, event, MouseButton::Right, ElementState::Pressed);
}
extern fn right_mouse_up(this: &Object, _sel: Sel, event: id) {
extern "C" fn right_mouse_up(this: &Object, _sel: Sel, event: id) {
mouse_click(this, event, MouseButton::Right, ElementState::Released);
}
extern fn other_mouse_down(this: &Object, _sel: Sel, event: id) {
extern "C" fn other_mouse_down(this: &Object, _sel: Sel, event: id) {
mouse_click(this, event, MouseButton::Middle, ElementState::Pressed);
}
extern fn other_mouse_up(this: &Object, _sel: Sel, event: id) {
extern "C" fn other_mouse_up(this: &Object, _sel: Sel, event: id) {
mouse_click(this, event, MouseButton::Middle, ElementState::Released);
}
@ -801,9 +790,10 @@ fn mouse_motion(this: &Object, event: id) {
let view_rect = NSView::frame(view);
if view_point.x.is_sign_negative()
|| view_point.y.is_sign_negative()
|| view_point.x > view_rect.size.width
|| view_point.y > view_rect.size.height {
|| view_point.y.is_sign_negative()
|| view_point.x > view_rect.size.width
|| view_point.y > view_rect.size.height
{
// Point is outside of the client area (view)
return;
}
@ -812,7 +802,7 @@ fn mouse_motion(this: &Object, event: id) {
let y = view_rect.size.height as f64 - view_point.y as f64;
let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)),
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::CursorMoved {
device_id: DEVICE_ID,
position: (x, y).into(),
@ -824,31 +814,33 @@ fn mouse_motion(this: &Object, event: id) {
}
}
extern fn mouse_moved(this: &Object, _sel: Sel, event: id) {
extern "C" fn mouse_moved(this: &Object, _sel: Sel, event: id) {
mouse_motion(this, event);
}
extern fn mouse_dragged(this: &Object, _sel: Sel, event: id) {
extern "C" fn mouse_dragged(this: &Object, _sel: Sel, event: id) {
mouse_motion(this, event);
}
extern fn right_mouse_dragged(this: &Object, _sel: Sel, event: id) {
extern "C" fn right_mouse_dragged(this: &Object, _sel: Sel, event: id) {
mouse_motion(this, event);
}
extern fn other_mouse_dragged(this: &Object, _sel: Sel, event: id) {
extern "C" fn other_mouse_dragged(this: &Object, _sel: Sel, event: id) {
mouse_motion(this, event);
}
extern fn mouse_entered(this: &Object, _sel: Sel, event: id) {
extern "C" fn mouse_entered(this: &Object, _sel: Sel, event: id) {
trace!("Triggered `mouseEntered`");
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
let enter_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)),
event: WindowEvent::CursorEntered { device_id: DEVICE_ID },
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::CursorEntered {
device_id: DEVICE_ID,
},
};
let move_event = {
@ -861,12 +853,12 @@ extern fn mouse_entered(this: &Object, _sel: Sel, event: id) {
let x = view_point.x as f64;
let y = (view_rect.size.height - view_point.y) as f64;
Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)),
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::CursorMoved {
device_id: DEVICE_ID,
position: (x, y).into(),
modifiers: event_mods(event),
}
},
}
};
@ -876,15 +868,17 @@ extern fn mouse_entered(this: &Object, _sel: Sel, event: id) {
trace!("Completed `mouseEntered`");
}
extern fn mouse_exited(this: &Object, _sel: Sel, _event: id) {
extern "C" fn mouse_exited(this: &Object, _sel: Sel, _event: id) {
trace!("Triggered `mouseExited`");
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
let state = &mut *(state_ptr as *mut ViewState);
let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)),
event: WindowEvent::CursorLeft { device_id: DEVICE_ID },
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::CursorLeft {
device_id: DEVICE_ID,
},
};
AppState::queue_event(window_event);
@ -892,7 +886,7 @@ extern fn mouse_exited(this: &Object, _sel: Sel, _event: id) {
trace!("Completed `mouseExited`");
}
extern fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
trace!("Triggered `scrollWheel`");
unsafe {
let delta = {
@ -904,7 +898,9 @@ extern fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
}
};
let phase = match event.phase() {
NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => TouchPhase::Started,
NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => {
TouchPhase::Started
}
NSEventPhase::NSEventPhaseEnded => TouchPhase::Ended,
_ => TouchPhase::Moved,
};
@ -918,7 +914,7 @@ extern fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
let state = &mut *(state_ptr as *mut ViewState);
let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)),
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::MouseWheel {
device_id: DEVICE_ID,
delta,
@ -933,7 +929,7 @@ extern fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
trace!("Completed `scrollWheel`");
}
extern fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) {
extern "C" fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) {
trace!("Triggered `pressureChangeWithEvent`");
unsafe {
let state_ptr: *mut c_void = *this.get_ivar("winitState");
@ -943,7 +939,7 @@ extern fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) {
let stage = event.stage();
let window_event = Event::WindowEvent {
window_id: WindowId(get_window_id(state.nswindow)),
window_id: WindowId(get_window_id(state.ns_window)),
event: WindowEvent::TouchpadPressure {
device_id: DEVICE_ID,
pressure,
@ -959,6 +955,6 @@ extern fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) {
// Allows us to receive Ctrl-Tab and Ctrl-Esc.
// Note that this *doesn't* help with any missing Cmd inputs.
// https://github.com/chromium/chromium/blob/a86a8a6bcfa438fa3ac2eba6f02b3ad1f8e0756f/ui/views/cocoa/bridged_content_view.mm#L816
extern fn wants_key_down_for_event(_this: &Object, _sel: Sel, _event: id) -> BOOL {
extern "C" fn wants_key_down_for_event(_this: &Object, _sel: Sel, _event: id) -> BOOL {
YES
}

View file

@ -1,34 +1,44 @@
use std::{
collections::VecDeque, f64, os::raw::c_void,
sync::{Arc, atomic::{Ordering, AtomicBool}, Mutex, Weak},
collections::VecDeque,
f64,
os::raw::c_void,
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex, Weak,
},
};
use cocoa::{
appkit::{
self, CGFloat, NSApp, NSApplication, NSApplicationActivationPolicy,
NSColor, NSRequestUserAttentionType, NSScreen, NSView, NSWindow,
NSWindowButton, NSWindowStyleMask, NSApplicationPresentationOptions
NSApplicationPresentationOptions, NSColor, NSRequestUserAttentionType, NSScreen, NSView,
NSWindow, NSWindowButton, NSWindowStyleMask,
},
base::{id, nil},
foundation::{NSAutoreleasePool, NSDictionary, NSPoint, NSRect, NSSize, NSString},
};
use core_graphics::display::CGDisplay;
use objc::{runtime::{Class, Object, Sel, BOOL, YES, NO}, declare::ClassDecl};
use {
dpi::{LogicalPosition, LogicalSize}, icon::Icon,
error::{ExternalError, NotSupportedError, OsError as RootOsError},
monitor::MonitorHandle as RootMonitorHandle,
window::{
CursorIcon, WindowAttributes, WindowId as RootWindowId,
},
use objc::{
declare::ClassDecl,
runtime::{Class, Object, Sel, BOOL, NO, YES},
};
use platform::macos::{ActivationPolicy, WindowExtMacOS};
use platform_impl::platform::{
OsError,
app_state::AppState, ffi, monitor::{self, MonitorHandle},
util::{self, IdRef}, view::{self, new_view},
window_delegate::new_delegate,
use crate::{
dpi::{LogicalPosition, LogicalSize},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
icon::Icon,
monitor::MonitorHandle as RootMonitorHandle,
platform::macos::{ActivationPolicy, RequestUserAttentionType, WindowExtMacOS},
platform_impl::platform::{
app_state::AppState,
ffi,
monitor::{self, MonitorHandle},
util::{self, IdRef},
view::{self, new_view},
window_delegate::new_delegate,
OsError,
},
window::{CursorIcon, WindowAttributes, WindowId as RootWindowId},
};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -60,26 +70,26 @@ pub struct PlatformSpecificWindowBuilderAttributes {
fn create_app(activation_policy: ActivationPolicy) -> Option<id> {
unsafe {
let nsapp = NSApp();
if nsapp == nil {
let ns_app = NSApp();
if ns_app == nil {
None
} else {
use self::NSApplicationActivationPolicy::*;
nsapp.setActivationPolicy_(match activation_policy {
ns_app.setActivationPolicy_(match activation_policy {
ActivationPolicy::Regular => NSApplicationActivationPolicyRegular,
ActivationPolicy::Accessory => NSApplicationActivationPolicyAccessory,
ActivationPolicy::Prohibited => NSApplicationActivationPolicyProhibited,
});
nsapp.finishLaunching();
Some(nsapp)
ns_app.finishLaunching();
Some(ns_app)
}
}
}
unsafe fn create_view(nswindow: id) -> Option<(IdRef, Weak<Mutex<util::Cursor>>)> {
let (nsview, cursor) = new_view(nswindow);
nsview.non_nil().map(|nsview| {
nsview.setWantsBestResolutionOpenGLSurface_(YES);
unsafe fn create_view(ns_window: id) -> Option<(IdRef, Weak<Mutex<util::Cursor>>)> {
let (ns_view, cursor) = new_view(ns_window);
ns_view.non_nil().map(|ns_view| {
ns_view.setWantsBestResolutionOpenGLSurface_(YES);
// On Mojave, views automatically become layer-backed shortly after being added to
// a window. Changing the layer-backedness of a view breaks the association between
@ -87,12 +97,12 @@ unsafe fn create_view(nswindow: id) -> Option<(IdRef, Weak<Mutex<util::Cursor>>)
// explicitly make the view layer-backed up front so that AppKit doesn't do it
// itself and break the association with its context.
if f64::floor(appkit::NSAppKitVersionNumber) > appkit::NSAppKitVersionNumber10_12 {
nsview.setWantsLayer(YES);
ns_view.setWantsLayer(YES);
}
nswindow.setContentView_(*nsview);
nswindow.makeFirstResponder_(*nsview);
(nsview, cursor)
ns_window.setContentView_(*ns_view);
ns_window.makeFirstResponder_(*ns_view);
(ns_view, cursor)
})
}
@ -104,36 +114,35 @@ fn create_window(
let pool = NSAutoreleasePool::new(nil);
let screen = match attrs.fullscreen {
Some(ref monitor_id) => {
let monitor_screen = monitor_id.inner.nsscreen();
let monitor_screen = monitor_id.inner.ns_screen();
Some(monitor_screen.unwrap_or(appkit::NSScreen::mainScreen(nil)))
},
}
_ => None,
};
let frame = match screen {
Some(screen) => appkit::NSScreen::frame(screen),
None => {
let (width, height) = attrs.inner_size
let (width, height) = attrs
.inner_size
.map(|logical| (logical.width, logical.height))
.unwrap_or_else(|| (800.0, 600.0));
NSRect::new(NSPoint::new(0.0, 0.0), NSSize::new(width, height))
},
}
};
let mut masks = if !attrs.decorations && !screen.is_some() {
// Resizable UnownedWindow without a titlebar or borders
// if decorations is set to false, ignore pl_attrs
NSWindowStyleMask::NSBorderlessWindowMask
| NSWindowStyleMask::NSResizableWindowMask
NSWindowStyleMask::NSBorderlessWindowMask | NSWindowStyleMask::NSResizableWindowMask
} else if pl_attrs.titlebar_hidden {
// if the titlebar is hidden, ignore other pl_attrs
NSWindowStyleMask::NSBorderlessWindowMask |
NSWindowStyleMask::NSResizableWindowMask
NSWindowStyleMask::NSBorderlessWindowMask | NSWindowStyleMask::NSResizableWindowMask
} else {
// default case, resizable window with titlebar and titlebar buttons
NSWindowStyleMask::NSClosableWindowMask |
NSWindowStyleMask::NSMiniaturizableWindowMask |
NSWindowStyleMask::NSResizableWindowMask |
NSWindowStyleMask::NSTitledWindowMask
NSWindowStyleMask::NSClosableWindowMask
| NSWindowStyleMask::NSMiniaturizableWindowMask
| NSWindowStyleMask::NSResizableWindowMask
| NSWindowStyleMask::NSTitledWindowMask
};
if !attrs.resizable {
@ -144,24 +153,24 @@ fn create_window(
masks |= NSWindowStyleMask::NSFullSizeContentViewWindowMask;
}
let nswindow: id = msg_send![WINDOW_CLASS.0, alloc];
let nswindow = IdRef::new(nswindow.initWithContentRect_styleMask_backing_defer_(
let ns_window: id = msg_send![WINDOW_CLASS.0, alloc];
let ns_window = IdRef::new(ns_window.initWithContentRect_styleMask_backing_defer_(
frame,
masks,
appkit::NSBackingStoreBuffered,
NO,
));
let res = nswindow.non_nil().map(|nswindow| {
let res = ns_window.non_nil().map(|ns_window| {
let title = IdRef::new(NSString::alloc(nil).init_str(&attrs.title));
nswindow.setReleasedWhenClosed_(NO);
nswindow.setTitle_(*title);
nswindow.setAcceptsMouseMovedEvents_(YES);
ns_window.setReleasedWhenClosed_(NO);
ns_window.setTitle_(*title);
ns_window.setAcceptsMouseMovedEvents_(YES);
if pl_attrs.titlebar_transparent {
nswindow.setTitlebarAppearsTransparent_(YES);
ns_window.setTitlebarAppearsTransparent_(YES);
}
if pl_attrs.title_hidden {
nswindow.setTitleVisibility_(appkit::NSWindowTitleVisibility::NSWindowTitleHidden);
ns_window.setTitleVisibility_(appkit::NSWindowTitleVisibility::NSWindowTitleHidden);
}
if pl_attrs.titlebar_buttons_hidden {
for titlebar_button in &[
@ -170,28 +179,31 @@ fn create_window(
NSWindowButton::NSWindowCloseButton,
NSWindowButton::NSWindowZoomButton,
] {
let button = nswindow.standardWindowButton_(*titlebar_button);
let _: () = msg_send![button, setHidden:YES];
let button = ns_window.standardWindowButton_(*titlebar_button);
let _: () = msg_send![button, setHidden: YES];
}
}
if pl_attrs.movable_by_window_background {
nswindow.setMovableByWindowBackground_(YES);
ns_window.setMovableByWindowBackground_(YES);
}
if attrs.always_on_top {
let _: () = msg_send![*nswindow, setLevel:ffi::NSWindowLevel::NSFloatingWindowLevel];
let _: () = msg_send![
*ns_window,
setLevel: ffi::NSWindowLevel::NSFloatingWindowLevel
];
}
if let Some(increments) = pl_attrs.resize_increments {
let (x, y) = (increments.width, increments.height);
if x >= 1.0 && y >= 1.0 {
let size = NSSize::new(x as CGFloat, y as CGFloat);
nswindow.setResizeIncrements_(size);
ns_window.setResizeIncrements_(size);
}
}
nswindow.center();
nswindow
ns_window.center();
ns_window
});
pool.drain();
res
@ -206,8 +218,14 @@ lazy_static! {
static ref WINDOW_CLASS: WindowClass = unsafe {
let window_superclass = class!(NSWindow);
let mut decl = ClassDecl::new("WinitWindow", window_superclass).unwrap();
decl.add_method(sel!(canBecomeMainWindow), util::yes as extern fn(&Object, Sel) -> BOOL);
decl.add_method(sel!(canBecomeKeyWindow), util::yes as extern fn(&Object, Sel) -> BOOL);
decl.add_method(
sel!(canBecomeMainWindow),
util::yes as extern "C" fn(&Object, Sel) -> BOOL,
);
decl.add_method(
sel!(canBecomeKeyWindow),
util::yes as extern "C" fn(&Object, Sel) -> BOOL,
);
WindowClass(decl.register())
};
}
@ -217,12 +235,19 @@ pub struct SharedState {
pub resizable: bool,
pub fullscreen: Option<RootMonitorHandle>,
pub maximized: bool,
standard_frame: Option<NSRect>,
pub standard_frame: Option<NSRect>,
is_simple_fullscreen: bool,
pub saved_style: Option<NSWindowStyleMask>,
save_presentation_opts: Option<NSApplicationPresentationOptions>,
}
impl SharedState {
pub fn saved_standard_frame(&self) -> NSRect {
self.standard_frame
.unwrap_or_else(|| NSRect::new(NSPoint::new(50.0, 50.0), NSSize::new(800.0, 600.0)))
}
}
impl From<WindowAttributes> for SharedState {
fn from(attribs: WindowAttributes) -> Self {
SharedState {
@ -235,14 +260,14 @@ impl From<WindowAttributes> for SharedState {
// identical, resulting in a no-op.
fullscreen: None,
maximized: attribs.maximized,
.. Default::default()
..Default::default()
}
}
}
pub struct UnownedWindow {
pub nswindow: IdRef, // never changes
pub nsview: IdRef, // never changes
pub ns_window: IdRef, // never changes
pub ns_view: IdRef, // never changes
input_context: IdRef, // never changes
pub shared_state: Arc<Mutex<SharedState>>,
decorations: AtomicBool,
@ -266,40 +291,45 @@ impl UnownedWindow {
let pool = unsafe { NSAutoreleasePool::new(nil) };
let nsapp = create_app(pl_attribs.activation_policy).ok_or_else(|| {
let ns_app = create_app(pl_attribs.activation_policy).ok_or_else(|| {
unsafe { pool.drain() };
os_error!(OsError::CreationError("Couldn't create `NSApplication`"))
})?;
let nswindow = create_window(&win_attribs, &pl_attribs).ok_or_else(|| {
let ns_window = create_window(&win_attribs, &pl_attribs).ok_or_else(|| {
unsafe { pool.drain() };
os_error!(OsError::CreationError("Couldn't create `NSWindow`"))
})?;
let (nsview, cursor) = unsafe { create_view(*nswindow) }.ok_or_else(|| {
let (ns_view, cursor) = unsafe { create_view(*ns_window) }.ok_or_else(|| {
unsafe { pool.drain() };
os_error!(OsError::CreationError("Couldn't create `NSView`"))
})?;
let input_context = unsafe { util::create_input_context(*nsview) };
let input_context = unsafe { util::create_input_context(*ns_view) };
unsafe {
if win_attribs.transparent {
nswindow.setOpaque_(NO);
nswindow.setBackgroundColor_(NSColor::clearColor(nil));
ns_window.setOpaque_(NO);
ns_window.setBackgroundColor_(NSColor::clearColor(nil));
}
nsapp.activateIgnoringOtherApps_(YES);
ns_app.activateIgnoringOtherApps_(YES);
win_attribs.min_inner_size.map(|dim| set_min_inner_size(*nswindow, dim));
win_attribs.max_inner_size.map(|dim| set_max_inner_size(*nswindow, dim));
win_attribs
.min_inner_size
.map(|dim| set_min_inner_size(*ns_window, dim));
win_attribs
.max_inner_size
.map(|dim| set_max_inner_size(*ns_window, dim));
use cocoa::foundation::NSArray;
// register for drag and drop operations.
let () = msg_send![*nswindow, registerForDraggedTypes:NSArray::arrayWithObject(
nil,
appkit::NSFilenamesPboardType,
)];
let () = msg_send![
*ns_window,
registerForDraggedTypes:
NSArray::arrayWithObject(nil, appkit::NSFilenamesPboardType)
];
}
// Since `win_attribs` is put into a mutex below, we'll just copy these
@ -313,8 +343,8 @@ impl UnownedWindow {
let decorations = win_attribs.decorations;
let window = Arc::new(UnownedWindow {
nsview,
nswindow,
ns_view,
ns_window,
input_context,
shared_state: Arc::new(Mutex::new(win_attribs.into())),
decorations: AtomicBool::new(decorations),
@ -341,9 +371,9 @@ impl UnownedWindow {
// before it transitions.
unsafe {
if visible {
window.nswindow.makeKeyAndOrderFront_(nil);
window.ns_window.makeKeyAndOrderFront_(nil);
} else {
window.nswindow.makeKeyWindow();
window.ns_window.makeKeyWindow();
}
}
@ -357,36 +387,27 @@ impl UnownedWindow {
}
fn set_style_mask_async(&self, mask: NSWindowStyleMask) {
unsafe { util::set_style_mask_async(
*self.nswindow,
*self.nsview,
mask,
) };
unsafe { util::set_style_mask_async(*self.ns_window, *self.ns_view, mask) };
}
fn set_style_mask_sync(&self, mask: NSWindowStyleMask) {
unsafe { util::set_style_mask_sync(
*self.nswindow,
*self.nsview,
mask,
) };
unsafe { util::set_style_mask_sync(*self.ns_window, *self.ns_view, mask) };
}
pub fn id(&self) -> Id {
get_window_id(*self.nswindow)
get_window_id(*self.ns_window)
}
pub fn set_title(&self, title: &str) {
unsafe {
let title = IdRef::new(NSString::alloc(nil).init_str(title));
self.nswindow.setTitle_(*title);
util::set_title_async(*self.ns_window, title.to_string());
}
}
pub fn set_visible(&self, visible: bool) {
match visible {
true => unsafe { util::make_key_and_order_front_async(*self.nswindow) },
false => unsafe { util::order_out_async(*self.nswindow) },
true => unsafe { util::make_key_and_order_front_async(*self.ns_window) },
false => unsafe { util::order_out_async(*self.ns_window) },
}
}
@ -395,24 +416,23 @@ impl UnownedWindow {
}
pub fn outer_position(&self) -> Result<LogicalPosition, NotSupportedError> {
let frame_rect = unsafe { NSWindow::frame(*self.nswindow) };
let frame_rect = unsafe { NSWindow::frame(*self.ns_window) };
Ok((
frame_rect.origin.x as f64,
util::bottom_left_to_top_left(frame_rect),
).into())
)
.into())
}
pub fn inner_position(&self) -> Result<LogicalPosition, NotSupportedError> {
let content_rect = unsafe {
NSWindow::contentRectForFrameRect_(
*self.nswindow,
NSWindow::frame(*self.nswindow),
)
NSWindow::contentRectForFrameRect_(*self.ns_window, NSWindow::frame(*self.ns_window))
};
Ok((
content_rect.origin.x as f64,
util::bottom_left_to_top_left(content_rect),
).into())
)
.into())
}
pub fn set_outer_position(&self, position: LogicalPosition) {
@ -426,40 +446,40 @@ impl UnownedWindow {
NSSize::new(0f64, 0f64),
);
unsafe {
util::set_frame_top_left_point_async(*self.nswindow, dummy.origin);
util::set_frame_top_left_point_async(*self.ns_window, dummy.origin);
}
}
#[inline]
pub fn inner_size(&self) -> LogicalSize {
let view_frame = unsafe { NSView::frame(*self.nsview) };
let view_frame = unsafe { NSView::frame(*self.ns_view) };
(view_frame.size.width as f64, view_frame.size.height as f64).into()
}
#[inline]
pub fn outer_size(&self) -> LogicalSize {
let view_frame = unsafe { NSWindow::frame(*self.nswindow) };
let view_frame = unsafe { NSWindow::frame(*self.ns_window) };
(view_frame.size.width as f64, view_frame.size.height as f64).into()
}
#[inline]
pub fn set_inner_size(&self, size: LogicalSize) {
unsafe {
util::set_content_size_async(*self.nswindow, size);
util::set_content_size_async(*self.ns_window, size);
}
}
pub fn set_min_inner_size(&self, dimensions: Option<LogicalSize>) {
unsafe {
let dimensions = dimensions.unwrap_or_else(|| (0, 0).into());
set_min_inner_size(*self.nswindow, dimensions);
set_min_inner_size(*self.ns_window, dimensions);
}
}
pub fn set_max_inner_size(&self, dimensions: Option<LogicalSize>) {
unsafe {
let dimensions = dimensions.unwrap_or_else(|| (!0, !0).into());
set_max_inner_size(*self.nswindow, dimensions);
set_max_inner_size(*self.ns_window, dimensions);
}
}
@ -473,7 +493,7 @@ impl UnownedWindow {
shared_state_lock.fullscreen.is_some()
};
if !fullscreen {
let mut mask = unsafe { self.nswindow.styleMask() };
let mut mask = unsafe { self.ns_window.styleMask() };
if resizable {
mask |= NSWindowStyleMask::NSResizableWindowMask;
} else {
@ -489,8 +509,8 @@ impl UnownedWindow {
*cursor_access.lock().unwrap() = cursor;
}
unsafe {
let _: () = msg_send![*self.nswindow,
invalidateCursorRectsForView:*self.nsview
let _: () = msg_send![*self.ns_window,
invalidateCursorRectsForView:*self.ns_view
];
}
}
@ -519,11 +539,14 @@ impl UnownedWindow {
#[inline]
pub fn hidpi_factor(&self) -> f64 {
unsafe { NSWindow::backingScaleFactor(*self.nswindow) as _ }
unsafe { NSWindow::backingScaleFactor(*self.ns_window) as _ }
}
#[inline]
pub fn set_cursor_position(&self, cursor_position: LogicalPosition) -> Result<(), ExternalError> {
pub fn set_cursor_position(
&self,
cursor_position: LogicalPosition,
) -> Result<(), ExternalError> {
let window_position = self.inner_position().unwrap();
let point = appkit::CGPoint {
x: (cursor_position.x + window_position.x) as CGFloat,
@ -540,16 +563,16 @@ impl UnownedWindow {
pub(crate) fn is_zoomed(&self) -> bool {
// because `isZoomed` doesn't work if the window's borderless,
// we make it resizable temporalily.
let curr_mask = unsafe { self.nswindow.styleMask() };
let curr_mask = unsafe { self.ns_window.styleMask() };
let required = NSWindowStyleMask::NSTitledWindowMask
| NSWindowStyleMask::NSResizableWindowMask;
let required =
NSWindowStyleMask::NSTitledWindowMask | NSWindowStyleMask::NSResizableWindowMask;
let needs_temp_mask = !curr_mask.contains(required);
if needs_temp_mask {
self.set_style_mask_sync(required);
}
let is_zoomed: BOOL = unsafe { msg_send![*self.nswindow, isZoomed] };
let is_zoomed: BOOL = unsafe { msg_send![*self.ns_window, isZoomed] };
// Roll back temp styles
if needs_temp_mask {
@ -560,9 +583,10 @@ impl UnownedWindow {
}
fn saved_style(&self, shared_state: &mut SharedState) -> NSWindowStyleMask {
let base_mask = shared_state.saved_style
let base_mask = shared_state
.saved_style
.take()
.unwrap_or_else(|| unsafe { self.nswindow.styleMask() });
.unwrap_or_else(|| unsafe { self.ns_window.styleMask() });
if shared_state.resizable {
base_mask | NSWindowStyleMask::NSResizableWindowMask
} else {
@ -570,13 +594,6 @@ impl UnownedWindow {
}
}
fn saved_standard_frame(shared_state: &mut SharedState) -> NSRect {
shared_state.standard_frame.unwrap_or_else(|| NSRect::new(
NSPoint::new(50.0, 50.0),
NSSize::new(800.0, 600.0),
))
}
pub(crate) fn restore_state_from_fullscreen(&self) {
let maximized = {
trace!("Locked shared state in `restore_state_from_fullscreen`");
@ -596,42 +613,17 @@ impl UnownedWindow {
#[inline]
pub fn set_maximized(&self, maximized: bool) {
let is_zoomed = self.is_zoomed();
if is_zoomed == maximized { return };
trace!("Locked shared state in `set_maximized`");
let mut shared_state_lock = self.shared_state.lock().unwrap();
// Save the standard frame sized if it is not zoomed
if !is_zoomed {
unsafe {
shared_state_lock.standard_frame = Some(NSWindow::frame(*self.nswindow));
}
}
shared_state_lock.maximized = maximized;
let curr_mask = unsafe { self.nswindow.styleMask() };
if shared_state_lock.fullscreen.is_some() {
// Handle it in window_did_exit_fullscreen
if is_zoomed == maximized {
return;
} else if curr_mask.contains(NSWindowStyleMask::NSResizableWindowMask) {
// Just use the native zoom if resizable
unsafe { self.nswindow.zoom_(nil) };
} else {
// if it's not resizable, we set the frame directly
unsafe {
let new_rect = if maximized {
let screen = NSScreen::mainScreen(nil);
NSScreen::visibleFrame(screen)
} else {
Self::saved_standard_frame(&mut *shared_state_lock)
};
// This probably isn't thread-safe!
self.nswindow.setFrame_display_(new_rect, 0);
}
};
unsafe {
util::set_maximized_async(
*self.ns_window,
is_zoomed,
maximized,
Arc::downgrade(&self.shared_state),
);
}
trace!("Unlocked shared state in `set_maximized`");
}
#[inline]
@ -646,7 +638,7 @@ impl UnownedWindow {
pub fn set_fullscreen(&self, monitor: Option<RootMonitorHandle>) {
let shared_state_lock = self.shared_state.lock().unwrap();
if shared_state_lock.is_simple_fullscreen {
return
return;
}
let not_fullscreen = {
@ -657,7 +649,7 @@ impl UnownedWindow {
// Our best bet is probably to move to the origin of the
// target monitor.
unimplemented!()
},
}
(&None, None) | (&Some(_), Some(_)) => return,
_ => (),
}
@ -665,12 +657,14 @@ impl UnownedWindow {
current.is_none()
};
unsafe { util::toggle_full_screen_async(
*self.nswindow,
*self.nsview,
not_fullscreen,
Arc::downgrade(&self.shared_state),
) };
unsafe {
util::toggle_full_screen_async(
*self.ns_window,
*self.ns_view,
not_fullscreen,
Arc::downgrade(&self.shared_state),
)
};
}
#[inline]
@ -690,7 +684,9 @@ impl UnownedWindow {
// If we're in fullscreen mode, we wait to apply decoration changes
// until we're in `window_did_exit_fullscreen`.
if fullscreen { return }
if fullscreen {
return;
}
let new_mask = {
let mut new_mask = if decorations {
@ -718,7 +714,7 @@ impl UnownedWindow {
} else {
ffi::NSWindowLevel::NSNormalWindowLevel
};
unsafe { util::set_level_async(*self.nswindow, level) };
unsafe { util::set_level_async(*self.ns_window, level) };
}
#[inline]
@ -737,7 +733,7 @@ impl UnownedWindow {
pub fn set_ime_position(&self, logical_spot: LogicalPosition) {
unsafe {
view::set_ime_position(
*self.nsview,
*self.ns_view,
*self.input_context,
logical_spot.x,
logical_spot.y,
@ -748,12 +744,14 @@ impl UnownedWindow {
#[inline]
pub fn current_monitor(&self) -> RootMonitorHandle {
unsafe {
let screen: id = msg_send![*self.nswindow, screen];
let screen: id = msg_send![*self.ns_window, screen];
let desc = NSScreen::deviceDescription(screen);
let key = IdRef::new(NSString::alloc(nil).init_str("NSScreenNumber"));
let value = NSDictionary::valueForKey_(desc, *key);
let display_id = msg_send![value, unsignedIntegerValue];
RootMonitorHandle { inner: MonitorHandle::new(display_id) }
RootMonitorHandle {
inner: MonitorHandle::new(display_id),
}
}
}
@ -770,21 +768,23 @@ impl UnownedWindow {
impl WindowExtMacOS for UnownedWindow {
#[inline]
fn nswindow(&self) -> *mut c_void {
*self.nswindow as *mut _
fn ns_window(&self) -> *mut c_void {
*self.ns_window as *mut _
}
#[inline]
fn nsview(&self) -> *mut c_void {
*self.nsview as *mut _
fn ns_view(&self) -> *mut c_void {
*self.ns_view as *mut _
}
#[inline]
fn request_user_attention(&self, is_critical: bool) {
fn request_user_attention(&self, request_type: RequestUserAttentionType) {
unsafe {
NSApp().requestUserAttention_(match is_critical {
true => NSRequestUserAttentionType::NSCriticalRequest,
false => NSRequestUserAttentionType::NSInformationalRequest,
NSApp().requestUserAttention_(match request_type {
RequestUserAttentionType::Critical => NSRequestUserAttentionType::NSCriticalRequest,
RequestUserAttentionType::Informational => {
NSRequestUserAttentionType::NSInformationalRequest
}
});
}
}
@ -805,14 +805,17 @@ impl WindowExtMacOS for UnownedWindow {
let is_simple_fullscreen = shared_state_lock.is_simple_fullscreen;
// Do nothing if native fullscreen is active.
if is_native_fullscreen || (fullscreen && is_simple_fullscreen) || (!fullscreen && !is_simple_fullscreen) {
if is_native_fullscreen
|| (fullscreen && is_simple_fullscreen)
|| (!fullscreen && !is_simple_fullscreen)
{
return false;
}
if fullscreen {
// Remember the original window's settings
shared_state_lock.standard_frame = Some(NSWindow::frame(*self.nswindow));
shared_state_lock.saved_style = Some(self.nswindow.styleMask());
shared_state_lock.standard_frame = Some(NSWindow::frame(*self.ns_window));
shared_state_lock.saved_style = Some(self.ns_window.styleMask());
shared_state_lock.save_presentation_opts = Some(app.presentationOptions_());
// Tell our window's state that we're in fullscreen
@ -825,17 +828,32 @@ impl WindowExtMacOS for UnownedWindow {
app.setPresentationOptions_(presentation_options);
// Hide the titlebar
util::toggle_style_mask(*self.nswindow, *self.nsview, NSWindowStyleMask::NSTitledWindowMask, false);
util::toggle_style_mask(
*self.ns_window,
*self.ns_view,
NSWindowStyleMask::NSTitledWindowMask,
false,
);
// Set the window frame to the screen frame size
let screen = self.nswindow.screen();
let screen = self.ns_window.screen();
let screen_frame = NSScreen::frame(screen);
NSWindow::setFrame_display_(*self.nswindow, screen_frame, YES);
NSWindow::setFrame_display_(*self.ns_window, screen_frame, YES);
// Fullscreen windows can't be resized, minimized, or moved
util::toggle_style_mask(*self.nswindow, *self.nsview, NSWindowStyleMask::NSMiniaturizableWindowMask, false);
util::toggle_style_mask(*self.nswindow, *self.nsview, NSWindowStyleMask::NSResizableWindowMask, false);
NSWindow::setMovable_(*self.nswindow, NO);
util::toggle_style_mask(
*self.ns_window,
*self.ns_view,
NSWindowStyleMask::NSMiniaturizableWindowMask,
false,
);
util::toggle_style_mask(
*self.ns_window,
*self.ns_view,
NSWindowStyleMask::NSResizableWindowMask,
false,
);
NSWindow::setMovable_(*self.ns_window, NO);
true
} else {
@ -847,9 +865,9 @@ impl WindowExtMacOS for UnownedWindow {
app.setPresentationOptions_(presentation_opts);
}
let frame = Self::saved_standard_frame(&mut *shared_state_lock);
NSWindow::setFrame_display_(*self.nswindow, frame, YES);
NSWindow::setMovable_(*self.nswindow, YES);
let frame = shared_state_lock.saved_standard_frame();
NSWindow::setFrame_display_(*self.ns_window, frame, YES);
NSWindow::setMovable_(*self.ns_window, YES);
true
}
@ -861,8 +879,8 @@ impl Drop for UnownedWindow {
fn drop(&mut self) {
trace!("Dropping `UnownedWindow` ({:?})", self as *mut _);
// Close the window if it has not yet been closed.
if *self.nswindow != nil {
unsafe { util::close_async(*self.nswindow) };
if *self.ns_window != nil {
unsafe { util::close_async(*self.ns_window) };
}
}
}

View file

@ -1,20 +1,33 @@
use std::{f64, os::raw::c_void, sync::{Arc, Weak}};
use std::{
f64,
os::raw::c_void,
sync::{Arc, Weak},
};
use cocoa::{
appkit::{self, NSView, NSWindow}, base::{id, nil},
appkit::{self, NSView, NSWindow},
base::{id, nil},
foundation::NSAutoreleasePool,
};
use objc::{runtime::{Class, Object, Sel, BOOL, YES, NO}, declare::ClassDecl};
use objc::{
declare::ClassDecl,
runtime::{Class, Object, Sel, BOOL, NO, YES},
};
use {dpi::LogicalSize, event::{Event, WindowEvent}, window::WindowId};
use platform_impl::platform::{
app_state::AppState, util::{self, IdRef},
window::{get_window_id, UnownedWindow},
use crate::{
dpi::LogicalSize,
event::{Event, WindowEvent},
platform_impl::platform::{
app_state::AppState,
util::{self, IdRef},
window::{get_window_id, UnownedWindow},
},
window::WindowId,
};
pub struct WindowDelegateState {
nswindow: IdRef, // never changes
nsview: IdRef, // never changes
ns_window: IdRef, // never changes
ns_view: IdRef, // never changes
window: Weak<UnownedWindow>,
@ -33,15 +46,12 @@ pub struct WindowDelegateState {
}
impl WindowDelegateState {
pub fn new(
window: &Arc<UnownedWindow>,
initial_fullscreen: bool,
) -> Self {
pub fn new(window: &Arc<UnownedWindow>, initial_fullscreen: bool) -> Self {
let dpi_factor = window.hidpi_factor();
let mut delegate_state = WindowDelegateState {
nswindow: window.nswindow.clone(),
nsview: window.nsview.clone(),
ns_window: window.ns_window.clone(),
ns_view: window.ns_view.clone(),
window: Arc::downgrade(&window),
initial_fullscreen,
previous_position: None,
@ -57,33 +67,32 @@ impl WindowDelegateState {
}
fn with_window<F, T>(&mut self, callback: F) -> Option<T>
where F: FnOnce(&UnownedWindow) -> T
where
F: FnOnce(&UnownedWindow) -> T,
{
self.window
.upgrade()
.map(|ref window| callback(window))
self.window.upgrade().map(|ref window| callback(window))
}
pub fn emit_event(&mut self, event: WindowEvent) {
let event = Event::WindowEvent {
window_id: WindowId(get_window_id(*self.nswindow)),
window_id: WindowId(get_window_id(*self.ns_window)),
event,
};
AppState::queue_event(event);
}
pub fn emit_resize_event(&mut self) {
let rect = unsafe { NSView::frame(*self.nsview) };
let rect = unsafe { NSView::frame(*self.ns_view) };
let size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64);
let event = Event::WindowEvent {
window_id: WindowId(get_window_id(*self.nswindow)),
window_id: WindowId(get_window_id(*self.ns_window)),
event: WindowEvent::Resized(size),
};
AppState::send_event_immediately(event);
}
fn emit_move_event(&mut self) {
let rect = unsafe { NSWindow::frame(*self.nswindow) };
let rect = unsafe { NSWindow::frame(*self.ns_window) };
let x = rect.origin.x as f64;
let y = util::bottom_left_to_top_left(rect);
let moved = self.previous_position != Some((x, y));
@ -100,7 +109,7 @@ pub fn new_delegate(window: &Arc<UnownedWindow>, initial_fullscreen: bool) -> Id
// This is free'd in `dealloc`
let state_ptr = Box::into_raw(Box::new(state)) as *mut c_void;
let delegate: id = msg_send![WINDOW_DELEGATE_CLASS.0, alloc];
IdRef::new(msg_send![delegate, initWithWinit:state_ptr])
IdRef::new(msg_send![delegate, initWithWinit: state_ptr])
}
}
@ -113,83 +122,81 @@ lazy_static! {
let superclass = class!(NSResponder);
let mut decl = ClassDecl::new("WinitWindowDelegate", superclass).unwrap();
decl.add_method(
sel!(dealloc),
dealloc as extern fn(&Object, Sel),
);
decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel));
decl.add_method(
sel!(initWithWinit:),
init_with_winit as extern fn(&Object, Sel, *mut c_void) -> id,
init_with_winit as extern "C" fn(&Object, Sel, *mut c_void) -> id,
);
decl.add_method(
sel!(windowShouldClose:),
window_should_close as extern fn(&Object, Sel, id) -> BOOL,
window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL,
);
decl.add_method(
sel!(windowWillClose:),
window_will_close as extern fn(&Object, Sel, id),
window_will_close as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(windowDidResize:),
window_did_resize as extern fn(&Object, Sel, id),
window_did_resize as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(windowDidMove:),
window_did_move as extern fn(&Object, Sel, id));
window_did_move as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(windowDidChangeScreen:),
window_did_change_screen as extern fn(&Object, Sel, id),
window_did_change_screen as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(windowDidChangeBackingProperties:),
window_did_change_backing_properties as extern fn(&Object, Sel, id),
window_did_change_backing_properties as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(windowDidBecomeKey:),
window_did_become_key as extern fn(&Object, Sel, id),
window_did_become_key as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(windowDidResignKey:),
window_did_resign_key as extern fn(&Object, Sel, id),
window_did_resign_key as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(draggingEntered:),
dragging_entered as extern fn(&Object, Sel, id) -> BOOL,
dragging_entered as extern "C" fn(&Object, Sel, id) -> BOOL,
);
decl.add_method(
sel!(prepareForDragOperation:),
prepare_for_drag_operation as extern fn(&Object, Sel, id) -> BOOL,
prepare_for_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL,
);
decl.add_method(
sel!(performDragOperation:),
perform_drag_operation as extern fn(&Object, Sel, id) -> BOOL,
perform_drag_operation as extern "C" fn(&Object, Sel, id) -> BOOL,
);
decl.add_method(
sel!(concludeDragOperation:),
conclude_drag_operation as extern fn(&Object, Sel, id),
conclude_drag_operation as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(draggingExited:),
dragging_exited as extern fn(&Object, Sel, id),
dragging_exited as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(windowDidEnterFullScreen:),
window_did_enter_fullscreen as extern fn(&Object, Sel, id),
window_did_enter_fullscreen as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(windowWillEnterFullScreen:),
window_will_enter_fullscreen as extern fn(&Object, Sel, id),
window_will_enter_fullscreen as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(windowDidExitFullScreen:),
window_did_exit_fullscreen as extern fn(&Object, Sel, id),
window_did_exit_fullscreen as extern "C" fn(&Object, Sel, id),
);
decl.add_method(
sel!(windowDidFailToEnterFullScreen:),
window_did_fail_to_enter_fullscreen as extern fn(&Object, Sel, id),
window_did_fail_to_enter_fullscreen as extern "C" fn(&Object, Sel, id),
);
decl.add_ivar::<*mut c_void>("winitState");
@ -207,47 +214,47 @@ fn with_state<F: FnOnce(&mut WindowDelegateState) -> T, T>(this: &Object, callba
callback(state_ptr);
}
extern fn dealloc(this: &Object, _sel: Sel) {
extern "C" fn dealloc(this: &Object, _sel: Sel) {
with_state(this, |state| unsafe {
Box::from_raw(state as *mut WindowDelegateState);
});
}
extern fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> id {
extern "C" fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> id {
unsafe {
let this: id = msg_send![this, init];
if this != nil {
(*this).set_ivar("winitState", state);
with_state(&*this, |state| {
let () = msg_send![*state.nswindow, setDelegate:this];
let () = msg_send![*state.ns_window, setDelegate: this];
});
}
this
}
}
extern fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
trace!("Triggered `windowShouldClose:`");
with_state(this, |state| state.emit_event(WindowEvent::CloseRequested));
trace!("Completed `windowShouldClose:`");
NO
}
extern fn window_will_close(this: &Object, _: Sel, _: id) {
extern "C" fn window_will_close(this: &Object, _: Sel, _: id) {
trace!("Triggered `windowWillClose:`");
with_state(this, |state| unsafe {
// `setDelegate:` retains the previous value and then autoreleases it
let pool = NSAutoreleasePool::new(nil);
// Since El Capitan, we need to be careful that delegate methods can't
// be called after the window closes.
let () = msg_send![*state.nswindow, setDelegate:nil];
let () = msg_send![*state.ns_window, setDelegate: nil];
pool.drain();
state.emit_event(WindowEvent::Destroyed);
});
trace!("Completed `windowWillClose:`");
}
extern fn window_did_resize(this: &Object, _: Sel, _: id) {
extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
trace!("Triggered `windowDidResize:`");
with_state(this, |state| {
state.emit_resize_event();
@ -257,7 +264,7 @@ extern fn window_did_resize(this: &Object, _: Sel, _: id) {
}
// This won't be triggered if the move was part of a resize.
extern fn window_did_move(this: &Object, _: Sel, _: id) {
extern "C" fn window_did_move(this: &Object, _: Sel, _: id) {
trace!("Triggered `windowDidMove:`");
with_state(this, |state| {
state.emit_move_event();
@ -265,12 +272,10 @@ extern fn window_did_move(this: &Object, _: Sel, _: id) {
trace!("Completed `windowDidMove:`");
}
extern fn window_did_change_screen(this: &Object, _: Sel, _: id) {
extern "C" fn window_did_change_screen(this: &Object, _: Sel, _: id) {
trace!("Triggered `windowDidChangeScreen:`");
with_state(this, |state| {
let dpi_factor = unsafe {
NSWindow::backingScaleFactor(*state.nswindow)
} as f64;
let dpi_factor = unsafe { NSWindow::backingScaleFactor(*state.ns_window) } as f64;
if state.previous_dpi_factor != dpi_factor {
state.previous_dpi_factor = dpi_factor;
state.emit_event(WindowEvent::HiDpiFactorChanged(dpi_factor));
@ -281,12 +286,10 @@ extern fn window_did_change_screen(this: &Object, _: Sel, _: id) {
}
// This will always be called before `window_did_change_screen`.
extern fn window_did_change_backing_properties(this: &Object, _:Sel, _:id) {
extern "C" fn window_did_change_backing_properties(this: &Object, _: Sel, _: id) {
trace!("Triggered `windowDidChangeBackingProperties:`");
with_state(this, |state| {
let dpi_factor = unsafe {
NSWindow::backingScaleFactor(*state.nswindow)
} as f64;
let dpi_factor = unsafe { NSWindow::backingScaleFactor(*state.ns_window) } as f64;
if state.previous_dpi_factor != dpi_factor {
state.previous_dpi_factor = dpi_factor;
state.emit_event(WindowEvent::HiDpiFactorChanged(dpi_factor));
@ -296,7 +299,7 @@ extern fn window_did_change_backing_properties(this: &Object, _:Sel, _:id) {
trace!("Completed `windowDidChangeBackingProperties:`");
}
extern fn window_did_become_key(this: &Object, _: Sel, _: id) {
extern "C" fn window_did_become_key(this: &Object, _: Sel, _: id) {
trace!("Triggered `windowDidBecomeKey:`");
with_state(this, |state| {
// TODO: center the cursor if the window had mouse grab when it
@ -306,7 +309,7 @@ extern fn window_did_become_key(this: &Object, _: Sel, _: id) {
trace!("Completed `windowDidBecomeKey:`");
}
extern fn window_did_resign_key(this: &Object, _: Sel, _: id) {
extern "C" fn window_did_resign_key(this: &Object, _: Sel, _: id) {
trace!("Triggered `windowDidResignKey:`");
with_state(this, |state| {
state.emit_event(WindowEvent::Focused(false));
@ -315,11 +318,10 @@ extern fn window_did_resign_key(this: &Object, _: Sel, _: id) {
}
/// Invoked when the dragged image enters destination bounds or frame
extern fn dragging_entered(this: &Object, _: Sel, sender: id) -> BOOL {
extern "C" fn dragging_entered(this: &Object, _: Sel, sender: id) -> BOOL {
trace!("Triggered `draggingEntered:`");
use cocoa::appkit::NSPasteboard;
use cocoa::foundation::NSFastEnumeration;
use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration};
use std::path::PathBuf;
let pb: id = unsafe { msg_send![sender, draggingPasteboard] };
@ -337,25 +339,24 @@ extern fn dragging_entered(this: &Object, _: Sel, sender: id) -> BOOL {
state.emit_event(WindowEvent::HoveredFile(PathBuf::from(path)));
});
}
};
}
trace!("Completed `draggingEntered:`");
YES
}
/// Invoked when the image is released
extern fn prepare_for_drag_operation(_: &Object, _: Sel, _: id) -> BOOL {
extern "C" fn prepare_for_drag_operation(_: &Object, _: Sel, _: id) -> BOOL {
trace!("Triggered `prepareForDragOperation:`");
trace!("Completed `prepareForDragOperation:`");
YES
}
/// Invoked after the released image has been removed from the screen
extern fn perform_drag_operation(this: &Object, _: Sel, sender: id) -> BOOL {
extern "C" fn perform_drag_operation(this: &Object, _: Sel, sender: id) -> BOOL {
trace!("Triggered `performDragOperation:`");
use cocoa::appkit::NSPasteboard;
use cocoa::foundation::NSFastEnumeration;
use cocoa::{appkit::NSPasteboard, foundation::NSFastEnumeration};
use std::path::PathBuf;
let pb: id = unsafe { msg_send![sender, draggingPasteboard] };
@ -373,38 +374,42 @@ extern fn perform_drag_operation(this: &Object, _: Sel, sender: id) -> BOOL {
state.emit_event(WindowEvent::DroppedFile(PathBuf::from(path)));
});
}
};
}
trace!("Completed `performDragOperation:`");
YES
}
/// Invoked when the dragging operation is complete
extern fn conclude_drag_operation(_: &Object, _: Sel, _: id) {
extern "C" fn conclude_drag_operation(_: &Object, _: Sel, _: id) {
trace!("Triggered `concludeDragOperation:`");
trace!("Completed `concludeDragOperation:`");
}
/// Invoked when the dragging operation is cancelled
extern fn dragging_exited(this: &Object, _: Sel, _: id) {
extern "C" fn dragging_exited(this: &Object, _: Sel, _: id) {
trace!("Triggered `draggingExited:`");
with_state(this, |state| state.emit_event(WindowEvent::HoveredFileCancelled));
with_state(this, |state| {
state.emit_event(WindowEvent::HoveredFileCancelled)
});
trace!("Completed `draggingExited:`");
}
/// Invoked when before enter fullscreen
extern fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
trace!("Triggered `windowWillEnterFullscreen:`");
with_state(this, |state| state.with_window(|window| {
trace!("Locked shared state in `window_will_enter_fullscreen`");
window.shared_state.lock().unwrap().maximized = window.is_zoomed();
trace!("Unlocked shared state in `window_will_enter_fullscreen`");
}));
with_state(this, |state| {
state.with_window(|window| {
trace!("Locked shared state in `window_will_enter_fullscreen`");
window.shared_state.lock().unwrap().maximized = window.is_zoomed();
trace!("Unlocked shared state in `window_will_enter_fullscreen`");
})
});
trace!("Completed `windowWillEnterFullscreen:`");
}
/// Invoked when entered fullscreen
extern fn window_did_enter_fullscreen(this: &Object, _: Sel, _: id) {
extern "C" fn window_did_enter_fullscreen(this: &Object, _: Sel, _: id) {
trace!("Triggered `windowDidEnterFullscreen:`");
with_state(this, |state| {
state.with_window(|window| {
@ -419,11 +424,13 @@ extern fn window_did_enter_fullscreen(this: &Object, _: Sel, _: id) {
}
/// Invoked when exited fullscreen
extern fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id) {
extern "C" fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id) {
trace!("Triggered `windowDidExitFullscreen:`");
with_state(this, |state| state.with_window(|window| {
window.restore_state_from_fullscreen();
}));
with_state(this, |state| {
state.with_window(|window| {
window.restore_state_from_fullscreen();
})
});
trace!("Completed `windowDidExitFullscreen:`");
}
@ -443,15 +450,17 @@ extern fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id) {
/// due to being in the midst of handling some other animation or user gesture.
/// This method indicates that there was an error, and you should clean up any
/// work you may have done to prepare to enter full-screen mode.
extern fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id) {
extern "C" fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id) {
trace!("Triggered `windowDidFailToEnterFullscreen:`");
with_state(this, |state| {
if state.initial_fullscreen {
let _: () = unsafe { msg_send![*state.nswindow,
performSelector:sel!(toggleFullScreen:)
withObject:nil
afterDelay: 0.5
] };
let _: () = unsafe {
msg_send![*state.ns_window,
performSelector:sel!(toggleFullScreen:)
withObject:nil
afterDelay: 0.5
]
};
} else {
state.with_window(|window| window.restore_state_from_fullscreen());
}

View file

@ -1,45 +1,39 @@
#![allow(non_snake_case, unused_unsafe)]
use std::mem;
use std::os::raw::c_void;
use std::sync::{Once, ONCE_INIT};
use std::{mem, os::raw::c_void, sync::Once};
use winapi::shared::minwindef::{BOOL, UINT, FALSE};
use winapi::shared::windef::{
DPI_AWARENESS_CONTEXT,
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE,
HMONITOR,
HWND,
use winapi::{
shared::{
minwindef::{BOOL, FALSE, UINT},
windef::{DPI_AWARENESS_CONTEXT, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE, HMONITOR, HWND},
winerror::S_OK,
},
um::{
libloaderapi::{GetProcAddress, LoadLibraryA},
shellscalingapi::{
MDT_EFFECTIVE_DPI, MONITOR_DPI_TYPE, PROCESS_DPI_AWARENESS,
PROCESS_PER_MONITOR_DPI_AWARE,
},
wingdi::{GetDeviceCaps, LOGPIXELSX},
winnt::{HRESULT, LPCSTR},
winuser::{self, MONITOR_DEFAULTTONEAREST},
},
};
use winapi::shared::winerror::S_OK;
use winapi::um::libloaderapi::{GetProcAddress, LoadLibraryA};
use winapi::um::shellscalingapi::{
MDT_EFFECTIVE_DPI,
MONITOR_DPI_TYPE,
PROCESS_DPI_AWARENESS,
PROCESS_PER_MONITOR_DPI_AWARE,
};
use winapi::um::wingdi::{GetDeviceCaps, LOGPIXELSX};
use winapi::um::winnt::{HRESULT, LPCSTR};
use winapi::um::winuser::{self, MONITOR_DEFAULTTONEAREST};
const DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2: DPI_AWARENESS_CONTEXT = -4isize as _;
type SetProcessDPIAware = unsafe extern "system" fn () -> BOOL;
type SetProcessDpiAwareness = unsafe extern "system" fn (
value: PROCESS_DPI_AWARENESS,
) -> HRESULT;
type SetProcessDpiAwarenessContext = unsafe extern "system" fn (
value: DPI_AWARENESS_CONTEXT,
) -> BOOL;
type GetDpiForWindow = unsafe extern "system" fn (hwnd: HWND) -> UINT;
type GetDpiForMonitor = unsafe extern "system" fn (
type SetProcessDPIAware = unsafe extern "system" fn() -> BOOL;
type SetProcessDpiAwareness = unsafe extern "system" fn(value: PROCESS_DPI_AWARENESS) -> HRESULT;
type SetProcessDpiAwarenessContext =
unsafe extern "system" fn(value: DPI_AWARENESS_CONTEXT) -> BOOL;
type GetDpiForWindow = unsafe extern "system" fn(hwnd: HWND) -> UINT;
type GetDpiForMonitor = unsafe extern "system" fn(
hmonitor: HMONITOR,
dpi_type: MONITOR_DPI_TYPE,
dpi_x: *mut UINT,
dpi_y: *mut UINT,
) -> HRESULT;
type EnableNonClientDpiScaling = unsafe extern "system" fn (hwnd: HWND) -> BOOL;
type EnableNonClientDpiScaling = unsafe extern "system" fn(hwnd: HWND) -> BOOL;
// Helper function to dynamically load function pointer.
// `library` and `function` must be zero-terminated.
@ -65,53 +59,48 @@ macro_rules! get_function {
($lib:expr, $func:ident) => {
get_function_impl(concat!($lib, '\0'), concat!(stringify!($func), '\0'))
.map(|f| unsafe { mem::transmute::<*const _, $func>(f) })
}
};
}
lazy_static! {
static ref GET_DPI_FOR_WINDOW: Option<GetDpiForWindow> = get_function!(
"user32.dll",
GetDpiForWindow
);
static ref GET_DPI_FOR_MONITOR: Option<GetDpiForMonitor> = get_function!(
"shcore.dll",
GetDpiForMonitor
);
static ref ENABLE_NON_CLIENT_DPI_SCALING: Option<EnableNonClientDpiScaling> = get_function!(
"user32.dll",
EnableNonClientDpiScaling
);
static ref GET_DPI_FOR_WINDOW: Option<GetDpiForWindow> =
get_function!("user32.dll", GetDpiForWindow);
static ref GET_DPI_FOR_MONITOR: Option<GetDpiForMonitor> =
get_function!("shcore.dll", GetDpiForMonitor);
static ref ENABLE_NON_CLIENT_DPI_SCALING: Option<EnableNonClientDpiScaling> =
get_function!("user32.dll", EnableNonClientDpiScaling);
}
pub fn become_dpi_aware(enable: bool) {
if !enable { return; }
static ENABLE_DPI_AWARENESS: Once = ONCE_INIT;
ENABLE_DPI_AWARENESS.call_once(|| { unsafe {
if let Some(SetProcessDpiAwarenessContext) = get_function!(
"user32.dll",
SetProcessDpiAwarenessContext
) {
// We are on Windows 10 Anniversary Update (1607) or later.
if SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)
== FALSE {
// V2 only works with Windows 10 Creators Update (1703). Try using the older
// V1 if we can't set V2.
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
if !enable {
return;
}
static ENABLE_DPI_AWARENESS: Once = Once::new();
ENABLE_DPI_AWARENESS.call_once(|| {
unsafe {
if let Some(SetProcessDpiAwarenessContext) =
get_function!("user32.dll", SetProcessDpiAwarenessContext)
{
// We are on Windows 10 Anniversary Update (1607) or later.
if SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)
== FALSE
{
// V2 only works with Windows 10 Creators Update (1703). Try using the older
// V1 if we can't set V2.
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
}
} else if let Some(SetProcessDpiAwareness) =
get_function!("shcore.dll", SetProcessDpiAwareness)
{
// We are on Windows 8.1 or later.
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
} else if let Some(SetProcessDPIAware) = get_function!("user32.dll", SetProcessDPIAware)
{
// We are on Vista or later.
SetProcessDPIAware();
}
} else if let Some(SetProcessDpiAwareness) = get_function!(
"shcore.dll",
SetProcessDpiAwareness
) {
// We are on Windows 8.1 or later.
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
} else if let Some(SetProcessDPIAware) = get_function!(
"user32.dll",
SetProcessDPIAware
) {
// We are on Vista or later.
SetProcessDPIAware();
}
} });
});
}
pub fn enable_non_client_dpi_scaling(hwnd: HWND) {
@ -132,7 +121,7 @@ pub fn get_monitor_dpi(hmonitor: HMONITOR) -> Option<u32> {
// MSDN says that "the values of *dpiX and *dpiY are identical. You only need to
// record one of the values to determine the DPI and respond appropriately".
// https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx
return Some(dpi_x as u32)
return Some(dpi_x as u32);
}
}
}

View file

@ -1,32 +1,39 @@
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::{mem, ptr};
use std::{
ffi::OsString,
os::windows::ffi::OsStringExt,
path::PathBuf,
ptr,
sync::atomic::{AtomicUsize, Ordering},
};
use winapi::ctypes::c_void;
use winapi::shared::guiddef::REFIID;
use winapi::shared::minwindef::{DWORD, MAX_PATH, UINT, ULONG};
use winapi::shared::windef::{HWND, POINTL};
use winapi::shared::winerror::S_OK;
use winapi::um::objidl::IDataObject;
use winapi::um::oleidl::{DROPEFFECT_COPY, DROPEFFECT_NONE, IDropTarget, IDropTargetVtbl};
use winapi::um::winnt::HRESULT;
use winapi::um::{shellapi, unknwnbase};
use winapi::{
ctypes::c_void,
shared::{
guiddef::REFIID,
minwindef::{DWORD, UINT, ULONG},
windef::{HWND, POINTL},
winerror::S_OK,
},
um::{
objidl::IDataObject,
oleidl::{IDropTarget, IDropTargetVtbl, DROPEFFECT_COPY, DROPEFFECT_NONE},
shellapi, unknwnbase,
winnt::HRESULT,
},
};
use platform_impl::platform::WindowId;
use crate::platform_impl::platform::WindowId;
use event::Event;
use window::WindowId as SuperWindowId;
use crate::{event::Event, window::WindowId as SuperWindowId};
#[repr(C)]
pub struct FileDropHandlerData {
pub interface: IDropTarget,
refcount: AtomicUsize,
window: HWND,
send_event: Box<Fn(Event<()>)>,
send_event: Box<dyn Fn(Event<()>)>,
cursor_effect: DWORD,
hovered_is_valid: bool, // If the currently hovered item is not valid there must not be any `HoveredFileCancelled` emitted
hovered_is_valid: bool, /* If the currently hovered item is not valid there must not be any `HoveredFileCancelled` emitted */
}
pub struct FileDropHandler {
@ -35,7 +42,7 @@ pub struct FileDropHandler {
#[allow(non_snake_case)]
impl FileDropHandler {
pub fn new(window: HWND, send_event: Box<Fn(Event<()>)>) -> FileDropHandler {
pub fn new(window: HWND, send_event: Box<dyn Fn(Event<()>)>) -> FileDropHandler {
let data = Box::new(FileDropHandlerData {
interface: IDropTarget {
lpVtbl: &DROP_TARGET_VTBL as *const IDropTargetVtbl,
@ -85,7 +92,7 @@ impl FileDropHandler {
_pt: *const POINTL,
pdwEffect: *mut DWORD,
) -> HRESULT {
use event::WindowEvent::HoveredFile;
use crate::event::WindowEvent::HoveredFile;
let drop_handler = Self::from_interface(this);
let hdrop = Self::iterate_filenames(pDataObj, |filename| {
drop_handler.send_event(Event::WindowEvent {
@ -117,7 +124,7 @@ impl FileDropHandler {
}
pub unsafe extern "system" fn DragLeave(this: *mut IDropTarget) -> HRESULT {
use event::WindowEvent::HoveredFileCancelled;
use crate::event::WindowEvent::HoveredFileCancelled;
let drop_handler = Self::from_interface(this);
if drop_handler.hovered_is_valid {
drop_handler.send_event(Event::WindowEvent {
@ -136,7 +143,7 @@ impl FileDropHandler {
_pt: *const POINTL,
_pdwEffect: *mut DWORD,
) -> HRESULT {
use event::WindowEvent::DroppedFile;
use crate::event::WindowEvent::DroppedFile;
let drop_handler = Self::from_interface(this);
let hdrop = Self::iterate_filenames(pDataObj, |filename| {
drop_handler.send_event(Event::WindowEvent {
@ -155,16 +162,24 @@ impl FileDropHandler {
&mut *(this as *mut _)
}
unsafe fn iterate_filenames<F>(data_obj: *const IDataObject, callback: F) -> Option<shellapi::HDROP>
unsafe fn iterate_filenames<F>(
data_obj: *const IDataObject,
callback: F,
) -> Option<shellapi::HDROP>
where
F: Fn(PathBuf),
{
use winapi::ctypes::wchar_t;
use winapi::shared::winerror::{SUCCEEDED, DV_E_FORMATETC};
use winapi::shared::wtypes::{CLIPFORMAT, DVASPECT_CONTENT};
use winapi::um::objidl::{FORMATETC, TYMED_HGLOBAL};
use winapi::um::shellapi::DragQueryFileW;
use winapi::um::winuser::CF_HDROP;
use winapi::{
shared::{
winerror::{DV_E_FORMATETC, SUCCEEDED},
wtypes::{CLIPFORMAT, DVASPECT_CONTENT},
},
um::{
objidl::{FORMATETC, TYMED_HGLOBAL},
shellapi::DragQueryFileW,
winuser::CF_HDROP,
},
};
let mut drop_format = FORMATETC {
cfFormat: CF_HDROP as CLIPFORMAT,
@ -174,7 +189,7 @@ impl FileDropHandler {
tymed: TYMED_HGLOBAL,
};
let mut medium = mem::uninitialized();
let mut medium = std::mem::zeroed();
let get_data_result = (*data_obj).GetData(&mut drop_format, &mut medium);
if SUCCEEDED(get_data_result) {
let hglobal = (*medium.u).hGlobal();
@ -183,15 +198,19 @@ impl FileDropHandler {
// The second parameter (0xFFFFFFFF) instructs the function to return the item count
let item_count = DragQueryFileW(hdrop, 0xFFFFFFFF, ptr::null_mut(), 0);
let mut pathbuf: [wchar_t; MAX_PATH] = mem::uninitialized();
for i in 0..item_count {
let character_count =
DragQueryFileW(hdrop, i, pathbuf.as_mut_ptr(), MAX_PATH as UINT) as usize;
// Get the length of the path string NOT including the terminating null character.
// Previously, this was using a fixed size array of MAX_PATH length, but the
// Windows API allows longer paths under certain circumstances.
let character_count = DragQueryFileW(hdrop, i, ptr::null_mut(), 0) as usize;
let str_len = character_count + 1;
if character_count > 0 {
callback(OsString::from_wide(&pathbuf[0..character_count]).into());
}
// Fill path_buf with the null-terminated file name
let mut path_buf = Vec::with_capacity(str_len);
DragQueryFileW(hdrop, i, path_buf.as_mut_ptr(), str_len as UINT);
path_buf.set_len(str_len);
callback(OsString::from_wide(&path_buf[0..character_count]).into());
}
return Some(hdrop);

View file

@ -1,16 +1,19 @@
use std::{char, ptr};
use std::os::raw::c_int;
use std::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
use std::{
char,
os::raw::c_int,
ptr,
sync::atomic::{AtomicBool, AtomicPtr, Ordering},
};
use event::{ScanCode, ModifiersState, VirtualKeyCode};
use crate::event::{ModifiersState, ScanCode, VirtualKeyCode};
use winapi::shared::minwindef::{WPARAM, LPARAM, UINT, HKL, HKL__};
use winapi::um::winuser;
use winapi::{
shared::minwindef::{HKL, HKL__, LPARAM, UINT, WPARAM},
um::winuser,
};
fn key_pressed(vkey: c_int) -> bool {
unsafe {
(winuser::GetKeyState(vkey) & (1 << 15)) == (1 << 15)
}
unsafe { (winuser::GetKeyState(vkey) & (1 << 15)) == (1 << 15) }
}
pub fn get_key_mods() -> ModifiersState {
@ -26,9 +29,19 @@ pub fn get_key_mods() -> ModifiersState {
unsafe fn get_char(keyboard_state: &[u8; 256], v_key: u32, hkl: HKL) -> Option<char> {
let mut unicode_bytes = [0u16; 5];
let len = winuser::ToUnicodeEx(v_key, 0, keyboard_state.as_ptr(), unicode_bytes.as_mut_ptr(), unicode_bytes.len() as _, 0, hkl);
let len = winuser::ToUnicodeEx(
v_key,
0,
keyboard_state.as_ptr(),
unicode_bytes.as_mut_ptr(),
unicode_bytes.len() as _,
0,
hkl,
);
if len >= 1 {
char::decode_utf16(unicode_bytes.into_iter().cloned()).next().and_then(|c| c.ok())
char::decode_utf16(unicode_bytes.into_iter().cloned())
.next()
.and_then(|c| c.ok())
} else {
None
}
@ -239,7 +252,7 @@ pub fn vkey_to_winit_vkey(vkey: c_int) -> Option<VirtualKeyCode> {
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_8 => Some(VirtualKeyCode::Oem_8), */
winuser::VK_OEM_102 => Some(VirtualKeyCode::OEM102),
/*winuser::VK_PROCESSKEY => Some(VirtualKeyCode::Processkey),
winuser::VK_PACKET => Some(VirtualKeyCode::Packet),
@ -252,49 +265,61 @@ pub fn vkey_to_winit_vkey(vkey: c_int) -> Option<VirtualKeyCode> {
winuser::VK_NONAME => Some(VirtualKeyCode::Noname),
winuser::VK_PA1 => Some(VirtualKeyCode::Pa1),
winuser::VK_OEM_CLEAR => Some(VirtualKeyCode::Oem_clear),*/
_ => None
_ => None,
}
}
pub fn handle_extended_keys(vkey: c_int, mut scancode: UINT, extended: bool) -> Option<(c_int, UINT)> {
pub fn handle_extended_keys(
vkey: c_int,
mut scancode: UINT,
extended: bool,
) -> Option<(c_int, UINT)> {
// Welcome to hell https://blog.molecular-matters.com/2011/09/05/properly-handling-keyboard-input/
let vkey = 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_SHIFT => unsafe {
winuser::MapVirtualKeyA(scancode, winuser::MAPVK_VSC_TO_VK_EX) as _
},
winuser::VK_MENU => if extended {
winuser::VK_RMENU
} else {
winuser::VK_LMENU
},
_ => match scancode {
// This is only triggered when using raw input. Without this check, we get two events whenever VK_PAUSE is
// pressed, the first one having scancode 0x1D but vkey VK_PAUSE...
0x1D if vkey == winuser::VK_PAUSE => return None,
// ...and the second having scancode 0x45 but an unmatched vkey!
0x45 => winuser::VK_PAUSE,
// VK_PAUSE and VK_SCROLL have the same scancode when using modifiers, alongside incorrect vkey values.
0x46 => {
if extended {
scancode = 0x45;
winuser::VK_PAUSE
} else {
winuser::VK_SCROLL
winuser::VK_CONTROL => {
if extended {
winuser::VK_RCONTROL
} else {
winuser::VK_LCONTROL
}
}
winuser::VK_MENU => {
if extended {
winuser::VK_RMENU
} else {
winuser::VK_LMENU
}
}
_ => {
match scancode {
// This is only triggered when using raw input. Without this check, we get two events whenever VK_PAUSE is
// pressed, the first one having scancode 0x1D but vkey VK_PAUSE...
0x1D if vkey == winuser::VK_PAUSE => return None,
// ...and the second having scancode 0x45 but an unmatched vkey!
0x45 => winuser::VK_PAUSE,
// VK_PAUSE and VK_SCROLL have the same scancode when using modifiers, alongside incorrect vkey values.
0x46 => {
if extended {
scancode = 0x45;
winuser::VK_PAUSE
} else {
winuser::VK_SCROLL
}
}
},
_ => vkey,
},
_ => vkey,
}
}
};
Some((vkey, scancode))
}
pub fn process_key_params(wparam: WPARAM, lparam: LPARAM) -> Option<(ScanCode, Option<VirtualKeyCode>)> {
pub fn process_key_params(
wparam: WPARAM,
lparam: LPARAM,
) -> Option<(ScanCode, Option<VirtualKeyCode>)> {
let scancode = ((lparam >> 16) & 0xff) as UINT;
let extended = (lparam & 0x01000000) != 0;
handle_extended_keys(wparam as _, scancode, extended)
@ -304,7 +329,9 @@ pub fn process_key_params(wparam: WPARAM, lparam: LPARAM) -> Option<(ScanCode, O
// 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, winuser::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),
@ -313,6 +340,6 @@ fn map_text_keys(win_virtual_key: i32) -> Option<VirtualKeyCode> {
Some(']') => Some(VirtualKeyCode::RBracket),
Some('\'') => Some(VirtualKeyCode::Apostrophe),
Some('\\') => Some(VirtualKeyCode::Backslash),
_ => None
_ => None,
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,15 @@
use std::{mem, ptr, io};
use std::os::windows::ffi::OsStrExt;
use std::path::Path;
use std::{io, mem, os::windows::ffi::OsStrExt, path::Path, ptr};
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 winapi::{
ctypes::{c_int, wchar_t},
shared::{
minwindef::{BYTE, LPARAM, WPARAM},
windef::{HICON, HWND},
},
um::winuser,
};
use icon::{Pixel, PIXEL_SIZE, Icon};
use crate::icon::{Icon, Pixel, PIXEL_SIZE};
impl Pixel {
fn to_bgra(&mut self) {
@ -103,11 +105,6 @@ impl Drop for WinIcon {
pub fn unset_for_window(hwnd: HWND, icon_type: IconType) {
unsafe {
winuser::SendMessageW(
hwnd,
winuser::WM_SETICON,
icon_type as WPARAM,
0 as LPARAM,
);
winuser::SendMessageW(hwnd, winuser::WM_SETICON, icon_type as WPARAM, 0 as LPARAM);
}
}

View file

@ -1,14 +1,14 @@
#![cfg(target_os = "windows")]
use winapi;
use winapi::shared::windef::HWND;
use winapi::{self, shared::windef::HWND};
pub use self::event_loop::{EventLoop, EventLoopWindowTarget, EventLoopProxy};
pub use self::monitor::MonitorHandle;
pub use self::window::Window;
pub use self::{
event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget},
monitor::MonitorHandle,
window::Window,
};
use window::Icon;
use event::DeviceId as RootDeviceId;
use crate::{event::DeviceId as RootDeviceId, window::Icon};
#[derive(Clone, Default)]
pub struct PlatformSpecificWindowBuilderAttributes {

View file

@ -1,21 +1,34 @@
use winapi::shared::minwindef::{BOOL, DWORD, LPARAM, TRUE};
use winapi::shared::windef::{HDC, HMONITOR, HWND, LPRECT, POINT};
use winapi::um::winnt::LONG;
use winapi::um::winuser;
use winapi::{
shared::{
minwindef::{BOOL, DWORD, LPARAM, TRUE, WORD},
windef::{HDC, HMONITOR, HWND, LPRECT, POINT},
},
um::{wingdi, winnt::LONG, winuser},
};
use std::{mem, ptr, io};
use std::collections::VecDeque;
use std::{
collections::{HashSet, VecDeque},
io, mem, ptr,
};
use super::{EventLoop, util};
use dpi::{PhysicalPosition, PhysicalSize};
use platform_impl::platform::dpi::{dpi_to_scale_factor, get_monitor_dpi};
use platform_impl::platform::window::Window;
use super::{util, EventLoop};
use crate::{
dpi::{PhysicalPosition, PhysicalSize},
monitor::VideoMode,
platform_impl::platform::{
dpi::{dpi_to_scale_factor, get_monitor_dpi},
window::Window,
},
};
/// Win32 implementation of the main `MonitorHandle` object.
#[derive(Debug, Clone)]
#[derive(Derivative)]
#[derivative(Debug, Clone)]
pub struct MonitorHandle {
/// Monitor handle.
hmonitor: HMonitor,
#[derivative(Debug = "ignore")]
monitor_info: winuser::MONITORINFOEXW,
/// The system name of the monitor.
monitor_name: String,
/// True if this is the primary monitor.
@ -65,16 +78,12 @@ pub fn available_monitors() -> VecDeque<MonitorHandle> {
pub fn primary_monitor() -> MonitorHandle {
const ORIGIN: POINT = POINT { x: 0, y: 0 };
let hmonitor = unsafe {
winuser::MonitorFromPoint(ORIGIN, winuser::MONITOR_DEFAULTTOPRIMARY)
};
let hmonitor = unsafe { winuser::MonitorFromPoint(ORIGIN, winuser::MONITOR_DEFAULTTOPRIMARY) };
MonitorHandle::from_hmonitor(hmonitor)
}
pub fn current_monitor(hwnd: HWND) -> MonitorHandle {
let hmonitor = unsafe {
winuser::MonitorFromWindow(hwnd, winuser::MONITOR_DEFAULTTONEAREST)
};
let hmonitor = unsafe { winuser::MonitorFromWindow(hwnd, winuser::MONITOR_DEFAULTTONEAREST) };
MonitorHandle::from_hmonitor(hmonitor)
}
@ -100,7 +109,7 @@ impl Window {
}
pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result<winuser::MONITORINFOEXW, io::Error> {
let mut monitor_info: winuser::MONITORINFOEXW = unsafe { mem::uninitialized() };
let mut monitor_info: winuser::MONITORINFOEXW = unsafe { mem::zeroed() };
monitor_info.cbSize = mem::size_of::<winuser::MONITORINFOEXW>() as DWORD;
let status = unsafe {
winuser::GetMonitorInfoW(
@ -130,6 +139,7 @@ impl MonitorHandle {
position: (place.left as i32, place.top as i32),
dimensions,
hidpi_factor: dpi_to_scale_factor(get_monitor_dpi(hmonitor).unwrap_or(96)),
monitor_info,
}
}
@ -157,7 +167,7 @@ impl MonitorHandle {
}
#[inline]
pub fn dimensions(&self) -> PhysicalSize {
pub fn size(&self) -> PhysicalSize {
self.dimensions.into()
}
@ -170,4 +180,39 @@ impl MonitorHandle {
pub fn hidpi_factor(&self) -> f64 {
self.hidpi_factor
}
#[inline]
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
// EnumDisplaySettingsExW can return duplicate values (or some of the
// fields are probably changing, but we aren't looking at those fields
// anyway), so we're using a HashSet deduplicate
let mut modes = HashSet::new();
let mut i = 0;
loop {
unsafe {
let device_name = self.monitor_info.szDevice.as_ptr();
let mut mode: wingdi::DEVMODEW = mem::zeroed();
mode.dmSize = mem::size_of_val(&mode) as WORD;
if winuser::EnumDisplaySettingsExW(device_name, i, &mut mode, 0) == 0 {
break;
}
i += 1;
const REQUIRED_FIELDS: DWORD = wingdi::DM_BITSPERPEL
| wingdi::DM_PELSWIDTH
| wingdi::DM_PELSHEIGHT
| wingdi::DM_DISPLAYFREQUENCY;
assert!(mode.dmFields & REQUIRED_FIELDS == REQUIRED_FIELDS);
modes.insert(VideoMode {
size: (mode.dmPelsWidth, mode.dmPelsHeight),
bit_depth: mode.dmBitsPerPel as u16,
refresh_rate: mode.dmDisplayFrequency as u16,
});
}
}
modes.into_iter()
}
}

View file

@ -1,49 +1,35 @@
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 std::{
mem::{self, size_of},
ptr,
};
use platform_impl::platform::util;
use event::ElementState;
use winapi::{
ctypes::wchar_t,
shared::{
hidusage::{HID_USAGE_GENERIC_KEYBOARD, HID_USAGE_GENERIC_MOUSE, HID_USAGE_PAGE_GENERIC},
minwindef::{TRUE, UINT, USHORT},
windef::HWND,
},
um::{
winnt::HANDLE,
winuser::{
self, HRAWINPUT, RAWINPUT, RAWINPUTDEVICE, RAWINPUTDEVICELIST, RAWINPUTHEADER,
RIDEV_DEVNOTIFY, RIDEV_INPUTSINK, RIDI_DEVICEINFO, RIDI_DEVICENAME, RID_DEVICE_INFO,
RID_DEVICE_INFO_HID, RID_DEVICE_INFO_KEYBOARD, RID_DEVICE_INFO_MOUSE, RID_INPUT,
RIM_TYPEHID, RIM_TYPEKEYBOARD, RIM_TYPEMOUSE,
},
},
};
use crate::{event::ElementState, platform_impl::platform::util};
#[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,
) };
let status =
unsafe { winuser::GetRawInputDeviceList(ptr::null_mut(), &mut num_devices, list_size) };
if status == UINT::max_value() {
return None;
@ -51,11 +37,9 @@ pub fn get_raw_input_device_list() -> Option<Vec<RAWINPUTDEVICELIST>> {
let mut buffer = Vec::with_capacity(num_devices as _);
let num_stored = unsafe { winuser::GetRawInputDeviceList(
buffer.as_ptr() as _,
&mut num_devices,
list_size,
) };
let num_stored = unsafe {
winuser::GetRawInputDeviceList(buffer.as_ptr() as _, &mut num_devices, list_size)
};
if num_stored == UINT::max_value() {
return None;
@ -90,18 +74,20 @@ impl From<RID_DEVICE_INFO> for RawDeviceInfo {
#[allow(dead_code)]
pub fn get_raw_input_device_info(handle: HANDLE) -> Option<RawDeviceInfo> {
let mut info: RID_DEVICE_INFO = unsafe { mem::uninitialized() };
let mut info: RID_DEVICE_INFO = unsafe { mem::zeroed() };
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,
) };
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;
@ -114,12 +100,9 @@ pub fn get_raw_input_device_info(handle: HANDLE) -> Option<RawDeviceInfo> {
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,
) };
let status = unsafe {
winuser::GetRawInputDeviceInfoW(handle, RIDI_DEVICENAME, ptr::null_mut(), &mut minimum_size)
};
if status != 0 {
return None;
@ -127,12 +110,14 @@ pub fn get_raw_input_device_name(handle: HANDLE) -> Option<String> {
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,
) };
let status = unsafe {
winuser::GetRawInputDeviceInfoW(
handle,
RIDI_DEVICENAME,
name.as_ptr() as _,
&mut minimum_size,
)
};
if status == UINT::max_value() || status == 0 {
return None;
@ -148,11 +133,9 @@ pub fn get_raw_input_device_name(handle: HANDLE) -> Option<String> {
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,
) };
let success = unsafe {
winuser::RegisterRawInputDevices(devices.as_ptr() as _, devices.len() as _, device_size)
};
success == TRUE
}
@ -181,17 +164,19 @@ pub fn register_all_mice_and_keyboards_for_raw_input(window_handle: HWND) -> boo
}
pub fn get_raw_input_data(handle: HRAWINPUT) -> Option<RAWINPUT> {
let mut data: RAWINPUT = unsafe { mem::uninitialized() };
let mut data: RAWINPUT = unsafe { mem::zeroed() };
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,
) };
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;
@ -200,10 +185,11 @@ pub fn get_raw_input_data(handle: HRAWINPUT) -> Option<RAWINPUT> {
Some(data)
}
fn button_flags_to_element_state(button_flags: USHORT, down_flag: USHORT, up_flag: USHORT)
-> Option<ElementState>
{
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)

View file

@ -1,17 +1,23 @@
use std::{mem, ptr, slice, io};
use std::ops::BitAnd;
use std::sync::atomic::{AtomicBool, Ordering};
use std::{
io, mem,
ops::BitAnd,
ptr, slice,
sync::atomic::{AtomicBool, Ordering},
};
use window::CursorIcon;
use winapi::ctypes::wchar_t;
use winapi::shared::minwindef::{BOOL, DWORD};
use winapi::shared::windef::{HWND, POINT, RECT};
use winapi::um::winbase::lstrlenW;
use winapi::um::winuser;
use crate::window::CursorIcon;
use winapi::{
ctypes::wchar_t,
shared::{
minwindef::{BOOL, DWORD},
windef::{HWND, POINT, RECT},
},
um::{winbase::lstrlenW, winuser},
};
pub fn has_flag<T>(bitset: T, flag: T) -> bool
where T:
Copy + PartialEq + BitAnd<T, Output = T>
where
T: Copy + PartialEq + BitAnd<T, Output = T>,
{
bitset & flag == flag
}
@ -27,7 +33,7 @@ pub fn wchar_ptr_to_string(wchar: *const wchar_t) -> String {
}
pub unsafe fn status_map<T, F: FnMut(&mut T) -> BOOL>(mut fun: F) -> Option<T> {
let mut data: T = mem::uninitialized();
let mut data: T = mem::zeroed();
if fun(&mut data) != 0 {
Some(data)
} else {
@ -53,7 +59,7 @@ pub fn get_window_rect(hwnd: HWND) -> Option<RECT> {
pub fn get_client_rect(hwnd: HWND) -> Result<RECT, io::Error> {
unsafe {
let mut rect = mem::uninitialized();
let mut rect = mem::zeroed();
let mut top_left = mem::zeroed();
win_to_err(|| winuser::ClientToScreen(hwnd, &mut top_left))?;
@ -75,32 +81,42 @@ pub fn adjust_window_rect(hwnd: HWND, rect: RECT) -> Option<RECT> {
}
}
pub fn adjust_window_rect_with_styles(hwnd: HWND, style: DWORD, style_ex: DWORD, rect: RECT) -> Option<RECT> {
unsafe { status_map(|r| {
*r = rect;
pub fn adjust_window_rect_with_styles(
hwnd: HWND,
style: DWORD,
style_ex: DWORD,
rect: RECT,
) -> Option<RECT> {
unsafe {
status_map(|r| {
*r = rect;
let b_menu = !winuser::GetMenu(hwnd).is_null() as BOOL;
winuser::AdjustWindowRectEx(r, style as _ , b_menu, style_ex as _)
}) }
let b_menu = !winuser::GetMenu(hwnd).is_null() as BOOL;
winuser::AdjustWindowRectEx(r, style as _, b_menu, style_ex as _)
})
}
}
pub fn set_cursor_hidden(hidden: bool) {
static HIDDEN: AtomicBool = AtomicBool::new(false);
let changed = HIDDEN.swap(hidden, Ordering::SeqCst) ^ hidden;
if changed {
unsafe{ winuser::ShowCursor(!hidden as BOOL) };
unsafe { winuser::ShowCursor(!hidden as BOOL) };
}
}
pub fn set_cursor_clip(rect: Option<RECT>) -> Result<(), io::Error> {
unsafe {
let rect_ptr = rect.as_ref().map(|r| r as *const RECT).unwrap_or(ptr::null());
let rect_ptr = rect
.as_ref()
.map(|r| r as *const RECT)
.unwrap_or(ptr::null());
win_to_err(|| winuser::ClipCursor(rect_ptr))
}
}
pub fn is_focused(window: HWND) -> bool {
window == unsafe{ winuser::GetActiveWindow() }
window == unsafe { winuser::GetActiveWindow() }
}
impl CursorIcon {
@ -111,16 +127,23 @@ impl CursorIcon {
CursorIcon::Crosshair => winuser::IDC_CROSS,
CursorIcon::Text | CursorIcon::VerticalText => winuser::IDC_IBEAM,
CursorIcon::NotAllowed | CursorIcon::NoDrop => winuser::IDC_NO,
CursorIcon::Grab | CursorIcon::Grabbing |
CursorIcon::Move | CursorIcon::AllScroll => winuser::IDC_SIZEALL,
CursorIcon::EResize | CursorIcon::WResize |
CursorIcon::EwResize | CursorIcon::ColResize => winuser::IDC_SIZEWE,
CursorIcon::NResize | CursorIcon::SResize |
CursorIcon::NsResize | CursorIcon::RowResize => winuser::IDC_SIZENS,
CursorIcon::NeResize | CursorIcon::SwResize |
CursorIcon::NeswResize => winuser::IDC_SIZENESW,
CursorIcon::NwResize | CursorIcon::SeResize |
CursorIcon::NwseResize => winuser::IDC_SIZENWSE,
CursorIcon::Grab | CursorIcon::Grabbing | CursorIcon::Move | CursorIcon::AllScroll => {
winuser::IDC_SIZEALL
}
CursorIcon::EResize
| CursorIcon::WResize
| CursorIcon::EwResize
| CursorIcon::ColResize => winuser::IDC_SIZEWE,
CursorIcon::NResize
| CursorIcon::SResize
| CursorIcon::NsResize
| CursorIcon::RowResize => winuser::IDC_SIZENS,
CursorIcon::NeResize | CursorIcon::SwResize | CursorIcon::NeswResize => {
winuser::IDC_SIZENESW
}
CursorIcon::NwResize | CursorIcon::SeResize | CursorIcon::NwseResize => {
winuser::IDC_SIZENWSE
}
CursorIcon::Wait => winuser::IDC_WAIT,
CursorIcon::Progress => winuser::IDC_APPSTARTING,
CursorIcon::Help => winuser::IDC_HELP,

View file

@ -1,37 +1,52 @@
#![cfg(target_os = "windows")]
use std::{io, mem, ptr};
use std::cell::Cell;
use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt;
use std::sync::Arc;
use std::sync::mpsc::channel;
use parking_lot::Mutex;
use std::{
cell::Cell,
ffi::OsStr,
io, mem,
os::windows::ffi::OsStrExt,
ptr,
sync::{mpsc::channel, Arc},
};
use winapi::ctypes::c_int;
use winapi::shared::minwindef::{DWORD, LPARAM, UINT, WORD, WPARAM};
use winapi::shared::windef::{HWND, POINT, RECT};
use winapi::um::{combaseapi, dwmapi, libloaderapi, ole2, winuser};
use winapi::um::objbase::COINIT_APARTMENTTHREADED;
use winapi::um::shobjidl_core::{CLSID_TaskbarList, ITaskbarList2};
use winapi::um::wingdi::{CreateRectRgn, DeleteObject};
use winapi::um::oleidl::LPDROPTARGET;
use winapi::um::winnt::{LONG, LPCWSTR};
use winapi::{
ctypes::c_int,
shared::{
minwindef::{DWORD, LPARAM, UINT, WORD, WPARAM},
windef::{HWND, POINT, RECT},
},
um::{
combaseapi, dwmapi, libloaderapi,
objbase::COINIT_APARTMENTTHREADED,
ole2,
oleidl::LPDROPTARGET,
shobjidl_core::{CLSID_TaskbarList, ITaskbarList2},
wingdi::{CreateRectRgn, DeleteObject},
winnt::{LONG, LPCWSTR},
winuser,
},
};
use window::{Icon, CursorIcon, WindowAttributes};
use error::{ExternalError, NotSupportedError, OsError as RootOsError};
use dpi::{LogicalPosition, LogicalSize, PhysicalSize};
use monitor::MonitorHandle as RootMonitorHandle;
use platform_impl::platform::{
{PlatformSpecificWindowBuilderAttributes, WindowId},
dpi::{dpi_to_scale_factor, hwnd_dpi},
drop_handler::FileDropHandler,
event_loop::{self, EventLoopWindowTarget, DESTROY_MSG_ID, INITIAL_DPI_MSG_ID, REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID},
icon::{self, IconType, WinIcon},
monitor,
raw_input::register_all_mice_and_keyboards_for_raw_input,
util,
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
use crate::{
dpi::{LogicalPosition, LogicalSize, PhysicalSize},
error::{ExternalError, NotSupportedError, OsError as RootOsError},
monitor::MonitorHandle as RootMonitorHandle,
platform_impl::platform::{
dpi::{dpi_to_scale_factor, hwnd_dpi},
drop_handler::FileDropHandler,
event_loop::{
self, EventLoopWindowTarget, DESTROY_MSG_ID, INITIAL_DPI_MSG_ID,
REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID,
},
icon::{self, IconType, WinIcon},
monitor,
raw_input::register_all_mice_and_keyboards_for_raw_input,
util,
window_state::{CursorFlags, SavedWindow, WindowFlags, WindowState},
PlatformSpecificWindowBuilderAttributes, WindowId,
},
window::{CursorIcon, Icon, WindowAttributes},
};
/// The Win32 implementation of the main `Window` object.
@ -73,11 +88,19 @@ impl Window {
let file_drop_runner = event_loop.runner_shared.clone();
let file_drop_handler = FileDropHandler::new(
win.window.0,
Box::new(move |event| if let Ok(e) = event.map_nonuser_event() {file_drop_runner.send_event(e)})
Box::new(move |event| {
if let Ok(e) = event.map_nonuser_event() {
file_drop_runner.send_event(e)
}
}),
);
let handler_interface_ptr = &mut (*file_drop_handler.data).interface as LPDROPTARGET;
let handler_interface_ptr =
&mut (*file_drop_handler.data).interface as LPDROPTARGET;
assert_eq!(ole2::RegisterDragDrop(win.window.0, handler_interface_ptr), S_OK);
assert_eq!(
ole2::RegisterDragDrop(win.window.0, handler_interface_ptr),
S_OK
);
file_drop_handler
};
@ -106,8 +129,12 @@ impl Window {
#[inline]
pub fn set_visible(&self, visible: bool) {
match visible {
true => unsafe { winuser::ShowWindow(self.window.0, winuser::SW_SHOW); },
false => unsafe { winuser::ShowWindow(self.window.0, winuser::SW_HIDE); },
true => unsafe {
winuser::ShowWindow(self.window.0, winuser::SW_SHOW);
},
false => unsafe {
winuser::ShowWindow(self.window.0, winuser::SW_HIDE);
},
}
}
@ -119,10 +146,14 @@ impl Window {
self.window.0,
ptr::null(),
ptr::null_mut(),
winuser::RDW_INTERNALPAINT
winuser::RDW_INTERNALPAINT,
);
} else {
winuser::PostMessageW(self.window.0, *REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID, 0, 0);
let mut window_state = self.window_state.lock();
if !window_state.queued_out_of_band_redraw {
window_state.queued_out_of_band_redraw = true;
winuser::PostMessageW(self.window.0, *REQUEST_REDRAW_NO_NEWEVENTS_MSG_ID, 0, 0);
}
}
}
}
@ -137,7 +168,10 @@ impl Window {
pub fn outer_position(&self) -> Result<LogicalPosition, NotSupportedError> {
let physical_position = self.outer_position_physical();
let dpi_factor = self.hidpi_factor();
Ok(LogicalPosition::from_physical(physical_position, dpi_factor))
Ok(LogicalPosition::from_physical(
physical_position,
dpi_factor,
))
}
pub(crate) fn inner_position_physical(&self) -> (i32, i32) {
@ -152,7 +186,10 @@ impl Window {
pub fn inner_position(&self) -> Result<LogicalPosition, NotSupportedError> {
let physical_position = self.inner_position_physical();
let dpi_factor = self.hidpi_factor();
Ok(LogicalPosition::from_physical(physical_position, dpi_factor))
Ok(LogicalPosition::from_physical(
physical_position,
dpi_factor,
))
}
pub(crate) fn set_position_physical(&self, x: i32, y: i32) {
@ -178,7 +215,7 @@ impl Window {
}
pub(crate) fn inner_size_physical(&self) -> (u32, u32) {
let mut rect: RECT = unsafe { mem::uninitialized() };
let mut rect: RECT = unsafe { mem::zeroed() };
if unsafe { winuser::GetClientRect(self.window.0, &mut rect) } == 0 {
panic!("Unexpected GetClientRect failure: please report this error to https://github.com/rust-windowing/winit")
}
@ -197,10 +234,12 @@ impl Window {
pub(crate) fn outer_size_physical(&self) -> (u32, u32) {
util::get_window_rect(self.window.0)
.map(|rect| (
(rect.right - rect.left) as u32,
(rect.bottom - rect.top) as u32,
))
.map(|rect| {
(
(rect.right - rect.left) as u32,
(rect.bottom - rect.top) as u32,
)
})
.unwrap()
}
@ -220,8 +259,9 @@ impl Window {
left: 0,
bottom: y as LONG,
right: x as LONG,
}
).expect("adjust_window_rect failed");
},
)
.expect("adjust_window_rect failed");
let outer_x = (rect.right - rect.left).abs() as c_int;
let outer_y = (rect.top - rect.bottom).abs() as c_int;
@ -233,9 +273,10 @@ impl Window {
outer_x,
outer_y,
winuser::SWP_ASYNCWINDOWPOS
| winuser::SWP_NOZORDER
| winuser::SWP_NOREPOSITION
| winuser::SWP_NOMOVE,
| winuser::SWP_NOZORDER
| winuser::SWP_NOREPOSITION
| winuser::SWP_NOMOVE
| winuser::SWP_NOACTIVATE,
);
winuser::UpdateWindow(self.window.0);
}
@ -286,12 +327,9 @@ impl Window {
let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || {
WindowState::set_window_flags(
window_state.lock(),
window.0,
None,
|f| f.set(WindowFlags::RESIZABLE, resizable),
);
WindowState::set_window_flags(window_state.lock(), window.0, None, |f| {
f.set(WindowFlags::RESIZABLE, resizable)
});
});
}
@ -305,10 +343,7 @@ impl Window {
pub fn set_cursor_icon(&self, cursor: CursorIcon) {
self.window_state.lock().mouse.cursor = cursor;
self.thread_executor.execute_in_thread(move || unsafe {
let cursor = winuser::LoadCursorW(
ptr::null_mut(),
cursor.to_windows_cursor(),
);
let cursor = winuser::LoadCursorW(ptr::null_mut(), cursor.to_windows_cursor());
winuser::SetCursor(cursor);
});
}
@ -320,7 +355,9 @@ impl Window {
let (tx, rx) = channel();
self.thread_executor.execute_in_thread(move || {
let result = window_state.lock().mouse
let result = window_state
.lock()
.mouse
.set_cursor_flags(window.0, |f| f.set(CursorFlags::GRABBED, grab))
.map_err(|e| ExternalError::Os(os_error!(e)));
let _ = tx.send(result);
@ -335,7 +372,9 @@ impl Window {
let (tx, rx) = channel();
self.thread_executor.execute_in_thread(move || {
let result = window_state.lock().mouse
let result = window_state
.lock()
.mouse
.set_cursor_flags(window.0, |f| f.set(CursorFlags::HIDDEN, !visible))
.map_err(|e| e.to_string());
let _ = tx.send(result);
@ -362,7 +401,10 @@ impl Window {
}
#[inline]
pub fn set_cursor_position(&self, logical_position: LogicalPosition) -> Result<(), ExternalError> {
pub fn set_cursor_position(
&self,
logical_position: LogicalPosition,
) -> Result<(), ExternalError> {
let dpi_factor = self.hidpi_factor();
let (x, y) = logical_position.to_physical(dpi_factor).into();
self.set_cursor_position_physical(x, y)
@ -379,12 +421,9 @@ impl Window {
let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || {
WindowState::set_window_flags(
window_state.lock(),
window.0,
None,
|f| f.set(WindowFlags::MAXIMIZED, maximized),
);
WindowState::set_window_flags(window_state.lock(), window.0, None, |f| {
f.set(WindowFlags::MAXIMIZED, maximized)
});
});
}
@ -403,16 +442,17 @@ impl Window {
match &monitor {
&Some(RootMonitorHandle { ref inner }) => {
let (x, y): (i32, i32) = inner.position().into();
let (width, height): (u32, u32) = inner.dimensions().into();
let (width, height): (u32, u32) = inner.size().into();
let mut monitor = monitor.clone();
self.thread_executor.execute_in_thread(move || {
let mut window_state_lock = window_state.lock();
let client_rect = util::get_client_rect(window.0).expect("get client rect failed!");
let client_rect =
util::get_client_rect(window.0).expect("get client rect failed!");
window_state_lock.saved_window = Some(SavedWindow {
client_rect,
dpi_factor: window_state_lock.dpi_factor
dpi_factor: window_state_lock.dpi_factor,
});
window_state_lock.fullscreen = monitor.take();
@ -424,7 +464,7 @@ impl Window {
top: y,
right: x + width as c_int,
bottom: y + height as c_int,
})
}),
);
mark_fullscreen(window.0, true);
@ -435,14 +475,18 @@ impl Window {
let mut window_state_lock = window_state.lock();
window_state_lock.fullscreen = None;
if let Some(SavedWindow{client_rect, dpi_factor}) = window_state_lock.saved_window {
if let Some(SavedWindow {
client_rect,
dpi_factor,
}) = window_state_lock.saved_window
{
window_state_lock.dpi_factor = dpi_factor;
window_state_lock.saved_window = None;
WindowState::refresh_window_state(
window_state_lock,
window.0,
Some(client_rect)
Some(client_rect),
);
}
@ -460,12 +504,9 @@ impl Window {
self.thread_executor.execute_in_thread(move || {
let client_rect = util::get_client_rect(window.0).expect("get client rect failed!");
WindowState::set_window_flags(
window_state.lock(),
window.0,
Some(client_rect),
|f| f.set(WindowFlags::DECORATIONS, decorations),
);
WindowState::set_window_flags(window_state.lock(), window.0, Some(client_rect), |f| {
f.set(WindowFlags::DECORATIONS, decorations)
});
});
}
@ -475,12 +516,9 @@ impl Window {
let window_state = Arc::clone(&self.window_state);
self.thread_executor.execute_in_thread(move || {
WindowState::set_window_flags(
window_state.lock(),
window.0,
None,
|f| f.set(WindowFlags::ALWAYS_ON_TOP, always_on_top),
);
WindowState::set_window_flags(window_state.lock(), window.0, None, |f| {
f.set(WindowFlags::ALWAYS_ON_TOP, always_on_top)
});
});
}
@ -573,9 +611,7 @@ unsafe fn init<T: 'static>(
.collect::<Vec<_>>();
let window_icon = {
let icon = attributes.window_icon
.take()
.map(WinIcon::from_icon);
let icon = attributes.window_icon.take().map(WinIcon::from_icon);
if let Some(icon) = icon {
Some(icon.map_err(|e| os_error!(e))?)
} else {
@ -583,9 +619,7 @@ unsafe fn init<T: 'static>(
}
};
let taskbar_icon = {
let icon = attributes.window_icon
.take()
.map(WinIcon::from_icon);
let icon = attributes.window_icon.take().map(WinIcon::from_icon);
if let Some(icon) = icon {
Some(icon.map_err(|e| os_error!(e))?)
} else {
@ -607,7 +641,10 @@ unsafe fn init<T: 'static>(
}
dpi_factor
} else {
return Err(os_error!(io::Error::new(io::ErrorKind::NotFound, "No monitors were detected.")));
return Err(os_error!(io::Error::new(
io::ErrorKind::NotFound,
"No monitors were detected."
)));
};
dpi_factor.unwrap_or_else(|| {
util::get_cursor_pos()
@ -631,7 +668,10 @@ unsafe fn init<T: 'static>(
let mut window_flags = WindowFlags::empty();
window_flags.set(WindowFlags::DECORATIONS, attributes.decorations);
window_flags.set(WindowFlags::ALWAYS_ON_TOP, attributes.always_on_top);
window_flags.set(WindowFlags::NO_BACK_BUFFER, pl_attribs.no_redirection_bitmap);
window_flags.set(
WindowFlags::NO_BACK_BUFFER,
pl_attribs.no_redirection_bitmap,
);
window_flags.set(WindowFlags::TRANSPARENT, attributes.transparent);
// WindowFlags::VISIBLE and MAXIMIZED are set down below after the window has been configured.
window_flags.set(WindowFlags::RESIZABLE, attributes.resizable);
@ -646,8 +686,10 @@ unsafe fn init<T: 'static>(
class_name.as_ptr(),
title.as_ptr() as LPCWSTR,
style,
winuser::CW_USEDEFAULT, winuser::CW_USEDEFAULT,
winuser::CW_USEDEFAULT, winuser::CW_USEDEFAULT,
winuser::CW_USEDEFAULT,
winuser::CW_USEDEFAULT,
winuser::CW_USEDEFAULT,
winuser::CW_USEDEFAULT,
pl_attribs.parent.unwrap_or(ptr::null_mut()),
ptr::null_mut(),
libloaderapi::GetModuleHandleW(ptr::null()),
@ -666,9 +708,9 @@ unsafe fn init<T: 'static>(
// Register for touch events if applicable
{
let digitizer = winuser::GetSystemMetrics( winuser::SM_DIGITIZER ) as u32;
let digitizer = winuser::GetSystemMetrics(winuser::SM_DIGITIZER) as u32;
if digitizer & winuser::NID_READY != 0 {
winuser::RegisterTouchWindow( real_window.0, winuser::TWF_WANTPALM );
winuser::RegisterTouchWindow(real_window.0, winuser::TWF_WANTPALM);
}
}
@ -712,7 +754,12 @@ unsafe fn init<T: 'static>(
// The color key can be any value except for black (0x0).
let color_key = 0x0030c100;
winuser::SetLayeredWindowAttributes(real_window.0, color_key, opacity, winuser::LWA_ALPHA);
winuser::SetLayeredWindowAttributes(
real_window.0,
color_key,
opacity,
winuser::LWA_ALPHA,
);
}
}
@ -720,19 +767,11 @@ unsafe fn init<T: 'static>(
window_flags.set(WindowFlags::MAXIMIZED, attributes.maximized);
let window_state = {
let window_state = WindowState::new(
&attributes,
window_icon,
taskbar_icon,
dpi_factor,
);
let window_state = WindowState::new(&attributes, window_icon, taskbar_icon, dpi_factor);
let window_state = Arc::new(Mutex::new(window_state));
WindowState::set_window_flags(
window_state.lock(),
real_window.0,
None,
|f| *f = window_flags,
);
WindowState::set_window_flags(window_state.lock(), real_window.0, None, |f| {
*f = window_flags
});
window_state
};
@ -803,7 +842,7 @@ impl Drop for ComInitialized {
}
}
thread_local!{
thread_local! {
static COM_INITIALIZED: ComInitialized = {
unsafe {
combaseapi::CoInitializeEx(ptr::null_mut(), COINIT_APARTMENTTHREADED);
@ -833,8 +872,7 @@ unsafe fn mark_fullscreen(handle: HWND, fullscreen: bool) {
let mut task_bar_list = task_bar_list_ptr.get();
if task_bar_list == ptr::null_mut() {
use winapi::shared::winerror::S_OK;
use winapi::Interface;
use winapi::{shared::winerror::S_OK, Interface};
let hr = combaseapi::CoCreateInstance(
&CLSID_TaskbarList,

View file

@ -1,13 +1,18 @@
use monitor::MonitorHandle;
use window::{CursorIcon, WindowAttributes};
use std::{io, ptr};
use crate::{
dpi::LogicalSize,
monitor::MonitorHandle,
platform_impl::platform::{event_loop, icon::WinIcon, util},
window::{CursorIcon, WindowAttributes},
};
use parking_lot::MutexGuard;
use dpi::LogicalSize;
use platform_impl::platform::{util, event_loop};
use platform_impl::platform::icon::WinIcon;
use winapi::shared::windef::{RECT, HWND};
use winapi::shared::minwindef::DWORD;
use winapi::um::winuser;
use std::{io, ptr};
use winapi::{
shared::{
minwindef::DWORD,
windef::{HWND, RECT},
},
um::winuser,
};
/// Contains information about states and the window that the callback is going to use.
#[derive(Clone)]
@ -25,6 +30,9 @@ pub struct WindowState {
pub dpi_factor: f64,
pub fullscreen: Option<MonitorHandle>,
/// Used to supress duplicate redraw attempts when calling `request_redraw` multiple
/// times in `EventsCleared`.
pub queued_out_of_band_redraw: bool,
window_flags: WindowFlags,
}
@ -86,7 +94,7 @@ impl WindowState {
attributes: &WindowAttributes,
window_icon: Option<WinIcon>,
taskbar_icon: Option<WinIcon>,
dpi_factor: f64
dpi_factor: f64,
) -> WindowState {
WindowState {
mouse: MouseProperties {
@ -105,7 +113,8 @@ impl WindowState {
dpi_factor,
fullscreen: None,
window_flags: WindowFlags::empty()
queued_out_of_band_redraw: false,
window_flags: WindowFlags::empty(),
}
}
@ -113,26 +122,37 @@ impl WindowState {
self.window_flags
}
pub fn set_window_flags<F>(mut this: MutexGuard<Self>, window: HWND, set_client_rect: Option<RECT>, f: F)
where F: FnOnce(&mut WindowFlags)
pub fn set_window_flags<F>(
mut this: MutexGuard<'_, Self>,
window: HWND,
set_client_rect: Option<RECT>,
f: F,
) where
F: FnOnce(&mut WindowFlags),
{
let old_flags = this.window_flags;
f(&mut this.window_flags);
let is_fullscreen = this.fullscreen.is_some();
this.window_flags.set(WindowFlags::MARKER_FULLSCREEN, is_fullscreen);
this.window_flags
.set(WindowFlags::MARKER_FULLSCREEN, is_fullscreen);
let new_flags = this.window_flags;
drop(this);
old_flags.apply_diff(window, new_flags, set_client_rect);
}
pub fn refresh_window_state(this: MutexGuard<Self>, window: HWND, set_client_rect: Option<RECT>) {
pub fn refresh_window_state(
this: MutexGuard<'_, Self>,
window: HWND,
set_client_rect: Option<RECT>,
) {
Self::set_window_flags(this, window, set_client_rect, |_| ());
}
pub fn set_window_flags_in_place<F>(&mut self, f: F)
where F: FnOnce(&mut WindowFlags)
where
F: FnOnce(&mut WindowFlags),
{
f(&mut self.window_flags);
}
@ -144,7 +164,8 @@ impl MouseProperties {
}
pub fn set_cursor_flags<F>(&mut self, window: HWND, f: F) -> Result<(), io::Error>
where F: FnOnce(&mut CursorFlags)
where
F: FnOnce(&mut CursorFlags),
{
let old_flags = self.cursor_flags;
f(&mut self.cursor_flags);
@ -198,9 +219,7 @@ impl WindowFlags {
if self.contains(WindowFlags::NO_BACK_BUFFER) {
style_ex |= WS_EX_NOREDIRECTIONBITMAP;
}
if self.contains(WindowFlags::TRANSPARENT) {
// Is this necessary? The docs say that WS_EX_LAYERED requires a windows class without
// CS_OWNDC, and Winit windows have that flag set.
if self.contains(WindowFlags::TRANSPARENT) && self.contains(WindowFlags::DECORATIONS) {
style_ex |= WS_EX_LAYERED;
}
if self.contains(WindowFlags::CHILD) {
@ -232,8 +251,8 @@ impl WindowFlags {
window,
match new.contains(WindowFlags::VISIBLE) {
true => winuser::SW_SHOW,
false => winuser::SW_HIDE
}
false => winuser::SW_HIDE,
},
);
}
}
@ -242,10 +261,13 @@ impl WindowFlags {
winuser::SetWindowPos(
window,
match new.contains(WindowFlags::ALWAYS_ON_TOP) {
true => winuser::HWND_TOPMOST,
true => winuser::HWND_TOPMOST,
false => winuser::HWND_NOTOPMOST,
},
0, 0, 0, 0,
0,
0,
0,
0,
winuser::SWP_ASYNCWINDOWPOS | winuser::SWP_NOMOVE | winuser::SWP_NOSIZE,
);
winuser::UpdateWindow(window);
@ -258,8 +280,8 @@ impl WindowFlags {
window,
match new.contains(WindowFlags::MAXIMIZED) {
true => winuser::SW_MAXIMIZE,
false => winuser::SW_RESTORE
}
false => winuser::SW_RESTORE,
},
);
}
}
@ -273,7 +295,9 @@ impl WindowFlags {
winuser::SetWindowLongW(window, winuser::GWL_STYLE, style as _);
winuser::SetWindowLongW(window, winuser::GWL_EXSTYLE, style_ex as _);
match set_client_rect.and_then(|r| util::adjust_window_rect_with_styles(window, style, style_ex, r)) {
match set_client_rect
.and_then(|r| util::adjust_window_rect_with_styles(window, style, style_ex, r))
{
Some(client_rect) => {
let (x, y, w, h) = (
client_rect.left,
@ -284,21 +308,29 @@ impl WindowFlags {
winuser::SetWindowPos(
window,
ptr::null_mut(),
x, y, w, h,
x,
y,
w,
h,
winuser::SWP_NOZORDER
| winuser::SWP_FRAMECHANGED,
| winuser::SWP_FRAMECHANGED
| winuser::SWP_NOACTIVATE,
);
},
}
None => {
// Refresh the window frame.
winuser::SetWindowPos(
window,
ptr::null_mut(),
0, 0, 0, 0,
0,
0,
0,
0,
winuser::SWP_NOZORDER
| winuser::SWP_NOMOVE
| winuser::SWP_NOSIZE
| winuser::SWP_FRAMECHANGED,
| winuser::SWP_NOMOVE
| winuser::SWP_NOSIZE
| winuser::SWP_FRAMECHANGED
| winuser::SWP_NOACTIVATE,
);
}
}