diff --git a/.circleci/config.yml b/.circleci/config.yml index 5ac68236..ab832e39 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,22 +2,52 @@ version: 2 jobs: - test: - working_directory: ~/tgst + android-test: + working_directory: ~/winit docker: - image: tomaka/cargo-apk steps: - checkout - restore_cache: - key: test-cache-{{ checksum "Cargo.toml" }} + key: android-test-cache-{{ checksum "Cargo.toml" }} - run: cargo apk build --example window - 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: + - target + workflows: version: 2 build-test-and-deploy: jobs: - - test + - android-test + - asmjs-test + - wasm-test diff --git a/README.md b/README.md index 3e82f258..6d2ea68f 100644 --- a/README.md +++ b/README.md @@ -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 `` 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 `` 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. diff --git a/src/platform/emscripten/ffi.rs b/src/platform/emscripten/ffi.rs new file mode 100644 index 00000000..5543836f --- /dev/null +++ b/src/platform/emscripten/ffi.rs @@ -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; + +pub type em_key_callback_func = Option EM_BOOL>; + +pub type em_pointerlockchange_callback_func = Option EM_BOOL>; + +pub type em_fullscreenchange_callback_func = Option 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::(), 280usize); + assert_eq!(mem::align_of::(), 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::(), 184usize); + assert_eq!(mem::align_of::(), 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::(), 260usize); + assert_eq!(mem::align_of::(), 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(); +} diff --git a/src/platform/emscripten/mod.rs b/src/platform/emscripten/mod.rs new file mode 100644 index 00000000..2ff71434 --- /dev/null +++ b/src/platform/emscripten/mod.rs @@ -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 { + 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(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::), 0, 1); } + + unsafe extern "C" fn wrapper() 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>>, + 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 { + let mut list = VecDeque::new(); + list.push_back(MonitorId); + list + } + + #[inline] + pub fn get_primary_monitor(&self) -> MonitorId { + MonitorId + } + + pub fn poll_events(&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(&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>>, +} + +pub struct Window { + window: Arc, +} + +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> = 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 + { + 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 +} diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 999a9f32..975d4301 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -15,8 +15,11 @@ mod platform; #[cfg(target_os = "ios")] #[path="ios/mod.rs"] 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"), 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;