use emscripten_functions_sys::websocket::{
pthread_t, EmscriptenWebSocketCloseEvent, EmscriptenWebSocketErrorEvent,
EmscriptenWebSocketMessageEvent, EmscriptenWebSocketOpenEvent, __pthread,
emscripten_websocket_close, emscripten_websocket_delete,
emscripten_websocket_get_buffered_amount, emscripten_websocket_get_protocol,
emscripten_websocket_get_protocol_length, emscripten_websocket_get_url,
emscripten_websocket_get_url_length, emscripten_websocket_is_supported,
emscripten_websocket_new, emscripten_websocket_send_binary,
emscripten_websocket_send_utf8_text, emscripten_websocket_set_onclose_callback_on_thread,
emscripten_websocket_set_onerror_callback_on_thread,
emscripten_websocket_set_onmessage_callback_on_thread,
emscripten_websocket_set_onopen_callback_on_thread, EmscriptenWebSocketCreateAttributes,
EMSCRIPTEN_RESULT_SUCCESS,
};
use std::ffi::CString;
pub const EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD: pthread_t = 0x2 as *mut __pthread;
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum WebSocketState {
Connecting,
Opened,
Closing,
Closed,
Error,
}
pub enum WebSocketData {
Text(String),
RawBuffer(Vec<u8>),
}
pub struct WebSocket {
id: i32,
state: WebSocketState,
open_cb: Option<fn(&mut Self)>,
error_cb: Option<fn(&mut Self)>,
close_cb: Option<fn(&mut Self)>,
message_cb: Option<fn(&mut Self, WebSocketData)>,
}
unsafe extern "C" fn on_open_callback(
_event_type: ::std::os::raw::c_int,
_websocket_event: *const EmscriptenWebSocketOpenEvent,
user_data: *mut ::std::os::raw::c_void,
) -> bool {
let ws: &mut WebSocket = &mut *(user_data as *mut WebSocket);
ws.state = WebSocketState::Opened;
if let Some(fn_cb) = ws.open_cb {
(fn_cb)(ws);
}
true
}
unsafe extern "C" fn on_error_callback(
_event_type: ::std::os::raw::c_int,
_websocket_event: *const EmscriptenWebSocketErrorEvent,
user_data: *mut ::std::os::raw::c_void,
) -> bool {
let ws: &mut WebSocket = &mut *(user_data as *mut WebSocket);
ws.state = WebSocketState::Error;
ws.clear_internal_callback();
if let Some(fn_cb) = ws.error_cb {
(fn_cb)(ws);
}
true
}
unsafe extern "C" fn on_close_callback(
_event_type: ::std::os::raw::c_int,
_websocket_event: *const EmscriptenWebSocketCloseEvent,
user_data: *mut ::std::os::raw::c_void,
) -> bool {
let ws: &mut WebSocket = &mut *(user_data as *mut WebSocket);
ws.state = WebSocketState::Closed;
ws.clear_internal_callback();
if let Some(fn_cb) = ws.close_cb {
(fn_cb)(ws);
}
true
}
unsafe extern "C" fn on_message_callback(
_event_type: ::std::os::raw::c_int,
websocket_event: *const EmscriptenWebSocketMessageEvent,
user_data: *mut ::std::os::raw::c_void,
) -> bool {
let ws: &mut WebSocket = &mut *(user_data as *mut WebSocket);
if ws.message_cb.is_none() {
return true;
}
let fn_cb = ws.message_cb.unwrap();
if (*websocket_event).isText {
let tmp_vec = Vec::from_raw_parts(
(*websocket_event).data,
(*websocket_event).numBytes as usize,
(*websocket_event).numBytes as usize,
);
(fn_cb)(ws, WebSocketData::Text(String::from_utf8(tmp_vec).unwrap()));
} else {
let tmp_vec = Vec::from_raw_parts(
(*websocket_event).data,
(*websocket_event).numBytes as usize,
(*websocket_event).numBytes as usize,
);
(fn_cb)(ws, WebSocketData::RawBuffer(tmp_vec));
}
true
}
impl WebSocket {
pub fn new() -> Option<WebSocket> {
if unsafe { emscripten_websocket_is_supported() } {
Some(WebSocket {
id: 0,
state: WebSocketState::Closed,
open_cb: None,
error_cb: None,
close_cb: None,
message_cb: None,
})
} else {
None
}
}
pub fn connect<T>(&mut self, url: T) -> bool
where
T: AsRef<str>,
{
if self.state != WebSocketState::Closed {
return false;
}
let url_cstr = CString::new(url.as_ref()).unwrap();
let mut create_attr = EmscriptenWebSocketCreateAttributes {
url: url_cstr.as_ptr(),
protocols: std::ptr::null(),
createOnMainThread: true,
};
let socket: i32 = unsafe { emscripten_websocket_new(&mut create_attr) };
if socket > 0 {
self.id = socket;
self.state = WebSocketState::Connecting;
self.init_internal_callback();
true
} else {
self.id = 0;
self.state = WebSocketState::Closed;
false
}
}
pub fn get_id(&self) -> i32 {
self.id
}
pub fn get_state(&self) -> WebSocketState {
self.state
}
pub fn get_buffered_amount(&self) -> usize {
let mut size: usize = 0;
unsafe {
emscripten_websocket_get_buffered_amount(self.id, &mut size);
}
size
}
pub fn get_url(&self) -> String {
let mut size: i32 = 0;
unsafe {
emscripten_websocket_get_url_length(self.id, &mut size);
let mut url_raw = vec![0u8; size as usize];
let url_raw_ptr = url_raw.as_mut_ptr() as *mut i8;
emscripten_websocket_get_url(self.id, url_raw_ptr, 26);
String::from_utf8(url_raw).unwrap()
}
}
pub fn get_protocol(&self) -> String {
let mut size: i32 = 0;
unsafe {
emscripten_websocket_get_protocol_length(self.id, &mut size);
let mut protocol_raw = vec![0u8; size as usize];
let protocol_raw_ptr = protocol_raw.as_mut_ptr() as *mut i8;
emscripten_websocket_get_protocol(self.id, protocol_raw_ptr, 26);
String::from_utf8(protocol_raw).unwrap()
}
}
fn init_internal_callback(&mut self) {
unsafe {
emscripten_websocket_set_onopen_callback_on_thread(
self.id,
self as *mut _ as *mut std::os::raw::c_void,
Some(on_open_callback),
EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD,
);
emscripten_websocket_set_onerror_callback_on_thread(
self.id,
self as *mut _ as *mut std::os::raw::c_void,
Some(on_error_callback),
EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD,
);
emscripten_websocket_set_onclose_callback_on_thread(
self.id,
self as *mut _ as *mut std::os::raw::c_void,
Some(on_close_callback),
EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD,
);
emscripten_websocket_set_onmessage_callback_on_thread(
self.id,
self as *mut _ as *mut std::os::raw::c_void,
Some(on_message_callback),
EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD,
);
}
}
pub fn clear_internal_callback(&mut self) {
unsafe {
emscripten_websocket_set_onopen_callback_on_thread(
self.id,
std::ptr::null_mut::<std::os::raw::c_void>(),
None,
EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD,
);
emscripten_websocket_set_onerror_callback_on_thread(
self.id,
std::ptr::null_mut::<std::os::raw::c_void>(),
None,
EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD,
);
emscripten_websocket_set_onclose_callback_on_thread(
self.id,
std::ptr::null_mut::<std::os::raw::c_void>(),
None,
EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD,
);
emscripten_websocket_set_onmessage_callback_on_thread(
self.id,
std::ptr::null_mut::<std::os::raw::c_void>(),
None,
EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD,
);
}
}
pub fn set_open_callback(&mut self, cb: Option<fn(&mut Self)>) {
self.open_cb = cb;
}
pub fn set_error_callback(&mut self, cb: Option<fn(&mut Self)>) {
self.error_cb = cb;
}
pub fn set_close_callback(&mut self, cb: Option<fn(&mut Self)>) {
self.close_cb = cb;
}
pub fn set_message_callback(&mut self, cb: Option<fn(&mut Self, WebSocketData)>) {
self.message_cb = cb;
}
pub fn send_utf8_text<T>(&mut self, string: T) -> bool
where
T: AsRef<str>,
{
let text_cstr = CString::new(string.as_ref()).unwrap();
unsafe {
let result = emscripten_websocket_send_utf8_text(self.id, text_cstr.as_ptr());
(result as u32) == EMSCRIPTEN_RESULT_SUCCESS
}
}
pub fn send_binary(&mut self, data: &mut [u8]) -> bool {
unsafe {
let result = emscripten_websocket_send_binary(
self.id,
data.as_mut_ptr() as *mut std::os::raw::c_void,
data.len() as u32,
);
(result as u32) == EMSCRIPTEN_RESULT_SUCCESS
}
}
pub fn close<T>(&mut self, code: u16, reason: T)
where
T: AsRef<str>,
{
self.state = WebSocketState::Closing;
let reason_cstr = CString::new(reason.as_ref()).unwrap();
unsafe {
emscripten_websocket_close(self.id, code, reason_cstr.as_ptr());
}
}
}
impl Drop for WebSocket {
fn drop(&mut self) {
unsafe {
emscripten_websocket_delete(self.id);
}
}
}