Merge pull request #164 from Ralith/rich-input

Richer input events
This commit is contained in:
tomaka 2017-05-07 08:35:03 +02:00 committed by GitHub
commit 15aafc2908
19 changed files with 994 additions and 786 deletions

View file

@ -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 {

View file

@ -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(),
_ => ()
}
},
_ => {}
}
});
}

View file

@ -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);
},
_ => (),
}
},
}
_ => {}
}
});
}

View file

@ -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>,

View file

@ -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 {

View file

@ -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 {

View file

@ -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);

View file

@ -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(_) => {

View file

@ -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
);
},

View file

@ -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 {
shift: mods.shift,
ctrl: mods.ctrl,
alt: mods.alt,
logo: mods.logo
}
),
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

View file

@ -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;

View file

@ -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,

View file

@ -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
}

View file

@ -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,
}
}
}

View file

@ -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) }
}

View file

@ -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);

View file

@ -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 {

View file

@ -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);

View file

@ -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