diff options
-rw-r--r-- | README.md | 13 | ||||
-rw-r--r-- | appveyor.yml | 16 | ||||
-rw-r--r-- | etc/32bits/libgdi32.a | bin | 0 -> 257548 bytes | |||
-rw-r--r-- | etc/32bits/libopengl32.a | bin | 0 -> 269090 bytes | |||
-rw-r--r-- | etc/64bits/libgdi32.a | bin | 0 -> 449524 bytes | |||
-rw-r--r-- | etc/64bits/libopengl32.a | bin | 0 -> 269008 bytes | |||
-rw-r--r-- | examples/support/mod.rs | 14 | ||||
-rw-r--r-- | src/android/ffi.rs | 7 | ||||
-rw-r--r-- | src/android/mod.rs | 5 | ||||
-rw-r--r-- | src/events.rs | 4 | ||||
-rw-r--r-- | src/lib.rs | 24 | ||||
-rw-r--r-- | src/osx/event.rs | 156 | ||||
-rw-r--r-- | src/osx/mod.rs | 99 | ||||
-rw-r--r-- | src/win32/ffi.rs | 18 | ||||
-rw-r--r-- | src/win32/init.rs | 21 | ||||
-rw-r--r-- | src/win32/mod.rs | 21 | ||||
-rw-r--r-- | src/win32/monitor.rs | 2 | ||||
-rw-r--r-- | src/x11/ffi.rs | 35 | ||||
-rw-r--r-- | src/x11/window/events.rs | 12 | ||||
-rw-r--r-- | src/x11/window/mod.rs | 109 | ||||
-rw-r--r-- | src/x11/window/monitor.rs | 8 | ||||
-rw-r--r-- | tests/headless.rs | 10 |
22 files changed, 506 insertions, 68 deletions
@@ -10,8 +10,7 @@ Alternative to GLFW in pure Rust. ```bash git clone https://github.com/tomaka/glutin cd glutin -cargo test -./target/test/window # or target\test\window.exe +cargo run --example window ``` ## Usage @@ -44,8 +43,8 @@ fn main() { ### Android - - To compile the examples for android, initialize the submodules, go to `deps/apk-builder/apk-builder` and run `cargo build`, then go back to `gl-init` and call `ANDROID_HOME=/path/to/sdk NDK_HOME=/path/to/ndk NDK_STANDALONE=/path/to/standalone cargo test --no-run --target=arm-linux-androideabi` - - Events are not implemented + - To compile the examples for android, initialize the submodules, go to `deps/apk-builder/apk-builder` and run `cargo build`, then go back to `glutin` and call `ANDROID_HOME=/path/to/sdk NDK_HOME=/path/to/ndk NDK_STANDALONE=/path/to/standalone cargo test --no-run --target=arm-linux-androideabi` + - Events and vsync are not implemented - Headless rendering doesn't work ### Emscripten @@ -54,7 +53,9 @@ fn main() { ### OS/X - - Events are not implemented + - Some events are not implemented + - Implementation is still work-in-progress + - Vsync not implemented ### Win32 @@ -66,4 +67,4 @@ fn main() { - Some input events are not implemented - Pixel formats not implemented - - The implementation probably needs a cleanup + - Vsync not implemented diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..ed0256b --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,16 @@ +install: + - ps: Start-FileDownload 'https://static.rust-lang.org/dist/rust-nightly-i686-w64-mingw32.exe' + - ps: Start-FileDownload 'https://static.rust-lang.org/cargo-dist/cargo-nightly-i686-w64-mingw32.tar.gz' + - rust-nightly-i686-w64-mingw32.exe /VERYSILENT /NORESTART + - 7z e cargo-nightly-i686-w64-mingw32.tar.gz + - 7z x cargo-nightly-i686-w64-mingw32.tar + - SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin + - SET PATH=%PATH%;%CD%\cargo-nightly-i686-w64-mingw32\bin + - mkdir %HOMEDRIVE%%HOMEPATH%\.rust + - cp etc/32bits/* %HOMEDRIVE%%HOMEPATH%\.rust + +build: false + +test_script: + - cargo test --verbose --no-default-features --features "window" + - cargo test --verbose --no-default-features --features "headless" diff --git a/etc/32bits/libgdi32.a b/etc/32bits/libgdi32.a Binary files differnew file mode 100644 index 0000000..013890d --- /dev/null +++ b/etc/32bits/libgdi32.a diff --git a/etc/32bits/libopengl32.a b/etc/32bits/libopengl32.a Binary files differnew file mode 100644 index 0000000..f0ec114 --- /dev/null +++ b/etc/32bits/libopengl32.a diff --git a/etc/64bits/libgdi32.a b/etc/64bits/libgdi32.a Binary files differnew file mode 100644 index 0000000..1ea92d2 --- /dev/null +++ b/etc/64bits/libgdi32.a diff --git a/etc/64bits/libopengl32.a b/etc/64bits/libopengl32.a Binary files differnew file mode 100644 index 0000000..7394b96 --- /dev/null +++ b/etc/64bits/libopengl32.a diff --git a/examples/support/mod.rs b/examples/support/mod.rs index f27c4e7..12aa68c 100644 --- a/examples/support/mod.rs +++ b/examples/support/mod.rs @@ -7,13 +7,23 @@ use glutin; #[cfg(not(target_os = "android"))] mod gl { - generate_gl_bindings!("gl", "core", "1.1", "struct") + generate_gl_bindings! { + api: "gl", + profile: "core", + version: "1.1", + generator: "struct" + } } #[cfg(target_os = "android")] mod gl { pub use self::Gles1 as Gl; - generate_gl_bindings!("gles1", "core", "1.1", "struct") + generate_gl_bindings! { + api: "gles1", + profile: "core", + version: "1.1", + generator: "struct" + } } pub struct Context { diff --git a/src/android/ffi.rs b/src/android/ffi.rs index a7482e3..b65f44d 100644 --- a/src/android/ffi.rs +++ b/src/android/ffi.rs @@ -17,7 +17,12 @@ pub mod egl { pub type NativePixmapType = super::EGLNativePixmapType; pub type NativeWindowType = super::EGLNativeWindowType; - generate_gl_bindings!("egl", "core", "1.5", "static") + generate_gl_bindings! { + api: "egl", + profile: "core", + version: "1.5", + generator: "static" + } } pub type khronos_utime_nanoseconds_t = khronos_uint64_t; diff --git a/src/android/mod.rs b/src/android/mod.rs index d900abe..db3dd19 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -1,6 +1,7 @@ extern crate android_glue; extern crate native; +use libc; use {Event, WindowBuilder}; pub struct Window { @@ -186,6 +187,10 @@ impl Window { ffi::egl::SwapBuffers(self.display, self.surface); } } + + pub fn platform_display(&self) -> *mut libc::c_void { + self.display as *mut libc::c_void + } } #[unsafe_destructor] diff --git a/src/events.rs b/src/events.rs index f28fb59..0c11a75 100644 --- a/src/events.rs +++ b/src/events.rs @@ -128,7 +128,8 @@ pub enum VirtualKeyCode { Kana, Kanji, L, - LCracket, + LAlt, + LBracket, LControl, Left, LMenu, @@ -171,6 +172,7 @@ pub enum VirtualKeyCode { Prevtrack, Q, R, + RAlt, RBracket, RControl, Return, @@ -37,6 +37,8 @@ extern crate libc; extern crate cocoa; #[cfg(target_os = "macos")] extern crate core_foundation; +#[cfg(target_os = "linux")] +extern crate sync; pub use events::*; @@ -71,6 +73,7 @@ pub struct WindowBuilder { title: String, monitor: Option<winimpl::MonitorID>, gl_version: Option<(uint, uint)>, + vsync: bool, } #[cfg(feature = "window")] @@ -79,9 +82,10 @@ impl WindowBuilder { pub fn new() -> WindowBuilder { WindowBuilder { dimensions: None, - title: "gl-init-rs window".to_string(), + title: "glutin window".to_string(), monitor: None, gl_version: None, + vsync: false, } } @@ -117,6 +121,12 @@ impl WindowBuilder { self } + /// Requests that the window has vsync enabled. + pub fn with_vsync(mut self) -> WindowBuilder { + self.vsync = true; + self + } + /// Builds the window. /// /// Error should be very rare and only occur in case of permission denied, incompatible system, @@ -337,10 +347,22 @@ impl Window { /// /// You should call this function every time you have finished rendering, or the image /// may not be displayed on the screen. + /// + /// **Warning**: if you enabled vsync, this function will block until the next time the screen + /// is refreshed. However drivers can choose to override your vsync settings, which means that + /// you can't know in advance whether `swap_buffers` will block or not. #[inline] pub fn swap_buffers(&self) { self.window.swap_buffers() } + + /// Gets the native platform specific display for this window. + /// This is typically only required when integrating with + /// other libraries that need this information. + #[inline] + pub unsafe fn platform_display(&self) -> *mut libc::c_void { + self.window.platform_display() + } } /// Represents a headless OpenGL context. diff --git a/src/osx/event.rs b/src/osx/event.rs new file mode 100644 index 0000000..a4128a4 --- /dev/null +++ b/src/osx/event.rs @@ -0,0 +1,156 @@ +use events; +use events::KeyModifiers; +use cocoa::base::NSUInteger; +use cocoa::appkit; + +pub fn modifierflag_to_element(flag: NSUInteger) -> KeyModifiers { + let mut modifiers = KeyModifiers::empty(); + if flag & appkit::NSShiftKeyMask as u64 == appkit::NSShiftKeyMask as u64 { + modifiers = modifiers | events::LEFT_SHIFT_MODIFIER; + } + if flag & appkit::NSControlKeyMask as u64 == appkit::NSControlKeyMask as u64 { + modifiers = modifiers | events::LEFT_CONTROL_MODIFIER; + } + if flag & appkit::NSAlternateKeyMask as u64 == appkit::NSAlternateKeyMask as u64 { + modifiers = modifiers | events::LEFT_ALT_MODIFIER; + } + if flag & appkit::NSNumericPadKeyMask as u64 == appkit::NSNumericPadKeyMask as u64 { + modifiers = modifiers | events::NUM_LOCK_MODIFIER; + } + modifiers +} + +pub fn vkeycode_to_element(code: u16) -> Option<events::VirtualKeyCode> { + Some(match code { + 0x00 => events::A, + 0x01 => events::S, + 0x02 => events::D, + 0x03 => events::F, + 0x04 => events::H, + 0x05 => events::G, + 0x06 => events::Z, + 0x07 => events::X, + 0x08 => events::C, + 0x09 => events::V, + //0x0a => World 1, + 0x0b => events::B, + 0x0c => events::Q, + 0x0d => events::W, + 0x0e => events::E, + 0x0f => events::R, + 0x10 => events::Y, + 0x11 => events::T, + 0x12 => events::Key1, + 0x13 => events::Key2, + 0x14 => events::Key3, + 0x15 => events::Key4, + 0x16 => events::Key6, + 0x17 => events::Key5, + 0x18 => events::Equals, + 0x19 => events::Key9, + 0x1a => events::Key7, + 0x1b => events::Minus, + 0x1c => events::Key8, + 0x1d => events::Key0, + 0x1e => events::RBracket, + 0x1f => events::O, + 0x20 => events::U, + 0x21 => events::LBracket, + 0x22 => events::I, + 0x23 => events::P, + 0x24 => events::Return, + 0x25 => events::L, + 0x26 => events::J, + 0x27 => events::Apostrophe, + 0x28 => events::K, + 0x29 => events::Semicolon, + 0x2a => events::Backslash, + 0x2b => events::Comma, + 0x2c => events::Slash, + 0x2d => events::N, + 0x2e => events::M, + 0x2f => events::Period, + 0x30 => events::Tab, + 0x31 => events::Space, + 0x32 => events::Grave, + 0x33 => events::Back, + //0x34 => unkown, + 0x35 => events::Escape, + 0x36 => events::RWin, + 0x37 => events::LWin, + 0x38 => events::LShift, + //0x39 => Caps lock, + //0x3a => Left alt, + 0x3b => events::LControl, + 0x3c => events::RShift, + //0x3d => Right alt, + 0x3e => events::RControl, + //0x3f => Fn key, + //0x40 => F17 Key, + 0x41 => events::Decimal, + //0x42 -> unkown, + 0x43 => events::Multiply, + //0x44 => unkown, + 0x45 => events::Add, + //0x46 => unkown, + 0x47 => events::Numlock, + //0x48 => KeypadClear, + 0x49 => events::VolumeUp, + 0x4a => events::VolumeDown, + 0x4b => events::Divide, + 0x4c => events::NumpadEnter, + //0x4d => unkown, + 0x4e => events::Subtract, + //0x4f => F18 key, + //0x50 => F19 Key, + 0x51 => events::NumpadEquals, + 0x52 => events::Numpad0, + 0x53 => events::Numpad1, + 0x54 => events::Numpad2, + 0x55 => events::Numpad3, + 0x56 => events::Numpad4, + 0x57 => events::Numpad5, + 0x58 => events::Numpad6, + 0x59 => events::Numpad7, + //0x5a => F20 Key, + 0x5b => events::Numpad8, + 0x5c => events::Numpad9, + //0x5d => unkown, + //0x5e => unkown, + //0x5f => unkown, + 0x60 => events::F5, + 0x61 => events::F6, + 0x62 => events::F7, + 0x63 => events::F3, + 0x64 => events::F8, + 0x65 => events::F9, + //0x66 => unkown, + 0x67 => events::F11, + //0x68 => unkown, + 0x69 => events::F13, + //0x6a => F16 Key, + 0x6b => events::F14, + //0x6c => unkown, + 0x6d => events::F10, + //0x6e => unkown, + 0x6f => events::F12, + //0x70 => unkown, + 0x71 => events::F15, + 0x72 => events::Insert, + 0x73 => events::Home, + 0x74 => events::PageUp, + 0x75 => events::Delete, + 0x76 => events::F4, + 0x77 => events::End, + 0x78 => events::F2, + 0x79 => events::PageDown, + 0x7a => events::F1, + 0x7b => events::Left, + 0x7c => events::Right, + 0x7d => events::Down, + 0x7e => events::Up, + //0x7f => unkown, + + _ => return None, + }) +} diff --git a/src/osx/mod.rs b/src/osx/mod.rs index 78d147f..287dcb4 100644 --- a/src/osx/mod.rs +++ b/src/osx/mod.rs @@ -1,4 +1,6 @@ use Event; +use libc; +use std::sync::atomic::AtomicBool; #[cfg(feature = "window")] use WindowBuilder; @@ -7,14 +9,30 @@ use WindowBuilder; use HeadlessRendererBuilder; use cocoa::base::{id, NSUInteger, nil}; +use cocoa::appkit; use cocoa::appkit::*; use core_foundation::base::TCFType; use core_foundation::string::CFString; use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName}; +use std::c_str::CString; +use {MouseInput, Pressed, Released, LeftMouseButton, RightMouseButton, MouseMoved, ReceivedCharacter, + KeyboardInput, KeyModifiers}; + +use events; + +mod event; + +static mut shift_pressed: bool = false; +static mut ctrl_pressed: bool = false; +static mut win_pressed: bool = false; +static mut alt_pressed: bool = false; + pub struct Window { + view: id, context: id, + is_closed: AtomicBool, } pub struct HeadlessContext(Window); @@ -86,7 +104,9 @@ impl Window { } let window = Window { + view: view, context: context, + is_closed: AtomicBool::new(false), }; Ok(window) @@ -123,6 +143,7 @@ impl Window { let title = NSString::alloc(nil).init_str(title); window.setTitle_(title); window.center(); + window.setAcceptsMouseMovedEvents_(true); Some(window) } } @@ -135,7 +156,7 @@ impl Window { None } else { view.setWantsBestResolutionOpenGLSurface_(true); - window.setContentView(view); + window.setContentView_(view); Some(view) } } @@ -169,8 +190,8 @@ impl Window { } pub fn is_closed(&self) -> bool { - // TODO: remove fake implementation - false + use std::sync::atomic::Relaxed; + self.is_closed.load(Relaxed) } pub fn set_title(&self, _title: &str) { @@ -202,23 +223,63 @@ impl Window { loop { unsafe { - use {MouseInput, Pressed, Released, LeftMouseButton, RightMouseButton}; let event = NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_( NSAnyEventMask as u64, NSDate::distantPast(nil), NSDefaultRunLoopMode, true); if event == nil { break; } + NSApp().sendEvent_(event); match event.get_type() { NSLeftMouseDown => { events.push(MouseInput(Pressed, LeftMouseButton)); }, NSLeftMouseUp => { events.push(MouseInput(Released, LeftMouseButton)); }, NSRightMouseDown => { events.push(MouseInput(Pressed, RightMouseButton)); }, NSRightMouseUp => { events.push(MouseInput(Released, RightMouseButton)); }, - NSMouseMoved => { }, - NSKeyDown => { }, - NSKeyUp => { }, - NSFlagsChanged => { }, + NSMouseMoved => { + let window_point = event.locationInWindow(); + let view_point = self.view.convertPoint_fromView_(window_point, nil); + events.push(MouseMoved((view_point.x as int, view_point.y as int))); + }, + NSKeyDown => { + let received_str = CString::new(event.characters().UTF8String(), false); + for received_char in received_str.as_str().unwrap().chars() { + if received_char.is_ascii() { + events.push(ReceivedCharacter(received_char)); + } + } + + let vkey = event::vkeycode_to_element(event.keycode()); + let modifiers = event::modifierflag_to_element(event.modifierFlags()); + events.push(KeyboardInput(Pressed, event.keycode() as u8, vkey, modifiers)); + }, + NSKeyUp => { + let vkey = event::vkeycode_to_element(event.keycode()); + let modifiers = event::modifierflag_to_element(event.modifierFlags()); + events.push(KeyboardInput(Released, event.keycode() as u8, vkey, modifiers)); + }, + NSFlagsChanged => { + let shift_modifier = Window::modifier_event(event, appkit::NSShiftKeyMask as u64, events::LShift, shift_pressed); + if shift_modifier.is_some() { + shift_pressed = !shift_pressed; + events.push(shift_modifier.unwrap()); + } + let ctrl_modifier = Window::modifier_event(event, appkit::NSControlKeyMask as u64, events::LControl, ctrl_pressed); + if ctrl_modifier.is_some() { + ctrl_pressed = !ctrl_pressed; + events.push(ctrl_modifier.unwrap()); + } + let win_modifier = Window::modifier_event(event, appkit::NSCommandKeyMask as u64, events::LWin, win_pressed); + if win_modifier.is_some() { + win_pressed = !win_pressed; + events.push(win_modifier.unwrap()); + } + let alt_modifier = Window::modifier_event(event, appkit::NSAlternateKeyMask as u64, events::LAlt, alt_pressed); + if alt_modifier.is_some() { + alt_pressed = !alt_pressed; + events.push(alt_modifier.unwrap()); + } + }, NSScrollWheel => { }, NSOtherMouseDown => { }, NSOtherMouseUp => { }, @@ -230,14 +291,30 @@ impl Window { events } + unsafe fn modifier_event(event: id, keymask: u64, key: events::VirtualKeyCode, key_pressed: bool) -> Option<Event> { + if !key_pressed && Window::modifier_key_pressed(event, keymask) { + return Some(KeyboardInput(Pressed, event.keycode() as u8, Some(key), KeyModifiers::empty())); + } + else if key_pressed && !Window::modifier_key_pressed(event, keymask) { + return Some(KeyboardInput(Released, event.keycode() as u8, Some(key), KeyModifiers::empty())); + } + + return None; + } + + unsafe fn modifier_key_pressed(event: id, modifier: u64) -> bool { + event.modifierFlags() & modifier != 0 + } + pub fn wait_events(&self) -> Vec<Event> { unsafe { let event = NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_( NSAnyEventMask as u64, NSDate::distantFuture(nil), NSDefaultRunLoopMode, - true); + false); NSApp().sendEvent_(event); + self.poll_events() } } @@ -261,4 +338,8 @@ impl Window { pub fn swap_buffers(&self) { unsafe { self.context.flushBuffer(); } } + + pub fn platform_display(&self) -> *mut libc::c_void { + unimplemented!() + } } diff --git a/src/win32/ffi.rs b/src/win32/ffi.rs index 2385954..71f8d55 100644 --- a/src/win32/ffi.rs +++ b/src/win32/ffi.rs @@ -7,12 +7,26 @@ use libc; /// WGL bindings pub mod wgl { - generate_gl_bindings!("wgl", "core", "1.0", "static") + generate_gl_bindings! { + api: "wgl", + profile: "core", + version: "1.0", + generator: "static" + } } /// Functions that are not necessarly always available pub mod wgl_extra { - generate_gl_bindings!("wgl", "core", "1.0", "struct", [ "WGL_ARB_create_context" ]) + generate_gl_bindings! { + api: "wgl", + profile: "core", + version: "1.0", + generator: "struct", + extensions: [ + "WGL_ARB_create_context", + "WGL_EXT_swap_control" + ] + } } // http://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx diff --git a/src/win32/init.rs b/src/win32/init.rs index bb3f53b..5168cc6 100644 --- a/src/win32/init.rs +++ b/src/win32/init.rs @@ -2,7 +2,7 @@ extern crate native; use self::native::NativeTaskBuilder; use std::task::TaskBuilder; -use std::sync::atomics::AtomicBool; +use std::sync::atomic::AtomicBool; use std::ptr; use super::{event, ffi}; use super::Window; @@ -16,7 +16,7 @@ local_data_key!(WINDOW: (ffi::HWND, Sender<Event>)) pub fn new_window(builder_dimensions: Option<(uint, uint)>, builder_title: String, builder_monitor: Option<super::MonitorID>, - builder_gl_version: Option<(uint, uint)>, + builder_gl_version: Option<(uint, uint)>, builder_vsync: bool, builder_headless: bool) -> Result<Window, String> { use std::mem; @@ -346,6 +346,23 @@ pub fn new_window(builder_dimensions: Option<(uint, uint)>, builder_title: Strin lib }; + // handling vsync + if builder_vsync { + if extra_functions.SwapIntervalEXT.is_loaded() { + unsafe { ffi::wgl::MakeCurrent(hdc, context) }; + if extra_functions.SwapIntervalEXT(1) == 0 { + tx.send(Err(format!("wglSwapIntervalEXT failed"))); + unsafe { ffi::wgl::DeleteContext(context); } + unsafe { ffi::DestroyWindow(real_window); } + return; + } + + // it is important to remove the current context, otherwise you get very weird + // errors + unsafe { ffi::wgl::MakeCurrent(ptr::null(), ptr::null()); } + } + } + // building the struct let window = Window{ window: real_window, diff --git a/src/win32/mod.rs b/src/win32/mod.rs index 8be3fdb..3cfdc7e 100644 --- a/src/win32/mod.rs +++ b/src/win32/mod.rs @@ -1,5 +1,6 @@ -use std::sync::atomics::AtomicBool; +use std::sync::atomic::AtomicBool; use std::ptr; +use libc; use Event; #[cfg(feature = "window")] @@ -24,7 +25,7 @@ impl HeadlessContext { /// See the docs in the crate root file. pub fn new(builder: HeadlessRendererBuilder) -> Result<HeadlessContext, String> { let HeadlessRendererBuilder { dimensions, gl_version } = builder; - init::new_window(Some(dimensions), "".to_string(), None, gl_version, true) + init::new_window(Some(dimensions), "".to_string(), None, gl_version, false, true) .map(|w| HeadlessContext(w)) } @@ -67,15 +68,15 @@ pub struct Window { impl Window { /// See the docs in the crate root file. pub fn new(builder: WindowBuilder) -> Result<Window, String> { - let WindowBuilder { dimensions, title, monitor, gl_version } = builder; - init::new_window(dimensions, title, monitor, gl_version, false) + let WindowBuilder { dimensions, title, monitor, gl_version, vsync } = builder; + init::new_window(dimensions, title, monitor, gl_version, vsync, false) } } impl Window { /// See the docs in the crate root file. pub fn is_closed(&self) -> bool { - use std::sync::atomics::Relaxed; + use std::sync::atomic::Relaxed; self.is_closed.load(Relaxed) } @@ -170,7 +171,7 @@ impl Window { // if one of the received events is `Closed`, setting `is_closed` to true if events.iter().find(|e| match e { &&::Closed => true, _ => false }).is_some() { - use std::sync::atomics::Relaxed; + use std::sync::atomic::Relaxed; self.is_closed.store(true, Relaxed); } @@ -185,7 +186,7 @@ impl Window { // if the received event is `Closed`, setting `is_closed` to true match ev { ::Closed => { - use std::sync::atomics::Relaxed; + use std::sync::atomic::Relaxed; self.is_closed.store(true, Relaxed); }, _ => () @@ -198,7 +199,7 @@ impl Window { }, Err(_) => { - use std::sync::atomics::Relaxed; + use std::sync::atomic::Relaxed; self.is_closed.store(true, Relaxed); vec![] } @@ -233,6 +234,10 @@ impl Window { ffi::SwapBuffers(self.hdc); } } + + pub fn platform_display(&self) -> *mut libc::c_void { + unimplemented!() + } } #[unsafe_destructor] diff --git a/src/win32/monitor.rs b/src/win32/monitor.rs index a45c8fb..3e6b79a 100644 --- a/src/win32/monitor.rs +++ b/src/win32/monitor.rs @@ -101,7 +101,7 @@ pub fn get_primary_monitor() -> MonitorID { } } - fail!("Failed to find the primary monitor") + panic!("Failed to find the primary monitor") } impl MonitorID { diff --git a/src/x11/ffi.rs b/src/x11/ffi.rs index 39a6ae5..6fbfd81 100644 --- a/src/x11/ffi.rs +++ b/src/x11/ffi.rs @@ -8,12 +8,25 @@ use libc; /// GLX bindings pub mod glx { - generate_gl_bindings!("glx", "core", "1.4", "static") + generate_gl_bindings! { + api: "glx", + profile: "core", + version: "1.4", + generator: "static" + } } /// Functions that are not necessarly always available pub mod glx_extra { - generate_gl_bindings!("glx", "core", "1.4", "struct", [ "GLX_ARB_create_context" ]) + generate_gl_bindings! { + api: "glx", + profile: "core", + version: "1.4", + generator: "struct", + extensions: [ + "GLX_ARB_create_context" + ] + } } pub type Atom = libc::c_ulong; @@ -1325,6 +1338,23 @@ pub struct XButtonEvent { } #[repr(C)] +pub struct XConfigureEvent { + pub type_: libc::c_int, + pub serial: libc::c_ulong, + pub send_event: Bool, + pub display: *mut Display, + pub event: Window, + pub window: Window, + pub x: libc::c_int, + pub y: libc::c_int, + pub width: libc::c_int, + pub height: libc::c_int, + pub border_width: libc::c_int, + pub above: Window, + pub override_redirect: Bool, +} + +#[repr(C)] pub struct XF86VidModeModeInfo { pub dotclock: libc::c_uint, pub hdisplay: libc::c_ushort, @@ -1397,6 +1427,7 @@ extern "C" { pub fn XMoveWindow(display: *mut Display, w: Window, x: libc::c_int, y: libc::c_int); pub fn XMapWindow(display: *mut Display, w: Window); pub fn XNextEvent(display: *mut Display, event_return: *mut XEvent); + pub fn XInitThreads() -> Status; pub fn XOpenDisplay(display_name: *const libc::c_char) -> *mut Display; pub fn XPeekEvent(display: *mut Display, event_return: *mut XEvent); pub fn XRefreshKeyboardMapping(event_map: *const XEvent); diff --git a/src/x11/window/events.rs b/src/x11/window/events.rs index 4d74eed..bf91e6a 100644 --- a/src/x11/window/events.rs +++ b/src/x11/window/events.rs @@ -4,7 +4,7 @@ use VirtualKeyCode; pub fn keycode_to_element(scancode: libc::c_uint) -> Option<VirtualKeyCode> { Some(match scancode { - //ffi::XK_BackSpace => events::Backspace, + ffi::XK_BackSpace => events::Back, ffi::XK_Tab => events::Tab, //ffi::XK_Linefeed => events::Linefeed, //ffi::XK_Clear => events::Clear, @@ -38,9 +38,9 @@ pub fn keycode_to_element(scancode: libc::c_uint) -> Option<VirtualKeyCode> { ffi::XK_Right => events::Right, ffi::XK_Down => events::Down, //ffi::XK_Prior => events::Prior, - //ffi::XK_Page_Up => events::Page_up, + ffi::XK_Page_Up => events::PageUp, //ffi::XK_Next => events::Next, - //ffi::XK_Page_Down => events::Page_down, + ffi::XK_Page_Down => events::PageDown, //ffi::XK_End => events::End, //ffi::XK_Begin => events::Begin, //ffi::XK_Win_L => events::Win_l, @@ -185,7 +185,7 @@ pub fn keycode_to_element(scancode: libc::c_uint) -> Option<VirtualKeyCode> { //ffi::XK_asterisk => events::Asterisk, //ffi::XK_plus => events::Plus, //ffi::XK_comma => events::Comma, - //ffi::XK_minus => events::Minus, + ffi::XK_minus => events::Minus, //ffi::XK_period => events::Period, //ffi::XK_slash => events::Slash, //ffi::XK_0 => events::0, @@ -201,7 +201,7 @@ pub fn keycode_to_element(scancode: libc::c_uint) -> Option<VirtualKeyCode> { //ffi::XK_colon => events::Colon, //ffi::XK_semicolon => events::Semicolon, //ffi::XK_less => events::Less, - //ffi::XK_equal => events::Equal, + ffi::XK_equal => events::Equals, //ffi::XK_greater => events::Greater, //ffi::XK_question => events::Question, //ffi::XK_at => events::At, @@ -999,4 +999,4 @@ pub fn keycode_to_element(scancode: libc::c_uint) -> Option<VirtualKeyCode> { //ffi::XK_Hebrew_switch => events::Hebrew_switch, _ => return None }) -}
\ No newline at end of file +} diff --git a/src/x11/window/mod.rs b/src/x11/window/mod.rs index ad10e16..26d5497 100644 --- a/src/x11/window/mod.rs +++ b/src/x11/window/mod.rs @@ -1,14 +1,26 @@ -use {Event, WindowBuilder}; +use {Event, WindowBuilder, KeyModifiers}; use libc; use std::{mem, ptr}; -use std::sync::atomics::AtomicBool; +use std::cell::Cell; +use std::sync::atomic::AtomicBool; use super::ffi; +use sync::one::{Once, ONCE_INIT}; pub use self::monitor::{MonitorID, get_available_monitors, get_primary_monitor}; mod events; mod monitor; +static THREAD_INIT: Once = ONCE_INIT; + +fn ensure_thread_init() { + THREAD_INIT.doit(|| { + unsafe { + ffi::XInitThreads(); + } + }); +} + pub struct Window { display: *mut ffi::Display, window: ffi::Window, @@ -20,10 +32,13 @@ pub struct Window { xf86_desk_mode: *mut ffi::XF86VidModeModeInfo, screen_id: libc::c_int, is_fullscreen: bool, + current_modifiers: Cell<KeyModifiers>, + current_size: Cell<(libc::c_int, libc::c_int)>, } impl Window { pub fn new(builder: WindowBuilder) -> Result<Window, String> { + ensure_thread_init(); let dimensions = builder.dimensions.unwrap_or((800, 600)); // calling XOpenDisplay @@ -83,7 +98,7 @@ impl Window { best_mode = i; } }; - if best_mode == -1 { + if best_mode == -1 && builder.monitor.is_some() { return Err(format!("Could not find a suitable graphics mode")); } @@ -120,7 +135,7 @@ impl Window { let mut set_win_attr = { let mut swa: ffi::XSetWindowAttributes = unsafe { mem::zeroed() }; swa.colormap = cmap; - swa.event_mask = ffi::ExposureMask | ffi::ResizeRedirectMask | + swa.event_mask = ffi::ExposureMask | ffi::StructureNotifyMask | ffi::VisibilityChangeMask | ffi::KeyPressMask | ffi::PointerMotionMask | ffi::KeyReleaseMask | ffi::ButtonPressMask | ffi::ButtonReleaseMask | ffi::KeymapStateMask; @@ -152,11 +167,12 @@ impl Window { let wm_delete_window = unsafe { use std::c_str::ToCStr; + let delete_window = "WM_DELETE_WINDOW".to_c_str(); ffi::XMapWindow(display, window); - let mut wm_delete_window = ffi::XInternAtom(display, - "WM_DELETE_WINDOW".to_c_str().as_ptr() as *const libc::c_char, 0); + let mut wm_delete_window = ffi::XInternAtom(display, delete_window.as_ptr(), 0); ffi::XSetWMProtocols(display, window, &mut wm_delete_window, 1); - ffi::XStoreName(display, window, mem::transmute(builder.title.as_slice().as_ptr())); + let c_title = builder.title.to_c_str(); + ffi::XStoreName(display, window, c_title.as_ptr()); ffi::XFlush(display); wm_delete_window @@ -175,8 +191,10 @@ impl Window { let ic = unsafe { use std::c_str::ToCStr; - let ic = ffi::XCreateIC(im, "inputStyle".to_c_str().as_ptr(), - ffi::XIMPreeditNothing | ffi::XIMStatusNothing, "clientWindow".to_c_str().as_ptr(), + let input_style = "inputStyle".to_c_str(); + let client_window = "clientWindow".to_c_str(); + let ic = ffi::XCreateIC(im, input_style.as_ptr(), + ffi::XIMPreeditNothing | ffi::XIMStatusNothing, client_window.as_ptr(), window, ptr::null()); if ic.is_null() { return Err(format!("XCreateIC failed")); @@ -231,6 +249,9 @@ impl Window { context }; + // Make context current before call to glViewport below. + unsafe { ffi::glx::MakeCurrent(display, window, context) }; + // creating the window object let window = Window { display: display, @@ -243,6 +264,8 @@ impl Window { xf86_desk_mode: xf86_desk_mode, screen_id: screen_id, is_fullscreen: builder.monitor.is_some(), + current_modifiers: Cell::new(KeyModifiers::empty()), + current_size: Cell::new((0, 0)), }; // calling glViewport @@ -260,14 +283,15 @@ impl Window { } pub fn is_closed(&self) -> bool { - use std::sync::atomics::Relaxed; + use std::sync::atomic::Relaxed; self.is_closed.load(Relaxed) } pub fn set_title(&self, title: &str) { + let c_title = title.to_c_str(); unsafe { - ffi::XStoreName(self.display, self.window, - mem::transmute(title.as_slice().as_ptr())); + ffi::XStoreName(self.display, self.window, c_title.as_ptr()); + ffi::XFlush(self.display); } } @@ -340,7 +364,7 @@ impl Window { ffi::ClientMessage => { use Closed; - use std::sync::atomics::Relaxed; + use std::sync::atomic::Relaxed; let client_msg: &ffi::XClientMessageEvent = unsafe { mem::transmute(&xev) }; @@ -350,10 +374,14 @@ impl Window { } }, - ffi::ResizeRequest => { + ffi::ConfigureNotify => { use Resized; - let rs_event: &ffi::XResizeRequestEvent = unsafe { mem::transmute(&xev) }; - events.push(Resized(rs_event.width as uint, rs_event.height as uint)); + 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(Resized(cfg_event.width as uint, cfg_event.height as uint)); + } }, ffi::MotionNotify => { @@ -363,7 +391,10 @@ impl Window { }, ffi::KeyPress | ffi::KeyRelease => { - use {KeyboardInput, Pressed, Released, ReceivedCharacter, KeyModifiers}; + use {KeyboardInput, Pressed, Released, ReceivedCharacter}; + use {LEFT_CONTROL_MODIFIER, RIGHT_CONTROL_MODIFIER}; + use {LEFT_SHIFT_MODIFIER, RIGHT_SHIFT_MODIFIER}; + use {LEFT_ALT_MODIFIER, RIGHT_ALT_MODIFIER, CAPS_LOCK_MODIFIER}; let event: &mut ffi::XKeyEvent = unsafe { mem::transmute(&xev) }; if event.type_ == ffi::KeyPress { @@ -394,16 +425,38 @@ impl Window { ffi::XKeycodeToKeysym(self.display, event.keycode as ffi::KeyCode, 0) }; + let modifier_flag = match keysym as u32 { + ffi::XK_Shift_L => Some(LEFT_SHIFT_MODIFIER), + ffi::XK_Shift_R => Some(RIGHT_SHIFT_MODIFIER), + ffi::XK_Control_L => Some(LEFT_CONTROL_MODIFIER), + ffi::XK_Control_R => Some(RIGHT_CONTROL_MODIFIER), + ffi::XK_Caps_Lock => Some(CAPS_LOCK_MODIFIER), + ffi::XK_Meta_L => Some(LEFT_ALT_MODIFIER), + ffi::XK_Meta_R => Some(RIGHT_ALT_MODIFIER), + _ => None, + }; + match modifier_flag { + Some(flag) => { + let mut current_modifiers = self.current_modifiers.get(); + match state { + Pressed => current_modifiers.insert(flag), + Released => current_modifiers.remove(flag), + } + self.current_modifiers.set(current_modifiers); + } + None => {} + } + let vkey = events::keycode_to_element(keysym as libc::c_uint); events.push(KeyboardInput(state, event.keycode as u8, - vkey, KeyModifiers::empty())); + vkey, self.current_modifiers.get())); // }, ffi::ButtonPress | ffi::ButtonRelease => { - use {MouseInput, Pressed, Released}; - use {LeftMouseButton, RightMouseButton, MiddleMouseButton, OtherMouseButton}; + use {MouseInput, MouseWheel, Pressed, Released}; + use {LeftMouseButton, RightMouseButton, MiddleMouseButton}; let event: &ffi::XButtonEvent = unsafe { mem::transmute(&xev) }; let state = if xev.type_ == ffi::ButtonPress { Pressed } else { Released }; @@ -412,8 +465,14 @@ impl Window { ffi::Button1 => Some(LeftMouseButton), ffi::Button2 => Some(MiddleMouseButton), ffi::Button3 => Some(RightMouseButton), - ffi::Button4 => Some(OtherMouseButton(4)), - ffi::Button5 => Some(OtherMouseButton(5)), + ffi::Button4 => { + events.push(MouseWheel(1)); + None + } + ffi::Button5 => { + events.push(MouseWheel(-1)); + None + } _ => None }; @@ -451,7 +510,7 @@ impl Window { pub unsafe fn make_current(&self) { let res = ffi::glx::MakeCurrent(self.display, self.window, self.context); if res == 0 { - fail!("glx::MakeCurrent failed"); + panic!("glx::MakeCurrent failed"); } } @@ -469,6 +528,10 @@ impl Window { pub fn swap_buffers(&self) { unsafe { ffi::glx::SwapBuffers(self.display, self.window) } } + + pub fn platform_display(&self) -> *mut libc::c_void { + self.display as *mut libc::c_void + } } impl Drop for Window { diff --git a/src/x11/window/monitor.rs b/src/x11/window/monitor.rs index b72ed15..f62a8ef 100644 --- a/src/x11/window/monitor.rs +++ b/src/x11/window/monitor.rs @@ -1,13 +1,15 @@ use std::{ptr}; use super::super::ffi; +use super::ensure_thread_init; pub struct MonitorID(pub uint); pub fn get_available_monitors() -> Vec<MonitorID> { + ensure_thread_init(); let nb_monitors = unsafe { let display = ffi::XOpenDisplay(ptr::null()); if display.is_null() { - fail!("get_available_monitors failed"); + panic!("get_available_monitors failed"); } let nb_monitors = ffi::XScreenCount(display); ffi::XCloseDisplay(display); @@ -20,10 +22,11 @@ pub fn get_available_monitors() -> Vec<MonitorID> { } pub fn get_primary_monitor() -> MonitorID { + ensure_thread_init(); let primary_monitor = unsafe { let display = ffi::XOpenDisplay(ptr::null()); if display.is_null() { - fail!("get_available_monitors failed"); + panic!("get_available_monitors failed"); } let primary_monitor = ffi::XDefaultScreen(display); ffi::XCloseDisplay(display); @@ -46,6 +49,7 @@ impl MonitorID { let screen = ffi::XScreenOfDisplay(display, screen_num as i32); let width = ffi::XWidthOfScreen(screen); let height = ffi::XHeightOfScreen(screen); + ffi::XCloseDisplay(display); (width as uint, height as uint) }; diff --git a/tests/headless.rs b/tests/headless.rs index ffc74b3..3c587ea 100644 --- a/tests/headless.rs +++ b/tests/headless.rs @@ -7,7 +7,12 @@ extern crate glutin; extern crate libc; mod gl { - generate_gl_bindings!("gl", "core", "1.1", "struct") + generate_gl_bindings! { + api: "gl", + profile: "core", + version: "1.1", + generator: "struct" + } } #[cfg(feature = "headless")] @@ -25,5 +30,6 @@ fn main() { let mut value: (u8, u8, u8, u8) = unsafe { std::mem::uninitialized() }; unsafe { gl.ReadPixels(0, 0, 1, 1, gl::RGBA, gl::UNSIGNED_BYTE, std::mem::transmute(&mut value)) }; - assert_eq!(value, (0, 255, 0, 255)); + assert!(value == (0, 255, 0, 255) || value == (0, 64, 0, 255) || + value == (0, 64, 0, 255) || value == (0, 64, 0, 0)); } |