mirror of
https://github.com/lxsang/ant-http
synced 2025-01-08 05:58:22 +01:00
1172 lines
51 KiB
JavaScript
1172 lines
51 KiB
JavaScript
|
/**
|
||
|
* This a rewrite of GTK broadway.js
|
||
|
*
|
||
|
* OS.js - JavaScript Cloud/Web Desktop Platform
|
||
|
*
|
||
|
* Copyright (c) 2011-2016, Anders Evenrud <andersevenrud@gmail.com>
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* Redistribution and use in source and binary forms, with or without
|
||
|
* modification, are permitted provided that the following conditions are met:
|
||
|
*
|
||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||
|
* list of conditions and the following disclaimer.
|
||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||
|
* this list of conditions and the following disclaimer in the documentation
|
||
|
* and/or other materials provided with the distribution.
|
||
|
*
|
||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
*
|
||
|
* @author Anders Evenrud <andersevenrud@gmail.com>
|
||
|
* @licence Simplified BSD License
|
||
|
*/
|
||
|
(function() {
|
||
|
|
||
|
var connected = false;
|
||
|
var lastTimeStamp = 0;
|
||
|
var lastState;
|
||
|
var inputSocket = null;
|
||
|
var globalOpts = {};
|
||
|
var connection;
|
||
|
var outstandingCommands = [];
|
||
|
var keyDownList = [];
|
||
|
var lastSerial = 0;
|
||
|
var surfaces = {};
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// CONSTANTS
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
var ON_KEYDOWN = 1 << 0; /* Report on keydown, otherwise wait until keypress */
|
||
|
var GDK_CROSSING_NORMAL = 0;
|
||
|
var GDK_CROSSING_GRAB = 1;
|
||
|
var GDK_CROSSING_UNGRAB = 2;
|
||
|
|
||
|
// GdkModifierType
|
||
|
var GDK_SHIFT_MASK = 1 << 0;
|
||
|
var GDK_LOCK_MASK = 1 << 1;
|
||
|
var GDK_CONTROL_MASK = 1 << 2;
|
||
|
var GDK_MOD1_MASK = 1 << 3;
|
||
|
var GDK_MOD2_MASK = 1 << 4;
|
||
|
var GDK_MOD3_MASK = 1 << 5;
|
||
|
var GDK_MOD4_MASK = 1 << 6;
|
||
|
var GDK_MOD5_MASK = 1 << 7;
|
||
|
var GDK_BUTTON1_MASK = 1 << 8;
|
||
|
var GDK_BUTTON2_MASK = 1 << 9;
|
||
|
var GDK_BUTTON3_MASK = 1 << 10;
|
||
|
var GDK_BUTTON4_MASK = 1 << 11;
|
||
|
var GDK_BUTTON5_MASK = 1 << 12;
|
||
|
var GDK_SUPER_MASK = 1 << 26;
|
||
|
var GDK_HYPER_MASK = 1 << 27;
|
||
|
var GDK_META_MASK = 1 << 28;
|
||
|
var GDK_RELEASE_MASK = 1 << 30;
|
||
|
|
||
|
var specialKeyTable = {
|
||
|
8: [0xFF08, ON_KEYDOWN], // BACKSPACE
|
||
|
13: [0xFF0D, ON_KEYDOWN], // ENTER
|
||
|
|
||
|
// This generates a keyDown and keyPress in Opera
|
||
|
9: [0xFF09, ON_KEYDOWN], // TAB
|
||
|
|
||
|
27: 0xFF1B, // ESCAPE
|
||
|
46: 0xFFFF, // DELETE
|
||
|
36: 0xFF50, // HOME
|
||
|
35: 0xFF57, // END
|
||
|
33: 0xFF55, // PAGE_UP
|
||
|
34: 0xFF56, // PAGE_DOWN
|
||
|
45: 0xFF63, // INSERT
|
||
|
37: 0xFF51, // LEFT
|
||
|
38: 0xFF52, // UP
|
||
|
39: 0xFF53, // RIGHT
|
||
|
40: 0xFF54, // DOWN
|
||
|
16: 0xFFE1, // SHIFT
|
||
|
17: 0xFFE3, // CONTROL
|
||
|
18: 0xFFE9, // Left ALT (Mac Command)
|
||
|
112: 0xFFBE, // F1
|
||
|
113: 0xFFBF, // F2
|
||
|
114: 0xFFC0, // F3
|
||
|
115: 0xFFC1, // F4
|
||
|
116: 0xFFC2, // F5
|
||
|
117: 0xFFC3, // F6
|
||
|
118: 0xFFC4, // F7
|
||
|
119: 0xFFC5, // F8
|
||
|
120: 0xFFC6, // F9
|
||
|
121: 0xFFC7, // F10
|
||
|
122: 0xFFC8, // F11
|
||
|
123: 0xFFC9 // F12
|
||
|
};
|
||
|
|
||
|
/* Some of the keyboard handling code is from noVNC and
|
||
|
* (c) Joel Martin (github@martintribe.org), used with permission
|
||
|
* Original code at:
|
||
|
* https://github.com/kanaka/noVNC/blob/master/include/input.js
|
||
|
*/
|
||
|
var unicodeTable = {
|
||
|
0x0104: 0x01a1, 0x02D8: 0x01a2, 0x0141: 0x01a3, 0x013D: 0x01a5, 0x015A: 0x01a6, 0x0160: 0x01a9, 0x015E: 0x01aa, 0x0164: 0x01ab, 0x0179: 0x01ac, 0x017D: 0x01ae, 0x017B: 0x01af, 0x0105: 0x01b1, 0x02DB: 0x01b2, 0x0142: 0x01b3, 0x013E: 0x01b5, 0x015B: 0x01b6, 0x02C7: 0x01b7, 0x0161: 0x01b9, 0x015F: 0x01ba, 0x0165: 0x01bb, 0x017A: 0x01bc, 0x02DD: 0x01bd, 0x017E: 0x01be, 0x017C: 0x01bf, 0x0154: 0x01c0, 0x0102: 0x01c3, 0x0139: 0x01c5, 0x0106: 0x01c6, 0x010C: 0x01c8, 0x0118: 0x01ca, 0x011A: 0x01cc, 0x010E: 0x01cf, 0x0110: 0x01d0, 0x0143: 0x01d1, 0x0147: 0x01d2, 0x0150: 0x01d5, 0x0158: 0x01d8, 0x016E: 0x01d9, 0x0170: 0x01db, 0x0162: 0x01de, 0x0155: 0x01e0, 0x0103: 0x01e3, 0x013A: 0x01e5, 0x0107: 0x01e6, 0x010D: 0x01e8, 0x0119: 0x01ea, 0x011B: 0x01ec, 0x010F: 0x01ef, 0x0111: 0x01f0, 0x0144: 0x01f1, 0x0148: 0x01f2, 0x0151: 0x01f5, 0x0171: 0x01fb, 0x0159: 0x01f8, 0x016F: 0x01f9, 0x0163: 0x01fe, 0x02D9: 0x01ff, 0x0126: 0x02a1, 0x0124: 0x02a6, 0x0130: 0x02a9, 0x011E: 0x02ab, 0x0134: 0x02ac, 0x0127: 0x02b1, 0x0125: 0x02b6, 0x0131: 0x02b9, 0x011F: 0x02bb, 0x0135: 0x02bc, 0x010A: 0x02c5, 0x0108: 0x02c6, 0x0120: 0x02d5, 0x011C: 0x02d8, 0x016C: 0x02dd, 0x015C: 0x02de, 0x010B: 0x02e5, 0x0109: 0x02e6, 0x0121: 0x02f5, 0x011D: 0x02f8, 0x016D: 0x02fd, 0x015D: 0x02fe, 0x0138: 0x03a2, 0x0156: 0x03a3, 0x0128: 0x03a5, 0x013B: 0x03a6, 0x0112: 0x03aa, 0x0122: 0x03ab, 0x0166: 0x03ac, 0x0157: 0x03b3, 0x0129: 0x03b5, 0x013C: 0x03b6, 0x0113: 0x03ba, 0x0123: 0x03bb, 0x0167: 0x03bc, 0x014A: 0x03bd, 0x014B: 0x03bf, 0x0100: 0x03c0, 0x012E: 0x03c7, 0x0116: 0x03cc, 0x012A: 0x03cf, 0x0145: 0x03d1, 0x014C: 0x03d2, 0x0136: 0x03d3, 0x0172: 0x03d9, 0x0168: 0x03dd, 0x016A: 0x03de, 0x0101: 0x03e0, 0x012F: 0x03e7, 0x0117: 0x03ec, 0x012B: 0x03ef, 0x0146: 0x03f1, 0x014D: 0x03f2, 0x0137: 0x03f3, 0x0173: 0x03f9, 0x0169: 0x03fd, 0x016B: 0x03fe, 0x1E02: 0x1001e02, 0x1E03: 0x1001e03, 0x1E0A: 0x1001e0a, 0x1E80: 0x1001e80, 0x1E82: 0x1001e82, 0x1E0B: 0x1001e0b, 0x1EF2: 0x1001ef2, 0x1E1E: 0x1001e1e, 0x1E1F: 0x1001e1f, 0x1E40: 0x1001e40, 0x1E41: 0x1001e41, 0x1E56: 0x1001e56, 0x1E81: 0x1001e81, 0x1E57: 0x1001e57, 0x1E83: 0x1001e83, 0x1E60: 0x1001e60, 0x1EF3: 0x1001ef3, 0x1E84: 0x1001e84, 0x1E85: 0x1001e85, 0x1E61: 0x1001e61, 0x0174: 0x1000174, 0x1E6A: 0x1001e6a, 0x0176: 0x1000176, 0x0175: 0x1000175, 0x1E6B: 0x1001e6b, 0x0177: 0x1000177, 0x0152: 0x13bc, 0x0153: 0x13bd, 0x0178: 0x13be, 0x203E: 0x047e, 0x3002: 0x04a1, 0x300C: 0x04a2, 0x300D: 0x04a3, 0x3001: 0x04a4, 0x30FB: 0x04a5, 0x30F2: 0x04a6, 0x30A1: 0x04a7, 0x30A3: 0x04a8, 0x30A5: 0x04a9, 0x30A7: 0x04aa, 0x30A9: 0x04ab, 0x30E3: 0x04ac, 0x30E5: 0x04ad, 0x30E7: 0x04ae, 0x30C3: 0x04af, 0x30FC: 0x04b0, 0x30A2: 0x04b1, 0x30A4: 0x04b2, 0x30A6: 0x04b3, 0x30A8: 0x04b4, 0x30AA: 0x04b5, 0x30AB: 0x04b6, 0x30AD: 0x04b7, 0x30AF: 0x04b8, 0x30B1: 0x04b9, 0x30B3: 0x04ba, 0x30B5: 0x04bb, 0x30B7: 0x04bc, 0x30B9: 0x04bd, 0x30BB: 0x04be, 0x30BD: 0x04bf, 0x30BF: 0x04c0, 0x30C1: 0x04c1, 0x30C4: 0x04c2, 0x30C6: 0x04c3, 0x30C8: 0x04c4, 0x30CA: 0x04c5, 0x30CB: 0x04c6, 0x30CC: 0x04c7, 0x30CD: 0x04c8, 0x30CE: 0x04c9, 0x30CF: 0x04ca, 0x30D2: 0x04cb, 0x30D5: 0x04cc, 0x30D8: 0x04cd, 0x30DB: 0x04ce, 0x30DE: 0x04cf, 0x30DF: 0x04d0, 0x30E0: 0x04d1, 0x30E1: 0x04d2, 0x30E2: 0x04d3, 0x30E4: 0x04d4, 0x30E6: 0x04d5, 0x30E8: 0x04d6, 0x30E9: 0x04d7, 0x30EA: 0x04d8, 0x30EB: 0x04d9, 0x30EC: 0x04da, 0x30ED: 0x04db, 0x30EF: 0x04dc, 0x30F3: 0x04dd, 0x309B: 0x04de, 0x309C: 0x04df, 0x06F0: 0x10006f0, 0x06F1: 0x10006f1, 0x06F2: 0x10006f2, 0x06F3: 0x10006f3, 0x06F4: 0x10006f4, 0x06F5: 0x10006f5, 0x06F6: 0x10006f6, 0x06F7: 0x10006f7, 0x06F8: 0x10006f8, 0x06F9: 0x10006f9, 0x066A: 0x100066a, 0x0670: 0x1000670, 0x0679: 0x1000679, 0x067E: 0x100067e, 0x0686: 0x1000686, 0x0688: 0x1000688, 0x0691: 0x1000691, 0x060C: 0x05ac, 0x06D4: 0x10006d4, 0x0660: 0x1000660, 0x0661: 0x1000661, 0x0662: 0x1000662, 0x0663: 0x1000663, 0x0664: 0x1000664, 0x0665: 0x1000665, 0x0666: 0x1000666, 0x0667: 0x1000667, 0x0668: 0x1000668, 0x0669: 0x1000669, 0x061B: 0x05bb, 0x061F: 0x05bf, 0x0621: 0x05c1, 0x0622: 0x05c2, 0x0623: 0x05c3, 0x0624: 0x05c4, 0x0625: 0x05c5, 0x0626: 0x05c6, 0x0627: 0x05c7, 0x0628: 0
|
||
|
};
|
||
|
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// HELPERS
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
function getKeysymSpecial(ev) {
|
||
|
if (ev.keyCode in specialKeyTable) {
|
||
|
var r = specialKeyTable[ev.keyCode];
|
||
|
var flags = 0;
|
||
|
if (typeof r !== 'number') {
|
||
|
flags = r[1];
|
||
|
r = r[0];
|
||
|
}
|
||
|
if (ev.type === 'keydown' || flags & ON_KEYDOWN) return r;
|
||
|
}
|
||
|
if (!ev.ctrlKey && !ev.altKey) return null;
|
||
|
|
||
|
var keysym = getEventKeySym(ev);
|
||
|
|
||
|
/* Remap symbols */
|
||
|
switch (keysym) {
|
||
|
case 186 : keysym = 59; break; // ; (IE)
|
||
|
case 187 : keysym = 61; break; // = (IE)
|
||
|
case 188 : keysym = 44; break; // , (Mozilla, IE)
|
||
|
case 109 : // - (Mozilla, Opera)
|
||
|
if (true /* TODO: check if browser is firefox or opera */)
|
||
|
keysym = 45;
|
||
|
break;
|
||
|
case 189 : keysym = 45; break; // - (IE)
|
||
|
case 190 : keysym = 46; break; // . (Mozilla, IE)
|
||
|
case 191 : keysym = 47; break; // / (Mozilla, IE)
|
||
|
case 192 : keysym = 96; break; // ` (Mozilla, IE)
|
||
|
case 219 : keysym = 91; break; // [ (Mozilla, IE)
|
||
|
case 220 : keysym = 92; break; // \ (Mozilla, IE)
|
||
|
case 221 : keysym = 93; break; // ] (Mozilla, IE)
|
||
|
case 222 : keysym = 39; break; // ' (Mozilla, IE)
|
||
|
}
|
||
|
|
||
|
/* Remap shifted and unshifted keys */
|
||
|
if (!!ev.shiftKey) {
|
||
|
switch (keysym) {
|
||
|
case 48 : keysym = 41 ; break; // ) (shifted 0)
|
||
|
case 49 : keysym = 33 ; break; // ! (shifted 1)
|
||
|
case 50 : keysym = 64 ; break; // @ (shifted 2)
|
||
|
case 51 : keysym = 35 ; break; // # (shifted 3)
|
||
|
case 52 : keysym = 36 ; break; // $ (shifted 4)
|
||
|
case 53 : keysym = 37 ; break; // % (shifted 5)
|
||
|
case 54 : keysym = 94 ; break; // ^ (shifted 6)
|
||
|
case 55 : keysym = 38 ; break; // & (shifted 7)
|
||
|
case 56 : keysym = 42 ; break; // * (shifted 8)
|
||
|
case 57 : keysym = 40 ; break; // ( (shifted 9)
|
||
|
case 59 : keysym = 58 ; break; // : (shifted `)
|
||
|
case 61 : keysym = 43 ; break; // + (shifted ;)
|
||
|
case 44 : keysym = 60 ; break; // < (shifted ,)
|
||
|
case 45 : keysym = 95 ; break; // _ (shifted -)
|
||
|
case 46 : keysym = 62 ; break; // > (shifted .)
|
||
|
case 47 : keysym = 63 ; break; // ? (shifted /)
|
||
|
case 96 : keysym = 126; break; // ~ (shifted `)
|
||
|
case 91 : keysym = 123; break; // { (shifted [)
|
||
|
case 92 : keysym = 124; break; // | (shifted \)
|
||
|
case 93 : keysym = 125; break; // } (shifted ])
|
||
|
case 39 : keysym = 34 ; break; // ' (shifted ')
|
||
|
}
|
||
|
} else if ((keysym >= 65) && (keysym <=90)) {
|
||
|
/* Remap unshifted A-Z */
|
||
|
keysym += 32;
|
||
|
} else if (ev.keyLocation === 3) {
|
||
|
// numpad keys
|
||
|
switch (keysym) {
|
||
|
case 96 : keysym = 48; break; // 0
|
||
|
case 97 : keysym = 49; break; // 1
|
||
|
case 98 : keysym = 50; break; // 2
|
||
|
case 99 : keysym = 51; break; // 3
|
||
|
case 100: keysym = 52; break; // 4
|
||
|
case 101: keysym = 53; break; // 5
|
||
|
case 102: keysym = 54; break; // 6
|
||
|
case 103: keysym = 55; break; // 7
|
||
|
case 104: keysym = 56; break; // 8
|
||
|
case 105: keysym = 57; break; // 9
|
||
|
case 109: keysym = 45; break; // -
|
||
|
case 110: keysym = 46; break; // .
|
||
|
case 111: keysym = 47; break; // /
|
||
|
}
|
||
|
}
|
||
|
return keysym;
|
||
|
}
|
||
|
|
||
|
function getEventKeySym(ev) {
|
||
|
if (typeof ev.which !== 'undefined' && ev.which > 0)
|
||
|
return ev.which;
|
||
|
return ev.keyCode;
|
||
|
}
|
||
|
|
||
|
function ignoreKeyEvent(ev) {
|
||
|
// Blarg. Some keys have a different keyCode on keyDown vs keyUp
|
||
|
if (ev.keyCode === 229) {
|
||
|
// French AZERTY keyboard dead key.
|
||
|
// Lame thing is that the respective keyUp is 219 so we can't
|
||
|
// properly ignore the keyUp event
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
function getKeysym(ev) {
|
||
|
var keysym = getEventKeySym(ev);
|
||
|
if ((keysym > 255) && (keysym < 0xFF00)) { // Map Unicode outside Latin 1 to gdk keysyms
|
||
|
keysym = (typeof keysym === 'undefined') ? 0 : unicodeTable[keysym];
|
||
|
}
|
||
|
return keysym;
|
||
|
}
|
||
|
|
||
|
function getRelativeLayer(id, ev, opts) {
|
||
|
opts = opts || {};
|
||
|
|
||
|
var cid = id;
|
||
|
if ( ev.target ) {
|
||
|
var tmp = ev.target.getAttribute('data-surface-id');
|
||
|
if ( tmp ) {
|
||
|
cid = parseInt(tmp, 10);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( cid !== id ) {
|
||
|
var surface = surfaces[cid];
|
||
|
if ( surface && surface.canvas ) {
|
||
|
opts.mx -= surface.canvas.offsetLeft;
|
||
|
opts.my -= surface.canvas.offsetTop;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return cid;
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// WRAPPERS
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
/**
|
||
|
* Command for binary/arraybuffer connection
|
||
|
*/
|
||
|
function BinCommands(message) {
|
||
|
this.arraybuffer = message;
|
||
|
this.u8 = new Uint8Array(message);
|
||
|
this.length = this.u8.length;
|
||
|
this.pos = 0;
|
||
|
}
|
||
|
BinCommands.prototype.get_char = function() {
|
||
|
return String.fromCharCode(this.u8[this.pos++]);
|
||
|
};
|
||
|
BinCommands.prototype.get_bool = function() {
|
||
|
return this.u8[this.pos++] !== 0;
|
||
|
};
|
||
|
BinCommands.prototype.get_flags = function() {
|
||
|
return this.u8[this.pos++];
|
||
|
}
|
||
|
BinCommands.prototype.get_16 = function() {
|
||
|
var v = this.u8[this.pos] + (this.u8[this.pos+1] << 8);
|
||
|
this.pos = this.pos + 2;
|
||
|
return v;
|
||
|
};
|
||
|
BinCommands.prototype.get_16s = function() {
|
||
|
var v = this.get_16 ();
|
||
|
if (v > 32767) return v - 65536;
|
||
|
return v;
|
||
|
};
|
||
|
BinCommands.prototype.get_32 = function() {
|
||
|
var v = this.u8[this.pos] +
|
||
|
(this.u8[this.pos+1] << 8) +
|
||
|
(this.u8[this.pos+2] << 16) +
|
||
|
(this.u8[this.pos+3] << 24);
|
||
|
|
||
|
this.pos = this.pos + 4;
|
||
|
return v;
|
||
|
};
|
||
|
BinCommands.prototype.get_image_url = function() {
|
||
|
var size = this.get_32();
|
||
|
var png_blob = new Blob ([new Uint8Array (this.arraybuffer, this.pos, size)], {type:'image/png'});
|
||
|
var url;
|
||
|
if (window.webkitURL) {
|
||
|
url = window.webkitURL.createObjectURL(png_blob);
|
||
|
} else {
|
||
|
url = window.URL.createObjectURL(png_blob, {oneTimeOnly: true});
|
||
|
}
|
||
|
this.pos = this.pos + size;
|
||
|
return url;
|
||
|
};
|
||
|
BinCommands.prototype.free_image_url = function(url) {
|
||
|
URL.revokeObjectURL(url);
|
||
|
};
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// ACTIONS
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
function doLogin() {
|
||
|
// TODO
|
||
|
}
|
||
|
|
||
|
function doLoggedIn() {
|
||
|
// TODO
|
||
|
}
|
||
|
|
||
|
function doDisconnect() {
|
||
|
// TODO
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Closes a surface
|
||
|
*/
|
||
|
function closeSurface(id) {
|
||
|
if ( surfaces[id] ) {
|
||
|
sendInput('W', [id]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Moves a surface
|
||
|
*/
|
||
|
function moveSurface(id, x, y) {
|
||
|
if ( surfaces[id] ) {
|
||
|
var surface = surfaces[id];
|
||
|
surface.x = x;
|
||
|
surface.y = y;
|
||
|
sendConfigureNotify(surface);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Resizes a surface
|
||
|
*/
|
||
|
function resizeSurface(id, w, h) {
|
||
|
if ( surfaces[id] ) {
|
||
|
var surface = surfaces[id];
|
||
|
surface.width = w;
|
||
|
surface.height = h;
|
||
|
console.log(surface);
|
||
|
sendConfigureNotify(surface);
|
||
|
// sendInput('m', [id, 1, surface.x, surface.y, 2, surface.width, surface.height]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// EVENTS
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
/**
|
||
|
* On Create Surface Event
|
||
|
*/
|
||
|
function onCreateSurface(cmd) {
|
||
|
var id = cmd.get_16();
|
||
|
var x = cmd.get_16s();
|
||
|
var y = cmd.get_16s();
|
||
|
var w = cmd.get_16();
|
||
|
var h = cmd.get_16();
|
||
|
var isTemp = cmd.get_bool();
|
||
|
var surface = { id: id, x: x, y:y, width: w, height: h, isTemp: isTemp };
|
||
|
surface.positioned = isTemp;
|
||
|
surface.drawQueue = [];
|
||
|
surface.transientParent = 0;
|
||
|
surface.visible = false;
|
||
|
surfaces[id] = surface;
|
||
|
sendConfigureNotify(surface);
|
||
|
|
||
|
console.debug('Broadway', 'onCreateSurface()', id, x, y, w, h, isTemp);
|
||
|
if ( isTemp ) {
|
||
|
surface.canvas = document.createElement('canvas');
|
||
|
surface.canvas.width = w;
|
||
|
surface.canvas.height = h;
|
||
|
surface.canvas.style.position = 'absolute';
|
||
|
surface.canvas.style.left = x + 'px';
|
||
|
surface.canvas.style.top = y + 'px';
|
||
|
surface.canvas.style.zIndex = '9999999';
|
||
|
surface.canvas.style.display = 'none';
|
||
|
surface.canvas.setAttribute('data-surface-id', id.toString());
|
||
|
} else {
|
||
|
if ( globalOpts.onCreateSurface ) {
|
||
|
var canvas = globalOpts.onCreateSurface(id, surface);
|
||
|
if ( canvas ) {
|
||
|
canvas.surfaceId = id;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* On Show Surface Event
|
||
|
*/
|
||
|
function onShowSurface(cmd) {
|
||
|
var id = cmd.get_16();
|
||
|
var surface = surfaces[id];
|
||
|
|
||
|
if ( surface ) {
|
||
|
surface.visible = true;
|
||
|
if ( surface.canvas ) {
|
||
|
surface.canvas.style.display = 'inline';
|
||
|
}
|
||
|
|
||
|
console.debug('Broadway', 'onShowSurface()', id);
|
||
|
if ( globalOpts.onShowSurface ) {
|
||
|
globalOpts.onShowSurface(id);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* On Hide Surface Event
|
||
|
*/
|
||
|
function onHideSurface(cmd) {
|
||
|
var id = cmd.get_16();
|
||
|
var surface = surfaces[id];
|
||
|
|
||
|
if ( surface ) {
|
||
|
surface.visible = false;
|
||
|
if ( surface.canvas ) {
|
||
|
surface.canvas.style.display = 'none';
|
||
|
}
|
||
|
|
||
|
console.debug('Broadway', 'onHideSurface()', id);
|
||
|
if ( globalOpts.onHideSurface ) {
|
||
|
globalOpts.onHideSurface(id);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* On Set Transient Event
|
||
|
*/
|
||
|
function onSetTransient(cmd) {
|
||
|
var id = cmd.get_16();
|
||
|
var parentId = cmd.get_16();
|
||
|
var surface = surfaces[id];
|
||
|
|
||
|
if ( surface ) {
|
||
|
console.debug('Broadway', 'onSetTransient()', id, parentId);
|
||
|
|
||
|
surface.transientParent = parentId;
|
||
|
if ( globalOpts.onSetTransient ) {
|
||
|
globalOpts.onSetTransient(id, parentId, surface);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* On Delete Surface Event
|
||
|
*/
|
||
|
function onDeleteSurface(cmd) {
|
||
|
var id = cmd.get_16();
|
||
|
var surface = surfaces[id];
|
||
|
|
||
|
console.debug('Broadway', 'onDeleteSurface()', id);
|
||
|
if ( surface ) {
|
||
|
if ( surface.canvas ) {
|
||
|
if ( surface.canvas.parentNode ) {
|
||
|
surface.canvas.parentNode.removeChild(surface.canvas);
|
||
|
}
|
||
|
} else {
|
||
|
if ( globalOpts.onDeleteSurface ) {
|
||
|
globalOpts.onDeleteSurface(id);
|
||
|
}
|
||
|
}
|
||
|
delete surfaces[id];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* On Move Surface Event
|
||
|
*/
|
||
|
function onMoveSurface(cmd) {
|
||
|
var id = cmd.get_16();
|
||
|
var ops = cmd.get_flags();
|
||
|
var surface = surfaces[id];
|
||
|
|
||
|
if ( !surface ) { return; }
|
||
|
|
||
|
var has_pos = ops & 1;
|
||
|
if (has_pos) {
|
||
|
surface.x = cmd.get_16s();
|
||
|
surface.y = cmd.get_16s();
|
||
|
}
|
||
|
|
||
|
var has_size = ops & 2;
|
||
|
if (has_size) {
|
||
|
surface.width = cmd.get_16();
|
||
|
surface.height = cmd.get_16();
|
||
|
}
|
||
|
|
||
|
console.debug('Broadway', 'onMoveSurface()', id, has_pos, has_size, surface);
|
||
|
if ( surface.isTemp ) {
|
||
|
if ( has_pos ) {
|
||
|
var par = surfaces[surface.transientParent] || {x: 0, y: 0};
|
||
|
console.log([surface.x, par.x], [surface.y, par.y]);
|
||
|
surface.canvas.style.left = (surface.x - par.x) + 'px';
|
||
|
surface.canvas.style.top = (surface.y - par.y) + 'px';
|
||
|
}
|
||
|
} else {
|
||
|
if ( globalOpts.onMoveSurface ) {
|
||
|
globalOpts.onMoveSurface(id, has_pos, has_size, surface);
|
||
|
}
|
||
|
}
|
||
|
sendConfigureNotify(surface);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* On Image Data Event
|
||
|
*/
|
||
|
function onImageData(cmd) {
|
||
|
var q = {
|
||
|
op: 'i',
|
||
|
id: cmd.get_16(),
|
||
|
x: cmd.get_16(),
|
||
|
y: cmd.get_16()
|
||
|
};
|
||
|
|
||
|
var url = cmd.get_image_url();
|
||
|
q.img = new Image();
|
||
|
q.img.src = url;
|
||
|
|
||
|
//console.debug('Broadway', 'onImageData()', url);
|
||
|
surfaces[q.id].drawQueue.push(q);
|
||
|
|
||
|
if (!q.img.complete) {
|
||
|
q.img.onload = function() {
|
||
|
cmd.free_image_url (url);
|
||
|
|
||
|
handleOutstanding();
|
||
|
};
|
||
|
return false;
|
||
|
}
|
||
|
cmd.free_image_url(url);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* On Copy Rects Event
|
||
|
*/
|
||
|
function onCopyRects(cmd) {
|
||
|
var id = cmd.get_16();
|
||
|
if ( surfaces[id] ) {
|
||
|
console.debug('Broadway', 'onCopyRects()', id);
|
||
|
|
||
|
var q = {
|
||
|
op: 'b',
|
||
|
id: id,
|
||
|
rects: []
|
||
|
};
|
||
|
|
||
|
var nrects = cmd.get_16();
|
||
|
for (var r = 0; r < nrects; r++) {
|
||
|
q.rects.push({
|
||
|
x: cmd.get_16(),
|
||
|
y: cmd.get_16(),
|
||
|
w: cmd.get_16(),
|
||
|
h: cmd.get_16()
|
||
|
});
|
||
|
}
|
||
|
|
||
|
q.dx = cmd.get_16s();
|
||
|
q.dy = cmd.get_16s();
|
||
|
surfaces[q.id].drawQueue.push(q);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* On Flush Sufrace Event
|
||
|
*/
|
||
|
function onFlushSurface(cmd) {
|
||
|
var id = cmd.get_16();
|
||
|
var surface = surfaces[id];
|
||
|
|
||
|
if ( surface ) {
|
||
|
//console.debug('Broadway', 'onFlushSurface()', id);
|
||
|
|
||
|
var canvas = surface.canvas;
|
||
|
if ( !surface.isTemp && globalOpts.onFlushSurface ) {
|
||
|
canvas = globalOpts.onFlushSurface(id);
|
||
|
}
|
||
|
|
||
|
if ( surface && canvas ) {
|
||
|
var commands = surface.drawQueue;
|
||
|
var context = canvas.getContext('2d');
|
||
|
context.globalCompositeOperation = 'source-over';
|
||
|
|
||
|
var i = 0;
|
||
|
var cmd, j, rect;
|
||
|
for (i; i < commands.length; i++) {
|
||
|
cmd = commands[i];
|
||
|
switch (cmd.op) {
|
||
|
case 'i': // put image data surface
|
||
|
context.globalCompositeOperation = 'source-over';
|
||
|
context.drawImage(cmd.img, cmd.x, cmd.y);
|
||
|
break;
|
||
|
|
||
|
case 'b': // copy rects
|
||
|
context.save();
|
||
|
context.beginPath();
|
||
|
|
||
|
for (j = 0; j < cmd.rects.length; j++) {
|
||
|
rect = cmd.rects[j];
|
||
|
context.rect(rect.x, rect.y, rect.w, rect.h);
|
||
|
}
|
||
|
|
||
|
context.clip();
|
||
|
context.drawImage(surface.canvas, cmd.dx, cmd.dy);
|
||
|
context.restore();
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
console.warn('Broadway', 'onFlushSurface()', 'Invalid command', cmd.op, cmd);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* On Grab Pointer Event
|
||
|
*/
|
||
|
function onGrabPointer(cmd) {
|
||
|
var id = cmd.get_16();
|
||
|
var ownerEvents = cmd.get_bool();
|
||
|
|
||
|
// TODO
|
||
|
sendInput('g', []);
|
||
|
console.debug('Broadway', 'onGrabPointer()', id, ownerEvents);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* On Ungrab Pointer Event
|
||
|
*/
|
||
|
function onUngrabPointer() {
|
||
|
sendInput('u', []);
|
||
|
|
||
|
// TODO
|
||
|
console.debug('Broadway', 'onUngrabPointer()');
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// ACTIONS
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
/**
|
||
|
* Handle outstanding commands
|
||
|
*/
|
||
|
function handleOutstanding() {
|
||
|
while ( outstandingCommands.length > 0 ) {
|
||
|
var cmd = outstandingCommands.shift();
|
||
|
if ( !handleCommands(cmd) ) {
|
||
|
outstandingCommands.unshift(cmd);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Handles a list of commands
|
||
|
*/
|
||
|
function handleCommands(cmd) {
|
||
|
var mapping = {
|
||
|
'l': doLogin,
|
||
|
'L': doLoggedIn,
|
||
|
'D': doDisconnect,
|
||
|
's': onCreateSurface,
|
||
|
'S': onShowSurface,
|
||
|
'H': onHideSurface,
|
||
|
'p': onSetTransient,
|
||
|
'd': onDeleteSurface,
|
||
|
'm': onMoveSurface,
|
||
|
'i': onImageData,
|
||
|
'b': onCopyRects,
|
||
|
'f': onFlushSurface,
|
||
|
'g': onGrabPointer,
|
||
|
'u': onUngrabPointer
|
||
|
};
|
||
|
|
||
|
while (cmd.pos < cmd.length) {
|
||
|
var command = cmd.get_char();
|
||
|
lastSerial = cmd.get_32();
|
||
|
|
||
|
//console.debug('Broadway', 'handleCommands()', command);
|
||
|
|
||
|
if ( mapping[command] ) {
|
||
|
if ( mapping[command](cmd) === false ) {
|
||
|
return false;
|
||
|
}
|
||
|
} else {
|
||
|
console.error('Invalid command', command);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Send configuration notification
|
||
|
*/
|
||
|
function sendConfigureNotify(surface) {
|
||
|
if ( !connected ) { return; }
|
||
|
|
||
|
sendInput('w', [surface.id, surface.x, surface.y, surface.width, surface.height]);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Send input
|
||
|
*/
|
||
|
function sendInput(cmd, args) {
|
||
|
if ( !connected ) { return; }
|
||
|
|
||
|
if ( inputSocket != null ) {
|
||
|
inputSocket.send(cmd + ([lastSerial, lastTimeStamp].concat(args)).join(','));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update last input state
|
||
|
*/
|
||
|
function updateForEvent(ev) {
|
||
|
lastState &= ~(GDK_SHIFT_MASK|GDK_CONTROL_MASK|GDK_MOD1_MASK);
|
||
|
if (ev.shiftKey) lastState |= GDK_SHIFT_MASK;
|
||
|
if (ev.ctrlKey) lastState |= GDK_CONTROL_MASK;
|
||
|
if (ev.altKey) lastState |= GDK_MOD1_MASK;
|
||
|
lastTimeStamp = ev.timeStamp;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get GDK button mask from DOM Event
|
||
|
*/
|
||
|
function getButtonMask (button) {
|
||
|
if (button === 1) return GDK_BUTTON1_MASK;
|
||
|
if (button === 2) return GDK_BUTTON2_MASK;
|
||
|
if (button === 3) return GDK_BUTTON3_MASK;
|
||
|
if (button === 4) return GDK_BUTTON4_MASK;
|
||
|
if (button === 5) return GDK_BUTTON5_MASK;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get GDK button
|
||
|
*/
|
||
|
function getKeyEvent(keyCode, pop) {
|
||
|
var i, fev = null;
|
||
|
for (i = keyDownList.length-1; i >= 0; i--) {
|
||
|
if ( keyDownList[i].keyCode === keyCode ) {
|
||
|
if ((typeof pop !== 'undefined') && pop)
|
||
|
fev = keyDownList.splice(i, 1)[0];
|
||
|
else
|
||
|
fev = keyDownList[i];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return fev;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Cancels an event
|
||
|
*/
|
||
|
function cancelEvent(ev) {
|
||
|
ev = ev ? ev : window.event;
|
||
|
if (ev.stopPropagation) ev.stopPropagation();
|
||
|
if (ev.preventDefault) ev.preventDefault();
|
||
|
ev.cancelBubble = true;
|
||
|
ev.cancel = true;
|
||
|
ev.returnValue = false;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Copy the key event object
|
||
|
*/
|
||
|
function copyKeyEvent(ev) {
|
||
|
var members = ['type', 'keyCode', 'charCode', 'which', 'altKey', 'ctrlKey', 'shiftKey', 'keyLocation', 'keyIdentifier'];
|
||
|
var i, obj = {};
|
||
|
for (i = 0; i < members.length; i++) {
|
||
|
if (typeof ev[members[i]] !== 'undefined')
|
||
|
obj[members[i]] = ev[members[i]];
|
||
|
}
|
||
|
return obj;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Keyboard Down
|
||
|
*/
|
||
|
function handleKeyDown(id, ev) {
|
||
|
updateForEvent(ev);
|
||
|
|
||
|
if ( surfaces[id] ) {
|
||
|
var suppress = false;
|
||
|
var fev = copyKeyEvent(ev);
|
||
|
var keysym = getKeysymSpecial(ev);
|
||
|
fev.keysym = keysym;
|
||
|
|
||
|
console.debug('Broadway', 'handleKeyDown()', fev, keysym);
|
||
|
|
||
|
if ( keysym ) {
|
||
|
if ( !ignoreKeyEvent(ev) ) {
|
||
|
sendInput('k', [id, keysym, lastState]);
|
||
|
}
|
||
|
suppress = true;
|
||
|
}
|
||
|
|
||
|
if ( !ignoreKeyEvent(ev) ) {
|
||
|
keyDownList.push(fev);
|
||
|
}
|
||
|
|
||
|
if ( suppress ) {
|
||
|
return cancelEvent(ev);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Keyboard Up
|
||
|
*/
|
||
|
function handleKeyUp(id, ev) {
|
||
|
updateForEvent(ev);
|
||
|
|
||
|
if ( surfaces[id] ) {
|
||
|
var fev = getKeyEvent(ev.keyCode, true);
|
||
|
var keysym = fev ? fev.keysym : 0;
|
||
|
|
||
|
console.debug('Broadway', 'handleKeyUp()', fev, keysym);
|
||
|
if ( keysym > 0 ) {
|
||
|
sendInput('K', [id, keysym, lastState]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return cancelEvent(ev);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Keyboard Press
|
||
|
*/
|
||
|
function handleKeyPress(id, ev) {
|
||
|
updateForEvent(ev);
|
||
|
|
||
|
if ( surfaces[id] ) {
|
||
|
|
||
|
if (((ev.which !== 'undefined') && (ev.which === 0)) || getKeysymSpecial(ev)) {
|
||
|
return cancelEvent(ev);
|
||
|
}
|
||
|
|
||
|
var keysym = getKeysym(ev);
|
||
|
var kdlen = keyDownList.length;
|
||
|
|
||
|
console.debug('Broadway', 'handleKeyPress()', keysym);
|
||
|
|
||
|
if ( kdlen > 0 ) {
|
||
|
keyDownList[kdlen-1].keysym = keysym;
|
||
|
}
|
||
|
|
||
|
if ( keysym > 0 ) {
|
||
|
sendInput('k', [id, keysym, lastState]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return cancelEvent(ev);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Mouse Wheel
|
||
|
*/
|
||
|
function handleMouseWheel(id, ev, opts) {
|
||
|
updateForEvent(ev);
|
||
|
if ( surfaces[id] ) {
|
||
|
var offset = ev.detail ? ev.detail : -ev.wheelDelta;
|
||
|
var dir = offset > 0 ? 1 : 0;
|
||
|
var cid = getRelativeLayer(id, ev, opts);
|
||
|
|
||
|
console.debug('Broadway', 'handleMouseWheel()', dir);
|
||
|
sendInput('s', [id, cid, ev.pageX, ev.pageY, opts.mx, opts.my, lastState, dir]);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Mouse Movment
|
||
|
*/
|
||
|
function handleMouseMove(id, ev, opts) {
|
||
|
updateForEvent(ev);
|
||
|
if ( surfaces[id] ) {
|
||
|
var cid = getRelativeLayer(id, ev, opts);
|
||
|
//console.debug('Broadway', 'handleMouseMove()', opts);
|
||
|
sendInput('m', [id, cid, ev.pageX, ev.pageY, opts.mx, opts.my, lastState]);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Mouse Down
|
||
|
*/
|
||
|
function handleMouseDown(id, ev, opts) {
|
||
|
updateForEvent(ev);
|
||
|
var button = ev.button + 1;
|
||
|
var cid = getRelativeLayer(id, ev, opts);
|
||
|
lastState = lastState | getButtonMask (button);
|
||
|
|
||
|
if ( surfaces[id] ) {
|
||
|
console.debug('Broadway', 'handleMouseDown()', opts);
|
||
|
sendInput('b', [id, cid, ev.pageX, ev.pageY, opts.mx, opts.my, lastState, button]);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Mouse Up
|
||
|
*/
|
||
|
function handleMouseUp(id, ev, opts) {
|
||
|
updateForEvent(ev);
|
||
|
var button = ev.button + 1;
|
||
|
var cid = getRelativeLayer(id, ev, opts);
|
||
|
lastState = lastState & ~getButtonMask (button);
|
||
|
if ( surfaces[id] ) {
|
||
|
console.debug('Broadway', 'handleMouseUp()', opts);
|
||
|
sendInput('B', [id, cid, ev.pageX, ev.pageY, opts.mx, opts.my, lastState, button]);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
function handleMouseOver(id, ev, opts) {
|
||
|
updateForEvent(ev);
|
||
|
var cid = getRelativeLayer(id, ev, opts);
|
||
|
if ( surfaces[id] ) {
|
||
|
//console.debug('Broadway', 'handleMouseOver()', opts);
|
||
|
sendInput('e', [id, cid, ev.pageX, ev.pageY, opts.mx, opts.my, lastState, GDK_CROSSING_NORMAL]);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
function handleMouseOut(id, ev, opts) {
|
||
|
updateForEvent(ev);
|
||
|
var cid = getRelativeLayer(id, ev, opts);
|
||
|
if ( surfaces[id] ) {
|
||
|
//console.debug('Broadway', 'handleMouseOut()', opts);
|
||
|
sendInput('l', [id, cid, ev.pageX, ev.pageY, opts.mx, opts.my, lastState, GDK_CROSSING_NORMAL]);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
function handleResize(ev) {
|
||
|
if ( connected ) {
|
||
|
sendInput('d', [window.innerWidth, window.innerHeight]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// API
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
/**
|
||
|
* On message() in socket
|
||
|
*/
|
||
|
function onSocketMessage(message) {
|
||
|
var cmd = new BinCommands(message);
|
||
|
outstandingCommands.push(cmd);
|
||
|
if ( outstandingCommands.length === 1 ) {
|
||
|
handleOutstanding();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Connects to Broadway server
|
||
|
*/
|
||
|
function connect(hostname, cb, cbclose) {
|
||
|
cb = cb || function() {};
|
||
|
cbclose = cbclose || function() {};
|
||
|
|
||
|
function onSocketOpen() {
|
||
|
connected = true;
|
||
|
|
||
|
handleResize();
|
||
|
|
||
|
if ( globalOpts.onSocketOpen ) {
|
||
|
globalOpts.onSocketOpen();
|
||
|
}
|
||
|
|
||
|
cb(false);
|
||
|
}
|
||
|
|
||
|
function onSocketClose() {
|
||
|
if ( globalOpts.onSocketClose ) {
|
||
|
globalOpts.onSocketClose();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
console.info('Broadway', 'Connecting to', hostname);
|
||
|
connection = new WebSocket(hostname, 'broadway');
|
||
|
connection.binaryType = 'arraybuffer';
|
||
|
|
||
|
connection.onerror = function() {
|
||
|
if ( !connected ) {
|
||
|
cb('Connection timeout?');
|
||
|
}
|
||
|
};
|
||
|
|
||
|
connection.onopen = function() {
|
||
|
inputSocket = connection;
|
||
|
|
||
|
onSocketOpen();
|
||
|
};
|
||
|
connection.onclose = function() {
|
||
|
onSocketClose();
|
||
|
|
||
|
connection = null;
|
||
|
inputSocket = null;
|
||
|
|
||
|
cbclose();
|
||
|
};
|
||
|
connection.onmessage = function(ev) {
|
||
|
onSocketMessage(ev.data);
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// EXPORTS
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
window.GTK = window.GTK || {};
|
||
|
|
||
|
window.GTK.disconnect = function() {
|
||
|
if ( connection && connected ) {
|
||
|
connection.close();
|
||
|
}
|
||
|
|
||
|
Object.keys(surfaces).forEach(function(i) {
|
||
|
if ( i && surfaces[i] ) {
|
||
|
if ( globalOpts.onDeleteSurface ) {
|
||
|
globalOpts.onDeleteSurface(i);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
connected = false;
|
||
|
surfaces = {};
|
||
|
lastTimeStamp = 0;
|
||
|
lastState = null;
|
||
|
keyDownList = [];
|
||
|
lastSerial = 0;
|
||
|
outstandingCommands = [];
|
||
|
|
||
|
connection.onclose();
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Connect to broadway
|
||
|
*
|
||
|
* Available callbacks in opts:
|
||
|
*
|
||
|
* onImageData REQUIRED Return your canvas object here
|
||
|
*
|
||
|
* onCreateSurface
|
||
|
* onShowSurface
|
||
|
* onHideSurface
|
||
|
* onSetTransient
|
||
|
* onDeleteSurface
|
||
|
* onMoveSurface
|
||
|
* onFlushSurface
|
||
|
*
|
||
|
* onSocketOpen
|
||
|
* onSocketClose
|
||
|
*
|
||
|
*/
|
||
|
window.GTK.connect = function(host, opts, cb, cbclose) {
|
||
|
if ( connection ) {
|
||
|
console.error('Broadway', 'Only one connection allowed!');
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
globalOpts = opts || {};
|
||
|
connect(host, cb, cbclose);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Closes a surface
|
||
|
*/
|
||
|
window.GTK.close = function(id) {
|
||
|
closeSurface(id);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Moves a surface
|
||
|
*/
|
||
|
window.GTK.move = function(id, x, y) {
|
||
|
moveSurface(id, x, y);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Resizes a surface
|
||
|
*/
|
||
|
window.GTK.resize = function(id, w, h) {
|
||
|
resizeSurface(id, w, h);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Inject keyboard/mouse event
|
||
|
*/
|
||
|
window.GTK.inject = function(id, type, ev, opts) {
|
||
|
if ( !connection ) {
|
||
|
console.error('Broadway', 'No connections created!');
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
switch ( type ) {
|
||
|
case 'resize' :
|
||
|
return handleResize();
|
||
|
break;
|
||
|
|
||
|
case 'mousewheel' :
|
||
|
return handleMouseWheel(id, ev, opts);
|
||
|
break;
|
||
|
|
||
|
case 'mousemove' :
|
||
|
return handleMouseMove(id, ev, opts);
|
||
|
break;
|
||
|
|
||
|
case 'mousedown' :
|
||
|
return handleMouseDown(id, ev, opts);
|
||
|
break;
|
||
|
|
||
|
case 'mouseup' :
|
||
|
return handleMouseUp(id, ev, opts);
|
||
|
break;
|
||
|
|
||
|
case 'keypress' :
|
||
|
return handleKeyPress(id, ev, opts);
|
||
|
break;
|
||
|
|
||
|
case 'keyup' :
|
||
|
return handleKeyUp(id, ev, opts);
|
||
|
break;
|
||
|
|
||
|
case 'keydown' :
|
||
|
return handleKeyDown(id, ev, opts);
|
||
|
break;
|
||
|
|
||
|
case 'mouseout' :
|
||
|
return handleMouseOver(id, ev, opts);
|
||
|
break;
|
||
|
|
||
|
case 'mouseover' :
|
||
|
return handleMouseOver(id, ev, opts);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
console.warn('Broadway', 'inject()', 'invalid type', type);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
};
|
||
|
|
||
|
})();
|