grab.window = null;
grab.ownerEvents = false;
grab.implicit = false;
+var localGrab = null;
var lastSerial = 0;
var lastX = 0;
var lastY = 0;
var realWindowWithMouse = 0;
var windowWithMouse = 0;
var surfaces = {};
+var stackingOrder = [];
var outstandingCommands = new Array();
var inputSocket = null;
-var frameSizeX = -1;
-var frameSizeY = -1;
-
var GDK_CROSSING_NORMAL = 0;
var GDK_CROSSING_GRAB = 1;
{
var commands = surface.drawQueue;
surface.queue = [];
- var context = surface.context;
+ var context = surface.canvas.getContext("2d");
+ context.globalCompositeOperation = "source-over";
var i = 0;
for (i = 0; i < commands.length; i++) {
var cmd = commands[i];
oldCanvas.parentNode.removeChild(oldCanvas);
surface.canvas = canvas;
+ if (surface.toplevelElement == oldCanvas)
+ surface.toplevelElement = canvas;
surface.document = doc;
-
- var context = canvas.getContext("2d");
- context.globalCompositeOperation = "source-over";
- surface.context = context;
}
}
+function sendConfigureNotify(surface)
+{
+ sendInput("w", [surface.id, surface.x, surface.y, surface.width, surface.height]);
+}
+
var windowGeometryTimeout = null;
-function updateBrowserWindowGeometry(win) {
+function updateBrowserWindowGeometry(win, alwaysSendConfigure) {
if (win.closed)
return;
var x = surface.x;
var y = surface.y;
- if (frameSizeX > 0) {
- x = win.screenX + frameSizeX;
- y = win.screenY + frameSizeY;
+
+ 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 (x != surface.x || y != surface.y ||
+ if (alwaysSendConfigure || x != surface.x || y != surface.y ||
innerW != surface.width || innerH != surface.height) {
var oldX = surface.x;
var oldY = surface.y;
resizeCanvas(surface.canvas, innerW, innerH);
surface.width = innerW;
surface.height = innerH;
- sendInput ("w", [surface.id, surface.x, surface.y, surface.width, surface.height]);
+ sendConfigureNotify(surface);
for (id in surfaces) {
- if (surfaces[id].transientToplevel != null && surfaces[id].transientToplevel == surface) {
- var childSurface = surfaces[id];
+ var childSurface = surfaces[id];
+ var transientToplevel = getTransientToplevel(childSurface);
+ if (transientToplevel != null && transientToplevel == surface) {
childSurface.x += surface.x - oldX;
childSurface.y += surface.y - oldY;
- sendInput ("w", [childSurface.id, childSurface.x, childSurface.y, childSurface.width, childSurface.height]);
+ sendConfigureNotify(childSurface);
}
}
}
sendInput ("W", [surface.id]);
for (id in surfaces) {
- if (surfaces[id].transientToplevel != null &&
- surfaces[id].transientToplevel == surface) {
- var childSurface = surfaces[id];
+ var childSurface = surfaces[id];
+ var transientToplevel = getTransientToplevel(childSurface);
+ if (transientToplevel != null && transientToplevel == surface) {
sendInput ("W", [childSurface.id]);
}
}
function registerWindow(win)
{
toplevelWindows.push(win);
- win.onresize = function(ev) { updateBrowserWindowGeometry(ev.target); };
+ win.onresize = function(ev) { updateBrowserWindowGeometry(ev.target, false); };
if (!windowGeometryTimeout)
- windowGeometryTimeout = setInterval(function () { toplevelWindows.forEach(updateBrowserWindowGeometry); }, 2000);
+ 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); };
}
function getTransientToplevel(surface)
{
- while (surface.transientParent != 0) {
+ while (surface && surface.transientParent != 0) {
surface = surfaces[surface.transientParent];
- if (surface.window)
+ if (surface && surface.window)
return surface;
}
return null;
}
+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;
+}
+
+function parseOffset(value)
+{
+ var px = value.indexOf("px");
+ if (px > 0)
+ return parseInt(value.slice(0,px));
+ return 0;
+}
+
+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.transientToplevel = null;
+ surface.frame = null;
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
canvas.surface = surface;
- canvas.style["position"] = "absolute";
- canvas.style["left"] = "0px";
- canvas.style["top"] = "0px";
- canvas.style["display"] = "none";
- document.body.appendChild(canvas);
surface.canvas = canvas;
+ var toplevelElement;
- var context = canvas.getContext("2d");
- context.globalCompositeOperation = "source-over";
- surface.context = context;
+ 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);
+
+ sendConfigureNotify(surface);
}
function cmdShowSurface(id)
if (useToplevelWindows) {
var doc = document;
if (!surface.isTemp) {
- var win = window.open('','_blank',
- 'width='+surface.width+',height='+surface.height+
- ',left='+surface.x+',top='+surface.y+',screenX='+surface.x+',screenY='+surface.y+
- ',location=no,menubar=no,scrollbars=no,toolbar=no');
+ 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;
xOffset = 0;
yOffset = 0;
} else {
- surface.transientToplevel = getTransientToplevel(surface);
- if (surface.transientToplevel) {
- doc = surface.transientToplevel.window.document;
- xOffset = surface.x - surface.transientToplevel.x;
- yOffset = surface.y - surface.transientToplevel.y;
+ var transientToplevel = getTransientToplevel(surface);
+ if (transientToplevel) {
+ doc = transientToplevel.window.document;
+ xOffset = surface.x - transientToplevel.x;
+ yOffset = surface.y - transientToplevel.y;
}
}
ensureSurfaceInDocument(surface, doc);
+ } else {
+ if (surface.frame) {
+ var offset = getFrameOffset(surface);
+ xOffset -= offset.x;
+ yOffset -= offset.y;
+ }
}
- surface.canvas.style["position"] = "absolute";
- surface.canvas.style["left"] = xOffset + "px";
- surface.canvas.style["top"] = yOffset + "px";
- surface.canvas.style["display"] = "inline";
+ surface.toplevelElement.style["left"] = xOffset + "px";
+ surface.toplevelElement.style["top"] = yOffset + "px";
+ surface.toplevelElement.style["visibility"] = "visible";
+
+ restackWindows();
+
+ if (surface.window)
+ updateBrowserWindowGeometry(surface.window, false);
}
function cmdHideSurface(id)
return;
surface.visible = false;
- surface.canvas.style["display"] = "none";
+ var element = surface.toplevelElement;
+
+ element.style["visibility"] = "hidden";
// Import the canvas into the main document
ensureSurfaceInDocument(surface, document);
return;
surface.transientParent = parentId;
- if (surface.visible && surface.isTemp) {
- alert("TODO: move temps between transient parents when visible");
+ 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 canvas = surfaces[id].canvas;
- delete surfaces[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;
* 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);
+ surface.window.moveTo(surface.x, surface.y);
} else {
var xOffset = surface.x;
var yOffset = surface.y;
yOffset = surface.y - transientToplevel.y;
}
- surface.canvas.style["left"] = xOffset + "px";
- surface.canvas.style["top"] = yOffset + "px";
+ 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";
}
}
+
+ if (surface.window) {
+ updateBrowserWindowGeometry(surface.window, true);
+ } else {
+ sendConfigureNotify(surface);
+ }
}
function cmdResizeSurface(id, w, h)
if (surface.window) {
resizeBrowserWindow(surface.window, w, h);
+ updateBrowserWindowGeometry(surface.window, true);
+ } else {
+ sendConfigureNotify(surface);
}
}
case 's': // create new surface
var id = base64_16(cmd, i);
i = i + 3;
- var x = base64_16(cmd, i);
+ var x = base64_16s(cmd, i);
i = i + 3;
- var y = base64_16(cmd, i);
+ var y = base64_16s(cmd, i);
i = i + 3;
var w = base64_16(cmd, i);
i = i + 3;
lastTimeStamp = ev.timeStamp;
if (ev.target.surface && ev.target.surface.window) {
var win = ev.target.surface.window;
- if (ev.screenX != undefined && ev.clientX != undefined) {
- var newFrameSizeX = ev.screenX - ev.clientX - win.screenX;
- var newFrameSizeY = ev.screenY - ev.clientY - win.screenY;
- if (newFrameSizeX != frameSizeX || newFrameSizeY != frameSizeY) {
- frameSizeX = newFrameSizeX;
- frameSizeY = newFrameSizeY;
- toplevelWindows.forEach(updateBrowserWindowGeometry);
- }
- }
- updateBrowserWindowGeometry(win);
+ 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);
function onMouseOver (ev) {
updateForEvent(ev);
+ if (localGrab)
+ return;
var id = getSurfaceId(ev);
realWindowWithMouse = id;
id = getEffectiveEventTarget (id);
function onMouseOut (ev) {
updateForEvent(ev);
+ if (localGrab)
+ return;
var id = getSurfaceId(ev);
var origId = id;
id = getEffectiveEventTarget (id);
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, true);
- var button = ev.button + 1;
- lastState = lastState | getButtonMask (button);
sendInput ("b", [realWindowWithMouse, id, pos.rootX, pos.rootY, pos.winX, pos.winY, lastState, button]);
}
function onMouseUp (ev) {
updateForEvent(ev);
- var id = getSurfaceId(ev);
- id = getEffectiveEventTarget (id);
- var pos = getPositionsFromEvent(ev, id);
var button = ev.button + 1;
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)
var lastKeyDown = 0;
function onKeyDown (ev) {
updateForEvent(ev);
+ if (localGrab)
+ return;
var keyCode = ev.keyCode;
if (keyCode != lastKeyDown) {
sendInput ("k", [keyCode]);
function onKeyUp (ev) {
updateForEvent(ev);
+ if (localGrab)
+ return;
var keyCode = ev.keyCode;
sendInput ("K", [keyCode]);
lastKeyDown = 0;
function onMouseWheel(ev)
{
updateForEvent(ev);
+ if (localGrab)
+ return;
ev = ev ? ev : window.event;
var id = getSurfaceId(ev);
function connect()
{
+ 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') {
var ws = new WebSocket(loc, "broadway");
ws.onopen = function() {
inputSocket = ws;
-
var w, h;
if (useToplevelWindows) {
w = window.screen.width;
} else {
w = window.innerWidth;
h = window.innerHeight;
- win.onresize = function(ev) {
+ window.onresize = function(ev) {
var w, h;
w = window.innerWidth;
h = window.innerHeight;