1 /* Helper functions for debugging */
5 logDiv = document.createElement('div');
6 document.body.appendChild(logDiv);
7 logDiv.style["position"] = "absolute";
8 logDiv.style["right"] = "0px";
10 logDiv.appendChild(document.createTextNode(str));
11 logDiv.appendChild(document.createElement('br'));
15 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
16 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
17 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
18 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255,
19 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
20 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
21 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
22 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255
25 function base64_8(str, index) {
27 (base64Values[str.charCodeAt(index)]) +
28 (base64Values[str.charCodeAt(index+1)] << 6);
32 function base64_16(str, index) {
34 (base64Values[str.charCodeAt(index)]) +
35 (base64Values[str.charCodeAt(index+1)] << 6) +
36 (base64Values[str.charCodeAt(index+2)] << 12);
40 function base64_16s(str, index) {
41 var v = base64_16(str, index);
48 function base64_24(str, index) {
50 (base64Values[str.charCodeAt(index)]) +
51 (base64Values[str.charCodeAt(index+1)] << 6) +
52 (base64Values[str.charCodeAt(index+2)] << 12) +
53 (base64Values[str.charCodeAt(index+3)] << 18);
57 function base64_32(str, index) {
59 (base64Values[str.charCodeAt(index)]) +
60 (base64Values[str.charCodeAt(index+1)] << 6) +
61 (base64Values[str.charCodeAt(index+2)] << 12) +
62 (base64Values[str.charCodeAt(index+3)] << 18) +
63 (base64Values[str.charCodeAt(index+4)] << 24) +
64 (base64Values[str.charCodeAt(index+5)] << 30);
70 try { return new XMLHttpRequest(); } catch(e) {}
71 try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) {}
72 try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e) {}
73 try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {}
74 try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
79 var grab = new Object();
81 grab.ownerEvents = false;
83 grab.implicit = false;
88 var lastTimeStamp = 0;
89 var realWindowWithMouse = 0;
90 var windowWithMouse = 0;
92 var outstandingCommands = new Array();
93 var inputSocket = null;
95 function initContext(canvas, x, y, id)
97 canvas.surfaceId = id;
98 canvas.style["position"] = "absolute";
99 canvas.style["left"] = x + "px";
100 canvas.style["top"] = y + "px";
101 canvas.style["display"] = "none";
102 var context = canvas.getContext("2d");
103 context.globalCompositeOperation = "source-over";
104 document.body.appendChild(canvas);
105 context.drawQueue = [];
110 var GDK_CROSSING_NORMAL = 0;
111 var GDK_CROSSING_GRAB = 1;
112 var GDK_CROSSING_UNGRAB = 2;
115 var GDK_SHIFT_MASK = 1 << 0;
116 var GDK_LOCK_MASK = 1 << 1;
117 var GDK_CONTROL_MASK = 1 << 2;
118 var GDK_MOD1_MASK = 1 << 3;
119 var GDK_MOD2_MASK = 1 << 4;
120 var GDK_MOD3_MASK = 1 << 5;
121 var GDK_MOD4_MASK = 1 << 6;
122 var GDK_MOD5_MASK = 1 << 7;
123 var GDK_BUTTON1_MASK = 1 << 8;
124 var GDK_BUTTON2_MASK = 1 << 9;
125 var GDK_BUTTON3_MASK = 1 << 10;
126 var GDK_BUTTON4_MASK = 1 << 11;
127 var GDK_BUTTON5_MASK = 1 << 12;
128 var GDK_SUPER_MASK = 1 << 26;
129 var GDK_HYPER_MASK = 1 << 27;
130 var GDK_META_MASK = 1 << 28;
131 var GDK_RELEASE_MASK = 1 << 30;
133 function getButtonMask (button) {
135 return GDK_BUTTON1_MASK;
137 return GDK_BUTTON2_MASK;
139 return GDK_BUTTON3_MASK;
141 return GDK_BUTTON4_MASK;
143 return GDK_BUTTON5_MASK;
147 function flushSurface(surface)
149 var commands = surface.drawQueue;
152 for (i = 0; i < commands.length; i++) {
153 var cmd = commands[i];
154 var context = surfaces[cmd.id];
156 /* put image data surface */
158 context.globalCompositeOperation = "source-over";
159 context.drawImage(cmd.img, cmd.x, cmd.y);
171 for (var j = 0; j < cmd.rects.length; j++) {
172 var rect = cmd.rects[j];
173 context.rect(rect.x, rect.y, rect.w, rect.h);
177 maxx = rect.x + rect.w;
178 maxy = rect.y + rect.h;
184 if (rect.x + rect.w > maxx)
185 maxx = rect.x + rect.w;
186 if (rect.y + rect.h > maxy)
187 maxy = rect.y + rect.h;
191 context.globalCompositeOperation = "copy";
192 context.drawImage(context.canvas,
193 minx - cmd.dx, miny - cmd.dy, maxx - minx, maxy - miny,
194 minx, miny, maxx - minx, maxy - miny);
199 alert("Unknown drawing op " + cmd.op);
204 function handleCommands(cmdObj)
206 var cmd = cmdObj.data;
209 while (i < cmd.length) {
210 var command = cmd[i++];
211 lastSerial = base64_32(cmd, i);
214 case 's': // create new surface
215 var id = base64_16(cmd, i);
217 var x = base64_16(cmd, i);
219 var y = base64_16(cmd, i);
221 var w = base64_16(cmd, i);
223 var h = base64_16(cmd, i);
225 var surface = document.createElement("canvas");
228 surfaces[id] = initContext(surface, x, y, id);
231 case 'S': // Show a surface
232 var id = base64_16(cmd, i);
234 surfaces[id].canvas.style["display"] = "inline";
237 case 'H': // Hide a surface
238 var id = base64_16(cmd, i);
240 surfaces[id].canvas.style["display"] = "none";
243 case 'd': // Delete surface
244 var id = base64_16(cmd, i);
246 var canvas = surfaces[id].canvas;
248 canvas.parentNode.removeChild(canvas);
252 case 'm': // Move a surface
253 var id = base64_16(cmd, i);
255 var x = base64_16(cmd, i);
257 var y = base64_16(cmd, i);
259 surfaces[id].canvas.style["left"] = x + "px";
260 surfaces[id].canvas.style["top"] = y + "px";
263 case 'r': // Resize a surface
264 var id = base64_16(cmd, i);
266 var w = base64_16(cmd, i);
268 var h = base64_16(cmd, i);
270 var surface = surfaces[id];
272 /* Flush any outstanding draw ops before changing size */
273 flushSurface(surface);
275 /* Canvas resize clears the data, so we need to save it first */
276 var tmpCanvas = document.createElement("canvas");
277 tmpCanvas.width = surface.canvas.width;
278 tmpCanvas.height = surface.canvas.height;
279 var tmpContext = tmpCanvas.getContext("2d");
280 tmpContext.globalCompositeOperation = "copy";
281 tmpContext.drawImage(surface.canvas, 0, 0, tmpCanvas.width, tmpCanvas.height);
283 surface.canvas.width = w;
284 surface.canvas.height = h;
286 surface.globalCompositeOperation = "copy";
287 surface.drawImage(tmpCanvas, 0, 0, tmpCanvas.width, tmpCanvas.height);
291 case 'i': // Put image data surface
292 var q = new Object();
294 q.id = base64_16(cmd, i);
296 q.x = base64_16(cmd, i);
298 q.y = base64_16(cmd, i);
300 var size = base64_32(cmd, i);
302 var url = cmd.slice(i, i + size);
306 surfaces[q.id].drawQueue.push(q);
307 if (!q.img.complete) {
309 q.img.onload = function() { handleOutstanding(); };
314 case 'b': // Copy rects
315 var q = new Object();
317 q.id = base64_16(cmd, i);
320 var nrects = base64_16(cmd, i);
324 for (var r = 0; r < nrects; r++) {
325 var rect = new Object();
326 rect.x = base64_16(cmd, i);
328 rect.y = base64_16(cmd, i);
330 rect.w = base64_16(cmd, i);
332 rect.h = base64_16(cmd, i);
337 q.dx = base64_16s(cmd, i);
339 q.dy = base64_16s(cmd, i);
341 surfaces[q.id].drawQueue.push(q);
344 case 'f': // Flush surface
345 var id = base64_16(cmd, i);
348 flushSurface(surfaces[id]);
351 case 'q': // Query pointer
352 var id = base64_16(cmd, i);
355 var pos = getPositionsFromAbsCoord(lastX, lastY, id);
357 sendInput ("q", [pos.rootX, pos.rootY, pos.winX, pos.winY, windowWithMouse]);
361 var id = base64_16(cmd, i);
363 var ownerEvents = cmd[i++] == '1';
365 doGrab(id, ownerEvents, time, false);
371 var time = base64_32(cmd, i);
375 if (grab.window != null) {
376 if (grab.time == 0 || time == 0 ||
383 alert("Unknown op " + command);
389 function handleOutstanding()
391 while (outstandingCommands.length > 0) {
392 var cmd = outstandingCommands.shift();
393 if (!handleCommands(cmd)) {
394 outstandingCommands.unshift(cmd);
400 function handleLoad(event)
403 cmdObj.data = event.target.responseText;
406 outstandingCommands.push(cmdObj);
407 if (outstandingCommands.length == 1) {
412 function getSurfaceId(ev) {
413 var id = ev.target.surfaceId;
419 function sendInput(cmd, args)
421 if (inputSocket != null) {
422 inputSocket.send(cmd + ([lastSerial, lastTimeStamp].concat(args)).join(","));
426 function getDocumentCoordinates(element)
428 var res = new Object();
429 res.x = element.offsetLeft;
430 res.y = element.offsetTop;
432 var offsetParent = element.offsetParent;
433 while (offsetParent != null) {
434 res.x += offsetParent.offsetLeft;
435 res.y += offsetParent.offsetTop;
436 offsetParent = offsetParent.offsetParent;
441 function getPositionsFromAbsCoord(absX, absY, relativeId) {
448 if (relativeId != 0) {
449 var pos = getDocumentCoordinates(surfaces[relativeId].canvas);
450 res.winX = res.winX - pos.x;
451 res.winY = res.winY - pos.y;
457 function getPositionsFromEvent(ev, relativeId) {
458 var res = getPositionsFromAbsCoord(ev.pageX, ev.pageY, relativeId);
466 function getEffectiveEventTarget (id) {
467 if (grab.window != null) {
468 if (!grab.ownerEvents)
476 function updateForEvent(ev) {
477 lastTimeStamp = ev.timeStamp;
480 function onMouseMove (ev) {
482 var id = getSurfaceId(ev);
483 id = getEffectiveEventTarget (id);
484 var pos = getPositionsFromEvent(ev, id);
485 sendInput ("m", [id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState]);
488 function onMouseOver (ev) {
490 var id = getSurfaceId(ev);
491 realWindowWithMouse = id;
492 id = getEffectiveEventTarget (id);
493 var pos = getPositionsFromEvent(ev, id);
494 windowWithMouse = id;
495 if (windowWithMouse != 0) {
496 sendInput ("e", [id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_NORMAL]);
500 function onMouseOut (ev) {
502 var id = getSurfaceId(ev);
504 id = getEffectiveEventTarget (id);
505 var pos = getPositionsFromEvent(ev, id);
508 sendInput ("l", [id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_NORMAL]);
510 realWindowWithMouse = 0;
514 function doGrab(id, ownerEvents, time, implicit) {
517 if (windowWithMouse != id) {
518 if (windowWithMouse != 0) {
519 pos = getPositionsFromAbsCoord(lastX, lastY, windowWithMouse);
520 sendInput ("l", [windowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_GRAB]);
522 pos = getPositionsFromAbsCoord(lastX, lastY, id);
523 sendInput ("e", [id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_GRAB]);
524 windowWithMouse = id;
528 grab.ownerEvents = ownerEvents;
530 grab.implicit = implicit;
533 function doUngrab(time) {
535 if (realWindowWithMouse != windowWithMouse) {
536 if (windowWithMouse != 0) {
537 pos = getPositionsFromAbsCoord(lastX, lastY, windowWithMouse);
538 sendInput ("l", [windowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_UNGRAB]);
540 if (realWindowWithMouse != 0) {
541 pos = getPositionsFromAbsCoord(lastX, lastY, realWindowWithMouse);
542 sendInput ("e", [realWindowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_UNGRAB]);
544 windowWithMouse = realWindowWithMouse;
549 function onMouseDown (ev) {
551 var id = getSurfaceId(ev);
552 id = getEffectiveEventTarget (id);
553 var pos = getPositionsFromEvent(ev, id);
554 if (grab.window != null)
555 doGrab (id, false, ev.timeStamp, true);
556 var button = ev.button + 1;
557 lastState = lastState | getButtonMask (button);
558 sendInput ("b", [id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, button]);
561 function onMouseUp (ev) {
563 var id = getSurfaceId(ev);
564 id = getEffectiveEventTarget (id);
565 var pos = getPositionsFromEvent(ev, id);
566 var button = ev.button + 1;
567 lastState = lastState & ~getButtonMask (button);
568 sendInput ("B", [id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, button]);
570 if (grab.window != null && grab.implicit)
571 doUngrab(ev.timeStamp);
575 function onKeyDown (ev) {
577 var keyCode = ev.keyCode;
578 if (keyCode != lastKeyDown) {
579 sendInput ("k", [keyCode]);
580 lastKeyDown = keyCode;
584 function onKeyUp (ev) {
586 var keyCode = ev.keyCode;
587 sendInput ("K", [keyCode]);
591 function cancelEvent(ev)
593 ev = ev ? ev : window.event;
594 if (ev.stopPropagation)
595 ev.stopPropagation();
596 if (ev.preventDefault)
598 ev.cancelBubble = true;
600 ev.returnValue = false;
604 function onMouseWheel(ev)
607 ev = ev ? ev : window.event;
609 var id = getSurfaceId(ev);
610 var pos = getPositionsFromEvent(ev, id);
612 var offset = ev.detail ? ev.detail : ev.wheelDelta;
616 sendInput ("s", [id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, dir]);
618 return cancelEvent(ev);
623 var xhr = createXHR();
625 if (typeof xhr.multipart == 'undefined') {
626 alert("Sorry, this example only works in browsers that support multipart.");
630 xhr.multipart = true;
631 xhr.open("GET", "/output", true);
632 xhr.onload = handleLoad;
636 if ("WebSocket" in window) {
637 var loc = window.location.toString().replace("http:", "ws:");
638 loc = loc.substr(0, loc.lastIndexOf('/')) + "/input";
639 var ws = new WebSocket(loc, "broadway");
640 ws.onopen = function() {
643 ws.onclose = function() {
647 alert("WebSocket not supported, input will not work!");
649 document.oncontextmenu = function () { return false; };
650 document.onmousemove = onMouseMove;
651 document.onmouseover = onMouseOver;
652 document.onmouseout = onMouseOut;
653 document.onmousedown = onMouseDown;
654 document.onmouseup = onMouseUp;
655 document.onkeydown = onKeyDown;
656 document.onkeyup = onKeyUp;
658 if (document.addEventListener) {
659 document.addEventListener('DOMMouseScroll', onMouseWheel, false);
660 document.addEventListener('mousewheel', onMouseWheel, false);
661 } else if (document.attachEvent) {
662 element.attachEvent("onmousewheel", onMouseWheel);