mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-24 22:31:30 +11:00
commit
15aafc2908
|
@ -1,6 +1,6 @@
|
|||
extern crate winit;
|
||||
|
||||
use winit::{Event, ElementState, MouseCursor, WindowEvent};
|
||||
use winit::{Event, ElementState, MouseCursor, WindowEvent, KeyboardInput};
|
||||
|
||||
fn main() {
|
||||
let events_loop = winit::EventsLoop::new();
|
||||
|
@ -13,7 +13,7 @@ fn main() {
|
|||
|
||||
events_loop.run_forever(|event| {
|
||||
match event {
|
||||
Event::WindowEvent { event: WindowEvent::KeyboardInput(ElementState::Pressed, _, _, _), .. } => {
|
||||
Event::WindowEvent { event: WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. }, .. } => {
|
||||
println!("Setting cursor to \"{:?}\"", cursors[cursor_idx]);
|
||||
window.set_cursor(cursors[cursor_idx]);
|
||||
if cursor_idx < cursors.len() - 1 {
|
||||
|
|
|
@ -37,10 +37,13 @@ fn main() {
|
|||
winit::Event::WindowEvent { event, .. } => {
|
||||
match event {
|
||||
winit::WindowEvent::Closed => events_loop.interrupt(),
|
||||
winit::WindowEvent::KeyboardInput(_, _, Some(winit::VirtualKeyCode::Escape), _) => events_loop.interrupt(),
|
||||
winit::WindowEvent::KeyboardInput {
|
||||
input: winit::KeyboardInput { virtual_keycode: Some(winit::VirtualKeyCode::Escape), .. }, ..
|
||||
} => events_loop.interrupt(),
|
||||
_ => ()
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
extern crate winit;
|
||||
|
||||
use winit::{WindowEvent, ElementState};
|
||||
use winit::{WindowEvent, ElementState, KeyboardInput};
|
||||
|
||||
fn main() {
|
||||
let events_loop = winit::EventsLoop::new();
|
||||
|
@ -16,7 +16,7 @@ fn main() {
|
|||
match event {
|
||||
winit::Event::WindowEvent { event, .. } => {
|
||||
match event {
|
||||
WindowEvent::KeyboardInput(ElementState::Pressed, _, _, _) => {
|
||||
WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. } => {
|
||||
if grabbed {
|
||||
grabbed = false;
|
||||
window.set_cursor_state(winit::CursorState::Normal)
|
||||
|
@ -30,13 +30,14 @@ fn main() {
|
|||
|
||||
WindowEvent::Closed => events_loop.interrupt(),
|
||||
|
||||
a @ WindowEvent::MouseMoved(_, _) => {
|
||||
a @ WindowEvent::MouseMoved { .. } => {
|
||||
println!("{:?}", a);
|
||||
},
|
||||
|
||||
_ => (),
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -57,6 +57,9 @@ macro_rules! gen_api_transition {
|
|||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(usize);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId;
|
||||
|
||||
pub struct Window2 {
|
||||
pub window: ::std::sync::Arc<Window>,
|
||||
events_loop: ::std::sync::Weak<EventsLoop>,
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
use std::path::PathBuf;
|
||||
use WindowId;
|
||||
use {WindowId, DeviceId, AxisId, ButtonId};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Event {
|
||||
WindowEvent {
|
||||
window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
}
|
||||
},
|
||||
DeviceEvent {
|
||||
device_id: DeviceId,
|
||||
event: DeviceEvent,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -35,31 +39,36 @@ pub enum WindowEvent {
|
|||
Focused(bool),
|
||||
|
||||
/// An event from the keyboard has been received.
|
||||
KeyboardInput(ElementState, ScanCode, Option<VirtualKeyCode>, ModifiersState),
|
||||
KeyboardInput { device_id: DeviceId, input: KeyboardInput },
|
||||
|
||||
/// The cursor has moved on the window.
|
||||
///
|
||||
/// The parameter are the (x,y) coords in pixels relative to the top-left corner of the window.
|
||||
MouseMoved(i32, i32),
|
||||
/// `position` is (x,y) coords in pixels relative to the top-left corner of the window. Because the range of this
|
||||
/// data is limited by the display area and it may have been transformed by the OS to implement effects such as
|
||||
/// mouse acceleration, it should not be used to implement non-cursor-like interactions such as 3D camera control.
|
||||
MouseMoved { device_id: DeviceId, position: (f64, f64) },
|
||||
|
||||
/// The cursor has entered the window.
|
||||
MouseEntered,
|
||||
MouseEntered { device_id: DeviceId },
|
||||
|
||||
/// The cursor has left the window.
|
||||
MouseLeft,
|
||||
MouseLeft { device_id: DeviceId },
|
||||
|
||||
/// A mouse wheel movement or touchpad scroll occurred.
|
||||
MouseWheel(MouseScrollDelta, TouchPhase),
|
||||
MouseWheel { device_id: DeviceId, delta: MouseScrollDelta, phase: TouchPhase },
|
||||
|
||||
/// An event from the mouse has been received.
|
||||
MouseInput(ElementState, MouseButton),
|
||||
/// An mouse button press has been received.
|
||||
MouseInput { device_id: DeviceId, state: ElementState, button: MouseButton },
|
||||
|
||||
/// Touchpad pressure event.
|
||||
///
|
||||
/// At the moment, only supported on Apple forcetouch-capable macbooks.
|
||||
/// The parameters are: pressure level (value between 0 and 1 representing how hard the touchpad
|
||||
/// is being pressed) and stage (integer representing the click level).
|
||||
TouchpadPressure(f32, i64),
|
||||
TouchpadPressure { device_id: DeviceId, pressure: f32, stage: i64 },
|
||||
|
||||
/// Motion on some analog axis not otherwise handled. May overlap with mouse motion.
|
||||
AxisMotion { device_id: DeviceId, axis: AxisId, value: f64 },
|
||||
|
||||
/// The window needs to be redrawn.
|
||||
Refresh,
|
||||
|
@ -73,6 +82,48 @@ pub enum WindowEvent {
|
|||
Touch(Touch)
|
||||
}
|
||||
|
||||
/// Represents raw hardware events that are not associated with any particular window.
|
||||
///
|
||||
/// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person
|
||||
/// game controls. Many physical actions, such as mouse movement, can produce both device and window events. Because
|
||||
/// window events typically arise from virtual devices (corresponding to GUI cursors and keyboard focus) the device IDs
|
||||
/// may not match.
|
||||
///
|
||||
/// Note that these events are delivered regardless of input focus.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum DeviceEvent {
|
||||
Added,
|
||||
Removed,
|
||||
Motion { axis: AxisId, value: f64 },
|
||||
Button { button: ButtonId, state: ElementState },
|
||||
Key(KeyboardInput),
|
||||
Text { codepoint: char },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct KeyboardInput {
|
||||
/// Identifies the physical key pressed
|
||||
///
|
||||
/// This should not change if the user adjusts the host's keyboard map. Use when the physical location of the
|
||||
/// key is more important than the key's host GUI semantics, such as for movement controls in a first-person
|
||||
/// game.
|
||||
pub scancode: ScanCode,
|
||||
|
||||
pub state: ElementState,
|
||||
|
||||
/// Identifies the semantic meaning of the key
|
||||
///
|
||||
/// Use when the semantics of the key are more important than the physical location of the key, such as when
|
||||
/// implementing appropriate behavior for "page up."
|
||||
pub virtual_keycode: Option<VirtualKeyCode>,
|
||||
|
||||
/// Modifier keys active at the time of this input.
|
||||
///
|
||||
/// This is tracked internally to avoid tracking errors arising from modifier key state changes when events from
|
||||
/// this device are not being delivered to the application, e.g. due to keyboard focus being elsewhere.
|
||||
pub modifiers: ModifiersState
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum TouchPhase {
|
||||
Started,
|
||||
|
@ -98,13 +149,14 @@ pub enum TouchPhase {
|
|||
///
|
||||
/// Touch may be cancelled if for example window lost focus.
|
||||
pub struct Touch {
|
||||
pub device_id: DeviceId,
|
||||
pub phase: TouchPhase,
|
||||
pub location: (f64,f64),
|
||||
/// unique identifier of a finger.
|
||||
pub id: u64
|
||||
}
|
||||
|
||||
pub type ScanCode = u8;
|
||||
pub type ScanCode = u32;
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum ElementState {
|
||||
|
|
16
src/lib.rs
16
src/lib.rs
|
@ -169,6 +169,22 @@ pub struct Window {
|
|||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(platform::WindowId);
|
||||
|
||||
/// Identifier of an input device.
|
||||
///
|
||||
/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which
|
||||
/// identifies its origin. Note that devices may be virtual (representing an on-screen cursor and keyboard focus) or
|
||||
/// physical. Virtual devices typically aggregate inputs from multiple physical devices.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId(platform::DeviceId);
|
||||
|
||||
/// Identifier for a specific analog axis on some device.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct AxisId(u32);
|
||||
|
||||
/// Identifier for a specific button on some device.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct ButtonId(u32);
|
||||
|
||||
/// Provides a way to retreive events from the windows that were registered to it.
|
||||
// TODO: document usage in multiple threads
|
||||
pub struct EventsLoop {
|
||||
|
|
|
@ -287,6 +287,7 @@ impl Window {
|
|||
let phase: i32 = msg_send![touch, phase];
|
||||
|
||||
state.events_queue.push_back(Event::Touch(Touch {
|
||||
device_id: DEVICE_ID,
|
||||
id: touch_id,
|
||||
location: (location.x as f64, location.y as f64),
|
||||
phase: match phase {
|
||||
|
@ -495,3 +496,6 @@ impl<'a> Iterator for PollEventsIterator<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Constant device ID, to be removed when this backend is updated to report real device IDs.
|
||||
const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);
|
||||
|
|
|
@ -58,6 +58,14 @@ pub enum WindowId {
|
|||
Wayland(wayland::WindowId)
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum DeviceId {
|
||||
#[doc(hidden)]
|
||||
X(x11::DeviceId),
|
||||
#[doc(hidden)]
|
||||
Wayland(wayland::DeviceId)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum MonitorId {
|
||||
#[doc(hidden)]
|
||||
|
@ -137,8 +145,8 @@ impl Window2 {
|
|||
}
|
||||
},
|
||||
|
||||
UnixBackend::X(ref connec) => {
|
||||
x11::Window2::new(events_loop, connec, window, pl_attribs).map(Window2::X)
|
||||
UnixBackend::X(_) => {
|
||||
x11::Window2::new(events_loop, window, pl_attribs).map(Window2::X)
|
||||
},
|
||||
UnixBackend::Error(_) => {
|
||||
// If the Backend is Error(), it is not possible to instanciate an EventsLoop at all,
|
||||
|
@ -308,8 +316,8 @@ impl EventsLoop {
|
|||
EventsLoop::Wayland(wayland::EventsLoop::new(ctxt.clone()))
|
||||
},
|
||||
|
||||
UnixBackend::X(_) => {
|
||||
EventsLoop::X(x11::EventsLoop::new())
|
||||
UnixBackend::X(ref ctxt) => {
|
||||
EventsLoop::X(x11::EventsLoop::new(ctxt.clone()))
|
||||
},
|
||||
|
||||
UnixBackend::Error(_) => {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase, ModifiersState};
|
||||
use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase, ModifiersState, KeyboardInput};
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
use super::{DecoratedHandler, WindowId, WaylandContext};
|
||||
use super::{DecoratedHandler, WindowId, DeviceId, WaylandContext};
|
||||
|
||||
|
||||
use wayland_client::{EventQueue, EventQueueHandle, Init, Proxy};
|
||||
|
@ -221,7 +221,7 @@ struct InputHandler {
|
|||
seat: Option<wl_seat::WlSeat>,
|
||||
mouse: Option<wl_pointer::WlPointer>,
|
||||
mouse_focus: Option<Arc<wl_surface::WlSurface>>,
|
||||
mouse_location: (i32, i32),
|
||||
mouse_location: (f64, f64),
|
||||
axis_buffer: Option<(f32, f32)>,
|
||||
axis_discrete_buffer: Option<(i32, i32)>,
|
||||
axis_state: TouchPhase,
|
||||
|
@ -242,7 +242,7 @@ impl InputHandler {
|
|||
seat: ctxt.get_seat(),
|
||||
mouse: None,
|
||||
mouse_focus: None,
|
||||
mouse_location: (0,0),
|
||||
mouse_location: (0.0,0.0),
|
||||
axis_buffer: None,
|
||||
axis_discrete_buffer: None,
|
||||
axis_state: TouchPhase::Started,
|
||||
|
@ -310,14 +310,17 @@ impl wl_pointer::Handler for InputHandler {
|
|||
surface_x: f64,
|
||||
surface_y: f64)
|
||||
{
|
||||
self.mouse_location = (surface_x as i32, surface_y as i32);
|
||||
self.mouse_location = (surface_x, surface_y);
|
||||
for window in &self.windows {
|
||||
if window.equals(surface) {
|
||||
self.mouse_focus = Some(window.clone());
|
||||
let (w, h) = self.mouse_location;
|
||||
let mut guard = self.callback.lock().unwrap();
|
||||
guard.send_event(Event::MouseEntered, make_wid(window));
|
||||
guard.send_event(Event::MouseMoved(w, h), make_wid(window));
|
||||
guard.send_event(Event::MouseEntered { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)) },
|
||||
make_wid(window));
|
||||
guard.send_event(Event::MouseMoved { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
position: (w, h) },
|
||||
make_wid(window));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -332,7 +335,8 @@ impl wl_pointer::Handler for InputHandler {
|
|||
self.mouse_focus = None;
|
||||
for window in &self.windows {
|
||||
if window.equals(surface) {
|
||||
self.callback.lock().unwrap().send_event(Event::MouseLeft, make_wid(window));
|
||||
self.callback.lock().unwrap().send_event(Event::MouseLeft { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)) },
|
||||
make_wid(window));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -344,10 +348,11 @@ impl wl_pointer::Handler for InputHandler {
|
|||
surface_x: f64,
|
||||
surface_y: f64)
|
||||
{
|
||||
self.mouse_location = (surface_x as i32, surface_y as i32);
|
||||
self.mouse_location = (surface_x, surface_y);
|
||||
if let Some(ref window) = self.mouse_focus {
|
||||
let (w,h) = self.mouse_location;
|
||||
self.callback.lock().unwrap().send_event(Event::MouseMoved(w, h), make_wid(window));
|
||||
self.callback.lock().unwrap().send_event(Event::MouseMoved { device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
position: (w, h) }, make_wid(window));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -371,7 +376,14 @@ impl wl_pointer::Handler for InputHandler {
|
|||
// TODO figure out the translation ?
|
||||
_ => return
|
||||
};
|
||||
self.callback.lock().unwrap().send_event(Event::MouseInput(state, button), make_wid(window));
|
||||
self.callback.lock().unwrap().send_event(
|
||||
Event::MouseInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
state: state,
|
||||
button: button,
|
||||
},
|
||||
make_wid(window)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -403,18 +415,20 @@ impl wl_pointer::Handler for InputHandler {
|
|||
if let Some(ref window) = self.mouse_focus {
|
||||
if let Some((x, y)) = axis_discrete_buffer {
|
||||
self.callback.lock().unwrap().send_event(
|
||||
Event::MouseWheel(
|
||||
MouseScrollDelta::LineDelta(x as f32, y as f32),
|
||||
self.axis_state
|
||||
),
|
||||
Event::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::LineDelta(x as f32, y as f32),
|
||||
phase: self.axis_state,
|
||||
},
|
||||
make_wid(window)
|
||||
);
|
||||
} else if let Some((x, y)) = axis_buffer {
|
||||
self.callback.lock().unwrap().send_event(
|
||||
Event::MouseWheel(
|
||||
MouseScrollDelta::PixelDelta(x as f32, y as f32),
|
||||
self.axis_state
|
||||
),
|
||||
Event::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
|
||||
phase: self.axis_state,
|
||||
},
|
||||
make_wid(window)
|
||||
);
|
||||
}
|
||||
|
@ -547,12 +561,15 @@ impl wl_keyboard::Handler for InputHandler {
|
|||
// anyway, as we need libxkbcommon to interpret it (it is
|
||||
// supposed to be serialized by the compositor using libxkbcommon)
|
||||
self.callback.lock().unwrap().send_event(
|
||||
Event::KeyboardInput(
|
||||
state,
|
||||
key as u8,
|
||||
None,
|
||||
ModifiersState::default()
|
||||
),
|
||||
Event::KeyboardInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: key,
|
||||
virtual_keycode: None,
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
},
|
||||
wid
|
||||
);
|
||||
},
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use {VirtualKeyCode, ElementState, WindowEvent as Event};
|
||||
use {VirtualKeyCode, ElementState, WindowEvent as Event, KeyboardInput};
|
||||
|
||||
use events::ModifiersState;
|
||||
|
||||
use super::{wayland_kbd, EventsLoopSink, WindowId};
|
||||
use super::{wayland_kbd, EventsLoopSink, WindowId, DeviceId};
|
||||
use wayland_client::EventQueueHandle;
|
||||
use wayland_client::protocol::wl_keyboard;
|
||||
|
||||
|
@ -39,17 +39,20 @@ impl wayland_kbd::Handler for KbdHandler {
|
|||
let vkcode = key_to_vkey(rawkey, keysym);
|
||||
let mut guard = self.sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
Event::KeyboardInput(
|
||||
state,
|
||||
rawkey as u8,
|
||||
vkcode,
|
||||
ModifiersState {
|
||||
Event::KeyboardInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: rawkey,
|
||||
virtual_keycode: vkcode,
|
||||
modifiers: ModifiersState {
|
||||
shift: mods.shift,
|
||||
ctrl: mods.ctrl,
|
||||
alt: mods.alt,
|
||||
logo: mods.logo
|
||||
}
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
wid
|
||||
);
|
||||
// send char event only on key press, not release
|
||||
|
|
|
@ -15,3 +15,6 @@ mod context;
|
|||
mod event_loop;
|
||||
mod keyboard;
|
||||
mod window;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId;
|
||||
|
|
|
@ -2,8 +2,8 @@ use {events, libc};
|
|||
use super::ffi;
|
||||
use VirtualKeyCode;
|
||||
|
||||
pub fn keycode_to_element(scancode: libc::c_uint) -> Option<VirtualKeyCode> {
|
||||
Some(match scancode {
|
||||
pub fn keysym_to_element(keysym: libc::c_uint) -> Option<VirtualKeyCode> {
|
||||
Some(match keysym {
|
||||
ffi::XK_BackSpace => events::VirtualKeyCode::Back,
|
||||
ffi::XK_Tab => events::VirtualKeyCode::Tab,
|
||||
//ffi::XK_Linefeed => events::VirtualKeyCode::Linefeed,
|
||||
|
|
|
@ -1,391 +0,0 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use libc;
|
||||
use std::{mem, ptr};
|
||||
use std::ffi::CString;
|
||||
use std::slice::from_raw_parts;
|
||||
|
||||
use WindowAttributes;
|
||||
|
||||
use events::WindowEvent as Event;
|
||||
use events::ModifiersState;
|
||||
|
||||
use super::{events, ffi};
|
||||
use super::XConnection;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum AxisType {
|
||||
HorizontalScroll,
|
||||
VerticalScroll
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Axis {
|
||||
id: i32,
|
||||
device_id: i32,
|
||||
axis_number: i32,
|
||||
axis_type: AxisType,
|
||||
scroll_increment: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct AxisValue {
|
||||
device_id: i32,
|
||||
axis_number: i32,
|
||||
value: f64
|
||||
}
|
||||
|
||||
struct InputState {
|
||||
/// Last-seen cursor position within a window in (x, y)
|
||||
/// coordinates
|
||||
cursor_pos: (f64, f64),
|
||||
/// Last-seen positions of axes, used to report delta
|
||||
/// movements when a new absolute axis value is received
|
||||
axis_values: Vec<AxisValue>
|
||||
}
|
||||
|
||||
pub struct XInputEventHandler {
|
||||
display: Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
ic: ffi::XIC,
|
||||
axis_list: Vec<Axis>,
|
||||
current_state: InputState,
|
||||
multitouch: bool,
|
||||
}
|
||||
|
||||
impl XInputEventHandler {
|
||||
pub fn new(display: &Arc<XConnection>, window: ffi::Window, ic: ffi::XIC,
|
||||
window_attrs: &WindowAttributes) -> XInputEventHandler {
|
||||
// query XInput support
|
||||
let mut opcode: libc::c_int = 0;
|
||||
let mut event: libc::c_int = 0;
|
||||
let mut error: libc::c_int = 0;
|
||||
let xinput_str = CString::new("XInputExtension").unwrap();
|
||||
|
||||
unsafe {
|
||||
if (display.xlib.XQueryExtension)(display.display, xinput_str.as_ptr(), &mut opcode, &mut event, &mut error) == ffi::False {
|
||||
panic!("XInput not available")
|
||||
}
|
||||
}
|
||||
|
||||
let mut xinput_major_ver = ffi::XI_2_Major;
|
||||
let mut xinput_minor_ver = ffi::XI_2_Minor;
|
||||
|
||||
unsafe {
|
||||
if (display.xinput2.XIQueryVersion)(display.display, &mut xinput_major_ver, &mut xinput_minor_ver) != ffi::Success as libc::c_int {
|
||||
panic!("Unable to determine XInput version");
|
||||
}
|
||||
}
|
||||
|
||||
// specify the XInput events we want to receive.
|
||||
// Button clicks and mouse events are handled via XInput
|
||||
// events. Key presses are still handled via plain core
|
||||
// X11 events.
|
||||
let mut mask: [libc::c_uchar; 3] = [0; 3];
|
||||
let mut input_event_mask = ffi::XIEventMask {
|
||||
deviceid: ffi::XIAllMasterDevices,
|
||||
mask_len: mask.len() as i32,
|
||||
mask: mask.as_mut_ptr()
|
||||
};
|
||||
let events = &[
|
||||
ffi::XI_ButtonPress,
|
||||
ffi::XI_ButtonRelease,
|
||||
ffi::XI_Motion,
|
||||
ffi::XI_Enter,
|
||||
ffi::XI_Leave,
|
||||
ffi::XI_FocusIn,
|
||||
ffi::XI_FocusOut,
|
||||
ffi::XI_TouchBegin,
|
||||
ffi::XI_TouchUpdate,
|
||||
ffi::XI_TouchEnd,
|
||||
];
|
||||
for event in events {
|
||||
ffi::XISetMask(&mut mask, *event);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
match (display.xinput2.XISelectEvents)(display.display, window, &mut input_event_mask, 1) {
|
||||
status if status as u8 == ffi::Success => (),
|
||||
err => panic!("Failed to select events {:?}", err)
|
||||
}
|
||||
}
|
||||
|
||||
XInputEventHandler {
|
||||
display: display.clone(),
|
||||
window: window,
|
||||
ic: ic,
|
||||
axis_list: read_input_axis_info(display),
|
||||
current_state: InputState {
|
||||
cursor_pos: (0.0, 0.0),
|
||||
axis_values: Vec::new()
|
||||
},
|
||||
multitouch: window_attrs.multitouch,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn translate_key_event(&self, event: &mut ffi::XKeyEvent) -> Vec<Event> {
|
||||
use events::WindowEvent::{KeyboardInput, ReceivedCharacter};
|
||||
use events::ElementState::{Pressed, Released};
|
||||
|
||||
let mut translated_events = Vec::new();
|
||||
|
||||
let state;
|
||||
if event.type_ == ffi::KeyPress {
|
||||
let raw_ev: *mut ffi::XKeyEvent = event;
|
||||
unsafe { (self.display.xlib.XFilterEvent)(mem::transmute(raw_ev), self.window) };
|
||||
state = Pressed;
|
||||
} else {
|
||||
state = Released;
|
||||
}
|
||||
|
||||
let mut kp_keysym = 0;
|
||||
|
||||
let mut ev_mods = ModifiersState::default();
|
||||
|
||||
let written = unsafe {
|
||||
use std::str;
|
||||
|
||||
let mut buffer: [u8; 16] = [mem::uninitialized(); 16];
|
||||
let raw_ev: *mut ffi::XKeyEvent = event;
|
||||
let count = (self.display.xlib.Xutf8LookupString)(self.ic, mem::transmute(raw_ev),
|
||||
mem::transmute(buffer.as_mut_ptr()),
|
||||
buffer.len() as libc::c_int, &mut kp_keysym, ptr::null_mut());
|
||||
|
||||
{
|
||||
// Translate x event state to mods
|
||||
let state = event.state;
|
||||
if (state & ffi::Mod1Mask) != 0 {
|
||||
ev_mods.alt = true;
|
||||
}
|
||||
|
||||
if (state & ffi::ShiftMask) != 0 {
|
||||
ev_mods.shift = true;
|
||||
}
|
||||
|
||||
if (state & ffi::ControlMask) != 0 {
|
||||
ev_mods.ctrl = true;
|
||||
}
|
||||
|
||||
if (state & ffi::Mod4Mask) != 0 {
|
||||
ev_mods.logo = true;
|
||||
}
|
||||
}
|
||||
|
||||
str::from_utf8(&buffer[..count as usize]).unwrap_or("").to_string()
|
||||
};
|
||||
|
||||
for chr in written.chars() {
|
||||
translated_events.push(ReceivedCharacter(chr));
|
||||
}
|
||||
|
||||
let mut keysym = unsafe {
|
||||
(self.display.xlib.XKeycodeToKeysym)(self.display.display, event.keycode as ffi::KeyCode, 0)
|
||||
};
|
||||
|
||||
if (ffi::XK_KP_Space as libc::c_ulong <= keysym) && (keysym <= ffi::XK_KP_9 as libc::c_ulong) {
|
||||
keysym = kp_keysym
|
||||
};
|
||||
|
||||
let vkey = events::keycode_to_element(keysym as libc::c_uint);
|
||||
|
||||
translated_events.push(KeyboardInput(state, event.keycode as u8, vkey, ev_mods));
|
||||
translated_events
|
||||
}
|
||||
|
||||
pub fn translate_event(&mut self, cookie: &ffi::XGenericEventCookie) -> Option<Event> {
|
||||
use events::WindowEvent::{Focused, MouseEntered, MouseInput, MouseLeft, MouseMoved, MouseWheel};
|
||||
use events::ElementState::{Pressed, Released};
|
||||
use events::MouseButton::{Left, Right, Middle};
|
||||
use events::MouseScrollDelta::LineDelta;
|
||||
use events::{Touch, TouchPhase};
|
||||
|
||||
match cookie.evtype {
|
||||
ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
|
||||
let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
|
||||
if self.multitouch && (event_data.flags & ffi::XIPointerEmulated) != 0 {
|
||||
// Deliver multi-touch events instead of emulated mouse events.
|
||||
return None
|
||||
}
|
||||
let state = if cookie.evtype == ffi::XI_ButtonPress {
|
||||
Pressed
|
||||
} else {
|
||||
Released
|
||||
};
|
||||
match event_data.detail as u32 {
|
||||
ffi::Button1 => Some(MouseInput(state, Left)),
|
||||
ffi::Button2 => Some(MouseInput(state, Middle)),
|
||||
ffi::Button3 => Some(MouseInput(state, Right)),
|
||||
ffi::Button4 | ffi::Button5 => {
|
||||
if event_data.flags & ffi::XIPointerEmulated == 0 {
|
||||
// scroll event from a traditional wheel with
|
||||
// distinct 'clicks'
|
||||
let delta = if event_data.detail as u32 == ffi::Button4 {
|
||||
1.0
|
||||
} else {
|
||||
-1.0
|
||||
};
|
||||
Some(MouseWheel(LineDelta(0.0, delta), TouchPhase::Moved))
|
||||
} else {
|
||||
// emulated button event from a touch/smooth-scroll
|
||||
// event. Ignore these events and handle scrolling
|
||||
// via XI_Motion event handler instead
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
},
|
||||
ffi::XI_Motion => {
|
||||
let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
|
||||
if self.multitouch && (event_data.flags & ffi::XIPointerEmulated) != 0 {
|
||||
// Deliver multi-touch events instead of emulated mouse events.
|
||||
return None
|
||||
}
|
||||
let axis_state = event_data.valuators;
|
||||
let mask = unsafe{ from_raw_parts(axis_state.mask, axis_state.mask_len as usize) };
|
||||
let mut axis_count = 0;
|
||||
|
||||
let mut scroll_delta = (0.0, 0.0);
|
||||
for axis_id in 0..axis_state.mask_len {
|
||||
if ffi::XIMaskIsSet(&mask, axis_id) {
|
||||
let axis_value = unsafe{*axis_state.values.offset(axis_count)};
|
||||
let delta = calc_scroll_deltas(event_data, axis_id, axis_value, &self.axis_list,
|
||||
&mut self.current_state.axis_values);
|
||||
scroll_delta.0 += delta.0;
|
||||
scroll_delta.1 += delta.1;
|
||||
axis_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if scroll_delta.0.abs() > 0.0 || scroll_delta.1.abs() > 0.0 {
|
||||
Some(MouseWheel(LineDelta(scroll_delta.0 as f32, scroll_delta.1 as f32),
|
||||
TouchPhase::Moved))
|
||||
} else {
|
||||
let new_cursor_pos = (event_data.event_x, event_data.event_y);
|
||||
if new_cursor_pos != self.current_state.cursor_pos {
|
||||
self.current_state.cursor_pos = new_cursor_pos;
|
||||
Some(MouseMoved(new_cursor_pos.0 as i32, new_cursor_pos.1 as i32))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
ffi::XI_Enter => {
|
||||
// axis movements whilst the cursor is outside the window
|
||||
// will alter the absolute value of the axes. We only want to
|
||||
// report changes in the axis value whilst the cursor is above
|
||||
// our window however, so clear the previous axis state whenever
|
||||
// the cursor re-enters the window
|
||||
self.current_state.axis_values.clear();
|
||||
Some(MouseEntered)
|
||||
},
|
||||
ffi::XI_Leave => Some(MouseLeft),
|
||||
ffi::XI_FocusIn => Some(Focused(true)),
|
||||
ffi::XI_FocusOut => Some(Focused(false)),
|
||||
ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => {
|
||||
if !self.multitouch {
|
||||
return None
|
||||
}
|
||||
let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
|
||||
let phase = match cookie.evtype {
|
||||
ffi::XI_TouchBegin => TouchPhase::Started,
|
||||
ffi::XI_TouchUpdate => TouchPhase::Moved,
|
||||
ffi::XI_TouchEnd => TouchPhase::Ended,
|
||||
_ => unreachable!()
|
||||
};
|
||||
Some(Event::Touch(Touch {
|
||||
phase: phase,
|
||||
location: (event_data.event_x, event_data.event_y),
|
||||
id: event_data.detail as u64,
|
||||
}))
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_input_axis_info(display: &Arc<XConnection>) -> Vec<Axis> {
|
||||
let mut axis_list = Vec::new();
|
||||
let mut device_count = 0;
|
||||
|
||||
// Check all input devices for scroll axes.
|
||||
let devices = unsafe{
|
||||
(display.xinput2.XIQueryDevice)(display.display, ffi::XIAllDevices, &mut device_count)
|
||||
};
|
||||
for i in 0..device_count {
|
||||
let device = unsafe { *(devices.offset(i as isize)) };
|
||||
for k in 0..device.num_classes {
|
||||
let class = unsafe { *(device.classes.offset(k as isize)) };
|
||||
match unsafe { (*class)._type } {
|
||||
// Note that scroll axis
|
||||
// are reported both as 'XIScrollClass' and 'XIValuatorClass'
|
||||
// axes. For the moment we only care about scrolling axes.
|
||||
ffi::XIScrollClass => {
|
||||
let scroll_class: &ffi::XIScrollClassInfo = unsafe{mem::transmute(class)};
|
||||
axis_list.push(Axis{
|
||||
id: scroll_class.sourceid,
|
||||
device_id: device.deviceid,
|
||||
axis_number: scroll_class.number,
|
||||
axis_type: match scroll_class.scroll_type {
|
||||
ffi::XIScrollTypeHorizontal => AxisType::HorizontalScroll,
|
||||
ffi::XIScrollTypeVertical => AxisType::VerticalScroll,
|
||||
_ => { unreachable!() }
|
||||
},
|
||||
scroll_increment: scroll_class.increment,
|
||||
})
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
(display.xinput2.XIFreeDeviceInfo)(devices);
|
||||
}
|
||||
|
||||
axis_list
|
||||
}
|
||||
|
||||
/// Given an input motion event for an axis and the previous
|
||||
/// state of the axes, return the horizontal/vertical
|
||||
/// scroll deltas
|
||||
fn calc_scroll_deltas(event: &ffi::XIDeviceEvent,
|
||||
axis_id: i32,
|
||||
axis_value: f64,
|
||||
axis_list: &[Axis],
|
||||
prev_axis_values: &mut Vec<AxisValue>) -> (f64, f64) {
|
||||
let prev_value_pos = prev_axis_values.iter().position(|prev_axis| {
|
||||
prev_axis.device_id == event.sourceid &&
|
||||
prev_axis.axis_number == axis_id
|
||||
});
|
||||
let delta = match prev_value_pos {
|
||||
Some(idx) => prev_axis_values[idx].value - axis_value,
|
||||
None => 0.0
|
||||
};
|
||||
|
||||
let new_axis_value = AxisValue{
|
||||
device_id: event.sourceid,
|
||||
axis_number: axis_id,
|
||||
value: axis_value
|
||||
};
|
||||
|
||||
match prev_value_pos {
|
||||
Some(idx) => prev_axis_values[idx] = new_axis_value,
|
||||
None => prev_axis_values.push(new_axis_value)
|
||||
}
|
||||
|
||||
let mut scroll_delta = (0.0, 0.0);
|
||||
|
||||
for axis in axis_list.iter() {
|
||||
if axis.id == event.sourceid &&
|
||||
axis.axis_number == axis_id {
|
||||
match axis.axis_type {
|
||||
AxisType::HorizontalScroll => scroll_delta.0 = delta / axis.scroll_increment,
|
||||
AxisType::VerticalScroll => scroll_delta.1 = delta / axis.scroll_increment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scroll_delta
|
||||
}
|
||||
|
|
@ -1,18 +1,22 @@
|
|||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
|
||||
pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
|
||||
pub use self::window::{Window, XWindow, PollEventsIterator, WaitEventsIterator, WindowProxy};
|
||||
pub use self::window::{Window, XWindow, WindowProxy};
|
||||
pub use self::xdisplay::{XConnection, XNotSupported, XError};
|
||||
|
||||
pub mod ffi;
|
||||
|
||||
use platform::PlatformSpecificWindowBuilderAttributes;
|
||||
use CreationError;
|
||||
use {CreationError, Event, WindowEvent, DeviceEvent, AxisId, ButtonId, KeyboardInput};
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::{mem, ptr, slice};
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CStr;
|
||||
|
||||
use libc::{self, c_uchar, c_char, c_int};
|
||||
|
||||
mod events;
|
||||
mod input;
|
||||
mod monitor;
|
||||
mod window;
|
||||
mod xdisplay;
|
||||
|
@ -25,16 +29,76 @@ mod xdisplay;
|
|||
// the one generated by the macro.
|
||||
|
||||
pub struct EventsLoop {
|
||||
windows: ::std::sync::Mutex<Vec<::std::sync::Arc<Window>>>,
|
||||
interrupted: ::std::sync::atomic::AtomicBool,
|
||||
display: Arc<XConnection>,
|
||||
wm_delete_window: ffi::Atom,
|
||||
windows: Mutex<HashMap<WindowId, WindowData>>,
|
||||
devices: Mutex<HashMap<DeviceId, Device>>,
|
||||
xi2ext: XExtension,
|
||||
root: ffi::Window,
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn new() -> EventsLoop {
|
||||
EventsLoop {
|
||||
windows: ::std::sync::Mutex::new(vec![]),
|
||||
interrupted: ::std::sync::atomic::AtomicBool::new(false),
|
||||
pub fn new(display: Arc<XConnection>) -> EventsLoop {
|
||||
let wm_delete_window = unsafe { (display.xlib.XInternAtom)(display.display, b"WM_DELETE_WINDOW\0".as_ptr() as *const c_char, 0) };
|
||||
display.check_errors().expect("Failed to call XInternAtom");
|
||||
|
||||
let xi2ext = unsafe {
|
||||
let mut result = XExtension {
|
||||
opcode: mem::uninitialized(),
|
||||
first_event_id: mem::uninitialized(),
|
||||
first_error_id: mem::uninitialized(),
|
||||
};
|
||||
let res = (display.xlib.XQueryExtension)(
|
||||
display.display,
|
||||
b"XInputExtension\0".as_ptr() as *const c_char,
|
||||
&mut result.opcode as *mut c_int,
|
||||
&mut result.first_event_id as *mut c_int,
|
||||
&mut result.first_error_id as *mut c_int);
|
||||
if res == ffi::False {
|
||||
panic!("X server missing XInput extension");
|
||||
}
|
||||
result
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let mut xinput_major_ver = ffi::XI_2_Major;
|
||||
let mut xinput_minor_ver = ffi::XI_2_Minor;
|
||||
|
||||
if (display.xinput2.XIQueryVersion)(display.display, &mut xinput_major_ver, &mut xinput_minor_ver) != ffi::Success as libc::c_int {
|
||||
panic!("X server has XInput extension {}.{} but does not support XInput2", xinput_major_ver, xinput_minor_ver);
|
||||
}
|
||||
}
|
||||
|
||||
let root = unsafe { (display.xlib.XDefaultRootWindow)(display.display) };
|
||||
|
||||
let result = EventsLoop {
|
||||
interrupted: ::std::sync::atomic::AtomicBool::new(false),
|
||||
display: display,
|
||||
wm_delete_window: wm_delete_window,
|
||||
windows: Mutex::new(HashMap::new()),
|
||||
devices: Mutex::new(HashMap::new()),
|
||||
xi2ext: xi2ext,
|
||||
root: root,
|
||||
};
|
||||
|
||||
{
|
||||
// Register for device hotplug events
|
||||
let mask = ffi::XI_HierarchyChangedMask;
|
||||
unsafe {
|
||||
let mut event_mask = ffi::XIEventMask{
|
||||
deviceid: ffi::XIAllDevices,
|
||||
mask: &mask as *const _ as *mut c_uchar,
|
||||
mask_len: mem::size_of_val(&mask) as c_int,
|
||||
};
|
||||
(result.display.xinput2.XISelectEvents)(result.display.display, root,
|
||||
&mut event_mask as *mut ffi::XIEventMask, 1);
|
||||
}
|
||||
|
||||
result.init_device(ffi::XIAllDevices);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn interrupt(&self) {
|
||||
|
@ -42,41 +106,440 @@ impl EventsLoop {
|
|||
}
|
||||
|
||||
pub fn poll_events<F>(&self, mut callback: F)
|
||||
where F: FnMut(::Event)
|
||||
where F: FnMut(Event)
|
||||
{
|
||||
let windows = self.windows.lock().unwrap();
|
||||
for window in windows.iter() {
|
||||
for event in window.poll_events() {
|
||||
callback(::Event::WindowEvent {
|
||||
window_id: ::WindowId(::platform::WindowId::X(WindowId(&**window as *const Window as usize))),
|
||||
event: event,
|
||||
})
|
||||
let xlib = &self.display.xlib;
|
||||
|
||||
let mut xev = unsafe { mem::uninitialized() };
|
||||
unsafe {
|
||||
// Ensure XNextEvent won't block
|
||||
let count = (xlib.XPending)(self.display.display);
|
||||
if count == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
(xlib.XNextEvent)(self.display.display, &mut xev);
|
||||
}
|
||||
self.process_event(&mut xev, &mut callback);
|
||||
}
|
||||
|
||||
pub fn run_forever<F>(&self, mut callback: F)
|
||||
where F: FnMut(::Event)
|
||||
where F: FnMut(Event)
|
||||
{
|
||||
self.interrupted.store(false, ::std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
// Yeah that's a very bad implementation.
|
||||
let xlib = &self.display.xlib;
|
||||
|
||||
let mut xev = unsafe { mem::uninitialized() };
|
||||
|
||||
loop {
|
||||
self.poll_events(|e| callback(e));
|
||||
::std::thread::sleep(::std::time::Duration::from_millis(5));
|
||||
unsafe { (xlib.XNextEvent)(self.display.display, &mut xev) }; // Blocks as necessary
|
||||
self.process_event(&mut xev, &mut callback);
|
||||
if self.interrupted.load(::std::sync::atomic::Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn device_name(&self, device: DeviceId) -> String {
|
||||
let devices = self.devices.lock().unwrap();
|
||||
let device = devices.get(&device).unwrap();
|
||||
device.name.clone()
|
||||
}
|
||||
|
||||
fn process_event<F>(&self, xev: &mut ffi::XEvent, callback: &mut F)
|
||||
where F: FnMut(Event)
|
||||
{
|
||||
let xlib = &self.display.xlib;
|
||||
|
||||
// Handle dead keys and other input method funtimes
|
||||
if ffi::True == unsafe { (self.display.xlib.XFilterEvent)(xev, { let xev: &ffi::XAnyEvent = xev.as_ref(); xev.window }) } {
|
||||
return;
|
||||
}
|
||||
|
||||
let xwindow = { let xev: &ffi::XAnyEvent = xev.as_ref(); xev.window };
|
||||
let wid = ::WindowId(::platform::WindowId::X(WindowId(xwindow)));
|
||||
match xev.get_type() {
|
||||
ffi::MappingNotify => {
|
||||
unsafe { (xlib.XRefreshKeyboardMapping)(xev.as_mut()); }
|
||||
self.display.check_errors().expect("Failed to call XRefreshKeyboardMapping");
|
||||
}
|
||||
|
||||
ffi::ClientMessage => {
|
||||
let client_msg: &ffi::XClientMessageEvent = xev.as_ref();
|
||||
|
||||
if client_msg.data.get_long(0) as ffi::Atom == self.wm_delete_window {
|
||||
callback(Event::WindowEvent { window_id: wid, event: WindowEvent::Closed })
|
||||
} else {
|
||||
// FIXME: Prone to spurious wakeups
|
||||
callback(Event::WindowEvent { window_id: wid, event: WindowEvent::Awakened })
|
||||
}
|
||||
}
|
||||
|
||||
ffi::ConfigureNotify => {
|
||||
let xev: &ffi::XConfigureEvent = xev.as_ref();
|
||||
let size = (xev.width, xev.height);
|
||||
let position = (xev.x, xev.y);
|
||||
// Gymnastics to ensure self.windows isn't locked when we invoke callback
|
||||
let (resized, moved) = {
|
||||
let mut windows = self.windows.lock().unwrap();
|
||||
let window_data = windows.get_mut(&WindowId(xwindow)).unwrap();
|
||||
if window_data.config.is_none() {
|
||||
window_data.config = Some(WindowConfig::new(xev));
|
||||
(true, true)
|
||||
} else {
|
||||
let window = window_data.config.as_mut().unwrap();
|
||||
(if window.size != size {
|
||||
window.size = size;
|
||||
true
|
||||
} else { false },
|
||||
if window.position != position {
|
||||
window.position = position;
|
||||
true
|
||||
} else { false })
|
||||
}
|
||||
};
|
||||
if resized {
|
||||
callback(Event::WindowEvent { window_id: wid, event: WindowEvent::Resized(xev.width as u32, xev.height as u32) });
|
||||
}
|
||||
if moved {
|
||||
callback(Event::WindowEvent { window_id: wid, event: WindowEvent::Moved(xev.x as i32, xev.y as i32) });
|
||||
}
|
||||
}
|
||||
|
||||
ffi::Expose => {
|
||||
callback(Event::WindowEvent { window_id: wid, event: WindowEvent::Refresh });
|
||||
}
|
||||
|
||||
// FIXME: Use XInput2 + libxkbcommon for keyboard input!
|
||||
ffi::KeyPress | ffi::KeyRelease => {
|
||||
use events::ModifiersState;
|
||||
use events::ElementState::{Pressed, Released};
|
||||
|
||||
let state;
|
||||
if xev.get_type() == ffi::KeyPress {
|
||||
state = Pressed;
|
||||
} else {
|
||||
state = Released;
|
||||
}
|
||||
|
||||
let xkev: &mut ffi::XKeyEvent = xev.as_mut();
|
||||
|
||||
|
||||
let mut ev_mods = ModifiersState::default();
|
||||
|
||||
if state == Pressed {
|
||||
let written = unsafe {
|
||||
use std::str;
|
||||
|
||||
let mut windows = self.windows.lock().unwrap();
|
||||
let window_data = windows.get_mut(&WindowId(xwindow)).unwrap();
|
||||
let mut buffer: [u8; 16] = [mem::uninitialized(); 16];
|
||||
let mut keysym = 0;
|
||||
let count = (self.display.xlib.Xutf8LookupString)(window_data.ic, xkev,
|
||||
mem::transmute(buffer.as_mut_ptr()),
|
||||
buffer.len() as libc::c_int, &mut keysym, ptr::null_mut());
|
||||
|
||||
{
|
||||
// Translate x event state to mods
|
||||
let state = xkev.state;
|
||||
if (state & ffi::Mod1Mask) != 0 {
|
||||
ev_mods.alt = true;
|
||||
}
|
||||
|
||||
if (state & ffi::ShiftMask) != 0 {
|
||||
ev_mods.shift = true;
|
||||
}
|
||||
|
||||
if (state & ffi::ControlMask) != 0 {
|
||||
ev_mods.ctrl = true;
|
||||
}
|
||||
|
||||
if (state & ffi::Mod4Mask) != 0 {
|
||||
ev_mods.logo = true;
|
||||
}
|
||||
}
|
||||
|
||||
str::from_utf8(&buffer[..count as usize]).unwrap_or("").to_string()
|
||||
};
|
||||
|
||||
for chr in written.chars() {
|
||||
callback(Event::WindowEvent { window_id: wid, event: WindowEvent::ReceivedCharacter(chr) })
|
||||
}
|
||||
}
|
||||
|
||||
let mut keysym = unsafe {
|
||||
(self.display.xlib.XKeycodeToKeysym)(self.display.display, xkev.keycode as ffi::KeyCode, 0)
|
||||
};
|
||||
|
||||
let vkey = events::keysym_to_element(keysym as libc::c_uint);
|
||||
|
||||
callback(Event::WindowEvent { window_id: wid, event: WindowEvent::KeyboardInput {
|
||||
// Typical virtual core keyboard ID. xinput2 needs to be used to get a reliable value.
|
||||
device_id: mkdid(3),
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: xkev.keycode,
|
||||
virtual_keycode: vkey,
|
||||
modifiers: ev_mods,
|
||||
},
|
||||
}});
|
||||
}
|
||||
|
||||
ffi::GenericEvent => {
|
||||
let guard = if let Some(e) = GenericEventCookie::from_event(&self.display, *xev) { e } else { return };
|
||||
let xev = &guard.cookie;
|
||||
if self.xi2ext.opcode != xev.extension {
|
||||
return;
|
||||
}
|
||||
|
||||
use events::WindowEvent::{Focused, MouseEntered, MouseInput, MouseLeft, MouseMoved, MouseWheel, AxisMotion};
|
||||
use events::ElementState::{Pressed, Released};
|
||||
use events::MouseButton::{Left, Right, Middle, Other};
|
||||
use events::MouseScrollDelta::LineDelta;
|
||||
use events::{Touch, TouchPhase};
|
||||
|
||||
match xev.evtype {
|
||||
ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
|
||||
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
||||
let wid = mkwid(xev.event);
|
||||
let did = mkdid(xev.deviceid);
|
||||
if (xev.flags & ffi::XIPointerEmulated) != 0 && self.windows.lock().unwrap().get(&WindowId(xev.event)).unwrap().multitouch {
|
||||
// Deliver multi-touch events instead of emulated mouse events.
|
||||
return;
|
||||
}
|
||||
let state = if xev.evtype == ffi::XI_ButtonPress {
|
||||
Pressed
|
||||
} else {
|
||||
Released
|
||||
};
|
||||
match xev.detail as u32 {
|
||||
ffi::Button1 => callback(Event::WindowEvent { window_id: wid, event:
|
||||
MouseInput { device_id: did, state: state, button: Left } }),
|
||||
ffi::Button2 => callback(Event::WindowEvent { window_id: wid, event:
|
||||
MouseInput { device_id: did, state: state, button: Middle } }),
|
||||
ffi::Button3 => callback(Event::WindowEvent { window_id: wid, event:
|
||||
MouseInput { device_id: did, state: state, button: Right } }),
|
||||
|
||||
// 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 => {}
|
||||
|
||||
x => callback(Event::WindowEvent { window_id: wid, event: MouseInput { device_id: did, state: state, button: Other(x as u8) } })
|
||||
}
|
||||
}
|
||||
ffi::XI_Motion => {
|
||||
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
||||
let did = mkdid(xev.deviceid);
|
||||
let wid = mkwid(xev.event);
|
||||
let new_cursor_pos = (xev.event_x, xev.event_y);
|
||||
|
||||
// Gymnastics to ensure self.windows isn't locked when we invoke callback
|
||||
if {
|
||||
let mut windows = self.windows.lock().unwrap();
|
||||
let window_data = windows.get_mut(&WindowId(xev.event)).unwrap();
|
||||
if Some(new_cursor_pos) != window_data.cursor_pos {
|
||||
window_data.cursor_pos = Some(new_cursor_pos);
|
||||
true
|
||||
} else { false }
|
||||
} {
|
||||
callback(Event::WindowEvent { window_id: wid, event: MouseMoved {
|
||||
device_id: did,
|
||||
position: new_cursor_pos
|
||||
}});
|
||||
}
|
||||
|
||||
// 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 mut devices = self.devices.lock().unwrap();
|
||||
let physical_device = devices.get_mut(&DeviceId(xev.sourceid)).unwrap();
|
||||
|
||||
let mut value = xev.valuators.values;
|
||||
for i in 0..xev.valuators.mask_len*8 {
|
||||
if ffi::XIMaskIsSet(mask, i) {
|
||||
if let Some(&mut (_, ref mut info)) = physical_device.scroll_axes.iter_mut().find(|&&mut (axis, _)| axis == i) {
|
||||
let delta = (unsafe { *value } - info.position) / info.increment;
|
||||
info.position = unsafe { *value };
|
||||
events.push(Event::WindowEvent { window_id: wid, event: MouseWheel {
|
||||
device_id: did,
|
||||
delta: match info.orientation {
|
||||
ScrollOrientation::Horizontal => LineDelta(delta as f32, 0.0),
|
||||
ScrollOrientation::Vertical => LineDelta(0.0, delta as f32),
|
||||
},
|
||||
phase: TouchPhase::Moved,
|
||||
}});
|
||||
} else {
|
||||
events.push(Event::WindowEvent { window_id: wid, event: AxisMotion {
|
||||
device_id: did,
|
||||
axis: AxisId(i as u32),
|
||||
value: unsafe { *value },
|
||||
}});
|
||||
}
|
||||
value = unsafe { value.offset(1) };
|
||||
}
|
||||
}
|
||||
}
|
||||
for event in events {
|
||||
callback(event);
|
||||
}
|
||||
}
|
||||
|
||||
ffi::XI_Enter => {
|
||||
let xev: &ffi::XIEnterEvent = unsafe { &*(xev.data as *const _) };
|
||||
callback(Event::WindowEvent { window_id: mkwid(xev.event), event: MouseEntered { device_id: mkdid(xev.deviceid) } })
|
||||
}
|
||||
ffi::XI_Leave => {
|
||||
let xev: &ffi::XILeaveEvent = unsafe { &*(xev.data as *const _) };
|
||||
callback(Event::WindowEvent { window_id: mkwid(xev.event), event: MouseLeft { device_id: mkdid(xev.deviceid) } })
|
||||
}
|
||||
ffi::XI_FocusIn => {
|
||||
let xev: &ffi::XIFocusInEvent = unsafe { &*(xev.data as *const _) };
|
||||
callback(Event::WindowEvent { window_id: mkwid(xev.event), event: Focused(true) })
|
||||
}
|
||||
ffi::XI_FocusOut => {
|
||||
let xev: &ffi::XIFocusOutEvent = unsafe { &*(xev.data as *const _) };
|
||||
callback(Event::WindowEvent { window_id: mkwid(xev.event), event: Focused(false) })
|
||||
}
|
||||
|
||||
ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => {
|
||||
let xev: &ffi::XIDeviceEvent = unsafe { &*(xev.data as *const _) };
|
||||
let wid = mkwid(xev.event);
|
||||
let phase = match xev.evtype {
|
||||
ffi::XI_TouchBegin => TouchPhase::Started,
|
||||
ffi::XI_TouchUpdate => TouchPhase::Moved,
|
||||
ffi::XI_TouchEnd => TouchPhase::Ended,
|
||||
_ => unreachable!()
|
||||
};
|
||||
callback(Event::WindowEvent { window_id: wid, event: WindowEvent::Touch(Touch {
|
||||
device_id: mkdid(xev.deviceid),
|
||||
phase: phase,
|
||||
location: (xev.event_x, xev.event_y),
|
||||
id: xev.detail as u64,
|
||||
})})
|
||||
}
|
||||
|
||||
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: ButtonId(xev.detail as u32),
|
||||
state: match xev.evtype {
|
||||
ffi::XI_RawButtonPress => Pressed,
|
||||
ffi::XI_RawButtonRelease => Released,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
ffi::XI_RawMotion => {
|
||||
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 mut value = xev.valuators.values;
|
||||
for i in 0..xev.valuators.mask_len*8 {
|
||||
if ffi::XIMaskIsSet(mask, i) {
|
||||
callback(Event::DeviceEvent { device_id: did, event: DeviceEvent::Motion {
|
||||
axis: AxisId(i as u32),
|
||||
value: unsafe { *value },
|
||||
}});
|
||||
value = unsafe { value.offset(1) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ffi::XI_RawKeyPress | ffi::XI_RawKeyRelease => {
|
||||
// TODO: Use xkbcommon for keysym and text decoding
|
||||
let xev: &ffi::XIRawEvent = unsafe { &*(xev.data as *const _) };
|
||||
let xkeysym = unsafe { (self.display.xlib.XKeycodeToKeysym)(self.display.display, xev.detail as ffi::KeyCode, 0) };
|
||||
callback(Event::DeviceEvent { device_id: mkdid(xev.deviceid), event: DeviceEvent::Key(KeyboardInput {
|
||||
scancode: xev.detail as u32,
|
||||
virtual_keycode: events::keysym_to_element(xkeysym as libc::c_uint),
|
||||
state: match xev.evtype {
|
||||
ffi::XI_RawKeyPress => Pressed,
|
||||
ffi::XI_RawKeyRelease => Released,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
modifiers: ::events::ModifiersState::default(),
|
||||
})});
|
||||
}
|
||||
|
||||
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) } {
|
||||
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 });
|
||||
let mut devices = self.devices.lock().unwrap();
|
||||
devices.remove(&DeviceId(info.deviceid));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn init_device(&self, device: c_int) {
|
||||
let mut devices = self.devices.lock().unwrap();
|
||||
for info in DeviceInfo::get(&self.display, device).iter() {
|
||||
devices.insert(DeviceId(info.deviceid), Device::new(&self, info));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DeviceInfo<'a> {
|
||||
display: &'a XConnection,
|
||||
info: *const ffi::XIDeviceInfo,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl<'a> DeviceInfo<'a> {
|
||||
fn get(display: &'a XConnection, device: c_int) -> Self {
|
||||
unsafe {
|
||||
let mut count = mem::uninitialized();
|
||||
let info = (display.xinput2.XIQueryDevice)(display.display, device, &mut count);
|
||||
DeviceInfo {
|
||||
display: display,
|
||||
info: info,
|
||||
count: count as usize,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for DeviceInfo<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { (self.display.xinput2.XIFreeDeviceInfo)(self.info as *mut _) };
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ::std::ops::Deref for DeviceInfo<'a> {
|
||||
type Target = [ffi::XIDeviceInfo];
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { slice::from_raw_parts(self.info, self.count) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(usize);
|
||||
pub struct WindowId(ffi::Window);
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId(c_int);
|
||||
|
||||
pub struct Window2 {
|
||||
pub window: ::std::sync::Arc<Window>,
|
||||
events_loop: ::std::sync::Weak<::platform::EventsLoop>,
|
||||
pub window: Arc<Window>,
|
||||
events_loop: Weak<::platform::EventsLoop>,
|
||||
}
|
||||
|
||||
impl ::std::ops::Deref for Window2 {
|
||||
|
@ -87,28 +550,61 @@ impl ::std::ops::Deref for Window2 {
|
|||
}
|
||||
}
|
||||
|
||||
// XOpenIM doesn't seem to be thread-safe
|
||||
lazy_static! { // TODO: use a static mutex when that's possible, and put me back in my function
|
||||
static ref GLOBAL_XOPENIM_LOCK: Mutex<()> = Mutex::new(());
|
||||
}
|
||||
|
||||
impl Window2 {
|
||||
pub fn new(events_loop: ::std::sync::Arc<::platform::EventsLoop>, display: &Arc<XConnection>,
|
||||
pub fn new(events_loop: Arc<::platform::EventsLoop>,
|
||||
window: &::WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window2, CreationError>
|
||||
{
|
||||
let win = ::std::sync::Arc::new(try!(Window::new(display, window, pl_attribs)));
|
||||
if let ::platform::EventsLoop::X(ref ev) = *events_loop {
|
||||
ev.windows.lock().unwrap().push(win.clone());
|
||||
} else {
|
||||
// It should not be possible to create an eventloop not matching the backend
|
||||
// in use
|
||||
unreachable!()
|
||||
let x_events_loop = if let ::platform::EventsLoop::X(ref e) = *events_loop { e } else { unreachable!() };
|
||||
let win = ::std::sync::Arc::new(try!(Window::new(&x_events_loop, window, pl_attribs)));
|
||||
|
||||
// creating IM
|
||||
let im = unsafe {
|
||||
let _lock = GLOBAL_XOPENIM_LOCK.lock().unwrap();
|
||||
|
||||
let im = (x_events_loop.display.xlib.XOpenIM)(x_events_loop.display.display, ptr::null_mut(), ptr::null_mut(), ptr::null_mut());
|
||||
if im.is_null() {
|
||||
panic!("XOpenIM failed");
|
||||
}
|
||||
im
|
||||
};
|
||||
|
||||
// creating input context
|
||||
let ic = unsafe {
|
||||
let ic = (x_events_loop.display.xlib.XCreateIC)(im,
|
||||
b"inputStyle\0".as_ptr() as *const _,
|
||||
ffi::XIMPreeditNothing | ffi::XIMStatusNothing, b"clientWindow\0".as_ptr() as *const _,
|
||||
win.id().0, ptr::null::<()>());
|
||||
if ic.is_null() {
|
||||
panic!("XCreateIC failed");
|
||||
}
|
||||
(x_events_loop.display.xlib.XSetICFocus)(ic);
|
||||
x_events_loop.display.check_errors().expect("Failed to call XSetICFocus");
|
||||
ic
|
||||
};
|
||||
|
||||
x_events_loop.windows.lock().unwrap().insert(win.id(), WindowData {
|
||||
im: im,
|
||||
ic: ic,
|
||||
config: None,
|
||||
multitouch: window.multitouch,
|
||||
cursor_pos: None,
|
||||
});
|
||||
|
||||
Ok(Window2 {
|
||||
window: win,
|
||||
events_loop: ::std::sync::Arc::downgrade(&events_loop),
|
||||
events_loop: Arc::downgrade(&events_loop),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
WindowId(&*self.window as *const Window as usize)
|
||||
self.window.id()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,8 +613,167 @@ impl Drop for Window2 {
|
|||
if let Some(ev) = self.events_loop.upgrade() {
|
||||
if let ::platform::EventsLoop::X(ref ev) = *ev {
|
||||
let mut windows = ev.windows.lock().unwrap();
|
||||
windows.retain(|w| &**w as *const Window != &*self.window as *const _);
|
||||
|
||||
|
||||
let w = windows.remove(&self.window.id()).unwrap();
|
||||
let _lock = GLOBAL_XOPENIM_LOCK.lock().unwrap();
|
||||
unsafe {
|
||||
(ev.display.xlib.XDestroyIC)(w.ic);
|
||||
(ev.display.xlib.XCloseIM)(w.im);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// State maintained for translating window-related events
|
||||
struct WindowData {
|
||||
config: Option<WindowConfig>,
|
||||
im: ffi::XIM,
|
||||
ic: ffi::XIC,
|
||||
multitouch: bool,
|
||||
cursor_pos: Option<(f64, f64)>,
|
||||
}
|
||||
|
||||
// Required by ffi members
|
||||
unsafe impl Send for WindowData {}
|
||||
|
||||
struct WindowConfig {
|
||||
size: (c_int, c_int),
|
||||
position: (c_int, c_int),
|
||||
}
|
||||
|
||||
impl WindowConfig {
|
||||
fn new(event: &ffi::XConfigureEvent) -> Self {
|
||||
WindowConfig {
|
||||
size: (event.width, event.height),
|
||||
position: (event.x, event.y),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// XEvents of type GenericEvent store their actual data in an XGenericEventCookie data structure. This is a wrapper to
|
||||
/// extract the cookie from a GenericEvent XEvent and release the cookie data once it has been processed
|
||||
struct GenericEventCookie<'a> {
|
||||
display: &'a XConnection,
|
||||
cookie: ffi::XGenericEventCookie
|
||||
}
|
||||
|
||||
impl<'a> GenericEventCookie<'a> {
|
||||
fn from_event<'b>(display: &'b XConnection, event: ffi::XEvent) -> Option<GenericEventCookie<'b>> {
|
||||
unsafe {
|
||||
let mut cookie: ffi::XGenericEventCookie = From::from(event);
|
||||
if (display.xlib.XGetEventData)(display.display, &mut cookie) == ffi::True {
|
||||
Some(GenericEventCookie{display: display, cookie: cookie})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for GenericEventCookie<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let xlib = &self.display.xlib;
|
||||
(xlib.XFreeEventData)(self.display.display, &mut self.cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct XExtension {
|
||||
opcode: c_int,
|
||||
first_event_id: c_int,
|
||||
first_error_id: c_int,
|
||||
}
|
||||
|
||||
fn mkwid(w: ffi::Window) -> ::WindowId { ::WindowId(::platform::WindowId::X(WindowId(w))) }
|
||||
fn mkdid(w: c_int) -> ::DeviceId { ::DeviceId(::platform::DeviceId::X(DeviceId(w))) }
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Device {
|
||||
name: String,
|
||||
scroll_axes: Vec<(i32, ScrollAxis)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct ScrollAxis {
|
||||
increment: f64,
|
||||
orientation: ScrollOrientation,
|
||||
position: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum ScrollOrientation {
|
||||
Vertical,
|
||||
Horizontal,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
fn new(el: &EventsLoop, info: &ffi::XIDeviceInfo) -> Self
|
||||
{
|
||||
let name = unsafe { CStr::from_ptr(info.name).to_string_lossy() };
|
||||
|
||||
let physical_device = info._use == ffi::XISlaveKeyboard || info._use == ffi::XISlavePointer || info._use == ffi::XIFloatingSlave;
|
||||
if physical_device {
|
||||
// Register for global raw events
|
||||
let mask = ffi::XI_RawMotionMask
|
||||
| ffi::XI_RawButtonPressMask | ffi::XI_RawButtonReleaseMask
|
||||
| ffi::XI_RawKeyPressMask | ffi::XI_RawKeyReleaseMask;
|
||||
unsafe {
|
||||
let mut event_mask = ffi::XIEventMask{
|
||||
deviceid: info.deviceid,
|
||||
mask: &mask as *const _ as *mut c_uchar,
|
||||
mask_len: mem::size_of_val(&mask) as c_int,
|
||||
};
|
||||
(el.display.xinput2.XISelectEvents)(el.display.display, el.root, &mut event_mask as *mut ffi::XIEventMask, 1);
|
||||
}
|
||||
}
|
||||
|
||||
let mut scroll_axes = Vec::new();
|
||||
|
||||
if physical_device {
|
||||
let classes : &[*const ffi::XIAnyClassInfo] =
|
||||
unsafe { slice::from_raw_parts(info.classes as *const *const ffi::XIAnyClassInfo, info.num_classes as usize) };
|
||||
// Identify scroll axes
|
||||
for class_ptr in classes {
|
||||
let class = unsafe { &**class_ptr };
|
||||
match class._type {
|
||||
ffi::XIScrollClass => {
|
||||
let info = unsafe { mem::transmute::<&ffi::XIAnyClassInfo, &ffi::XIScrollClassInfo>(class) };
|
||||
scroll_axes.push((info.number, ScrollAxis {
|
||||
increment: info.increment,
|
||||
orientation: match info.scroll_type {
|
||||
ffi::XIScrollTypeHorizontal => ScrollOrientation::Horizontal,
|
||||
ffi::XIScrollTypeVertical => ScrollOrientation::Vertical,
|
||||
_ => { unreachable!() }
|
||||
},
|
||||
position: 0.0,
|
||||
}));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// Fix up initial scroll positions
|
||||
for class_ptr in classes {
|
||||
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)) = scroll_axes.iter_mut().find(|&&mut (axis, _)| axis == info.number) {
|
||||
axis.position = info.value;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Device {
|
||||
name: name.into_owned(),
|
||||
scroll_axes: scroll_axes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
use {WindowEvent as Event, MouseCursor};
|
||||
use MouseCursor;
|
||||
use CreationError;
|
||||
use CreationError::OsError;
|
||||
use libc;
|
||||
use std::borrow::Borrow;
|
||||
use std::{mem, ptr, cmp};
|
||||
use std::cell::Cell;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::os::raw::c_long;
|
||||
use std::os::raw::{c_int, c_long, c_uchar};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
|
@ -18,14 +15,8 @@ use platform::PlatformSpecificWindowBuilderAttributes;
|
|||
|
||||
use platform::MonitorId as PlatformMonitorId;
|
||||
|
||||
use super::input::XInputEventHandler;
|
||||
use super::{ffi};
|
||||
use super::{MonitorId, XConnection};
|
||||
|
||||
// XOpenIM doesn't seem to be thread-safe
|
||||
lazy_static! { // TODO: use a static mutex when that's possible, and put me back in my function
|
||||
static ref GLOBAL_XOPENIM_LOCK: Mutex<()> = Mutex::new(());
|
||||
}
|
||||
use super::{MonitorId, XConnection, WindowId, EventsLoop};
|
||||
|
||||
// TODO: remove me
|
||||
fn with_c_str<F, T>(s: &str, f: F) -> T where F: FnOnce(*const libc::c_char) -> T {
|
||||
|
@ -47,8 +38,6 @@ pub struct XWindow {
|
|||
is_fullscreen: bool,
|
||||
screen_id: libc::c_int,
|
||||
xf86_desk_mode: Option<ffi::XF86VidModeModeInfo>,
|
||||
ic: ffi::XIC,
|
||||
im: ffi::XIM,
|
||||
window_proxy_data: Arc<Mutex<Option<WindowProxyData>>>,
|
||||
}
|
||||
|
||||
|
@ -65,8 +54,6 @@ impl Drop for XWindow {
|
|||
// are no longer able to send messages to this window.
|
||||
*self.window_proxy_data.lock().unwrap() = None;
|
||||
|
||||
let _lock = GLOBAL_XOPENIM_LOCK.lock().unwrap();
|
||||
|
||||
if self.is_fullscreen {
|
||||
if let Some(mut xf86_desk_mode) = self.xf86_desk_mode {
|
||||
(self.display.xf86vmode.XF86VidModeSwitchToMode)(self.display.display, self.screen_id, &mut xf86_desk_mode);
|
||||
|
@ -74,8 +61,6 @@ impl Drop for XWindow {
|
|||
(self.display.xf86vmode.XF86VidModeSetViewPort)(self.display.display, self.screen_id, 0, 0);
|
||||
}
|
||||
|
||||
(self.display.xlib.XDestroyIC)(self.ic);
|
||||
(self.display.xlib.XCloseIM)(self.im);
|
||||
(self.display.xlib.XDestroyWindow)(self.display.display, self.window);
|
||||
}
|
||||
}
|
||||
|
@ -111,190 +96,17 @@ impl WindowProxy {
|
|||
}
|
||||
}
|
||||
|
||||
// XEvents of type GenericEvent store their actual data
|
||||
// in an XGenericEventCookie data structure. This is a wrapper
|
||||
// to extract the cookie from a GenericEvent XEvent and release
|
||||
// the cookie data once it has been processed
|
||||
struct GenericEventCookie<'a> {
|
||||
display: &'a XConnection,
|
||||
cookie: ffi::XGenericEventCookie
|
||||
}
|
||||
|
||||
impl<'a> GenericEventCookie<'a> {
|
||||
fn from_event<'b>(display: &'b XConnection, event: ffi::XEvent) -> Option<GenericEventCookie<'b>> {
|
||||
unsafe {
|
||||
let mut cookie: ffi::XGenericEventCookie = From::from(event);
|
||||
if (display.xlib.XGetEventData)(display.display, &mut cookie) == ffi::True {
|
||||
Some(GenericEventCookie{display: display, cookie: cookie})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for GenericEventCookie<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let xlib = &self.display.xlib;
|
||||
(xlib.XFreeEventData)(self.display.display, &mut self.cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PollEventsIterator<'a> {
|
||||
window: &'a Window
|
||||
}
|
||||
|
||||
impl<'a> Iterator for PollEventsIterator<'a> {
|
||||
type Item = Event;
|
||||
|
||||
fn next(&mut self) -> Option<Event> {
|
||||
let xlib = &self.window.x.display.xlib;
|
||||
|
||||
loop {
|
||||
if let Some(ev) = self.window.pending_events.lock().unwrap().pop_front() {
|
||||
return Some(ev);
|
||||
}
|
||||
|
||||
let mut xev = unsafe { mem::uninitialized() };
|
||||
|
||||
// Get the next X11 event. XNextEvent will block if there's no
|
||||
// events available; checking the count first ensures an event will
|
||||
// be returned without blocking.
|
||||
//
|
||||
// Functions like XCheckTypedEvent can prevent events from being
|
||||
// popped if they are of the wrong type in which case winit would
|
||||
// enter a busy loop. To avoid that, XNextEvent is used to pop
|
||||
// events off the queue since it will accept any event type.
|
||||
unsafe {
|
||||
let count = (xlib.XPending)(self.window.x.display.display);
|
||||
if count == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let res = (xlib.XNextEvent)(self.window.x.display.display, &mut xev);
|
||||
|
||||
// Can res ever be none zero if count is > 0?
|
||||
assert!(res == 0);
|
||||
};
|
||||
|
||||
match xev.get_type() {
|
||||
ffi::MappingNotify => {
|
||||
unsafe { (xlib.XRefreshKeyboardMapping)(mem::transmute(&xev)); }
|
||||
self.window.x.display.check_errors().expect("Failed to call XRefreshKeyboardMapping");
|
||||
},
|
||||
|
||||
ffi::ClientMessage => {
|
||||
use events::WindowEvent::{Closed, Awakened};
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
|
||||
let client_msg: &ffi::XClientMessageEvent = unsafe { mem::transmute(&xev) };
|
||||
|
||||
if client_msg.data.get_long(0) == self.window.wm_delete_window as libc::c_long {
|
||||
self.window.is_closed.store(true, Relaxed);
|
||||
return Some(Closed);
|
||||
} else {
|
||||
return Some(Awakened);
|
||||
}
|
||||
},
|
||||
|
||||
ffi::ConfigureNotify => {
|
||||
use events::WindowEvent::Resized;
|
||||
let cfg_event: &ffi::XConfigureEvent = unsafe { mem::transmute(&xev) };
|
||||
let (current_width, current_height) = self.window.current_size.get();
|
||||
if current_width != cfg_event.width || current_height != cfg_event.height {
|
||||
self.window.current_size.set((cfg_event.width, cfg_event.height));
|
||||
return Some(Resized(cfg_event.width as u32, cfg_event.height as u32));
|
||||
}
|
||||
},
|
||||
|
||||
ffi::Expose => {
|
||||
use events::WindowEvent::Refresh;
|
||||
return Some(Refresh);
|
||||
},
|
||||
|
||||
ffi::KeyPress | ffi::KeyRelease => {
|
||||
let mut event: &mut ffi::XKeyEvent = unsafe { mem::transmute(&mut xev) };
|
||||
let events = self.window.input_handler.lock().unwrap().translate_key_event(&mut event);
|
||||
for event in events {
|
||||
self.window.pending_events.lock().unwrap().push_back(event);
|
||||
}
|
||||
},
|
||||
|
||||
ffi::GenericEvent => {
|
||||
if let Some(cookie) = GenericEventCookie::from_event(self.window.x.display.borrow(), xev) {
|
||||
match cookie.cookie.evtype {
|
||||
ffi::XI_DeviceChanged...ffi::XI_LASTEVENT => {
|
||||
match self.window.input_handler.lock() {
|
||||
Ok(mut handler) => {
|
||||
match handler.translate_event(&cookie.cookie) {
|
||||
Some(event) => self.window.pending_events.lock().unwrap().push_back(event),
|
||||
None => {}
|
||||
}
|
||||
},
|
||||
Err(_) => {}
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WaitEventsIterator<'a> {
|
||||
window: &'a Window,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for WaitEventsIterator<'a> {
|
||||
type Item = Event;
|
||||
|
||||
fn next(&mut self) -> Option<Event> {
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
use std::mem;
|
||||
|
||||
while !self.window.is_closed.load(Relaxed) {
|
||||
if let Some(ev) = self.window.pending_events.lock().unwrap().pop_front() {
|
||||
return Some(ev);
|
||||
}
|
||||
|
||||
// this will block until an event arrives, but doesn't remove
|
||||
// it from the queue
|
||||
let mut xev = unsafe { mem::uninitialized() };
|
||||
unsafe { (self.window.x.display.xlib.XPeekEvent)(self.window.x.display.display, &mut xev) };
|
||||
self.window.x.display.check_errors().expect("Failed to call XPeekEvent");
|
||||
|
||||
// calling poll_events()
|
||||
if let Some(ev) = self.window.poll_events().next() {
|
||||
return Some(ev);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Window {
|
||||
pub x: Arc<XWindow>,
|
||||
is_closed: AtomicBool,
|
||||
wm_delete_window: ffi::Atom,
|
||||
current_size: Cell<(libc::c_int, libc::c_int)>,
|
||||
/// Events that have been retreived with XLib but not dispatched with iterators yet
|
||||
pending_events: Mutex<VecDeque<Event>>,
|
||||
cursor_state: Mutex<CursorState>,
|
||||
input_handler: Mutex<XInputEventHandler>
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(display: &Arc<XConnection>, window_attrs: &WindowAttributes,
|
||||
pub fn new(ctx: &EventsLoop, window_attrs: &WindowAttributes,
|
||||
pl_attribs: &PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window, CreationError>
|
||||
{
|
||||
let display = &ctx.display;
|
||||
let dimensions = {
|
||||
|
||||
// x11 only applies constraints when the window is actively resized
|
||||
|
@ -426,49 +238,13 @@ impl Window {
|
|||
display.check_errors().expect("Failed to set window visibility");
|
||||
}
|
||||
|
||||
// creating window, step 2
|
||||
let wm_delete_window = unsafe {
|
||||
let mut wm_delete_window = with_c_str("WM_DELETE_WINDOW", |delete_window|
|
||||
(display.xlib.XInternAtom)(display.display, delete_window, 0)
|
||||
);
|
||||
display.check_errors().expect("Failed to call XInternAtom");
|
||||
(display.xlib.XSetWMProtocols)(display.display, window, &mut wm_delete_window, 1);
|
||||
// Opt into handling window close
|
||||
unsafe {
|
||||
(display.xlib.XSetWMProtocols)(display.display, window, &ctx.wm_delete_window as *const _ as *mut _, 1);
|
||||
display.check_errors().expect("Failed to call XSetWMProtocols");
|
||||
(display.xlib.XFlush)(display.display);
|
||||
display.check_errors().expect("Failed to call XFlush");
|
||||
|
||||
wm_delete_window
|
||||
};
|
||||
|
||||
// creating IM
|
||||
let im = unsafe {
|
||||
let _lock = GLOBAL_XOPENIM_LOCK.lock().unwrap();
|
||||
|
||||
let im = (display.xlib.XOpenIM)(display.display, ptr::null_mut(), ptr::null_mut(), ptr::null_mut());
|
||||
if im.is_null() {
|
||||
return Err(OsError(format!("XOpenIM failed")));
|
||||
}
|
||||
im
|
||||
};
|
||||
|
||||
// creating input context
|
||||
let ic = unsafe {
|
||||
let ic = with_c_str("inputStyle", |input_style|
|
||||
with_c_str("clientWindow", |client_window|
|
||||
(display.xlib.XCreateIC)(
|
||||
im, input_style,
|
||||
ffi::XIMPreeditNothing | ffi::XIMStatusNothing, client_window,
|
||||
window, ptr::null::<()>()
|
||||
)
|
||||
)
|
||||
);
|
||||
if ic.is_null() {
|
||||
return Err(OsError(format!("XCreateIC failed")));
|
||||
}
|
||||
(display.xlib.XSetICFocus)(ic);
|
||||
display.check_errors().expect("Failed to call XSetICFocus");
|
||||
ic
|
||||
};
|
||||
|
||||
// Attempt to make keyboard input repeat detectable
|
||||
unsafe {
|
||||
|
@ -570,6 +346,25 @@ impl Window {
|
|||
|
||||
}
|
||||
|
||||
// Select XInput2 events
|
||||
{
|
||||
let mask = ffi::XI_MotionMask
|
||||
| ffi::XI_ButtonPressMask | ffi::XI_ButtonReleaseMask
|
||||
// | ffi::XI_KeyPressMask | ffi::XI_KeyReleaseMask
|
||||
| ffi::XI_EnterMask | ffi::XI_LeaveMask
|
||||
| ffi::XI_FocusInMask | ffi::XI_FocusOutMask
|
||||
| if window_attrs.multitouch { ffi::XI_TouchBeginMask | ffi::XI_TouchUpdateMask | ffi::XI_TouchEndMask } else { 0 };
|
||||
unsafe {
|
||||
let mut event_mask = ffi::XIEventMask{
|
||||
deviceid: ffi::XIAllMasterDevices,
|
||||
mask: mem::transmute::<*const i32, *mut c_uchar>(&mask as *const i32),
|
||||
mask_len: mem::size_of_val(&mask) as c_int,
|
||||
};
|
||||
(display.xinput2.XISelectEvents)(display.display, window,
|
||||
&mut event_mask as *mut ffi::XIEventMask, 1);
|
||||
};
|
||||
}
|
||||
|
||||
// creating the window object
|
||||
let window_proxy_data = WindowProxyData {
|
||||
display: display.clone(),
|
||||
|
@ -581,19 +376,12 @@ impl Window {
|
|||
x: Arc::new(XWindow {
|
||||
display: display.clone(),
|
||||
window: window,
|
||||
im: im,
|
||||
ic: ic,
|
||||
screen_id: screen_id,
|
||||
is_fullscreen: is_fullscreen,
|
||||
xf86_desk_mode: xf86_desk_mode,
|
||||
window_proxy_data: window_proxy_data,
|
||||
}),
|
||||
is_closed: AtomicBool::new(false),
|
||||
wm_delete_window: wm_delete_window,
|
||||
current_size: Cell::new((0, 0)),
|
||||
pending_events: Mutex::new(VecDeque::new()),
|
||||
cursor_state: Mutex::new(CursorState::Normal),
|
||||
input_handler: Mutex::new(XInputEventHandler::new(display, window, ic, window_attrs))
|
||||
};
|
||||
|
||||
window.set_title(&window_attrs.title);
|
||||
|
@ -769,20 +557,6 @@ impl Window {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn poll_events(&self) -> PollEventsIterator {
|
||||
PollEventsIterator {
|
||||
window: self
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn wait_events(&self) -> WaitEventsIterator {
|
||||
WaitEventsIterator {
|
||||
window: self
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_xlib_display(&self) -> *mut libc::c_void {
|
||||
self.x.display.display as *mut libc::c_void
|
||||
|
@ -1017,4 +791,7 @@ impl Window {
|
|||
self.x.display.check_errors().map_err(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId { WindowId(self.x.window) }
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use cocoa::{self, appkit, foundation};
|
||||
use cocoa::appkit::{NSApplication, NSEvent, NSView, NSWindow};
|
||||
use events::{self, ElementState, Event, MouseButton, TouchPhase, WindowEvent, ModifiersState};
|
||||
use events::{self, ElementState, Event, MouseButton, TouchPhase, WindowEvent, ModifiersState, KeyboardInput};
|
||||
use super::window::Window;
|
||||
use std;
|
||||
use super::DeviceId;
|
||||
|
||||
|
||||
pub struct EventsLoop {
|
||||
|
@ -315,8 +316,16 @@ impl EventsLoop {
|
|||
|
||||
let vkey = to_virtual_key_code(NSEvent::keyCode(ns_event));
|
||||
let state = ElementState::Pressed;
|
||||
let code = NSEvent::keyCode(ns_event) as u8;
|
||||
let window_event = WindowEvent::KeyboardInput(state, code, vkey, event_mods(ns_event));
|
||||
let code = NSEvent::keyCode(ns_event) as u32;
|
||||
let window_event = WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: code,
|
||||
virtual_keycode: vkey,
|
||||
modifiers: event_mods(ns_event),
|
||||
},
|
||||
};
|
||||
events.push_back(into_event(window_event));
|
||||
let event = events.pop_front();
|
||||
self.pending_events.lock().unwrap().extend(events.into_iter());
|
||||
|
@ -327,8 +336,16 @@ impl EventsLoop {
|
|||
let vkey = to_virtual_key_code(NSEvent::keyCode(ns_event));
|
||||
|
||||
let state = ElementState::Released;
|
||||
let code = NSEvent::keyCode(ns_event) as u8;
|
||||
let window_event = WindowEvent::KeyboardInput(state, code, vkey, event_mods(ns_event));
|
||||
let code = NSEvent::keyCode(ns_event) as u32;
|
||||
let window_event = WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: code,
|
||||
virtual_keycode: vkey,
|
||||
modifiers: event_mods(ns_event),
|
||||
},
|
||||
};
|
||||
Some(into_event(window_event))
|
||||
},
|
||||
|
||||
|
@ -342,14 +359,30 @@ impl EventsLoop {
|
|||
{
|
||||
if !key_pressed && NSEvent::modifierFlags(event).contains(keymask) {
|
||||
let state = ElementState::Pressed;
|
||||
let code = NSEvent::keyCode(event) as u8;
|
||||
let window_event = WindowEvent::KeyboardInput(state, code, Some(key), event_mods(event));
|
||||
let code = NSEvent::keyCode(event) as u32;
|
||||
let window_event = WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: code,
|
||||
virtual_keycode: Some(key),
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
};
|
||||
Some(window_event)
|
||||
|
||||
} else if key_pressed && !NSEvent::modifierFlags(event).contains(keymask) {
|
||||
let state = ElementState::Released;
|
||||
let code = NSEvent::keyCode(event) as u8;
|
||||
let window_event = WindowEvent::KeyboardInput(state, code, Some(key), event_mods(event));
|
||||
let code = NSEvent::keyCode(event) as u32;
|
||||
let window_event = WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: code,
|
||||
virtual_keycode: Some(key),
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
};
|
||||
Some(window_event)
|
||||
|
||||
} else {
|
||||
|
@ -399,15 +432,15 @@ impl EventsLoop {
|
|||
event
|
||||
},
|
||||
|
||||
appkit::NSLeftMouseDown => { Some(into_event(WindowEvent::MouseInput(ElementState::Pressed, MouseButton::Left))) },
|
||||
appkit::NSLeftMouseUp => { Some(into_event(WindowEvent::MouseInput(ElementState::Released, MouseButton::Left))) },
|
||||
appkit::NSRightMouseDown => { Some(into_event(WindowEvent::MouseInput(ElementState::Pressed, MouseButton::Right))) },
|
||||
appkit::NSRightMouseUp => { Some(into_event(WindowEvent::MouseInput(ElementState::Released, MouseButton::Right))) },
|
||||
appkit::NSOtherMouseDown => { Some(into_event(WindowEvent::MouseInput(ElementState::Pressed, MouseButton::Middle))) },
|
||||
appkit::NSOtherMouseUp => { Some(into_event(WindowEvent::MouseInput(ElementState::Released, MouseButton::Middle))) },
|
||||
appkit::NSLeftMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Left })) },
|
||||
appkit::NSLeftMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Left })) },
|
||||
appkit::NSRightMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Right })) },
|
||||
appkit::NSRightMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Right })) },
|
||||
appkit::NSOtherMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Middle })) },
|
||||
appkit::NSOtherMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Middle })) },
|
||||
|
||||
appkit::NSMouseEntered => { Some(into_event(WindowEvent::MouseEntered)) },
|
||||
appkit::NSMouseExited => { Some(into_event(WindowEvent::MouseLeft)) },
|
||||
appkit::NSMouseEntered => { Some(into_event(WindowEvent::MouseEntered { device_id: DEVICE_ID })) },
|
||||
appkit::NSMouseExited => { Some(into_event(WindowEvent::MouseLeft { device_id: DEVICE_ID })) },
|
||||
|
||||
appkit::NSMouseMoved |
|
||||
appkit::NSLeftMouseDragged |
|
||||
|
@ -433,9 +466,9 @@ impl EventsLoop {
|
|||
let view_rect = NSView::frame(*window.view);
|
||||
let scale_factor = window.hidpi_factor();
|
||||
|
||||
let x = (scale_factor * view_point.x as f32) as i32;
|
||||
let y = (scale_factor * (view_rect.size.height - view_point.y) as f32) as i32;
|
||||
let window_event = WindowEvent::MouseMoved(x, y);
|
||||
let x = (scale_factor * view_point.x as f32) as f64;
|
||||
let y = (scale_factor * (view_rect.size.height - view_point.y) as f32) as f64;
|
||||
let window_event = WindowEvent::MouseMoved { device_id: DEVICE_ID, position: (x, y) };
|
||||
let event = Event::WindowEvent { window_id: ::WindowId(window.id()), event: window_event };
|
||||
Some(event)
|
||||
},
|
||||
|
@ -461,14 +494,14 @@ impl EventsLoop {
|
|||
appkit::NSEventPhaseEnded => TouchPhase::Ended,
|
||||
_ => TouchPhase::Moved,
|
||||
};
|
||||
let window_event = WindowEvent::MouseWheel(delta, phase);
|
||||
let window_event = WindowEvent::MouseWheel { device_id: DEVICE_ID, delta: delta, phase: phase };
|
||||
Some(into_event(window_event))
|
||||
},
|
||||
|
||||
appkit::NSEventTypePressure => {
|
||||
let pressure = ns_event.pressure();
|
||||
let stage = ns_event.stage();
|
||||
let window_event = WindowEvent::TouchpadPressure(pressure, stage);
|
||||
let window_event = WindowEvent::TouchpadPressure { device_id: DEVICE_ID, pressure: pressure, stage: stage };
|
||||
Some(into_event(window_event))
|
||||
},
|
||||
|
||||
|
@ -632,3 +665,6 @@ fn event_mods(event: cocoa::base::id) -> ModifiersState {
|
|||
logo: flags.contains(appkit::NSCommandKeyMask),
|
||||
}
|
||||
}
|
||||
|
||||
// Constant device ID, to be removed when this backend is updated to report real device IDs.
|
||||
const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);
|
||||
|
|
|
@ -4,6 +4,9 @@ pub use self::events_loop::EventsLoop;
|
|||
pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
|
||||
pub use self::window::{Id as WindowId, PlatformSpecificWindowBuilderAttributes, Window};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId;
|
||||
|
||||
use {CreationError};
|
||||
|
||||
pub struct Window2 {
|
||||
|
|
|
@ -8,6 +8,7 @@ use std::os::windows::ffi::OsStringExt;
|
|||
|
||||
use CursorState;
|
||||
use WindowEvent as Event;
|
||||
use KeyboardInput;
|
||||
use events::ModifiersState;
|
||||
use super::event;
|
||||
use super::WindowState;
|
||||
|
@ -140,7 +141,7 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
|
|||
});
|
||||
|
||||
if mouse_outside_window {
|
||||
send_event(window, MouseEntered);
|
||||
send_event(window, MouseEntered { device_id: DEVICE_ID });
|
||||
|
||||
// Calling TrackMouseEvent in order to receive mouse leave events.
|
||||
user32::TrackMouseEvent(&mut winapi::TRACKMOUSEEVENT {
|
||||
|
@ -151,10 +152,10 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
|
|||
});
|
||||
}
|
||||
|
||||
let x = winapi::GET_X_LPARAM(lparam) as i32;
|
||||
let y = winapi::GET_Y_LPARAM(lparam) as i32;
|
||||
let x = winapi::GET_X_LPARAM(lparam) as f64;
|
||||
let y = winapi::GET_Y_LPARAM(lparam) as f64;
|
||||
|
||||
send_event(window, MouseMoved(x, y));
|
||||
send_event(window, MouseMoved { device_id: DEVICE_ID, position: (x, y) });
|
||||
|
||||
0
|
||||
},
|
||||
|
@ -174,7 +175,7 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
|
|||
});
|
||||
|
||||
if mouse_in_window {
|
||||
send_event(window, MouseLeft);
|
||||
send_event(window, MouseLeft { device_id: DEVICE_ID });
|
||||
}
|
||||
|
||||
0
|
||||
|
@ -189,28 +190,42 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
|
|||
let value = value as i32;
|
||||
let value = value as f32 / winapi::WHEEL_DELTA as f32;
|
||||
|
||||
send_event(window, MouseWheel(LineDelta(0.0, value), TouchPhase::Moved));
|
||||
send_event(window, MouseWheel { device_id: DEVICE_ID, delta: LineDelta(0.0, value), phase: TouchPhase::Moved });
|
||||
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_KEYDOWN | winapi::WM_SYSKEYDOWN => {
|
||||
use events::WindowEvent::KeyboardInput;
|
||||
use events::ElementState::Pressed;
|
||||
if msg == winapi::WM_SYSKEYDOWN && wparam as i32 == winapi::VK_F4 {
|
||||
user32::DefWindowProcW(window, msg, wparam, lparam)
|
||||
} else {
|
||||
let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
|
||||
send_event(window, KeyboardInput(Pressed, scancode, vkey, event::get_key_mods()));
|
||||
send_event(window, Event::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: Pressed,
|
||||
scancode: scancode,
|
||||
virtual_keycode: vkey,
|
||||
modifiers: event::get_key_mods(),
|
||||
}
|
||||
});
|
||||
0
|
||||
}
|
||||
},
|
||||
|
||||
winapi::WM_KEYUP | winapi::WM_SYSKEYUP => {
|
||||
use events::WindowEvent::KeyboardInput;
|
||||
use events::ElementState::Released;
|
||||
let (scancode, vkey) = event::vkeycode_to_element(wparam, lparam);
|
||||
send_event(window, KeyboardInput(Released, scancode, vkey, event::get_key_mods()));
|
||||
send_event(window, Event::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: Released,
|
||||
scancode: scancode,
|
||||
virtual_keycode: vkey,
|
||||
modifiers: event::get_key_mods(),
|
||||
},
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
|
@ -218,7 +233,7 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
|
|||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Left;
|
||||
use events::ElementState::Pressed;
|
||||
send_event(window, MouseInput(Pressed, Left));
|
||||
send_event(window, MouseInput { device_id: DEVICE_ID, state: Pressed, button: Left });
|
||||
0
|
||||
},
|
||||
|
||||
|
@ -226,7 +241,7 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
|
|||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Left;
|
||||
use events::ElementState::Released;
|
||||
send_event(window, MouseInput(Released, Left));
|
||||
send_event(window, MouseInput { device_id: DEVICE_ID, state: Released, button: Left });
|
||||
0
|
||||
},
|
||||
|
||||
|
@ -234,7 +249,7 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
|
|||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Right;
|
||||
use events::ElementState::Pressed;
|
||||
send_event(window, MouseInput(Pressed, Right));
|
||||
send_event(window, MouseInput { device_id: DEVICE_ID, state: Pressed, button: Right });
|
||||
0
|
||||
},
|
||||
|
||||
|
@ -242,7 +257,7 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
|
|||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Right;
|
||||
use events::ElementState::Released;
|
||||
send_event(window, MouseInput(Released, Right));
|
||||
send_event(window, MouseInput { device_id: DEVICE_ID, state: Released, button: Right });
|
||||
0
|
||||
},
|
||||
|
||||
|
@ -250,7 +265,7 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
|
|||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Middle;
|
||||
use events::ElementState::Pressed;
|
||||
send_event(window, MouseInput(Pressed, Middle));
|
||||
send_event(window, MouseInput { device_id: DEVICE_ID, state: Pressed, button: Middle });
|
||||
0
|
||||
},
|
||||
|
||||
|
@ -258,7 +273,7 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
|
|||
use events::WindowEvent::MouseInput;
|
||||
use events::MouseButton::Middle;
|
||||
use events::ElementState::Released;
|
||||
send_event(window, MouseInput(Released, Middle));
|
||||
send_event(window, MouseInput { device_id: DEVICE_ID, state: Released, button: Middle });
|
||||
0
|
||||
},
|
||||
|
||||
|
@ -267,7 +282,7 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
|
|||
use events::MouseButton::Other;
|
||||
use events::ElementState::Pressed;
|
||||
let xbutton = winapi::HIWORD(wparam as winapi::DWORD) as winapi::c_int; // waiting on PR for winapi to add GET_XBUTTON_WPARAM
|
||||
send_event(window, MouseInput(Pressed, Other(xbutton as u8)));
|
||||
send_event(window, MouseInput { device_id: DEVICE_ID, state: Pressed, button: Other(xbutton as u8) });
|
||||
0
|
||||
},
|
||||
|
||||
|
@ -276,7 +291,7 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
|
|||
use events::MouseButton::Other;
|
||||
use events::ElementState::Released;
|
||||
let xbutton = winapi::HIWORD(wparam as winapi::DWORD) as winapi::c_int;
|
||||
send_event(window, MouseInput(Released, Other(xbutton as u8)));
|
||||
send_event(window, MouseInput { device_id: DEVICE_ID, state: Released, button: Other(xbutton as u8) });
|
||||
0
|
||||
},
|
||||
|
||||
|
@ -405,3 +420,6 @@ pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Constant device ID, to be removed when this backend is updated to report real device IDs.
|
||||
const DEVICE_ID: ::DeviceId = ::DeviceId(super::DeviceId);
|
||||
|
|
|
@ -26,10 +26,10 @@ pub fn get_key_mods() -> ModifiersState {
|
|||
}
|
||||
|
||||
pub fn vkeycode_to_element(wparam: winapi::WPARAM, lparam: winapi::LPARAM) -> (ScanCode, Option<VirtualKeyCode>) {
|
||||
let scancode = ((lparam >> 16) & 0xff) as u8;
|
||||
let scancode = ((lparam >> 16) & 0xff) as u32;
|
||||
let extended = (lparam & 0x01000000) != 0;
|
||||
let vk = match wparam as i32 {
|
||||
winapi::VK_SHIFT => unsafe { user32::MapVirtualKeyA(scancode as u32, MAPVK_VSC_TO_VK_EX) as i32 },
|
||||
winapi::VK_SHIFT => unsafe { user32::MapVirtualKeyA(scancode, MAPVK_VSC_TO_VK_EX) as i32 },
|
||||
winapi::VK_CONTROL => if extended { winapi::VK_RCONTROL } else { winapi::VK_LCONTROL },
|
||||
winapi::VK_MENU => if extended { winapi::VK_RMENU } else { winapi::VK_LMENU },
|
||||
other => other
|
||||
|
|
Loading…
Reference in a new issue