diff options
author | tomaka <pierre.krieger1708@gmail.com> | 2015-02-10 22:34:18 +0100 |
---|---|---|
committer | tomaka <pierre.krieger1708@gmail.com> | 2015-02-10 22:34:18 +0100 |
commit | 4500702a0294065f876ca10e7629fd32e213ca76 (patch) | |
tree | 0bf6f0f1580345f6441765e6e74cc64bcaf7e79a /src | |
parent | 605bd37554655436841e4cd2c4fbb3d046de2330 (diff) | |
parent | 95b1c96181e538004800942ae8ab07a81ec49454 (diff) | |
download | glutin-4500702a0294065f876ca10e7629fd32e213ca76.tar.gz glutin-4500702a0294065f876ca10e7629fd32e213ca76.zip |
Merge pull request #70 from tomaka/fix-iterators
Use platform-specific iterators instead
Diffstat (limited to 'src')
-rw-r--r-- | src/android/mod.rs | 78 | ||||
-rw-r--r-- | src/cocoa/mod.rs | 260 | ||||
-rw-r--r-- | src/lib.rs | 34 | ||||
-rw-r--r-- | src/win32/mod.rs | 90 | ||||
-rw-r--r-- | src/x11/mod.rs | 2 | ||||
-rw-r--r-- | src/x11/window/mod.rs | 317 |
6 files changed, 440 insertions, 341 deletions
diff --git a/src/android/mod.rs b/src/android/mod.rs index ec6e1a3..5f05300 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -74,6 +74,52 @@ unsafe impl Send for HeadlessContext {} #[cfg(feature = "headless")] unsafe impl Sync for HeadlessContext {} +pub struct PollEventsIterator<'a> { + window: &'a Window, +} + +impl<'a> Iterator for PollEventsIterator<'a> { + type Item = Event; + + fn next(&mut self) -> Option<Event> { + match self.window.event_rx.try_recv() { + Ok(event) => { + Some(match event { + android_glue::Event::EventDown => MouseInput(Pressed, MouseButton::Left), + android_glue::Event::EventUp => MouseInput(Released, MouseButton::Left), + android_glue::Event::EventMove(x, y) => MouseMoved((x as i32, y as i32)), + }) + } + Err(_) => { + None + } + } + } +} + +pub struct WaitEventsIterator<'a> { + window: &'a Window, +} + +impl<'a> Iterator for WaitEventsIterator<'a> { + type Item = Event; + + fn next(&mut self) -> Option<Event> { + use std::time::Duration; + use std::old_io::timer; + + loop { + // calling poll_events() + if let Some(ev) = self.window.poll_events().next() { + return Some(ev); + } + + // TODO: Implement a proper way of sleeping on the event queue + timer::sleep(Duration::milliseconds(16)); + } + } +} + impl Window { pub fn new(builder: BuilderAttribs) -> Result<Window, CreationError> { use std::{mem, ptr}; @@ -242,34 +288,16 @@ impl Window { WindowProxy } - pub fn poll_events(&self) -> RingBuf<Event> { - let mut events = RingBuf::new(); - loop { - match self.event_rx.try_recv() { - Ok(event) => match event { - android_glue::Event::EventDown => { - events.push_back(MouseInput(Pressed, MouseButton::Left)); - }, - android_glue::Event::EventUp => { - events.push_back(MouseInput(Released, MouseButton::Left)); - }, - android_glue::Event::EventMove(x, y) => { - events.push_back(MouseMoved((x as i32, y as i32))); - }, - }, - Err(_) => { - break; - }, - } + pub fn poll_events(&self) -> PollEventsIterator { + PollEventsIterator { + window: self } - events } - pub fn wait_events(&self) -> RingBuf<Event> { - use std::time::Duration; - use std::old_io::timer; - timer::sleep(Duration::milliseconds(16)); - self.poll_events() + pub fn wait_events(&self) -> WaitEventsIterator { + WaitEventsIterator { + window: self + } } pub fn make_current(&self) { diff --git a/src/cocoa/mod.rs b/src/cocoa/mod.rs index 44a92cd..98b1879 100644 --- a/src/cocoa/mod.rs +++ b/src/cocoa/mod.rs @@ -25,6 +25,7 @@ use std::ptr; use std::collections::RingBuf; use std::str::FromStr; use std::str::from_utf8; +use std::sync::Mutex; use std::ascii::AsciiExt; use events::Event::{MouseInput, MouseMoved, ReceivedCharacter, KeyboardInput, MouseWheel}; @@ -164,6 +165,9 @@ pub struct Window { resize: Option<fn(u32, u32)>, is_closed: Cell<bool>, + + /// Events that have been retreived with XLib but not dispatched with iterators yet + pending_events: Mutex<RingBuf<Event>>, } #[cfg(feature = "window")] @@ -189,6 +193,146 @@ impl WindowProxy { } } +pub struct PollEventsIterator<'a> { + window: &'a Window, +} + +impl<'a> Iterator for PollEventsIterator<'a> { + type Item = Event; + + fn next(&mut self) -> Option<Event> { + if let Some(ev) = self.window.pending_events.lock().unwrap().pop_front() { + return Some(ev); + } + + unsafe { + let event = NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_( + NSAnyEventMask.bits(), + NSDate::distantPast(nil), + NSDefaultRunLoopMode, + YES); + if event == nil { return None; } + { + // Create a temporary structure with state that delegates called internally + // by sendEvent can read and modify. When that returns, update window state. + // This allows the synchronous resize loop to continue issuing callbacks + // to the user application, by passing handler through to the delegate state. + let mut ds = DelegateState { + is_closed: self.window.is_closed.get(), + context: self.window.context, + window: self.window.window, + view: self.window.view, + handler: self.window.resize, + }; + self.window.delegate.set_state(&mut ds); + NSApp().sendEvent_(event); + self.window.delegate.set_state(ptr::null_mut()); + self.window.is_closed.set(ds.is_closed); + } + + let event = match msg_send()(event, selector("type")) { + NSLeftMouseDown => { Some(MouseInput(Pressed, MouseButton::Left)) }, + NSLeftMouseUp => { Some(MouseInput(Released, MouseButton::Left)) }, + NSRightMouseDown => { Some(MouseInput(Pressed, MouseButton::Right)) }, + NSRightMouseUp => { Some(MouseInput(Released, MouseButton::Right)) }, + NSMouseMoved => { + let window_point = event.locationInWindow(); + let window: id = msg_send()(event, selector("window")); + let view_point = if window == 0 { + let window_rect = self.window.window.convertRectFromScreen_(NSRect::new(window_point, NSSize::new(0.0, 0.0))); + self.window.view.convertPoint_fromView_(window_rect.origin, nil) + } else { + self.window.view.convertPoint_fromView_(window_point, nil) + }; + let view_rect = NSView::frame(self.window.view); + let scale_factor = self.window.hidpi_factor(); + Some(MouseMoved(((scale_factor * view_point.x as f32) as i32, + (scale_factor * (view_rect.size.height - view_point.y) as f32) as i32))) + }, + NSKeyDown => { + let mut events = RingBuf::new(); + let received_c_str = event.characters().UTF8String(); + let received_str = CString::from_slice(c_str_to_bytes(&received_c_str)); + for received_char in from_utf8(received_str.as_bytes()).unwrap().chars() { + if received_char.is_ascii() { + events.push_back(ReceivedCharacter(received_char)); + } + } + + let vkey = event::vkeycode_to_element(NSEvent::keyCode(event)); + events.push_back(KeyboardInput(Pressed, NSEvent::keyCode(event) as u8, vkey)); + let event = events.pop_front(); + self.window.pending_events.lock().unwrap().extend(events.into_iter()); + event + }, + NSKeyUp => { + let vkey = event::vkeycode_to_element(NSEvent::keyCode(event)); + Some(KeyboardInput(Released, NSEvent::keyCode(event) as u8, vkey)) + }, + NSFlagsChanged => { + let mut events = RingBuf::new(); + let shift_modifier = Window::modifier_event(event, appkit::NSShiftKeyMask, events::VirtualKeyCode::LShift, shift_pressed); + if shift_modifier.is_some() { + shift_pressed = !shift_pressed; + events.push_back(shift_modifier.unwrap()); + } + let ctrl_modifier = Window::modifier_event(event, appkit::NSControlKeyMask, events::VirtualKeyCode::LControl, ctrl_pressed); + if ctrl_modifier.is_some() { + ctrl_pressed = !ctrl_pressed; + events.push_back(ctrl_modifier.unwrap()); + } + let win_modifier = Window::modifier_event(event, appkit::NSCommandKeyMask, events::VirtualKeyCode::LWin, win_pressed); + if win_modifier.is_some() { + win_pressed = !win_pressed; + events.push_back(win_modifier.unwrap()); + } + let alt_modifier = Window::modifier_event(event, appkit::NSAlternateKeyMask, events::VirtualKeyCode::LAlt, alt_pressed); + if alt_modifier.is_some() { + alt_pressed = !alt_pressed; + events.push_back(alt_modifier.unwrap()); + } + let event = events.pop_front(); + self.window.pending_events.lock().unwrap().extend(events.into_iter()); + event + }, + NSScrollWheel => { Some(MouseWheel(-event.scrollingDeltaY() as i32)) }, + _ => { None }, + }; + + event + } + } +} + +pub struct WaitEventsIterator<'a> { + window: &'a Window, +} + +impl<'a> Iterator for WaitEventsIterator<'a> { + type Item = Event; + + fn next(&mut self) -> Option<Event> { + loop { + if let Some(ev) = self.window.pending_events.lock().unwrap().pop_front() { + return Some(ev); + } + + unsafe { + let event = NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_( + NSAnyEventMask.bits(), + NSDate::distantFuture(nil), + NSDefaultRunLoopMode, + NO); + } + + // calling poll_events() + if let Some(ev) = self.window.poll_events().next() { + return Some(ev); + } + } + } +} + impl Window { #[cfg(feature = "window")] pub fn new(builder: BuilderAttribs) -> Result<Window, CreationError> { @@ -234,6 +378,7 @@ impl Window { resize: None, is_closed: Cell::new(false), + pending_events: Mutex::new(RingBuf::new()), }; Ok(window) @@ -403,102 +548,16 @@ impl Window { WindowProxy } - pub fn poll_events(&self) -> RingBuf<Event> { - let mut events = RingBuf::new(); - - loop { - unsafe { - let event = NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_( - NSAnyEventMask.bits(), - NSDate::distantPast(nil), - NSDefaultRunLoopMode, - YES); - if event == nil { break; } - { - // Create a temporary structure with state that delegates called internally - // by sendEvent can read and modify. When that returns, update window state. - // This allows the synchronous resize loop to continue issuing callbacks - // to the user application, by passing handler through to the delegate state. - let mut ds = DelegateState { - is_closed: self.is_closed.get(), - context: self.context, - window: self.window, - view: self.view, - handler: self.resize, - }; - self.delegate.set_state(&mut ds); - NSApp().sendEvent_(event); - self.delegate.set_state(ptr::null_mut()); - self.is_closed.set(ds.is_closed); - } - - match msg_send()(event, selector("type")) { - NSLeftMouseDown => { events.push_back(MouseInput(Pressed, MouseButton::Left)); }, - NSLeftMouseUp => { events.push_back(MouseInput(Released, MouseButton::Left)); }, - NSRightMouseDown => { events.push_back(MouseInput(Pressed, MouseButton::Right)); }, - NSRightMouseUp => { events.push_back(MouseInput(Released, MouseButton::Right)); }, - NSMouseMoved => { - let window_point: NSPoint = msg_send()(event, selector("locationInWindow")); - // let window_point = event.locationInWindow(); - let window: id = msg_send()(event, selector("window")); - let view_point = if window == 0 { - let window_rect = self.window.convertRectFromScreen_(NSRect::new(window_point, NSSize::new(0.0, 0.0))); - self.view.convertPoint_fromView_(window_rect.origin, nil) - } else { - self.view.convertPoint_fromView_(window_point, nil) - }; - let view_rect = NSView::frame(self.view); - let scale_factor = self.hidpi_factor(); - events.push_back(MouseMoved(((scale_factor * view_point.x as f32) as i32, - (scale_factor * (view_rect.size.height - view_point.y) as f32) as i32))); - }, - NSKeyDown => { - let received_c_str = event.characters().UTF8String(); - let received_str = CString::from_slice(c_str_to_bytes(&received_c_str)); - for received_char in from_utf8(received_str.as_bytes()).unwrap().chars() { - if received_char.is_ascii() { - events.push_back(ReceivedCharacter(received_char)); - } - } + pub fn poll_events(&self) -> PollEventsIterator { + PollEventsIterator { + window: self + } + } - let vkey = event::vkeycode_to_element(NSEvent::keyCode(event)); - events.push_back(KeyboardInput(Pressed, NSEvent::keyCode(event) as u8, vkey)); - }, - NSKeyUp => { - let vkey = event::vkeycode_to_element(NSEvent::keyCode(event)); - events.push_back(KeyboardInput(Released, NSEvent::keyCode(event) as u8, vkey)); - }, - NSFlagsChanged => { - let shift_modifier = Window::modifier_event(event, appkit::NSShiftKeyMask, events::VirtualKeyCode::LShift, shift_pressed); - if shift_modifier.is_some() { - shift_pressed = !shift_pressed; - events.push_back(shift_modifier.unwrap()); - } - let ctrl_modifier = Window::modifier_event(event, appkit::NSControlKeyMask, events::VirtualKeyCode::LControl, ctrl_pressed); - if ctrl_modifier.is_some() { - ctrl_pressed = !ctrl_pressed; - events.push_back(ctrl_modifier.unwrap()); - } - let win_modifier = Window::modifier_event(event, appkit::NSCommandKeyMask, events::VirtualKeyCode::LWin, win_pressed); - if win_modifier.is_some() { - win_pressed = !win_pressed; - events.push_back(win_modifier.unwrap()); - } - let alt_modifier = Window::modifier_event(event, appkit::NSAlternateKeyMask, events::VirtualKeyCode::LAlt, alt_pressed); - if alt_modifier.is_some() { - alt_pressed = !alt_pressed; - events.push_back(alt_modifier.unwrap()); - } - }, - NSScrollWheel => { events.push_back(MouseWheel(event.scrollingDeltaY() as i32)); }, - NSOtherMouseDown => { }, - NSOtherMouseUp => { }, - NSOtherMouseDragged => { }, - _ => { }, - } - } + pub fn wait_events(&self) -> WaitEventsIterator { + WaitEventsIterator { + window: self } - events } unsafe fn modifier_event(event: id, keymask: NSEventModifierFlags, key: events::VirtualKeyCode, key_pressed: bool) -> Option<Event> { @@ -511,19 +570,6 @@ impl Window { return None; } - pub fn wait_events(&self) -> RingBuf<Event> { - unsafe { - let event = NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_( - NSAnyEventMask.bits(), - NSDate::distantFuture(nil), - NSDefaultRunLoopMode, - NO); - NSApp().sendEvent_(event); - - self.poll_events() - } - } - pub unsafe fn make_current(&self) { let _: id = msg_send()(self.context, selector("update")); self.context.makeCurrentContext(); @@ -592,7 +592,7 @@ impl Window { /// Contrary to `wait_events`, this function never blocks. #[inline] pub fn poll_events(&self) -> PollEventsIterator { - PollEventsIterator { window: self, data: self.window.poll_events().into_iter() } + PollEventsIterator(self.window.poll_events()) } /// Returns an iterator that returns events one by one, blocking if necessary until one is @@ -601,7 +601,7 @@ impl Window { /// The iterator never returns `None`. #[inline] pub fn wait_events(&self) -> WaitEventsIterator { - WaitEventsIterator { window: self, data: self.window.wait_events().into_iter() } + WaitEventsIterator(self.window.wait_events()) } /// Sets the context as the current context. @@ -754,24 +754,13 @@ impl gl_common::GlFunctionsSource for HeadlessContext { // Implementation note: we retreive the list once, then serve each element by one by one. // This may change in the future. #[cfg(feature = "window")] -pub struct PollEventsIterator<'a> { - window: &'a Window, - data: RingBufIter<Event>, -} +pub struct PollEventsIterator<'a>(winimpl::PollEventsIterator<'a>); #[cfg(feature = "window")] impl<'a> Iterator for PollEventsIterator<'a> { type Item = Event; fn next(&mut self) -> Option<Event> { - if let Some(ev) = self.data.next() { - return Some(ev); - } - - let PollEventsIterator { window, data } = self.window.poll_events(); - self.window = window; - self.data = data; - - self.data.next() + self.0.next() } } @@ -779,24 +768,13 @@ impl<'a> Iterator for PollEventsIterator<'a> { // Implementation note: we retreive the list once, then serve each element by one by one. // This may change in the future. #[cfg(feature = "window")] -pub struct WaitEventsIterator<'a> { - window: &'a Window, - data: RingBufIter<Event>, -} +pub struct WaitEventsIterator<'a>(winimpl::WaitEventsIterator<'a>); #[cfg(feature = "window")] impl<'a> Iterator for WaitEventsIterator<'a> { type Item = Event; fn next(&mut self) -> Option<Event> { - if let Some(ev) = self.data.next() { - return Some(ev); - } - - let WaitEventsIterator { window, data } = self.window.wait_events(); - self.window = window; - self.data = data; - - self.next() + self.0.next() } } diff --git a/src/win32/mod.rs b/src/win32/mod.rs index beb7825..c502e3d 100644 --- a/src/win32/mod.rs +++ b/src/win32/mod.rs @@ -201,50 +201,16 @@ impl Window { } /// See the docs in the crate root file. - // TODO: return iterator - pub fn poll_events(&self) -> RingBuf<Event> { - let mut events = RingBuf::new(); - loop { - match self.events_receiver.try_recv() { - Ok(ev) => events.push_back(ev), - Err(_) => break - } + pub fn poll_events(&self) -> PollEventsIterator { + PollEventsIterator { + window: self, } - - // if one of the received events is `Closed`, setting `is_closed` to true - if events.iter().any(|e| match e { &::events::Event::Closed => true, _ => false }) { - use std::sync::atomic::Ordering::Relaxed; - self.is_closed.store(true, Relaxed); - } - - events } /// See the docs in the crate root file. - // TODO: return iterator - pub fn wait_events(&self) -> RingBuf<Event> { - match self.events_receiver.recv() { - Ok(ev) => { - // if the received event is `Closed`, setting `is_closed` to true - match ev { - ::events::Event::Closed => { - use std::sync::atomic::Ordering::Relaxed; - self.is_closed.store(true, Relaxed); - }, - _ => () - }; - - // looing for other possible events in the queue - let mut result = self.poll_events(); - result.insert(0, ev); - result - }, - - Err(_) => { - use std::sync::atomic::Ordering::Relaxed; - self.is_closed.store(true, Relaxed); - RingBuf::new() - } + pub fn wait_events(&self) -> WaitEventsIterator { + WaitEventsIterator { + window: self, } } @@ -294,6 +260,50 @@ impl Window { } } +pub struct PollEventsIterator<'a> { + window: &'a Window, +} + +impl<'a> Iterator for PollEventsIterator<'a> { + type Item = Event; + + fn next(&mut self) -> Option<Event> { + use events::Event::Closed; + + match self.window.events_receiver.recv() { + Ok(Closed) => { + use std::sync::atomic::Ordering::Relaxed; + self.window.is_closed.store(true, Relaxed); + Some(Closed) + }, + Ok(ev) => Some(ev), + Err(_) => None + } + } +} + +pub struct WaitEventsIterator<'a> { + window: &'a Window, +} + +impl<'a> Iterator for WaitEventsIterator<'a> { + type Item = Event; + + fn next(&mut self) -> Option<Event> { + use events::Event::Closed; + + match self.window.events_receiver.recv() { + Ok(Closed) => { + use std::sync::atomic::Ordering::Relaxed; + self.window.is_closed.store(true, Relaxed); + Some(Closed) + }, + Ok(ev) => Some(ev), + Err(_) => None + } + } +} + #[unsafe_destructor] impl Drop for Window { fn drop(&mut self) { diff --git a/src/x11/mod.rs b/src/x11/mod.rs index 22184a9..cc87b2b 100644 --- a/src/x11/mod.rs +++ b/src/x11/mod.rs @@ -3,6 +3,8 @@ pub use self::headless::HeadlessContext; #[cfg(feature = "window")] pub use self::window::{Window, WindowProxy, MonitorID, get_available_monitors, get_primary_monitor}; +#[cfg(feature = "window")] +pub use self::window::{WaitEventsIterator, PollEventsIterator}; mod ffi; diff --git a/src/x11/window/mod.rs b/src/x11/window/mod.rs index bcb8726..b1cd6d4 100644 --- a/src/x11/window/mod.rs +++ b/src/x11/window/mod.rs @@ -7,7 +7,7 @@ use std::cell::Cell; use std::sync::atomic::AtomicBool; use std::collections::RingBuf; use super::ffi; -use std::sync::{Arc, Once, ONCE_INIT, Weak}; +use std::sync::{Arc, Mutex, Once, ONCE_INIT, Weak}; use std::sync::{StaticMutex, MUTEX_INIT}; pub use self::monitor::{MonitorID, get_available_monitors, get_primary_monitor}; @@ -105,11 +105,179 @@ impl WindowProxy { } } +pub struct PollEventsIterator<'a> { + window: &'a Window, +} + +impl<'a> Iterator for PollEventsIterator<'a> { + type Item = Event; + + fn next(&mut self) -> Option<Event> { + use std::num::Int; + + if let Some(ev) = self.window.pending_events.lock().unwrap().pop_front() { + return Some(ev); + } + + let mut xev = unsafe { mem::uninitialized() }; + let res = unsafe { ffi::XCheckMaskEvent(self.window.x.display, Int::max_value(), &mut xev) }; + + if res == 0 { + let res = unsafe { ffi::XCheckTypedEvent(self.window.x.display, ffi::ClientMessage, &mut xev) }; + + if res == 0 { + return None; + } + } + + match xev.type_ { + ffi::KeymapNotify => { + unsafe { ffi::XRefreshKeyboardMapping(&xev) } + }, + + ffi::ClientMessage => { + use events::Event::{Closed, Awakened}; + use std::sync::atomic::Ordering::Relaxed; + + let client_msg: &ffi::XClientMessageEvent = unsafe { mem::transmute(&xev) }; + + if client_msg.l[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::Event::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::MotionNotify => { + use events::Event::MouseMoved; + let event: &ffi::XMotionEvent = unsafe { mem::transmute(&xev) }; + return Some(MouseMoved((event.x as i32, event.y as i32))); + }, + + ffi::KeyPress | ffi::KeyRelease => { + use events::Event::{KeyboardInput, ReceivedCharacter}; + use events::ElementState::{Pressed, Released}; + let event: &mut ffi::XKeyEvent = unsafe { mem::transmute(&xev) }; + + if event.type_ == ffi::KeyPress { + let raw_ev: *mut ffi::XKeyEvent = event; + unsafe { ffi::XFilterEvent(mem::transmute(raw_ev), self.window.x.window) }; + } + + let state = if xev.type_ == ffi::KeyPress { Pressed } else { Released }; + + let written = unsafe { + use std::str; + + let mut buffer: [u8; 16] = [mem::uninitialized(); 16]; + let raw_ev: *mut ffi::XKeyEvent = event; + let count = ffi::Xutf8LookupString(self.window.x.ic, mem::transmute(raw_ev), + mem::transmute(buffer.as_mut_ptr()), + buffer.len() as libc::c_int, ptr::null_mut(), ptr::null_mut()); + + str::from_utf8(&buffer.as_slice()[..count as usize]).unwrap_or("").to_string() + }; + + { + let mut pending = self.window.pending_events.lock().unwrap(); + for chr in written.as_slice().chars() { + pending.push_back(ReceivedCharacter(chr)); + } + } + + let keysym = unsafe { + ffi::XKeycodeToKeysym(self.window.x.display, event.keycode as ffi::KeyCode, 0) + }; + + let vkey = events::keycode_to_element(keysym as libc::c_uint); + + return Some(KeyboardInput(state, event.keycode as u8, vkey)); + }, + + ffi::ButtonPress | ffi::ButtonRelease => { + use events::Event::{MouseInput, MouseWheel}; + use events::ElementState::{Pressed, Released}; + use events::MouseButton::{Left, Right, Middle}; + + let event: &ffi::XButtonEvent = unsafe { mem::transmute(&xev) }; + + let state = if xev.type_ == ffi::ButtonPress { Pressed } else { Released }; + + let button = match event.button { + ffi::Button1 => Some(Left), + ffi::Button2 => Some(Middle), + ffi::Button3 => Some(Right), + ffi::Button4 => { + self.window.pending_events.lock().unwrap().push_back(MouseWheel(1)); + None + } + ffi::Button5 => { + self.window.pending_events.lock().unwrap().push_back(MouseWheel(-1)); + None + } + _ => None + }; + + match button { + Some(button) => + return Some(MouseInput(state, button)), + None => () + }; + }, + + _ => () + }; + + return None; + } +} + +pub struct WaitEventsIterator<'a> { + window: &'a Window, +} + +impl<'a> Iterator for WaitEventsIterator<'a> { + type Item = Event; + + fn next(&mut self) -> Option<Event> { + use std::mem; + + loop { + 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 { ffi::XPeekEvent(self.window.x.display, &mut xev) }; + + // calling poll_events() + if let Some(ev) = self.window.poll_events().next() { + return Some(ev); + } + } + } +} + pub struct Window { 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<RingBuf<Event>>, } impl Window { @@ -419,6 +587,7 @@ impl Window { is_closed: AtomicBool::new(false), wm_delete_window: wm_delete_window, current_size: Cell::new((0, 0)), + pending_events: Mutex::new(RingBuf::new()), }; // returning @@ -500,149 +669,15 @@ impl Window { } } - pub fn poll_events(&self) -> RingBuf<Event> { - use std::mem; - - let mut events = RingBuf::new(); - - loop { - use std::num::Int; - - let mut xev = unsafe { mem::uninitialized() }; - let res = unsafe { ffi::XCheckMaskEvent(self.x.display, Int::max_value(), &mut xev) }; - - if res == 0 { - let res = unsafe { ffi::XCheckTypedEvent(self.x.display, ffi::ClientMessage, &mut xev) }; - - if res == 0 { - break - } - } - - match xev.type_ { - ffi::KeymapNotify => { - unsafe { ffi::XRefreshKeyboardMapping(&xev) } - }, - - ffi::ClientMessage => { - use events::Event::{Closed, Awakened}; - use std::sync::atomic::Ordering::Relaxed; - - let client_msg: &ffi::XClientMessageEvent = unsafe { mem::transmute(&xev) }; - - if client_msg.l[0] == self.wm_delete_window as libc::c_long { - self.is_closed.store(true, Relaxed); - events.push_back(Closed); - } else { - events.push_back(Awakened); - } - }, - - ffi::ConfigureNotify => { - use events::Event::Resized; - let cfg_event: &ffi::XConfigureEvent = unsafe { mem::transmute(&xev) }; - let (current_width, current_height) = self.current_size.get(); - if current_width != cfg_event.width || current_height != cfg_event.height { - self.current_size.set((cfg_event.width, cfg_event.height)); - events.push_back(Resized(cfg_event.width as u32, cfg_event.height as u32)); - } - }, - - ffi::MotionNotify => { - use events::Event::MouseMoved; - let event: &ffi::XMotionEvent = unsafe { mem::transmute(&xev) }; - events.push_back(MouseMoved((event.x as i32, event.y as i32))); - }, - - ffi::KeyPress | ffi::KeyRelease => { - use events::Event::{KeyboardInput, ReceivedCharacter}; - use events::ElementState::{Pressed, Released}; - let event: &mut ffi::XKeyEvent = unsafe { mem::transmute(&xev) }; - - if event.type_ == ffi::KeyPress { - let raw_ev: *mut ffi::XKeyEvent = event; - unsafe { ffi::XFilterEvent(mem::transmute(raw_ev), self.x.window) }; - } - - let state = if xev.type_ == ffi::KeyPress { Pressed } else { Released }; - - let written = unsafe { - use std::str; - - let mut buffer: [u8; 16] = [mem::uninitialized(); 16]; - let raw_ev: *mut ffi::XKeyEvent = event; - let count = ffi::Xutf8LookupString(self.x.ic, mem::transmute(raw_ev), - mem::transmute(buffer.as_mut_ptr()), - buffer.len() as libc::c_int, ptr::null_mut(), ptr::null_mut()); - - str::from_utf8(&buffer.as_slice()[..count as usize]).unwrap_or("").to_string() - }; - - for chr in written.as_slice().chars() { - events.push_back(ReceivedCharacter(chr)); - } - - let keysym = unsafe { - ffi::XKeycodeToKeysym(self.x.display, event.keycode as ffi::KeyCode, 0) - }; - - let vkey = events::keycode_to_element(keysym as libc::c_uint); - - events.push_back(KeyboardInput(state, event.keycode as u8, vkey)); - }, - - ffi::ButtonPress | ffi::ButtonRelease => { - use events::Event::{MouseInput, MouseWheel}; - use events::ElementState::{Pressed, Released}; - use events::MouseButton::{Left, Right, Middle}; - - let event: &ffi::XButtonEvent = unsafe { mem::transmute(&xev) }; - - let state = if xev.type_ == ffi::ButtonPress { Pressed } else { Released }; - - let button = match event.button { - ffi::Button1 => Some(Left), - ffi::Button2 => Some(Middle), - ffi::Button3 => Some(Right), - ffi::Button4 => { - events.push_back(MouseWheel(1)); - None - } - ffi::Button5 => { - events.push_back(MouseWheel(-1)); - None - } - _ => None - }; - - match button { - Some(button) => - events.push_back(MouseInput(state, button)), - None => () - }; - }, - - _ => () - } + pub fn poll_events(&self) -> PollEventsIterator { + PollEventsIterator { + window: self } - - events } - pub fn wait_events(&self) -> RingBuf<Event> { - use std::mem; - - loop { - // this will block until an event arrives, but doesn't remove - // it from the queue - let mut xev = unsafe { mem::uninitialized() }; - unsafe { ffi::XPeekEvent(self.x.display, &mut xev) }; - - // calling poll_events() - let ev = self.poll_events(); - if ev.len() >= 1 { - return ev; - } + pub fn wait_events(&self) -> WaitEventsIterator { + WaitEventsIterator { + window: self } } |