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'));
14 function getStackTrace()
17 var isCallstackPopulated = false;
21 if (e.stack) { // Firefox
22 var lines = e.stack.split("\n");
23 for (var i=0, len=lines.length; i<len; i++) {
24 if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
25 callstack.push(lines[i]);
28 // Remove call to getStackTrace()
30 isCallstackPopulated = true;
31 } else if (window.opera && e.message) { // Opera
32 var lines = e.message.split("\n");
33 for (var i=0, len=lines.length; i<len; i++) {
34 if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
36 // Append next line also since it has the file info
38 entry += " at " + lines[i+1];
41 callstack.push(entry);
44 // Remove call to getStackTrace()
46 isCallstackPopulated = true;
49 if (!isCallstackPopulated) { //IE and Safari
50 var currentFunction = arguments.callee.caller;
51 while (currentFunction) {
52 var fn = currentFunction.toString();
53 var fname = fn.substring(fn.indexOf("function") + 8, fn.indexOf("(")) || "anonymous";
54 callstack.push(fname);
55 currentFunction = currentFunction.caller;
61 function logStackTrace(len) {
62 var callstack = getStackTrace();
63 var end = callstack.length;
65 end = Math.min(len + 1, end);
66 for (var i = 1; i < end; i++)
71 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
72 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
73 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
74 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255,
75 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
76 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
77 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
78 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255
81 function base64_8(str, index) {
83 (base64Values[str.charCodeAt(index)]) +
84 (base64Values[str.charCodeAt(index+1)] << 6);
88 function base64_16(str, index) {
90 (base64Values[str.charCodeAt(index)]) +
91 (base64Values[str.charCodeAt(index+1)] << 6) +
92 (base64Values[str.charCodeAt(index+2)] << 12);
96 function base64_16s(str, index) {
97 var v = base64_16(str, index);
104 function base64_24(str, index) {
106 (base64Values[str.charCodeAt(index)]) +
107 (base64Values[str.charCodeAt(index+1)] << 6) +
108 (base64Values[str.charCodeAt(index+2)] << 12) +
109 (base64Values[str.charCodeAt(index+3)] << 18);
113 function base64_32(str, index) {
115 (base64Values[str.charCodeAt(index)]) +
116 (base64Values[str.charCodeAt(index+1)] << 6) +
117 (base64Values[str.charCodeAt(index+2)] << 12) +
118 (base64Values[str.charCodeAt(index+3)] << 18) +
119 (base64Values[str.charCodeAt(index+4)] << 24) +
120 (base64Values[str.charCodeAt(index+5)] << 30);
126 try { return new XMLHttpRequest(); } catch(e) {}
127 try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) {}
128 try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e) {}
129 try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {}
130 try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
135 function resizeCanvas(canvas, w, h)
137 /* Canvas resize clears the data, so we need to save it first */
138 var tmpCanvas = canvas.ownerDocument.createElement("canvas");
139 tmpCanvas.width = canvas.width;
140 tmpCanvas.height = canvas.height;
141 var tmpContext = tmpCanvas.getContext("2d");
142 tmpContext.globalCompositeOperation = "copy";
143 tmpContext.drawImage(canvas, 0, 0, tmpCanvas.width, tmpCanvas.height);
148 var context = canvas.getContext("2d");
150 context.globalCompositeOperation = "copy";
151 context.drawImage(tmpCanvas, 0, 0, tmpCanvas.width, tmpCanvas.height);
154 var grab = new Object();
156 grab.ownerEvents = false;
157 grab.implicit = false;
158 var localGrab = null;
159 var keyDownList = [];
164 var lastTimeStamp = 0;
165 var realWindowWithMouse = 0;
166 var windowWithMouse = 0;
168 var stackingOrder = [];
169 var outstandingCommands = new Array();
170 var inputSocket = null;
172 var GDK_CROSSING_NORMAL = 0;
173 var GDK_CROSSING_GRAB = 1;
174 var GDK_CROSSING_UNGRAB = 2;
177 var GDK_SHIFT_MASK = 1 << 0;
178 var GDK_LOCK_MASK = 1 << 1;
179 var GDK_CONTROL_MASK = 1 << 2;
180 var GDK_MOD1_MASK = 1 << 3;
181 var GDK_MOD2_MASK = 1 << 4;
182 var GDK_MOD3_MASK = 1 << 5;
183 var GDK_MOD4_MASK = 1 << 6;
184 var GDK_MOD5_MASK = 1 << 7;
185 var GDK_BUTTON1_MASK = 1 << 8;
186 var GDK_BUTTON2_MASK = 1 << 9;
187 var GDK_BUTTON3_MASK = 1 << 10;
188 var GDK_BUTTON4_MASK = 1 << 11;
189 var GDK_BUTTON5_MASK = 1 << 12;
190 var GDK_SUPER_MASK = 1 << 26;
191 var GDK_HYPER_MASK = 1 << 27;
192 var GDK_META_MASK = 1 << 28;
193 var GDK_RELEASE_MASK = 1 << 30;
195 function getButtonMask (button) {
197 return GDK_BUTTON1_MASK;
199 return GDK_BUTTON2_MASK;
201 return GDK_BUTTON3_MASK;
203 return GDK_BUTTON4_MASK;
205 return GDK_BUTTON5_MASK;
209 function flushSurface(surface)
211 var commands = surface.drawQueue;
213 var context = surface.canvas.getContext("2d");
214 context.globalCompositeOperation = "source-over";
216 for (i = 0; i < commands.length; i++) {
217 var cmd = commands[i];
219 case 'i': // put image data surface
220 context.globalCompositeOperation = "source-over";
221 context.drawImage(cmd.img, cmd.x, cmd.y);
224 case 'b': // copy rects
232 for (var j = 0; j < cmd.rects.length; j++) {
233 var rect = cmd.rects[j];
234 context.rect(rect.x, rect.y, rect.w, rect.h);
238 maxx = rect.x + rect.w;
239 maxy = rect.y + rect.h;
245 if (rect.x + rect.w > maxx)
246 maxx = rect.x + rect.w;
247 if (rect.y + rect.h > maxy)
248 maxy = rect.y + rect.h;
252 context.globalCompositeOperation = "copy";
253 context.drawImage(context.canvas,
254 minx - cmd.dx, miny - cmd.dy, maxx - minx, maxy - miny,
255 minx, miny, maxx - minx, maxy - miny);
260 alert("Unknown drawing op " + cmd.op);
265 function sendConfigureNotify(surface)
267 sendInput("w", [surface.id, surface.x, surface.y, surface.width, surface.height]);
270 function getStyle(el, styleProp)
272 if (el.currentStyle) {
273 return el.currentStyle[styleProp];
274 } else if (window.getComputedStyle) {
275 var win = el.ownerDocument.defaultView;
276 return win.getComputedStyle(el, null).getPropertyValue(styleProp);
281 function parseOffset(value)
283 var px = value.indexOf("px");
285 return parseInt(value.slice(0,px));
289 function getFrameOffset(surface) {
292 var el = surface.canvas;
293 while (el != null && el != surface.frame) {
297 /* For some reason the border is not includes in the offsets.. */
298 x += parseOffset(getStyle(el, "border-left-width"));
299 y += parseOffset(getStyle(el, "border-top-width"));
301 el = el.offsetParent;
304 /* Also include frame border as per above */
305 x += parseOffset(getStyle(el, "border-left-width"));
306 y += parseOffset(getStyle(el, "border-top-width"));
311 var positionIndex = 0;
312 function cmdCreateSurface(id, x, y, width, height, isTemp)
314 var surface = { id: id, x: x, y:y, width: width, height: height, isTemp: isTemp };
315 surface.positioned = isTemp;
316 surface.drawQueue = [];
317 surface.transientParent = 0;
318 surface.visible = false;
319 surface.frame = null;
321 var canvas = document.createElement("canvas");
322 canvas.width = width;
323 canvas.height = height;
324 canvas.surface = surface;
325 surface.canvas = canvas;
329 toplevelElement = canvas;
330 document.body.appendChild(canvas);
332 var frame = document.createElement("div");
333 frame.frameFor = surface;
334 frame.className = "frame-window";
335 surface.frame = frame;
337 var button = document.createElement("center");
338 button.closeFor = surface;
339 var X = document.createTextNode("\u00d7");
340 button.appendChild(X);
341 button.className = "frame-close";
342 frame.appendChild(button);
344 var contents = document.createElement("div");
345 contents.className = "frame-contents";
346 frame.appendChild(contents);
348 canvas.style["display"] = "block";
349 contents.appendChild(canvas);
351 toplevelElement = frame;
352 document.body.appendChild(frame);
355 surface.toplevelElement = toplevelElement;
356 toplevelElement.style["position"] = "absolute";
357 /* This positioning isn't strictly right for apps in another topwindow,
358 * but that will be fixed up when showing. */
359 toplevelElement.style["left"] = surface.x + "px";
360 toplevelElement.style["top"] = surface.y + "px";
361 toplevelElement.style["display"] = "inline";
363 /* We hide the frame with visibility rather than display none
364 * so getFrameOffset still works with hidden windows. */
365 toplevelElement.style["visibility"] = "hidden";
367 surfaces[id] = surface;
368 stackingOrder.push(surface);
370 sendConfigureNotify(surface);
373 function cmdShowSurface(id)
375 var surface = surfaces[id];
379 surface.visible = true;
381 var xOffset = surface.x;
382 var yOffset = surface.y;
385 var offset = getFrameOffset(surface);
390 surface.toplevelElement.style["left"] = xOffset + "px";
391 surface.toplevelElement.style["top"] = yOffset + "px";
392 surface.toplevelElement.style["visibility"] = "visible";
397 function cmdHideSurface(id)
399 if (grab.window == id)
402 var surface = surfaces[id];
404 if (!surface.visible)
406 surface.visible = false;
408 var element = surface.toplevelElement;
410 element.style["visibility"] = "hidden";
413 function cmdSetTransientFor(id, parentId)
415 var surface = surfaces[id];
417 if (surface.transientParent == parentId)
420 surface.transientParent = parentId;
421 if (parentId != 0 && surfaces[parentId]) {
422 moveToHelper(surface, stackingOrder.indexOf(surfaces[parentId])+1);
425 if (surface.visible) {
430 function restackWindows() {
431 for (var i = 0; i < stackingOrder.length; i++) {
432 var surface = stackingOrder[i];
433 surface.toplevelElement.style.zIndex = i;
437 function moveToHelper(surface, position) {
438 var i = stackingOrder.indexOf(surface);
439 stackingOrder.splice(i, 1);
440 if (position != undefined)
441 stackingOrder.splice(position, 0, surface);
443 stackingOrder.push(surface);
445 for (var cid in surfaces) {
446 var child = surfaces[cid];
447 if (child.transientParent == surface.id)
448 moveToHelper(child, stackingOrder.indexOf(surface) + 1);
452 function moveToTop(surface) {
453 moveToHelper(surface);
458 function cmdDeleteSurface(id)
460 if (grab.window == id)
463 var surface = surfaces[id];
464 var i = stackingOrder.indexOf(surface);
466 stackingOrder.splice(i, 1);
467 var canvas = surface.canvas;
468 canvas.parentNode.removeChild(canvas);
469 var frame = surface.frame;
471 frame.parentNode.removeChild(frame);
475 function cmdMoveResizeSurface(id, has_pos, x, y, has_size, w, h)
477 var surface = surfaces[id];
479 surface.positioned = true;
488 /* Flush any outstanding draw ops before (possibly) changing size */
489 flushSurface(surface);
492 resizeCanvas(surface.canvas, w, h);
494 if (surface.visible) {
496 var xOffset = surface.x;
497 var yOffset = surface.y;
499 var transientToplevel = getTransientToplevel(surface);
500 if (transientToplevel) {
501 xOffset = surface.x - transientToplevel.x;
502 yOffset = surface.y - transientToplevel.y;
505 var element = surface.canvas;
507 element = surface.frame;
508 var offset = getFrameOffset(surface);
513 element.style["left"] = xOffset + "px";
514 element.style["top"] = yOffset + "px";
518 sendConfigureNotify(surface);
521 function cmdFlushSurface(id)
523 flushSurface(surfaces[id]);
526 function cmdGrabPointer(id, ownerEvents)
528 doGrab(id, ownerEvents, false);
532 function cmdUngrabPointer()
539 function handleCommands(cmd)
541 while (cmd.pos < cmd.length) {
542 var id, x, y, w, h, q;
543 var command = cmd.get_char();
544 lastSerial = cmd.get_32();
546 case 's': // create new surface
552 var isTemp = cmd.get_bool();
553 cmdCreateSurface(id, x, y, w, h, isTemp);
556 case 'S': // Show a surface
561 case 'H': // Hide a surface
566 case 'p': // Set transient parent
568 var parentId = cmd.get_16();
569 cmdSetTransientFor(id, parentId);
572 case 'd': // Delete surface
574 cmdDeleteSurface(id);
577 case 'm': // Move a surface
579 var ops = cmd.get_flags();
580 var has_pos = ops & 1;
585 var has_size = ops & 2;
590 cmdMoveResizeSurface(id, has_pos, x, y, has_size, w, h);
593 case 'i': // Put image data surface
599 var url = cmd.get_image_url ();
602 surfaces[q.id].drawQueue.push(q);
603 if (!q.img.complete) {
604 q.img.onload = function() { cmd.free_image_url (url); handleOutstanding(); };
607 cmd.free_image_url (url);
610 case 'b': // Copy rects
614 var nrects = cmd.get_16();
617 for (var r = 0; r < nrects; r++) {
618 var rect = new Object();
619 rect.x = cmd.get_16();
620 rect.y = cmd.get_16();
621 rect.w = cmd.get_16();
622 rect.h = cmd.get_16();
626 q.dx = cmd.get_16s();
627 q.dy = cmd.get_16s();
628 surfaces[q.id].drawQueue.push(q);
631 case 'f': // Flush surface
639 var ownerEvents = cmd.get_bool ();
641 cmdGrabPointer(id, ownerEvents);
648 alert("Unknown op " + command);
654 function handleOutstanding()
656 while (outstandingCommands.length > 0) {
657 var cmd = outstandingCommands.shift();
658 if (!handleCommands(cmd)) {
659 outstandingCommands.unshift(cmd);
665 function TextCommands(message) {
667 this.length = message.length;
671 TextCommands.prototype.get_char = function() {
672 return this.data[this.pos++];
674 TextCommands.prototype.get_bool = function() {
675 return this.get_char() == '1';
677 TextCommands.prototype.get_flags = function() {
678 return this.get_char() - 48;
680 TextCommands.prototype.get_16 = function() {
681 var n = base64_16(this.data, this.pos);
682 this.pos = this.pos + 3;
685 TextCommands.prototype.get_16s = function() {
686 var n = base64_16s(this.data, this.pos);
687 this.pos = this.pos + 3;
690 TextCommands.prototype.get_32 = function() {
691 var n = base64_32(this.data, this.pos);
692 this.pos = this.pos + 6;
695 TextCommands.prototype.get_image_url = function() {
696 var size = this.get_32();
697 var url = this.data.slice(this.pos, this.pos + size);
698 this.pos = this.pos + size;
701 TextCommands.prototype.free_image_url = function(url) {
704 function BinCommands(message) {
705 this.arraybuffer = message;
706 this.u8 = new Uint8Array(message);
707 this.length = this.u8.length;
711 BinCommands.prototype.get_char = function() {
712 return String.fromCharCode(this.u8[this.pos++]);
714 BinCommands.prototype.get_bool = function() {
715 return this.u8[this.pos++] != 0;
717 BinCommands.prototype.get_flags = function() {
718 return this.u8[this.pos++];
720 BinCommands.prototype.get_16 = function() {
723 (this.u8[this.pos+1] << 8);
724 this.pos = this.pos + 2;
727 BinCommands.prototype.get_16s = function() {
728 var v = this.get_16 ();
734 BinCommands.prototype.get_32 = function() {
737 (this.u8[this.pos+1] << 8) +
738 (this.u8[this.pos+2] << 16) +
739 (this.u8[this.pos+3] << 24);
740 this.pos = this.pos + 4;
743 BinCommands.prototype.get_image_url = function() {
744 var size = this.get_32();
745 var png_blob = new Blob ([this.arraybuffer.slice (this.pos, this.pos + size)], {type:"image/png"});
746 var url = URL.createObjectURL(png_blob, {oneTimeOnly: true});
747 this.pos = this.pos + size;
750 BinCommands.prototype.free_image_url = function(url) {
751 URL.revokeObjectURL(url);
754 function handleMessage(message)
757 if (message instanceof ArrayBuffer)
758 cmd = new BinCommands(message);
760 cmd = new TextCommands(message);
761 outstandingCommands.push(cmd);
762 if (outstandingCommands.length == 1) {
767 function getSurfaceId(ev) {
768 var surface = ev.target.surface;
769 if (surface != undefined)
774 function sendInput(cmd, args)
776 if (inputSocket != null) {
777 inputSocket.send(cmd + ([lastSerial, lastTimeStamp].concat(args)).join(","));
781 function getPositionsFromAbsCoord(absX, absY, relativeId) {
788 if (relativeId != 0) {
789 var surface = surfaces[relativeId];
790 res.winX = res.winX - surface.x;
791 res.winY = res.winY - surface.y;
797 function getPositionsFromEvent(ev, relativeId) {
801 var res = getPositionsFromAbsCoord(absX, absY, relativeId);
809 function getEffectiveEventTarget (id) {
810 if (grab.window != null) {
811 if (!grab.ownerEvents)
819 function updateForEvent(ev) {
820 lastState &= ~(GDK_SHIFT_MASK|GDK_CONTROL_MASK|GDK_MOD1_MASK);
822 lastState |= GDK_SHIFT_MASK;
824 lastState |= GDK_CONTROL_MASK;
826 lastState |= GDK_MOD1_MASK;
828 lastTimeStamp = ev.timeStamp;
831 function onMouseMove (ev) {
834 if (localGrab.type == "move") {
835 var dx = ev.pageX - localGrab.lastX;
836 var dy = ev.pageY - localGrab.lastY;
837 var surface = localGrab.surface;
840 var offset = getFrameOffset(surface);
841 if (surface.y < offset.y)
842 surface.y = offset.y;
843 localGrab.frame.style["left"] = (surface.x - offset.x) + "px";
844 localGrab.frame.style["top"] = (surface.y - offset.y) + "px";
845 sendConfigureNotify(surface);
846 localGrab.lastX = ev.pageX;
847 localGrab.lastY = ev.pageY;
851 var id = getSurfaceId(ev);
852 id = getEffectiveEventTarget (id);
853 var pos = getPositionsFromEvent(ev, id);
854 sendInput ("m", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState]);
857 function onMouseOver (ev) {
860 if (!grab.window && ev.target.closeFor) {
861 ev.target.className = ev.target.className + " frame-hover";
862 if (ev.target.isDown)
863 ev.target.className = ev.target.className + " frame-active";
868 var id = getSurfaceId(ev);
869 realWindowWithMouse = id;
870 id = getEffectiveEventTarget (id);
871 var pos = getPositionsFromEvent(ev, id);
872 windowWithMouse = id;
873 if (windowWithMouse != 0) {
874 sendInput ("e", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_NORMAL]);
878 function onMouseOut (ev) {
880 if (ev.target.closeFor) {
881 ev.target.className = ev.target.className.replace(" frame-hover", "");
882 if (ev.target.isDown)
883 ev.target.className = ev.target.className.replace(" frame-active", "");
887 var id = getSurfaceId(ev);
889 id = getEffectiveEventTarget (id);
890 var pos = getPositionsFromEvent(ev, id);
893 sendInput ("l", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_NORMAL]);
895 realWindowWithMouse = 0;
899 function doGrab(id, ownerEvents, implicit) {
902 if (windowWithMouse != id) {
903 if (windowWithMouse != 0) {
904 pos = getPositionsFromAbsCoord(lastX, lastY, windowWithMouse);
905 sendInput ("l", [realWindowWithMouse, windowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_GRAB]);
907 pos = getPositionsFromAbsCoord(lastX, lastY, id);
908 sendInput ("e", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_GRAB]);
909 windowWithMouse = id;
913 grab.ownerEvents = ownerEvents;
914 grab.implicit = implicit;
917 function doUngrab() {
919 if (realWindowWithMouse != windowWithMouse) {
920 if (windowWithMouse != 0) {
921 pos = getPositionsFromAbsCoord(lastX, lastY, windowWithMouse);
922 sendInput ("l", [realWindowWithMouse, windowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_UNGRAB]);
924 if (realWindowWithMouse != 0) {
925 pos = getPositionsFromAbsCoord(lastX, lastY, realWindowWithMouse);
926 sendInput ("e", [realWindowWithMouse, realWindowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_UNGRAB]);
928 windowWithMouse = realWindowWithMouse;
933 function onMouseDown (ev) {
935 var button = ev.button + 1;
936 lastState = lastState | getButtonMask (button);
937 var id = getSurfaceId(ev);
938 id = getEffectiveEventTarget (id);
940 if (id == 0 && ev.target.frameFor) { /* mouse click on frame */
941 localGrab = new Object();
942 localGrab.surface = ev.target.frameFor;
943 localGrab.type = "move";
944 localGrab.frame = ev.target;
945 localGrab.lastX = ev.pageX;
946 localGrab.lastY = ev.pageY;
947 moveToTop(localGrab.frame.frameFor);
951 if (id == 0 && ev.target.closeFor) { /* mouse click on frame */
952 ev.target.isDown = true;
953 ev.target.className = ev.target.className + " frame-active";
954 localGrab = new Object();
955 localGrab.surface = ev.target.closeFor;
956 localGrab.type = "close";
957 localGrab.button = ev.target;
958 localGrab.lastX = ev.pageX;
959 localGrab.lastY = ev.pageY;
963 var pos = getPositionsFromEvent(ev, id);
964 if (grab.window == null)
965 doGrab (id, false, true);
966 sendInput ("b", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, button]);
970 function onMouseUp (ev) {
972 var button = ev.button + 1;
973 lastState = lastState & ~getButtonMask (button);
974 var evId = getSurfaceId(ev);
975 id = getEffectiveEventTarget (evId);
976 var pos = getPositionsFromEvent(ev, id);
979 realWindowWithMouse = evId;
980 if (windowWithMouse != id) {
981 if (windowWithMouse != 0) {
982 sendInput ("l", [realWindowWithMouse, windowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_NORMAL]);
984 windowWithMouse = id;
985 if (windowWithMouse != 0) {
986 sendInput ("e", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_NORMAL]);
990 if (localGrab.type == "close") {
991 localGrab.button.isDown = false;
992 localGrab.button.className = localGrab.button.className.replace( " frame-active", "");
993 if (ev.target == localGrab.button)
994 sendInput ("W", [localGrab.surface.id]);
1000 sendInput ("B", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, button]);
1002 if (grab.window != null && grab.implicit)
1008 /* Some of the keyboard handling code is from noVNC and
1009 * (c) Joel Martin (github@martintribe.org), used with permission
1011 * https://github.com/kanaka/noVNC/blob/master/include/input.js
1014 var unicodeTable = {
2275 var ON_KEYDOWN = 1 << 0; /* Report on keydown, otherwise wait until keypress */
2278 var specialKeyTable = {
2279 // These generate a keyDown and keyPress in Firefox and Opera
2280 8: [0xFF08, ON_KEYDOWN], // BACKSPACE
2281 13: [0xFF0D, ON_KEYDOWN], // ENTER
2283 // This generates a keyDown and keyPress in Opera
2284 9: [0xFF09, ON_KEYDOWN], // TAB
2286 27: 0xFF1B, // ESCAPE
2287 46: 0xFFFF, // DELETE
2290 33: 0xFF55, // PAGE_UP
2291 34: 0xFF56, // PAGE_DOWN
2292 45: 0xFF63, // INSERT
2295 39: 0xFF53, // RIGHT
2297 16: 0xFFE1, // SHIFT
2298 17: 0xFFE3, // CONTROL
2299 18: 0xFFE9, // Left ALT (Mac Command)
2314 function getEventKeySym(ev) {
2315 if (typeof ev.which !== "undefined" && ev.which > 0)
2320 // This is based on the approach from noVNC. We handle
2321 // everything in keydown that we have all info for, and that
2322 // are not safe to pass on to the browser (as it may do something
2323 // with the key. The rest we pass on to keypress so we can get the
2324 // translated keysym.
2325 function getKeysymSpecial(ev) {
2326 if (ev.keyCode in specialKeyTable) {
2327 var r = specialKeyTable[ev.keyCode];
2329 if (typeof r != 'number') {
2333 if (ev.type === 'keydown' || flags & ON_KEYDOWN)
2336 // If we don't hold alt or ctrl, then we should be safe to pass
2337 // on to keypressed and look at the translated data
2338 if (!ev.ctrlKey && !ev.altKey)
2341 var keysym = getEventKeySym(ev);
2345 case 186 : keysym = 59; break; // ; (IE)
2346 case 187 : keysym = 61; break; // = (IE)
2347 case 188 : keysym = 44; break; // , (Mozilla, IE)
2348 case 109 : // - (Mozilla, Opera)
2349 if (true /* TODO: check if browser is firefox or opera */)
2352 case 189 : keysym = 45; break; // - (IE)
2353 case 190 : keysym = 46; break; // . (Mozilla, IE)
2354 case 191 : keysym = 47; break; // / (Mozilla, IE)
2355 case 192 : keysym = 96; break; // ` (Mozilla, IE)
2356 case 219 : keysym = 91; break; // [ (Mozilla, IE)
2357 case 220 : keysym = 92; break; // \ (Mozilla, IE)
2358 case 221 : keysym = 93; break; // ] (Mozilla, IE)
2359 case 222 : keysym = 39; break; // ' (Mozilla, IE)
2362 /* Remap shifted and unshifted keys */
2363 if (!!ev.shiftKey) {
2365 case 48 : keysym = 41 ; break; // ) (shifted 0)
2366 case 49 : keysym = 33 ; break; // ! (shifted 1)
2367 case 50 : keysym = 64 ; break; // @ (shifted 2)
2368 case 51 : keysym = 35 ; break; // # (shifted 3)
2369 case 52 : keysym = 36 ; break; // $ (shifted 4)
2370 case 53 : keysym = 37 ; break; // % (shifted 5)
2371 case 54 : keysym = 94 ; break; // ^ (shifted 6)
2372 case 55 : keysym = 38 ; break; // & (shifted 7)
2373 case 56 : keysym = 42 ; break; // * (shifted 8)
2374 case 57 : keysym = 40 ; break; // ( (shifted 9)
2375 case 59 : keysym = 58 ; break; // : (shifted `)
2376 case 61 : keysym = 43 ; break; // + (shifted ;)
2377 case 44 : keysym = 60 ; break; // < (shifted ,)
2378 case 45 : keysym = 95 ; break; // _ (shifted -)
2379 case 46 : keysym = 62 ; break; // > (shifted .)
2380 case 47 : keysym = 63 ; break; // ? (shifted /)
2381 case 96 : keysym = 126; break; // ~ (shifted `)
2382 case 91 : keysym = 123; break; // { (shifted [)
2383 case 92 : keysym = 124; break; // | (shifted \)
2384 case 93 : keysym = 125; break; // } (shifted ])
2385 case 39 : keysym = 34 ; break; // " (shifted ')
2387 } else if ((keysym >= 65) && (keysym <=90)) {
2388 /* Remap unshifted A-Z */
2390 } else if (ev.keyLocation === 3) {
2393 case 96 : keysym = 48; break; // 0
2394 case 97 : keysym = 49; break; // 1
2395 case 98 : keysym = 50; break; // 2
2396 case 99 : keysym = 51; break; // 3
2397 case 100: keysym = 52; break; // 4
2398 case 101: keysym = 53; break; // 5
2399 case 102: keysym = 54; break; // 6
2400 case 103: keysym = 55; break; // 7
2401 case 104: keysym = 56; break; // 8
2402 case 105: keysym = 57; break; // 9
2403 case 109: keysym = 45; break; // -
2404 case 110: keysym = 46; break; // .
2405 case 111: keysym = 47; break; // /
2412 /* Translate DOM keyPress event to keysym value */
2413 function getKeysym(ev) {
2416 keysym = getEventKeySym(ev);
2418 if ((keysym > 255) && (keysym < 0xFF00)) {
2419 // Map Unicode outside Latin 1 to gdk keysyms
2420 keysym = unicodeTable[keysym];
2421 if (typeof keysym === 'undefined')
2428 function copyKeyEvent(ev) {
2429 var members = ['type', 'keyCode', 'charCode', 'which',
2430 'altKey', 'ctrlKey', 'shiftKey',
2431 'keyLocation', 'keyIdentifier'], i, obj = {};
2432 for (i = 0; i < members.length; i++) {
2433 if (typeof ev[members[i]] !== "undefined")
2434 obj[members[i]] = ev[members[i]];
2439 function pushKeyEvent(fev) {
2440 keyDownList.push(fev);
2443 function getKeyEvent(keyCode, pop) {
2445 for (i = keyDownList.length-1; i >= 0; i--) {
2446 if (keyDownList[i].keyCode === keyCode) {
2447 if ((typeof pop !== "undefined") && pop)
2448 fev = keyDownList.splice(i, 1)[0];
2450 fev = keyDownList[i];
2457 function ignoreKeyEvent(ev) {
2458 // Blarg. Some keys have a different keyCode on keyDown vs keyUp
2459 if (ev.keyCode === 229) {
2460 // French AZERTY keyboard dead key.
2461 // Lame thing is that the respective keyUp is 219 so we can't
2462 // properly ignore the keyUp event
2468 function handleKeyDown(e) {
2469 var fev = null, ev = (e ? e : window.event), keysym = null, suppress = false;
2471 fev = copyKeyEvent(ev);
2473 keysym = getKeysymSpecial(ev);
2474 // Save keysym decoding for use in keyUp
2475 fev.keysym = keysym;
2477 // If it is a key or key combination that might trigger
2478 // browser behaviors or it has no corresponding keyPress
2479 // event, then send it immediately
2480 if (!ignoreKeyEvent(ev))
2481 sendInput("k", [realWindowWithMouse, keysym, lastState]);
2485 if (! ignoreKeyEvent(ev)) {
2486 // Add it to the list of depressed keys
2491 // Suppress bubbling/default actions
2492 return cancelEvent(ev);
2495 // Allow the event to bubble and become a keyPress event which
2496 // will have the character code translated
2500 function handleKeyPress(e) {
2501 var ev = (e ? e : window.event), kdlen = keyDownList.length, keysym = null;
2503 if (((ev.which !== "undefined") && (ev.which === 0)) ||
2504 getKeysymSpecial(ev)) {
2505 // Firefox and Opera generate a keyPress event even if keyDown
2506 // is suppressed. But the keys we want to suppress will have
2508 // - the which attribute set to 0
2509 // - getKeysymSpecial() will identify it
2510 return cancelEvent(ev);
2513 keysym = getKeysym(ev);
2515 // Modify the the which attribute in the depressed keys list so
2516 // that the keyUp event will be able to have the character code
2517 // translation available.
2519 keyDownList[kdlen-1].keysym = keysym;
2521 //log("keyDownList empty when keyPress triggered");
2524 // Send the translated keysym
2526 sendInput ("k", [realWindowWithMouse, keysym, lastState]);
2528 // Stop keypress events just in case
2529 return cancelEvent(ev);
2532 function handleKeyUp(e) {
2533 var fev = null, ev = (e ? e : window.event), i, keysym;
2535 fev = getKeyEvent(ev.keyCode, true);
2538 keysym = fev.keysym;
2540 //log("Key event (keyCode = " + ev.keyCode + ") not found on keyDownList");
2545 sendInput ("K", [realWindowWithMouse, keysym, lastState]);
2546 return cancelEvent(ev);
2549 function onKeyDown (ev) {
2552 return cancelEvent(ev);
2553 return handleKeyDown(ev);
2556 function onKeyPress(ev) {
2559 return cancelEvent(ev);
2560 return handleKeyPress(ev);
2563 function onKeyUp (ev) {
2566 return cancelEvent(ev);
2567 return handleKeyUp(ev);
2570 function cancelEvent(ev)
2572 ev = ev ? ev : window.event;
2573 if (ev.stopPropagation)
2574 ev.stopPropagation();
2575 if (ev.preventDefault)
2576 ev.preventDefault();
2577 ev.cancelBubble = true;
2579 ev.returnValue = false;
2583 function onMouseWheel(ev)
2588 ev = ev ? ev : window.event;
2590 var id = getSurfaceId(ev);
2591 var pos = getPositionsFromEvent(ev, id);
2593 var offset = ev.detail ? ev.detail : ev.wheelDelta;
2597 sendInput ("s", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, dir]);
2599 return cancelEvent(ev);
2602 function setupDocument(document)
2604 document.oncontextmenu = function () { return false; };
2605 document.onmousemove = onMouseMove;
2606 document.onmouseover = onMouseOver;
2607 document.onmouseout = onMouseOut;
2608 document.onmousedown = onMouseDown;
2609 document.onmouseup = onMouseUp;
2610 document.onkeydown = onKeyDown;
2611 document.onkeypress = onKeyPress;
2612 document.onkeyup = onKeyUp;
2614 if (document.addEventListener) {
2615 document.addEventListener('DOMMouseScroll', onMouseWheel, false);
2616 document.addEventListener('mousewheel', onMouseWheel, false);
2617 } else if (document.attachEvent) {
2618 element.attachEvent("onmousewheel", onMouseWheel);
2622 function newWS(loc) {
2624 if ("WebSocket" in window) {
2625 ws = new WebSocket(loc, "broadway");
2626 } else if ("MozWebSocket" in window) { // Firefox 6
2627 ws = new MozWebSocket(loc);
2629 alert("WebSocket not supported, broadway will not work!");
2636 var url = window.location.toString();
2637 var query_string = url.split("?");
2638 if (query_string.length > 1) {
2639 var params = query_string[1].split("&");
2642 var loc = window.location.toString().replace("http:", "ws:");
2643 loc = loc.substr(0, loc.lastIndexOf('/')) + "/socket";
2645 var supports_binary = newWS (loc + "-test").binaryType == "blob";
2646 if (supports_binary) {
2647 ws = newWS (loc + "-bin");
2648 ws.binaryType = "arraybuffer";
2653 ws.onopen = function() {
2656 w = window.innerWidth;
2657 h = window.innerHeight;
2658 window.onresize = function(ev) {
2660 w = window.innerWidth;
2661 h = window.innerHeight;
2662 sendInput ("d", [w, h]);
2664 sendInput ("d", [w, h]);
2666 ws.onclose = function() {
2669 ws.onmessage = function(event) {
2670 handleMessage(event.data);
2673 setupDocument(document);