]> Pileus Git - ~andy/gtk/blobdiff - gdk/broadway/broadway.js
[broadway] Always send configure events from browser
[~andy/gtk] / gdk / broadway / broadway.js
index 63680b448aa8b731144a8616ade9050bc8f111bd..1386cb03a13cc7fd790eb68eecde03812f2528fa 100644 (file)
@@ -113,6 +113,7 @@ 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;
@@ -121,11 +122,9 @@ var lastTimeStamp = 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;
@@ -168,7 +167,8 @@ function flushSurface(surface)
 {
     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];
@@ -229,17 +229,20 @@ function ensureSurfaceInDocument(surface, doc)
        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;
 
@@ -250,12 +253,18 @@ function updateBrowserWindowGeometry(win) {
 
     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;
@@ -265,13 +274,14 @@ function updateBrowserWindowGeometry(win) {
            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);
            }
        }
     }
@@ -282,9 +292,9 @@ function browserWindowClosed(win) {
 
     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]);
        }
     }
@@ -293,9 +303,12 @@ function browserWindowClosed(win) {
 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); };
 }
 
@@ -313,40 +326,120 @@ function unregisterWindow(win)
 
 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)
@@ -363,10 +456,13 @@ 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;
@@ -378,21 +474,31 @@ function cmdShowSurface(id)
            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)
@@ -403,7 +509,9 @@ 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);
@@ -423,21 +531,61 @@ function cmdSetTransientFor(id, parentId)
        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;
 
@@ -447,7 +595,7 @@ function cmdMoveSurface(id, x, 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;
@@ -458,10 +606,24 @@ function cmdMoveSurface(id, x, 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)
@@ -478,6 +640,9 @@ function cmdResizeSurface(id, w, h)
 
     if (surface.window) {
        resizeBrowserWindow(surface.window, w, h);
+       updateBrowserWindowGeometry(surface.window, true);
+    } else {
+       sendConfigureNotify(surface);
     }
 }
 
@@ -512,9 +677,9 @@ function handleCommands(cmdObj)
        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;
@@ -733,21 +898,26 @@ function updateForEvent(ev) {
     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);
@@ -756,6 +926,8 @@ function onMouseMove (ev) {
 
 function onMouseOver (ev) {
     updateForEvent(ev);
+    if (localGrab)
+       return;
     var id = getSurfaceId(ev);
     realWindowWithMouse = id;
     id = getEffectiveEventTarget (id);
@@ -768,6 +940,8 @@ function onMouseOver (ev) {
 
 function onMouseOut (ev) {
     updateForEvent(ev);
+    if (localGrab)
+       return;
     var id = getSurfaceId(ev);
     var origId = id;
     id = getEffectiveEventTarget (id);
@@ -816,23 +990,49 @@ function doUngrab() {
 
 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)
@@ -842,6 +1042,8 @@ function onMouseUp (ev) {
 var lastKeyDown = 0;
 function onKeyDown (ev) {
     updateForEvent(ev);
+    if (localGrab)
+       return;
     var keyCode = ev.keyCode;
     if (keyCode != lastKeyDown) {
        sendInput ("k", [keyCode]);
@@ -851,6 +1053,8 @@ function onKeyDown (ev) {
 
 function onKeyUp (ev) {
     updateForEvent(ev);
+    if (localGrab)
+       return;
     var keyCode = ev.keyCode;
     sendInput ("K", [keyCode]);
     lastKeyDown = 0;
@@ -872,6 +1076,8 @@ function cancelEvent(ev)
 function onMouseWheel(ev)
 {
     updateForEvent(ev);
+    if (localGrab)
+       return;
     ev = ev ? ev : window.event;
 
     var id = getSurfaceId(ev);
@@ -907,6 +1113,13 @@ function setupDocument(document)
 
 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') {
@@ -926,7 +1139,6 @@ function connect()
        var ws = new WebSocket(loc, "broadway");
        ws.onopen = function() {
            inputSocket = ws;
-           
            var w, h;
            if (useToplevelWindows) {
                w = window.screen.width;
@@ -934,7 +1146,7 @@ function connect()
            } else {
                w = window.innerWidth;
                h = window.innerHeight;
-               win.onresize = function(ev) { 
+               window.onresize = function(ev) {
                    var w, h;
                    w = window.innerWidth;
                    h = window.innerHeight;