logDiv.appendChild(document.createElement('br'));
}
-var base64_val = [
- 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
- 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
- 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255,
- 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
- 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
- 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255
+var base64Values = [
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+ 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255,
+ 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
+ 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255
]
function base64_8(str, index) {
- var v =
- (base64_val[str.charCodeAt(index)]) +
- (base64_val[str.charCodeAt(index+1)] << 6);
- return v;
+ var v =
+ (base64Values[str.charCodeAt(index)]) +
+ (base64Values[str.charCodeAt(index+1)] << 6);
+ return v;
}
function base64_16(str, index) {
- var v =
- (base64_val[str.charCodeAt(index)]) +
- (base64_val[str.charCodeAt(index+1)] << 6) +
- (base64_val[str.charCodeAt(index+2)] << 12);
- return v;
+ var v =
+ (base64Values[str.charCodeAt(index)]) +
+ (base64Values[str.charCodeAt(index+1)] << 6) +
+ (base64Values[str.charCodeAt(index+2)] << 12);
+ return v;
}
function base64_16s(str, index) {
- var v = base64_16(str, index);
- if (v > 32767)
- return v - 65536;
- else
- return v;
+ var v = base64_16(str, index);
+ if (v > 32767)
+ return v - 65536;
+ else
+ return v;
}
function base64_24(str, index) {
- var v =
- (base64_val[str.charCodeAt(index)]) +
- (base64_val[str.charCodeAt(index+1)] << 6) +
- (base64_val[str.charCodeAt(index+2)] << 12) +
- (base64_val[str.charCodeAt(index+3)] << 18);
- return v;
+ var v =
+ (base64Values[str.charCodeAt(index)]) +
+ (base64Values[str.charCodeAt(index+1)] << 6) +
+ (base64Values[str.charCodeAt(index+2)] << 12) +
+ (base64Values[str.charCodeAt(index+3)] << 18);
+ return v;
}
function base64_32(str, index) {
- var v =
- (base64_val[str.charCodeAt(index)]) +
- (base64_val[str.charCodeAt(index+1)] << 6) +
- (base64_val[str.charCodeAt(index+2)] << 12) +
- (base64_val[str.charCodeAt(index+3)] << 18) +
- (base64_val[str.charCodeAt(index+4)] << 24) +
- (base64_val[str.charCodeAt(index+5)] << 30);
- return v;
+ var v =
+ (base64Values[str.charCodeAt(index)]) +
+ (base64Values[str.charCodeAt(index+1)] << 6) +
+ (base64Values[str.charCodeAt(index+2)] << 12) +
+ (base64Values[str.charCodeAt(index+3)] << 18) +
+ (base64Values[str.charCodeAt(index+4)] << 24) +
+ (base64Values[str.charCodeAt(index+5)] << 30);
+ return v;
}
function createXHR()
{
- try { return new XMLHttpRequest(); } catch(e) {}
- try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) {}
- try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e) {}
- try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {}
- try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
+ try { return new XMLHttpRequest(); } catch(e) {}
+ try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) {}
+ try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e) {}
+ try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {}
+ try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
- return null;
+ return null;
}
-var grab = new Object();
-grab.window = null;
-grab.owner_events = false;
-grab.time = 0;
-grab.implicit = false;
-var last_serial = 0;
-var last_x = 0;
-var last_y = 0;
-var last_state;
-var real_window_with_mouse = 0;
-var window_with_mouse = 0;
-var surfaces = {};
-var outstanding_commands = new Array();
-var input_socket = null;
+/* This resizes the window so the *inner* size is the specified size */
+function resizeBrowserWindow(window, w, h) {
+ var innerW = window.innerWidth;
+ var innerH = window.innerHeight;
+
+ var outerW = window.outerWidth;
+ var outerH = window.outerHeight;
-function initContext(canvas, x, y, id)
+ window.resizeTo(w + outerW - innerW,
+ h + outerH - innerH);
+}
+
+function resizeCanvas(canvas, w, h)
{
- canvas.surface_id = id;
- canvas.style["position"] = "absolute";
- canvas.style["left"] = x + "px";
- canvas.style["top"] = y + "px";
- canvas.style["display"] = "none";
- var context = canvas.getContext("2d");
- context.globalCompositeOperation = "source-over";
- document.body.appendChild(canvas);
- context.drawQueue = [];
+ /* Canvas resize clears the data, so we need to save it first */
+ var tmpCanvas = canvas.ownerDocument.createElement("canvas");
+ tmpCanvas.width = canvas.width;
+ tmpCanvas.height = canvas.height;
+ var tmpContext = tmpCanvas.getContext("2d");
+ tmpContext.globalCompositeOperation = "copy";
+ tmpContext.drawImage(canvas, 0, 0, tmpCanvas.width, tmpCanvas.height);
+
+ canvas.width = w;
+ canvas.height = h;
- return context;
+ var context = canvas.getContext("2d");
+
+ context.globalCompositeOperation = "copy";
+ context.drawImage(tmpCanvas, 0, 0, tmpCanvas.width, tmpCanvas.height);
}
-var GDK_GRAB_SUCCESS = 0;
-var GDK_GRAB_ALREADY_GRABBED = 1;
-var GDK_GRAB_INVALID_TIME = 2;
+var useToplevelWindows = false;
+var toplevelWindows = [];
+var grab = new Object();
+grab.window = null;
+grab.ownerEvents = false;
+grab.implicit = false;
+var localGrab = null;
+var lastSerial = 0;
+var lastX = 0;
+var lastY = 0;
+var lastState;
+var lastTimeStamp = 0;
+var realWindowWithMouse = 0;
+var windowWithMouse = 0;
+var surfaces = {};
+var stackingOrder = [];
+var outstandingCommands = new Array();
+var inputSocket = null;
var GDK_CROSSING_NORMAL = 0;
var GDK_CROSSING_GRAB = 1;
{
var commands = surface.drawQueue;
surface.queue = [];
+ var context = surface.canvas.getContext("2d");
+ context.globalCompositeOperation = "source-over";
var i = 0;
for (i = 0; i < commands.length; i++) {
var cmd = commands[i];
- var context = surfaces[cmd.id];
switch (cmd.op) {
- /* put image data surface */
- case 'i':
+ case 'i': // put image data surface
context.globalCompositeOperation = "source-over";
context.drawImage(cmd.img, cmd.x, cmd.y);
break;
- /* copy rects */
- case 'b':
+ case 'b': // copy rects
context.save();
context.beginPath();
context.restore();
break;
- default:
+ default:
alert("Unknown drawing op " + cmd.op);
}
}
}
-function handleCommands(cmd_obj)
+function ensureSurfaceInDocument(surface, doc)
{
- var cmd = cmd_obj.data;
- var i = cmd_obj.pos;
-
- while (i < cmd.length) {
- var command = cmd[i++];
- last_serial = base64_32(cmd, i);
- i = i + 6;
- switch (command) {
- /* create new surface */
- case 's':
- var id = base64_16(cmd, i);
- i = i + 3;
- var x = base64_16(cmd, i);
- i = i + 3;
- var y = base64_16(cmd, i);
- i = i + 3;
- var w = base64_16(cmd, i);
- i = i + 3;
- var h = base64_16(cmd, i);
- i = i + 3;
- var surface = document.createElement("canvas");
- surface.width = w;
- surface.height = h;
- surfaces[id] = initContext(surface, x, y, id);
- break;
-
- /* show a surface */
- case 'S':
- var id = base64_16(cmd, i);
- i = i + 3;
- surfaces[id].canvas.style["display"] = "inline";
- break;
-
- /* hide a surface */
- case 'H':
- var id = base64_16(cmd, i);
- i = i + 3;
- surfaces[id].canvas.style["display"] = "none";
- break;
-
- /* delete surface */
- case 'd':
- var id = base64_16(cmd, i);
- i = i + 3;
- var canvas = surfaces[id].canvas;
- delete surfaces[id];
- canvas.parentNode.removeChild(canvas);
-
- break;
-
- /* move a surface */
- case 'm':
- var id = base64_16(cmd, i);
- i = i + 3;
- var x = base64_16(cmd, i);
- i = i + 3;
- var y = base64_16(cmd, i);
- i = i + 3;
- surfaces[id].canvas.style["left"] = x + "px";
- surfaces[id].canvas.style["top"] = y + "px";
- break;
-
- /* resize a surface */
- case 'r':
- var id = base64_16(cmd, i);
- i = i + 3;
- var w = base64_16(cmd, i);
- i = i + 3;
- var h = base64_16(cmd, i);
- i = i + 3;
- var surface = surfaces[id];
-
- /* Flush any outstanding draw ops before changing size */
- flushSurface(surface);
-
- /* Canvas resize clears the data, so we need to save it first */
- var tmpCanvas = document.createElement("canvas");
- tmpCanvas.width = surface.canvas.width;
- tmpCanvas.height = surface.canvas.height;
- var tmpContext = tmpCanvas.getContext("2d");
- tmpContext.globalCompositeOperation = "copy";
- tmpContext.drawImage(surface.canvas, 0, 0, tmpCanvas.width, tmpCanvas.height);
-
- surface.canvas.width = w;
- surface.canvas.height = h;
-
- surface.globalCompositeOperation = "copy";
- surface.drawImage(tmpCanvas, 0, 0, tmpCanvas.width, tmpCanvas.height);
-
- break;
-
- /* put image data surface */
- case 'i':
- var q = new Object();
- q.op = 'i';
- q.id = base64_16(cmd, i);
- i = i + 3;
- q.x = base64_16(cmd, i);
- i = i + 3;
- q.y = base64_16(cmd, i);
- i = i + 3;
- var size = base64_32(cmd, i);
- i = i + 6;
- var url = cmd.slice(i, i + size);
- i = i + size;
- q.img = new Image();
- q.img.src = url;
- surfaces[q.id].drawQueue.push(q);
- if (!q.img.complete) {
- cmd_obj.pos = i;
- q.img.onload = function() { handleOutstanding(); };
- return false;
+ if (surface.document != doc) {
+ var oldCanvas = surface.canvas;
+ var canvas = doc.importNode(oldCanvas, false);
+ doc.body.appendChild(canvas);
+ canvas.surface = surface;
+ oldCanvas.parentNode.removeChild(oldCanvas);
+
+ surface.canvas = canvas;
+ if (surface.toplevelElement == oldCanvas)
+ surface.toplevelElement = canvas;
+ surface.document = doc;
+ }
+}
+
+function sendConfigureNotify(surface)
+{
+ sendInput("w", [surface.id, surface.x, surface.y, surface.width, surface.height]);
+}
+
+var windowGeometryTimeout = null;
+
+function updateBrowserWindowGeometry(win, alwaysSendConfigure) {
+ if (win.closed)
+ return;
+
+ var surface = win.surface;
+
+ var innerW = win.innerWidth;
+ var innerH = win.innerHeight;
+
+ var x = surface.x;
+ var y = surface.y;
+
+ if (win.mozInnerScreenX != undefined) {
+ x = win.mozInnerScreenX;
+ y = win.mozInnerScreenY;
+ } else if (win.screenTop != undefined) {
+ x = win.screenTop;
+ y = win.screenLeft;
+ } else {
+ alert("No implementation to get window position");
+ }
+
+ if (alwaysSendConfigure || x != surface.x || y != surface.y ||
+ innerW != surface.width || innerH != surface.height) {
+ var oldX = surface.x;
+ var oldY = surface.y;
+ surface.x = x;
+ surface.y = y;
+ if (surface.width != innerW || surface.height != innerH)
+ resizeCanvas(surface.canvas, innerW, innerH);
+ surface.width = innerW;
+ surface.height = innerH;
+ sendConfigureNotify(surface);
+ for (id in surfaces) {
+ var childSurface = surfaces[id];
+ var transientToplevel = getTransientToplevel(childSurface);
+ if (transientToplevel != null && transientToplevel == surface) {
+ childSurface.x += surface.x - oldX;
+ childSurface.y += surface.y - oldY;
+ sendConfigureNotify(childSurface);
+ }
}
+ }
+}
+
+function browserWindowClosed(win) {
+ var surface = win.surface;
- break;
-
- /* copy rects */
- case 'b':
- var q = new Object();
- q.op = 'b';
- q.id = base64_16(cmd, i);
- i = i + 3;
-
- var nrects = base64_16(cmd, i);
- i = i + 3;
-
- q.rects = [];
- for (var r = 0; r < nrects; r++) {
- var rect = new Object();
- rect.x = base64_16(cmd, i);
- i = i + 3;
- rect.y = base64_16(cmd, i);
- i = i + 3;
- rect.w = base64_16(cmd, i);
- i = i + 3;
- rect.h = base64_16(cmd, i);
- i = i + 3;
- q.rects.push (rect);
+ sendInput ("W", [surface.id]);
+ for (id in surfaces) {
+ var childSurface = surfaces[id];
+ var transientToplevel = getTransientToplevel(childSurface);
+ if (transientToplevel != null && transientToplevel == surface) {
+ sendInput ("W", [childSurface.id]);
}
+ }
+}
- q.dx = base64_16s(cmd, i);
- i = i + 3;
- q.dy = base64_16s(cmd, i);
- i = i + 3;
- surfaces[q.id].drawQueue.push(q);
- break;
+function registerWindow(win)
+{
+ toplevelWindows.push(win);
+ win.onresize = function(ev) { updateBrowserWindowGeometry(ev.target, false); };
+ if (!windowGeometryTimeout)
+ windowGeometryTimeout = setInterval(function () {
+ for (var i = 0; i < toplevelWindows.length; i++)
+ toplevelWindows[i].updateBrowserWindowGeometry(toplevelWindows[i], false);
+ }, 2000);
+ win.onunload = function(ev) { browserWindowClosed(ev.target.defaultView); };
+}
- case 'f': // Flush surface
- var id = base64_16(cmd, i);
- i = i + 3;
+function unregisterWindow(win)
+{
+ var i = toplevelWindows.indexOf(win);
+ if (i >= 0)
+ toplevelWindows.splice(i, 1);
- flushSurface(surfaces[id]);
- break;
+ if (windowGeometryTimeout && toplevelWindows.length == 0) {
+ clearInterval(windowGeometryTimeout);
+ windowGeometryTimeout = null;
+ }
+}
- case 'q': // Query pointer
- var id = base64_16(cmd, i);
- i = i + 3;
+function getTransientToplevel(surface)
+{
+ while (surface && surface.transientParent != 0) {
+ surface = surfaces[surface.transientParent];
+ if (surface && surface.window)
+ return surface;
+ }
+ return null;
+}
- var pos = getPositionsFromAbsCoord(last_x, last_y, id);
+function getStyle(el, styleProp)
+{
+ if (el.currentStyle) {
+ return el.currentStyle[styleProp];
+ } else if (window.getComputedStyle) {
+ var win = el.ownerDocument.defaultView;
+ return win.getComputedStyle(el, null).getPropertyValue(styleProp);
+ }
+ return undefined;
+}
- send_input ("q", [pos.root_x, pos.root_y, pos.win_x, pos.win_y, window_with_mouse]);
- break;
+function parseOffset(value)
+{
+ var px = value.indexOf("px");
+ if (px > 0)
+ return parseInt(value.slice(0,px));
+ return 0;
+}
- case 'g': // Grab
- var id = base64_16(cmd, i);
- i = i + 3;
- var owner_events = cmd[i++] == '1';
- var time = base64_32(cmd, i);
- i = i + 6;
+function getFrameOffset(surface) {
+ var x = 0;
+ var y = 0;
+ var el = surface.canvas;
+ while (el != null && el != surface.frame) {
+ x += el.offsetLeft;
+ y += el.offsetTop;
+
+ /* For some reason the border is not includes in the offsets.. */
+ x += parseOffset(getStyle(el, "border-left-width"));
+ y += parseOffset(getStyle(el, "border-top-width"));
+
+ el = el.offsetParent;
+ }
+
+ /* Also include frame border as per above */
+ x += parseOffset(getStyle(el, "border-left-width"));
+ y += parseOffset(getStyle(el, "border-top-width"));
+
+ return {x: x, y: y};
+}
+
+var positionIndex = 0;
+function cmdCreateSurface(id, x, y, width, height, isTemp)
+{
+ var surface = { id: id, x: x, y:y, width: width, height: height, isTemp: isTemp };
+ surface.positioned = isTemp;
+ surface.drawQueue = [];
+ surface.transientParent = 0;
+ surface.visible = false;
+ surface.window = null;
+ surface.document = document;
+ surface.frame = null;
+
+ var canvas = document.createElement("canvas");
+ canvas.width = width;
+ canvas.height = height;
+ canvas.surface = surface;
+ surface.canvas = canvas;
+ var toplevelElement;
+
+ if (useToplevelWindows || isTemp) {
+ toplevelElement = canvas;
+ document.body.appendChild(canvas);
+ } else {
+ var frame = document.createElement("div");
+ frame.frameFor = surface;
+ frame.className = "frame-window";
+ surface.frame = frame;
+
+ var button = document.createElement("center");
+ var X = document.createTextNode("X");
+ button.appendChild(X);
+ button.className = "frame-close";
+ frame.appendChild(button);
+
+ var contents = document.createElement("div");
+ contents.className = "frame-contents";
+ frame.appendChild(contents);
+
+ canvas.style["display"] = "block";
+ contents.appendChild(canvas);
+
+ toplevelElement = frame;
+ document.body.appendChild(frame);
+
+ surface.x = 100 + positionIndex * 10;
+ surface.y = 100 + positionIndex * 10;
+ positionIndex = (positionIndex + 1) % 20;
+ }
+
+ surface.toplevelElement = toplevelElement;
+ toplevelElement.style["position"] = "absolute";
+ /* This positioning isn't strictly right for apps in another topwindow,
+ * but that will be fixed up when showing. */
+ toplevelElement.style["left"] = surface.x + "px";
+ toplevelElement.style["top"] = surface.y + "px";
+ toplevelElement.style["display"] = "inline";
+
+ /* We hide the frame with visibility rather than display none
+ * so getFrameOffset still works with hidden windows. */
+ toplevelElement.style["visibility"] = "hidden";
+
+ surfaces[id] = surface;
+ stackingOrder.push(surface);
- if (grab.window != null) {
- /* Previous grab, compare times */
- if (time != 0 && grab.time != 0 &&
- time > grab.time) {
- send_input ("g", [GDK_GRAB_INVALID_TIME]);
- break;
+ sendConfigureNotify(surface);
+}
+
+function cmdShowSurface(id)
+{
+ var surface = surfaces[id];
+
+ if (surface.visible)
+ return;
+ surface.visible = true;
+
+ var xOffset = surface.x;
+ var yOffset = surface.y;
+
+ if (useToplevelWindows) {
+ var doc = document;
+ if (!surface.isTemp) {
+ var options =
+ 'width='+surface.width+',height='+surface.height+
+ ',location=no,menubar=no,scrollbars=no,toolbar=no';
+ if (surface.positioned)
+ options = options +
+ ',left='+surface.x+',top='+surface.y+',screenX='+surface.x+',screenY='+surface.y;
+ var win = window.open('','_blank', options);
+ win.surface = surface;
+ registerWindow(win);
+ doc = win.document;
+ doc.open();
+ doc.write("<body></body>");
+ setupDocument(doc);
+
+ surface.window = win;
+ xOffset = 0;
+ yOffset = 0;
+ } else {
+ var transientToplevel = getTransientToplevel(surface);
+ if (transientToplevel) {
+ doc = transientToplevel.window.document;
+ xOffset = surface.x - transientToplevel.x;
+ yOffset = surface.y - transientToplevel.y;
}
}
- doGrab(id, owner_events, time, false);
+ ensureSurfaceInDocument(surface, doc);
+ } else {
+ if (surface.frame) {
+ var offset = getFrameOffset(surface);
+ xOffset -= offset.x;
+ yOffset -= offset.y;
+ }
+ }
- send_input ("g", [GDK_GRAB_SUCCESS]);
+ surface.toplevelElement.style["left"] = xOffset + "px";
+ surface.toplevelElement.style["top"] = yOffset + "px";
+ surface.toplevelElement.style["visibility"] = "visible";
- break;
+ restackWindows();
- case 'u': // Ungrab
- var time = base64_32(cmd, i);
- i = i + 6;
- send_input ("u", []);
+ if (surface.window)
+ updateBrowserWindowGeometry(surface.window, false);
+}
+
+function cmdHideSurface(id)
+{
+ var surface = surfaces[id];
+
+ if (!surface.visible)
+ return;
+ surface.visible = false;
+
+ var element = surface.toplevelElement;
+
+ element.style["visibility"] = "hidden";
- if (grab.window != null) {
- if (grab.time == 0 || time == 0 ||
- grab.time < time)
- grab.window = null;
+ // Import the canvas into the main document
+ ensureSurfaceInDocument(surface, document);
+
+ if (surface.window) {
+ unregisterWindow(surface.window);
+ surface.window.close();
+ surface.window = null;
+ }
+}
+
+function cmdSetTransientFor(id, parentId)
+{
+ var surface = surfaces[id];
+
+ if (surface.transientParent == parentId)
+ return;
+
+ surface.transientParent = parentId;
+ if (parentId != 0 && surfaces[parentId]) {
+ moveToHelper(surface, stackingOrder.indexOf(surfaces[parentId])+1);
+ }
+
+ if (surface.visible) {
+ restackWindows();
+ }
+}
+
+function restackWindows() {
+ for (var i = 0; i < stackingOrder.length; i++) {
+ var surface = stackingOrder[i];
+ surface.toplevelElement.style.zIndex = i;
+ }
+}
+
+function moveToHelper(surface, position) {
+ var i = stackingOrder.indexOf(surface);
+ stackingOrder.splice(i, 1);
+ if (position != undefined)
+ stackingOrder.splice(position, 0, surface);
+ else
+ stackingOrder.push(surface);
+
+ for (var cid in surfaces) {
+ var child = surfaces[cid];
+ if (child.transientParent == surface.id)
+ moveToHelper(child, stackingOrder.indexOf(surface) + 1);
+ }
+}
+
+function moveToTop(surface) {
+ moveToHelper(surface);
+ restackWindows();
+}
+
+
+function cmdDeleteSurface(id)
+{
+ var surface = surfaces[id];
+ var i = stackingOrder.indexOf(surface);
+ if (i >= 0)
+ stackingOrder.splice(i, 1);
+ var canvas = surface.canvas;
+ canvas.parentNode.removeChild(canvas);
+ var frame = surface.frame;
+ if (frame)
+ frame.parentNode.removeChild(frame);
+ delete surfaces[id];
+}
+
+function cmdMoveSurface(id, x, y)
+{
+ var surface = surfaces[id];
+ surface.positioned = true;
+ surface.x = x;
+ surface.y = y;
+
+ if (surface.visible) {
+ if (surface.window) {
+ /* TODO: This moves the outer frame position, we really want the inner position.
+ * However this isn't *strictly* invalid, as any WM could have done whatever it
+ * wanted with the positioning of the window.
+ */
+ surface.window.moveTo(surface.x, surface.y);
+ } else {
+ var xOffset = surface.x;
+ var yOffset = surface.y;
+
+ var transientToplevel = getTransientToplevel(surface);
+ if (transientToplevel) {
+ xOffset = surface.x - transientToplevel.x;
+ yOffset = surface.y - transientToplevel.y;
+ }
+
+ var element = surface.canvas;
+ if (surface.frame) {
+ element = surface.frame;
+ var offset = getFrameOffset(surface);
+ xOffset -= offset.x;
+ yOffset -= offset.y;
+ }
+
+ element.style["left"] = xOffset + "px";
+ element.style["top"] = yOffset + "px";
}
+ }
- break;
- default:
- alert("Unknown op " + command);
+ if (surface.window) {
+ updateBrowserWindowGeometry(surface.window, true);
+ } else {
+ sendConfigureNotify(surface);
}
- }
- return true;
}
-function handleOutstanding()
+function cmdResizeSurface(id, w, h)
{
- while (outstanding_commands.length > 0) {
- var cmd = outstanding_commands.shift();
- if (!handleCommands(cmd)) {
- outstanding_commands.unshift(cmd);
- return;
+ var surface = surfaces[id];
+
+ surface.width = w;
+ surface.height = h;
+
+ /* Flush any outstanding draw ops before changing size */
+ flushSurface(surface);
+
+ resizeCanvas(surface.canvas, w, h);
+
+ if (surface.window) {
+ resizeBrowserWindow(surface.window, w, h);
+ updateBrowserWindowGeometry(surface.window, true);
+ } else {
+ sendConfigureNotify(surface);
}
- }
}
-function handleLoad(event)
+function cmdFlushSurface(id)
{
- var cmd_obj = {};
- cmd_obj.data = event.target.responseText;
- cmd_obj.pos = 0;
+ flushSurface(surfaces[id]);
+}
- outstanding_commands.push(cmd_obj);
- if (outstanding_commands.length == 1) {
- handleOutstanding();
- }
+function cmdGrabPointer(id, ownerEvents)
+{
+ doGrab(id, ownerEvents, false);
+ sendInput ("g", []);
}
-function get_surface_id(ev) {
- var id = ev.target.surface_id;
- if (id != undefined)
- return id;
- return 0;
+function cmdUngrabPointer()
+{
+ sendInput ("u", []);
+
+ grab.window = null;
+}
+
+function handleCommands(cmdObj)
+{
+ var cmd = cmdObj.data;
+ var i = cmdObj.pos;
+
+ while (i < cmd.length) {
+ var command = cmd[i++];
+ lastSerial = base64_32(cmd, i);
+ i = i + 6;
+ switch (command) {
+ case 's': // create new surface
+ var id = base64_16(cmd, i);
+ i = i + 3;
+ var x = base64_16s(cmd, i);
+ i = i + 3;
+ var y = base64_16s(cmd, i);
+ i = i + 3;
+ var w = base64_16(cmd, i);
+ i = i + 3;
+ var h = base64_16(cmd, i);
+ i = i + 3;
+ var isTemp = cmd[i] == '1';
+ i = i + 1;
+ cmdCreateSurface(id, x, y, w, h, isTemp);
+ break;
+
+ case 'S': // Show a surface
+ var id = base64_16(cmd, i);
+ i = i + 3;
+ cmdShowSurface(id);
+ break;
+
+ case 'H': // Hide a surface
+ var id = base64_16(cmd, i);
+ i = i + 3;
+ cmdHideSurface(id);
+ break;
+
+ case 'p': // Set transient parent
+ var id = base64_16(cmd, i);
+ i = i + 3;
+ var parentId = base64_16(cmd, i);
+ i = i + 3;
+ cmdSetTransientFor(id, parentId);
+ break;
+
+ case 'd': // Delete surface
+ var id = base64_16(cmd, i);
+ i = i + 3;
+ cmdDeleteSurface(id);
+ break;
+
+ case 'm': // Move a surface
+ var id = base64_16(cmd, i);
+ i = i + 3;
+ var x = base64_16(cmd, i);
+ i = i + 3;
+ var y = base64_16(cmd, i);
+ i = i + 3;
+ cmdMoveSurface(id, x, y);
+ break;
+
+ case 'r': // Resize a surface
+ var id = base64_16(cmd, i);
+ i = i + 3;
+ var w = base64_16(cmd, i);
+ i = i + 3;
+ var h = base64_16(cmd, i);
+ i = i + 3;
+ cmdResizeSurface(id, w, h);
+ break;
+
+ case 'i': // Put image data surface
+ var q = new Object();
+ q.op = 'i';
+ q.id = base64_16(cmd, i);
+ i = i + 3;
+ q.x = base64_16(cmd, i);
+ i = i + 3;
+ q.y = base64_16(cmd, i);
+ i = i + 3;
+ var size = base64_32(cmd, i);
+ i = i + 6;
+ var url = cmd.slice(i, i + size);
+ i = i + size;
+ q.img = new Image();
+ q.img.src = url;
+ surfaces[q.id].drawQueue.push(q);
+ if (!q.img.complete) {
+ cmdObj.pos = i;
+ q.img.onload = function() { handleOutstanding(); };
+ return false;
+ }
+ break;
+
+ case 'b': // Copy rects
+ var q = new Object();
+ q.op = 'b';
+ q.id = base64_16(cmd, i);
+ i = i + 3;
+
+ var nrects = base64_16(cmd, i);
+ i = i + 3;
+
+ q.rects = [];
+ for (var r = 0; r < nrects; r++) {
+ var rect = new Object();
+ rect.x = base64_16(cmd, i);
+ i = i + 3;
+ rect.y = base64_16(cmd, i);
+ i = i + 3;
+ rect.w = base64_16(cmd, i);
+ i = i + 3;
+ rect.h = base64_16(cmd, i);
+ i = i + 3;
+ q.rects.push (rect);
+ }
+
+ q.dx = base64_16s(cmd, i);
+ i = i + 3;
+ q.dy = base64_16s(cmd, i);
+ i = i + 3;
+ surfaces[q.id].drawQueue.push(q);
+ break;
+
+ case 'f': // Flush surface
+ var id = base64_16(cmd, i);
+ i = i + 3;
+
+ cmdFlushSurface(id);
+ break;
+
+ case 'g': // Grab
+ var id = base64_16(cmd, i);
+ i = i + 3;
+ var ownerEvents = cmd[i++] == '1';
+
+ cmdGrabPointer(id, ownerEvents);
+ break;
+
+ case 'u': // Ungrab
+ cmdUngrabPointer();
+ break;
+ default:
+ alert("Unknown op " + command);
+ }
+ }
+ return true;
}
-function send_input(cmd, args)
+function handleOutstanding()
{
- if (input_socket != null) {
- input_socket.send(cmd + ([last_serial].concat(args)).join(","));
- }
+ while (outstandingCommands.length > 0) {
+ var cmd = outstandingCommands.shift();
+ if (!handleCommands(cmd)) {
+ outstandingCommands.unshift(cmd);
+ return;
+ }
+ }
}
-function get_document_coordinates(element)
+function handleLoad(event)
{
- var res = new Object();
- res.x = element.offsetLeft;
- res.y = element.offsetTop;
+ var cmdObj = {};
+ cmdObj.data = event.target.responseText;
+ cmdObj.pos = 0;
- var offsetParent = element.offsetParent;
- while (offsetParent != null) {
- res.x += offsetParent.offsetLeft;
- res.y += offsetParent.offsetTop;
- offsetParent = offsetParent.offsetParent;
+ outstandingCommands.push(cmdObj);
+ if (outstandingCommands.length == 1) {
+ handleOutstanding();
+ }
+}
+
+function getSurfaceId(ev) {
+ var surface = ev.target.surface;
+ if (surface != undefined)
+ return surface.id;
+ return 0;
+}
+
+function sendInput(cmd, args)
+{
+ if (inputSocket != null) {
+ inputSocket.send(cmd + ([lastSerial, lastTimeStamp].concat(args)).join(","));
}
- return res;
}
function getPositionsFromAbsCoord(absX, absY, relativeId) {
var res = Object();
- res.root_x = absX;
- res.root_y = absY;
- res.win_x = absX;
- res.win_y = absY;
+ res.rootX = absX;
+ res.rootY = absY;
+ res.winX = absX;
+ res.winY = absY;
if (relativeId != 0) {
- var pos = get_document_coordinates(surfaces[relativeId].canvas);
- res.win_x = res.win_x - pos.x;
- res.win_y = res.win_y - pos.y;
+ var surface = surfaces[relativeId];
+ res.winX = res.winX - surface.x;
+ res.winY = res.winY - surface.y;
}
return res;
}
function getPositionsFromEvent(ev, relativeId) {
- var res = getPositionsFromAbsCoord(ev.pageX, ev.pageY, relativeId);
+ var absX, absY;
+ if (useToplevelWindows) {
+ absX = ev.screenX;
+ absY = ev.screenY;
+ } else {
+ absX = ev.pageX;
+ absY = ev.pageY;
+ }
+ var res = getPositionsFromAbsCoord(absX, absY, relativeId);
- last_x = res.root_x;
- last_y = res.root_y;
+ lastX = res.rootX;
+ lastY = res.rootY;
return res;
}
function getEffectiveEventTarget (id) {
if (grab.window != null) {
- if (!grab.owner_events)
+ if (!grab.ownerEvents)
return grab.window;
if (id == 0)
return grab.window;
return id;
}
-function on_mouse_move (ev) {
- var id = get_surface_id(ev);
+function updateForEvent(ev) {
+ lastTimeStamp = ev.timeStamp;
+ if (ev.target.surface && ev.target.surface.window) {
+ var win = ev.target.surface.window;
+ updateBrowserWindowGeometry(win, false);
+ }
+}
+
+function onMouseMove (ev) {
+ updateForEvent(ev);
+ if (localGrab) {
+ var dx = ev.pageX - localGrab.lastX;
+ var dy = ev.pageY - localGrab.lastY;
+ var surface = localGrab.frame.frameFor;
+ surface.x += dx;
+ surface.y += dy;
+ var offset = getFrameOffset(surface);
+ localGrab.frame.style["left"] = (surface.x - offset.x) + "px";
+ localGrab.frame.style["top"] = (surface.y - offset.y) + "px";
+ sendConfigureNotify(surface);
+ localGrab.lastX = ev.pageX;
+ localGrab.lastY = ev.pageY;
+ return;
+ }
+ var id = getSurfaceId(ev);
id = getEffectiveEventTarget (id);
var pos = getPositionsFromEvent(ev, id);
- send_input ("m", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, last_state, ev.timeStamp]);
+ sendInput ("m", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState]);
}
-function on_mouse_over (ev) {
- var id = get_surface_id(ev);
- real_window_with_mouse = id;
+function onMouseOver (ev) {
+ updateForEvent(ev);
+ if (localGrab)
+ return;
+ var id = getSurfaceId(ev);
+ realWindowWithMouse = id;
id = getEffectiveEventTarget (id);
var pos = getPositionsFromEvent(ev, id);
- window_with_mouse = id;
- if (window_with_mouse != 0) {
- send_input ("e", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, last_state, ev.timeStamp, GDK_CROSSING_NORMAL]);
+ windowWithMouse = id;
+ if (windowWithMouse != 0) {
+ sendInput ("e", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_NORMAL]);
}
}
-function on_mouse_out (ev) {
- var id = get_surface_id(ev);
+function onMouseOut (ev) {
+ updateForEvent(ev);
+ if (localGrab)
+ return;
+ var id = getSurfaceId(ev);
var origId = id;
id = getEffectiveEventTarget (id);
var pos = getPositionsFromEvent(ev, id);
if (id != 0) {
- send_input ("l", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, last_state, ev.timeStamp, GDK_CROSSING_NORMAL]);
+ sendInput ("l", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_NORMAL]);
}
- real_window_with_mouse = 0;
- window_with_mouse = 0;
+ realWindowWithMouse = 0;
+ windowWithMouse = 0;
}
-function doGrab(id, owner_events, time, implicit) {
+function doGrab(id, ownerEvents, implicit) {
var pos;
- if (window_with_mouse != id) {
- if (window_with_mouse != 0) {
- pos = getPositionsFromAbsCoord(last_x, last_y, window_with_mouse);
- send_input ("l", [window_with_mouse, pos.root_x, pos.root_y, pos.win_x, pos.win_y, last_state, time, GDK_CROSSING_GRAB]);
+ if (windowWithMouse != id) {
+ if (windowWithMouse != 0) {
+ pos = getPositionsFromAbsCoord(lastX, lastY, windowWithMouse);
+ sendInput ("l", [realWindowWithMouse, windowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_GRAB]);
}
- pos = getPositionsFromAbsCoord(last_x, last_y, id);
- send_input ("e", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, last_state, time, GDK_CROSSING_GRAB]);
- window_with_mouse = id;
+ pos = getPositionsFromAbsCoord(lastX, lastY, id);
+ sendInput ("e", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_GRAB]);
+ windowWithMouse = id;
}
grab.window = id;
- grab.owner_events = owner_events;
- grab.time = time;
+ grab.ownerEvents = ownerEvents;
grab.implicit = implicit;
}
-function doUngrab(time) {
+function doUngrab() {
var pos;
- if (real_window_with_mouse != window_with_mouse) {
- if (window_with_mouse != 0) {
- pos = getPositionsFromAbsCoord(last_x, last_y, window_with_mouse);
- send_input ("l", [window_with_mouse, pos.root_x, pos.root_y, pos.win_x, pos.win_y, last_state, time, GDK_CROSSING_UNGRAB]);
+ if (realWindowWithMouse != windowWithMouse) {
+ if (windowWithMouse != 0) {
+ pos = getPositionsFromAbsCoord(lastX, lastY, windowWithMouse);
+ sendInput ("l", [realWindowWithMouse, windowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_UNGRAB]);
}
- if (real_window_with_mouse != 0) {
- pos = getPositionsFromAbsCoord(last_x, last_y, real_window_with_mouse);
- send_input ("e", [real_window_with_mouse, pos.root_x, pos.root_y, pos.win_x, pos.win_y, last_state, time, GDK_CROSSING_UNGRAB]);
+ if (realWindowWithMouse != 0) {
+ pos = getPositionsFromAbsCoord(lastX, lastY, realWindowWithMouse);
+ sendInput ("e", [realWindowWithMouse, realWindowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_UNGRAB]);
}
- window_with_mouse = real_window_with_mouse;
+ windowWithMouse = realWindowWithMouse;
}
grab.window = null;
}
-function on_mouse_down (ev) {
- var id = get_surface_id(ev);
+function onMouseDown (ev) {
+ updateForEvent(ev);
+ var button = ev.button + 1;
+ lastState = lastState | getButtonMask (button);
+ var id = getSurfaceId(ev);
id = getEffectiveEventTarget (id);
+
+ if (id == 0 && ev.target.frameFor) { /* mouse click on frame */
+ localGrab = new Object();
+ localGrab.frame = ev.target;
+ localGrab.lastX = ev.pageX;
+ localGrab.lastY = ev.pageY;
+ moveToTop(localGrab.frame.frameFor);
+ return;
+ }
+
var pos = getPositionsFromEvent(ev, id);
- if (grab.window != null)
- doGrab (id, false, ev.timeStamp, true);
- var button = ev.button + 1;
- last_state = last_state | getButtonMask (button);
- send_input ("b", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, last_state, ev.timeStamp, button]);
+ if (grab.window == null)
+ doGrab (id, false, true);
+ sendInput ("b", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, button]);
}
-function on_mouse_up (ev) {
- var id = get_surface_id(ev);
- id = getEffectiveEventTarget (id);
- var pos = getPositionsFromEvent(ev, id);
+function onMouseUp (ev) {
+ updateForEvent(ev);
var button = ev.button + 1;
- last_state = last_state & ~getButtonMask (button);
- send_input ("B", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, last_state, ev.timeStamp, button]);
+ lastState = lastState & ~getButtonMask (button);
+ var evId = getSurfaceId(ev);
+ id = getEffectiveEventTarget (evId);
+ var pos = getPositionsFromEvent(ev, id);
+
+ if (localGrab) {
+ localGrab = null;
+ realWindowWithMouse = evId;
+ if (windowWithMouse != id) {
+ if (windowWithMouse != 0) {
+ sendInput ("l", [realWindowWithMouse, windowWithMouse, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_NORMAL]);
+ }
+ windowWithMouse = id;
+ if (windowWithMouse != 0) {
+ sendInput ("e", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, GDK_CROSSING_NORMAL]);
+ }
+ }
+ return;
+ }
+
+ sendInput ("B", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, button]);
if (grab.window != null && grab.implicit)
doUngrab(ev.timeStamp);
}
-var last_key_down = 0;
-function on_key_down (ev) {
- var key_code = ev.keyCode;
- if (key_code != last_key_down) {
- send_input ("k", [key_code, ev.timeStamp]);
- last_key_down = key_code;
- }
+var lastKeyDown = 0;
+function onKeyDown (ev) {
+ updateForEvent(ev);
+ if (localGrab)
+ return;
+ var keyCode = ev.keyCode;
+ if (keyCode != lastKeyDown) {
+ sendInput ("k", [keyCode]);
+ lastKeyDown = keyCode;
+ }
}
-function on_key_up (ev) {
- var key_code = ev.keyCode;
- send_input ("K", [key_code, ev.timeStamp]);
- last_key_down = 0;
+function onKeyUp (ev) {
+ updateForEvent(ev);
+ if (localGrab)
+ return;
+ var keyCode = ev.keyCode;
+ sendInput ("K", [keyCode]);
+ lastKeyDown = 0;
}
-function cancel_event(ev)
+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;
-}
-
-function on_mouse_wheel(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;
+}
+
+function onMouseWheel(ev)
{
- ev = ev ? ev : window.event;
+ updateForEvent(ev);
+ if (localGrab)
+ return;
+ ev = ev ? ev : window.event;
- var id = get_surface_id(ev);
- var pos = getPositionsFromEvent(ev, id);
+ var id = getSurfaceId(ev);
+ var pos = getPositionsFromEvent(ev, id);
- var offset = ev.detail ? ev.detail : ev.wheelDelta;
- var dir = 0;
- if (offset > 0)
- dir = 1;
- send_input ("s", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, last_state, ev.timeStamp, dir]);
+ var offset = ev.detail ? ev.detail : ev.wheelDelta;
+ var dir = 0;
+ if (offset > 0)
+ dir = 1;
+ sendInput ("s", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, dir]);
- return cancel_event(ev);
+ return cancelEvent(ev);
+}
+
+function setupDocument(document)
+{
+ document.oncontextmenu = function () { return false; };
+ document.onmousemove = onMouseMove;
+ document.onmouseover = onMouseOver;
+ document.onmouseout = onMouseOut;
+ document.onmousedown = onMouseDown;
+ document.onmouseup = onMouseUp;
+ document.onkeydown = onKeyDown;
+ document.onkeyup = onKeyUp;
+
+ if (document.addEventListener) {
+ document.addEventListener('DOMMouseScroll', onMouseWheel, false);
+ document.addEventListener('mousewheel', onMouseWheel, false);
+ } else if (document.attachEvent) {
+ element.attachEvent("onmousewheel", onMouseWheel);
+ }
}
function connect()
{
- var xhr = createXHR();
- if (xhr) {
- if (typeof xhr.multipart == 'undefined') {
- alert("Sorry, this example only works in browsers that support multipart.");
- return;
- }
-
- xhr.multipart = true;
- xhr.open("GET", "/output", true);
- xhr.onload = handleLoad;
- xhr.send(null);
- }
-
- if ("WebSocket" in window) {
- var loc = window.location.toString().replace("http:", "ws:");
- loc = loc.substr(0, loc.lastIndexOf('/')) + "/input";
- var ws = new WebSocket(loc, "broadway");
- ws.onopen = function() {
- input_socket = ws;
- };
- ws.onclose = function() {
- input_socket = null;
- };
- } else {
- alert("WebSocket not supported, input will not work!");
- }
- document.oncontextmenu = function () { return false; };
- document.onmousemove = on_mouse_move;
- document.onmouseover = on_mouse_over;
- document.onmouseout = on_mouse_out;
- document.onmousedown = on_mouse_down;
- document.onmouseup = on_mouse_up;
- document.onkeydown = on_key_down;
- document.onkeyup = on_key_up;
-
- if (document.addEventListener) {
- document.addEventListener('DOMMouseScroll', on_mouse_wheel, false);
- document.addEventListener('mousewheel', on_mouse_wheel, false);
- } else if (document.attachEvent) {
- element.attachEvent("onmousewheel", on_mouse_wheel);
- }
+ var url = window.location.toString();
+ var query_string = url.split("?");
+ if (query_string.length > 1) {
+ var params = query_string[1].split("&");
+ if (params[0].indexOf("toplevel") != -1)
+ useToplevelWindows = true;
+ }
+ var xhr = createXHR();
+ if (xhr) {
+ if (typeof xhr.multipart == 'undefined') {
+ alert("Sorry, this example only works in browsers that support multipart.");
+ return;
+ }
+
+ xhr.multipart = true;
+ xhr.open("GET", "/output", true);
+ xhr.onload = handleLoad;
+ xhr.send(null);
+ }
+ if ("WebSocket" in window) {
+ var loc = window.location.toString().replace("http:", "ws:");
+ loc = loc.substr(0, loc.lastIndexOf('/')) + "/input";
+ var ws = new WebSocket(loc, "broadway");
+ ws.onopen = function() {
+ inputSocket = ws;
+ var w, h;
+ if (useToplevelWindows) {
+ w = window.screen.width;
+ h = window.screen.height;
+ } else {
+ w = window.innerWidth;
+ h = window.innerHeight;
+ window.onresize = function(ev) {
+ var w, h;
+ w = window.innerWidth;
+ h = window.innerHeight;
+ sendInput ("d", [w, h]);
+ };
+ }
+ sendInput ("d", [w, h]);
+ };
+ ws.onclose = function() {
+ inputSocket = null;
+ };
+ } else {
+ alert("WebSocket not supported, input will not work!");
+ }
+ setupDocument(document);
+ window.onunload = function (ev) {
+ for (var i = 0; i < toplevelWindows.length; i++)
+ toplevelWindows[i].close();
+ };
}