diff --git a/.travis.yml b/.travis.yml index 6f259699..486fc727 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,8 @@ rust: - nightly - stable +cache: cargo + addons: apt: packages: @@ -18,14 +20,6 @@ os: - osx after_success: - - | - [ $TRAVIS_BRANCH = master ] && - [ $TRAVIS_PULL_REQUEST = false ] && - cargo doc && - echo '' > target/doc/index.html && - sudo pip install ghp-import && - ghp-import -n target/doc && - git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages - | [ $TRAVIS_BRANCH = master ] && [ $TRAVIS_PULL_REQUEST = false ] && diff --git a/Cargo.toml b/Cargo.toml index e473053f..b2af1c94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,27 +7,30 @@ keywords = ["windowing"] license = "Apache-2.0" readme = "README.md" repository = "https://github.com/tomaka/winit" -documentation = "https://tomaka.github.io/winit/" +documentation = "https://docs.rs/winit" [dependencies] -lazy_static = "0.1.10" +lazy_static = "0.2.0" libc = "0.2" shared_library = "0.1.0" -[target.arm-linux-androideabi.dependencies.android_glue] +# [build-dependencies] +# gl_generator = "0.5" + +[target.'cfg(target_os = "android")'.dependencies.android_glue] version = "0.2" -[target.'cfg(target_os="ios")'.dependencies] +[target.'cfg(target_os = "ios")'.dependencies] objc = "0.2" -[target.'cfg(target_os="macos")'.dependencies] +[target.'cfg(target_os = "macos")'.dependencies] objc = "0.2" cgl = "0.1" -cocoa = "0.3" +cocoa = "0.3.2" core-foundation = "0" core-graphics = "0.3" -[target.'cfg(windows)'.dependencies] +[target.'cfg(target_os = "windows")'.dependencies] winapi = "0.2" shell32-sys = "0.1" gdi32-sys = "0.1" diff --git a/README.md b/README.md index ca079cbb..546a187f 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ [![](http://meritbadge.herokuapp.com/glutin)](https://crates.io/crates/glutin) +[![Docs.rs](https://docs.rs/glutin/badge.svg)](https://docs.rs/glutin) + Alternative to GLFW in pure Rust. [![Build Status](https://travis-ci.org/tomaka/glutin.png?branch=master)](https://travis-ci.org/tomaka/glutin) @@ -13,7 +15,7 @@ Alternative to GLFW in pure Rust. glutin = "*" ``` -## [Documentation](http://tomaka.github.io/glutin/) +## [Documentation](https://docs.rs/glutin) ## Try it! diff --git a/examples/fullscreen.rs b/examples/fullscreen.rs index 70f357e8..5ed2ecbc 100644 --- a/examples/fullscreen.rs +++ b/examples/fullscreen.rs @@ -30,7 +30,7 @@ fn main() { }; let window = winit::WindowBuilder::new() - .with_title("Hello world!".to_string()) + .with_title("Hello world!") .with_fullscreen(monitor) .build() .unwrap(); diff --git a/examples/grabbing.rs b/examples/grabbing.rs index ec08eac5..6a1c47cf 100644 --- a/examples/grabbing.rs +++ b/examples/grabbing.rs @@ -31,7 +31,7 @@ fn main() { Event::Closed => break, - a @ Event::MouseMoved(_) => { + a @ Event::MouseMoved(_, _) => { println!("{:?}", a); }, diff --git a/src/api/android/ffi.rs b/src/api/android/ffi.rs index 1398b772..af8c50b3 100644 --- a/src/api/android/ffi.rs +++ b/src/api/android/ffi.rs @@ -4,6 +4,7 @@ #![allow(non_upper_case_globals)] use libc; +use std::os::raw; #[link(name = "android")] #[link(name = "EGL")] @@ -13,12 +14,12 @@ extern {} /** * asset_manager.h */ -pub type AAssetManager = (); +pub type AAssetManager = raw::c_void; /** * native_window.h */ -pub type ANativeWindow = (); +pub type ANativeWindow = raw::c_void; extern { pub fn ANativeWindow_getHeight(window: *const ANativeWindow) -> libc::int32_t; diff --git a/src/api/android/mod.rs b/src/api/android/mod.rs index d1d2053b..d3973481 100644 --- a/src/api/android/mod.rs +++ b/src/api/android/mod.rs @@ -56,9 +56,9 @@ impl MonitorId { } } -#[derive(Default)] +#[derive(Clone, Default)] pub struct PlatformSpecificWindowBuilderAttributes; -#[derive(Default)] +#[derive(Clone, Default)] pub struct PlatformSpecificHeadlessBuilderAttributes; pub struct PollEventsIterator<'a> { diff --git a/src/api/cocoa/mod.rs b/src/api/cocoa/mod.rs index 23a6e2a1..186578d7 100644 --- a/src/api/cocoa/mod.rs +++ b/src/api/cocoa/mod.rs @@ -6,6 +6,7 @@ use libc; use WindowAttributes; use native_monitor::NativeMonitorId; +use os::macos::ActivationPolicy; use objc::runtime::{Class, Object, Sel, BOOL, YES, NO}; use objc::declare::ClassDecl; @@ -15,9 +16,7 @@ use cgl::{CGLEnable, kCGLCECrashOnRemovedFunctions, CGLSetParameter, kCGLCPSurfa use cocoa::base::{id, nil}; use cocoa::foundation::{NSAutoreleasePool, NSDate, NSDefaultRunLoopMode, NSPoint, NSRect, NSSize, NSString, NSUInteger}; -use cocoa::appkit; -use cocoa::appkit::*; -use cocoa::appkit::NSEventSubtype::*; +use cocoa::appkit::{self, NSApplication, NSEvent, NSOpenGLContext, NSOpenGLPixelFormat, NSView, NSWindow}; use core_foundation::base::TCFType; use core_foundation::string::CFString; @@ -30,12 +29,9 @@ use std::collections::VecDeque; use std::str::FromStr; use std::str::from_utf8; use std::sync::Mutex; -use std::ascii::AsciiExt; use std::ops::Deref; -use events::ElementState::{Pressed, Released}; -use events::Event::{Awakened, MouseInput, MouseMoved, ReceivedCharacter, KeyboardInput}; -use events::Event::{MouseWheel, Closed, Focused, TouchpadPressure}; +use events::ElementState; use events::{self, MouseButton, TouchPhase}; pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor}; @@ -72,7 +68,7 @@ impl WindowDelegate { unsafe { let state: *mut c_void = *this.get_ivar("glutinState"); let state = state as *mut DelegateState; - (*state).pending_events.lock().unwrap().push_back(Closed); + (*state).pending_events.lock().unwrap().push_back(Event::Closed); } YES } @@ -101,7 +97,7 @@ impl WindowDelegate { let state: *mut c_void = *this.get_ivar("glutinState"); let state = state as *mut DelegateState; - (*state).pending_events.lock().unwrap().push_back(Focused(true)); + (*state).pending_events.lock().unwrap().push_back(Event::Focused(true)); } } @@ -109,7 +105,7 @@ impl WindowDelegate { unsafe { let state: *mut c_void = *this.get_ivar("glutinState"); let state = state as *mut DelegateState; - (*state).pending_events.lock().unwrap().push_back(Focused(false)); + (*state).pending_events.lock().unwrap().push_back(Event::Focused(false)); } } @@ -167,8 +163,10 @@ impl Drop for WindowDelegate { } } -#[derive(Default)] -pub struct PlatformSpecificWindowBuilderAttributes; +#[derive(Clone, Default)] +pub struct PlatformSpecificWindowBuilderAttributes { + pub activation_policy: ActivationPolicy, +} pub struct Window { view: IdRef, @@ -188,9 +186,9 @@ impl WindowProxy { let pool = NSAutoreleasePool::new(nil); let event = NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_( - nil, NSApplicationDefined, NSPoint::new(0.0, 0.0), NSEventModifierFlags::empty(), - 0.0, 0, nil, NSApplicationActivatedEventType, 0, 0); - NSApp().postEvent_atStart_(event, NO); + nil, appkit::NSApplicationDefined, NSPoint::new(0.0, 0.0), appkit::NSEventModifierFlags::empty(), + 0.0, 0, nil, appkit::NSEventSubtype::NSApplicationActivatedEventType, 0, 0); + appkit::NSApp().postEvent_atStart_(event, NO); pool.drain(); } } @@ -212,8 +210,8 @@ impl<'a> Iterator for PollEventsIterator<'a> { unsafe { let pool = NSAutoreleasePool::new(nil); - let nsevent = NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_( - NSAnyEventMask.bits() | NSEventMaskPressure.bits(), + let nsevent = appkit::NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_( + appkit::NSAnyEventMask.bits() | appkit::NSEventMaskPressure.bits(), NSDate::distantPast(nil), NSDefaultRunLoopMode, YES); @@ -241,8 +239,8 @@ impl<'a> Iterator for WaitEventsIterator<'a> { unsafe { let pool = NSAutoreleasePool::new(nil); - let nsevent = NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_( - NSAnyEventMask.bits() | NSEventMaskPressure.bits(), + let nsevent = appkit::NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_( + appkit::NSAnyEventMask.bits() | appkit::NSEventMaskPressure.bits(), NSDate::distantFuture(nil), NSDefaultRunLoopMode, YES); @@ -252,7 +250,7 @@ impl<'a> Iterator for WaitEventsIterator<'a> { } if event.is_none() { - return Some(Awakened); + return Some(Event::Awakened); } else { return event; } @@ -261,14 +259,15 @@ impl<'a> Iterator for WaitEventsIterator<'a> { impl Window { pub fn new(win_attribs: &WindowAttributes, - _: &PlatformSpecificWindowBuilderAttributes) - -> Result + pl_attribs: &PlatformSpecificWindowBuilderAttributes) + -> Result { // not implemented assert!(win_attribs.min_dimensions.is_none()); assert!(win_attribs.max_dimensions.is_none()); - let app = match Window::create_app() { + // let app = match Window::create_app() { + let app = match Window::create_app(pl_attribs.activation_policy) { Some(app) => app, None => { return Err(OsError(format!("Couldn't create NSApplication"))); }, }; @@ -312,13 +311,13 @@ impl Window { Ok(window) } - fn create_app() -> Option { + fn create_app(activation_policy: ActivationPolicy) -> Option { unsafe { - let app = NSApp(); + let app = appkit::NSApp(); if app == nil { None } else { - app.setActivationPolicy_(NSApplicationActivationPolicyRegular); + app.setActivationPolicy_(activation_policy.into()); app.finishLaunching(); Some(app) } @@ -334,13 +333,13 @@ impl Window { _ => panic!("OS X monitors should always have a numeric native ID") }; let matching_screen = { - let screens = NSScreen::screens(nil); + let screens = appkit::NSScreen::screens(nil); let count: NSUInteger = msg_send![screens, count]; let key = IdRef::new(NSString::alloc(nil).init_str("NSScreenNumber")); let mut matching_screen: Option = None; for i in 0..count { let screen = msg_send![screens, objectAtIndex:i as NSUInteger]; - let device_description = NSScreen::deviceDescription(screen); + let device_description = appkit::NSScreen::deviceDescription(screen); let value: id = msg_send![device_description, objectForKey:*key]; if value != nil { let screen_number: NSUInteger = msg_send![value, unsignedIntegerValue]; @@ -352,12 +351,12 @@ impl Window { } matching_screen }; - Some(matching_screen.unwrap_or(NSScreen::mainScreen(nil))) + Some(matching_screen.unwrap_or(appkit::NSScreen::mainScreen(nil))) }, None => None }; let frame = match screen { - Some(screen) => NSScreen::frame(screen), + Some(screen) => appkit::NSScreen::frame(screen), None => { let (width, height) = attrs.dimensions.unwrap_or((800, 600)); NSRect::new(NSPoint::new(0., 0.), NSSize::new(width as f64, height as f64)) @@ -366,42 +365,43 @@ impl Window { let masks = if screen.is_some() || attrs.transparent { // Fullscreen or transparent window - NSBorderlessWindowMask as NSUInteger | - NSResizableWindowMask as NSUInteger | - NSTitledWindowMask as NSUInteger + appkit::NSBorderlessWindowMask as NSUInteger | + appkit::NSResizableWindowMask as NSUInteger | + appkit::NSTitledWindowMask as NSUInteger } else if attrs.decorations { // Classic opaque window with titlebar - NSClosableWindowMask as NSUInteger | - NSMiniaturizableWindowMask as NSUInteger | - NSResizableWindowMask as NSUInteger | - NSTitledWindowMask as NSUInteger + appkit::NSClosableWindowMask as NSUInteger | + appkit::NSMiniaturizableWindowMask as NSUInteger | + appkit::NSResizableWindowMask as NSUInteger | + appkit::NSTitledWindowMask as NSUInteger } else { // Opaque window without a titlebar - NSClosableWindowMask as NSUInteger | - NSMiniaturizableWindowMask as NSUInteger | - NSResizableWindowMask as NSUInteger | - NSTitledWindowMask as NSUInteger | - NSFullSizeContentViewWindowMask as NSUInteger + appkit::NSClosableWindowMask as NSUInteger | + appkit::NSMiniaturizableWindowMask as NSUInteger | + appkit::NSResizableWindowMask as NSUInteger | + appkit::NSTitledWindowMask as NSUInteger | + appkit::NSFullSizeContentViewWindowMask as NSUInteger }; let window = IdRef::new(NSWindow::alloc(nil).initWithContentRect_styleMask_backing_defer_( frame, masks, - NSBackingStoreBuffered, + appkit::NSBackingStoreBuffered, NO, )); window.non_nil().map(|window| { let title = IdRef::new(NSString::alloc(nil).init_str(&attrs.title)); + window.setReleasedWhenClosed_(NO); window.setTitle_(*title); window.setAcceptsMouseMovedEvents_(YES); if !attrs.decorations { - window.setTitleVisibility_(NSWindowTitleVisibility::NSWindowTitleHidden); + window.setTitleVisibility_(appkit::NSWindowTitleVisibility::NSWindowTitleHidden); window.setTitlebarAppearsTransparent_(YES); } if screen.is_some() { - window.setLevel_(NSMainMenuWindowLevel as i64 + 1); + window.setLevel_(appkit::NSMainMenuWindowLevel as i64 + 1); } else { window.center(); @@ -511,11 +511,11 @@ impl Window { } } - unsafe fn modifier_event(event: id, keymask: NSEventModifierFlags, key: events::VirtualKeyCode, key_pressed: bool) -> Option { + unsafe fn modifier_event(event: id, keymask: appkit::NSEventModifierFlags, key: events::VirtualKeyCode, key_pressed: bool) -> Option { if !key_pressed && NSEvent::modifierFlags(event).contains(keymask) { - return Some(KeyboardInput(Pressed, NSEvent::keyCode(event) as u8, Some(key))); + return Some(Event::KeyboardInput(ElementState::Pressed, NSEvent::keyCode(event) as u8, Some(key))); } else if key_pressed && !NSEvent::modifierFlags(event).contains(keymask) { - return Some(KeyboardInput(Released, NSEvent::keyCode(event) as u8, Some(key))); + return Some(Event::KeyboardInput(ElementState::Released, NSEvent::keyCode(event) as u8, Some(key))); } return None; @@ -609,7 +609,10 @@ impl Window { unsafe { // TODO: Check for errors. - let _ = CGWarpMouseCursorPosition(CGPoint { x: cursor_x as CGFloat, y: cursor_y as CGFloat }); + let _ = CGWarpMouseCursorPosition(appkit::CGPoint { + x: cursor_x as appkit::CGFloat, + y: cursor_y as appkit::CGFloat, + }); let _ = CGAssociateMouseAndMouseCursorPosition(true); } @@ -661,22 +664,22 @@ impl Clone for IdRef { } } -#[allow(non_snake_case)] +#[allow(non_snake_case, non_upper_case_globals)] unsafe fn NSEventToEvent(window: &Window, nsevent: id) -> Option { if nsevent == nil { return None; } let event_type = nsevent.eventType(); - NSApp().sendEvent_(if let NSKeyDown = event_type { nil } else { nsevent }); + appkit::NSApp().sendEvent_(if let appkit::NSKeyDown = event_type { nil } else { nsevent }); match event_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 | - NSLeftMouseDragged | - NSOtherMouseDragged | - NSRightMouseDragged => { + appkit::NSLeftMouseDown => { Some(Event::MouseInput(ElementState::Pressed, MouseButton::Left)) }, + appkit::NSLeftMouseUp => { Some(Event::MouseInput(ElementState::Released, MouseButton::Left)) }, + appkit::NSRightMouseDown => { Some(Event::MouseInput(ElementState::Pressed, MouseButton::Right)) }, + appkit::NSRightMouseUp => { Some(Event::MouseInput(ElementState::Released, MouseButton::Right)) }, + appkit::NSMouseMoved | + appkit::NSLeftMouseDragged | + appkit::NSOtherMouseDragged | + appkit::NSRightMouseDragged => { let window_point = nsevent.locationInWindow(); let cWindow: id = msg_send![nsevent, window]; let view_point = if cWindow == nil { @@ -688,29 +691,29 @@ unsafe fn NSEventToEvent(window: &Window, nsevent: id) -> Option { let view_rect = NSView::frame(*window.view); let scale_factor = 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))) + Some(Event::MouseMoved((scale_factor * view_point.x as f32) as i32, + (scale_factor * (view_rect.size.height - view_point.y) as f32) as i32)) }, - NSKeyDown => { + appkit::NSKeyDown => { let mut events = VecDeque::new(); let received_c_str = nsevent.characters().UTF8String(); let received_str = CStr::from_ptr(received_c_str); for received_char in from_utf8(received_str.to_bytes()).unwrap().chars() { - events.push_back(ReceivedCharacter(received_char)); + events.push_back(Event::ReceivedCharacter(received_char)); } let vkey = event::vkeycode_to_element(NSEvent::keyCode(nsevent)); - events.push_back(KeyboardInput(Pressed, NSEvent::keyCode(nsevent) as u8, vkey)); + events.push_back(Event::KeyboardInput(ElementState::Pressed, NSEvent::keyCode(nsevent) as u8, vkey)); let event = events.pop_front(); window.delegate.state.pending_events.lock().unwrap().extend(events.into_iter()); event }, - NSKeyUp => { + appkit::NSKeyUp => { let vkey = event::vkeycode_to_element(NSEvent::keyCode(nsevent)); - Some(KeyboardInput(Released, NSEvent::keyCode(nsevent) as u8, vkey)) + Some(Event::KeyboardInput(ElementState::Released, NSEvent::keyCode(nsevent) as u8, vkey)) }, - NSFlagsChanged => { + appkit::NSFlagsChanged => { let mut events = VecDeque::new(); let shift_modifier = Window::modifier_event(nsevent, appkit::NSShiftKeyMask, events::VirtualKeyCode::LShift, shift_pressed); if shift_modifier.is_some() { @@ -736,7 +739,7 @@ unsafe fn NSEventToEvent(window: &Window, nsevent: id) -> Option { window.delegate.state.pending_events.lock().unwrap().extend(events.into_iter()); event }, - NSScrollWheel => { + appkit::NSScrollWheel => { use events::MouseScrollDelta::{LineDelta, PixelDelta}; let scale_factor = window.hidpi_factor(); let delta = if nsevent.hasPreciseScrollingDeltas() == YES { @@ -747,14 +750,14 @@ unsafe fn NSEventToEvent(window: &Window, nsevent: id) -> Option { scale_factor * nsevent.scrollingDeltaY() as f32) }; let phase = match nsevent.phase() { - NSEventPhaseMayBegin | NSEventPhaseBegan => TouchPhase::Started, - NSEventPhaseEnded => TouchPhase::Ended, + appkit::NSEventPhaseMayBegin | appkit::NSEventPhaseBegan => TouchPhase::Started, + appkit::NSEventPhaseEnded => TouchPhase::Ended, _ => TouchPhase::Moved, }; - Some(MouseWheel(delta, phase)) + Some(Event::MouseWheel(delta, phase)) }, - NSEventTypePressure => { - Some(TouchpadPressure(nsevent.pressure(), nsevent.stage())) + appkit::NSEventTypePressure => { + Some(Event::TouchpadPressure(nsevent.pressure(), nsevent.stage())) }, _ => { None }, } diff --git a/src/api/ios/mod.rs b/src/api/ios/mod.rs index afcc6f42..8bee5134 100644 --- a/src/api/ios/mod.rs +++ b/src/api/ios/mod.rs @@ -177,7 +177,7 @@ impl MonitorId { } } -#[derive(Default)] +#[derive(Clone, Default)] pub struct PlatformSpecificWindowBuilderAttributes; impl Window { diff --git a/src/api/wayland/events.rs b/src/api/wayland/events.rs index 5e0c3fd0..92a0b95f 100644 --- a/src/api/wayland/events.rs +++ b/src/api/wayland/events.rs @@ -52,7 +52,7 @@ pub fn translate_event( if known_surfaces.contains(&surface) { focuses.pointer_on = Some(surface); focuses.pointer_at = Some((x, y)); - Some((GlutinEvent::MouseMoved((x as i32, y as i32)), surface)) + Some((GlutinEvent::MouseMoved(x as i32, y as i32), surface)) } else { None } @@ -65,7 +65,7 @@ pub fn translate_event( WlPointerEvent::Motion(_, x, y) => { if let Some(surface) = focuses.pointer_on { focuses.pointer_at = Some((x, y)); - Some((GlutinEvent::MouseMoved((x as i32, y as i32)), surface)) + Some((GlutinEvent::MouseMoved(x as i32, y as i32), surface)) } else { None } diff --git a/src/api/win32/callback.rs b/src/api/win32/callback.rs index e2b74121..6f6bd2cb 100644 --- a/src/api/win32/callback.rs +++ b/src/api/win32/callback.rs @@ -136,15 +136,7 @@ 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 mut mouse_track = winapi::TRACKMOUSEEVENT { - cbSize: mem::size_of::() as winapi::DWORD, - dwFlags: winapi::TME_HOVER | winapi::TME_LEAVE, - hwndTrack: window, - dwHoverTime: winapi::HOVER_DEFAULT - }; - user32::TrackMouseEvent(&mut mouse_track); - - send_event(window, MouseMoved((x, y))); + send_event(window, MouseMoved(x, y)); 0 }, diff --git a/src/api/x11/events.rs b/src/api/x11/events.rs index 1c6668c3..4754b1dc 100644 --- a/src/api/x11/events.rs +++ b/src/api/x11/events.rs @@ -14,7 +14,7 @@ pub fn keycode_to_element(scancode: libc::c_uint) -> Option { //ffi::XK_Sys_Req => events::VirtualKeyCode::Sys_req, ffi::XK_Escape => events::VirtualKeyCode::Escape, ffi::XK_Delete => events::VirtualKeyCode::Delete, - //ffi::XK_Multi_key => events::VirtualKeyCode::Multi_key, + ffi::XK_Multi_key => events::VirtualKeyCode::Compose, //ffi::XK_Kanji => events::VirtualKeyCode::Kanji, //ffi::XK_Muhenkan => events::VirtualKeyCode::Muhenkan, //ffi::XK_Henkan_Mode => events::VirtualKeyCode::Henkan_mode, diff --git a/src/api/x11/input.rs b/src/api/x11/input.rs index a05b22e1..310f1bcb 100644 --- a/src/api/x11/input.rs +++ b/src/api/x11/input.rs @@ -241,7 +241,7 @@ impl XInputEventHandler { 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))) + Some(MouseMoved(new_cursor_pos.0 as i32, new_cursor_pos.1 as i32)) } else { None } diff --git a/src/api/x11/window.rs b/src/api/x11/window.rs index 005fe6d2..b2013d23 100644 --- a/src/api/x11/window.rs +++ b/src/api/x11/window.rs @@ -3,7 +3,7 @@ use CreationError; use CreationError::OsError; use libc; use std::borrow::Borrow; -use std::{mem, ptr}; +use std::{mem, ptr, cmp}; use std::cell::Cell; use std::sync::atomic::AtomicBool; use std::collections::VecDeque; @@ -285,11 +285,23 @@ impl Window { pub fn new(display: &Arc, window_attrs: &WindowAttributes) -> Result { - let dimensions = window_attrs.dimensions.unwrap_or((800, 600)); + let dimensions = { - // not implemented - assert!(window_attrs.min_dimensions.is_none()); - assert!(window_attrs.max_dimensions.is_none()); + // x11 only applies constraints when the window is actively resized + // by the user, so we have to manually apply the initial constraints + let mut dimensions = window_attrs.dimensions.unwrap_or((800, 600)); + if let Some(max) = window_attrs.max_dimensions { + dimensions.0 = cmp::min(dimensions.0, max.0); + dimensions.1 = cmp::min(dimensions.1, max.1); + } + + if let Some(min) = window_attrs.min_dimensions { + dimensions.0 = cmp::max(dimensions.0, min.0); + dimensions.1 = cmp::max(dimensions.1, min.1); + } + dimensions + + }; let screen_id = match window_attrs.monitor { Some(PlatformMonitorId::X(MonitorId(_, monitor))) => monitor as i32, @@ -502,6 +514,32 @@ impl Window { (display.xf86vmode.XF86VidModeSetViewPort)(display.display, screen_id, 0, 0); display.check_errors().expect("Failed to call XF86VidModeSetViewPort"); } + + } else { + + // set size hints + let mut size_hints: ffi::XSizeHints = unsafe { mem::zeroed() }; + size_hints.flags = ffi::PSize; + size_hints.width = dimensions.0 as i32; + size_hints.height = dimensions.1 as i32; + + if let Some(dimensions) = window_attrs.min_dimensions { + size_hints.flags |= ffi::PMinSize; + size_hints.min_width = dimensions.0 as i32; + size_hints.min_height = dimensions.1 as i32; + } + + if let Some(dimensions) = window_attrs.max_dimensions { + size_hints.flags |= ffi::PMaxSize; + size_hints.max_width = dimensions.0 as i32; + size_hints.max_height = dimensions.1 as i32; + } + + unsafe { + (display.xlib.XSetNormalHints)(display.display, window, &mut size_hints); + display.check_errors().expect("Failed to call XSetNormalHints"); + } + } // creating the window object @@ -706,51 +744,115 @@ impl Window { pub fn set_cursor(&self, cursor: MouseCursor) { unsafe { - use std::ffi::CString; - let cursor_name = match cursor { - MouseCursor::Alias => "link", - MouseCursor::Arrow => "arrow", - MouseCursor::Cell => "plus", - MouseCursor::Copy => "copy", - MouseCursor::Crosshair => "crosshair", - MouseCursor::Default => "left_ptr", - MouseCursor::Grabbing => "grabbing", - MouseCursor::Hand | MouseCursor::Grab => "hand", - MouseCursor::Help => "question_arrow", - MouseCursor::Move => "move", - MouseCursor::NoDrop => "circle", - MouseCursor::NotAllowed => "crossed_circle", - MouseCursor::Progress => "left_ptr_watch", + let load = |name: &str| { + self.load_cursor(name) + }; + + let loadn = |names: &[&str]| { + self.load_first_existing_cursor(names) + }; + + // Try multiple names in some cases where the name + // differs on the desktop environments or themes. + // + // Try the better looking (or more suiting) names first. + let mut xcursor = match cursor { + MouseCursor::Alias => load("link"), + MouseCursor::Arrow => load("arrow"), + MouseCursor::Cell => load("plus"), + MouseCursor::Copy => load("copy"), + MouseCursor::Crosshair => load("crosshair"), + MouseCursor::Default => load("left_ptr"), + MouseCursor::Hand => load("hand1"), + MouseCursor::Help => load("question_arrow"), + MouseCursor::Move => load("move"), + MouseCursor::Grab => loadn(&["openhand", "grab"]), + MouseCursor::Grabbing => loadn(&["closedhand", "grabbing"]), + MouseCursor::Progress => load("left_ptr_watch"), + MouseCursor::AllScroll => load("all-scroll"), + MouseCursor::ContextMenu => load("context-menu"), + + MouseCursor::NoDrop => loadn(&["no-drop", "circle"]), + MouseCursor::NotAllowed => load("crossed_circle"), + /// Resize cursors - MouseCursor::EResize => "right_side", - MouseCursor::NResize => "top_side", - MouseCursor::NeResize => "top_right_corner", - MouseCursor::NwResize => "top_left_corner", - MouseCursor::SResize => "bottom_side", - MouseCursor::SeResize => "bottom_right_corner", - MouseCursor::SwResize => "bottom_left_corner", - MouseCursor::WResize => "left_side", - MouseCursor::EwResize | MouseCursor::ColResize => "h_double_arrow", - MouseCursor::NsResize | MouseCursor::RowResize => "v_double_arrow", - MouseCursor::NwseResize => "bd_double_arrow", - MouseCursor::NeswResize => "fd_double_arrow", + MouseCursor::EResize => load("right_side"), + MouseCursor::NResize => load("top_side"), + MouseCursor::NeResize => load("top_right_corner"), + MouseCursor::NwResize => load("top_left_corner"), + MouseCursor::SResize => load("bottom_side"), + MouseCursor::SeResize => load("bottom_right_corner"), + MouseCursor::SwResize => load("bottom_left_corner"), + MouseCursor::WResize => load("left_side"), + MouseCursor::EwResize => load("h_double_arrow"), + MouseCursor::NsResize => load("v_double_arrow"), + MouseCursor::NwseResize => loadn(&["bd_double_arrow", "size_bdiag"]), + MouseCursor::NeswResize => loadn(&["fd_double_arrow", "size_fdiag"]), + MouseCursor::ColResize => loadn(&["split_h", "h_double_arrow"]), + MouseCursor::RowResize => loadn(&["split_v", "v_double_arrow"]), - MouseCursor::Text | MouseCursor::VerticalText => "xterm", - MouseCursor::Wait => "watch", + MouseCursor::Text => loadn(&["text", "xterm"]), + MouseCursor::VerticalText => load("vertical-text"), - /// TODO: Find matching X11 cursors - MouseCursor::ContextMenu | MouseCursor::NoneCursor | - MouseCursor::AllScroll | MouseCursor::ZoomIn | - MouseCursor::ZoomOut => "left_ptr", + MouseCursor::Wait => load("watch"), + + MouseCursor::ZoomIn => load("zoom-in"), + MouseCursor::ZoomOut => load("zoom-out"), + + MouseCursor::NoneCursor => self.create_empty_cursor(), }; - let c_string = CString::new(cursor_name.as_bytes().to_vec()).unwrap(); - let xcursor = (self.x.display.xcursor.XcursorLibraryLoadCursor)(self.x.display.display, c_string.as_ptr()); - self.x.display.check_errors().expect("Failed to call XcursorLibraryLoadCursor"); + (self.x.display.xlib.XDefineCursor)(self.x.display.display, self.x.window, xcursor); - (self.x.display.xlib.XFlush)(self.x.display.display); - (self.x.display.xlib.XFreeCursor)(self.x.display.display, xcursor); - self.x.display.check_errors().expect("Failed to call XDefineCursor"); + if xcursor != 0 { + (self.x.display.xlib.XFreeCursor)(self.x.display.display, xcursor); + } + self.x.display.check_errors().expect("Failed to set or free the cursor"); + } + } + + fn load_cursor(&self, name: &str) -> ffi::Cursor { + use std::ffi::CString; + unsafe { + let c_string = CString::new(name.as_bytes()).unwrap(); + (self.x.display.xcursor.XcursorLibraryLoadCursor)(self.x.display.display, c_string.as_ptr()) + } + } + + fn load_first_existing_cursor(&self, names :&[&str]) -> ffi::Cursor { + for name in names.iter() { + let xcursor = self.load_cursor(name); + if xcursor != 0 { + return xcursor; + } + } + 0 + } + + // TODO: This could maybe be cached. I don't think it's worth + // the complexity, since cursor changes are not so common, + // and this is just allocating a 1x1 pixmap... + fn create_empty_cursor(&self) -> ffi::Cursor { + use std::mem; + + let data = 0; + unsafe { + let pixmap = (self.x.display.xlib.XCreateBitmapFromData)(self.x.display.display, self.x.window, &data, 1, 1); + if pixmap == 0 { + // Failed to allocate + return 0; + } + + // We don't care about this color, since it only fills bytes + // in the pixmap which are not 0 in the mask. + let dummy_color: ffi::XColor = mem::uninitialized(); + let cursor = (self.x.display.xlib.XCreatePixmapCursor)(self.x.display.display, + pixmap, + pixmap, + &dummy_color as *const _ as *mut _, + &dummy_color as *const _ as *mut _, 0, 0); + (self.x.display.xlib.XFreePixmap)(self.x.display.display, pixmap); + cursor } } @@ -772,13 +874,10 @@ impl Window { }, Normal => {}, Hide => { + // NB: Calling XDefineCursor with None (aka 0) + // as a value resets the cursor to the default. unsafe { - let xcursor = (self.x.display.xlib.XCreateFontCursor)(self.x.display.display, 68/*XC_left_ptr*/); - self.x.display.check_errors().expect("Failed to call XCreateFontCursor"); - (self.x.display.xlib.XDefineCursor)(self.x.display.display, self.x.window, xcursor); - self.x.display.check_errors().expect("Failed to call XDefineCursor"); - (self.x.display.xlib.XFlush)(self.x.display.display); - (self.x.display.xlib.XFreeCursor)(self.x.display.display, xcursor); + (self.x.display.xlib.XDefineCursor)(self.x.display.display, self.x.window, 0); } }, } @@ -787,25 +886,20 @@ impl Window { match state { Normal => Ok(()), Hide => { - let data = &[0, 0, 0, 0, 0, 0, 0, 0]; unsafe { - let mut black = ffi::XColor { - red: 0, green: 0, blue: 0, - pad: 0, pixel: 0, flags: 0, - }; - let bitmap = (self.x.display.xlib.XCreateBitmapFromData)(self.x.display.display, self.x.window, data.as_ptr(), 8, 8); - let cursor = (self.x.display.xlib.XCreatePixmapCursor)(self.x.display.display, bitmap, bitmap, &mut black, &mut black, 0, 0); + let cursor = self.create_empty_cursor(); (self.x.display.xlib.XDefineCursor)(self.x.display.display, self.x.window, cursor); - self.x.display.check_errors().expect("Failed to call XDefineCursor"); - (self.x.display.xlib.XFreeCursor)(self.x.display.display, cursor); - (self.x.display.xlib.XFreePixmap)(self.x.display.display, bitmap); + if cursor != 0 { + (self.x.display.xlib.XFreeCursor)(self.x.display.display, cursor); + } + self.x.display.check_errors().expect("Failed to call XDefineCursor or free the empty cursor"); } Ok(()) }, Grab => { unsafe { match (self.x.display.xlib.XGrabPointer)( - self.x.display.display, self.x.window, ffi::False, + self.x.display.display, self.x.window, ffi::True, (ffi::ButtonPressMask | ffi::ButtonReleaseMask | ffi::EnterWindowMask | ffi::LeaveWindowMask | ffi::PointerMotionMask | ffi::PointerMotionHintMask | ffi::Button1MotionMask | ffi::Button2MotionMask | ffi::Button3MotionMask | diff --git a/src/events.rs b/src/events.rs index 9fff1fb7..7186256c 100644 --- a/src/events.rs +++ b/src/events.rs @@ -28,7 +28,7 @@ pub enum Event { /// 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)), + MouseMoved(i32, i32), /// A mouse wheel movement or touchpad scroll occurred. MouseWheel(MouseScrollDelta, TouchPhase), @@ -220,6 +220,9 @@ pub enum VirtualKeyCode { /// The space bar. Space, + /// The "Compose" key on Linux. + Compose, + Numlock, Numpad0, Numpad1, diff --git a/src/lib.rs b/src/lib.rs index dea319cc..e37b9bf4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,7 +61,7 @@ extern crate x11_dl; extern crate wayland_client; pub use events::*; -pub use window::{WindowBuilder, WindowProxy, PollEventsIterator, WaitEventsIterator}; +pub use window::{WindowProxy, PollEventsIterator, WaitEventsIterator}; pub use window::{AvailableMonitorsIter, MonitorId, get_available_monitors, get_primary_monitor}; pub use native_monitor::NativeMonitorId; @@ -99,6 +99,16 @@ pub struct Window { window: platform::Window, } +/// Object that allows you to build windows. +#[derive(Clone)] +pub struct WindowBuilder { + /// The attributes to use to create the window. + pub window: WindowAttributes, + + /// Platform-specific configuration. + platform_specific: platform::PlatformSpecificWindowBuilderAttributes, +} + /// Error that can happen while creating a window or a headless renderer. #[derive(Debug)] pub enum CreationError { @@ -128,7 +138,7 @@ impl std::error::Error for CreationError { } } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub enum MouseCursor { /// The platform-dependent default cursor. Default, @@ -185,7 +195,7 @@ pub enum MouseCursor { } /// Describes how glutin handles the cursor. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub enum CursorState { /// Normal cursor behavior. Normal, diff --git a/src/os/macos.rs b/src/os/macos.rs index 16ffe33c..a632e3e2 100644 --- a/src/os/macos.rs +++ b/src/os/macos.rs @@ -1,7 +1,9 @@ #![cfg(target_os = "macos")] +use std::convert::From; use std::os::raw::c_void; -use Window; +use cocoa::appkit::NSApplicationActivationPolicy; +use {Window, WindowBuilder}; /// Additional methods on `Window` that are specific to MacOS. pub trait WindowExt { @@ -17,3 +19,47 @@ impl WindowExt for Window { self.window.platform_window() as *mut c_void } } + +/// Corresponds to `NSApplicationActivationPolicy`. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum ActivationPolicy { + /// Corresponds to `NSApplicationActivationPolicyRegular`. + Regular, + /// Corresponds to `NSApplicationActivationPolicyAccessory`. + Accessory, + /// Corresponds to `NSApplicationActivationPolicyProhibited`. + Prohibited, +} + +impl Default for ActivationPolicy { + fn default() -> Self { + ActivationPolicy::Regular + } +} + +impl From for NSApplicationActivationPolicy { + fn from(activation_policy: ActivationPolicy) -> Self { + match activation_policy { + ActivationPolicy::Regular => + NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular, + ActivationPolicy::Accessory => + NSApplicationActivationPolicy::NSApplicationActivationPolicyAccessory, + ActivationPolicy::Prohibited => + NSApplicationActivationPolicy::NSApplicationActivationPolicyProhibited, + } + } +} + +/// Additional methods on `WindowBuilder` that are specific to MacOS. +pub trait WindowBuilderExt { + fn with_activation_policy(mut self, activation_policy: ActivationPolicy) -> WindowBuilder; +} + +impl WindowBuilderExt for WindowBuilder { + /// Sets the activation policy for the window being built + #[inline] + fn with_activation_policy(mut self, activation_policy: ActivationPolicy) -> WindowBuilder { + self.platform_specific.activation_policy = activation_policy; + self + } +} diff --git a/src/platform/emscripten/mod.rs b/src/platform/emscripten/mod.rs index ce8d201c..22cb5653 100644 --- a/src/platform/emscripten/mod.rs +++ b/src/platform/emscripten/mod.rs @@ -58,7 +58,7 @@ impl GlContext for HeadlessContext { unsafe impl Send for HeadlessContext {} unsafe impl Sync for HeadlessContext {} -#[derive(Default)] +#[derive(Clone, Default)] pub struct PlatformSpecificWindowBuilderAttributes; -#[derive(Default)] +#[derive(Clone, Default)] pub struct PlatformSpecificHeadlessBuilderAttributes; diff --git a/src/platform/ios/mod.rs b/src/platform/ios/mod.rs index 2e6fbec0..0b1a5509 100644 --- a/src/platform/ios/mod.rs +++ b/src/platform/ios/mod.rs @@ -8,7 +8,7 @@ use ContextError; pub use api::ios::*; -#[derive(Default)] +#[derive(Clone, Default)] pub struct PlatformSpecificHeadlessBuilderAttributes; pub struct HeadlessContext(i32); diff --git a/src/platform/linux/api_dispatch.rs b/src/platform/linux/api_dispatch.rs index 7e5e3e17..975cadaf 100644 --- a/src/platform/linux/api_dispatch.rs +++ b/src/platform/linux/api_dispatch.rs @@ -17,7 +17,7 @@ use api::x11::XConnection; use api::x11::XError; use api::x11::XNotSupported; -#[derive(Default)] +#[derive(Clone, Default)] pub struct PlatformSpecificWindowBuilderAttributes; enum Backend { diff --git a/src/platform/windows/mod.rs b/src/platform/windows/mod.rs index 249ee157..76747ae2 100644 --- a/src/platform/windows/mod.rs +++ b/src/platform/windows/mod.rs @@ -9,9 +9,9 @@ use WindowAttributes; use std::ops::{Deref, DerefMut}; -#[derive(Default)] +#[derive(Clone, Default)] pub struct PlatformSpecificWindowBuilderAttributes; -#[derive(Default)] +#[derive(Clone, Default)] pub struct PlatformSpecificHeadlessBuilderAttributes; /// The Win32 implementation of the main `Window` object. diff --git a/src/window.rs b/src/window.rs index 4142aa4b..d5603f74 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,26 +1,16 @@ use std::collections::vec_deque::IntoIter as VecDequeIter; -use std::default::Default; use CreationError; use CursorState; use Event; use MouseCursor; use Window; -use WindowAttributes; +use WindowBuilder; use native_monitor::NativeMonitorId; use libc; use platform; -/// Object that allows you to build windows. -pub struct WindowBuilder { - /// The attributes to use to create the window. - pub window: WindowAttributes, - - /// Platform-specific configuration. - platform_specific: platform::PlatformSpecificWindowBuilderAttributes, -} - impl WindowBuilder { /// Initializes a new `WindowBuilder` with default values. #[inline] @@ -60,8 +50,8 @@ impl WindowBuilder { /// Requests a specific title for the window. #[inline] - pub fn with_title(mut self, title: String) -> WindowBuilder { - self.window.title = title; + pub fn with_title>(mut self, title: T) -> WindowBuilder { + self.window.title = title.into(); self }