1use emscripten_functions_sys::websocket::{
9 pthread_t, EmscriptenWebSocketCloseEvent, EmscriptenWebSocketErrorEvent,
10 EmscriptenWebSocketMessageEvent, EmscriptenWebSocketOpenEvent, __pthread,
11 emscripten_websocket_close, emscripten_websocket_delete,
12 emscripten_websocket_get_buffered_amount, emscripten_websocket_get_protocol,
13 emscripten_websocket_get_protocol_length, emscripten_websocket_get_url,
14 emscripten_websocket_get_url_length, emscripten_websocket_is_supported,
15 emscripten_websocket_new, emscripten_websocket_send_binary,
16 emscripten_websocket_send_utf8_text, emscripten_websocket_set_onclose_callback_on_thread,
17 emscripten_websocket_set_onerror_callback_on_thread,
18 emscripten_websocket_set_onmessage_callback_on_thread,
19 emscripten_websocket_set_onopen_callback_on_thread, EmscriptenWebSocketCreateAttributes,
20 EMSCRIPTEN_RESULT_SUCCESS,
21};
22use std::ffi::CString;
23
24pub const EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD: pthread_t = 0x2 as *mut __pthread;
25
26#[derive(Clone, Copy, PartialEq, Eq)]
27pub enum WebSocketState {
28 Connecting,
29 Opened,
30 Closing,
31 Closed,
32 Error,
33}
34
35pub enum WebSocketData {
36 Text(String),
37 RawBuffer(Vec<u8>),
38}
39
40pub struct WebSocket {
41 id: i32,
42 state: WebSocketState,
43
44 open_cb: Option<fn(&mut Self)>,
45 error_cb: Option<fn(&mut Self)>,
46 close_cb: Option<fn(&mut Self)>,
47 message_cb: Option<fn(&mut Self, WebSocketData)>,
48}
49
50unsafe extern "C" fn on_open_callback(
51 _event_type: ::std::os::raw::c_int,
52 _websocket_event: *const EmscriptenWebSocketOpenEvent,
53 user_data: *mut ::std::os::raw::c_void,
54) -> bool {
55 let ws: &mut WebSocket = &mut *(user_data as *mut WebSocket);
56 ws.state = WebSocketState::Opened;
57
58 if let Some(fn_cb) = ws.open_cb {
59 (fn_cb)(ws);
60 }
61
62 true
63}
64
65unsafe extern "C" fn on_error_callback(
66 _event_type: ::std::os::raw::c_int,
67 _websocket_event: *const EmscriptenWebSocketErrorEvent,
68 user_data: *mut ::std::os::raw::c_void,
69) -> bool {
70 let ws: &mut WebSocket = &mut *(user_data as *mut WebSocket);
71 ws.state = WebSocketState::Error;
72 ws.clear_internal_callback();
73
74 if let Some(fn_cb) = ws.error_cb {
75 (fn_cb)(ws);
76 }
77
78 true
79}
80
81unsafe extern "C" fn on_close_callback(
82 _event_type: ::std::os::raw::c_int,
83 _websocket_event: *const EmscriptenWebSocketCloseEvent,
84 user_data: *mut ::std::os::raw::c_void,
85) -> bool {
86 let ws: &mut WebSocket = &mut *(user_data as *mut WebSocket);
87 ws.state = WebSocketState::Closed;
88 ws.clear_internal_callback();
89
90 if let Some(fn_cb) = ws.close_cb {
91 (fn_cb)(ws);
92 }
93
94 true
95}
96
97unsafe extern "C" fn on_message_callback(
98 _event_type: ::std::os::raw::c_int,
99 websocket_event: *const EmscriptenWebSocketMessageEvent,
100 user_data: *mut ::std::os::raw::c_void,
101) -> bool {
102 let ws: &mut WebSocket = &mut *(user_data as *mut WebSocket);
103 if ws.message_cb.is_none() {
104 return true;
105 }
106
107 let fn_cb = ws.message_cb.unwrap();
108 if (*websocket_event).isText {
109 let tmp_vec = Vec::from_raw_parts(
110 (*websocket_event).data,
111 (*websocket_event).numBytes as usize,
112 (*websocket_event).numBytes as usize,
113 );
114 (fn_cb)(ws, WebSocketData::Text(String::from_utf8(tmp_vec).unwrap()));
115 } else {
116 let tmp_vec = Vec::from_raw_parts(
117 (*websocket_event).data,
118 (*websocket_event).numBytes as usize,
119 (*websocket_event).numBytes as usize,
120 );
121 (fn_cb)(ws, WebSocketData::RawBuffer(tmp_vec));
122 }
123
124 true
125}
126
127impl WebSocket {
128 pub fn new() -> Option<WebSocket> {
136 if unsafe { emscripten_websocket_is_supported() } {
137 Some(WebSocket {
138 id: 0,
139 state: WebSocketState::Closed,
140 open_cb: None,
141 error_cb: None,
142 close_cb: None,
143 message_cb: None,
144 })
145 } else {
146 None
147 }
148 }
149
150 pub fn connect<T>(&mut self, url: T) -> bool
160 where
161 T: AsRef<str>,
162 {
163 if self.state != WebSocketState::Closed {
164 return false;
165 }
166
167 let url_cstr = CString::new(url.as_ref()).unwrap();
168 let mut create_attr = EmscriptenWebSocketCreateAttributes {
169 url: url_cstr.as_ptr(),
170 protocols: std::ptr::null(),
171 createOnMainThread: true,
172 };
173
174 let socket: i32 = unsafe { emscripten_websocket_new(&mut create_attr) };
175 if socket > 0 {
176 self.id = socket;
177 self.state = WebSocketState::Connecting;
178 self.init_internal_callback();
179
180 true
181 } else {
182 self.id = 0;
183 self.state = WebSocketState::Closed;
184
185 false
186 }
187 }
188
189 pub fn get_id(&self) -> i32 {
191 self.id
192 }
193 pub fn get_state(&self) -> WebSocketState {
195 self.state
196 }
197 pub fn get_buffered_amount(&self) -> usize {
199 let mut size: usize = 0;
200 unsafe {
201 emscripten_websocket_get_buffered_amount(self.id, &mut size);
202 }
203
204 size
205 }
206 pub fn get_url(&self) -> String {
208 let mut size: i32 = 0;
209 unsafe {
210 emscripten_websocket_get_url_length(self.id, &mut size);
211 let mut url_raw = vec![0u8; size as usize];
212 let url_raw_ptr = url_raw.as_mut_ptr() as *mut i8;
213 emscripten_websocket_get_url(self.id, url_raw_ptr, 26);
214 String::from_utf8(url_raw).unwrap()
215 }
216 }
217 pub fn get_protocol(&self) -> String {
219 let mut size: i32 = 0;
220 unsafe {
221 emscripten_websocket_get_protocol_length(self.id, &mut size);
222 let mut protocol_raw = vec![0u8; size as usize];
223 let protocol_raw_ptr = protocol_raw.as_mut_ptr() as *mut i8;
224 emscripten_websocket_get_protocol(self.id, protocol_raw_ptr, 26);
225 String::from_utf8(protocol_raw).unwrap()
226 }
227 }
228
229 fn init_internal_callback(&mut self) {
231 unsafe {
232 emscripten_websocket_set_onopen_callback_on_thread(
233 self.id,
234 self as *mut _ as *mut std::os::raw::c_void,
235 Some(on_open_callback),
236 EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD,
237 );
238 emscripten_websocket_set_onerror_callback_on_thread(
239 self.id,
240 self as *mut _ as *mut std::os::raw::c_void,
241 Some(on_error_callback),
242 EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD,
243 );
244 emscripten_websocket_set_onclose_callback_on_thread(
245 self.id,
246 self as *mut _ as *mut std::os::raw::c_void,
247 Some(on_close_callback),
248 EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD,
249 );
250 emscripten_websocket_set_onmessage_callback_on_thread(
251 self.id,
252 self as *mut _ as *mut std::os::raw::c_void,
253 Some(on_message_callback),
254 EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD,
255 );
256 }
257 }
258 pub fn clear_internal_callback(&mut self) {
260 unsafe {
261 emscripten_websocket_set_onopen_callback_on_thread(
262 self.id,
263 std::ptr::null_mut::<std::os::raw::c_void>(),
264 None,
265 EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD,
266 );
267 emscripten_websocket_set_onerror_callback_on_thread(
268 self.id,
269 std::ptr::null_mut::<std::os::raw::c_void>(),
270 None,
271 EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD,
272 );
273 emscripten_websocket_set_onclose_callback_on_thread(
274 self.id,
275 std::ptr::null_mut::<std::os::raw::c_void>(),
276 None,
277 EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD,
278 );
279 emscripten_websocket_set_onmessage_callback_on_thread(
280 self.id,
281 std::ptr::null_mut::<std::os::raw::c_void>(),
282 None,
283 EM_CALLBACK_THREAD_CONTEXT_CALLING_THREAD,
284 );
285 }
286 }
287
288 pub fn set_open_callback(&mut self, cb: Option<fn(&mut Self)>) {
290 self.open_cb = cb;
291 }
292 pub fn set_error_callback(&mut self, cb: Option<fn(&mut Self)>) {
294 self.error_cb = cb;
295 }
296 pub fn set_close_callback(&mut self, cb: Option<fn(&mut Self)>) {
298 self.close_cb = cb;
299 }
300 pub fn set_message_callback(&mut self, cb: Option<fn(&mut Self, WebSocketData)>) {
302 self.message_cb = cb;
303 }
304
305 pub fn send_utf8_text<T>(&mut self, string: T) -> bool
315 where
316 T: AsRef<str>,
317 {
318 let text_cstr = CString::new(string.as_ref()).unwrap();
319 unsafe {
320 let result = emscripten_websocket_send_utf8_text(self.id, text_cstr.as_ptr());
321 (result as u32) == EMSCRIPTEN_RESULT_SUCCESS
322 }
323 }
324
325 pub fn send_binary(&mut self, data: &mut [u8]) -> bool {
335 unsafe {
336 let result = emscripten_websocket_send_binary(
337 self.id,
338 data.as_mut_ptr() as *mut std::os::raw::c_void,
339 data.len() as u32,
340 );
341
342 (result as u32) == EMSCRIPTEN_RESULT_SUCCESS
343 }
344 }
345
346 pub fn close<T>(&mut self, code: u16, reason: T)
356 where
357 T: AsRef<str>,
358 {
359 self.state = WebSocketState::Closing;
360 let reason_cstr = CString::new(reason.as_ref()).unwrap();
361 unsafe {
362 emscripten_websocket_close(self.id, code, reason_cstr.as_ptr());
363 }
364 }
365}
366
367impl Drop for WebSocket {
368 fn drop(&mut self) {
369 unsafe {
370 emscripten_websocket_delete(self.id);
371 }
372 }
373}