Browse Source

Betta websocket reconnect

infinite
A.Olokhtonov 2 years ago
parent
commit
ac0d8f7605
  1. 58
      client/aux.js
  2. 5
      client/client_recv.js
  3. 6
      client/client_send.js
  4. 39
      client/default.css
  5. 89
      client/index.html
  6. 143
      client/index.js
  7. 7
      client/tools.js
  8. 269
      client/webgl.js
  9. 83
      client/webgl_draw.js
  10. 19
      client/webgl_geometry.js
  11. 24
      client/webgl_listeners.js
  12. 34
      client/websocket.js

58
client/aux.js

@ -1,3 +1,61 @@ @@ -1,3 +1,61 @@
function ui_offline() {
document.body.classList.add('offline');
document.querySelector('.offline-toast').classList.remove('hidden');
}
function ui_online() {
document.body.classList.remove('offline');
document.querySelector('.offline-toast').classList.add('hidden');
}
function event_size(event) {
let size = 1 + 3; // type + padding
switch (event.type) {
case EVENT.PREDRAW: {
size += 4 * 2;
break;
}
case EVENT.SET_COLOR: {
size += 4;
break;
}
case EVENT.SET_WIDTH: {
size += 2;
break;
}
case EVENT.STROKE: {
size += 4 + 2 + 2 + 4 + event.points.length * 4 * 2; // u32 stroke id + u16 (count) + u16 (width) + u32 (color + count * (f32, f32) points
break;
}
case EVENT.UNDO:
case EVENT.REDO: {
break;
}
case EVENT.IMAGE:
case EVENT.IMAGE_MOVE: {
size += 4 + 4 + 4; // file id + x + y
break;
}
case EVENT.ERASER: {
size += 4; // stroke id
break;
}
default: {
console.error('fuck');
}
}
return size;
}
function find_touch(touchlist, id) {
for (const touch of touchlist) {
if (touch.identifier === id) {

5
client/client_recv.js

@ -298,6 +298,7 @@ async function handle_message(state, context, d) { @@ -298,6 +298,7 @@ async function handle_message(state, context, d) {
case MESSAGE.INIT: {
state.me = des_u32(d);
state.server_lsn = des_u32(d);
state.online = true;
init_player_defaults(state, state.me);
@ -320,7 +321,7 @@ async function handle_message(state, context, d) { @@ -320,7 +321,7 @@ async function handle_message(state, context, d) {
state.events.length = 0;
for (let i = 0; i < user_count; ++i) {
for (let i = 0; i < user_count; ++i) {
const user_id = des_u32(d);
const user_color = des_u32(d);
const user_width = des_u16(d);
@ -340,9 +341,7 @@ async function handle_message(state, context, d) { @@ -340,9 +341,7 @@ async function handle_message(state, context, d) {
do_draw = true;
recompute_static_data(context);
send_ack(event_count);
sync_queue(state);
break;

6
client/client_send.js

@ -192,6 +192,8 @@ function push_event(state, event) { @@ -192,6 +192,8 @@ function push_event(state, event) {
// Queue an event and initialize repated sends until ACKed
function queue_event(state, event, skip = false) {
if (!state.online) { return; }
push_event(state, event);
if (skip) {
@ -206,7 +208,9 @@ function queue_event(state, event, skip = false) { @@ -206,7 +208,9 @@ function queue_event(state, event, skip = false) {
}
// Fire and forget. Doesn't do anything if we are offline
async function fire_event(event) {
async function fire_event(state, event) {
if (!state.online) { return; }
const s = serializer_create(1 + event_size(event));
ser_u8(s, MESSAGE.FIRE);

39
client/default.css

@ -15,6 +15,14 @@ html, body { @@ -15,6 +15,14 @@ html, body {
overflow: hidden;
}
body .main {
height: 100%;
}
body.offline .main {
filter: brightness(50%);
}
.dhide {
display: none !important;
}
@ -255,4 +263,33 @@ input[type=range]::-moz-range-track { @@ -255,4 +263,33 @@ input[type=range]::-moz-range-track {
left: 50%;
top: 96px;
transform: translate(-50%, -50%);
}
}
.offline-toast {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 999;
background: rgba(0, 0, 0, 0.8);
display: flex;
align-items: center;
padding: 10px;
border-radius: var(--radius);
box-shadow: 0px 2px 3px 0px rgba(155, 150, 100, 0.2);
transition: transform .1s ease-in-out, opacity .1s;
font-size: 14px;
color: white;
font-weight: bold;
user-select: none;
transition: transform .1s ease-in-out, opacity .1s;
}
.offline-toast.hidden {
transform: translate(-50%, -5px);
opacity: 0;
}
body.offline * {
pointer-events: none;
}

89
client/index.html

@ -3,59 +3,66 @@ @@ -3,59 +3,66 @@
<head>
<meta charset="utf-8">
<title>Desk</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="shortcut icon" href="icons/favicon.svg" id="favicon">
<link rel="stylesheet" type="text/css" href="default.css?v=17">
<script type="text/javascript" src="math.js?v=17"></script>
<script type="text/javascript" src="aux.js?v=17"></script>
<script type="text/javascript" src="tools.js?v=17"></script>
<script type="text/javascript" src="webgl_geometry.js?v=17"></script>
<script type="text/javascript" src="webgl_shaders.js?v=17"></script>
<script type="text/javascript" src="webgl_listeners.js?v=17"></script>
<script type="text/javascript" src="webgl.js?v=17"></script>
<script type="text/javascript" src="client_send.js?v=17"></script>
<script type="text/javascript" src="client_recv.js?v=17"></script>
<script type="text/javascript" src="websocket.js?v=17"></script>
<link rel="stylesheet" type="text/css" href="default.css?v=19">
<script type="text/javascript" src="aux.js?v=19"></script>
<script type="text/javascript" src="math.js?v=19"></script>
<script type="text/javascript" src="tools.js?v=19"></script>
<script type="text/javascript" src="webgl_geometry.js?v=19"></script>
<script type="text/javascript" src="webgl_shaders.js?v=19"></script>
<script type="text/javascript" src="webgl_listeners.js?v=19"></script>
<script type="text/javascript" src="webgl_draw.js?v=19"></script>
<script type="text/javascript" src="index.js?v=19"></script>
<script type="text/javascript" src="client_send.js?v=19"></script>
<script type="text/javascript" src="client_recv.js?v=19"></script>
<script type="text/javascript" src="websocket.js?v=19"></script>
</head>
<body>
<canvas id="c"></canvas>
<div class="main">
<canvas id="c"></canvas>
<div class="sizer-wrapper">
<div class="sizer">
<input type="range" class="slider" id="stroke-width" min="1" max="64">
<div class="sizer-wrapper">
<div class="sizer">
<input type="range" class="slider" id="stroke-width" min="1" max="64">
</div>
<div id="stroke-preview" class="dhide"></div>
</div>
<div id="stroke-preview" class="dhide"></div>
</div>
<div class="pallete-wrapper">
<div class="pallete">
<div class="color active" data-color="000000"><div class="color-pane" style="background: #000000;"></div></div>
<div class="color" data-color="ffffff"><div class="color-pane" style="background: #ffffff;"></div></div>
<div class="color" data-color="d65c5c"><div class="color-pane" style="background: #d65c5c;"></div></div>
<div class="color" data-color="d6835c"><div class="color-pane" style="background: #d6835c;"></div></div>
<div class="color" data-color="72d65c"><div class="color-pane" style="background: #72d65c;"></div></div>
<div class="color" data-color="5cd6ce"><div class="color-pane" style="background: #5cd6ce;"></div></div>
<div class="color" data-color="5c89d6"><div class="color-pane" style="background: #5c89d6;"></div></div>
<div class="color" data-color="6e5cd6"><div class="color-pane" style="background: #6e5cd6;"></div></div>
<div class="pallete-wrapper">
<div class="pallete">
<div class="color active" data-color="000000"><div class="color-pane" style="background: #000000;"></div></div>
<div class="color" data-color="ffffff"><div class="color-pane" style="background: #ffffff;"></div></div>
<div class="color" data-color="d65c5c"><div class="color-pane" style="background: #d65c5c;"></div></div>
<div class="color" data-color="d6835c"><div class="color-pane" style="background: #d6835c;"></div></div>
<div class="color" data-color="72d65c"><div class="color-pane" style="background: #72d65c;"></div></div>
<div class="color" data-color="5cd6ce"><div class="color-pane" style="background: #5cd6ce;"></div></div>
<div class="color" data-color="5c89d6"><div class="color-pane" style="background: #5c89d6;"></div></div>
<div class="color" data-color="6e5cd6"><div class="color-pane" style="background: #6e5cd6;"></div></div>
</div>
</div>
</div>
<div class="tools-wrapper">
<div class="tools">
<div class="tool active" data-tool="pencil"><img draggable="false" src="icons/draw.svg"></div>
<div class="tool" data-tool="ruler"><img draggable="false" src="icons/ruler.svg"></div>
<div class="tool" data-tool="eraser"><img draggable="false" src="icons/erase.svg"></div>
<div class="tool" data-tool="undo"><img draggable="false" src="icons/undo.svg"></div>
<!-- <div class="tool" data-tool="redo"><img draggable="false" src="icons/redo.svg"></div> -->
</div>
<div class="tools-wrapper">
<div class="tools">
<div class="tool active" data-tool="pencil"><img draggable="false" src="icons/draw.svg"></div>
<div class="tool" data-tool="ruler"><img draggable="false" src="icons/ruler.svg"></div>
<div class="tool" data-tool="eraser"><img draggable="false" src="icons/erase.svg"></div>
<div class="tool" data-tool="undo"><img draggable="false" src="icons/undo.svg"></div>
<!-- <div class="tool" data-tool="redo"><img draggable="false" src="icons/redo.svg"></div> -->
</div>
<div class="phone-extra-controls">
<img draggable="false" src="icons/cheeseburga.svg">
<div class="phone-extra-controls">
<img draggable="false" src="icons/cheeseburga.svg">
</div>
</div>
</div>
<div class="offline-toast hidden">
Whiteboard offline
</div>
</body>
</html>

143
client/index.js

@ -0,0 +1,143 @@ @@ -0,0 +1,143 @@
// NEXT: fire events for brush changes
document.addEventListener('DOMContentLoaded', main);
const config = {
ws_url: 'ws://192.168.100.2/ws/',
ping_url: 'http://192.168.100.2/api/ping',
image_url: 'http://192.168.100.2/images/',
sync_timeout: 1000,
ws_reconnect_timeout: 2000,
brush_preview_timeout: 1000,
second_finger_timeout: 500,
buffer_first_touchmoves: 5,
debug_print: true,
min_zoom: 0.01,
max_zoom: 100.0,
initial_offline_timeout: 1000,
default_color: 0x00,
default_width: 8,
};
const EVENT = Object.freeze({
PREDRAW: 10,
SET_COLOR: 11,
SET_WIDTH: 12,
STROKE: 20,
RULER: 21, /* gets re-written with EVENT.STROKE before sending to server */
UNDO: 30,
REDO: 31,
IMAGE: 40,
IMAGE_MOVE: 41,
ERASER: 50,
});
const MESSAGE = Object.freeze({
INIT: 100,
SYN: 101,
ACK: 102,
FULL: 103,
FIRE: 104,
JOIN: 105,
});
function main() {
const state = {
'online': false,
'me': null,
'canvas': {
'offset': { 'x': 0, 'y': 0 },
'zoom': 1.0,
},
'cursor': {
'x': 0,
'y': 0,
},
'sn': 0,
'lsn': 0,
'server_lsn': 0,
'touch': {
'moves': 0,
'drawing': false,
'moving': false,
'waiting_for_second_finger': false,
'first_finger_position': null,
'second_finger_position': null,
'buffered': [],
'ids': [],
},
'moving': false,
'drawing': false,
'spacedown': false,
'current_strokes': {},
'queue': [],
'events': [],
'tools': {
'active': null,
'active_element': null,
},
'colors': {
'active_element': null,
},
'timers': {
'hide_preview': null,
'offline_toast': null,
'raf': false,
},
'players': {},
};
const context = {
'canvas': null,
'gl': null,
'programs': {},
'buffers': {},
'locations': {},
'textures': {},
'dynamic_positions': {},
'dynamic_colors': {},
'quad_positions': [],
'quad_texcoords': [],
'static_positions': [],
'static_colors': [],
'static_positions_f32': new Float32Array(0),
'dynamic_positions_f32': new Float32Array(0),
'static_colors_u8': new Uint8Array(0),
'dynamic_colors_u8': new Uint8Array(0),
'quad_positions_f32': new Float32Array(0),
'quad_texcoords_f32': new Float32Array(0),
'bgcolor': {'r': 1.0, 'g': 1.0, 'b': 1.0},
};
const url = new URL(window.location.href);
const parts = url.pathname.split('/');
state.desk_id = parts.length > 0 ? parts[parts.length - 1] : 0;
init_webgl(state, context);
init_listeners(state, context);
init_tools(state);
ws_connect(state, context, true);
schedule_draw(state, context);
state.timers.offline_toast = setTimeout(() => ui_offline(), config.initial_offline_timeout);
}

7
client/tools.js

@ -20,7 +20,7 @@ function switch_color(state, item) { @@ -20,7 +20,7 @@ function switch_color(state, item) {
if (state.me in state.players) {
const color_u32 = color_to_u32(color);
state.players[state.me].color = color_u32
fire_event(color_event(color_u32));
fire_event(state, color_event(color_u32));
}
state.colors.active_element = item;
@ -42,7 +42,10 @@ function hide_stroke_preview() { @@ -42,7 +42,10 @@ function hide_stroke_preview() {
}
function switch_stroke_width(e, state) {
if (!state.online) return;
const value = e.target.value;
state.players[state.me].width = value;
show_stroke_preview(state, value);
@ -55,7 +58,7 @@ function switch_stroke_width(e, state) { @@ -55,7 +58,7 @@ function switch_stroke_width(e, state) {
function broadcast_stroke_width(e, state) {
const value = e.target.value;
fire_event(width_event(value));
fire_event(state, width_event(value));
}
function init_tools(state) {

269
client/webgl.js

@ -1,269 +0,0 @@ @@ -1,269 +0,0 @@
// NEXT: fire events for brush changes
document.addEventListener('DOMContentLoaded', main);
function draw(state, context) {
state.timers.raf = false;
const gl = context.gl;
const width = window.innerWidth;
const height = window.innerHeight;
let locations;
let buffers;
gl.viewport(0, 0, context.canvas.width, context.canvas.height);
gl.clearColor(context.bgcolor.r, context.bgcolor.g, context.bgcolor.b, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// Draw images
locations = context.locations['quad'];
buffers = context.buffers['quad'];
gl.useProgram(context.programs['quad']);
gl.enableVertexAttribArray(locations['a_pos']);
gl.enableVertexAttribArray(locations['a_texcoord']);
gl.uniform2f(locations['u_res'], context.canvas.width, context.canvas.height);
gl.uniform2f(locations['u_scale'], state.canvas.zoom, state.canvas.zoom);
gl.uniform2f(locations['u_translation'], state.canvas.offset.x, state.canvas.offset.y);
gl.uniform1i(locations['u_layer'], 0);
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_pos']);
gl.vertexAttribPointer(locations['a_pos'], 2, gl.FLOAT, false, 0, 0);
gl.bufferData(gl.ARRAY_BUFFER, context.quad_positions_f32, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_texcoord']);
gl.vertexAttribPointer(locations['a_texcoord'], 2, gl.FLOAT, false, 0, 0);
gl.bufferData(gl.ARRAY_BUFFER, context.quad_texcoords_f32, gl.STATIC_DRAW);
let tex_index = 0;
for (const key in context.textures) {
gl.bindTexture(gl.TEXTURE_2D, context.textures[key]);
gl.drawArrays(gl.TRIANGLES, tex_index * 6, 6);
++tex_index;
}
// Draw strokes
locations = context.locations['stroke'];
buffers = context.buffers['stroke'];
gl.useProgram(context.programs['stroke']);
gl.enableVertexAttribArray(locations['a_pos']);
gl.enableVertexAttribArray(locations['a_color']);
gl.uniform2f(locations['u_res'], context.canvas.width, context.canvas.height);
gl.uniform2f(locations['u_scale'], state.canvas.zoom, state.canvas.zoom);
gl.uniform2f(locations['u_translation'], state.canvas.offset.x, state.canvas.offset.y);
gl.uniform1i(locations['u_layer'], 1);
const total_pos_size = context.static_positions_f32.byteLength + context.dynamic_positions_f32.byteLength;
const total_color_size = context.static_colors_u8.byteLength + context.dynamic_colors_u8.byteLength;
const total_point_count = (context.static_positions.length + total_dynamic_positions(context)) / 2;
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_pos']);
gl.vertexAttribPointer(locations['a_pos'], 2, gl.FLOAT, false, 0, 0);
gl.bufferData(gl.ARRAY_BUFFER, total_pos_size, gl.DYNAMIC_DRAW);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, context.static_positions_f32);
gl.bufferSubData(gl.ARRAY_BUFFER, context.static_positions_f32.byteLength, context.dynamic_positions_f32);
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_color']);
gl.vertexAttribPointer(locations['a_color'], 3, gl.UNSIGNED_BYTE, true, 0, 0);
gl.bufferData(gl.ARRAY_BUFFER, total_color_size, gl.DYNAMIC_DRAW);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, context.static_colors_u8);
gl.bufferSubData(gl.ARRAY_BUFFER, context.static_colors_u8.byteLength, context.dynamic_colors_u8);
gl.drawArrays(gl.TRIANGLES, 0, total_point_count);
}
const config = {
ws_url: 'ws://192.168.100.2/ws/',
image_url: 'http://192.168.100.2/images/',
sync_timeout: 1000,
ws_reconnect_timeout: 10000,
brush_preview_timeout: 1000,
second_finger_timeout: 500,
buffer_first_touchmoves: 5,
debug_print: true,
min_zoom: 0.01,
max_zoom: 100.0,
default_color: 0x00,
default_width: 8,
};
const EVENT = Object.freeze({
PREDRAW: 10,
SET_COLOR: 11,
SET_WIDTH: 12,
STROKE: 20,
RULER: 21, /* gets re-written with EVENT.STROKE before sending to server */
UNDO: 30,
REDO: 31,
IMAGE: 40,
IMAGE_MOVE: 41,
ERASER: 50,
});
const MESSAGE = Object.freeze({
INIT: 100,
SYN: 101,
ACK: 102,
FULL: 103,
FIRE: 104,
JOIN: 105,
});
function event_size(event) {
let size = 1 + 3; // type + padding
switch (event.type) {
case EVENT.PREDRAW: {
size += 4 * 2;
break;
}
case EVENT.SET_COLOR: {
size += 4;
break;
}
case EVENT.SET_WIDTH: {
size += 2;
break;
}
case EVENT.STROKE: {
size += 4 + 2 + 2 + 4 + event.points.length * 4 * 2; // u32 stroke id + u16 (count) + u16 (width) + u32 (color + count * (f32, f32) points
break;
}
case EVENT.UNDO:
case EVENT.REDO: {
break;
}
case EVENT.IMAGE:
case EVENT.IMAGE_MOVE: {
size += 4 + 4 + 4; // file id + x + y
break;
}
case EVENT.ERASER: {
size += 4; // stroke id
break;
}
default: {
console.error('fuck');
}
}
return size;
}
function main() {
const state = {
'me': 333,
'canvas': {
'offset': { 'x': 0, 'y': 0 },
'zoom': 1.0,
},
'cursor': {
'x': 0,
'y': 0,
},
'sn': 0,
'lsn': 0,
'server_lsn': 0,
'touch': {
'moves': 0,
'drawing': false,
'moving': false,
'waiting_for_second_finger': false,
'first_finger_position': null,
'second_finger_position': null,
'buffered': [],
'ids': [],
},
'moving': false,
'drawing': false,
'spacedown': false,
'current_strokes': {},
'fire_queue': [],
'queue': [],
'events': [],
'tools': {
'active': null,
'active_element': null,
},
'colors': {
'active_element': null,
},
'timers': {
'ws_reconnect': null,
'hide_preview': null,
'raf': false,
},
'players': {},
};
const context = {
'canvas': null,
'gl': null,
'programs': {},
'buffers': {},
'locations': {},
'textures': {},
'dynamic_positions': {},
'dynamic_colors': {},
'quad_positions': [],
'quad_texcoords': [],
'static_positions': [],
'static_colors': [],
'static_positions_f32': new Float32Array(0),
'dynamic_positions_f32': new Float32Array(0),
'static_colors_u8': new Uint8Array(0),
'dynamic_colors_u8': new Uint8Array(0),
'quad_positions_f32': new Float32Array(0),
'quad_texcoords_f32': new Float32Array(0),
'bgcolor': {'r': 1.0, 'g': 1.0, 'b': 1.0},
};
const url = new URL(window.location.href);
const parts = url.pathname.split('/');
state.desk_id = parts.length > 0 ? parts[parts.length - 1] : 0;
init_webgl(state, context);
init_listeners(state, context);
init_tools(state);
ws_connect(state, context, true);
schedule_draw(state, context);
}
function schedule_draw(state, context) {
if (!state.timers.raf) {
window.requestAnimationFrame(() => draw(state, context));
state.timers.raf = true;
}
}

83
client/webgl_draw.js

@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
function schedule_draw(state, context) {
if (!state.timers.raf) {
window.requestAnimationFrame(() => draw(state, context));
state.timers.raf = true;
}
}
function draw(state, context) {
state.timers.raf = false;
const gl = context.gl;
const width = window.innerWidth;
const height = window.innerHeight;
let locations;
let buffers;
gl.viewport(0, 0, context.canvas.width, context.canvas.height);
gl.clearColor(context.bgcolor.r, context.bgcolor.g, context.bgcolor.b, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// Draw images
locations = context.locations['quad'];
buffers = context.buffers['quad'];
gl.useProgram(context.programs['quad']);
gl.enableVertexAttribArray(locations['a_pos']);
gl.enableVertexAttribArray(locations['a_texcoord']);
gl.uniform2f(locations['u_res'], context.canvas.width, context.canvas.height);
gl.uniform2f(locations['u_scale'], state.canvas.zoom, state.canvas.zoom);
gl.uniform2f(locations['u_translation'], state.canvas.offset.x, state.canvas.offset.y);
gl.uniform1i(locations['u_layer'], 0);
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_pos']);
gl.vertexAttribPointer(locations['a_pos'], 2, gl.FLOAT, false, 0, 0);
gl.bufferData(gl.ARRAY_BUFFER, context.quad_positions_f32, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_texcoord']);
gl.vertexAttribPointer(locations['a_texcoord'], 2, gl.FLOAT, false, 0, 0);
gl.bufferData(gl.ARRAY_BUFFER, context.quad_texcoords_f32, gl.STATIC_DRAW);
let tex_index = 0;
for (const key in context.textures) {
gl.bindTexture(gl.TEXTURE_2D, context.textures[key]);
gl.drawArrays(gl.TRIANGLES, tex_index * 6, 6);
++tex_index;
}
// Draw strokes
locations = context.locations['stroke'];
buffers = context.buffers['stroke'];
gl.useProgram(context.programs['stroke']);
gl.enableVertexAttribArray(locations['a_pos']);
gl.enableVertexAttribArray(locations['a_color']);
gl.uniform2f(locations['u_res'], context.canvas.width, context.canvas.height);
gl.uniform2f(locations['u_scale'], state.canvas.zoom, state.canvas.zoom);
gl.uniform2f(locations['u_translation'], state.canvas.offset.x, state.canvas.offset.y);
gl.uniform1i(locations['u_layer'], 1);
const total_pos_size = context.static_positions_f32.byteLength + context.dynamic_positions_f32.byteLength;
const total_color_size = context.static_colors_u8.byteLength + context.dynamic_colors_u8.byteLength;
const total_point_count = (context.static_positions.length + total_dynamic_positions(context)) / 2;
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_pos']);
gl.vertexAttribPointer(locations['a_pos'], 2, gl.FLOAT, false, 0, 0);
gl.bufferData(gl.ARRAY_BUFFER, total_pos_size, gl.DYNAMIC_DRAW);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, context.static_positions_f32);
gl.bufferSubData(gl.ARRAY_BUFFER, context.static_positions_f32.byteLength, context.dynamic_positions_f32);
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_color']);
gl.vertexAttribPointer(locations['a_color'], 3, gl.UNSIGNED_BYTE, true, 0, 0);
gl.bufferData(gl.ARRAY_BUFFER, total_color_size, gl.DYNAMIC_DRAW);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, context.static_colors_u8);
gl.bufferSubData(gl.ARRAY_BUFFER, context.static_colors_u8.byteLength, context.dynamic_colors_u8);
gl.drawArrays(gl.TRIANGLES, 0, total_point_count);
}

19
client/webgl_geometry.js

@ -111,7 +111,22 @@ function pop_stroke(state, context) { @@ -111,7 +111,22 @@ function pop_stroke(state, context) {
// }
}
function get_static_stroke(state) {
if (!state.online) {
return null;
}
return {
'color': state.players[state.me].color,
'width': state.players[state.me].width,
'points': process_stroke(state.current_strokes[state.me].points),
'user_id': state.me,
};
}
function add_static_stroke(state, context, stroke, relax = false) {
if (!state.online || !stroke) return;
push_stroke(state, stroke, context.static_positions, context.static_colors);
if (!relax) {
@ -163,6 +178,8 @@ function recompute_dynamic_data(state, context) { @@ -163,6 +178,8 @@ function recompute_dynamic_data(state, context) {
}
function update_dynamic_stroke(state, context, player_id, point) {
if (!state.online) return;
if (!(player_id in state.current_strokes)) {
state.current_strokes[player_id] = {
'points': [],
@ -188,6 +205,8 @@ function update_dynamic_stroke(state, context, player_id, point) { @@ -188,6 +205,8 @@ function update_dynamic_stroke(state, context, player_id, point) {
}
function clear_dynamic_stroke(state, context, player_id) {
if (!state.online) return;
if (player_id in state.current_strokes) {
state.current_strokes[player_id].points.length = 0;
state.current_strokes[player_id].color = state.players[state.me].color;

24
client/webgl_listeners.js

@ -84,7 +84,7 @@ function mousemove(e, state, context) { @@ -84,7 +84,7 @@ function mousemove(e, state, context) {
if (state.drawing) {
update_dynamic_stroke(state, context, state.me, canvasp);
fire_event(predraw_event(canvasp.x, canvasp.y));
fire_event(state, predraw_event(canvasp.x, canvasp.y));
do_draw = true;
}
@ -107,21 +107,17 @@ function mouseup(e, state, context) { @@ -107,21 +107,17 @@ function mouseup(e, state, context) {
}
if (state.drawing) {
const stroke = {
'color': state.players[state.me].color,
'width': state.players[state.me].width,
'points': process_stroke(state.current_strokes[state.me].points),
'user_id': state.me,
};
const stroke = get_static_stroke(state);
add_static_stroke(state, context, stroke);
queue_event(state, stroke_event(state));
clear_dynamic_stroke(state, context, state.me);
if (stroke) {
add_static_stroke(state, context, stroke);
queue_event(state, stroke_event(state));
clear_dynamic_stroke(state, context, state.me);
schedule_draw(state, context);
}
state.drawing = false;
schedule_draw(state, context);
return;
}
}
@ -238,14 +234,14 @@ function touchmove(e, state, context) { @@ -238,14 +234,14 @@ function touchmove(e, state, context) {
// BUG: can't see these on other clients!!
for (const p of state.touch.buffered) {
update_dynamic_stroke(state, context, state.me, p);
fire_event(predraw_event(canvasp.x, canvasp.y));
fire_event(state, predraw_event(canvasp.x, canvasp.y));
}
state.touch.buffered.length = 0;
}
update_dynamic_stroke(state, context, state.me, canvasp);
fire_event(predraw_event(canvasp.x, canvasp.y));
fire_event(state, predraw_event(canvasp.x, canvasp.y));
schedule_draw(state, context);
}

34
client/websocket.js

@ -6,20 +6,35 @@ @@ -6,20 +6,35 @@
//
// Details best described here: https://github.com/kee-org/KeeFox/issues/189
function ws_connect(state, context, first_connect = false) {
async function ws_connect(state, context, first_connect = false) {
const session_id = localStorage.getItem('sessionId') || '0';
const desk_id = state.desk_id;
ws = new WebSocket(`${config.ws_url}?deskId=${desk_id}&sessionId=${session_id}`);
ws.addEventListener('open', () => on_open(state));
ws.addEventListener('message', (e) => on_message(state, context, e));
ws.addEventListener('error', () => on_error(state, context));
ws.addEventListener('close', () => on_close(state, context));
try {
const resp = await fetch(config.ping_url);
if (resp.ok) {
const text = await resp.text();
if (text === 'pong') {
ws = new WebSocket(`${config.ws_url}?deskId=${desk_id}&sessionId=${session_id}`);
ws.addEventListener('open', () => on_open(state));
ws.addEventListener('message', (e) => on_message(state, context, e));
ws.addEventListener('error', () => on_error(state, context));
ws.addEventListener('close', () => on_close(state, context));
return;
}
}
} catch (e) {}
state.timers.offline_toast = setTimeout(() => ws_connect(state, context, first_connect), config.ws_reconnect_timeout);
}
function on_open(state) {
clearTimeout(state.timers.ws_reconnect);
clearTimeout(state.timers.offline_toast);
ui_online();
if (config.debug_print) console.debug('open')
}
@ -47,9 +62,10 @@ async function on_message(state, context, event) { @@ -47,9 +62,10 @@ async function on_message(state, context, event) {
}
function on_close(state, context) {
state.timers.offline_toast = setTimeout(() => ui_offline(), config.initial_offline_timeout);
ws = null;
if (config.debug_print) console.debug('close');
state.timers.ws_reconnect = setTimeout(() => ws_connect(state, context, false), config.ws_reconnect_timeout);
ws_connect(state, context, false);
}
function on_error(state, context) {

Loading…
Cancel
Save