]> Pileus Git - ~andy/gtk/blob - gdk/broadway/broadway.js
broadway: Get query_state window coords from browser side
[~andy/gtk] / gdk / broadway / broadway.js
1 var base64_val = [
2   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
3   255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
4   255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
5    52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,  0,255,255,
6   255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
7    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
8   255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
9    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255
10 ]
11
12 function base64_8(str, index) {
13   var v =
14     (base64_val[str.charCodeAt(index)]) +
15     (base64_val[str.charCodeAt(index+1)] << 6);
16   return v;
17 }
18
19 function base64_16(str, index) {
20   var v =
21     (base64_val[str.charCodeAt(index)]) +
22     (base64_val[str.charCodeAt(index+1)] << 6) +
23     (base64_val[str.charCodeAt(index+2)] << 12);
24   return v;
25 }
26
27 function base64_16s(str, index) {
28   var v = base64_16(str, index);
29   if (v > 32767)
30     return v - 65536;
31   else
32     return v;
33 }
34
35 function base64_24(str, index) {
36   var v =
37     (base64_val[str.charCodeAt(index)]) +
38     (base64_val[str.charCodeAt(index+1)] << 6) +
39     (base64_val[str.charCodeAt(index+2)] << 12) +
40     (base64_val[str.charCodeAt(index+3)] << 18);
41   return v;
42 }
43
44 function base64_32(str, index) {
45   var v =
46     (base64_val[str.charCodeAt(index)]) +
47     (base64_val[str.charCodeAt(index+1)] << 6) +
48     (base64_val[str.charCodeAt(index+2)] << 12) +
49     (base64_val[str.charCodeAt(index+3)] << 18) +
50     (base64_val[str.charCodeAt(index+4)] << 24) +
51     (base64_val[str.charCodeAt(index+5)] << 30);
52   return v;
53 }
54
55 function createXHR()
56 {
57   try { return new XMLHttpRequest(); } catch(e) {}
58   try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) {}
59   try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e) {}
60   try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) {}
61   try { return new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {}
62
63   return null;
64 }
65
66 var last_serial = 0;
67 var last_x = 0;
68 var last_y = 0;
69 var window_with_mouse = 0;
70 var surfaces = {};
71 var outstanding_commands = new Array();
72 var input_socket = null;
73
74 function initContext(canvas, x, y, id)
75 {
76   canvas.surface_id = id;
77   canvas.style["position"] = "absolute";
78   canvas.style["left"] = x + "px";
79   canvas.style["top"] = y + "px";
80   canvas.style["display"] = "none";
81   var context = canvas.getContext("2d");
82   context.globalCompositeOperation = "src-over";
83   context.fillRect(0, 0, canvas.width, canvas.height);
84   document.body.appendChild(canvas);
85
86   return context;
87 }
88
89 function handleCommands(cmd_obj)
90 {
91   var cmd = cmd_obj.data;
92   var i = cmd_obj.pos;
93
94   while (i < cmd.length) {
95     var command = cmd[i++];
96     last_serial = base64_32(cmd, i);
97     i = i + 6;
98     switch (command) {
99       /* create new surface */
100       case 's':
101         var id = base64_16(cmd, i);
102         i = i + 3;
103         var x = base64_16(cmd, i);
104         i = i + 3;
105         var y = base64_16(cmd, i);
106         i = i + 3;
107         var w = base64_16(cmd, i);
108         i = i + 3;
109         var h = base64_16(cmd, i);
110         i = i + 3;
111         var surface = document.createElement("canvas");
112         surface.width = w;
113         surface.height = h;
114         surfaces[id] = initContext(surface, x, y, id);
115         break;
116
117       /* show a surface */
118       case 'S':
119         var id = base64_16(cmd, i);
120         i = i + 3;
121         surfaces[id].canvas.style["display"] = "inline";
122         break;
123
124       /* hide a surface */
125       case 'H':
126         var id = base64_16(cmd, i);
127         i = i + 3;
128         surfaces[id].canvas.style["display"] = "inline";
129         break;
130
131       /* delete surface */
132       case 'd':
133         var id = base64_16(cmd, i);
134         i = i + 3;
135         var canvas = surfaces[id].canvas;
136         delete surfaces[id];
137         canvas.parentNode.removeChild(canvas);
138
139         break;
140
141       /* move a surface */
142       case 'm':
143         var id = base64_16(cmd, i);
144         i = i + 3;
145         var x = base64_16(cmd, i);
146         i = i + 3;
147         var y = base64_16(cmd, i);
148         i = i + 3;
149         surfaces[id].canvas.style["left"] = x + "px";
150         surfaces[id].canvas.style["top"] = y + "px";
151         break;
152
153       /* resize a surface */
154       case 'r':
155         var id = base64_16(cmd, i);
156         i = i + 3;
157         var w = base64_16(cmd, i);
158         i = i + 3;
159         var h = base64_16(cmd, i);
160         i = i + 3;
161         surfaces[id].canvas.width = w;
162         surfaces[id].canvas.height = h;
163         break;
164
165       /* put image data surface */
166       case 'i':
167         var id = base64_16(cmd, i);
168         i = i + 3;
169         var x = base64_16(cmd, i);
170         i = i + 3;
171         var y = base64_16(cmd, i);
172         i = i + 3;
173         var size = base64_32(cmd, i);
174         i = i + 6;
175         var url = cmd.slice(i, i + size);
176         i = i + size;
177         var img = new Image();
178         img.src = url;
179         if (img.complete) {
180           surfaces[id].drawImage(img, x, y);
181         } else {
182           cmd_obj.pos = i;
183           img.onload = function() { surfaces[id].drawImage(img, x, y); handleOutstanding(); };
184           return false;
185         }
186
187         break;
188
189       /* copy rects */
190       case 'b':
191         var id = base64_16(cmd, i);
192         i = i + 3;
193
194         var nrects = base64_16(cmd, i);
195         i = i + 3;
196
197         var context = surfaces[id];
198         context.save();
199
200         var minx;
201         var miny;
202         var maxx;
203         var maxy;
204         for (var r = 0; r < nrects; r++) {
205           var x = base64_16(cmd, i);
206           i = i + 3;
207           var y = base64_16(cmd, i);
208           i = i + 3;
209           var w = base64_16(cmd, i);
210           i = i + 3;
211           var h = base64_16(cmd, i);
212           i = i + 3;
213           context.rect(x, y, w, h);
214
215           if (r == 0) {
216               minx = x;
217               miny = y;
218               maxx = x + w;
219               maxy = y + h;
220           } else {
221               if (x < minx)
222                   minx = x;
223               if (y < miny)
224                   miny = y;
225               if (x + w > maxx)
226                   maxx = x + w;
227               if (y + h > maxy)
228                   maxy = y + h;
229           }
230         }
231
232         context.clip();
233
234         var dx = base64_16s(cmd, i);
235         i = i + 3;
236         var dy = base64_16s(cmd, i);
237         i = i + 3;
238
239         context.drawImage(context.canvas,
240                           minx - dx, miny - dy, maxx - minx, maxy - miny,
241                           minx, miny, maxx - minx, maxy - miny);
242
243         context.restore();
244         break;
245
246       case 'q': // Query pointer
247         var id = base64_16(cmd, i);
248         i = i + 3;
249
250         var pos = getPositionsFromAbsCoord(last_x, last_y, id);
251
252         send_input ("q", [pos.root_x, pos.root_y, pos.win_x, pos.win_x, window_with_mouse]);
253         break;
254
255       default:
256         alert("Unknown op " + command);
257     }
258   }
259   return true;
260 }
261
262 function handleOutstanding()
263 {
264   while (outstanding_commands.length > 0) {
265     var cmd = outstanding_commands.shift();
266     if (!handleCommands(cmd)) {
267       outstanding_commands.unshift(cmd);
268       return;
269     }
270   }
271 }
272
273 function handleLoad(event)
274 {
275   var cmd_obj = {};
276   cmd_obj.data = event.target.responseText;
277   cmd_obj.pos = 0;
278
279   outstanding_commands.push(cmd_obj);
280   if (outstanding_commands.length == 1) {
281     handleOutstanding();
282   }
283 }
284
285 function get_surface_id(ev) {
286   var id = ev.target.surface_id;
287   if (id != undefined)
288     return id;
289   return 0;
290 }
291
292 function send_input(cmd, args)
293 {
294   if (input_socket != null) {
295       input_socket.send(cmd + ([last_serial].concat(args)).join(","));
296   }
297 }
298
299 function get_document_coordinates(element)
300 {
301     var res = new Object();
302     res.x = element.offsetLeft;
303     res.y = element.offsetTop;
304
305     var offsetParent = element.offsetParent;
306     while (offsetParent != null) {
307         res.x += offsetParent.offsetLeft;
308         res.y += offsetParent.offsetTop;
309         offsetParent = offsetParent.offsetParent;
310     }
311     return res;
312 }
313
314 function getPositionsFromAbsCoord(absX, absY, relativeId) {
315     var res = Object();
316
317     res.root_x = absX;
318     res.root_y = absY;
319     res.win_x = absX;
320     res.win_y = absY;
321     if (relativeId != 0) {
322         var pos = get_document_coordinates(surfaces[relativeId].canvas);
323         res.win_x = res.win_x - pos.x;
324         res.win_y = res.win_y - pos.y;
325     }
326
327     return res;
328 }
329
330 function getPositionsFromEvent(ev, relativeId) {
331     var res = getPositionsFromAbsCoord(ev.pageX, ev.pageY, relativeId);
332
333     last_x = res.root_x;
334     last_y = res.root_y;
335
336     return res;
337 }
338
339 function on_mouse_move (ev) {
340     var id = get_surface_id(ev);
341     var pos = getPositionsFromEvent(ev, id);
342     send_input ("m", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp]);
343 }
344
345 function on_mouse_over (ev) {
346     var id = get_surface_id(ev);
347     var pos = getPositionsFromEvent(ev, id);
348     window_with_mouse = id;
349     if (window_with_mouse != 0) {
350         send_input ("e", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp]);
351     }
352 }
353
354 function on_mouse_out (ev) {
355     var id = get_surface_id(ev);
356     var pos = getPositionsFromEvent(ev, id);
357
358     if (id != 0) {
359         send_input ("l", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp]);
360     }
361     window_with_mouse = 0;
362 }
363
364 function on_mouse_down (ev) {
365     var id = get_surface_id(ev);
366     var pos = getPositionsFromEvent(ev, id);
367     send_input ("b", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp, ev.button]);
368 }
369
370 function on_mouse_up (ev) {
371     var id = get_surface_id(ev);
372     var pos = getPositionsFromEvent(ev, id);
373     send_input ("B", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp, ev.button]);
374 }
375
376 var last_key_down = 0;
377 function on_key_down (ev) {
378   var key_code = ev.keyCode;
379   if (key_code != last_key_down) {
380     send_input ("k", [key_code, ev.timeStamp]);
381     last_key_down = key_code;
382   }
383 }
384
385 function on_key_up (ev) {
386   var key_code = ev.keyCode;
387   send_input ("K", [key_code, ev.timeStamp]);
388   last_key_down = 0;
389 }
390
391 function cancel_event(ev)
392 {
393   ev = ev ? ev : window.event;
394   if (ev.stopPropagation)
395     ev.stopPropagation();
396   if (ev.preventDefault)
397     ev.preventDefault();
398   ev.cancelBubble = true;
399   ev.cancel = true;
400   ev.returnValue = false;
401   return false;
402 }
403
404 function on_mouse_wheel(ev)
405 {
406   ev = ev ? ev : window.event;
407
408   var id = get_surface_id(ev);
409   var pos = getPositionsFromEvent(ev, id);
410
411   var offset = ev.detail ? ev.detail : ev.wheelDelta;
412   var dir = 0;
413   if (offset > 0)
414     dir = 1;
415   send_input ("s", [id, pos.root_x, pos.root_y, pos.win_x, pos.win_y, ev.timeStamp, dir]);
416
417   return cancel_event(ev);
418 }
419
420 function connect()
421 {
422   var xhr = createXHR();
423   if (xhr) {
424     if (typeof xhr.multipart == 'undefined') {
425       alert("Sorry, this example only works in browsers that support multipart.");
426       return;
427     }
428
429     xhr.multipart = true;
430     xhr.open("GET", "/output", true);
431     xhr.onload = handleLoad;
432     xhr.send(null);
433   }
434
435   if ("WebSocket" in window) {
436     var loc = window.location.toString().replace("http:", "ws:");
437     loc = loc.substr(0, loc.lastIndexOf('/')) + "/input";
438     var ws = new WebSocket(loc, "broadway");
439     ws.onopen = function() {
440       input_socket = ws;
441     };
442     ws.onclose = function() {
443       input_socket = null;
444     };
445   } else {
446      alert("WebSocket not supported, input will not work!");
447   }
448   document.oncontextmenu = function () { return false; };
449   document.onmousemove = on_mouse_move;
450   document.onmouseover = on_mouse_over;
451   document.onmouseout = on_mouse_out;
452   document.onmousedown = on_mouse_down;
453   document.onmouseup = on_mouse_up;
454   document.onkeydown = on_key_down;
455   document.onkeyup = on_key_up;
456
457   if (document.addEventListener) {
458     document.addEventListener('DOMMouseScroll', on_mouse_wheel, false);
459     document.addEventListener('mousewheel', on_mouse_wheel, false);
460   } else if (document.attachEvent) {
461     element.attachEvent("onmousewheel", on_mouse_wheel);
462   }
463
464 }