xpra icon
Bug tracker and wiki

This bug tracker and wiki are being discontinued
please use https://github.com/Xpra-org/xpra instead.


Ticket #473: xprahtml5.patch

File xprahtml5.patch, 26.7 KB (added by Antoine Martin, 8 years ago)

work by Joshua Higgins to make this use a canvas within a div for each window (much much better!)

  • index.html

     
    3434                                border: 0px solid black;
    3535                        }
    3636                        canvas {
    37                                 border: 0px solid black;
    38                                 display: none;
     37                                border: 1px solid black;
    3938                        }
     39                        .window {
     40                                border: 10px solid black;
     41                                position: absolute;
     42                                box-shadow: 0px 0px 35px #000000;
     43                        }
     44                        .windowhead {
     45                                height: 20px;
     46                                color: #000000;
     47                                background-color: #ffffff;
     48                        }
    4049                </style>
    4150
    4251                <script type="text/javascript" src="include/websock.js"></script>
     
    4857                <script type="text/javascript" src="include/window.js"></script>
    4958                <script type="text/javascript" src="include/util.js"></script>
    5059
     60                <script type="text/javascript" src="include/jquery.min.js"></script>
     61                <script type="text/javascript" src="include/jqueryui.min.js"></script>
     62
    5163                <script>
    5264                        var protocol;
    5365                        var status = "ready";
    5466
     67                        var topwindow = null;
     68
    5569                        var id_to_window = {};
    5670                        var window_to_id = {};
    5771                        var focus = -1;
    5872                        var screen_size_change_pending = false;
    5973
     74                        var screen_width = 800;
     75                        var screen_height = 600;
     76
    6077                        var OLD_ENCODING_NAMES_TO_NEW = {"x264" : "h264", "vpx" : "vp8"};
    6178                        var RGB_FORMATS = ["RGBX", "RGBA"];
    62                         var canvas_state = null;
    6379
    6480                        var caps_lock = null;
    6581                        var alt_modifier = null;
    6682                        var meta_modifier = null;
    6783
     84                        // disable right click menu:
     85                        window.oncontextmenu = function(e) {
     86                                //showCustomMenu();
     87                                return false;
     88                        }
     89
    6890                        /**
    6991                         * Returns the modifiers set for the current event.
    7092                         * We get the list of modifiers using "get_event_modifiers"
     
    95117                         */
    96118                        function processKeyEvent(pressed, event) {
    97119                                "use strict";
    98                                 if (canvas_state==null)
    99                                         return;
    100120                                // MSIE hack
    101121                                if (window.event)
    102122                                        event = window.event;
     
    125145                                if ((caps_lock && shift) || (!caps_lock && !shift))
    126146                                        str = str.toLowerCase();
    127147
    128                                 var win = canvas_state.topOfStack();
    129                                 if (win != null) {
     148                                //var win = canvas_state.topOfStack();
     149                                if (topwindow != null) {
    130150                                        //show("win="+win.toSource()+", keycode="+keycode+", modifiers=["+modifiers+"], str="+str);
    131                                         var wid = window_to_id[win];
    132                                         var packet = ["key-action", wid, keyname, pressed, modifiers, keyval, str, keycode, group];
     151                                        var packet = ["key-action", topwindow, keyname, pressed, modifiers, keyval, str, keycode, group];
    133152                                        send(packet);
    134153                                }
    135154                        }
     
    278297                        function mouse_click(win, button, pressed, x, y, modifiers, buttons) {
    279298                                "use strict";
    280299                                var wid = window_to_id[win];
    281                                 //show("mouse_click(..) wid("+win+")="+wid);
    282                                 check_focus();
     300                                set_focus(wid);
    283301                                send(["button-action", wid, button, pressed, [x, y], modifiers, buttons]);
    284302                        };
    285303
    286304                        function check_focus() {
    287305                                "use strict";
    288306                                var wid = -1;
    289                                 var win = canvas_state.topOfStack();
    290307                                if (win != null)
    291308                                        wid = window_to_id[win];
    292309                                //show("check_focus() window_to_id["+win+"]="+wid+", focus="+wid+", window_to_id="+window_to_id.toSource());
     
    298315                                "use strict";
    299316                                focus = wid;
    300317                                send(["focus", focus, []]);
     318                                topwindow = wid;
    301319                                //set the focused flag on all windows:
    302320                var win;
    303321                                for (var i in id_to_window) {
     
    315333
    316334                        function make_new_window(wid, x, y, w, h, metadata, override_redirect, client_properties) {
    317335                                "use strict";
    318                                 var win = new XpraWindow(canvas_state, wid, x, y, w, h, metadata, override_redirect, client_properties,
     336                                // each window needs their own DIV that contains a canvas
     337                                var mydiv = document.createElement("div");
     338                                mydiv.id = String(wid);
     339                                var mycanvas = document.createElement("canvas");
     340                                mydiv.appendChild(mycanvas);
     341                                document.body.appendChild(mydiv);
     342                                // set initial sizes
     343                                mycanvas.width = w;
     344                                mycanvas.height = h;
     345                                jQuery(mydiv).addClass("window");
     346                                jQuery(mydiv).prepend('<div id="head' + String(wid) + '"class="windowhead">window title</div>');
     347                                jQuery(mydiv).draggable({cancel: "canvas"});
     348                                // create the XpraWindow object that sits in the new div
     349                                var win = new XpraWindow(mycanvas, wid, x, y, w, h, metadata, override_redirect, client_properties,
    319350                                                window_geometry_changed, mouse_move, mouse_click, window_closed);
    320                                 //show("make_new_window("+wid+", "+x+", "+y+", "+w+", "+h+", "+metadata+", "+override_redirect+", "+client_properties+")="+win);
    321351                                id_to_window[wid] = win;
    322352                                window_to_id[win] = wid;
    323353                                var geom = win.get_internal_geometry();
     
    423453                                var wid = packet[1];
    424454                                var win = id_to_window[wid];
    425455                                if (win!=null) {
    426                                         canvas_state.raiseShape(win);
     456                                        //canvas_state.raiseShape(win);
    427457                                        focus = wid;
    428458                                        check_focus();
    429459                                }
     
    465495
    466496                        function get_desktop_size() {
    467497                                "use strict";
    468                                 var canvas = document.getElementById('screen');
    469                                 return [canvas.width, canvas.height];
     498                                return [screen_width, screen_height];
    470499                        }
    471500                        function get_screen_sizes() {
    472501                                "use strict";
    473                                 var canvas = document.getElementById('screen');
    474502                                var dpi = get_DPI();
    475503                                /*
    476504                                equivallent GTK code:
     
    482510                                                monitors,
    483511                                                work_x, work_y, work_width, work_height)
    484512                                */
    485                                 var wmm = Math.round(canvas.width*25.4/dpi);
    486                                 var hmm = Math.round(canvas.height*25.4/dpi);
    487                                 var monitor = ["Canvas", 0, 0, canvas.width, canvas.height, wmm, hmm];
    488                                 var screen = ["HTML", canvas.width, canvas.height,
     513                                var wmm = Math.round(screen_width*25.4/dpi);
     514                                var hmm = Math.round(screen_height*25.4/dpi);
     515                                var monitor = ["Canvas", 0, 0, screen_width, screen_height, wmm, hmm];
     516                                var screen = ["HTML", screen_width, screen_height,
    489517                                                        wmm, hmm,
    490518                                                        [monitor],
    491                                                         0, 0, canvas.width, canvas.height
     519                                                        0, 0, screen_width, screen_height
    492520                                                ];
    493521                                //just a single screen:
    494522                                return [screen];
     
    510538                        function make_hello() {
    511539                                "use strict";
    512540                                return  {
    513                                                 "version"                                       : "0.14.0",
     541                                                "version"                                       : "0.11.0",
    514542                                                "platform"                                      : guess_platform(),
    515543                                                "platform.name"                         : guess_platform_name(),
    516544                                                "platform.processor"            : guess_platform_processor(),
     
    593621
    594622                        function do_send_new_screen_size() {
    595623                                //show("do_send_new_screen_size()");
    596                                 if (protocol==null || canvas_state==null)
    597                                         return;
    598624                                screen_size_change_pending = false;
    599625                                var ds = get_desktop_size();
    600626                                var ss = get_screen_sizes();
     
    607633                                }
    608634                        }
    609635
    610                         function onresize() {
    611                                 var container = document.getElementById('screen_container');
    612                                 var d_form = document.getElementById('disconnect_form');
    613                                 var c_form = document.getElementById('connect_form');
    614                                 /*show("onresize() container size: "+container.clientWidth+"x"+container.clientHeight+
    615                                                                 ", window size: "+window.innerWidth+"x"+window.innerHeight+
    616                                                                 ", d form size: "+d_form.offsetWidth+"x"+d_form.offsetHeight+
    617                                                                 ", c form size: "+c_form.offsetWidth+"x"+c_form.offsetHeight);*/
    618                                 if (canvas_state!=null) {
    619                                         var width = Math.min(container.clientWidth, window.innerWidth);
    620                                         var height = Math.max(0, window.innerHeight-d_form.offsetHeight-c_form.offsetHeight);
    621                                         canvas_state.canvas.width = width;
    622                                         canvas_state.canvas.height = height;
    623                                         canvas_state.width = width;
    624                                         canvas_state.height = height;
    625                                         canvas_state.invalidate();
    626                                 }
    627                                 send_new_screen_size();
    628                         }
    629 
    630636                        function do_start(params) {
    631637                                "use strict";
    632638                                show("start()");
    633                                 onresize();
    634639
    635640                                //hook document and window events:
    636641                                document.onkeydown = onkeydown;
    637642                                document.onkeyup = onkeyup;
    638643                                document.onkeypress = onkeypress;
    639                                 window.addEventListener("resize", onresize, false);
    640 
    641                                 var canvas = document.getElementById('screen');
    642                                 canvas.style.display = "block";
    643 
    644                                 if (canvas_state==null)
    645                                         canvas_state = new CanvasState(canvas);
    646644                                protocol = new Protocol();
    647645                                protocol.set_packet_handler("open", process_open);
    648646                                protocol.set_packet_handler("close", process_close);
     
    672670                                document.onkeydown = null;
    673671                                document.onkeyup = null;
    674672                                document.onkeypress = null;
    675                                 window.removeEventListener("resize", onresize);
    676673                                if (protocol!=null) {
    677674                                        protocol.close();
    678675                                        protocol = null;
    679676                                }
    680                                 if (canvas_state!=null) {
    681                                         canvas_state.destroy();
    682                                         canvas_state = null;
    683                                 }
     677
    684678                                document.getElementById('connect_form').style.display = "block";
    685679                                document.getElementById('disconnect_form').style.display = "none";
    686680
    687                                 var canvas = document.getElementById('screen');
    688                                 canvas.style.display = "none";
    689681                                status = "ready";
    690682                        }
    691683
     
    714706                                        document.getElementById('disconnect_form').style.display = "block";
    715707                                else
    716708                                        document.getElementById('disconnect_form').style.display = "none";
    717                                 onresize();
    718709                                set_ui_message("");
    719710
    720711                                show("connected to server - sending hello");
     
    746737                                send(["hello", hello]);
    747738                        }
    748739
     740                        function pass() {
     741
     742                        }
     743
    749744                        function force_refresh() {
    750745                                "use strict";
    751746                                if (protocol!=null) {
     
    891886                                </fieldset>
    892887                        </form>
    893888                </div>
    894                 <div id="screen_container">
    895                         <canvas id="screen" width="800" height="600"></canvas>
    896                 </div>
    897889                <div id="dpi" style="width: 1in; height: 1in; left: -100%; top: -100%; position: absolute;">
    898890                </div>
    899891        </body>
  • include/window.js

     
    77 * Based on shape.js
    88 */
    99
    10 var window_icons = {};
    11 //load the window icons
    12 var image_names = ["maximize", "minimize", "close"];
    13 function load_icon(name) {
    14         console.log("loading "+name);
    15         var tmp_canvas = document.createElement('canvas');
    16         var tmp_context = tmp_canvas.getContext('2d');
    17         var image = new Image();
    18         image.onload = function() {
    19                 show(""+name+"="+image);
    20                 tmp_context.drawImage(image, 0, 0);
    21                 var image_data = tmp_context.getImageData(0, 0, image.width, image.height);
    22                 window_icons[name] = image_data;
    23         };
    24         image.src = '/include/'+name+'.png';
    25 }
    2610
    27 for (var i in image_names) {
    28         load_icon(image_names[i]);
    29 }
    3011
    31 /**
    32  * A simple button we use to decorate the window.
    33  */
    34 function Button(canvas_state, name, x, y, w, h, fill, icon_name, click_callback) {
    35         "use strict";
    36         this.state = canvas_state;
    37         this.name = name;
    38         this.x = x || 0;
    39         this.y = y || 0;
    40         this.w = w || 1;
    41         this.h = h || 1;
    42         var ctx = canvas_state.canvas.getContext('2d');
    43         this.image = ctx.createImageData(w, h);
    44         //var data = this.image.data;
    45         for (var i=0; i<w*h*4; i++) {
    46                 this.image.data[i] = (fill || 0xAA) &0xFF;
    47         }
    48         var icon = window_icons[icon_name];
    49         if (icon) {
    50                 this.update_image(icon.width, icon.height, "premult_argb32", icon.data);
    51         }
    52         this.click_callback = click_callback;
    53 }
    54 Button.prototype.draw_at = function(ctx, x, y) {
    55         "use strict";
    56         //draw the window pixels:
    57         ctx.putImageData(this.image, this.x + x, this.y + y);
    58 };
    59 function rectangle_contains(rect, mx, my) {
    60         "use strict";
    61         // All we have to do is make sure the Mouse X,Y fall in the area between
    62         // the shape's X and (X + Height) and its Y and (Y + Height)
    63         return  (rect.x <= mx) && (rect.x + rect.w >= mx) &&
    64                                         (rect.y <= my) && (rect.y + rect.h >= my);
    65 };
    66 Button.prototype.get_geometry = function() {
    67         "use strict";
    68         return { x : this.x, y : this.y, w : this.w, h : this.h };
    69 };
    70 Button.prototype.contains = function(mx, my) {
    71         "use strict";
    72         return rectangle_contains(this.get_geometry(), mx, my);
    73 };
    74 Button.prototype.update_image = function(w, h, pixel_format, data) {
    75         "use strict";
    76         if (pixel_format!="premult_argb32") {
    77                 return;
    78         }
    79         var s, d, i;
    80         //to make this faster and better looking,
    81         //we could draw to a temporary canvas to scale it,
    82         //as per: http://stackoverflow.com/a/3449416/428751
    83         //here we just scale it by hand
    84         for (var x=0; x<this.w; x++) {
    85                 for (var y=0; y<this.h; y++) {
    86                         //destination index (simple)
    87                         d = ((y*this.w) + x) * 4;
    88                         //source index (scaled)
    89                         s = (Math.round(y*h/this.h)*w + Math.round(w*x/this.w)) * 4;
    90                         for (i=0; i<4; i++) {
    91                                 this.image.data[d+i] = data[s+i];
    92                         }
    93                 }
    94         }
    95 }
    9612
    97 /**
    98  * toString allows us to identify buttons:
    99  */
    100 Button.prototype.toString = function() {
    101         "use strict";
    102         return "Button("+this.name+")";
    103 };
    10413
    105 
    106 
    10714/**
    10815 * This is the class representing a window we draw on the canvas.
    10916 * It has a geometry, it may have borders and a top bar.
    11017 * The contents of the window is an image, which gets updated
    11118 * when we receive pixels from the server.
    11219 */
    113 function XpraWindow(canvas_state, wid, x, y, w, h, metadata, override_redirect, client_properties,
    114                 geometry_cb, mouse_move_cb, mouse_click_cb, window_closed) {
     20function XpraWindow(canvas_state, wid, x, y, w, h, metadata, override_redirect, client_properties, geometry_cb, mouse_move_cb, mouse_click_cb, window_closed, htmldiv) {
    11521        "use strict";
    11622        //keep reference to the canvas:
    117         this.state = canvas_state;
     23        this.canvas = canvas_state;
     24        this.div = jQuery("#" + String(wid));
    11825        //callbacks start null until we finish init:
    11926        this.geometry_cb = null;
    12027        this.mouse_move_cb = null;
     
    12431        //styling:
    12532        this.borderColor = '#101028';
    12633        this.topBarColor = '#B8B8C0';
     34        // This complicates things a little but but fixes mouse co-ordinate problems
     35        // when there's a border or padding. See getMouse for more detail
     36        var stylePaddingLeft, stylePaddingTop, styleBorderLeft, styleBorderTop,
     37                        html, self, i;
     38        if (document.defaultView && document.defaultView.getComputedStyle) {
     39                this.stylePaddingLeft   = parseInt(document.defaultView.getComputedStyle(this.canvas, null).paddingLeft, 10)            || 0;
     40                this.stylePaddingTop    = parseInt(document.defaultView.getComputedStyle(this.canvas, null).paddingTop, 10)             || 0;
     41                this.styleBorderLeft    = parseInt(document.defaultView.getComputedStyle(this.canvas, null).borderLeftWidth, 10)        || 0;
     42                this.styleBorderTop             = parseInt(document.defaultView.getComputedStyle(this.canvas, null).borderTopWidth, 10) || 0;
     43        }
     44        // Some pages have fixed-position bars (like the stumbleupon bar) at the top or left of the page
     45        // They will mess up mouse coordinates and this fixes that
     46        html = document.body.parentNode;
     47        this.htmlTop = html.offsetTop;
     48        this.htmlLeft = html.offsetLeft;
    12749
    12850        //the window "backing":
    12951        this.image = null;
     
    14365
    14466        //not the real geometry we will use,
    14567        //but enough to avoid errors if update_metadata fires changes
    146         this.x = x;
    147         this.y = y;
     68        this.x = 0;
     69        this.y = 0;
    14870        this.w = w;
    14971        this.h = h;
    15072
     
    15577        // the space taken by window decorations:
    15678        this.calculate_offsets();
    15779
    158         if (!this.fullscreen && !this.maximized) {
    159                 // if fullscreen or maximized, the metadata update will have set the new size already
    160                 // account for borders, and try to make the image area map to (x,y):
    161                 var rx = (x || 0) - this.borderWidth;
    162                 var ry = (y || 0) - this.topBarHeight + this.borderWidth;
    163                 var rw = (w || 1) + this.borderWidth*2;
    164                 var rh = (h || 1) + this.borderWidth*2 + this.topBarHeight;
    165                 this.move_resize(rx, ry, rw, rh);
    166                 //show("after move resize: ("+this.w+", "+this.h+")");
     80        console.log(x, y, w, h);
     81
     82        // Hook up the events we want to receive:
     83        this.event_listeners = []
     84        var listeners = [
     85                        ['mousedown'    , true],
     86                        ['mouseup'              , true],
     87                        ['mousemove'    , true]
     88                        ];
     89        for (i = 0; i < listeners.length; i += 1) {
     90                var l = listeners[i];
     91                this.registerEventListener(l[0], l[1]);
    16792        }
    16893
    16994        // now safe to assign the callbacks:
     
    17297        this.mouse_click_cb = mouse_click_cb || null;
    17398        this.window_closed_cb = window_closed || null;
    17499
    175         //create the buttons:
    176         if (!this.override_redirect) {
    177                 this.create_buttons();
    178         }
    179100        //create the image holding the pixels (the "backing"):
    180101        this.create_image_backing();
    181         canvas_state.addShape(this);
    182102};
    183103
    184 /**
    185  * Creates the minimize, maximize and close buttons.
    186  */
    187 XpraWindow.prototype.create_buttons = function() {
    188         "use strict";
    189         var w = 24;
    190         var h = 24;
     104XpraWindow.prototype.registerEventListener = function(event_type, useCapture) {
    191105        var self = this;
    192         this.buttons["icon"] = new Button(this.state, "icon",           this.borderWidth,       this.borderWidth, w, h, 0x11, null, null);
    193         /*this.buttons["minimize"] = new Button(this.state, "minimize", this.w-(w+2)*3,         this.borderWidth, w, h, 0x44, function() {
    194                 //TODO!
    195         });*/
    196         this.buttons["maximize"] = new Button(this.state, "maximize",   this.w-(w+2)*2,         this.borderWidth, w, h, 0x66, "maximize", function() {
    197                 var m = !self.maximized;
    198                 self.client_properties["maximized"] = m;
    199                 self.set_maximized(m);
    200         });
    201         this.buttons["close"] = new Button(this.state, "close",         this.w-(w+2)*1,         this.borderWidth, w, h, 0x99, "close", function() {
    202                 if (self.window_closed_cb) {
    203                         self.window_closed_cb(self);
    204                 }
    205         });
     106        var fn_name = "on_"+event_type;
     107        var handler = self[fn_name];
     108        var fn = function(e) {
     109                handler.call(self, e);
     110        };
     111        this.event_listeners[event_type] = fn;
     112        this.canvas["on"+event_type] = fn;
    206113};
    207 /**
    208  * Ensures that the buttons are always in the same place
    209  * after a resize.
    210  */
    211 XpraWindow.prototype.move_buttons = function() {
    212         var w = 24;
    213         if ("maximize" in this.buttons) {
    214                 this.buttons["maximize"].x = this.w-(w+2)*2;
     114
     115XpraWindow.prototype.getMouse = function(e) {
     116        "use strict";
     117        var element = this.canvas, offsetX = 0, offsetY = 0, mx, my;
     118
     119        // Compute the total offset
     120        if (element.offsetParent !== undefined) {
     121                do {
     122                        offsetX += element.offsetLeft;
     123                        offsetY += element.offsetTop;
     124                        element = element.offsetParent;
     125                } while (element);
    215126        }
    216         if ("close" in this.buttons) {
    217                 this.buttons["close"].x = this.w-(w+2)*1;
     127
     128        // Add padding and border style widths to offset
     129        // Also add the <html> offsets in case there's a position:fixed bar
     130        offsetX += this.stylePaddingLeft + this.styleBorderLeft + this.htmlLeft;
     131        offsetY += this.stylePaddingTop + this.styleBorderTop + this.htmlTop;
     132
     133        mx = e.pageX - offsetX;
     134        my = e.pageY - offsetY;
     135
     136        var mbutton = 0;
     137        if ("which" in e)  // Gecko (Firefox), WebKit (Safari/Chrome) & Opera
     138                mbutton = Math.max(0, e.which);
     139        else if ("button" in e)  // IE, Opera (zero based)
     140                mbutton = Math.max(0, e.button)+1;
     141        //show("getmouse: button="+mbutton+", which="+e.which+", button="+e.button);
     142
     143        // We return a simple javascript object (a hash) with x and y defined
     144        return {x: mx, y: my, button: mbutton};
     145};
     146
     147XpraWindow.prototype.on_mousemove = function(e) {
     148        var mouse = this.getMouse(e),
     149                        mx = mouse.x,
     150                        my = mouse.y;
     151
     152                        var modifiers = [];
     153                        var buttons = [];
     154                        this.handle_mouse_move(mx, my, modifiers, buttons);
     155
     156};
     157
     158XpraWindow.prototype.on_mousedown = function(e) {
     159        var mouse, mx, my, shapes, l, i, mySel;
     160
     161        mouse = this.getMouse(e);
     162        mx = mouse.x;
     163        my = mouse.y;
     164
     165        // pass the click to the area:
     166        var modifiers = [];
     167        var buttons = [];
     168        this.handle_mouse_click(mouse.button, true, mx, my, modifiers, buttons);
     169        return;
     170};
     171
     172XpraWindow.prototype.on_mouseup = function(e) {
     173        // if not handling it ourselves, pass it down:
     174        var mouse = this.getMouse(e),
     175                        mx = mouse.x,
     176                        my = mouse.y;
     177        if (!this.dragging) {
     178                var modifiers = [];
     179                var buttons = [];
     180                this.handle_mouse_click(mouse.button, false, mx, my, modifiers, buttons);
    218181        }
    219 }
    220182
     183        this.dragging = false;
     184};
    221185
    222186/**
    223187 * toString allows us to identify windows by their unique window id.
     
    235199        var previous_image = this.image;
    236200        var img_geom = this.get_internal_geometry();
    237201        //show("createImageData: "+img_geom.toSource());
    238         this.image = canvas_state.canvas.getContext('2d').createImageData(img_geom.w, img_geom.h);
     202        this.image = this.canvas.getContext('2d').createImageData(img_geom.w, img_geom.h);
    239203        if (previous_image) {
    240204                //copy previous pixels to new image, ignoring bit gravity
    241205                //TODO!
     
    249213 */
    250214XpraWindow.prototype.calculate_offsets = function() {
    251215        "use strict";
    252         if (this.override_redirect || this.fullscreen) {
    253                 //no borders or top bar at all:
    254                 this.borderWidth = 0;
    255                 this.topBarHeight = 0;
    256         }
    257         else {
    258                 //regular borders and top bar:
    259                 this.borderWidth = 2;
    260                 this.topBarHeight = 24;
    261         }
    262         this.offsets = [this.borderWidth+this.topBarHeight, this.borderWidth, this.borderWidth, this.borderWidth];
     216
     217        this.offsets = [0, 0, 0, 0];
    263218};
    264219
    265220/**
     
    288243    }
    289244    if ("title" in metadata) {
    290245        this.title = metadata["title"];
    291         //redraw everything (a bit wasteful):
    292         this.state.invalidate();
    293246    }
    294247};
    295248
     
    377330        "use strict";
    378331        this.x = 0;
    379332        this.y = 0;
    380         this.w = this.state.width;
    381         this.h = this.state.height;
     333        this.w = 640;
     334        this.h = 480;
    382335};
    383336
    384337/**
     
    390343XpraWindow.prototype.handle_resize = function() {
    391344        "use strict";
    392345        this.create_image_backing();
    393         this.state.invalidate();
     346        //this.state.invalidate();
    394347        this.move_buttons();
    395348        if (this.geometry_cb!=null) {
    396349                this.geometry_cb(this);
     
    423376        // This should always be true:
    424377        //this.image.width = this.w - this.borderWidth*2;
    425378        //this.image.height = this.h - (this.borderWidth*2 + this.topBarHeight);
    426         return { x : this.x+this.borderWidth,
    427                          y : this.y+this.borderWidth+this.topBarHeight,
    428                          w : this.w - this.borderWidth*2,
    429                          h : this.h - (this.borderWidth*2 + this.topBarHeight)};
     379        return { x : this.x,
     380                         y : this.y,
     381                         w : this.w,
     382                         h : this.h};
    430383};
    431384
    432385/**
     
    436389XpraWindow.prototype.handle_mouse_click = function(button, pressed, mx, my, modifiers, buttons) {
    437390        "use strict";
    438391        var igeom = this.get_internal_geometry();
     392        console.log("got mouse click at ", mx, my)
    439393        if (this.mouse_click_cb!=null && rectangle_contains(igeom, mx, my)) {
    440394                this.mouse_click_cb(this, button, pressed, mx, my, modifiers, buttons);
    441395                return;
    442396        }
    443         //maybe one of the buttons:
    444         //(use relative coordinates)
    445         var x = mx-this.x;
    446         var y = my-this.y
    447         for (var name in this.buttons) {
    448                 var button = this.buttons[name];
    449                 if (button.contains(x, y)) {
    450                         show("clicked on button "+name);
    451                         var cb = button.click_callback;
    452                         if (cb) {
    453                                 cb();
    454                         }
    455                         return;
    456                 }
    457         }
    458397};
    459398
    460399/**
     
    463402 */
    464403XpraWindow.prototype.handle_mouse_move = function(mx, my, modifiers, buttons) {
    465404        "use strict";
    466         var igeom = this.get_internal_geometry();
    467         if (this.mouse_move_cb!=null && rectangle_contains(igeom, mx, my)) {
    468                 this.mouse_move_cb(this, mx, my, modifiers, buttons);
    469         }
     405        // should we handle mouse move?
     406        this.mouse_move_cb(this, mx, my, modifiers, buttons);
    470407};
    471408
    472409
    473410XpraWindow.prototype.update_icon = function(w, h, pixel_format, data) {
    474411        "use strict";
    475         var icon = this.buttons["icon"];
    476         if (icon) {
    477                 icon.update_image(w, h, pixel_format, data);
    478                 this.state.invalidate();
    479         }
     412        // update icon
    480413}
    481414
    482415/**
     
    488421XpraWindow.prototype.draw = function(ctx) {
    489422        "use strict";
    490423
    491         if (!this.override_redirect && !this.fullscreen) {
    492                 //draw window frame:
    493                 this.draw_frame(ctx);
    494         }
    495 
    496424        //draw the window pixels:
    497         ctx.putImageData(this.image, this.x + this.borderWidth, this.y + this.borderWidth + this.topBarHeight);
     425        ctx.putImageData(this.image, this.x, this.y);
    498426
    499         if (this.state.selection === this && !this.override_redirect) {
    500                 //window.alert("Shape.prototype.draw_selection="+Shape.prototype.draw_selection);
    501                 this.draw_selection(ctx);
    502         }
    503427};
    504428
    505 /**
    506  * Draws the window frame:
    507  * a simple rectangle around the edge.
    508  */
    509 XpraWindow.prototype.draw_frame = function(ctx) {
    510         "use strict";
    511429
    512         // draw border:
    513         ctx.strokeStyle = this.borderColor;
    514         ctx.lineWidth = this.borderWidth;
    515         var hw = this.borderWidth/2;
    516         ctx.strokeRect(this.x+hw,this.y+hw,this.w-this.borderWidth,this.h-this.borderWidth);
    517 
    518         // draw top bar:
    519         ctx.fillStyle = this.topBarColor;
    520         ctx.fillRect(this.x+this.borderWidth, this.y+this.borderWidth, this.w-this.borderWidth*2, this.topBarHeight);
    521         // draw title:
    522         if (this.title) {
    523                 var size = 18;
    524                 ctx.font = ""+size+"px sans-serif";
    525                 ctx.fillStyle = "#FFFFFF";
    526                 ctx.fillText(this.title, this.x+32, this.y+this.borderWidth+this.topBarHeight-size/3);
    527         }
    528 
    529         // draw buttons:
    530         for (var name in this.buttons) {
    531                 var button = this.buttons[name];
    532                 button.draw_at(ctx, this.x, this.y);
    533         }
    534 };
    535 
    536 
    537430/**
    538  * Draws border and selection rectangles
    539  * which indicate that the window is selected
    540  */
    541 XpraWindow.prototype.draw_selection = function(ctx) {
    542         "use strict";
    543         if (this.maximized || this.fullscreen) {
    544                 return;
    545         }
    546         var i, cur, half;
    547 
    548         ctx.strokeStyle = this.state.selectionColor;
    549         ctx.lineWidth = this.state.selectionWidth;
    550         ctx.strokeRect(this.x,this.y,this.w,this.h);
    551 
    552         // draw the boxes
    553         half = this.state.selectionBoxSize / 2;
    554 
    555         // 0  1  2
    556         // 3     4
    557         // 5  6  7
    558 
    559         // top left, middle, right
    560         this.state.selectionHandles[0].x = this.x-half;
    561         this.state.selectionHandles[0].y = this.y-half;
    562 
    563         this.state.selectionHandles[1].x = this.x+this.w/2-half;
    564         this.state.selectionHandles[1].y = this.y-half;
    565 
    566         this.state.selectionHandles[2].x = this.x+this.w-half;
    567         this.state.selectionHandles[2].y = this.y-half;
    568 
    569         //middle left
    570         this.state.selectionHandles[3].x = this.x-half;
    571         this.state.selectionHandles[3].y = this.y+this.h/2-half;
    572 
    573         //middle right
    574         this.state.selectionHandles[4].x = this.x+this.w-half;
    575         this.state.selectionHandles[4].y = this.y+this.h/2-half;
    576 
    577         //bottom left, middle, right
    578         this.state.selectionHandles[6].x = this.x+this.w/2-half;
    579         this.state.selectionHandles[6].y = this.y+this.h-half;
    580 
    581         this.state.selectionHandles[5].x = this.x-half;
    582         this.state.selectionHandles[5].y = this.y+this.h-half;
    583 
    584         this.state.selectionHandles[7].x = this.x+this.w-half;
    585         this.state.selectionHandles[7].y = this.y+this.h-half;
    586 
    587         ctx.fillStyle = this.state.selectionBoxColor;
    588         for (i = 0; i < 8; i += 1) {
    589                 cur = this.state.selectionHandles[i];
    590                 ctx.fillRect(cur.x, cur.y, this.state.selectionBoxSize, this.state.selectionBoxSize);
    591         }
    592 };
    593 
    594 /**
    595431 * Updates the window image with new pixel data
    596432 * we have received from the server.
    597433 */
     
    625461        var stride = this.image.width*4;
    626462        //and we can paint the canvas with it
    627463        //(if we have transparency, we should probably repaint what is underneath...)
    628         var ctx = this.state.ctx;
     464        var ctx = this.canvas.getContext('2d');
    629465
    630466        if (x==0 && width==this.image.width && y+height<=this.image.height) {
    631467                //take a shortcut: copy all lines
     
    633469
    634470                if (this.focused) {
    635471                        //shortcut: paint canvas directly
    636                         ctx.putImageData(this.image, this.x + this.borderWidth, this.y + this.borderWidth + this.topBarHeight);
     472                        ctx.putImageData(this.image, this.x , this.y);
    637473                        return;
    638474                }
    639475        }
     
    650486
    651487                if (this.focused) {
    652488                        //shortcut: paint canvas directly
    653                         ctx.putImageData(img, this.x + this.borderWidth + x, this.y + this.borderWidth + this.topBarHeight + y);
     489                        ctx.putImageData(img, this.x + x, this.y + y);
    654490                        return;
    655491                }
    656492        }
     
    658494                //no action taken, no need to invalidate
    659495                return;
    660496        }
    661         this.state.invalidate();
     497        //this.state.invalidate();
    662498};
    663499
    664500/**
     
    666502 */
    667503XpraWindow.prototype.destroy = function destroy() {
    668504        "use strict";
    669         if (this.state!=null) {
    670                 this.state.removeShape(this);
    671                 this.state = null;
    672         }
     505        // remove div
     506        this.div.remove()
    673507};
    674508
    675509/**