Emscripten rework (#286)

* partial implementation for emscripten

this pull request contain a partial but working implementation of emscripten backend

some implementations may be controversial.

here some implementation detail:

* cursor state:
  * on grab: emscripten request pointer lock deferred and also set a callback when pointer lock change
             the callback request pointer lock deferred.
  * on hide: `emscripten_hide_mouse` exist but not `emscripten_show_mouse`
             a pull request has been open on october 2016 but never been merged
             so I copied the javascript function and put it in emscripten_asm_const function

* fullscreen: if fullscreen is requested then it request fullscreen deferred and set a callback on fullscreen change
              the callback request fullscreen deferred

* run forever: this method use emscripten main loop to run an infinite loop

* keyboard callback doesn't consume the event. I think it is more apopriate as in desktop environment it is the same, is it ?

* emscripten dir is added in example and contains html pages

Some things that are not implemented:

* lots of events

* min and max dimension can be implemented with a callback that listen to size change and resize if dimension out of bound

* title may be implemented using javascript to change document.title

* Use std::os::raw in the emscripten bindings

* Fix emscripten code

* Update code

* Add CI

* Remove the emscripten-specific examples

* Add some information to the README
This commit is contained in:
tomaka 2017-09-14 16:31:34 +02:00 committed by GitHub
parent ae7802c8c2
commit 769d4fe897
5 changed files with 709 additions and 6 deletions

View file

@ -2,17 +2,45 @@ version: 2
jobs: jobs:
test: android-test:
working_directory: ~/tgst working_directory: ~/winit
docker: docker:
- image: tomaka/cargo-apk - image: tomaka/cargo-apk
steps: steps:
- checkout - checkout
- restore_cache: - restore_cache:
key: test-cache-{{ checksum "Cargo.toml" }} key: android-test-cache-{{ checksum "Cargo.toml" }}
- run: cargo apk build --example window - run: cargo apk build --example window
- save_cache: - save_cache:
key: test-cache-{{ checksum "Cargo.toml" }} key: android-test-cache-{{ checksum "Cargo.toml" }}
paths:
- target
asmjs-test:
working_directory: ~/winit
docker:
- image: tomaka/rustc-emscripten
steps:
- checkout
- restore_cache:
key: asmjs-test-cache-{{ checksum "Cargo.toml" }}
- run: cargo build --example window --target asmjs-unknown-emscripten
- save_cache:
key: asmjs-test-cache-{{ checksum "Cargo.toml" }}
paths:
- target
wasm-test:
working_directory: ~/winit
docker:
- image: tomaka/rustc-emscripten
steps:
- checkout
- restore_cache:
key: wasm-test-cache-{{ checksum "Cargo.toml" }}
- run: cargo build --example window --target wasm32-unknown-emscripten
- save_cache:
key: wasm-test-cache-{{ checksum "Cargo.toml" }}
paths: paths:
- target - target
@ -20,4 +48,6 @@ workflows:
version: 2 version: 2
build-test-and-deploy: build-test-and-deploy:
jobs: jobs:
- test - android-test
- asmjs-test
- wasm-test

View file

@ -41,3 +41,15 @@ fn main() {
}); });
} }
``` ```
### Platform-specific usage
#### Emscripten and WebAssembly
Building a binary will yield a `.js` file. In order to use it in an HTML file, you need to:
- Put a `<canvas id="my_id"></canvas>` element somewhere. A canvas corresponds to a winit "window".
- Write a Javascript code that creates a global variable named `Module`. Set `Module.canvas` to
the ID of the `<canvas>` element (in the example this would be `"my_id"`). More information
[here](https://kripken.github.io/emscripten-site/docs/api_reference/module.html).
- Make sure that you insert the `.js` file generated by Rust after the `Module` variable is created.

View file

@ -0,0 +1,189 @@
#![allow(dead_code)]
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
use std::mem;
use std::os::raw::{c_int, c_char, c_void, c_ulong, c_double};
pub type EM_BOOL = c_int;
pub type EM_UTF8 = c_char;
pub type EMSCRIPTEN_RESULT = c_int;
pub const EM_TRUE: EM_BOOL = 1;
pub const EM_FALSE: EM_BOOL = 0;
// values for EMSCRIPTEN_RESULT
pub const EMSCRIPTEN_RESULT_SUCCESS: c_int = 0;
pub const EMSCRIPTEN_RESULT_DEFERRED: c_int = 1;
pub const EMSCRIPTEN_RESULT_NOT_SUPPORTED: c_int = -1;
pub const EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED: c_int = -2;
pub const EMSCRIPTEN_RESULT_INVALID_TARGET: c_int = -3;
pub const EMSCRIPTEN_RESULT_UNKNOWN_TARGET: c_int = -4;
pub const EMSCRIPTEN_RESULT_INVALID_PARAM: c_int = -5;
pub const EMSCRIPTEN_RESULT_FAILED: c_int = -6;
pub const EMSCRIPTEN_RESULT_NO_DATA: c_int = -7;
// values for EMSCRIPTEN EVENT
pub const EMSCRIPTEN_EVENT_KEYPRESS: c_int = 1;
pub const EMSCRIPTEN_EVENT_KEYDOWN: c_int = 2;
pub const EMSCRIPTEN_EVENT_KEYUP: c_int = 3;
pub const EMSCRIPTEN_EVENT_CLICK: c_int = 4;
pub const EMSCRIPTEN_EVENT_MOUSEDOWN: c_int = 5;
pub const EMSCRIPTEN_EVENT_MOUSEUP: c_int = 6;
pub const EMSCRIPTEN_EVENT_DBLCLICK: c_int = 7;
pub const EMSCRIPTEN_EVENT_MOUSEMOVE: c_int = 8;
pub const EMSCRIPTEN_EVENT_WHEEL: c_int = 9;
pub const EMSCRIPTEN_EVENT_RESIZE: c_int = 10;
pub const EMSCRIPTEN_EVENT_SCROLL: c_int = 11;
pub const EMSCRIPTEN_EVENT_BLUR: c_int = 12;
pub const EMSCRIPTEN_EVENT_FOCUS: c_int = 13;
pub const EMSCRIPTEN_EVENT_FOCUSIN: c_int = 14;
pub const EMSCRIPTEN_EVENT_FOCUSOUT: c_int = 15;
pub const EMSCRIPTEN_EVENT_DEVICEORIENTATION: c_int = 16;
pub const EMSCRIPTEN_EVENT_DEVICEMOTION: c_int = 17;
pub const EMSCRIPTEN_EVENT_ORIENTATIONCHANGE: c_int = 18;
pub const EMSCRIPTEN_EVENT_FULLSCREENCHANGE: c_int = 19;
pub const EMSCRIPTEN_EVENT_POINTERLOCKCHANGE: c_int = 20;
pub const EMSCRIPTEN_EVENT_VISIBILITYCHANGE: c_int = 21;
pub const EMSCRIPTEN_EVENT_TOUCHSTART: c_int = 22;
pub const EMSCRIPTEN_EVENT_TOUCHEND: c_int = 23;
pub const EMSCRIPTEN_EVENT_TOUCHMOVE: c_int = 24;
pub const EMSCRIPTEN_EVENT_TOUCHCANCEL: c_int = 25;
pub const EMSCRIPTEN_EVENT_GAMEPADCONNECTED: c_int = 26;
pub const EMSCRIPTEN_EVENT_GAMEPADDISCONNECTED: c_int = 27;
pub const EMSCRIPTEN_EVENT_BEFOREUNLOAD: c_int = 28;
pub const EMSCRIPTEN_EVENT_BATTERYCHARGINGCHANGE: c_int = 29;
pub const EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE: c_int = 30;
pub const EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST: c_int = 31;
pub const EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED: c_int = 32;
pub const EMSCRIPTEN_EVENT_MOUSEENTER: c_int = 33;
pub const EMSCRIPTEN_EVENT_MOUSELEAVE: c_int = 34;
pub const EMSCRIPTEN_EVENT_MOUSEOVER: c_int = 35;
pub const EMSCRIPTEN_EVENT_MOUSEOUT: c_int = 36;
pub const EMSCRIPTEN_EVENT_CANVASRESIZED: c_int = 37;
pub const EMSCRIPTEN_EVENT_POINTERLOCKERROR: c_int = 38;
pub const EM_HTML5_SHORT_STRING_LEN_BYTES: usize = 32;
pub type em_callback_func = Option<unsafe extern "C" fn()>;
pub type em_key_callback_func = Option<unsafe extern "C" fn(
eventType: c_int,
keyEvent: *const EmscriptenKeyboardEvent,
userData: *mut c_void) -> EM_BOOL>;
pub type em_pointerlockchange_callback_func = Option<unsafe extern "C" fn(
eventType: c_int,
pointerlockChangeEvent: *const EmscriptenPointerlockChangeEvent,
userData: *mut c_void) -> EM_BOOL>;
pub type em_fullscreenchange_callback_func = Option<unsafe extern "C" fn(
eventType: c_int,
fullscreenChangeEvent: *const EmscriptenFullscreenChangeEvent,
userData: *mut c_void) -> EM_BOOL>;
#[repr(C)]
pub struct EmscriptenFullscreenChangeEvent {
pub isFullscreen: c_int,
pub fullscreenEnabled: c_int,
pub nodeName: [c_char; 128usize],
pub id: [c_char; 128usize],
pub elementWidth: c_int,
pub elementHeight: c_int,
pub screenWidth: c_int,
pub screenHeight: c_int,
}
#[test]
fn bindgen_test_layout_EmscriptenFullscreenChangeEvent() {
assert_eq!(mem::size_of::<EmscriptenFullscreenChangeEvent>(), 280usize);
assert_eq!(mem::align_of::<EmscriptenFullscreenChangeEvent>(), 4usize);
}
#[repr(C)]
#[derive(Debug, Copy)]
pub struct EmscriptenKeyboardEvent {
pub key: [c_char; 32usize],
pub code: [c_char; 32usize],
pub location: c_ulong,
pub ctrlKey: c_int,
pub shiftKey: c_int,
pub altKey: c_int,
pub metaKey: c_int,
pub repeat: c_int,
pub locale: [c_char; 32usize],
pub charValue: [c_char; 32usize],
pub charCode: c_ulong,
pub keyCode: c_ulong,
pub which: c_ulong,
}
#[test]
fn bindgen_test_layout_EmscriptenKeyboardEvent() {
assert_eq!(mem::size_of::<EmscriptenKeyboardEvent>(), 184usize);
assert_eq!(mem::align_of::<EmscriptenKeyboardEvent>(), 8usize);
}
impl Clone for EmscriptenKeyboardEvent {
fn clone(&self) -> Self { *self }
}
#[repr(C)]
pub struct EmscriptenPointerlockChangeEvent {
pub isActive: c_int,
pub nodeName: [c_char; 128usize],
pub id: [c_char; 128usize],
}
#[test]
fn bindgen_test_layout_EmscriptenPointerlockChangeEvent() {
assert_eq!(mem::size_of::<EmscriptenPointerlockChangeEvent>(), 260usize);
assert_eq!(mem::align_of::<EmscriptenPointerlockChangeEvent>(), 4usize);
}
extern "C" {
pub fn emscripten_set_element_css_size(
target: *const c_char, width: c_double,
height: c_double) -> EMSCRIPTEN_RESULT;
pub fn emscripten_get_element_css_size(
target: *const c_char, width: *mut c_double,
height: *mut c_double) -> EMSCRIPTEN_RESULT;
pub fn emscripten_request_pointerlock(
target: *const c_char, deferUntilInEventHandler: EM_BOOL)
-> EMSCRIPTEN_RESULT;
pub fn emscripten_exit_pointerlock() -> EMSCRIPTEN_RESULT;
pub fn emscripten_request_fullscreen(
target: *const c_char, deferUntilInEventHandler: EM_BOOL)
-> EMSCRIPTEN_RESULT;
pub fn emscripten_exit_fullscreen() -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_keydown_callback(
target: *const c_char, userData: *mut c_void,
useCapture: EM_BOOL, callback: em_key_callback_func)
-> EMSCRIPTEN_RESULT;
pub fn emscripten_set_keyup_callback(
target: *const c_char, userData: *mut c_void,
useCapture: EM_BOOL, callback: em_key_callback_func)
-> EMSCRIPTEN_RESULT;
pub fn emscripten_hide_mouse();
pub fn emscripten_get_device_pixel_ratio() -> f64;
pub fn emscripten_set_pointerlockchange_callback(
target: *const c_char, userData: *mut c_void, useCapture: EM_BOOL,
callback: em_pointerlockchange_callback_func) -> EMSCRIPTEN_RESULT;
pub fn emscripten_set_fullscreenchange_callback(
target: *const c_char, userData: *mut c_void, useCapture: EM_BOOL,
callback: em_fullscreenchange_callback_func) -> EMSCRIPTEN_RESULT;
pub fn emscripten_asm_const(code: *const c_char);
pub fn emscripten_set_main_loop(
func: em_callback_func, fps: c_int, simulate_infinite_loop: EM_BOOL);
pub fn emscripten_cancel_main_loop();
}

View file

@ -0,0 +1,469 @@
#![cfg(target_os = "emscripten")]
mod ffi;
use std::mem;
use std::os::raw::{c_char, c_void, c_double, c_ulong, c_int};
use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Mutex, Arc, Weak};
use std::cell::RefCell;
use std::collections::VecDeque;
const DOCUMENT_NAME: &'static str = "#document\0";
#[derive(Clone, Default)]
pub struct PlatformSpecificWindowBuilderAttributes;
unsafe impl Send for PlatformSpecificWindowBuilderAttributes {}
unsafe impl Sync for PlatformSpecificWindowBuilderAttributes {}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DeviceId;
#[derive(Clone, Default)]
pub struct PlatformSpecificHeadlessBuilderAttributes;
#[derive(Clone)]
pub struct MonitorId;
impl MonitorId {
#[inline]
pub fn get_name(&self) -> Option<String> {
Some("Canvas".to_owned())
}
#[inline]
pub fn get_position(&self) -> (u32, u32) {
unimplemented!()
}
#[inline]
pub fn get_dimensions(&self) -> (u32, u32) {
(0, 0)
}
}
// Used to assign a callback to emscripten main loop
thread_local!(static MAIN_LOOP_CALLBACK: RefCell<*mut c_void> = RefCell::new(ptr::null_mut()));
// Used to assign a callback to emscripten main loop
pub fn set_main_loop_callback<F>(callback : F) where F : FnMut() {
MAIN_LOOP_CALLBACK.with(|log| {
*log.borrow_mut() = &callback as *const _ as *mut c_void;
});
unsafe { ffi::emscripten_set_main_loop(Some(wrapper::<F>), 0, 1); }
unsafe extern "C" fn wrapper<F>() where F : FnMut() {
MAIN_LOOP_CALLBACK.with(|z| {
let closure = *z.borrow_mut() as *mut F;
(*closure)();
});
}
}
pub struct EventsLoopProxy;
impl EventsLoopProxy {
pub fn wakeup(&self) -> Result<(), ::EventsLoopClosed> {
unimplemented!()
}
}
pub struct EventsLoop {
window: Mutex<Option<Arc<Window2>>>,
interrupted: AtomicBool,
}
impl EventsLoop {
pub fn new() -> EventsLoop {
EventsLoop {
window: Mutex::new(None),
interrupted: AtomicBool::new(false),
}
}
pub fn interrupt(&self) {
self.interrupted.store(true, Ordering::Relaxed);
}
pub fn create_proxy(&self) -> EventsLoopProxy {
unimplemented!()
}
#[inline]
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
let mut list = VecDeque::new();
list.push_back(MonitorId);
list
}
#[inline]
pub fn get_primary_monitor(&self) -> MonitorId {
MonitorId
}
pub fn poll_events<F>(&self, mut callback: F)
where F: FnMut(::Event)
{
let ref mut window = *self.window.lock().unwrap();
if let &mut Some(ref mut window) = window {
while let Some(event) = window.events.borrow_mut().pop_front() {
callback(event)
}
}
}
pub fn run_forever<F>(&self, mut callback: F)
where F: FnMut(::Event) -> ::ControlFlow
{
self.interrupted.store(false, Ordering::Relaxed);
// TODO: handle control flow
set_main_loop_callback(|| {
self.poll_events(|e| { callback(e); });
::std::thread::sleep(::std::time::Duration::from_millis(5));
if self.interrupted.load(Ordering::Relaxed) {
unsafe { ffi::emscripten_cancel_main_loop(); }
}
});
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WindowId(usize);
pub struct Window2 {
cursor_state: Mutex<::CursorState>,
is_fullscreen: bool,
events: Box<RefCell<VecDeque<::Event>>>,
}
pub struct Window {
window: Arc<Window2>,
}
fn show_mouse() {
// Hide mouse hasn't show mouse equivalent.
// There is a pull request on emscripten that hasn't been merged #4616
// that contains:
//
// var styleSheet = document.styleSheets[0];
// var rules = styleSheet.cssRules;
// for (var i = 0; i < rules.length; i++) {
// if (rules[i].cssText.substr(0, 6) == 'canvas') {
// styleSheet.deleteRule(i);
// i--;
// }
// }
// styleSheet.insertRule('canvas.emscripten { border: none; cursor: auto; }', 0);
unsafe {
ffi::emscripten_asm_const(b"var styleSheet = document.styleSheets[0]; var rules = styleSheet.cssRules; for (var i = 0; i < rules.length; i++) { if (rules[i].cssText.substr(0, 6) == 'canvas') { styleSheet.deleteRule(i); i--; } } styleSheet.insertRule('canvas.emscripten { border: none; cursor: auto; }', 0);\0".as_ptr() as *const c_char);
}
}
extern "C" fn keyboard_callback(
event_type: c_int,
event: *const ffi::EmscriptenKeyboardEvent,
event_queue: *mut c_void) -> ffi::EM_BOOL
{
unsafe {
let queue: &RefCell<VecDeque<::Event>> = mem::transmute(event_queue);
match event_type {
ffi::EMSCRIPTEN_EVENT_KEYDOWN => {
queue.borrow_mut().push_back(::Event::WindowEvent {
window_id: ::WindowId(WindowId(0)),
event: ::WindowEvent::KeyboardInput {
device_id: ::DeviceId(DeviceId),
input: ::KeyboardInput {
scancode: key_translate((*event).key) as u32,
state: ::ElementState::Pressed,
virtual_keycode: key_translate_virt((*event).key, (*event).location),
modifiers: ::ModifiersState::default() // TODO:
},
},
});
},
ffi::EMSCRIPTEN_EVENT_KEYUP => {
queue.borrow_mut().push_back(::Event::WindowEvent {
window_id: ::WindowId(WindowId(0)),
event: ::WindowEvent::KeyboardInput {
device_id: ::DeviceId(DeviceId),
input: ::KeyboardInput {
scancode: key_translate((*event).key) as u32,
state: ::ElementState::Released,
virtual_keycode: key_translate_virt((*event).key, (*event).location),
modifiers: ::ModifiersState::default() // TODO:
},
},
});
},
_ => {
}
}
}
ffi::EM_FALSE
}
// In case of fullscreen window this method will request fullscreen on change
#[allow(non_snake_case)]
unsafe extern "C" fn fullscreen_callback(
_eventType: c_int,
_fullscreenChangeEvent: *const ffi::EmscriptenFullscreenChangeEvent,
_userData: *mut c_void) -> ffi::EM_BOOL
{
ffi::emscripten_request_fullscreen(ptr::null(), ffi::EM_TRUE);
ffi::EM_FALSE
}
// In case of pointer grabbed this method will request pointer lock on change
#[allow(non_snake_case)]
unsafe extern "C" fn pointerlockchange_callback(
_eventType: c_int,
_pointerlockChangeEvent: *const ffi::EmscriptenPointerlockChangeEvent,
_userData: *mut c_void) -> ffi::EM_BOOL
{
ffi::emscripten_request_pointerlock(ptr::null(), ffi::EM_TRUE);
ffi::EM_FALSE
}
fn em_try(res: ffi::EMSCRIPTEN_RESULT) -> Result<(), String> {
match res {
ffi::EMSCRIPTEN_RESULT_SUCCESS | ffi::EMSCRIPTEN_RESULT_DEFERRED => Ok(()),
r @ _ => Err(error_to_str(r).to_string()),
}
}
impl Window {
pub fn new(events_loop: &EventsLoop, attribs: &::WindowAttributes,
_pl_attribs: &PlatformSpecificWindowBuilderAttributes)
-> Result<Window, ::CreationError>
{
if events_loop.window.lock().unwrap().is_some() {
return Err(::CreationError::OsError("Cannot create another window".to_owned()));
}
let w = Window2 {
cursor_state: Mutex::new(::CursorState::Normal),
events: Box::new(RefCell::new(VecDeque::new())),
is_fullscreen: attribs.fullscreen.is_some(),
};
let window = Window {
window: Arc::new(w),
};
// TODO: set up more event callbacks
unsafe {
em_try(ffi::emscripten_set_keydown_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(keyboard_callback)))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
em_try(ffi::emscripten_set_keyup_callback(DOCUMENT_NAME.as_ptr() as *const c_char, mem::transmute(&*window.window.events), ffi::EM_FALSE, Some(keyboard_callback)))
.map_err(|e| ::CreationError::OsError(format!("emscripten error: {}", e)))?;
}
if attribs.fullscreen.is_some() {
unsafe {
em_try(ffi::emscripten_request_fullscreen(ptr::null(), ffi::EM_TRUE))
.map_err(|e| ::CreationError::OsError(e))?;
em_try(ffi::emscripten_set_fullscreenchange_callback(ptr::null(), 0 as *mut c_void, ffi::EM_FALSE, Some(fullscreen_callback)))
.map_err(|e| ::CreationError::OsError(e))?;
}
} else if let Some((w, h)) = attribs.dimensions {
window.set_inner_size(w, h);
}
*events_loop.window.lock().unwrap() = Some(window.window.clone());
Ok(window)
}
#[inline]
pub fn id(&self) -> WindowId {
WindowId(0)
}
#[inline]
pub fn set_title(&self, _title: &str) {
}
#[inline]
pub fn get_position(&self) -> Option<(i32, i32)> {
Some((0, 0))
}
#[inline]
pub fn set_position(&self, _: i32, _: i32) {
}
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
unsafe {
use std::{mem, ptr};
let mut width = mem::uninitialized();
let mut height = mem::uninitialized();
if ffi::emscripten_get_element_css_size(ptr::null(), &mut width, &mut height)
!= ffi::EMSCRIPTEN_RESULT_SUCCESS
{
None
} else {
Some((width as u32, height as u32))
}
}
}
#[inline]
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
self.get_inner_size()
}
#[inline]
pub fn set_inner_size(&self, width: u32, height: u32) {
unsafe {
use std::ptr;
ffi::emscripten_set_element_css_size(ptr::null(), width as c_double, height
as c_double);
}
}
#[inline]
pub fn show(&self) {}
#[inline]
pub fn hide(&self) {}
#[inline]
pub fn platform_display(&self) -> *mut ::libc::c_void {
unimplemented!()
}
#[inline]
pub fn platform_window(&self) -> *mut ::libc::c_void {
unimplemented!()
}
#[inline]
pub fn set_cursor(&self, _cursor: ::MouseCursor) {}
#[inline]
pub fn set_cursor_state(&self, state: ::CursorState) -> Result<(), String> {
unsafe {
use ::CursorState::*;
let mut old_state = self.window.cursor_state.lock().unwrap();
if state == *old_state {
return Ok(());
}
// Set or unset grab callback
match state {
Hide | Normal => em_try(ffi::emscripten_set_pointerlockchange_callback(ptr::null(), 0 as *mut c_void, ffi::EM_FALSE, None))?,
Grab => em_try(ffi::emscripten_set_pointerlockchange_callback(ptr::null(), 0 as *mut c_void, ffi::EM_FALSE, Some(pointerlockchange_callback)))?,
}
// Go back to normal cursor state
match *old_state {
Hide => show_mouse(),
Grab => em_try(ffi::emscripten_exit_pointerlock())?,
Normal => (),
}
// Set cursor from normal cursor state
match state {
Hide => ffi::emscripten_hide_mouse(),
Grab => em_try(ffi::emscripten_request_pointerlock(ptr::null(), ffi::EM_TRUE))?,
Normal => (),
}
// Update
*old_state = state;
Ok(())
}
}
#[inline]
pub fn hidpi_factor(&self) -> f32 {
unsafe { ffi::emscripten_get_device_pixel_ratio() as f32 }
}
#[inline]
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
Err(())
}
#[inline]
pub fn set_maximized(&self, _maximized: bool) {
// iOS has single screen maximized apps so nothing to do
}
#[inline]
pub fn set_fullscreen(&self, _monitor: Option<::MonitorId>) {
// iOS has single screen maximized apps so nothing to do
}
#[inline]
pub fn get_current_monitor(&self) -> ::MonitorId {
::MonitorId{inner: MonitorId}
}
}
impl Drop for Window {
fn drop(&mut self) {
// Delete window from events_loop
// TODO: ?
/*if let Some(ev) = self.events_loop.upgrade() {
let _ = ev.window.lock().unwrap().take().unwrap();
}*/
unsafe {
// Return back to normal cursor state
let _ = self.set_cursor_state(::CursorState::Normal);
// Exit fullscreen if on
if self.window.is_fullscreen {
ffi::emscripten_set_fullscreenchange_callback(ptr::null(), 0 as *mut c_void, ffi::EM_FALSE, None);
ffi::emscripten_exit_fullscreen();
}
// Delete callbacks
ffi::emscripten_set_keydown_callback(DOCUMENT_NAME.as_ptr() as *const c_char, 0 as *mut c_void, ffi::EM_FALSE,None);
ffi::emscripten_set_keyup_callback(DOCUMENT_NAME.as_ptr() as *const c_char, 0 as *mut c_void, ffi::EM_FALSE,None);
}
}
}
fn error_to_str(code: ffi::EMSCRIPTEN_RESULT) -> &'static str {
match code {
ffi::EMSCRIPTEN_RESULT_SUCCESS | ffi::EMSCRIPTEN_RESULT_DEFERRED
=> "Internal error in the library (success detected as failure)",
ffi::EMSCRIPTEN_RESULT_NOT_SUPPORTED => "Not supported",
ffi::EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED => "Failed not deferred",
ffi::EMSCRIPTEN_RESULT_INVALID_TARGET => "Invalid target",
ffi::EMSCRIPTEN_RESULT_UNKNOWN_TARGET => "Unknown target",
ffi::EMSCRIPTEN_RESULT_INVALID_PARAM => "Invalid parameter",
ffi::EMSCRIPTEN_RESULT_FAILED => "Failed",
ffi::EMSCRIPTEN_RESULT_NO_DATA => "No data",
_ => "Undocumented error"
}
}
fn key_translate(input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES]) -> u8 {
use std::str;
let slice = &input[0..input.iter().take_while(|x| **x != 0).count()];
let key = unsafe { str::from_utf8(mem::transmute::<&[i8], &[u8]>(slice)).unwrap() };
if key.chars().count() == 1 {
key.as_bytes()[0]
} else {
0
}
}
fn key_translate_virt(_input: [ffi::EM_UTF8; ffi::EM_HTML5_SHORT_STRING_LEN_BYTES],
_location: c_ulong) -> Option<::VirtualKeyCode>
{
// TODO
None
}

View file

@ -15,8 +15,11 @@ mod platform;
#[cfg(target_os = "ios")] #[cfg(target_os = "ios")]
#[path="ios/mod.rs"] #[path="ios/mod.rs"]
mod platform; mod platform;
#[cfg(target_os = "emscripten")]
#[path="emscripten/mod.rs"]
mod platform;
#[cfg(all(not(target_os = "ios"), not(target_os = "windows"), not(target_os = "linux"), #[cfg(all(not(target_os = "ios"), not(target_os = "windows"), not(target_os = "linux"),
not(target_os = "macos"), not(target_os = "android"), not(target_os = "dragonfly"), not(target_os = "macos"), not(target_os = "android"), not(target_os = "dragonfly"),
not(target_os = "freebsd"), not(target_os = "openbsd")))] not(target_os = "freebsd"), not(target_os = "openbsd"), not(target_os = "emscripten")))]
use this_platform_is_not_supported; use this_platform_is_not_supported;