Create reorganization
This commit is contained in:
parent
c1af76550f
commit
3ad7f9a584
27 changed files with 42 additions and 15 deletions
130
src/api/android/ffi.rs
Normal file
130
src/api/android/ffi.rs
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
#![allow(dead_code)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
use libc;
|
||||
|
||||
pub mod egl {
|
||||
pub type khronos_utime_nanoseconds_t = super::khronos_utime_nanoseconds_t;
|
||||
pub type khronos_uint64_t = super::khronos_uint64_t;
|
||||
pub type khronos_ssize_t = super::khronos_ssize_t;
|
||||
pub type EGLNativeDisplayType = super::EGLNativeDisplayType;
|
||||
pub type EGLNativePixmapType = super::EGLNativePixmapType;
|
||||
pub type EGLNativeWindowType = super::EGLNativeWindowType;
|
||||
pub type EGLint = super::EGLint;
|
||||
pub type NativeDisplayType = super::EGLNativeDisplayType;
|
||||
pub type NativePixmapType = super::EGLNativePixmapType;
|
||||
pub type NativeWindowType = super::EGLNativeWindowType;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs"));
|
||||
}
|
||||
|
||||
pub type khronos_utime_nanoseconds_t = khronos_uint64_t;
|
||||
pub type khronos_uint64_t = libc::uint64_t;
|
||||
pub type khronos_ssize_t = libc::c_long;
|
||||
pub type EGLint = libc::int32_t;
|
||||
pub type EGLNativeDisplayType = *const libc::c_void;
|
||||
pub type EGLNativePixmapType = *const libc::c_void; // FIXME: egl_native_pixmap_t instead
|
||||
pub type EGLNativeWindowType = *const ANativeWindow;
|
||||
|
||||
#[link(name = "android")]
|
||||
#[link(name = "EGL")]
|
||||
#[link(name = "GLESv2")]
|
||||
extern {}
|
||||
|
||||
/**
|
||||
* asset_manager.h
|
||||
*/
|
||||
pub type AAssetManager = ();
|
||||
|
||||
/**
|
||||
* native_window.h
|
||||
*/
|
||||
pub type ANativeWindow = ();
|
||||
|
||||
extern {
|
||||
pub fn ANativeWindow_getHeight(window: *const ANativeWindow) -> libc::int32_t;
|
||||
pub fn ANativeWindow_getWidth(window: *const ANativeWindow) -> libc::int32_t;
|
||||
}
|
||||
|
||||
/**
|
||||
* native_activity.h
|
||||
*/
|
||||
pub type JavaVM = ();
|
||||
pub type JNIEnv = ();
|
||||
pub type jobject = *const libc::c_void;
|
||||
|
||||
pub type AInputQueue = (); // FIXME: wrong
|
||||
pub type ARect = (); // FIXME: wrong
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ANativeActivity {
|
||||
pub callbacks: *mut ANativeActivityCallbacks,
|
||||
pub vm: *mut JavaVM,
|
||||
pub env: *mut JNIEnv,
|
||||
pub clazz: jobject,
|
||||
pub internalDataPath: *const libc::c_char,
|
||||
pub externalDataPath: *const libc::c_char,
|
||||
pub sdkVersion: libc::int32_t,
|
||||
pub instance: *mut libc::c_void,
|
||||
pub assetManager: *mut AAssetManager,
|
||||
pub obbPath: *const libc::c_char,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ANativeActivityCallbacks {
|
||||
pub onStart: extern fn(*mut ANativeActivity),
|
||||
pub onResume: extern fn(*mut ANativeActivity),
|
||||
pub onSaveInstanceState: extern fn(*mut ANativeActivity, *mut libc::size_t),
|
||||
pub onPause: extern fn(*mut ANativeActivity),
|
||||
pub onStop: extern fn(*mut ANativeActivity),
|
||||
pub onDestroy: extern fn(*mut ANativeActivity),
|
||||
pub onWindowFocusChanged: extern fn(*mut ANativeActivity, libc::c_int),
|
||||
pub onNativeWindowCreated: extern fn(*mut ANativeActivity, *const ANativeWindow),
|
||||
pub onNativeWindowResized: extern fn(*mut ANativeActivity, *const ANativeWindow),
|
||||
pub onNativeWindowRedrawNeeded: extern fn(*mut ANativeActivity, *const ANativeWindow),
|
||||
pub onNativeWindowDestroyed: extern fn(*mut ANativeActivity, *const ANativeWindow),
|
||||
pub onInputQueueCreated: extern fn(*mut ANativeActivity, *mut AInputQueue),
|
||||
pub onInputQueueDestroyed: extern fn(*mut ANativeActivity, *mut AInputQueue),
|
||||
pub onContentRectChanged: extern fn(*mut ANativeActivity, *const ARect),
|
||||
pub onConfigurationChanged: extern fn(*mut ANativeActivity),
|
||||
pub onLowMemory: extern fn(*mut ANativeActivity),
|
||||
}
|
||||
|
||||
/**
|
||||
* looper.h
|
||||
*/
|
||||
pub type ALooper = ();
|
||||
|
||||
#[link(name = "android")]
|
||||
extern {
|
||||
pub fn ALooper_forThread() -> *const ALooper;
|
||||
pub fn ALooper_acquire(looper: *const ALooper);
|
||||
pub fn ALooper_release(looper: *const ALooper);
|
||||
pub fn ALooper_prepare(opts: libc::c_int) -> *const ALooper;
|
||||
pub fn ALooper_pollOnce(timeoutMillis: libc::c_int, outFd: *mut libc::c_int,
|
||||
outEvents: *mut libc::c_int, outData: *mut *mut libc::c_void) -> libc::c_int;
|
||||
pub fn ALooper_pollAll(timeoutMillis: libc::c_int, outFd: *mut libc::c_int,
|
||||
outEvents: *mut libc::c_int, outData: *mut *mut libc::c_void) -> libc::c_int;
|
||||
pub fn ALooper_wake(looper: *const ALooper);
|
||||
pub fn ALooper_addFd(looper: *const ALooper, fd: libc::c_int, ident: libc::c_int,
|
||||
events: libc::c_int, callback: ALooper_callbackFunc, data: *mut libc::c_void)
|
||||
-> libc::c_int;
|
||||
pub fn ALooper_removeFd(looper: *const ALooper, fd: libc::c_int) -> libc::c_int;
|
||||
}
|
||||
|
||||
pub const ALOOPER_PREPARE_ALLOW_NON_CALLBACKS: libc::c_int = 1 << 0;
|
||||
|
||||
pub const ALOOPER_POLL_WAKE: libc::c_int = -1;
|
||||
pub const ALOOPER_POLL_CALLBACK: libc::c_int = -2;
|
||||
pub const ALOOPER_POLL_TIMEOUT: libc::c_int = -3;
|
||||
pub const ALOOPER_POLL_ERROR: libc::c_int = -4;
|
||||
|
||||
pub const ALOOPER_EVENT_INPUT: libc::c_int = 1 << 0;
|
||||
pub const ALOOPER_EVENT_OUTPUT: libc::c_int = 1 << 1;
|
||||
pub const ALOOPER_EVENT_ERROR: libc::c_int = 1 << 2;
|
||||
pub const ALOOPER_EVENT_HANGUP: libc::c_int = 1 << 3;
|
||||
pub const ALOOPER_EVENT_INVALID: libc::c_int = 1 << 4;
|
||||
|
||||
pub type ALooper_callbackFunc = extern fn(libc::c_int, libc::c_int, *mut libc::c_void) -> libc::c_int;
|
||||
405
src/api/android/mod.rs
Normal file
405
src/api/android/mod.rs
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
#![cfg(target_os = "android")]
|
||||
|
||||
extern crate android_glue;
|
||||
|
||||
use libc;
|
||||
use std::ffi::{CString};
|
||||
use std::sync::mpsc::{Receiver, channel};
|
||||
use {CreationError, Event, MouseCursor};
|
||||
use CreationError::OsError;
|
||||
use events::ElementState::{Pressed, Released};
|
||||
use events::Event::{MouseInput, MouseMoved};
|
||||
use events::MouseButton;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use Api;
|
||||
use BuilderAttribs;
|
||||
use CursorState;
|
||||
use GlRequest;
|
||||
use PixelFormat;
|
||||
use native_monitor::NativeMonitorId;
|
||||
|
||||
pub struct Window {
|
||||
display: ffi::egl::types::EGLDisplay,
|
||||
context: ffi::egl::types::EGLContext,
|
||||
surface: ffi::egl::types::EGLSurface,
|
||||
event_rx: Receiver<android_glue::Event>,
|
||||
}
|
||||
|
||||
pub struct MonitorID;
|
||||
|
||||
mod ffi;
|
||||
|
||||
pub fn get_available_monitors() -> VecDeque <MonitorID> {
|
||||
let mut rb = VecDeque::new();
|
||||
rb.push_back(MonitorID);
|
||||
rb
|
||||
}
|
||||
|
||||
pub fn get_primary_monitor() -> MonitorID {
|
||||
MonitorID
|
||||
}
|
||||
|
||||
impl MonitorID {
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
Some("Primary".to_string())
|
||||
}
|
||||
|
||||
pub fn get_native_identifier(&self) -> NativeMonitorId {
|
||||
NativeMonitorId::Unavailable
|
||||
}
|
||||
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "headless")]
|
||||
pub struct HeadlessContext(i32);
|
||||
|
||||
#[cfg(feature = "headless")]
|
||||
impl HeadlessContext {
|
||||
/// See the docs in the crate root file.
|
||||
pub fn new(_builder: BuilderAttribs) -> Result<HeadlessContext, CreationError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub unsafe fn make_current(&self) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn is_current(&self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn get_proc_address(&self, _addr: &str) -> *const () {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_api(&self) -> ::Api {
|
||||
::Api::OpenGlEs
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "headless")]
|
||||
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) => {
|
||||
match event {
|
||||
android_glue::Event::EventDown => Some(MouseInput(Pressed, MouseButton::Left)),
|
||||
android_glue::Event::EventUp => Some(MouseInput(Released, MouseButton::Left)),
|
||||
android_glue::Event::EventMove(x, y) => Some(MouseMoved((x as i32, y as i32))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WaitEventsIterator<'a> {
|
||||
window: &'a Window,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for WaitEventsIterator<'a> {
|
||||
type Item = Event;
|
||||
|
||||
fn next(&mut self) -> Option<Event> {
|
||||
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};
|
||||
|
||||
if builder.sharing.is_some() {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
let native_window = unsafe { android_glue::get_native_window() };
|
||||
if native_window.is_null() {
|
||||
return Err(OsError(format!("Android's native window is null")));
|
||||
}
|
||||
|
||||
let display = unsafe {
|
||||
let display = ffi::egl::GetDisplay(mem::transmute(ffi::egl::DEFAULT_DISPLAY));
|
||||
if display.is_null() {
|
||||
return Err(OsError("No EGL display connection available".to_string()));
|
||||
}
|
||||
display
|
||||
};
|
||||
|
||||
android_glue::write_log("eglGetDisplay succeeded");
|
||||
|
||||
let (_major, _minor) = unsafe {
|
||||
let mut major: ffi::egl::types::EGLint = mem::uninitialized();
|
||||
let mut minor: ffi::egl::types::EGLint = mem::uninitialized();
|
||||
|
||||
if ffi::egl::Initialize(display, &mut major, &mut minor) == 0 {
|
||||
return Err(OsError(format!("eglInitialize failed")))
|
||||
}
|
||||
|
||||
(major, minor)
|
||||
};
|
||||
|
||||
android_glue::write_log("eglInitialize succeeded");
|
||||
|
||||
let use_gles2 = match builder.gl_version {
|
||||
GlRequest::Specific(Api::OpenGlEs, (2, _)) => true,
|
||||
GlRequest::Specific(Api::OpenGlEs, _) => false,
|
||||
GlRequest::Specific(_, _) => panic!("Only OpenGL ES is supported"), // FIXME: return a result
|
||||
GlRequest::GlThenGles { opengles_version: (2, _), .. } => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let mut attribute_list = vec!();
|
||||
|
||||
if use_gles2 {
|
||||
attribute_list.push(ffi::egl::RENDERABLE_TYPE as i32);
|
||||
attribute_list.push(ffi::egl::OPENGL_ES2_BIT as i32);
|
||||
}
|
||||
|
||||
{
|
||||
let (red, green, blue) = match builder.color_bits.unwrap_or(24) {
|
||||
24 => (8, 8, 8),
|
||||
16 => (6, 5, 6),
|
||||
_ => panic!("Bad color_bits"),
|
||||
};
|
||||
attribute_list.push(ffi::egl::RED_SIZE as i32);
|
||||
attribute_list.push(red);
|
||||
attribute_list.push(ffi::egl::GREEN_SIZE as i32);
|
||||
attribute_list.push(green);
|
||||
attribute_list.push(ffi::egl::BLUE_SIZE as i32);
|
||||
attribute_list.push(blue);
|
||||
}
|
||||
|
||||
attribute_list.push(ffi::egl::DEPTH_SIZE as i32);
|
||||
attribute_list.push(builder.depth_bits.unwrap_or(8) as i32);
|
||||
|
||||
attribute_list.push(ffi::egl::NONE as i32);
|
||||
|
||||
let config = unsafe {
|
||||
let mut num_config: ffi::egl::types::EGLint = mem::uninitialized();
|
||||
let mut config: ffi::egl::types::EGLConfig = mem::uninitialized();
|
||||
if ffi::egl::ChooseConfig(display, attribute_list.as_ptr(), &mut config, 1,
|
||||
&mut num_config) == 0
|
||||
{
|
||||
return Err(OsError(format!("eglChooseConfig failed")))
|
||||
}
|
||||
|
||||
if num_config <= 0 {
|
||||
return Err(OsError(format!("eglChooseConfig returned no available config")))
|
||||
}
|
||||
|
||||
config
|
||||
};
|
||||
|
||||
android_glue::write_log("eglChooseConfig succeeded");
|
||||
|
||||
let context = unsafe {
|
||||
let mut context_attributes = vec!();
|
||||
if use_gles2 {
|
||||
context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
|
||||
context_attributes.push(2);
|
||||
}
|
||||
context_attributes.push(ffi::egl::NONE as i32);
|
||||
|
||||
let context = ffi::egl::CreateContext(display, config, ptr::null(),
|
||||
context_attributes.as_ptr());
|
||||
if context.is_null() {
|
||||
return Err(OsError(format!("eglCreateContext failed")))
|
||||
}
|
||||
context
|
||||
};
|
||||
|
||||
android_glue::write_log("eglCreateContext succeeded");
|
||||
|
||||
let surface = unsafe {
|
||||
let surface = ffi::egl::CreateWindowSurface(display, config, native_window, ptr::null());
|
||||
if surface.is_null() {
|
||||
return Err(OsError(format!("eglCreateWindowSurface failed")))
|
||||
}
|
||||
surface
|
||||
};
|
||||
|
||||
android_glue::write_log("eglCreateWindowSurface succeeded");
|
||||
|
||||
let (tx, rx) = channel();
|
||||
android_glue::add_sender(tx);
|
||||
|
||||
Ok(Window {
|
||||
display: display,
|
||||
context: context,
|
||||
surface: surface,
|
||||
event_rx: rx,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_closed(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn set_title(&self, _: &str) {
|
||||
}
|
||||
|
||||
pub fn show(&self) {
|
||||
}
|
||||
|
||||
pub fn hide(&self) {
|
||||
}
|
||||
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn set_position(&self, _x: i32, _y: i32) {
|
||||
}
|
||||
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
let native_window = unsafe { android_glue::get_native_window() };
|
||||
|
||||
if native_window.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some((
|
||||
unsafe { ffi::ANativeWindow_getWidth(native_window) } as u32,
|
||||
unsafe { ffi::ANativeWindow_getHeight(native_window) } as u32
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
self.get_inner_size()
|
||||
}
|
||||
|
||||
pub fn set_inner_size(&self, _x: u32, _y: u32) {
|
||||
}
|
||||
|
||||
pub fn create_window_proxy(&self) -> WindowProxy {
|
||||
WindowProxy
|
||||
}
|
||||
|
||||
pub fn poll_events(&self) -> PollEventsIterator {
|
||||
PollEventsIterator {
|
||||
window: self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wait_events(&self) -> WaitEventsIterator {
|
||||
WaitEventsIterator {
|
||||
window: self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_current(&self) {
|
||||
unsafe {
|
||||
ffi::egl::MakeCurrent(self.display, self.surface, self.surface, self.context);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_current(&self) -> bool {
|
||||
unsafe { ffi::egl::GetCurrentContext() == self.context }
|
||||
}
|
||||
|
||||
pub fn get_proc_address(&self, addr: &str) -> *const () {
|
||||
let addr = CString::new(addr.as_bytes()).unwrap();
|
||||
let addr = addr.as_ptr();
|
||||
unsafe {
|
||||
ffi::egl::GetProcAddress(addr) as *const ()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn swap_buffers(&self) {
|
||||
unsafe {
|
||||
ffi::egl::SwapBuffers(self.display, self.surface);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
self.display as *mut libc::c_void
|
||||
}
|
||||
|
||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_api(&self) -> ::Api {
|
||||
::Api::OpenGlEs
|
||||
}
|
||||
|
||||
pub fn get_pixel_format(&self) -> PixelFormat {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
|
||||
}
|
||||
|
||||
pub fn set_cursor(&self, _: MouseCursor) {
|
||||
}
|
||||
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
1.0
|
||||
}
|
||||
|
||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Window {}
|
||||
unsafe impl Sync for Window {}
|
||||
|
||||
#[cfg(feature = "window")]
|
||||
#[derive(Clone)]
|
||||
pub struct WindowProxy;
|
||||
|
||||
impl WindowProxy {
|
||||
pub fn wakeup_event_loop(&self) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
use std::ptr;
|
||||
|
||||
unsafe {
|
||||
// we don't call MakeCurrent(0, 0) because we are not sure that the context
|
||||
// is still the current one
|
||||
android_glue::write_log("Destroying gl-init window");
|
||||
ffi::egl::DestroySurface(self.display, self.surface);
|
||||
ffi::egl::DestroyContext(self.display, self.context);
|
||||
ffi::egl::Terminate(self.display);
|
||||
}
|
||||
}
|
||||
}
|
||||
136
src/api/cocoa/event.rs
Normal file
136
src/api/cocoa/event.rs
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
use events;
|
||||
|
||||
pub fn vkeycode_to_element(code: u16) -> Option<events::VirtualKeyCode> {
|
||||
Some(match code {
|
||||
0x00 => events::VirtualKeyCode::A,
|
||||
0x01 => events::VirtualKeyCode::S,
|
||||
0x02 => events::VirtualKeyCode::D,
|
||||
0x03 => events::VirtualKeyCode::F,
|
||||
0x04 => events::VirtualKeyCode::H,
|
||||
0x05 => events::VirtualKeyCode::G,
|
||||
0x06 => events::VirtualKeyCode::Z,
|
||||
0x07 => events::VirtualKeyCode::X,
|
||||
0x08 => events::VirtualKeyCode::C,
|
||||
0x09 => events::VirtualKeyCode::V,
|
||||
//0x0a => World 1,
|
||||
0x0b => events::VirtualKeyCode::B,
|
||||
0x0c => events::VirtualKeyCode::Q,
|
||||
0x0d => events::VirtualKeyCode::W,
|
||||
0x0e => events::VirtualKeyCode::E,
|
||||
0x0f => events::VirtualKeyCode::R,
|
||||
0x10 => events::VirtualKeyCode::Y,
|
||||
0x11 => events::VirtualKeyCode::T,
|
||||
0x12 => events::VirtualKeyCode::Key1,
|
||||
0x13 => events::VirtualKeyCode::Key2,
|
||||
0x14 => events::VirtualKeyCode::Key3,
|
||||
0x15 => events::VirtualKeyCode::Key4,
|
||||
0x16 => events::VirtualKeyCode::Key6,
|
||||
0x17 => events::VirtualKeyCode::Key5,
|
||||
0x18 => events::VirtualKeyCode::Equals,
|
||||
0x19 => events::VirtualKeyCode::Key9,
|
||||
0x1a => events::VirtualKeyCode::Key7,
|
||||
0x1b => events::VirtualKeyCode::Minus,
|
||||
0x1c => events::VirtualKeyCode::Key8,
|
||||
0x1d => events::VirtualKeyCode::Key0,
|
||||
0x1e => events::VirtualKeyCode::RBracket,
|
||||
0x1f => events::VirtualKeyCode::O,
|
||||
0x20 => events::VirtualKeyCode::U,
|
||||
0x21 => events::VirtualKeyCode::LBracket,
|
||||
0x22 => events::VirtualKeyCode::I,
|
||||
0x23 => events::VirtualKeyCode::P,
|
||||
0x24 => events::VirtualKeyCode::Return,
|
||||
0x25 => events::VirtualKeyCode::L,
|
||||
0x26 => events::VirtualKeyCode::J,
|
||||
0x27 => events::VirtualKeyCode::Apostrophe,
|
||||
0x28 => events::VirtualKeyCode::K,
|
||||
0x29 => events::VirtualKeyCode::Semicolon,
|
||||
0x2a => events::VirtualKeyCode::Backslash,
|
||||
0x2b => events::VirtualKeyCode::Comma,
|
||||
0x2c => events::VirtualKeyCode::Slash,
|
||||
0x2d => events::VirtualKeyCode::N,
|
||||
0x2e => events::VirtualKeyCode::M,
|
||||
0x2f => events::VirtualKeyCode::Period,
|
||||
0x30 => events::VirtualKeyCode::Tab,
|
||||
0x31 => events::VirtualKeyCode::Space,
|
||||
0x32 => events::VirtualKeyCode::Grave,
|
||||
0x33 => events::VirtualKeyCode::Back,
|
||||
//0x34 => unkown,
|
||||
0x35 => events::VirtualKeyCode::Escape,
|
||||
0x36 => events::VirtualKeyCode::RWin,
|
||||
0x37 => events::VirtualKeyCode::LWin,
|
||||
0x38 => events::VirtualKeyCode::LShift,
|
||||
//0x39 => Caps lock,
|
||||
//0x3a => Left alt,
|
||||
0x3b => events::VirtualKeyCode::LControl,
|
||||
0x3c => events::VirtualKeyCode::RShift,
|
||||
//0x3d => Right alt,
|
||||
0x3e => events::VirtualKeyCode::RControl,
|
||||
//0x3f => Fn key,
|
||||
//0x40 => F17 Key,
|
||||
0x41 => events::VirtualKeyCode::Decimal,
|
||||
//0x42 -> unkown,
|
||||
0x43 => events::VirtualKeyCode::Multiply,
|
||||
//0x44 => unkown,
|
||||
0x45 => events::VirtualKeyCode::Add,
|
||||
//0x46 => unkown,
|
||||
0x47 => events::VirtualKeyCode::Numlock,
|
||||
//0x48 => KeypadClear,
|
||||
0x49 => events::VirtualKeyCode::VolumeUp,
|
||||
0x4a => events::VirtualKeyCode::VolumeDown,
|
||||
0x4b => events::VirtualKeyCode::Divide,
|
||||
0x4c => events::VirtualKeyCode::NumpadEnter,
|
||||
//0x4d => unkown,
|
||||
0x4e => events::VirtualKeyCode::Subtract,
|
||||
//0x4f => F18 key,
|
||||
//0x50 => F19 Key,
|
||||
0x51 => events::VirtualKeyCode::NumpadEquals,
|
||||
0x52 => events::VirtualKeyCode::Numpad0,
|
||||
0x53 => events::VirtualKeyCode::Numpad1,
|
||||
0x54 => events::VirtualKeyCode::Numpad2,
|
||||
0x55 => events::VirtualKeyCode::Numpad3,
|
||||
0x56 => events::VirtualKeyCode::Numpad4,
|
||||
0x57 => events::VirtualKeyCode::Numpad5,
|
||||
0x58 => events::VirtualKeyCode::Numpad6,
|
||||
0x59 => events::VirtualKeyCode::Numpad7,
|
||||
//0x5a => F20 Key,
|
||||
0x5b => events::VirtualKeyCode::Numpad8,
|
||||
0x5c => events::VirtualKeyCode::Numpad9,
|
||||
//0x5d => unkown,
|
||||
//0x5e => unkown,
|
||||
//0x5f => unkown,
|
||||
0x60 => events::VirtualKeyCode::F5,
|
||||
0x61 => events::VirtualKeyCode::F6,
|
||||
0x62 => events::VirtualKeyCode::F7,
|
||||
0x63 => events::VirtualKeyCode::F3,
|
||||
0x64 => events::VirtualKeyCode::F8,
|
||||
0x65 => events::VirtualKeyCode::F9,
|
||||
//0x66 => unkown,
|
||||
0x67 => events::VirtualKeyCode::F11,
|
||||
//0x68 => unkown,
|
||||
0x69 => events::VirtualKeyCode::F13,
|
||||
//0x6a => F16 Key,
|
||||
0x6b => events::VirtualKeyCode::F14,
|
||||
//0x6c => unkown,
|
||||
0x6d => events::VirtualKeyCode::F10,
|
||||
//0x6e => unkown,
|
||||
0x6f => events::VirtualKeyCode::F12,
|
||||
//0x70 => unkown,
|
||||
0x71 => events::VirtualKeyCode::F15,
|
||||
0x72 => events::VirtualKeyCode::Insert,
|
||||
0x73 => events::VirtualKeyCode::Home,
|
||||
0x74 => events::VirtualKeyCode::PageUp,
|
||||
0x75 => events::VirtualKeyCode::Delete,
|
||||
0x76 => events::VirtualKeyCode::F4,
|
||||
0x77 => events::VirtualKeyCode::End,
|
||||
0x78 => events::VirtualKeyCode::F2,
|
||||
0x79 => events::VirtualKeyCode::PageDown,
|
||||
0x7a => events::VirtualKeyCode::F1,
|
||||
0x7b => events::VirtualKeyCode::Left,
|
||||
0x7c => events::VirtualKeyCode::Right,
|
||||
0x7d => events::VirtualKeyCode::Down,
|
||||
0x7e => events::VirtualKeyCode::Up,
|
||||
//0x7f => unkown,
|
||||
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
110
src/api/cocoa/headless.rs
Normal file
110
src/api/cocoa/headless.rs
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
use CreationError;
|
||||
use CreationError::OsError;
|
||||
use BuilderAttribs;
|
||||
use libc;
|
||||
use std::ptr;
|
||||
|
||||
use core_foundation::base::TCFType;
|
||||
use core_foundation::string::CFString;
|
||||
use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName};
|
||||
use cocoa::base::{id, nil};
|
||||
use cocoa::appkit::*;
|
||||
|
||||
mod gl {
|
||||
include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
|
||||
}
|
||||
|
||||
static mut framebuffer: u32 = 0;
|
||||
static mut texture: u32 = 0;
|
||||
|
||||
pub struct HeadlessContext {
|
||||
width: u32,
|
||||
height: u32,
|
||||
context: id,
|
||||
}
|
||||
|
||||
impl HeadlessContext {
|
||||
pub fn new(builder: BuilderAttribs) -> Result<HeadlessContext, CreationError> {
|
||||
let (width, height) = builder.dimensions.unwrap_or((1024, 768));
|
||||
let context = unsafe {
|
||||
let attributes = [
|
||||
NSOpenGLPFAAccelerated as u32,
|
||||
NSOpenGLPFAAllowOfflineRenderers as u32,
|
||||
NSOpenGLPFADoubleBuffer as u32,
|
||||
0
|
||||
];
|
||||
|
||||
let pixelformat = NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attributes);
|
||||
if pixelformat == nil {
|
||||
return Err(OsError(format!("Could not create the pixel format")));
|
||||
}
|
||||
let context = NSOpenGLContext::alloc(nil).initWithFormat_shareContext_(pixelformat, nil);
|
||||
if context == nil {
|
||||
return Err(OsError(format!("Could not create the rendering context")));
|
||||
}
|
||||
context
|
||||
};
|
||||
|
||||
let headless = HeadlessContext {
|
||||
width: width,
|
||||
height: height,
|
||||
context: context,
|
||||
};
|
||||
|
||||
// Load the function pointers as we need them to create the FBO
|
||||
gl::load_with(|s| headless.get_proc_address(s) as *const libc::c_void);
|
||||
|
||||
Ok(headless)
|
||||
}
|
||||
|
||||
pub unsafe fn make_current(&self) {
|
||||
self.context.makeCurrentContext();
|
||||
|
||||
gl::GenFramebuffersEXT(1, &mut framebuffer);
|
||||
gl::BindFramebufferEXT(gl::FRAMEBUFFER_EXT, framebuffer);
|
||||
gl::GenTextures(1, &mut texture);
|
||||
gl::BindTexture(gl::TEXTURE_2D, texture);
|
||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
|
||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
|
||||
gl::TexImage2D(gl::TEXTURE_2D, 0, gl::RGBA8 as i32, self.width as i32, self.height as i32,
|
||||
0, gl::RGBA, gl::UNSIGNED_BYTE, ptr::null());
|
||||
gl::FramebufferTexture2DEXT(gl::FRAMEBUFFER_EXT, gl::COLOR_ATTACHMENT0_EXT,
|
||||
gl::TEXTURE_2D, texture, 0);
|
||||
let status = gl::CheckFramebufferStatusEXT(gl::FRAMEBUFFER_EXT);
|
||||
if status != gl::FRAMEBUFFER_COMPLETE_EXT {
|
||||
panic!("Error while creating the framebuffer");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_current(&self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_proc_address(&self, _addr: &str) -> *const () {
|
||||
let symbol_name: CFString = _addr.parse().unwrap();
|
||||
let framework_name: CFString = "com.apple.opengl".parse().unwrap();
|
||||
let framework = unsafe {
|
||||
CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef())
|
||||
};
|
||||
let symbol = unsafe {
|
||||
CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef())
|
||||
};
|
||||
symbol as *const ()
|
||||
}
|
||||
|
||||
pub fn get_api(&self) -> ::Api {
|
||||
::Api::OpenGl
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for HeadlessContext {}
|
||||
unsafe impl Sync for HeadlessContext {}
|
||||
|
||||
impl Drop for HeadlessContext {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gl::DeleteTextures(1, &texture);
|
||||
gl::DeleteFramebuffersEXT(1, &framebuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
741
src/api/cocoa/mod.rs
Normal file
741
src/api/cocoa/mod.rs
Normal file
|
|
@ -0,0 +1,741 @@
|
|||
#![cfg(target_os = "macos")]
|
||||
|
||||
#[cfg(feature = "headless")]
|
||||
pub use self::headless::HeadlessContext;
|
||||
|
||||
use {CreationError, Event, MouseCursor, CursorState};
|
||||
use CreationError::OsError;
|
||||
use libc;
|
||||
|
||||
use Api;
|
||||
use BuilderAttribs;
|
||||
use GlRequest;
|
||||
use PixelFormat;
|
||||
use native_monitor::NativeMonitorId;
|
||||
|
||||
use objc::runtime::{Class, Object, Sel, BOOL, YES, NO};
|
||||
use objc::declare::ClassDecl;
|
||||
|
||||
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 core_foundation::base::TCFType;
|
||||
use core_foundation::string::CFString;
|
||||
use core_foundation::bundle::{CFBundleGetBundleWithIdentifier, CFBundleGetFunctionPointerForName};
|
||||
|
||||
use std::ffi::CStr;
|
||||
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::Event::{Awakened, MouseInput, MouseMoved, ReceivedCharacter, KeyboardInput, MouseWheel, Closed};
|
||||
use events::ElementState::{Pressed, Released};
|
||||
use events::MouseButton;
|
||||
use events;
|
||||
|
||||
pub use self::monitor::{MonitorID, get_available_monitors, get_primary_monitor};
|
||||
|
||||
mod monitor;
|
||||
mod event;
|
||||
|
||||
#[cfg(feature = "headless")]
|
||||
mod headless;
|
||||
|
||||
static mut shift_pressed: bool = false;
|
||||
static mut ctrl_pressed: bool = false;
|
||||
static mut win_pressed: bool = false;
|
||||
static mut alt_pressed: bool = false;
|
||||
|
||||
struct DelegateState {
|
||||
is_closed: bool,
|
||||
context: IdRef,
|
||||
view: IdRef,
|
||||
window: IdRef,
|
||||
resize_handler: Option<fn(u32, u32)>,
|
||||
|
||||
/// Events that have been retreived with XLib but not dispatched with iterators yet
|
||||
pending_events: Mutex<VecDeque<Event>>,
|
||||
}
|
||||
|
||||
struct WindowDelegate {
|
||||
state: Box<DelegateState>,
|
||||
_this: IdRef,
|
||||
}
|
||||
|
||||
impl WindowDelegate {
|
||||
/// Get the delegate class, initiailizing it neccessary
|
||||
fn class() -> *const Class {
|
||||
use std::sync::{Once, ONCE_INIT};
|
||||
|
||||
extern fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
|
||||
unsafe {
|
||||
let state: *mut libc::c_void = *this.get_ivar("glutinState");
|
||||
let state = state as *mut DelegateState;
|
||||
(*state).is_closed = true;
|
||||
|
||||
(*state).pending_events.lock().unwrap().push_back(Closed);
|
||||
}
|
||||
YES
|
||||
}
|
||||
|
||||
extern fn window_did_resize(this: &Object, _: Sel, _: id) {
|
||||
unsafe {
|
||||
let state: *mut libc::c_void = *this.get_ivar("glutinState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
|
||||
let _: () = msg_send![*state.context, update];
|
||||
|
||||
if let Some(handler) = state.resize_handler {
|
||||
let rect = NSView::frame(*state.view);
|
||||
let scale_factor = NSWindow::backingScaleFactor(*state.window) as f32;
|
||||
(handler)((scale_factor * rect.size.width as f32) as u32,
|
||||
(scale_factor * rect.size.height as f32) as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static mut delegate_class: *const Class = 0 as *const Class;
|
||||
static INIT: Once = ONCE_INIT;
|
||||
|
||||
INIT.call_once(|| unsafe {
|
||||
// Create new NSWindowDelegate
|
||||
let superclass = Class::get("NSObject").unwrap();
|
||||
let mut decl = ClassDecl::new(superclass, "GlutinWindowDelegate").unwrap();
|
||||
|
||||
// Add callback methods
|
||||
decl.add_method(sel!(windowShouldClose:),
|
||||
window_should_close as extern fn(&Object, Sel, id) -> BOOL);
|
||||
decl.add_method(sel!(windowDidResize:),
|
||||
window_did_resize as extern fn(&Object, Sel, id));
|
||||
|
||||
// Store internal state as user data
|
||||
decl.add_ivar::<*mut libc::c_void>("glutinState");
|
||||
|
||||
delegate_class = decl.register();
|
||||
});
|
||||
|
||||
unsafe {
|
||||
delegate_class
|
||||
}
|
||||
}
|
||||
|
||||
fn new(state: DelegateState) -> WindowDelegate {
|
||||
// Box the state so we can give a pointer to it
|
||||
let mut state = Box::new(state);
|
||||
let state_ptr: *mut DelegateState = &mut *state;
|
||||
unsafe {
|
||||
let delegate = IdRef::new(msg_send![WindowDelegate::class(), new]);
|
||||
|
||||
(&mut **delegate).set_ivar("glutinState", state_ptr as *mut libc::c_void);
|
||||
let _: () = msg_send![*state.window, setDelegate:*delegate];
|
||||
|
||||
WindowDelegate { state: state, _this: delegate }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WindowDelegate {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// Nil the window's delegate so it doesn't still reference us
|
||||
let _: () = msg_send![*self.state.window, setDelegate:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Window {
|
||||
view: IdRef,
|
||||
window: IdRef,
|
||||
context: IdRef,
|
||||
delegate: WindowDelegate,
|
||||
}
|
||||
|
||||
#[cfg(feature = "window")]
|
||||
unsafe impl Send for Window {}
|
||||
#[cfg(feature = "window")]
|
||||
unsafe impl Sync for Window {}
|
||||
|
||||
#[cfg(feature = "window")]
|
||||
#[derive(Clone)]
|
||||
pub struct WindowProxy;
|
||||
|
||||
impl WindowProxy {
|
||||
pub fn wakeup_event_loop(&self) {
|
||||
unsafe {
|
||||
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, YES);
|
||||
pool.drain();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.delegate.state.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; }
|
||||
NSApp().sendEvent_(event);
|
||||
|
||||
let event = match msg_send![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 => {
|
||||
let window_point = event.locationInWindow();
|
||||
let window: id = msg_send![event, window];
|
||||
let view_point = if window == nil {
|
||||
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 = VecDeque::new();
|
||||
let received_c_str = event.characters().UTF8String();
|
||||
let received_str = CStr::from_ptr(received_c_str);
|
||||
for received_char in from_utf8(received_str.to_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.delegate.state.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 = VecDeque::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.delegate.state.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.delegate.state.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);
|
||||
} else {
|
||||
return Some(Awakened);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
#[cfg(feature = "window")]
|
||||
pub fn new(builder: BuilderAttribs) -> Result<Window, CreationError> {
|
||||
if builder.sharing.is_some() {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
let app = match Window::create_app() {
|
||||
Some(app) => app,
|
||||
None => { return Err(OsError(format!("Couldn't create NSApplication"))); },
|
||||
};
|
||||
let window = match Window::create_window(builder.dimensions.unwrap_or((800, 600)),
|
||||
&*builder.title,
|
||||
builder.monitor)
|
||||
{
|
||||
Some(window) => window,
|
||||
None => { return Err(OsError(format!("Couldn't create NSWindow"))); },
|
||||
};
|
||||
let view = match Window::create_view(*window) {
|
||||
Some(view) => view,
|
||||
None => { return Err(OsError(format!("Couldn't create NSView"))); },
|
||||
};
|
||||
|
||||
let context = match Window::create_context(*view, builder.vsync, builder.gl_version) {
|
||||
Some(context) => context,
|
||||
None => { return Err(OsError(format!("Couldn't create OpenGL context"))); },
|
||||
};
|
||||
|
||||
unsafe {
|
||||
app.activateIgnoringOtherApps_(YES);
|
||||
if builder.visible {
|
||||
window.makeKeyAndOrderFront_(nil);
|
||||
} else {
|
||||
window.makeKeyWindow();
|
||||
}
|
||||
}
|
||||
|
||||
let ds = DelegateState {
|
||||
is_closed: false,
|
||||
context: context.clone(),
|
||||
view: view.clone(),
|
||||
window: window.clone(),
|
||||
resize_handler: None,
|
||||
pending_events: Mutex::new(VecDeque::new()),
|
||||
};
|
||||
|
||||
let window = Window {
|
||||
view: view,
|
||||
window: window,
|
||||
context: context,
|
||||
delegate: WindowDelegate::new(ds),
|
||||
};
|
||||
|
||||
Ok(window)
|
||||
}
|
||||
|
||||
fn create_app() -> Option<id> {
|
||||
unsafe {
|
||||
let app = NSApp();
|
||||
if app == nil {
|
||||
None
|
||||
} else {
|
||||
app.setActivationPolicy_(NSApplicationActivationPolicyRegular);
|
||||
app.finishLaunching();
|
||||
Some(app)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_window(dimensions: (u32, u32), title: &str, monitor: Option<MonitorID>) -> Option<IdRef> {
|
||||
unsafe {
|
||||
let screen = monitor.map(|monitor_id| {
|
||||
let native_id = match monitor_id.get_native_identifier() {
|
||||
NativeMonitorId::Numeric(num) => num,
|
||||
_ => panic!("OS X monitors should always have a numeric native ID")
|
||||
};
|
||||
let matching_screen = {
|
||||
let screens = 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<id> = None;
|
||||
for i in (0..count) {
|
||||
let screen = msg_send![screens, objectAtIndex:i as NSUInteger];
|
||||
let device_description = NSScreen::deviceDescription(screen);
|
||||
let value: id = msg_send![device_description, objectForKey:*key];
|
||||
if value != nil {
|
||||
let screen_number: NSUInteger = msg_send![value, unsignedIntegerValue];
|
||||
if screen_number as u32 == native_id {
|
||||
matching_screen = Some(screen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
matching_screen
|
||||
};
|
||||
matching_screen.unwrap_or(NSScreen::mainScreen(nil))
|
||||
});
|
||||
let frame = match screen {
|
||||
Some(screen) => NSScreen::frame(screen),
|
||||
None => {
|
||||
let (width, height) = dimensions;
|
||||
NSRect::new(NSPoint::new(0., 0.), NSSize::new(width as f64, height as f64))
|
||||
}
|
||||
};
|
||||
|
||||
let masks = if screen.is_some() {
|
||||
NSBorderlessWindowMask as NSUInteger
|
||||
} else {
|
||||
NSTitledWindowMask as NSUInteger |
|
||||
NSClosableWindowMask as NSUInteger |
|
||||
NSMiniaturizableWindowMask as NSUInteger |
|
||||
NSResizableWindowMask as NSUInteger
|
||||
};
|
||||
|
||||
let window = IdRef::new(NSWindow::alloc(nil).initWithContentRect_styleMask_backing_defer_(
|
||||
frame,
|
||||
masks,
|
||||
NSBackingStoreBuffered,
|
||||
NO,
|
||||
));
|
||||
window.non_nil().map(|window| {
|
||||
let title = IdRef::new(NSString::alloc(nil).init_str(title));
|
||||
window.setTitle_(*title);
|
||||
window.setAcceptsMouseMovedEvents_(YES);
|
||||
if screen.is_some() {
|
||||
window.setLevel_(NSMainMenuWindowLevel as i64 + 1);
|
||||
}
|
||||
else {
|
||||
window.center();
|
||||
}
|
||||
window
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn create_view(window: id) -> Option<IdRef> {
|
||||
unsafe {
|
||||
let view = IdRef::new(NSView::alloc(nil).init());
|
||||
view.non_nil().map(|view| {
|
||||
view.setWantsBestResolutionOpenGLSurface_(YES);
|
||||
window.setContentView_(*view);
|
||||
view
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn create_context(view: id, vsync: bool, gl_version: GlRequest) -> Option<IdRef> {
|
||||
let profile = match gl_version {
|
||||
GlRequest::Latest => NSOpenGLProfileVersion4_1Core as u32,
|
||||
GlRequest::Specific(Api::OpenGl, (1 ... 2, _)) => NSOpenGLProfileVersionLegacy as u32,
|
||||
GlRequest::Specific(Api::OpenGl, (3, 0)) => NSOpenGLProfileVersionLegacy as u32,
|
||||
GlRequest::Specific(Api::OpenGl, (3, 1 ... 2)) => NSOpenGLProfileVersion3_2Core as u32,
|
||||
GlRequest::Specific(Api::OpenGl, _) => NSOpenGLProfileVersion4_1Core as u32,
|
||||
GlRequest::Specific(_, _) => panic!("Only the OpenGL API is supported"), // FIXME: return Result
|
||||
GlRequest::GlThenGles { opengl_version: (1 ... 2, _), .. } => NSOpenGLProfileVersionLegacy as u32,
|
||||
GlRequest::GlThenGles { opengl_version: (3, 0), .. } => NSOpenGLProfileVersionLegacy as u32,
|
||||
GlRequest::GlThenGles { opengl_version: (3, 1 ... 2), .. } => NSOpenGLProfileVersion3_2Core as u32,
|
||||
GlRequest::GlThenGles { .. } => NSOpenGLProfileVersion4_1Core as u32,
|
||||
};
|
||||
unsafe {
|
||||
let attributes = [
|
||||
NSOpenGLPFADoubleBuffer as u32,
|
||||
NSOpenGLPFAClosestPolicy as u32,
|
||||
NSOpenGLPFAColorSize as u32, 24,
|
||||
NSOpenGLPFAAlphaSize as u32, 8,
|
||||
NSOpenGLPFADepthSize as u32, 24,
|
||||
NSOpenGLPFAStencilSize as u32, 8,
|
||||
NSOpenGLPFAOpenGLProfile as u32, profile,
|
||||
0
|
||||
];
|
||||
|
||||
let pixelformat = IdRef::new(NSOpenGLPixelFormat::alloc(nil).initWithAttributes_(&attributes));
|
||||
pixelformat.non_nil().map(|pixelformat| {
|
||||
let context = IdRef::new(NSOpenGLContext::alloc(nil).initWithFormat_shareContext_(*pixelformat, nil));
|
||||
context.non_nil().map(|context| {
|
||||
context.setView_(view);
|
||||
if vsync {
|
||||
let value = 1;
|
||||
context.setValues_forParameter_(&value, NSOpenGLContextParameter::NSOpenGLCPSwapInterval);
|
||||
}
|
||||
context
|
||||
})
|
||||
}).unwrap_or(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_closed(&self) -> bool {
|
||||
self.delegate.state.is_closed
|
||||
}
|
||||
|
||||
pub fn set_title(&self, title: &str) {
|
||||
unsafe {
|
||||
let title = IdRef::new(NSString::alloc(nil).init_str(title));
|
||||
self.window.setTitle_(*title);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn show(&self) {
|
||||
unsafe { NSWindow::makeKeyAndOrderFront_(*self.window, nil); }
|
||||
}
|
||||
|
||||
pub fn hide(&self) {
|
||||
unsafe { NSWindow::orderOut_(*self.window, nil); }
|
||||
}
|
||||
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
unsafe {
|
||||
let content_rect = NSWindow::contentRectForFrameRect_(*self.window, NSWindow::frame(*self.window));
|
||||
// NOTE: coordinate system might be inconsistent with other backends
|
||||
Some((content_rect.origin.x as i32, content_rect.origin.y as i32))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_position(&self, x: i32, y: i32) {
|
||||
unsafe {
|
||||
// NOTE: coordinate system might be inconsistent with other backends
|
||||
NSWindow::setFrameOrigin_(*self.window, NSPoint::new(x as f64, y as f64));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
unsafe {
|
||||
let view_frame = NSView::frame(*self.view);
|
||||
Some((view_frame.size.width as u32, view_frame.size.height as u32))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
unsafe {
|
||||
let window_frame = NSWindow::frame(*self.window);
|
||||
Some((window_frame.size.width as u32, window_frame.size.height as u32))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_inner_size(&self, width: u32, height: u32) {
|
||||
unsafe {
|
||||
NSWindow::setContentSize_(*self.window, NSSize::new(width as f64, height as f64));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_window_proxy(&self) -> WindowProxy {
|
||||
WindowProxy
|
||||
}
|
||||
|
||||
pub fn poll_events(&self) -> PollEventsIterator {
|
||||
PollEventsIterator {
|
||||
window: self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wait_events(&self) -> WaitEventsIterator {
|
||||
WaitEventsIterator {
|
||||
window: self
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn modifier_event(event: id, keymask: NSEventModifierFlags, key: events::VirtualKeyCode, key_pressed: bool) -> Option<Event> {
|
||||
if !key_pressed && NSEvent::modifierFlags(event).contains(keymask) {
|
||||
return Some(KeyboardInput(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 None;
|
||||
}
|
||||
|
||||
pub unsafe fn make_current(&self) {
|
||||
let _: () = msg_send![*self.context, update];
|
||||
self.context.makeCurrentContext();
|
||||
}
|
||||
|
||||
pub fn is_current(&self) -> bool {
|
||||
unsafe {
|
||||
let current = NSOpenGLContext::currentContext(nil);
|
||||
if current != nil {
|
||||
let is_equal: BOOL = msg_send![current, isEqual:*self.context];
|
||||
is_equal != NO
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_proc_address(&self, _addr: &str) -> *const () {
|
||||
let symbol_name: CFString = FromStr::from_str(_addr).unwrap();
|
||||
let framework_name: CFString = FromStr::from_str("com.apple.opengl").unwrap();
|
||||
let framework = unsafe {
|
||||
CFBundleGetBundleWithIdentifier(framework_name.as_concrete_TypeRef())
|
||||
};
|
||||
let symbol = unsafe {
|
||||
CFBundleGetFunctionPointerForName(framework, symbol_name.as_concrete_TypeRef())
|
||||
};
|
||||
symbol as *const ()
|
||||
}
|
||||
|
||||
pub fn swap_buffers(&self) {
|
||||
unsafe { self.context.flushBuffer(); }
|
||||
}
|
||||
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_api(&self) -> ::Api {
|
||||
::Api::OpenGl
|
||||
}
|
||||
|
||||
pub fn get_pixel_format(&self) -> PixelFormat {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn set_window_resize_callback(&mut self, callback: Option<fn(u32, u32)>) {
|
||||
self.delegate.state.resize_handler = callback;
|
||||
}
|
||||
|
||||
pub fn set_cursor(&self, cursor: MouseCursor) {
|
||||
let cursor_name = match cursor {
|
||||
MouseCursor::Arrow | MouseCursor::Default => "arrowCursor",
|
||||
MouseCursor::Hand => "pointingHandCursor",
|
||||
MouseCursor::Grabbing | MouseCursor::Grab => "closedHandCursor",
|
||||
MouseCursor::Text => "IBeamCursor",
|
||||
MouseCursor::VerticalText => "IBeamCursorForVerticalLayout",
|
||||
MouseCursor::Copy => "dragCopyCursor",
|
||||
MouseCursor::Alias => "dragLinkCursor",
|
||||
MouseCursor::NotAllowed | MouseCursor::NoDrop => "operationNotAllowedCursor",
|
||||
MouseCursor::ContextMenu => "contextualMenuCursor",
|
||||
MouseCursor::Crosshair => "crosshairCursor",
|
||||
MouseCursor::EResize => "resizeRightCursor",
|
||||
MouseCursor::NResize => "resizeUpCursor",
|
||||
MouseCursor::WResize => "resizeLeftCursor",
|
||||
MouseCursor::SResize => "resizeDownCursor",
|
||||
MouseCursor::EwResize | MouseCursor::ColResize => "resizeLeftRightCursor",
|
||||
MouseCursor::NsResize | MouseCursor::RowResize => "resizeUpDownCursor",
|
||||
|
||||
/// TODO: Find appropriate OSX cursors
|
||||
MouseCursor::NeResize | MouseCursor::NwResize |
|
||||
MouseCursor::SeResize | MouseCursor::SwResize |
|
||||
MouseCursor::NwseResize | MouseCursor::NeswResize |
|
||||
|
||||
MouseCursor::Cell | MouseCursor::NoneCursor |
|
||||
MouseCursor::Wait | MouseCursor::Progress | MouseCursor::Help |
|
||||
MouseCursor::Move | MouseCursor::AllScroll | MouseCursor::ZoomIn |
|
||||
MouseCursor::ZoomOut => "arrowCursor",
|
||||
};
|
||||
let sel = Sel::register(cursor_name);
|
||||
let cls = Class::get("NSCursor").unwrap();
|
||||
unsafe {
|
||||
use objc::MessageArguments;
|
||||
let cursor: id = ().send(cls as *const _ as id, sel);
|
||||
let _: () = msg_send![cursor, set];
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
let cls = Class::get("NSCursor").unwrap();
|
||||
match state {
|
||||
CursorState::Normal => {
|
||||
let _: () = unsafe { msg_send![cls, unhide] };
|
||||
Ok(())
|
||||
},
|
||||
CursorState::Hide => {
|
||||
let _: () = unsafe { msg_send![cls, hide] };
|
||||
Ok(())
|
||||
},
|
||||
CursorState::Grab => {
|
||||
Err("Mouse grabbing is unimplemented".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
unsafe {
|
||||
NSWindow::backingScaleFactor(*self.window) as f32
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
struct IdRef(id);
|
||||
|
||||
impl IdRef {
|
||||
fn new(i: id) -> IdRef {
|
||||
IdRef(i)
|
||||
}
|
||||
|
||||
fn retain(i: id) -> IdRef {
|
||||
if i != nil {
|
||||
let _: id = unsafe { msg_send![i, retain] };
|
||||
}
|
||||
IdRef(i)
|
||||
}
|
||||
|
||||
fn non_nil(self) -> Option<IdRef> {
|
||||
if self.0 == nil { None } else { Some(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for IdRef {
|
||||
fn drop(&mut self) {
|
||||
if self.0 != nil {
|
||||
let _: () = unsafe { msg_send![self.0, release] };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for IdRef {
|
||||
type Target = id;
|
||||
fn deref<'a>(&'a self) -> &'a id {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for IdRef {
|
||||
fn clone(&self) -> IdRef {
|
||||
if self.0 != nil {
|
||||
let _: id = unsafe { msg_send![self.0, retain] };
|
||||
}
|
||||
IdRef(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
53
src/api/cocoa/monitor.rs
Normal file
53
src/api/cocoa/monitor.rs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
use core_graphics::display;
|
||||
use std::collections::VecDeque;
|
||||
use native_monitor::NativeMonitorId;
|
||||
|
||||
pub struct MonitorID(u32);
|
||||
|
||||
pub fn get_available_monitors() -> VecDeque<MonitorID> {
|
||||
let mut monitors = VecDeque::new();
|
||||
unsafe {
|
||||
let max_displays = 10u32;
|
||||
let mut active_displays = [0u32; 10];
|
||||
let mut display_count = 0;
|
||||
display::CGGetActiveDisplayList(max_displays,
|
||||
&mut active_displays[0],
|
||||
&mut display_count);
|
||||
for i in 0..display_count as usize {
|
||||
monitors.push_back(MonitorID(active_displays[i]));
|
||||
}
|
||||
}
|
||||
monitors
|
||||
}
|
||||
|
||||
pub fn get_primary_monitor() -> MonitorID {
|
||||
let id = unsafe {
|
||||
MonitorID(display::CGMainDisplayID())
|
||||
};
|
||||
id
|
||||
}
|
||||
|
||||
impl MonitorID {
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
let MonitorID(display_id) = *self;
|
||||
let screen_num = unsafe {
|
||||
display::CGDisplayModelNumber(display_id)
|
||||
};
|
||||
Some(format!("Monitor #{}", screen_num))
|
||||
}
|
||||
|
||||
pub fn get_native_identifier(&self) -> NativeMonitorId {
|
||||
let MonitorID(display_id) = *self;
|
||||
NativeMonitorId::Numeric(display_id)
|
||||
}
|
||||
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
let MonitorID(display_id) = *self;
|
||||
let dimension = unsafe {
|
||||
let height = display::CGDisplayPixelsHigh(display_id);
|
||||
let width = display::CGDisplayPixelsWide(display_id);
|
||||
(width as u32, height as u32)
|
||||
};
|
||||
dimension
|
||||
}
|
||||
}
|
||||
4
src/api/mod.rs
Normal file
4
src/api/mod.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
pub mod android;
|
||||
pub mod cocoa;
|
||||
pub mod win32;
|
||||
pub mod x11;
|
||||
253
src/api/win32/callback.rs
Normal file
253
src/api/win32/callback.rs
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::cell::RefCell;
|
||||
use std::sync::mpsc::Sender;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use CursorState;
|
||||
use Event;
|
||||
use super::event;
|
||||
|
||||
use user32;
|
||||
use winapi;
|
||||
|
||||
/// There's no parameters passed to the callback function, so it needs to get
|
||||
/// its context (the HWND, the Sender for events, etc.) stashed in
|
||||
/// a thread-local variable.
|
||||
thread_local!(pub static CONTEXT_STASH: RefCell<Option<ThreadLocalData>> = RefCell::new(None));
|
||||
|
||||
pub struct ThreadLocalData {
|
||||
pub win: winapi::HWND,
|
||||
pub sender: Sender<Event>,
|
||||
pub cursor_state: Arc<Mutex<CursorState>>
|
||||
}
|
||||
|
||||
/// Checks that the window is the good one, and if so send the event to it.
|
||||
fn send_event(input_window: winapi::HWND, event: Event) {
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
let context_stash = context_stash.borrow();
|
||||
let stored = match *context_stash {
|
||||
None => return,
|
||||
Some(ref v) => v
|
||||
};
|
||||
|
||||
let &ThreadLocalData { ref win, ref sender, .. } = stored;
|
||||
|
||||
if win != &input_window {
|
||||
return;
|
||||
}
|
||||
|
||||
sender.send(event).ok(); // ignoring if closed
|
||||
});
|
||||
}
|
||||
|
||||
/// This is the callback that is called by `DispatchMessage` in the events loop.
|
||||
///
|
||||
/// Returning 0 tells the Win32 API that the message has been processed.
|
||||
pub unsafe extern "system" fn callback(window: winapi::HWND, msg: winapi::UINT,
|
||||
wparam: winapi::WPARAM, lparam: winapi::LPARAM)
|
||||
-> winapi::LRESULT
|
||||
{
|
||||
match msg {
|
||||
winapi::WM_DESTROY => {
|
||||
use events::Event::Closed;
|
||||
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
let context_stash = context_stash.borrow();
|
||||
let stored = match *context_stash {
|
||||
None => return,
|
||||
Some(ref v) => v
|
||||
};
|
||||
|
||||
let &ThreadLocalData { ref win, .. } = stored;
|
||||
|
||||
if win == &window {
|
||||
user32::PostQuitMessage(0);
|
||||
}
|
||||
});
|
||||
|
||||
send_event(window, Closed);
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_ERASEBKGND => {
|
||||
1
|
||||
},
|
||||
|
||||
winapi::WM_SIZE => {
|
||||
use events::Event::Resized;
|
||||
let w = winapi::LOWORD(lparam as winapi::DWORD) as u32;
|
||||
let h = winapi::HIWORD(lparam as winapi::DWORD) as u32;
|
||||
send_event(window, Resized(w, h));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_MOVE => {
|
||||
use events::Event::Moved;
|
||||
let x = winapi::LOWORD(lparam as winapi::DWORD) as i32;
|
||||
let y = winapi::HIWORD(lparam as winapi::DWORD) as i32;
|
||||
send_event(window, Moved(x, y));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_CHAR => {
|
||||
use std::mem;
|
||||
use events::Event::ReceivedCharacter;
|
||||
let chr: char = mem::transmute(wparam as u32);
|
||||
send_event(window, ReceivedCharacter(chr));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_MOUSEMOVE => {
|
||||
use events::Event::MouseMoved;
|
||||
|
||||
let x = winapi::GET_X_LPARAM(lparam) as i32;
|
||||
let y = winapi::GET_Y_LPARAM(lparam) as i32;
|
||||
|
||||
send_event(window, MouseMoved((x, y)));
|
||||
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_MOUSEWHEEL => {
|
||||
use events::Event::MouseWheel;
|
||||
|
||||
let value = (wparam >> 16) as i16;
|
||||
let value = value as i32;
|
||||
|
||||
send_event(window, MouseWheel(value));
|
||||
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_KEYDOWN => {
|
||||
use events::Event::KeyboardInput;
|
||||
use events::ElementState::Pressed;
|
||||
let scancode = ((lparam >> 16) & 0xff) as u8;
|
||||
let vkey = event::vkeycode_to_element(wparam);
|
||||
send_event(window, KeyboardInput(Pressed, scancode, vkey));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_KEYUP => {
|
||||
use events::Event::KeyboardInput;
|
||||
use events::ElementState::Released;
|
||||
let scancode = ((lparam >> 16) & 0xff) as u8;
|
||||
let vkey = event::vkeycode_to_element(wparam);
|
||||
send_event(window, KeyboardInput(Released, scancode, vkey));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_LBUTTONDOWN => {
|
||||
use events::Event::MouseInput;
|
||||
use events::MouseButton::Left;
|
||||
use events::ElementState::Pressed;
|
||||
send_event(window, MouseInput(Pressed, Left));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_LBUTTONUP => {
|
||||
use events::Event::MouseInput;
|
||||
use events::MouseButton::Left;
|
||||
use events::ElementState::Released;
|
||||
send_event(window, MouseInput(Released, Left));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_RBUTTONDOWN => {
|
||||
use events::Event::MouseInput;
|
||||
use events::MouseButton::Right;
|
||||
use events::ElementState::Pressed;
|
||||
send_event(window, MouseInput(Pressed, Right));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_RBUTTONUP => {
|
||||
use events::Event::MouseInput;
|
||||
use events::MouseButton::Right;
|
||||
use events::ElementState::Released;
|
||||
send_event(window, MouseInput(Released, Right));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_MBUTTONDOWN => {
|
||||
use events::Event::MouseInput;
|
||||
use events::MouseButton::Middle;
|
||||
use events::ElementState::Pressed;
|
||||
send_event(window, MouseInput(Pressed, Middle));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_MBUTTONUP => {
|
||||
use events::Event::MouseInput;
|
||||
use events::MouseButton::Middle;
|
||||
use events::ElementState::Released;
|
||||
send_event(window, MouseInput(Released, Middle));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_INPUT => {
|
||||
let mut data: winapi::RAWINPUT = mem::uninitialized();
|
||||
let mut data_size = mem::size_of::<winapi::RAWINPUT>() as winapi::UINT;
|
||||
user32::GetRawInputData(mem::transmute(lparam), winapi::RID_INPUT,
|
||||
mem::transmute(&mut data), &mut data_size,
|
||||
mem::size_of::<winapi::RAWINPUTHEADER>() as winapi::UINT);
|
||||
|
||||
if data.header.dwType == winapi::RIM_TYPEMOUSE {
|
||||
let _x = data.mouse.lLastX; // FIXME: this is not always the relative movement
|
||||
let _y = data.mouse.lLastY;
|
||||
// TODO:
|
||||
//send_event(window, Event::MouseRawMovement { x: x, y: y });
|
||||
|
||||
0
|
||||
|
||||
} else {
|
||||
user32::DefWindowProcW(window, msg, wparam, lparam)
|
||||
}
|
||||
},
|
||||
|
||||
winapi::WM_SETFOCUS => {
|
||||
use events::Event::Focused;
|
||||
send_event(window, Focused(true));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_KILLFOCUS => {
|
||||
use events::Event::Focused;
|
||||
send_event(window, Focused(false));
|
||||
0
|
||||
},
|
||||
|
||||
winapi::WM_SETCURSOR => {
|
||||
CONTEXT_STASH.with(|context_stash| {
|
||||
let cstash = context_stash.borrow();
|
||||
let cstash = cstash.as_ref();
|
||||
// there's a very bizarre borrow checker bug
|
||||
// possibly related to rust-lang/rust/#23338
|
||||
let cursor_state = if let Some(cstash) = cstash {
|
||||
if let Ok(cursor_state) = cstash.cursor_state.lock() {
|
||||
match *cursor_state {
|
||||
CursorState::Normal => {
|
||||
user32::SetCursor(user32::LoadCursorW(
|
||||
ptr::null_mut(),
|
||||
winapi::IDC_ARROW));
|
||||
},
|
||||
CursorState::Grab | CursorState::Hide => {
|
||||
user32::SetCursor(ptr::null_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return
|
||||
};
|
||||
|
||||
// let &ThreadLocalData { ref cursor_state, .. } = stored;
|
||||
});
|
||||
0
|
||||
},
|
||||
|
||||
_ => {
|
||||
user32::DefWindowProcW(window, msg, wparam, lparam)
|
||||
}
|
||||
}
|
||||
}
|
||||
181
src/api/win32/event.rs
Normal file
181
src/api/win32/event.rs
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
use events::VirtualKeyCode;
|
||||
use winapi;
|
||||
|
||||
pub fn vkeycode_to_element(code: winapi::WPARAM) -> Option<VirtualKeyCode> {
|
||||
match code {
|
||||
//winapi::VK_LBUTTON => Some(VirtualKeyCode::Lbutton),
|
||||
//winapi::VK_RBUTTON => Some(VirtualKeyCode::Rbutton),
|
||||
//winapi::VK_CANCEL => Some(VirtualKeyCode::Cancel),
|
||||
//winapi::VK_MBUTTON => Some(VirtualKeyCode::Mbutton),
|
||||
//winapi::VK_XBUTTON1 => Some(VirtualKeyCode::Xbutton1),
|
||||
//winapi::VK_XBUTTON2 => Some(VirtualKeyCode::Xbutton2),
|
||||
winapi::VK_BACK => Some(VirtualKeyCode::Back),
|
||||
winapi::VK_TAB => Some(VirtualKeyCode::Tab),
|
||||
//winapi::VK_CLEAR => Some(VirtualKeyCode::Clear),
|
||||
winapi::VK_RETURN => Some(VirtualKeyCode::Return),
|
||||
//winapi::VK_SHIFT => Some(VirtualKeyCode::Shift),
|
||||
//winapi::VK_CONTROL => Some(VirtualKeyCode::Control),
|
||||
//winapi::VK_MENU => Some(VirtualKeyCode::Menu),
|
||||
winapi::VK_PAUSE => Some(VirtualKeyCode::Pause),
|
||||
winapi::VK_CAPITAL => Some(VirtualKeyCode::Capital),
|
||||
winapi::VK_KANA => Some(VirtualKeyCode::Kana),
|
||||
//winapi::VK_HANGUEL => Some(VirtualKeyCode::Hanguel),
|
||||
//winapi::VK_HANGUL => Some(VirtualKeyCode::Hangul),
|
||||
//winapi::VK_JUNJA => Some(VirtualKeyCode::Junja),
|
||||
//winapi::VK_FINAL => Some(VirtualKeyCode::Final),
|
||||
//winapi::VK_HANJA => Some(VirtualKeyCode::Hanja),
|
||||
winapi::VK_KANJI => Some(VirtualKeyCode::Kanji),
|
||||
winapi::VK_ESCAPE => Some(VirtualKeyCode::Escape),
|
||||
winapi::VK_CONVERT => Some(VirtualKeyCode::Convert),
|
||||
//winapi::VK_NONCONVERT => Some(VirtualKeyCode::Nonconvert),
|
||||
//winapi::VK_ACCEPT => Some(VirtualKeyCode::Accept),
|
||||
//winapi::VK_MODECHANGE => Some(VirtualKeyCode::Modechange),
|
||||
winapi::VK_SPACE => Some(VirtualKeyCode::Space),
|
||||
winapi::VK_PRIOR => Some(VirtualKeyCode::PageUp),
|
||||
winapi::VK_NEXT => Some(VirtualKeyCode::PageDown),
|
||||
winapi::VK_END => Some(VirtualKeyCode::End),
|
||||
winapi::VK_HOME => Some(VirtualKeyCode::Home),
|
||||
winapi::VK_LEFT => Some(VirtualKeyCode::Left),
|
||||
winapi::VK_UP => Some(VirtualKeyCode::Up),
|
||||
winapi::VK_RIGHT => Some(VirtualKeyCode::Right),
|
||||
winapi::VK_DOWN => Some(VirtualKeyCode::Down),
|
||||
//winapi::VK_SELECT => Some(VirtualKeyCode::Select),
|
||||
//winapi::VK_PRINT => Some(VirtualKeyCode::Print),
|
||||
//winapi::VK_EXECUTE => Some(VirtualKeyCode::Execute),
|
||||
winapi::VK_SNAPSHOT => Some(VirtualKeyCode::Snapshot),
|
||||
winapi::VK_INSERT => Some(VirtualKeyCode::Insert),
|
||||
winapi::VK_DELETE => Some(VirtualKeyCode::Delete),
|
||||
//winapi::VK_HELP => Some(VirtualKeyCode::Help),
|
||||
0x30 => Some(VirtualKeyCode::Key0),
|
||||
0x31 => Some(VirtualKeyCode::Key1),
|
||||
0x32 => Some(VirtualKeyCode::Key2),
|
||||
0x33 => Some(VirtualKeyCode::Key3),
|
||||
0x34 => Some(VirtualKeyCode::Key4),
|
||||
0x35 => Some(VirtualKeyCode::Key5),
|
||||
0x36 => Some(VirtualKeyCode::Key6),
|
||||
0x37 => Some(VirtualKeyCode::Key7),
|
||||
0x38 => Some(VirtualKeyCode::Key8),
|
||||
0x39 => Some(VirtualKeyCode::Key9),
|
||||
0x41 => Some(VirtualKeyCode::A),
|
||||
0x42 => Some(VirtualKeyCode::B),
|
||||
0x43 => Some(VirtualKeyCode::C),
|
||||
0x44 => Some(VirtualKeyCode::D),
|
||||
0x45 => Some(VirtualKeyCode::E),
|
||||
0x46 => Some(VirtualKeyCode::F),
|
||||
0x47 => Some(VirtualKeyCode::G),
|
||||
0x48 => Some(VirtualKeyCode::H),
|
||||
0x49 => Some(VirtualKeyCode::I),
|
||||
0x4A => Some(VirtualKeyCode::J),
|
||||
0x4B => Some(VirtualKeyCode::K),
|
||||
0x4C => Some(VirtualKeyCode::L),
|
||||
0x4D => Some(VirtualKeyCode::M),
|
||||
0x4E => Some(VirtualKeyCode::N),
|
||||
0x4F => Some(VirtualKeyCode::O),
|
||||
0x50 => Some(VirtualKeyCode::P),
|
||||
0x51 => Some(VirtualKeyCode::Q),
|
||||
0x52 => Some(VirtualKeyCode::R),
|
||||
0x53 => Some(VirtualKeyCode::S),
|
||||
0x54 => Some(VirtualKeyCode::T),
|
||||
0x55 => Some(VirtualKeyCode::U),
|
||||
0x56 => Some(VirtualKeyCode::V),
|
||||
0x57 => Some(VirtualKeyCode::W),
|
||||
0x58 => Some(VirtualKeyCode::X),
|
||||
0x59 => Some(VirtualKeyCode::Y),
|
||||
0x5A => Some(VirtualKeyCode::Z),
|
||||
//winapi::VK_LWIN => Some(VirtualKeyCode::Lwin),
|
||||
//winapi::VK_RWIN => Some(VirtualKeyCode::Rwin),
|
||||
winapi::VK_APPS => Some(VirtualKeyCode::Apps),
|
||||
winapi::VK_SLEEP => Some(VirtualKeyCode::Sleep),
|
||||
winapi::VK_NUMPAD0 => Some(VirtualKeyCode::Numpad0),
|
||||
winapi::VK_NUMPAD1 => Some(VirtualKeyCode::Numpad1),
|
||||
winapi::VK_NUMPAD2 => Some(VirtualKeyCode::Numpad2),
|
||||
winapi::VK_NUMPAD3 => Some(VirtualKeyCode::Numpad3),
|
||||
winapi::VK_NUMPAD4 => Some(VirtualKeyCode::Numpad4),
|
||||
winapi::VK_NUMPAD5 => Some(VirtualKeyCode::Numpad5),
|
||||
winapi::VK_NUMPAD6 => Some(VirtualKeyCode::Numpad6),
|
||||
winapi::VK_NUMPAD7 => Some(VirtualKeyCode::Numpad7),
|
||||
winapi::VK_NUMPAD8 => Some(VirtualKeyCode::Numpad8),
|
||||
winapi::VK_NUMPAD9 => Some(VirtualKeyCode::Numpad9),
|
||||
winapi::VK_MULTIPLY => Some(VirtualKeyCode::Multiply),
|
||||
winapi::VK_ADD => Some(VirtualKeyCode::Add),
|
||||
//winapi::VK_SEPARATOR => Some(VirtualKeyCode::Separator),
|
||||
winapi::VK_SUBTRACT => Some(VirtualKeyCode::Subtract),
|
||||
winapi::VK_DECIMAL => Some(VirtualKeyCode::Decimal),
|
||||
winapi::VK_DIVIDE => Some(VirtualKeyCode::Divide),
|
||||
winapi::VK_F1 => Some(VirtualKeyCode::F1),
|
||||
winapi::VK_F2 => Some(VirtualKeyCode::F2),
|
||||
winapi::VK_F3 => Some(VirtualKeyCode::F3),
|
||||
winapi::VK_F4 => Some(VirtualKeyCode::F4),
|
||||
winapi::VK_F5 => Some(VirtualKeyCode::F5),
|
||||
winapi::VK_F6 => Some(VirtualKeyCode::F6),
|
||||
winapi::VK_F7 => Some(VirtualKeyCode::F7),
|
||||
winapi::VK_F8 => Some(VirtualKeyCode::F8),
|
||||
winapi::VK_F9 => Some(VirtualKeyCode::F9),
|
||||
winapi::VK_F10 => Some(VirtualKeyCode::F10),
|
||||
winapi::VK_F11 => Some(VirtualKeyCode::F11),
|
||||
winapi::VK_F12 => Some(VirtualKeyCode::F12),
|
||||
winapi::VK_F13 => Some(VirtualKeyCode::F13),
|
||||
winapi::VK_F14 => Some(VirtualKeyCode::F14),
|
||||
winapi::VK_F15 => Some(VirtualKeyCode::F15),
|
||||
/*winapi::VK_F16 => Some(VirtualKeyCode::F16),
|
||||
winapi::VK_F17 => Some(VirtualKeyCode::F17),
|
||||
winapi::VK_F18 => Some(VirtualKeyCode::F18),
|
||||
winapi::VK_F19 => Some(VirtualKeyCode::F19),
|
||||
winapi::VK_F20 => Some(VirtualKeyCode::F20),
|
||||
winapi::VK_F21 => Some(VirtualKeyCode::F21),
|
||||
winapi::VK_F22 => Some(VirtualKeyCode::F22),
|
||||
winapi::VK_F23 => Some(VirtualKeyCode::F23),
|
||||
winapi::VK_F24 => Some(VirtualKeyCode::F24),*/
|
||||
winapi::VK_NUMLOCK => Some(VirtualKeyCode::Numlock),
|
||||
winapi::VK_SCROLL => Some(VirtualKeyCode::Scroll),
|
||||
/*winapi::VK_LSHIFT => Some(VirtualKeyCode::Lshift),
|
||||
winapi::VK_RSHIFT => Some(VirtualKeyCode::Rshift),
|
||||
winapi::VK_LCONTROL => Some(VirtualKeyCode::Lcontrol),
|
||||
winapi::VK_RCONTROL => Some(VirtualKeyCode::Rcontrol),
|
||||
winapi::VK_LMENU => Some(VirtualKeyCode::Lmenu),
|
||||
winapi::VK_RMENU => Some(VirtualKeyCode::Rmenu),
|
||||
winapi::VK_BROWSER_BACK => Some(VirtualKeyCode::Browser_back),
|
||||
winapi::VK_BROWSER_FORWARD => Some(VirtualKeyCode::Browser_forward),
|
||||
winapi::VK_BROWSER_REFRESH => Some(VirtualKeyCode::Browser_refresh),
|
||||
winapi::VK_BROWSER_STOP => Some(VirtualKeyCode::Browser_stop),
|
||||
winapi::VK_BROWSER_SEARCH => Some(VirtualKeyCode::Browser_search),
|
||||
winapi::VK_BROWSER_FAVORITES => Some(VirtualKeyCode::Browser_favorites),
|
||||
winapi::VK_BROWSER_HOME => Some(VirtualKeyCode::Browser_home),
|
||||
winapi::VK_VOLUME_MUTE => Some(VirtualKeyCode::Volume_mute),
|
||||
winapi::VK_VOLUME_DOWN => Some(VirtualKeyCode::Volume_down),
|
||||
winapi::VK_VOLUME_UP => Some(VirtualKeyCode::Volume_up),
|
||||
winapi::VK_MEDIA_NEXT_TRACK => Some(VirtualKeyCode::Media_next_track),
|
||||
winapi::VK_MEDIA_PREV_TRACK => Some(VirtualKeyCode::Media_prev_track),
|
||||
winapi::VK_MEDIA_STOP => Some(VirtualKeyCode::Media_stop),
|
||||
winapi::VK_MEDIA_PLAY_PAUSE => Some(VirtualKeyCode::Media_play_pause),
|
||||
winapi::VK_LAUNCH_MAIL => Some(VirtualKeyCode::Launch_mail),
|
||||
winapi::VK_LAUNCH_MEDIA_SELECT => Some(VirtualKeyCode::Launch_media_select),
|
||||
winapi::VK_LAUNCH_APP1 => Some(VirtualKeyCode::Launch_app1),
|
||||
winapi::VK_LAUNCH_APP2 => Some(VirtualKeyCode::Launch_app2),
|
||||
winapi::VK_OEM_1 => Some(VirtualKeyCode::Oem_1),
|
||||
winapi::VK_OEM_PLUS => Some(VirtualKeyCode::Oem_plus),
|
||||
winapi::VK_OEM_COMMA => Some(VirtualKeyCode::Oem_comma),
|
||||
winapi::VK_OEM_MINUS => Some(VirtualKeyCode::Oem_minus),
|
||||
winapi::VK_OEM_PERIOD => Some(VirtualKeyCode::Oem_period),
|
||||
winapi::VK_OEM_2 => Some(VirtualKeyCode::Oem_2),
|
||||
winapi::VK_OEM_3 => Some(VirtualKeyCode::Oem_3),
|
||||
winapi::VK_OEM_4 => Some(VirtualKeyCode::Oem_4),
|
||||
winapi::VK_OEM_5 => Some(VirtualKeyCode::Oem_5),
|
||||
winapi::VK_OEM_6 => Some(VirtualKeyCode::Oem_6),
|
||||
winapi::VK_OEM_7 => Some(VirtualKeyCode::Oem_7),
|
||||
winapi::VK_OEM_8 => Some(VirtualKeyCode::Oem_8),
|
||||
winapi::VK_OEM_102 => Some(VirtualKeyCode::Oem_102),
|
||||
winapi::VK_PROCESSKEY => Some(VirtualKeyCode::Processkey),
|
||||
winapi::VK_PACKET => Some(VirtualKeyCode::Packet),
|
||||
winapi::VK_ATTN => Some(VirtualKeyCode::Attn),
|
||||
winapi::VK_CRSEL => Some(VirtualKeyCode::Crsel),
|
||||
winapi::VK_EXSEL => Some(VirtualKeyCode::Exsel),
|
||||
winapi::VK_EREOF => Some(VirtualKeyCode::Ereof),
|
||||
winapi::VK_PLAY => Some(VirtualKeyCode::Play),
|
||||
winapi::VK_ZOOM => Some(VirtualKeyCode::Zoom),
|
||||
winapi::VK_NONAME => Some(VirtualKeyCode::Noname),
|
||||
winapi::VK_PA1 => Some(VirtualKeyCode::Pa1),
|
||||
winapi::VK_OEM_CLEAR => Some(VirtualKeyCode::Oem_clear),*/
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
12
src/api/win32/gl.rs
Normal file
12
src/api/win32/gl.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
/// WGL bindings
|
||||
pub mod wgl {
|
||||
include!(concat!(env!("OUT_DIR"), "/wgl_bindings.rs"));
|
||||
}
|
||||
|
||||
/// Functions that are not necessarly always available
|
||||
pub mod wgl_extra {
|
||||
include!(concat!(env!("OUT_DIR"), "/wgl_extra_bindings.rs"));
|
||||
}
|
||||
|
||||
#[link(name = "opengl32")]
|
||||
extern {}
|
||||
40
src/api/win32/headless.rs
Normal file
40
src/api/win32/headless.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
use super::Window;
|
||||
use super::init;
|
||||
|
||||
use Api;
|
||||
use BuilderAttribs;
|
||||
use CreationError;
|
||||
|
||||
///
|
||||
pub struct HeadlessContext(Window);
|
||||
|
||||
impl HeadlessContext {
|
||||
/// See the docs in the crate root file.
|
||||
pub fn new(builder: BuilderAttribs) -> Result<HeadlessContext, CreationError> {
|
||||
let (builder, _) = builder.extract_non_static();
|
||||
init::new_window(builder, None).map(|w| HeadlessContext(w))
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub unsafe fn make_current(&self) {
|
||||
self.0.make_current()
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn is_current(&self) -> bool {
|
||||
self.0.is_current()
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn get_proc_address(&self, addr: &str) -> *const () {
|
||||
self.0.get_proc_address(addr)
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn get_api(&self) -> Api {
|
||||
Api::OpenGl
|
||||
}
|
||||
|
||||
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
|
||||
}
|
||||
}
|
||||
586
src/api/win32/init.rs
Normal file
586
src/api/win32/init.rs
Normal file
|
|
@ -0,0 +1,586 @@
|
|||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::io;
|
||||
use std::ptr;
|
||||
use std::mem;
|
||||
use std::thread;
|
||||
|
||||
use super::callback;
|
||||
use super::Window;
|
||||
use super::MonitorID;
|
||||
use super::ContextWrapper;
|
||||
use super::WindowWrapper;
|
||||
use super::make_current_guard::CurrentContextGuard;
|
||||
|
||||
use Api;
|
||||
use BuilderAttribs;
|
||||
use CreationError;
|
||||
use CreationError::OsError;
|
||||
use CursorState;
|
||||
use GlRequest;
|
||||
use PixelFormat;
|
||||
|
||||
use std::ffi::{CStr, CString, OsStr};
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
use libc;
|
||||
use super::gl;
|
||||
use winapi;
|
||||
use kernel32;
|
||||
use user32;
|
||||
use gdi32;
|
||||
|
||||
/// Work-around the fact that HGLRC doesn't implement Send
|
||||
pub struct ContextHack(pub winapi::HGLRC);
|
||||
unsafe impl Send for ContextHack {}
|
||||
|
||||
pub fn new_window(builder: BuilderAttribs<'static>, builder_sharelists: Option<ContextHack>)
|
||||
-> Result<Window, CreationError>
|
||||
{
|
||||
// initializing variables to be sent to the task
|
||||
|
||||
let title = OsStr::new(&builder.title).encode_wide().chain(Some(0).into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let (tx, rx) = channel();
|
||||
|
||||
// `GetMessage` must be called in the same thread as CreateWindow, so we create a new thread
|
||||
// dedicated to this window.
|
||||
thread::spawn(move || {
|
||||
unsafe {
|
||||
// creating and sending the `Window`
|
||||
match init(title, builder, builder_sharelists) {
|
||||
Ok(w) => tx.send(Ok(w)).ok(),
|
||||
Err(e) => {
|
||||
tx.send(Err(e)).ok();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// now that the `Window` struct is initialized, the main `Window::new()` function will
|
||||
// return and this events loop will run in parallel
|
||||
loop {
|
||||
let mut msg = mem::uninitialized();
|
||||
|
||||
if user32::GetMessageW(&mut msg, ptr::null_mut(), 0, 0) == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
user32::TranslateMessage(&msg);
|
||||
user32::DispatchMessageW(&msg); // calls `callback` (see the callback module)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
rx.recv().unwrap()
|
||||
}
|
||||
|
||||
unsafe fn init(title: Vec<u16>, builder: BuilderAttribs<'static>,
|
||||
builder_sharelists: Option<ContextHack>) -> Result<Window, CreationError>
|
||||
{
|
||||
let builder_sharelists = builder_sharelists.map(|s| s.0);
|
||||
|
||||
// registering the window class
|
||||
let class_name = register_window_class();
|
||||
|
||||
// building a RECT object with coordinates
|
||||
let mut rect = winapi::RECT {
|
||||
left: 0, right: builder.dimensions.unwrap_or((1024, 768)).0 as winapi::LONG,
|
||||
top: 0, bottom: builder.dimensions.unwrap_or((1024, 768)).1 as winapi::LONG,
|
||||
};
|
||||
|
||||
// switching to fullscreen if necessary
|
||||
// this means adjusting the window's position so that it overlaps the right monitor,
|
||||
// and change the monitor's resolution if necessary
|
||||
if builder.monitor.is_some() {
|
||||
let monitor = builder.monitor.as_ref().unwrap();
|
||||
try!(switch_to_fullscreen(&mut rect, monitor));
|
||||
}
|
||||
|
||||
// computing the style and extended style of the window
|
||||
let (ex_style, style) = if builder.monitor.is_some() {
|
||||
(winapi::WS_EX_APPWINDOW, winapi::WS_POPUP | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN)
|
||||
} else {
|
||||
(winapi::WS_EX_APPWINDOW | winapi::WS_EX_WINDOWEDGE,
|
||||
winapi::WS_OVERLAPPEDWINDOW | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN)
|
||||
};
|
||||
|
||||
// adjusting the window coordinates using the style
|
||||
user32::AdjustWindowRectEx(&mut rect, style, 0, ex_style);
|
||||
|
||||
// the first step is to create a dummy window and a dummy context which we will use
|
||||
// to load the pointers to some functions in the OpenGL driver in `extra_functions`
|
||||
let extra_functions = {
|
||||
// creating a dummy invisible window
|
||||
let dummy_window = {
|
||||
let handle = user32::CreateWindowExW(ex_style, class_name.as_ptr(),
|
||||
title.as_ptr() as winapi::LPCWSTR,
|
||||
style | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN,
|
||||
winapi::CW_USEDEFAULT, winapi::CW_USEDEFAULT,
|
||||
rect.right - rect.left, rect.bottom - rect.top,
|
||||
ptr::null_mut(), ptr::null_mut(), kernel32::GetModuleHandleW(ptr::null()),
|
||||
ptr::null_mut());
|
||||
|
||||
if handle.is_null() {
|
||||
return Err(OsError(format!("CreateWindowEx function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
let hdc = user32::GetDC(handle);
|
||||
if hdc.is_null() {
|
||||
let err = Err(OsError(format!("GetDC function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
return err;
|
||||
}
|
||||
|
||||
WindowWrapper(handle, hdc)
|
||||
};
|
||||
|
||||
// getting the pixel format that we will use and setting it
|
||||
{
|
||||
let formats = enumerate_native_pixel_formats(&dummy_window);
|
||||
let id = try!(choose_dummy_pixel_format(formats.into_iter()));
|
||||
try!(set_pixel_format(&dummy_window, id));
|
||||
}
|
||||
|
||||
// creating the dummy OpenGL context and making it current
|
||||
let dummy_context = try!(create_context(None, &dummy_window, None));
|
||||
let current_context = try!(CurrentContextGuard::make_current(&dummy_window,
|
||||
&dummy_context));
|
||||
|
||||
// loading the extra WGL functions
|
||||
gl::wgl_extra::Wgl::load_with(|addr| {
|
||||
use libc;
|
||||
|
||||
let addr = CString::new(addr.as_bytes()).unwrap();
|
||||
let addr = addr.as_ptr();
|
||||
|
||||
gl::wgl::GetProcAddress(addr) as *const libc::c_void
|
||||
})
|
||||
};
|
||||
|
||||
// creating the real window this time, by using the functions in `extra_functions`
|
||||
let real_window = {
|
||||
let (width, height) = if builder.monitor.is_some() || builder.dimensions.is_some() {
|
||||
(Some(rect.right - rect.left), Some(rect.bottom - rect.top))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let (x, y) = if builder.monitor.is_some() {
|
||||
(Some(rect.left), Some(rect.top))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let style = if !builder.visible || builder.headless {
|
||||
style
|
||||
} else {
|
||||
style | winapi::WS_VISIBLE
|
||||
};
|
||||
|
||||
let handle = user32::CreateWindowExW(ex_style, class_name.as_ptr(),
|
||||
title.as_ptr() as winapi::LPCWSTR,
|
||||
style | winapi::WS_CLIPSIBLINGS | winapi::WS_CLIPCHILDREN,
|
||||
x.unwrap_or(winapi::CW_USEDEFAULT), y.unwrap_or(winapi::CW_USEDEFAULT),
|
||||
width.unwrap_or(winapi::CW_USEDEFAULT), height.unwrap_or(winapi::CW_USEDEFAULT),
|
||||
ptr::null_mut(), ptr::null_mut(), kernel32::GetModuleHandleW(ptr::null()),
|
||||
ptr::null_mut());
|
||||
|
||||
if handle.is_null() {
|
||||
return Err(OsError(format!("CreateWindowEx function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
let hdc = user32::GetDC(handle);
|
||||
if hdc.is_null() {
|
||||
return Err(OsError(format!("GetDC function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
WindowWrapper(handle, hdc)
|
||||
};
|
||||
|
||||
// calling SetPixelFormat
|
||||
let pixel_format = {
|
||||
let formats = if extra_functions.GetPixelFormatAttribivARB.is_loaded() {
|
||||
enumerate_arb_pixel_formats(&extra_functions, &real_window)
|
||||
} else {
|
||||
enumerate_native_pixel_formats(&real_window)
|
||||
};
|
||||
|
||||
let (id, f) = try!(builder.choose_pixel_format(formats.into_iter().map(|(a, b)| (b, a))));
|
||||
try!(set_pixel_format(&real_window, id));
|
||||
f
|
||||
};
|
||||
|
||||
// creating the OpenGL context
|
||||
let context = try!(create_context(Some((&extra_functions, &builder)), &real_window,
|
||||
builder_sharelists));
|
||||
|
||||
// calling SetForegroundWindow if fullscreen
|
||||
if builder.monitor.is_some() {
|
||||
user32::SetForegroundWindow(real_window.0);
|
||||
}
|
||||
|
||||
// Creating a mutex to track the current cursor state
|
||||
let cursor_state = Arc::new(Mutex::new(CursorState::Normal));
|
||||
|
||||
// filling the CONTEXT_STASH task-local storage so that we can start receiving events
|
||||
let events_receiver = {
|
||||
let (tx, rx) = channel();
|
||||
let mut tx = Some(tx);
|
||||
callback::CONTEXT_STASH.with(|context_stash| {
|
||||
let data = callback::ThreadLocalData {
|
||||
win: real_window.0,
|
||||
sender: tx.take().unwrap(),
|
||||
cursor_state: cursor_state.clone()
|
||||
};
|
||||
(*context_stash.borrow_mut()) = Some(data);
|
||||
});
|
||||
rx
|
||||
};
|
||||
|
||||
// loading the opengl32 module
|
||||
let gl_library = try!(load_opengl32_dll());
|
||||
|
||||
// handling vsync
|
||||
if builder.vsync {
|
||||
if extra_functions.SwapIntervalEXT.is_loaded() {
|
||||
let _guard = try!(CurrentContextGuard::make_current(&real_window, &context));
|
||||
|
||||
if extra_functions.SwapIntervalEXT(1) == 0 {
|
||||
return Err(OsError(format!("wglSwapIntervalEXT failed")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// building the struct
|
||||
Ok(Window {
|
||||
window: real_window,
|
||||
context: context,
|
||||
gl_library: gl_library,
|
||||
events_receiver: events_receiver,
|
||||
is_closed: AtomicBool::new(false),
|
||||
cursor_state: cursor_state,
|
||||
pixel_format: pixel_format,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn register_window_class() -> Vec<u16> {
|
||||
let class_name = OsStr::new("Window Class").encode_wide().chain(Some(0).into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let class = winapi::WNDCLASSEXW {
|
||||
cbSize: mem::size_of::<winapi::WNDCLASSEXW>() as winapi::UINT,
|
||||
style: winapi::CS_HREDRAW | winapi::CS_VREDRAW | winapi::CS_OWNDC,
|
||||
lpfnWndProc: Some(callback::callback),
|
||||
cbClsExtra: 0,
|
||||
cbWndExtra: 0,
|
||||
hInstance: kernel32::GetModuleHandleW(ptr::null()),
|
||||
hIcon: ptr::null_mut(),
|
||||
hCursor: ptr::null_mut(), // must be null in order for cursor state to work properly
|
||||
hbrBackground: ptr::null_mut(),
|
||||
lpszMenuName: ptr::null(),
|
||||
lpszClassName: class_name.as_ptr(),
|
||||
hIconSm: ptr::null_mut(),
|
||||
};
|
||||
|
||||
// We ignore errors because registering the same window class twice would trigger
|
||||
// an error, and because errors here are detected during CreateWindowEx anyway.
|
||||
// Also since there is no weird element in the struct, there is no reason for this
|
||||
// call to fail.
|
||||
user32::RegisterClassExW(&class);
|
||||
|
||||
class_name
|
||||
}
|
||||
|
||||
unsafe fn switch_to_fullscreen(rect: &mut winapi::RECT, monitor: &MonitorID)
|
||||
-> Result<(), CreationError>
|
||||
{
|
||||
// adjusting the rect
|
||||
{
|
||||
let pos = monitor.get_position();
|
||||
rect.left += pos.0 as winapi::LONG;
|
||||
rect.right += pos.0 as winapi::LONG;
|
||||
rect.top += pos.1 as winapi::LONG;
|
||||
rect.bottom += pos.1 as winapi::LONG;
|
||||
}
|
||||
|
||||
// changing device settings
|
||||
let mut screen_settings: winapi::DEVMODEW = mem::zeroed();
|
||||
screen_settings.dmSize = mem::size_of::<winapi::DEVMODEW>() as winapi::WORD;
|
||||
screen_settings.dmPelsWidth = (rect.right - rect.left) as winapi::DWORD;
|
||||
screen_settings.dmPelsHeight = (rect.bottom - rect.top) as winapi::DWORD;
|
||||
screen_settings.dmBitsPerPel = 32; // TODO: ?
|
||||
screen_settings.dmFields = winapi::DM_BITSPERPEL | winapi::DM_PELSWIDTH | winapi::DM_PELSHEIGHT;
|
||||
|
||||
let result = user32::ChangeDisplaySettingsExW(monitor.get_adapter_name().as_ptr(),
|
||||
&mut screen_settings, ptr::null_mut(),
|
||||
winapi::CDS_FULLSCREEN, ptr::null_mut());
|
||||
|
||||
if result != winapi::DISP_CHANGE_SUCCESSFUL {
|
||||
return Err(OsError(format!("ChangeDisplaySettings failed: {}", result)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn create_context(extra: Option<(&gl::wgl_extra::Wgl, &BuilderAttribs<'static>)>,
|
||||
hdc: &WindowWrapper, share: Option<winapi::HGLRC>)
|
||||
-> Result<ContextWrapper, CreationError>
|
||||
{
|
||||
let share = share.unwrap_or(ptr::null_mut());
|
||||
|
||||
let ctxt = if let Some((extra_functions, builder)) = extra {
|
||||
if extra_functions.CreateContextAttribsARB.is_loaded() {
|
||||
let mut attributes = Vec::new();
|
||||
|
||||
match builder.gl_version {
|
||||
GlRequest::Latest => {},
|
||||
GlRequest::Specific(Api::OpenGl, (major, minor)) => {
|
||||
attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int);
|
||||
attributes.push(major as libc::c_int);
|
||||
attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int);
|
||||
attributes.push(minor as libc::c_int);
|
||||
},
|
||||
GlRequest::Specific(Api::OpenGlEs, (major, minor)) => {
|
||||
if is_extension_supported(extra_functions, hdc,
|
||||
"WGL_EXT_create_context_es2_profile")
|
||||
{
|
||||
attributes.push(gl::wgl_extra::CONTEXT_PROFILE_MASK_ARB as libc::c_int);
|
||||
attributes.push(gl::wgl_extra::CONTEXT_ES2_PROFILE_BIT_EXT as libc::c_int);
|
||||
} else {
|
||||
return Err(CreationError::NotSupported);
|
||||
}
|
||||
|
||||
attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int);
|
||||
attributes.push(major as libc::c_int);
|
||||
attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int);
|
||||
attributes.push(minor as libc::c_int);
|
||||
},
|
||||
GlRequest::Specific(_, _) => return Err(CreationError::NotSupported),
|
||||
GlRequest::GlThenGles { opengl_version: (major, minor), .. } => {
|
||||
attributes.push(gl::wgl_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int);
|
||||
attributes.push(major as libc::c_int);
|
||||
attributes.push(gl::wgl_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int);
|
||||
attributes.push(minor as libc::c_int);
|
||||
},
|
||||
}
|
||||
|
||||
if builder.gl_debug {
|
||||
attributes.push(gl::wgl_extra::CONTEXT_FLAGS_ARB as libc::c_int);
|
||||
attributes.push(gl::wgl_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int);
|
||||
}
|
||||
|
||||
attributes.push(0);
|
||||
|
||||
Some(extra_functions.CreateContextAttribsARB(hdc.1 as *const libc::c_void,
|
||||
share as *const libc::c_void,
|
||||
attributes.as_ptr()))
|
||||
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let ctxt = match ctxt {
|
||||
Some(ctxt) => ctxt,
|
||||
None => {
|
||||
let ctxt = gl::wgl::CreateContext(hdc.1 as *const libc::c_void);
|
||||
if !ctxt.is_null() && !share.is_null() {
|
||||
gl::wgl::ShareLists(share as *const libc::c_void, ctxt);
|
||||
};
|
||||
ctxt
|
||||
}
|
||||
};
|
||||
|
||||
if ctxt.is_null() {
|
||||
return Err(OsError(format!("OpenGL context creation failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
Ok(ContextWrapper(ctxt as winapi::HGLRC))
|
||||
}
|
||||
|
||||
unsafe fn enumerate_native_pixel_formats(hdc: &WindowWrapper) -> Vec<(PixelFormat, libc::c_int)> {
|
||||
let size_of_pxfmtdescr = mem::size_of::<winapi::PIXELFORMATDESCRIPTOR>() as u32;
|
||||
let num = gdi32::DescribePixelFormat(hdc.1, 1, size_of_pxfmtdescr, ptr::null_mut());
|
||||
|
||||
let mut result = Vec::new();
|
||||
|
||||
for index in (0 .. num) {
|
||||
let mut output: winapi::PIXELFORMATDESCRIPTOR = mem::zeroed();
|
||||
|
||||
if gdi32::DescribePixelFormat(hdc.1, index, size_of_pxfmtdescr, &mut output) == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (output.dwFlags & winapi::PFD_DRAW_TO_WINDOW) == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (output.dwFlags & winapi::PFD_SUPPORT_OPENGL) == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if output.iPixelType != winapi::PFD_TYPE_RGBA {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push((PixelFormat {
|
||||
hardware_accelerated: (output.dwFlags & winapi::PFD_GENERIC_FORMAT) == 0,
|
||||
red_bits: output.cRedBits,
|
||||
green_bits: output.cGreenBits,
|
||||
blue_bits: output.cBlueBits,
|
||||
alpha_bits: output.cAlphaBits,
|
||||
depth_bits: output.cDepthBits,
|
||||
stencil_bits: output.cStencilBits,
|
||||
stereoscopy: (output.dwFlags & winapi::PFD_STEREO) != 0,
|
||||
double_buffer: (output.dwFlags & winapi::PFD_DOUBLEBUFFER) != 0,
|
||||
multisampling: None,
|
||||
srgb: false,
|
||||
}, index));
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
unsafe fn enumerate_arb_pixel_formats(extra: &gl::wgl_extra::Wgl, hdc: &WindowWrapper)
|
||||
-> Vec<(PixelFormat, libc::c_int)>
|
||||
{
|
||||
let get_info = |index: u32, attrib: u32| {
|
||||
let mut value = mem::uninitialized();
|
||||
extra.GetPixelFormatAttribivARB(hdc.1 as *const libc::c_void, index as libc::c_int,
|
||||
0, 1, [attrib as libc::c_int].as_ptr(),
|
||||
&mut value);
|
||||
value as u32
|
||||
};
|
||||
|
||||
// getting the number of formats
|
||||
// the `1` is ignored
|
||||
let num = get_info(1, gl::wgl_extra::NUMBER_PIXEL_FORMATS_ARB);
|
||||
|
||||
let mut result = Vec::new();
|
||||
|
||||
for index in (0 .. num) {
|
||||
if get_info(index, gl::wgl_extra::DRAW_TO_WINDOW_ARB) == 0 {
|
||||
continue;
|
||||
}
|
||||
if get_info(index, gl::wgl_extra::SUPPORT_OPENGL_ARB) == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if get_info(index, gl::wgl_extra::ACCELERATION_ARB) == gl::wgl_extra::NO_ACCELERATION_ARB {
|
||||
continue;
|
||||
}
|
||||
|
||||
if get_info(index, gl::wgl_extra::PIXEL_TYPE_ARB) != gl::wgl_extra::TYPE_RGBA_ARB {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push((PixelFormat {
|
||||
hardware_accelerated: true,
|
||||
red_bits: get_info(index, gl::wgl_extra::RED_BITS_ARB) as u8,
|
||||
green_bits: get_info(index, gl::wgl_extra::GREEN_BITS_ARB) as u8,
|
||||
blue_bits: get_info(index, gl::wgl_extra::BLUE_BITS_ARB) as u8,
|
||||
alpha_bits: get_info(index, gl::wgl_extra::ALPHA_BITS_ARB) as u8,
|
||||
depth_bits: get_info(index, gl::wgl_extra::DEPTH_BITS_ARB) as u8,
|
||||
stencil_bits: get_info(index, gl::wgl_extra::STENCIL_BITS_ARB) as u8,
|
||||
stereoscopy: get_info(index, gl::wgl_extra::STEREO_ARB) != 0,
|
||||
double_buffer: get_info(index, gl::wgl_extra::DOUBLE_BUFFER_ARB) != 0,
|
||||
multisampling: {
|
||||
if is_extension_supported(extra, hdc, "WGL_ARB_multisample") {
|
||||
match get_info(index, gl::wgl_extra::SAMPLES_ARB) {
|
||||
0 => None,
|
||||
a => Some(a as u16),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
srgb: if is_extension_supported(extra, hdc, "WGL_ARB_framebuffer_sRGB") {
|
||||
get_info(index, gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB) != 0
|
||||
} else if is_extension_supported(extra, hdc, "WGL_EXT_framebuffer_sRGB") {
|
||||
get_info(index, gl::wgl_extra::FRAMEBUFFER_SRGB_CAPABLE_EXT) != 0
|
||||
} else {
|
||||
false
|
||||
},
|
||||
}, index as libc::c_int));
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
unsafe fn set_pixel_format(hdc: &WindowWrapper, id: libc::c_int) -> Result<(), CreationError> {
|
||||
let mut output: winapi::PIXELFORMATDESCRIPTOR = mem::zeroed();
|
||||
|
||||
if gdi32::DescribePixelFormat(hdc.1, id, mem::size_of::<winapi::PIXELFORMATDESCRIPTOR>()
|
||||
as winapi::UINT, &mut output) == 0
|
||||
{
|
||||
return Err(OsError(format!("DescribePixelFormat function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
if gdi32::SetPixelFormat(hdc.1, id, &output) == 0 {
|
||||
return Err(OsError(format!("SetPixelFormat function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn load_opengl32_dll() -> Result<winapi::HMODULE, CreationError> {
|
||||
let name = OsStr::new("opengl32.dll").encode_wide().chain(Some(0).into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let lib = kernel32::LoadLibraryW(name.as_ptr());
|
||||
|
||||
if lib.is_null() {
|
||||
return Err(OsError(format!("LoadLibrary function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
Ok(lib)
|
||||
}
|
||||
|
||||
unsafe fn is_extension_supported(extra: &gl::wgl_extra::Wgl, hdc: &WindowWrapper,
|
||||
extension: &str) -> bool
|
||||
{
|
||||
let extensions = if extra.GetExtensionsStringARB.is_loaded() {
|
||||
let data = extra.GetExtensionsStringARB(hdc.1 as *const _);
|
||||
let data = CStr::from_ptr(data).to_bytes().to_vec();
|
||||
String::from_utf8(data).unwrap()
|
||||
|
||||
} else if extra.GetExtensionsStringEXT.is_loaded() {
|
||||
let data = extra.GetExtensionsStringEXT();
|
||||
let data = CStr::from_ptr(data).to_bytes().to_vec();
|
||||
String::from_utf8(data).unwrap()
|
||||
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
|
||||
extensions.split(" ").find(|&e| e == extension).is_some()
|
||||
}
|
||||
|
||||
fn choose_dummy_pixel_format<I>(iter: I) -> Result<libc::c_int, CreationError>
|
||||
where I: Iterator<Item=(PixelFormat, libc::c_int)>
|
||||
{
|
||||
let mut backup_id = None;
|
||||
|
||||
for (format, id) in iter {
|
||||
if backup_id.is_none() {
|
||||
backup_id = Some(id);
|
||||
}
|
||||
|
||||
if format.hardware_accelerated {
|
||||
return Ok(id);
|
||||
}
|
||||
}
|
||||
|
||||
backup_id.ok_or(CreationError::NotSupported)
|
||||
}
|
||||
52
src/api/win32/make_current_guard.rs
Normal file
52
src/api/win32/make_current_guard.rs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
use std::marker::PhantomData;
|
||||
use std::io;
|
||||
|
||||
use libc;
|
||||
use winapi;
|
||||
use CreationError;
|
||||
|
||||
use super::gl;
|
||||
use super::ContextWrapper;
|
||||
use super::WindowWrapper;
|
||||
|
||||
/// A guard for when you want to make the context current. Destroying the guard restores the
|
||||
/// previously-current context.
|
||||
pub struct CurrentContextGuard<'a, 'b> {
|
||||
previous_hdc: winapi::HDC,
|
||||
previous_hglrc: winapi::HGLRC,
|
||||
marker1: PhantomData<&'a ()>,
|
||||
marker2: PhantomData<&'b ()>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> CurrentContextGuard<'a, 'b> {
|
||||
pub unsafe fn make_current(window: &'a WindowWrapper, context: &'b ContextWrapper)
|
||||
-> Result<CurrentContextGuard<'a, 'b>, CreationError>
|
||||
{
|
||||
let previous_hdc = gl::wgl::GetCurrentDC() as winapi::HDC;
|
||||
let previous_hglrc = gl::wgl::GetCurrentContext() as winapi::HGLRC;
|
||||
|
||||
let result = gl::wgl::MakeCurrent(window.1 as *const libc::c_void,
|
||||
context.0 as *const libc::c_void);
|
||||
|
||||
if result == 0 {
|
||||
return Err(CreationError::OsError(format!("wglMakeCurrent function failed: {}",
|
||||
format!("{}", io::Error::last_os_error()))));
|
||||
}
|
||||
|
||||
Ok(CurrentContextGuard {
|
||||
previous_hdc: previous_hdc,
|
||||
previous_hglrc: previous_hglrc,
|
||||
marker1: PhantomData,
|
||||
marker2: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Drop for CurrentContextGuard<'a, 'b> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gl::wgl::MakeCurrent(self.previous_hdc as *const libc::c_void,
|
||||
self.previous_hglrc as *const libc::c_void);
|
||||
}
|
||||
}
|
||||
}
|
||||
419
src/api/win32/mod.rs
Normal file
419
src/api/win32/mod.rs
Normal file
|
|
@ -0,0 +1,419 @@
|
|||
#![cfg(target_os = "windows")]
|
||||
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::ffi::CString;
|
||||
use std::ffi::OsStr;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::sync::{
|
||||
Arc,
|
||||
Mutex
|
||||
};
|
||||
use std::sync::mpsc::Receiver;
|
||||
use libc;
|
||||
use {CreationError, Event, MouseCursor};
|
||||
use CursorState;
|
||||
|
||||
use PixelFormat;
|
||||
use BuilderAttribs;
|
||||
|
||||
pub use self::headless::HeadlessContext;
|
||||
pub use self::monitor::{MonitorID, get_available_monitors, get_primary_monitor};
|
||||
|
||||
use winapi;
|
||||
use user32;
|
||||
use kernel32;
|
||||
use gdi32;
|
||||
|
||||
mod callback;
|
||||
mod event;
|
||||
mod gl;
|
||||
mod headless;
|
||||
mod init;
|
||||
mod make_current_guard;
|
||||
mod monitor;
|
||||
|
||||
/// The Win32 implementation of the main `Window` object.
|
||||
pub struct Window {
|
||||
/// Main handle for the window.
|
||||
window: WindowWrapper,
|
||||
|
||||
/// OpenGL context.
|
||||
context: ContextWrapper,
|
||||
|
||||
/// Binded to `opengl32.dll`.
|
||||
///
|
||||
/// `wglGetProcAddress` returns null for GL 1.1 functions because they are
|
||||
/// already defined by the system. This module contains them.
|
||||
gl_library: winapi::HMODULE,
|
||||
|
||||
/// Receiver for the events dispatched by the window callback.
|
||||
events_receiver: Receiver<Event>,
|
||||
|
||||
/// True if a `Closed` event has been received.
|
||||
is_closed: AtomicBool,
|
||||
|
||||
/// The current cursor state.
|
||||
cursor_state: Arc<Mutex<CursorState>>,
|
||||
|
||||
/// The pixel format that has been used to create this window.
|
||||
pixel_format: PixelFormat,
|
||||
}
|
||||
|
||||
unsafe impl Send for Window {}
|
||||
unsafe impl Sync for Window {}
|
||||
|
||||
/// A simple wrapper that destroys the context when it is destroyed.
|
||||
// FIXME: remove `pub` (https://github.com/rust-lang/rust/issues/23585)
|
||||
#[doc(hidden)]
|
||||
pub struct ContextWrapper(pub winapi::HGLRC);
|
||||
|
||||
impl Drop for ContextWrapper {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gl::wgl::DeleteContext(self.0 as *const libc::c_void);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A simple wrapper that destroys the window when it is destroyed.
|
||||
// FIXME: remove `pub` (https://github.com/rust-lang/rust/issues/23585)
|
||||
#[doc(hidden)]
|
||||
pub struct WindowWrapper(pub winapi::HWND, pub winapi::HDC);
|
||||
|
||||
impl Drop for WindowWrapper {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
user32::DestroyWindow(self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WindowProxy;
|
||||
|
||||
impl WindowProxy {
|
||||
pub fn wakeup_event_loop(&self) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
/// See the docs in the crate root file.
|
||||
pub fn new(builder: BuilderAttribs) -> Result<Window, CreationError> {
|
||||
let (builder, sharing) = builder.extract_non_static();
|
||||
let sharing = sharing.map(|w| init::ContextHack(w.context.0));
|
||||
init::new_window(builder, sharing)
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn is_closed(&self) -> bool {
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
self.is_closed.load(Relaxed)
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
///
|
||||
/// Calls SetWindowText on the HWND.
|
||||
pub fn set_title(&self, text: &str) {
|
||||
let text = OsStr::new(text).encode_wide().chain(Some(0).into_iter())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
unsafe {
|
||||
user32::SetWindowTextW(self.window.0, text.as_ptr() as winapi::LPCWSTR);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn show(&self) {
|
||||
unsafe {
|
||||
user32::ShowWindow(self.window.0, winapi::SW_SHOW);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hide(&self) {
|
||||
unsafe {
|
||||
user32::ShowWindow(self.window.0, winapi::SW_HIDE);
|
||||
}
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
use std::mem;
|
||||
|
||||
let mut placement: winapi::WINDOWPLACEMENT = unsafe { mem::zeroed() };
|
||||
placement.length = mem::size_of::<winapi::WINDOWPLACEMENT>() as winapi::UINT;
|
||||
|
||||
if unsafe { user32::GetWindowPlacement(self.window.0, &mut placement) } == 0 {
|
||||
return None
|
||||
}
|
||||
|
||||
let ref rect = placement.rcNormalPosition;
|
||||
Some((rect.left as i32, rect.top as i32))
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn set_position(&self, x: i32, y: i32) {
|
||||
use libc;
|
||||
|
||||
unsafe {
|
||||
user32::SetWindowPos(self.window.0, ptr::null_mut(), x as libc::c_int, y as libc::c_int,
|
||||
0, 0, winapi::SWP_NOZORDER | winapi::SWP_NOSIZE);
|
||||
user32::UpdateWindow(self.window.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
let mut rect: winapi::RECT = unsafe { mem::uninitialized() };
|
||||
|
||||
if unsafe { user32::GetClientRect(self.window.0, &mut rect) } == 0 {
|
||||
return None
|
||||
}
|
||||
|
||||
Some((
|
||||
(rect.right - rect.left) as u32,
|
||||
(rect.bottom - rect.top) as u32
|
||||
))
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
let mut rect: winapi::RECT = unsafe { mem::uninitialized() };
|
||||
|
||||
if unsafe { user32::GetWindowRect(self.window.0, &mut rect) } == 0 {
|
||||
return None
|
||||
}
|
||||
|
||||
Some((
|
||||
(rect.right - rect.left) as u32,
|
||||
(rect.bottom - rect.top) as u32
|
||||
))
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn set_inner_size(&self, x: u32, y: u32) {
|
||||
use libc;
|
||||
|
||||
unsafe {
|
||||
user32::SetWindowPos(self.window.0, ptr::null_mut(), 0, 0, x as libc::c_int,
|
||||
y as libc::c_int, winapi::SWP_NOZORDER | winapi::SWP_NOREPOSITION);
|
||||
user32::UpdateWindow(self.window.0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_window_proxy(&self) -> WindowProxy {
|
||||
WindowProxy
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn poll_events(&self) -> PollEventsIterator {
|
||||
PollEventsIterator {
|
||||
window: self,
|
||||
}
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn wait_events(&self) -> WaitEventsIterator {
|
||||
WaitEventsIterator {
|
||||
window: self,
|
||||
}
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub unsafe fn make_current(&self) {
|
||||
// TODO: check return value
|
||||
gl::wgl::MakeCurrent(self.window.1 as *const libc::c_void,
|
||||
self.context.0 as *const libc::c_void);
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn is_current(&self) -> bool {
|
||||
unsafe { gl::wgl::GetCurrentContext() == self.context.0 as *const libc::c_void }
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn get_proc_address(&self, addr: &str) -> *const () {
|
||||
let addr = CString::new(addr.as_bytes()).unwrap();
|
||||
let addr = addr.as_ptr();
|
||||
|
||||
unsafe {
|
||||
let p = gl::wgl::GetProcAddress(addr) as *const ();
|
||||
if !p.is_null() { return p; }
|
||||
kernel32::GetProcAddress(self.gl_library, addr) as *const ()
|
||||
}
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn swap_buffers(&self) {
|
||||
unsafe {
|
||||
gdi32::SwapBuffers(self.window.1);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
||||
self.window.0 as *mut libc::c_void
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn get_api(&self) -> ::Api {
|
||||
::Api::OpenGl
|
||||
}
|
||||
|
||||
pub fn get_pixel_format(&self) -> PixelFormat {
|
||||
self.pixel_format.clone()
|
||||
}
|
||||
|
||||
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
|
||||
}
|
||||
|
||||
pub fn set_cursor(&self, _cursor: MouseCursor) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
let mut current_state = self.cursor_state.lock().unwrap();
|
||||
|
||||
let foreground_thread_id = unsafe { user32::GetWindowThreadProcessId(self.window.0, ptr::null_mut()) };
|
||||
let current_thread_id = unsafe { kernel32::GetCurrentThreadId() };
|
||||
|
||||
unsafe { user32::AttachThreadInput(foreground_thread_id, current_thread_id, 1) };
|
||||
|
||||
let res = match (state, *current_state) {
|
||||
(CursorState::Normal, CursorState::Normal) => Ok(()),
|
||||
(CursorState::Hide, CursorState::Hide) => Ok(()),
|
||||
(CursorState::Grab, CursorState::Grab) => Ok(()),
|
||||
|
||||
(CursorState::Hide, CursorState::Normal) => {
|
||||
unsafe {
|
||||
user32::SetCursor(ptr::null_mut());
|
||||
*current_state = CursorState::Hide;
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
|
||||
(CursorState::Normal, CursorState::Hide) => {
|
||||
unsafe {
|
||||
user32::SetCursor(user32::LoadCursorW(ptr::null_mut(), winapi::IDC_ARROW));
|
||||
*current_state = CursorState::Normal;
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
|
||||
(CursorState::Grab, CursorState::Normal) => {
|
||||
unsafe {
|
||||
user32::SetCursor(ptr::null_mut());
|
||||
let mut rect = mem::uninitialized();
|
||||
if user32::GetClientRect(self.window.0, &mut rect) == 0 {
|
||||
return Err(format!("GetWindowRect failed"));
|
||||
}
|
||||
user32::ClientToScreen(self.window.0, mem::transmute(&mut rect.left));
|
||||
user32::ClientToScreen(self.window.0, mem::transmute(&mut rect.right));
|
||||
if user32::ClipCursor(&rect) == 0 {
|
||||
return Err(format!("ClipCursor failed"));
|
||||
}
|
||||
*current_state = CursorState::Grab;
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
|
||||
(CursorState::Normal, CursorState::Grab) => {
|
||||
unsafe {
|
||||
user32::SetCursor(user32::LoadCursorW(ptr::null_mut(), winapi::IDC_ARROW));
|
||||
if user32::ClipCursor(ptr::null()) == 0 {
|
||||
return Err(format!("ClipCursor failed"));
|
||||
}
|
||||
*current_state = CursorState::Normal;
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
|
||||
unsafe { user32::AttachThreadInput(foreground_thread_id, current_thread_id, 0) };
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
1.0
|
||||
}
|
||||
|
||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
||||
let mut point = winapi::POINT {
|
||||
x: x,
|
||||
y: y,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
if user32::ClientToScreen(self.window.0, &mut point) == 0 {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
if user32::SetCursorPos(point.x, point.y) == 0 {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
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.try_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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// we don't call MakeCurrent(0, 0) because we are not sure that the context
|
||||
// is still the current one
|
||||
user32::PostMessageW(self.window.0, winapi::WM_DESTROY, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
180
src/api/win32/monitor.rs
Normal file
180
src/api/win32/monitor.rs
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
use winapi;
|
||||
use user32;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::mem;
|
||||
|
||||
use native_monitor::NativeMonitorId;
|
||||
|
||||
/// Win32 implementation of the main `MonitorID` object.
|
||||
pub struct MonitorID {
|
||||
/// The system name of the adapter.
|
||||
adapter_name: [winapi::WCHAR; 32],
|
||||
|
||||
/// The system name of the monitor.
|
||||
monitor_name: String,
|
||||
|
||||
/// Name to give to the user.
|
||||
readable_name: String,
|
||||
|
||||
/// See the `StateFlags` element here:
|
||||
/// http://msdn.microsoft.com/en-us/library/dd183569(v=vs.85).aspx
|
||||
flags: winapi::DWORD,
|
||||
|
||||
/// True if this is the primary monitor.
|
||||
primary: bool,
|
||||
|
||||
/// The position of the monitor in pixels on the desktop.
|
||||
///
|
||||
/// A window that is positionned at these coordinates will overlap the monitor.
|
||||
position: (u32, u32),
|
||||
|
||||
/// The current resolution in pixels on the monitor.
|
||||
dimensions: (u32, u32),
|
||||
}
|
||||
|
||||
struct DeviceEnumerator {
|
||||
parent_device: *const winapi::WCHAR,
|
||||
current_index: u32,
|
||||
}
|
||||
|
||||
impl DeviceEnumerator {
|
||||
fn adapters() -> DeviceEnumerator {
|
||||
use std::ptr;
|
||||
DeviceEnumerator {
|
||||
parent_device: ptr::null(),
|
||||
current_index: 0
|
||||
}
|
||||
}
|
||||
|
||||
fn monitors(adapter_name: *const winapi::WCHAR) -> DeviceEnumerator {
|
||||
DeviceEnumerator {
|
||||
parent_device: adapter_name,
|
||||
current_index: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for DeviceEnumerator {
|
||||
type Item = winapi::DISPLAY_DEVICEW;
|
||||
fn next(&mut self) -> Option<winapi::DISPLAY_DEVICEW> {
|
||||
use std::mem;
|
||||
loop {
|
||||
let mut output: winapi::DISPLAY_DEVICEW = unsafe { mem::zeroed() };
|
||||
output.cb = mem::size_of::<winapi::DISPLAY_DEVICEW>() as winapi::DWORD;
|
||||
|
||||
if unsafe { user32::EnumDisplayDevicesW(self.parent_device,
|
||||
self.current_index as winapi::DWORD, &mut output, 0) } == 0
|
||||
{
|
||||
// the device doesn't exist, which means we have finished enumerating
|
||||
break;
|
||||
}
|
||||
self.current_index += 1;
|
||||
|
||||
if (output.StateFlags & winapi::DISPLAY_DEVICE_ACTIVE) == 0 ||
|
||||
(output.StateFlags & winapi::DISPLAY_DEVICE_MIRRORING_DRIVER) != 0
|
||||
{
|
||||
// the device is not active
|
||||
// the Win32 api usually returns a lot of inactive devices
|
||||
continue;
|
||||
}
|
||||
|
||||
return Some(output);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn wchar_as_string(wchar: &[winapi::WCHAR]) -> String {
|
||||
String::from_utf16_lossy(wchar)
|
||||
.trim_right_matches(0 as char)
|
||||
.to_string()
|
||||
}
|
||||
|
||||
/// Win32 implementation of the main `get_available_monitors` function.
|
||||
pub fn get_available_monitors() -> VecDeque<MonitorID> {
|
||||
// return value
|
||||
let mut result = VecDeque::new();
|
||||
|
||||
for adapter in DeviceEnumerator::adapters() {
|
||||
// getting the position
|
||||
let (position, dimensions) = unsafe {
|
||||
let mut dev: winapi::DEVMODEW = mem::zeroed();
|
||||
dev.dmSize = mem::size_of::<winapi::DEVMODEW>() as winapi::WORD;
|
||||
|
||||
if user32::EnumDisplaySettingsExW(adapter.DeviceName.as_ptr(),
|
||||
winapi::ENUM_CURRENT_SETTINGS,
|
||||
&mut dev, 0) == 0
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let point: &winapi::POINTL = mem::transmute(&dev.union1);
|
||||
let position = (point.x as u32, point.y as u32);
|
||||
|
||||
let dimensions = (dev.dmPelsWidth as u32, dev.dmPelsHeight as u32);
|
||||
|
||||
(position, dimensions)
|
||||
};
|
||||
|
||||
for (num, monitor) in DeviceEnumerator::monitors(adapter.DeviceName.as_ptr()).enumerate() {
|
||||
// adding to the resulting list
|
||||
result.push_back(MonitorID {
|
||||
adapter_name: adapter.DeviceName,
|
||||
monitor_name: wchar_as_string(&monitor.DeviceName),
|
||||
readable_name: wchar_as_string(&monitor.DeviceString),
|
||||
flags: monitor.StateFlags,
|
||||
primary: (adapter.StateFlags & winapi::DISPLAY_DEVICE_PRIMARY_DEVICE) != 0 &&
|
||||
num == 0,
|
||||
position: position,
|
||||
dimensions: dimensions,
|
||||
});
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Win32 implementation of the main `get_primary_monitor` function.
|
||||
pub fn get_primary_monitor() -> MonitorID {
|
||||
// we simply get all available monitors and return the one with the `PRIMARY_DEVICE` flag
|
||||
// TODO: it is possible to query the win32 API for the primary monitor, this should be done
|
||||
// instead
|
||||
for monitor in get_available_monitors().into_iter() {
|
||||
if monitor.primary {
|
||||
return monitor;
|
||||
}
|
||||
}
|
||||
|
||||
panic!("Failed to find the primary monitor")
|
||||
}
|
||||
|
||||
impl MonitorID {
|
||||
/// See the docs if the crate root file.
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
Some(self.readable_name.clone())
|
||||
}
|
||||
|
||||
/// See the docs of the crate root file.
|
||||
pub fn get_native_identifier(&self) -> NativeMonitorId {
|
||||
NativeMonitorId::Name(self.monitor_name.clone())
|
||||
}
|
||||
|
||||
/// See the docs if the crate root file.
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
// TODO: retreive the dimensions every time this is called
|
||||
self.dimensions
|
||||
}
|
||||
|
||||
/// This is a Win32-only function for `MonitorID` that returns the system name of the adapter
|
||||
/// device.
|
||||
pub fn get_adapter_name(&self) -> &[winapi::WCHAR] {
|
||||
&self.adapter_name
|
||||
}
|
||||
|
||||
/// This is a Win32-only function for `MonitorID` that returns the position of the
|
||||
/// monitor on the desktop.
|
||||
/// A window that is positionned at these coordinates will overlap the monitor.
|
||||
pub fn get_position(&self) -> (u32, u32) {
|
||||
self.position
|
||||
}
|
||||
}
|
||||
19
src/api/x11/ffi.rs
Normal file
19
src/api/x11/ffi.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#[cfg(feature="headless")]
|
||||
pub use osmesa_sys::*;
|
||||
pub use x11::keysym::*;
|
||||
pub use x11::xcursor::*;
|
||||
pub use x11::xf86vmode::*;
|
||||
pub use x11::xlib::*;
|
||||
pub use x11::xlib::xkb::*;
|
||||
|
||||
pub use self::glx::types::GLXContext;
|
||||
|
||||
/// GLX bindings
|
||||
pub mod glx {
|
||||
include!(concat!(env!("OUT_DIR"), "/glx_bindings.rs"));
|
||||
}
|
||||
|
||||
/// Functions that are not necessarly always available
|
||||
pub mod glx_extra {
|
||||
include!(concat!(env!("OUT_DIR"), "/glx_extra_bindings.rs"));
|
||||
}
|
||||
72
src/api/x11/headless.rs
Normal file
72
src/api/x11/headless.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
use BuilderAttribs;
|
||||
use CreationError;
|
||||
use CreationError::OsError;
|
||||
use libc;
|
||||
use std::{mem, ptr};
|
||||
use super::ffi;
|
||||
|
||||
pub struct HeadlessContext {
|
||||
context: ffi::OSMesaContext,
|
||||
buffer: Vec<u32>,
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
impl HeadlessContext {
|
||||
pub fn new(builder: BuilderAttribs) -> Result<HeadlessContext, CreationError> {
|
||||
let dimensions = builder.dimensions.unwrap();
|
||||
|
||||
Ok(HeadlessContext {
|
||||
width: dimensions.0,
|
||||
height: dimensions.1,
|
||||
buffer: ::std::iter::repeat(unsafe { mem::uninitialized() })
|
||||
.take((dimensions.0 * dimensions.1) as usize).collect(),
|
||||
context: unsafe {
|
||||
let ctxt = ffi::OSMesaCreateContext(0x1908, ptr::null_mut());
|
||||
if ctxt.is_null() {
|
||||
return Err(OsError("OSMesaCreateContext failed".to_string()));
|
||||
}
|
||||
ctxt
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn make_current(&self) {
|
||||
let ret = ffi::OSMesaMakeCurrent(self.context,
|
||||
self.buffer.as_ptr() as *mut libc::c_void,
|
||||
0x1401, self.width as libc::c_int, self.height as libc::c_int);
|
||||
|
||||
if ret == 0 {
|
||||
panic!("OSMesaMakeCurrent failed")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_current(&self) -> bool {
|
||||
unsafe { ffi::OSMesaGetCurrentContext() == self.context }
|
||||
}
|
||||
|
||||
pub fn get_proc_address(&self, addr: &str) -> *const () {
|
||||
unsafe {
|
||||
use std::ffi::CString;
|
||||
let c_str = CString::new(addr.as_bytes().to_vec()).unwrap();
|
||||
mem::transmute(ffi::OSMesaGetProcAddress(mem::transmute(c_str.as_ptr())))
|
||||
}
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn get_api(&self) -> ::Api {
|
||||
::Api::OpenGl
|
||||
}
|
||||
|
||||
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for HeadlessContext {
|
||||
fn drop(&mut self) {
|
||||
unsafe { ffi::OSMesaDestroyContext(self.context) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for HeadlessContext {}
|
||||
unsafe impl Sync for HeadlessContext {}
|
||||
22
src/api/x11/mod.rs
Normal file
22
src/api/x11/mod.rs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#![cfg(target_os = "linux")]
|
||||
|
||||
#[cfg(feature = "headless")]
|
||||
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;
|
||||
|
||||
#[cfg(feature = "headless")]
|
||||
mod headless;
|
||||
|
||||
#[cfg(feature = "window")]
|
||||
mod window;
|
||||
|
||||
#[cfg(not(feature = "window"))]
|
||||
pub type Window = (); // TODO: hack to make things work
|
||||
#[cfg(not(feature = "window"))]
|
||||
pub type MonitorID = (); // TODO: hack to make things work
|
||||
1002
src/api/x11/window/events.rs
Normal file
1002
src/api/x11/window/events.rs
Normal file
File diff suppressed because it is too large
Load diff
885
src/api/x11/window/mod.rs
Normal file
885
src/api/x11/window/mod.rs
Normal file
|
|
@ -0,0 +1,885 @@
|
|||
use {Event, BuilderAttribs, MouseCursor};
|
||||
use CreationError;
|
||||
use CreationError::OsError;
|
||||
use libc;
|
||||
use std::{mem, ptr};
|
||||
use std::cell::Cell;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::collections::VecDeque;
|
||||
use super::ffi;
|
||||
use std::sync::{Arc, Mutex, Once, ONCE_INIT};
|
||||
|
||||
use Api;
|
||||
use CursorState;
|
||||
use GlRequest;
|
||||
use PixelFormat;
|
||||
|
||||
pub use self::monitor::{MonitorID, get_available_monitors, get_primary_monitor};
|
||||
|
||||
mod events;
|
||||
mod monitor;
|
||||
|
||||
static THREAD_INIT: Once = ONCE_INIT;
|
||||
|
||||
// XOpenIM doesn't seem to be thread-safe
|
||||
lazy_static! { // TODO: use a static mutex when that's possible, and put me back in my function
|
||||
static ref GLOBAL_XOPENIM_LOCK: Mutex<()> = Mutex::new(());
|
||||
}
|
||||
|
||||
unsafe extern "C" fn x_error_callback(_: *mut ffi::Display, event: *mut ffi::XErrorEvent) -> libc::c_int {
|
||||
println!("[glutin] x error code={} major={} minor={}!", (*event).error_code, (*event).request_code, (*event).minor_code);
|
||||
0
|
||||
}
|
||||
|
||||
fn ensure_thread_init() {
|
||||
THREAD_INIT.call_once(|| {
|
||||
unsafe {
|
||||
ffi::XInitThreads();
|
||||
ffi::XSetErrorHandler(Some(x_error_callback));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn with_c_str<F, T>(s: &str, f: F) -> T where F: FnOnce(*const libc::c_char) -> T {
|
||||
use std::ffi::CString;
|
||||
let c_str = CString::new(s.as_bytes().to_vec()).unwrap();
|
||||
f(c_str.as_ptr())
|
||||
}
|
||||
|
||||
struct XWindow {
|
||||
display: *mut ffi::Display,
|
||||
window: ffi::Window,
|
||||
context: ffi::GLXContext,
|
||||
is_fullscreen: bool,
|
||||
screen_id: libc::c_int,
|
||||
xf86_desk_mode: *mut ffi::XF86VidModeModeInfo,
|
||||
ic: ffi::XIC,
|
||||
im: ffi::XIM,
|
||||
}
|
||||
|
||||
unsafe impl Send for XWindow {}
|
||||
unsafe impl Sync for XWindow {}
|
||||
|
||||
unsafe impl Send for Window {}
|
||||
unsafe impl Sync for Window {}
|
||||
|
||||
impl Drop for XWindow {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// we don't call MakeCurrent(0, 0) because we are not sure that the context
|
||||
// is still the current one
|
||||
ffi::glx::DestroyContext(self.display as *mut _, self.context);
|
||||
|
||||
if self.is_fullscreen {
|
||||
ffi::XF86VidModeSwitchToMode(self.display, self.screen_id, self.xf86_desk_mode);
|
||||
ffi::XF86VidModeSetViewPort(self.display, self.screen_id, 0, 0);
|
||||
}
|
||||
|
||||
ffi::XDestroyIC(self.ic);
|
||||
ffi::XCloseIM(self.im);
|
||||
ffi::XDestroyWindow(self.display, self.window);
|
||||
ffi::XCloseDisplay(self.display);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WindowProxy {
|
||||
x: Arc<XWindow>,
|
||||
}
|
||||
|
||||
impl WindowProxy {
|
||||
pub fn wakeup_event_loop(&self) {
|
||||
let mut xev = ffi::XClientMessageEvent {
|
||||
type_: ffi::ClientMessage,
|
||||
window: self.x.window,
|
||||
format: 32,
|
||||
message_type: 0,
|
||||
serial: 0,
|
||||
send_event: 0,
|
||||
display: self.x.display,
|
||||
data: unsafe { mem::zeroed() },
|
||||
};
|
||||
|
||||
unsafe {
|
||||
ffi::XSendEvent(self.x.display, self.x.window, 0, 0, mem::transmute(&mut xev));
|
||||
ffi::XFlush(self.x.display);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
loop {
|
||||
let mut xev = unsafe { mem::uninitialized() };
|
||||
let res = unsafe { ffi::XCheckMaskEvent(self.window.x.display, -1, &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.get_type() {
|
||||
ffi::KeymapNotify => {
|
||||
unsafe { ffi::XRefreshKeyboardMapping(mem::transmute(&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.data.get_long(0) == self.window.wm_delete_window as libc::c_long {
|
||||
self.window.is_closed.store(true, Relaxed);
|
||||
return Some(Closed);
|
||||
} else {
|
||||
return Some(Awakened);
|
||||
}
|
||||
},
|
||||
|
||||
ffi::ConfigureNotify => {
|
||||
use events::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::Expose => {
|
||||
use events::Event::Refresh;
|
||||
return Some(Refresh);
|
||||
},
|
||||
|
||||
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.get_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[..count as usize]).unwrap_or("").to_string()
|
||||
};
|
||||
|
||||
{
|
||||
let mut pending = self.window.pending_events.lock().unwrap();
|
||||
for chr in written.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.get_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 => ()
|
||||
};
|
||||
},
|
||||
|
||||
_ => ()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
while !self.window.is_closed() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Window {
|
||||
x: Arc<XWindow>,
|
||||
is_closed: AtomicBool,
|
||||
wm_delete_window: ffi::Atom,
|
||||
current_size: Cell<(libc::c_int, libc::c_int)>,
|
||||
pixel_format: PixelFormat,
|
||||
/// Events that have been retreived with XLib but not dispatched with iterators yet
|
||||
pending_events: Mutex<VecDeque<Event>>,
|
||||
cursor_state: Mutex<CursorState>,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(builder: BuilderAttribs) -> Result<Window, CreationError> {
|
||||
ensure_thread_init();
|
||||
let dimensions = builder.dimensions.unwrap_or((800, 600));
|
||||
|
||||
// calling XOpenDisplay
|
||||
let display = unsafe {
|
||||
let display = ffi::XOpenDisplay(ptr::null());
|
||||
if display.is_null() {
|
||||
return Err(OsError(format!("XOpenDisplay failed")));
|
||||
}
|
||||
display
|
||||
};
|
||||
|
||||
let screen_id = match builder.monitor {
|
||||
Some(MonitorID(monitor)) => monitor as i32,
|
||||
None => unsafe { ffi::XDefaultScreen(display) },
|
||||
};
|
||||
|
||||
// getting the FBConfig
|
||||
let fb_config = unsafe {
|
||||
let mut visual_attributes = vec![
|
||||
ffi::glx::X_RENDERABLE as libc::c_int, 1,
|
||||
ffi::glx::DRAWABLE_TYPE as libc::c_int, ffi::glx::WINDOW_BIT as libc::c_int,
|
||||
ffi::glx::RENDER_TYPE as libc::c_int, ffi::glx::RGBA_BIT as libc::c_int,
|
||||
ffi::glx::X_VISUAL_TYPE as libc::c_int, ffi::glx::TRUE_COLOR as libc::c_int,
|
||||
ffi::glx::RED_SIZE as libc::c_int, 8,
|
||||
ffi::glx::GREEN_SIZE as libc::c_int, 8,
|
||||
ffi::glx::BLUE_SIZE as libc::c_int, 8,
|
||||
ffi::glx::ALPHA_SIZE as libc::c_int, 8,
|
||||
ffi::glx::DEPTH_SIZE as libc::c_int, 24,
|
||||
ffi::glx::STENCIL_SIZE as libc::c_int, 8,
|
||||
ffi::glx::DOUBLEBUFFER as libc::c_int, 1,
|
||||
];
|
||||
|
||||
if let Some(val) = builder.multisampling {
|
||||
visual_attributes.push(ffi::glx::SAMPLE_BUFFERS as libc::c_int);
|
||||
visual_attributes.push(1);
|
||||
visual_attributes.push(ffi::glx::SAMPLES as libc::c_int);
|
||||
visual_attributes.push(val as libc::c_int);
|
||||
}
|
||||
|
||||
if let Some(val) = builder.srgb {
|
||||
visual_attributes.push(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as libc::c_int);
|
||||
visual_attributes.push(if val {1} else {0});
|
||||
}
|
||||
|
||||
visual_attributes.push(0);
|
||||
|
||||
let mut num_fb: libc::c_int = mem::uninitialized();
|
||||
|
||||
let fb = ffi::glx::ChooseFBConfig(display as *mut _, ffi::XDefaultScreen(display),
|
||||
visual_attributes.as_ptr(), &mut num_fb);
|
||||
if fb.is_null() {
|
||||
return Err(OsError(format!("glx::ChooseFBConfig failed")));
|
||||
}
|
||||
let preferred_fb = *fb; // TODO: choose more wisely
|
||||
ffi::XFree(fb as *mut _);
|
||||
preferred_fb
|
||||
};
|
||||
|
||||
let mut best_mode = -1;
|
||||
let modes = unsafe {
|
||||
let mut mode_num: libc::c_int = mem::uninitialized();
|
||||
let mut modes: *mut *mut ffi::XF86VidModeModeInfo = mem::uninitialized();
|
||||
if ffi::XF86VidModeGetAllModeLines(display, screen_id, &mut mode_num, &mut modes) == 0 {
|
||||
return Err(OsError(format!("Could not query the video modes")));
|
||||
}
|
||||
|
||||
for i in 0..mode_num {
|
||||
let mode: ffi::XF86VidModeModeInfo = ptr::read(*modes.offset(i as isize) as *const _);
|
||||
if mode.hdisplay == dimensions.0 as u16 && mode.vdisplay == dimensions.1 as u16 {
|
||||
best_mode = i;
|
||||
}
|
||||
};
|
||||
if best_mode == -1 && builder.monitor.is_some() {
|
||||
return Err(OsError(format!("Could not find a suitable graphics mode")));
|
||||
}
|
||||
|
||||
modes
|
||||
};
|
||||
|
||||
let xf86_desk_mode = unsafe {
|
||||
*modes.offset(0)
|
||||
};
|
||||
|
||||
// getting the visual infos
|
||||
let mut visual_infos: ffi::glx::types::XVisualInfo = unsafe {
|
||||
let vi = ffi::glx::GetVisualFromFBConfig(display as *mut _, fb_config);
|
||||
if vi.is_null() {
|
||||
return Err(OsError(format!("glx::ChooseVisual failed")));
|
||||
}
|
||||
let vi_copy = ptr::read(vi as *const _);
|
||||
ffi::XFree(vi as *mut _);
|
||||
vi_copy
|
||||
};
|
||||
|
||||
// querying the chosen pixel format
|
||||
let pixel_format = {
|
||||
let get_attrib = |attrib: libc::c_int| -> i32 {
|
||||
let mut value = 0;
|
||||
unsafe { ffi::glx::GetFBConfigAttrib(display as *mut _, fb_config, attrib, &mut value); }
|
||||
value
|
||||
};
|
||||
|
||||
PixelFormat {
|
||||
hardware_accelerated: true,
|
||||
red_bits: get_attrib(ffi::glx::RED_SIZE as libc::c_int) as u8,
|
||||
green_bits: get_attrib(ffi::glx::GREEN_SIZE as libc::c_int) as u8,
|
||||
blue_bits: get_attrib(ffi::glx::BLUE_SIZE as libc::c_int) as u8,
|
||||
alpha_bits: get_attrib(ffi::glx::ALPHA_SIZE as libc::c_int) as u8,
|
||||
depth_bits: get_attrib(ffi::glx::DEPTH_SIZE as libc::c_int) as u8,
|
||||
stencil_bits: get_attrib(ffi::glx::STENCIL_SIZE as libc::c_int) as u8,
|
||||
stereoscopy: get_attrib(ffi::glx::STEREO as libc::c_int) != 0,
|
||||
double_buffer: get_attrib(ffi::glx::DOUBLEBUFFER as libc::c_int) != 0,
|
||||
multisampling: if get_attrib(ffi::glx::SAMPLE_BUFFERS as libc::c_int) != 0 {
|
||||
Some(get_attrib(ffi::glx::SAMPLES as libc::c_int) as u16)
|
||||
}else { None },
|
||||
srgb: get_attrib(ffi::glx_extra::FRAMEBUFFER_SRGB_CAPABLE_ARB as libc::c_int) != 0,
|
||||
}
|
||||
};
|
||||
|
||||
// getting the root window
|
||||
let root = unsafe { ffi::XDefaultRootWindow(display) };
|
||||
|
||||
// creating the color map
|
||||
let cmap = unsafe {
|
||||
let cmap = ffi::XCreateColormap(display, root,
|
||||
visual_infos.visual as *mut _, ffi::AllocNone);
|
||||
// TODO: error checking?
|
||||
cmap
|
||||
};
|
||||
|
||||
// creating
|
||||
let mut set_win_attr = {
|
||||
let mut swa: ffi::XSetWindowAttributes = unsafe { mem::zeroed() };
|
||||
swa.colormap = cmap;
|
||||
swa.event_mask = ffi::ExposureMask | ffi::StructureNotifyMask |
|
||||
ffi::VisibilityChangeMask | ffi::KeyPressMask | ffi::PointerMotionMask |
|
||||
ffi::KeyReleaseMask | ffi::ButtonPressMask |
|
||||
ffi::ButtonReleaseMask | ffi::KeymapStateMask;
|
||||
swa.border_pixel = 0;
|
||||
swa.override_redirect = 0;
|
||||
swa
|
||||
};
|
||||
|
||||
let mut window_attributes = ffi::CWBorderPixel | ffi::CWColormap | ffi:: CWEventMask;
|
||||
if builder.monitor.is_some() {
|
||||
window_attributes |= ffi::CWOverrideRedirect;
|
||||
unsafe {
|
||||
ffi::XF86VidModeSwitchToMode(display, screen_id, *modes.offset(best_mode as isize));
|
||||
ffi::XF86VidModeSetViewPort(display, screen_id, 0, 0);
|
||||
set_win_attr.override_redirect = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// finally creating the window
|
||||
let window = unsafe {
|
||||
let win = ffi::XCreateWindow(display, root, 0, 0, dimensions.0 as libc::c_uint,
|
||||
dimensions.1 as libc::c_uint, 0, visual_infos.depth, ffi::InputOutput as libc::c_uint,
|
||||
visual_infos.visual as *mut _, window_attributes,
|
||||
&mut set_win_attr);
|
||||
win
|
||||
};
|
||||
|
||||
// set visibility
|
||||
if builder.visible {
|
||||
unsafe {
|
||||
ffi::XMapRaised(display, window);
|
||||
ffi::XFlush(display);
|
||||
}
|
||||
}
|
||||
|
||||
// creating window, step 2
|
||||
let wm_delete_window = unsafe {
|
||||
let mut wm_delete_window = with_c_str("WM_DELETE_WINDOW", |delete_window|
|
||||
ffi::XInternAtom(display, delete_window, 0)
|
||||
);
|
||||
ffi::XSetWMProtocols(display, window, &mut wm_delete_window, 1);
|
||||
with_c_str(&*builder.title, |title| {;
|
||||
ffi::XStoreName(display, window, title);
|
||||
});
|
||||
ffi::XFlush(display);
|
||||
|
||||
wm_delete_window
|
||||
};
|
||||
|
||||
// creating IM
|
||||
let im = unsafe {
|
||||
let _lock = GLOBAL_XOPENIM_LOCK.lock().unwrap();
|
||||
|
||||
let im = ffi::XOpenIM(display, ptr::null_mut(), ptr::null_mut(), ptr::null_mut());
|
||||
if im.is_null() {
|
||||
return Err(OsError(format!("XOpenIM failed")));
|
||||
}
|
||||
im
|
||||
};
|
||||
|
||||
// creating input context
|
||||
let ic = unsafe {
|
||||
let ic = with_c_str("inputStyle", |input_style|
|
||||
with_c_str("clientWindow", |client_window|
|
||||
ffi::XCreateIC(
|
||||
im, input_style,
|
||||
ffi::XIMPreeditNothing | ffi::XIMStatusNothing, client_window,
|
||||
window, ptr::null::<()>()
|
||||
)
|
||||
)
|
||||
);
|
||||
if ic.is_null() {
|
||||
return Err(OsError(format!("XCreateIC failed")));
|
||||
}
|
||||
ffi::XSetICFocus(ic);
|
||||
ic
|
||||
};
|
||||
|
||||
// Attempt to make keyboard input repeat detectable
|
||||
unsafe {
|
||||
let mut supported_ptr = ffi::False;
|
||||
ffi::XkbSetDetectableAutoRepeat(display, ffi::True, &mut supported_ptr);
|
||||
if supported_ptr == ffi::False {
|
||||
return Err(OsError(format!("XkbSetDetectableAutoRepeat failed")));
|
||||
}
|
||||
}
|
||||
|
||||
// Set ICCCM WM_CLASS property based on initial window title
|
||||
unsafe {
|
||||
with_c_str(&*builder.title, |c_name| {
|
||||
let hint = ffi::XAllocClassHint();
|
||||
(*hint).res_name = c_name as *mut i8;
|
||||
(*hint).res_class = c_name as *mut i8;
|
||||
ffi::XSetClassHint(display, window, hint);
|
||||
ffi::XFree(hint as *mut libc::c_void);
|
||||
});
|
||||
}
|
||||
|
||||
// creating GL context
|
||||
let (context, extra_functions) = unsafe {
|
||||
let mut attributes = Vec::new();
|
||||
|
||||
match builder.gl_version {
|
||||
GlRequest::Latest => {},
|
||||
GlRequest::Specific(Api::OpenGl, (major, minor)) => {
|
||||
attributes.push(ffi::glx_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int);
|
||||
attributes.push(major as libc::c_int);
|
||||
attributes.push(ffi::glx_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int);
|
||||
attributes.push(minor as libc::c_int);
|
||||
},
|
||||
GlRequest::Specific(_, _) => panic!("Only OpenGL is supported"),
|
||||
GlRequest::GlThenGles { opengl_version: (major, minor), .. } => {
|
||||
attributes.push(ffi::glx_extra::CONTEXT_MAJOR_VERSION_ARB as libc::c_int);
|
||||
attributes.push(major as libc::c_int);
|
||||
attributes.push(ffi::glx_extra::CONTEXT_MINOR_VERSION_ARB as libc::c_int);
|
||||
attributes.push(minor as libc::c_int);
|
||||
},
|
||||
}
|
||||
|
||||
if builder.gl_debug {
|
||||
attributes.push(ffi::glx_extra::CONTEXT_FLAGS_ARB as libc::c_int);
|
||||
attributes.push(ffi::glx_extra::CONTEXT_DEBUG_BIT_ARB as libc::c_int);
|
||||
}
|
||||
|
||||
attributes.push(0);
|
||||
|
||||
// loading the extra GLX functions
|
||||
let extra_functions = ffi::glx_extra::Glx::load_with(|addr| {
|
||||
with_c_str(addr, |s| {
|
||||
use libc;
|
||||
ffi::glx::GetProcAddress(s as *const u8) as *const libc::c_void
|
||||
})
|
||||
});
|
||||
|
||||
let share = if let Some(win) = builder.sharing {
|
||||
win.x.context
|
||||
} else {
|
||||
ptr::null()
|
||||
};
|
||||
|
||||
let mut context = if extra_functions.CreateContextAttribsARB.is_loaded() {
|
||||
extra_functions.CreateContextAttribsARB(display as *mut ffi::glx_extra::types::Display,
|
||||
fb_config, share, 1, attributes.as_ptr())
|
||||
} else {
|
||||
ptr::null()
|
||||
};
|
||||
|
||||
if context.is_null() {
|
||||
context = ffi::glx::CreateContext(display as *mut _, &mut visual_infos, share, 1)
|
||||
}
|
||||
|
||||
if context.is_null() {
|
||||
return Err(OsError(format!("GL context creation failed")));
|
||||
}
|
||||
|
||||
(context, extra_functions)
|
||||
};
|
||||
|
||||
// vsync
|
||||
if builder.vsync {
|
||||
unsafe { ffi::glx::MakeCurrent(display as *mut _, window, context) };
|
||||
|
||||
if extra_functions.SwapIntervalEXT.is_loaded() {
|
||||
// this should be the most common extension
|
||||
unsafe {
|
||||
extra_functions.SwapIntervalEXT(display as *mut _, window, 1);
|
||||
}
|
||||
|
||||
// checking that it worked
|
||||
if builder.strict {
|
||||
let mut swap = unsafe { mem::uninitialized() };
|
||||
unsafe {
|
||||
ffi::glx::QueryDrawable(display as *mut _, window,
|
||||
ffi::glx_extra::SWAP_INTERVAL_EXT as i32,
|
||||
&mut swap);
|
||||
}
|
||||
|
||||
if swap != 1 {
|
||||
return Err(OsError(format!("Couldn't setup vsync: expected \
|
||||
interval `1` but got `{}`", swap)));
|
||||
}
|
||||
}
|
||||
|
||||
// GLX_MESA_swap_control is not official
|
||||
/*} else if extra_functions.SwapIntervalMESA.is_loaded() {
|
||||
unsafe {
|
||||
extra_functions.SwapIntervalMESA(1);
|
||||
}*/
|
||||
|
||||
} else if extra_functions.SwapIntervalSGI.is_loaded() {
|
||||
unsafe {
|
||||
extra_functions.SwapIntervalSGI(1);
|
||||
}
|
||||
|
||||
} else if builder.strict {
|
||||
return Err(OsError(format!("Couldn't find any available vsync extension")));
|
||||
}
|
||||
|
||||
unsafe { ffi::glx::MakeCurrent(display as *mut _, 0, ptr::null()) };
|
||||
}
|
||||
|
||||
// creating the window object
|
||||
let window = Window {
|
||||
x: Arc::new(XWindow {
|
||||
display: display,
|
||||
window: window,
|
||||
im: im,
|
||||
ic: ic,
|
||||
context: context,
|
||||
screen_id: screen_id,
|
||||
is_fullscreen: builder.monitor.is_some(),
|
||||
xf86_desk_mode: xf86_desk_mode,
|
||||
}),
|
||||
is_closed: AtomicBool::new(false),
|
||||
wm_delete_window: wm_delete_window,
|
||||
current_size: Cell::new((0, 0)),
|
||||
pixel_format: pixel_format,
|
||||
pending_events: Mutex::new(VecDeque::new()),
|
||||
cursor_state: Mutex::new(CursorState::Normal),
|
||||
};
|
||||
|
||||
// returning
|
||||
Ok(window)
|
||||
}
|
||||
|
||||
pub fn is_closed(&self) -> bool {
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
self.is_closed.load(Relaxed)
|
||||
}
|
||||
|
||||
pub fn set_title(&self, title: &str) {
|
||||
with_c_str(title, |title| unsafe {
|
||||
ffi::XStoreName(self.x.display, self.x.window, title);
|
||||
ffi::XFlush(self.x.display);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn show(&self) {
|
||||
unsafe {
|
||||
ffi::XMapRaised(self.x.display, self.x.window);
|
||||
ffi::XFlush(self.x.display);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hide(&self) {
|
||||
unsafe {
|
||||
ffi::XUnmapWindow(self.x.display, self.x.window);
|
||||
ffi::XFlush(self.x.display);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_geometry(&self) -> Option<(i32, i32, u32, u32, u32)> {
|
||||
unsafe {
|
||||
use std::mem;
|
||||
|
||||
let mut root: ffi::Window = mem::uninitialized();
|
||||
let mut x: libc::c_int = mem::uninitialized();
|
||||
let mut y: libc::c_int = mem::uninitialized();
|
||||
let mut width: libc::c_uint = mem::uninitialized();
|
||||
let mut height: libc::c_uint = mem::uninitialized();
|
||||
let mut border: libc::c_uint = mem::uninitialized();
|
||||
let mut depth: libc::c_uint = mem::uninitialized();
|
||||
|
||||
if ffi::XGetGeometry(self.x.display, self.x.window,
|
||||
&mut root, &mut x, &mut y, &mut width, &mut height,
|
||||
&mut border, &mut depth) == 0
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
Some((x as i32, y as i32, width as u32, height as u32, border as u32))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
self.get_geometry().map(|(x, y, _, _, _)| (x, y))
|
||||
}
|
||||
|
||||
pub fn set_position(&self, x: i32, y: i32) {
|
||||
unsafe { ffi::XMoveWindow(self.x.display, self.x.window, x as libc::c_int, y as libc::c_int); }
|
||||
}
|
||||
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
self.get_geometry().map(|(_, _, w, h, _)| (w, h))
|
||||
}
|
||||
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
self.get_geometry().map(|(_, _, w, h, b)| (w + b, h + b)) // TODO: is this really outside?
|
||||
}
|
||||
|
||||
pub fn set_inner_size(&self, _x: u32, _y: u32) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn create_window_proxy(&self) -> WindowProxy {
|
||||
WindowProxy {
|
||||
x: self.x.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll_events(&self) -> PollEventsIterator {
|
||||
PollEventsIterator {
|
||||
window: self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wait_events(&self) -> WaitEventsIterator {
|
||||
WaitEventsIterator {
|
||||
window: self
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn make_current(&self) {
|
||||
let res = ffi::glx::MakeCurrent(self.x.display as *mut _, self.x.window, self.x.context);
|
||||
if res == 0 {
|
||||
panic!("glx::MakeCurrent failed");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_current(&self) -> bool {
|
||||
unsafe { ffi::glx::GetCurrentContext() == self.x.context }
|
||||
}
|
||||
|
||||
pub fn get_proc_address(&self, addr: &str) -> *const () {
|
||||
use std::mem;
|
||||
|
||||
unsafe {
|
||||
with_c_str(addr, |s| {
|
||||
ffi::glx::GetProcAddress(mem::transmute(s)) as *const ()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn swap_buffers(&self) {
|
||||
unsafe { ffi::glx::SwapBuffers(self.x.display as *mut _, self.x.window) }
|
||||
}
|
||||
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
self.x.display as *mut libc::c_void
|
||||
}
|
||||
|
||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// See the docs in the crate root file.
|
||||
pub fn get_api(&self) -> ::Api {
|
||||
::Api::OpenGl
|
||||
}
|
||||
|
||||
pub fn get_pixel_format(&self) -> PixelFormat {
|
||||
self.pixel_format.clone()
|
||||
}
|
||||
|
||||
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
|
||||
}
|
||||
|
||||
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",
|
||||
|
||||
/// 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::Text | MouseCursor::VerticalText => "xterm",
|
||||
MouseCursor::Wait => "watch",
|
||||
|
||||
/// TODO: Find matching X11 cursors
|
||||
MouseCursor::ContextMenu | MouseCursor::NoneCursor |
|
||||
MouseCursor::AllScroll | MouseCursor::ZoomIn |
|
||||
MouseCursor::ZoomOut => "left_ptr",
|
||||
};
|
||||
let c_string = CString::new(cursor_name.as_bytes().to_vec()).unwrap();
|
||||
let xcursor = ffi::XcursorLibraryLoadCursor(self.x.display, c_string.as_ptr());
|
||||
ffi::XDefineCursor (self.x.display, self.x.window, xcursor);
|
||||
ffi::XFlush(self.x.display);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
let mut cursor_state = self.cursor_state.lock().unwrap();
|
||||
|
||||
match (state, *cursor_state) {
|
||||
(CursorState::Normal, CursorState::Grab) => {
|
||||
unsafe {
|
||||
ffi::XUngrabPointer(self.x.display, ffi::CurrentTime);
|
||||
*cursor_state = CursorState::Normal;
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
|
||||
(CursorState::Grab, CursorState::Normal) => {
|
||||
unsafe {
|
||||
*cursor_state = CursorState::Grab;
|
||||
|
||||
match ffi::XGrabPointer(
|
||||
self.x.display, self.x.window, ffi::False,
|
||||
(ffi::ButtonPressMask | ffi::ButtonReleaseMask | ffi::EnterWindowMask |
|
||||
ffi::LeaveWindowMask | ffi::PointerMotionMask | ffi::PointerMotionHintMask |
|
||||
ffi::Button1MotionMask | ffi::Button2MotionMask | ffi::Button3MotionMask |
|
||||
ffi::Button4MotionMask | ffi::Button5MotionMask | ffi::ButtonMotionMask |
|
||||
ffi::KeymapStateMask) as libc::c_uint,
|
||||
ffi::GrabModeAsync, ffi::GrabModeAsync,
|
||||
self.x.window, 0, ffi::CurrentTime
|
||||
) {
|
||||
ffi::GrabSuccess => Ok(()),
|
||||
ffi::AlreadyGrabbed | ffi::GrabInvalidTime |
|
||||
ffi::GrabNotViewable | ffi::GrabFrozen
|
||||
=> Err("cursor could not be grabbed".to_string()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
1.0
|
||||
}
|
||||
|
||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
||||
unsafe {
|
||||
ffi::XWarpPointer(self.x.display, 0, self.x.window, 0, 0, 0, 0, x, y);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
66
src/api/x11/window/monitor.rs
Normal file
66
src/api/x11/window/monitor.rs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
use std::ptr;
|
||||
use std::collections::VecDeque;
|
||||
use super::super::ffi;
|
||||
use super::ensure_thread_init;
|
||||
use native_monitor::NativeMonitorId;
|
||||
|
||||
pub struct MonitorID(pub u32);
|
||||
|
||||
pub fn get_available_monitors() -> VecDeque<MonitorID> {
|
||||
ensure_thread_init();
|
||||
let nb_monitors = unsafe {
|
||||
let display = ffi::XOpenDisplay(ptr::null());
|
||||
if display.is_null() {
|
||||
panic!("get_available_monitors failed");
|
||||
}
|
||||
let nb_monitors = ffi::XScreenCount(display);
|
||||
ffi::XCloseDisplay(display);
|
||||
nb_monitors
|
||||
};
|
||||
|
||||
let mut monitors = VecDeque::new();
|
||||
monitors.extend((0..nb_monitors).map(|i| MonitorID(i as u32)));
|
||||
monitors
|
||||
}
|
||||
|
||||
pub fn get_primary_monitor() -> MonitorID {
|
||||
ensure_thread_init();
|
||||
let primary_monitor = unsafe {
|
||||
let display = ffi::XOpenDisplay(ptr::null());
|
||||
if display.is_null() {
|
||||
panic!("get_available_monitors failed");
|
||||
}
|
||||
let primary_monitor = ffi::XDefaultScreen(display);
|
||||
ffi::XCloseDisplay(display);
|
||||
primary_monitor
|
||||
};
|
||||
|
||||
MonitorID(primary_monitor as u32)
|
||||
}
|
||||
|
||||
impl MonitorID {
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
let MonitorID(screen_num) = *self;
|
||||
Some(format!("Monitor #{}", screen_num))
|
||||
}
|
||||
|
||||
pub fn get_native_identifier(&self) -> NativeMonitorId {
|
||||
let MonitorID(screen_num) = *self;
|
||||
NativeMonitorId::Numeric(screen_num)
|
||||
}
|
||||
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
let dimensions = unsafe {
|
||||
let display = ffi::XOpenDisplay(ptr::null());
|
||||
let MonitorID(screen_num) = *self;
|
||||
let screen = ffi::XScreenOfDisplay(display, screen_num as i32);
|
||||
let width = ffi::XWidthOfScreen(screen);
|
||||
let height = ffi::XHeightOfScreen(screen);
|
||||
ffi::XCloseDisplay(display);
|
||||
(width as u32, height as u32)
|
||||
};
|
||||
|
||||
dimensions
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue