spice-html5-rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. data/COPYING +674 -0
  2. data/COPYING.LESSER +165 -0
  3. data/LICENSE.txt +15 -0
  4. data/README.md +35 -0
  5. data/lib/spice-html5-rails/version.rb +7 -0
  6. data/lib/spice-html5-rails.rb +10 -0
  7. data/vendor/assets/javascripts/spice-html5.js +25 -0
  8. data/vendor/assets/javascripts/spiceHTML5/atKeynames.js +183 -0
  9. data/vendor/assets/javascripts/spiceHTML5/bitmap.js +51 -0
  10. data/vendor/assets/javascripts/spiceHTML5/cursor.js +92 -0
  11. data/vendor/assets/javascripts/spiceHTML5/display.js +806 -0
  12. data/vendor/assets/javascripts/spiceHTML5/enums.js +282 -0
  13. data/vendor/assets/javascripts/spiceHTML5/inputs.js +271 -0
  14. data/vendor/assets/javascripts/spiceHTML5/lz.js +166 -0
  15. data/vendor/assets/javascripts/spiceHTML5/main.js +177 -0
  16. data/vendor/assets/javascripts/spiceHTML5/png.js +256 -0
  17. data/vendor/assets/javascripts/spiceHTML5/quic.js +1335 -0
  18. data/vendor/assets/javascripts/spiceHTML5/spiceconn.js +455 -0
  19. data/vendor/assets/javascripts/spiceHTML5/spicedataview.js +96 -0
  20. data/vendor/assets/javascripts/spiceHTML5/spicemsg.js +883 -0
  21. data/vendor/assets/javascripts/spiceHTML5/spicetype.js +480 -0
  22. data/vendor/assets/javascripts/spiceHTML5/thirdparty/jsbn.js +589 -0
  23. data/vendor/assets/javascripts/spiceHTML5/thirdparty/prng4.js +79 -0
  24. data/vendor/assets/javascripts/spiceHTML5/thirdparty/rng.js +102 -0
  25. data/vendor/assets/javascripts/spiceHTML5/thirdparty/rsa.js +146 -0
  26. data/vendor/assets/javascripts/spiceHTML5/thirdparty/sha1.js +346 -0
  27. data/vendor/assets/javascripts/spiceHTML5/ticket.js +250 -0
  28. data/vendor/assets/javascripts/spiceHTML5/utils.js +261 -0
  29. data/vendor/assets/javascripts/spiceHTML5/wire.js +123 -0
  30. metadata +108 -0
@@ -0,0 +1,806 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (C) 2012 by Jeremy P. White <jwhite@codeweavers.com>
4
+
5
+ This file is part of spice-html5.
6
+
7
+ spice-html5 is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU Lesser General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ spice-html5 is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU Lesser General Public License for more details.
16
+
17
+ You should have received a copy of the GNU Lesser General Public License
18
+ along with spice-html5. If not, see <http://www.gnu.org/licenses/>.
19
+ */
20
+
21
+
22
+ /*----------------------------------------------------------------------------
23
+ ** FIXME: putImageData does not support Alpha blending
24
+ ** or compositing. So if we have data in an ImageData
25
+ ** format, we have to draw it onto a context,
26
+ ** and then use drawImage to put it onto the target,
27
+ ** as drawImage does alpha.
28
+ **--------------------------------------------------------------------------*/
29
+ function putImageDataWithAlpha(context, d, x, y)
30
+ {
31
+ var c = document.createElement("canvas");
32
+ var t = c.getContext("2d");
33
+ c.setAttribute('width', d.width);
34
+ c.setAttribute('height', d.height);
35
+ t.putImageData(d, 0, 0);
36
+ context.drawImage(c, x, y, d.width, d.height);
37
+ }
38
+
39
+ /*----------------------------------------------------------------------------
40
+ ** FIXME: Spice will send an image with '0' alpha when it is intended to
41
+ ** go on a surface w/no alpha. So in that case, we have to strip
42
+ ** out the alpha. The test case for this was flux box; in a Xspice
43
+ ** server, right click on the desktop to get the menu; the top bar
44
+ ** doesn't paint/highlight correctly w/out this change.
45
+ **--------------------------------------------------------------------------*/
46
+ function stripAlpha(d)
47
+ {
48
+ var i;
49
+ for (i = 0; i < (d.width * d.height * 4); i += 4)
50
+ d.data[i + 3] = 255;
51
+ }
52
+
53
+ /*----------------------------------------------------------------------------
54
+ ** SpiceDisplayConn
55
+ ** Drive the Spice Display Channel
56
+ **--------------------------------------------------------------------------*/
57
+ function SpiceDisplayConn()
58
+ {
59
+ SpiceConn.apply(this, arguments);
60
+ }
61
+
62
+ SpiceDisplayConn.prototype = Object.create(SpiceConn.prototype);
63
+ SpiceDisplayConn.prototype.process_channel_message = function(msg)
64
+ {
65
+ if (msg.type == SPICE_MSG_DISPLAY_MARK)
66
+ {
67
+ // FIXME - DISPLAY_MARK not implemented (may be hard or impossible)
68
+ this.known_unimplemented(msg.type, "Display Mark");
69
+ return true;
70
+ }
71
+
72
+ if (msg.type == SPICE_MSG_DISPLAY_RESET)
73
+ {
74
+ DEBUG > 2 && console.log("Display reset");
75
+ this.surfaces[this.primary_surface].canvas.context.restore();
76
+ return true;
77
+ }
78
+
79
+ if (msg.type == SPICE_MSG_DISPLAY_DRAW_COPY)
80
+ {
81
+ var draw_copy = new SpiceMsgDisplayDrawCopy(msg.data);
82
+
83
+ DEBUG > 1 && this.log_draw("DrawCopy", draw_copy);
84
+
85
+ if (! draw_copy.base.box.is_same_size(draw_copy.data.src_area))
86
+ this.log_warn("FIXME: DrawCopy src_area is a different size than base.box; we do not handle that yet.");
87
+ if (draw_copy.base.clip.type != SPICE_CLIP_TYPE_NONE)
88
+ this.log_warn("FIXME: DrawCopy we don't handle clipping yet");
89
+ if (draw_copy.data.rop_descriptor != SPICE_ROPD_OP_PUT)
90
+ this.log_warn("FIXME: DrawCopy we don't handle ropd type: " + draw_copy.data.rop_descriptor);
91
+ if (draw_copy.data.mask.flags)
92
+ this.log_warn("FIXME: DrawCopy we don't handle mask flag: " + draw_copy.data.mask.flags);
93
+ if (draw_copy.data.mask.bitmap)
94
+ this.log_warn("FIXME: DrawCopy we don't handle mask");
95
+
96
+ if (draw_copy.data && draw_copy.data.src_bitmap)
97
+ {
98
+ if (draw_copy.data.src_bitmap.descriptor.flags &&
99
+ draw_copy.data.src_bitmap.descriptor.flags != SPICE_IMAGE_FLAGS_CACHE_ME &&
100
+ draw_copy.data.src_bitmap.descriptor.flags != SPICE_IMAGE_FLAGS_HIGH_BITS_SET)
101
+ {
102
+ this.log_warn("FIXME: DrawCopy unhandled image flags: " + draw_copy.data.src_bitmap.descriptor.flags);
103
+ DEBUG <= 1 && this.log_draw("DrawCopy", draw_copy);
104
+ }
105
+
106
+ if (draw_copy.data.src_bitmap.descriptor.type == SPICE_IMAGE_TYPE_QUIC)
107
+ {
108
+ var canvas = this.surfaces[draw_copy.base.surface_id].canvas;
109
+ if (! draw_copy.data.src_bitmap.quic)
110
+ {
111
+ this.log_warn("FIXME: DrawCopy could not handle this QUIC file.");
112
+ return false;
113
+ }
114
+ var source_img = convert_spice_quic_to_web(canvas.context,
115
+ draw_copy.data.src_bitmap.quic);
116
+
117
+ return this.draw_copy_helper(
118
+ { base: draw_copy.base,
119
+ src_area: draw_copy.data.src_area,
120
+ image_data: source_img,
121
+ tag: "copyquic." + draw_copy.data.src_bitmap.quic.type,
122
+ has_alpha: (draw_copy.data.src_bitmap.quic.type == QUIC_IMAGE_TYPE_RGBA ? true : false) ,
123
+ descriptor : draw_copy.data.src_bitmap.descriptor
124
+ });
125
+ }
126
+ else if (draw_copy.data.src_bitmap.descriptor.type == SPICE_IMAGE_TYPE_FROM_CACHE ||
127
+ draw_copy.data.src_bitmap.descriptor.type == SPICE_IMAGE_TYPE_FROM_CACHE_LOSSLESS)
128
+ {
129
+ if (! this.cache || ! this.cache[draw_copy.data.src_bitmap.descriptor.id])
130
+ {
131
+ this.log_warn("FIXME: DrawCopy did not find image id " + draw_copy.data.src_bitmap.descriptor.id + " in cache.");
132
+ return false;
133
+ }
134
+
135
+ return this.draw_copy_helper(
136
+ { base: draw_copy.base,
137
+ src_area: draw_copy.data.src_area,
138
+ image_data: this.cache[draw_copy.data.src_bitmap.descriptor.id],
139
+ tag: "copycache." + draw_copy.data.src_bitmap.descriptor.id,
140
+ has_alpha: true, /* FIXME - may want this to be false... */
141
+ descriptor : draw_copy.data.src_bitmap.descriptor
142
+ });
143
+
144
+ /* FIXME - LOSSLESS CACHE ramifications not understood or handled */
145
+ }
146
+ else if (draw_copy.data.src_bitmap.descriptor.type == SPICE_IMAGE_TYPE_SURFACE)
147
+ {
148
+ var source_context = this.surfaces[draw_copy.data.src_bitmap.surface_id].canvas.context;
149
+ var target_context = this.surfaces[draw_copy.base.surface_id].canvas.context;
150
+
151
+ var source_img = source_context.getImageData(
152
+ draw_copy.data.src_area.left, draw_copy.data.src_area.top,
153
+ draw_copy.data.src_area.right - draw_copy.data.src_area.left,
154
+ draw_copy.data.src_area.bottom - draw_copy.data.src_area.top);
155
+ var computed_src_area = new SpiceRect;
156
+ computed_src_area.top = computed_src_area.left = 0;
157
+ computed_src_area.right = source_img.width;
158
+ computed_src_area.bottom = source_img.height;
159
+
160
+ /* FIXME - there is a potential optimization here.
161
+ That is, if the surface is from 0,0, and
162
+ both surfaces are alpha surfaces, you should
163
+ be able to just do a drawImage, which should
164
+ save time. */
165
+
166
+ return this.draw_copy_helper(
167
+ { base: draw_copy.base,
168
+ src_area: computed_src_area,
169
+ image_data: source_img,
170
+ tag: "copysurf." + draw_copy.data.src_bitmap.surface_id,
171
+ has_alpha: this.surfaces[draw_copy.data.src_bitmap.surface_id].format == SPICE_SURFACE_FMT_32_xRGB ? false : true,
172
+ descriptor : draw_copy.data.src_bitmap.descriptor
173
+ });
174
+
175
+ return true;
176
+ }
177
+ else if (draw_copy.data.src_bitmap.descriptor.type == SPICE_IMAGE_TYPE_JPEG)
178
+ {
179
+ if (! draw_copy.data.src_bitmap.jpeg)
180
+ {
181
+ this.log_warn("FIXME: DrawCopy could not handle this JPEG file.");
182
+ return false;
183
+ }
184
+
185
+ // FIXME - how lame is this. Be have it in binary format, and we have
186
+ // to put it into string to get it back into jpeg. Blech.
187
+ var tmpstr = "data:image/jpeg,";
188
+ var img = new Image;
189
+ var i;
190
+ var qdv = new Uint8Array(draw_copy.data.src_bitmap.jpeg.data);
191
+ for (i = 0; i < qdv.length; i++)
192
+ {
193
+ tmpstr += '%';
194
+ if (qdv[i] < 16)
195
+ tmpstr += '0';
196
+ tmpstr += qdv[i].toString(16);
197
+ }
198
+
199
+ img.o =
200
+ { base: draw_copy.base,
201
+ tag: "jpeg." + draw_copy.data.src_bitmap.surface_id,
202
+ descriptor : draw_copy.data.src_bitmap.descriptor,
203
+ sc : this,
204
+ };
205
+ img.onload = handle_draw_jpeg_onload;
206
+ img.src = tmpstr;
207
+
208
+ return true;
209
+ }
210
+ else if (draw_copy.data.src_bitmap.descriptor.type == SPICE_IMAGE_TYPE_JPEG_ALPHA)
211
+ {
212
+ if (! draw_copy.data.src_bitmap.jpeg_alpha)
213
+ {
214
+ this.log_warn("FIXME: DrawCopy could not handle this JPEG ALPHA file.");
215
+ return false;
216
+ }
217
+
218
+ // FIXME - how lame is this. Be have it in binary format, and we have
219
+ // to put it into string to get it back into jpeg. Blech.
220
+ var tmpstr = "data:image/jpeg,";
221
+ var img = new Image;
222
+ var i;
223
+ var qdv = new Uint8Array(draw_copy.data.src_bitmap.jpeg_alpha.data);
224
+ for (i = 0; i < qdv.length; i++)
225
+ {
226
+ tmpstr += '%';
227
+ if (qdv[i] < 16)
228
+ tmpstr += '0';
229
+ tmpstr += qdv[i].toString(16);
230
+ }
231
+
232
+ img.o =
233
+ { base: draw_copy.base,
234
+ tag: "jpeg." + draw_copy.data.src_bitmap.surface_id,
235
+ descriptor : draw_copy.data.src_bitmap.descriptor,
236
+ sc : this,
237
+ };
238
+
239
+ if (this.surfaces[draw_copy.base.surface_id].format == SPICE_SURFACE_FMT_32_ARGB)
240
+ {
241
+
242
+ var canvas = this.surfaces[draw_copy.base.surface_id].canvas;
243
+ img.alpha_img = convert_spice_lz_to_web(canvas.context,
244
+ draw_copy.data.src_bitmap.jpeg_alpha.alpha);
245
+ }
246
+ img.onload = handle_draw_jpeg_onload;
247
+ img.src = tmpstr;
248
+
249
+ return true;
250
+ }
251
+ else if (draw_copy.data.src_bitmap.descriptor.type == SPICE_IMAGE_TYPE_BITMAP)
252
+ {
253
+ var canvas = this.surfaces[draw_copy.base.surface_id].canvas;
254
+ if (! draw_copy.data.src_bitmap.bitmap)
255
+ {
256
+ this.log_err("null bitmap");
257
+ return false;
258
+ }
259
+
260
+ var source_img = convert_spice_bitmap_to_web(canvas.context,
261
+ draw_copy.data.src_bitmap.bitmap);
262
+ if (! source_img)
263
+ {
264
+ this.log_warn("FIXME: Unable to interpret bitmap of format: " +
265
+ draw_copy.data.src_bitmap.bitmap.format);
266
+ return false;
267
+ }
268
+
269
+ return this.draw_copy_helper(
270
+ { base: draw_copy.base,
271
+ src_area: draw_copy.data.src_area,
272
+ image_data: source_img,
273
+ tag: "bitmap." + draw_copy.data.src_bitmap.bitmap.format,
274
+ has_alpha: draw_copy.data.src_bitmap.bitmap == SPICE_BITMAP_FMT_32BIT ? false : true,
275
+ descriptor : draw_copy.data.src_bitmap.descriptor
276
+ });
277
+ }
278
+ else if (draw_copy.data.src_bitmap.descriptor.type == SPICE_IMAGE_TYPE_LZ_RGB)
279
+ {
280
+ var canvas = this.surfaces[draw_copy.base.surface_id].canvas;
281
+ if (! draw_copy.data.src_bitmap.lz_rgb)
282
+ {
283
+ this.log_err("null lz_rgb ");
284
+ return false;
285
+ }
286
+
287
+ if (draw_copy.data.src_bitmap.lz_rgb.top_down != 1)
288
+ this.log_warn("FIXME: Implement non top down support for lz_rgb");
289
+
290
+ var source_img = convert_spice_lz_to_web(canvas.context,
291
+ draw_copy.data.src_bitmap.lz_rgb);
292
+ if (! source_img)
293
+ {
294
+ this.log_warn("FIXME: Unable to interpret bitmap of type: " +
295
+ draw_copy.data.src_bitmap.lz_rgb.type);
296
+ return false;
297
+ }
298
+
299
+ return this.draw_copy_helper(
300
+ { base: draw_copy.base,
301
+ src_area: draw_copy.data.src_area,
302
+ image_data: source_img,
303
+ tag: "lz_rgb." + draw_copy.data.src_bitmap.lz_rgb.type,
304
+ has_alpha: draw_copy.data.src_bitmap.lz_rgb.type == LZ_IMAGE_TYPE_RGBA ? true : false ,
305
+ descriptor : draw_copy.data.src_bitmap.descriptor
306
+ });
307
+ }
308
+ else
309
+ {
310
+ this.log_warn("FIXME: DrawCopy unhandled image type: " + draw_copy.data.src_bitmap.descriptor.type);
311
+ this.log_draw("DrawCopy", draw_copy);
312
+ return false;
313
+ }
314
+ }
315
+
316
+ this.log_warn("FIXME: DrawCopy no src_bitmap.");
317
+ return false;
318
+ }
319
+
320
+ if (msg.type == SPICE_MSG_DISPLAY_DRAW_FILL)
321
+ {
322
+ var draw_fill = new SpiceMsgDisplayDrawFill(msg.data);
323
+
324
+ DEBUG > 1 && this.log_draw("DrawFill", draw_fill);
325
+
326
+ if (draw_fill.data.rop_descriptor != SPICE_ROPD_OP_PUT)
327
+ this.log_warn("FIXME: DrawFill we don't handle ropd type: " + draw_fill.data.rop_descriptor);
328
+ if (draw_fill.data.mask.flags)
329
+ this.log_warn("FIXME: DrawFill we don't handle mask flag: " + draw_fill.data.mask.flags);
330
+ if (draw_fill.data.mask.bitmap)
331
+ this.log_warn("FIXME: DrawFill we don't handle mask");
332
+
333
+ if (draw_fill.data.brush.type == SPICE_BRUSH_TYPE_SOLID)
334
+ {
335
+ // FIXME - do brushes ever have alpha?
336
+ var color = draw_fill.data.brush.color & 0xffffff;
337
+ var color_str = "rgb(" + (color >> 16) + ", " + ((color >> 8) & 0xff) + ", " + (color & 0xff) + ")";
338
+ this.surfaces[draw_fill.base.surface_id].canvas.context.fillStyle = color_str;
339
+
340
+ this.surfaces[draw_fill.base.surface_id].canvas.context.fillRect(
341
+ draw_fill.base.box.left, draw_fill.base.box.top,
342
+ draw_fill.base.box.right - draw_fill.base.box.left,
343
+ draw_fill.base.box.bottom - draw_fill.base.box.top);
344
+
345
+ if (DUMP_DRAWS && this.parent.dump_id)
346
+ {
347
+ var debug_canvas = document.createElement("canvas");
348
+ debug_canvas.setAttribute('width', this.surfaces[draw_fill.base.surface_id].canvas.width);
349
+ debug_canvas.setAttribute('height', this.surfaces[draw_fill.base.surface_id].canvas.height);
350
+ debug_canvas.setAttribute('id', "fillbrush." + draw_fill.base.surface_id + "." + this.surfaces[draw_fill.base.surface_id].draw_count);
351
+ debug_canvas.getContext("2d").fillStyle = color_str;
352
+ debug_canvas.getContext("2d").fillRect(
353
+ draw_fill.base.box.left, draw_fill.base.box.top,
354
+ draw_fill.base.box.right - draw_fill.base.box.left,
355
+ draw_fill.base.box.bottom - draw_fill.base.box.top);
356
+ document.getElementById(this.parent.dump_id).appendChild(debug_canvas);
357
+ }
358
+
359
+ this.surfaces[draw_fill.base.surface_id].draw_count++;
360
+
361
+ }
362
+ else
363
+ {
364
+ this.log_warn("FIXME: DrawFill can't handle brush type: " + draw_fill.data.brush.type);
365
+ }
366
+ return true;
367
+ }
368
+
369
+ if (msg.type == SPICE_MSG_DISPLAY_COPY_BITS)
370
+ {
371
+ var copy_bits = new SpiceMsgDisplayCopyBits(msg.data);
372
+
373
+ DEBUG > 1 && this.log_draw("CopyBits", copy_bits);
374
+
375
+ var source_canvas = this.surfaces[copy_bits.base.surface_id].canvas;
376
+ var source_context = source_canvas.context;
377
+
378
+ var width = source_canvas.width - copy_bits.src_pos.x;
379
+ var height = source_canvas.height - copy_bits.src_pos.y;
380
+ if (width > (copy_bits.base.box.right - copy_bits.base.box.left))
381
+ width = copy_bits.base.box.right - copy_bits.base.box.left;
382
+ if (height > (copy_bits.base.box.bottom - copy_bits.base.box.top))
383
+ height = copy_bits.base.box.bottom - copy_bits.base.box.top;
384
+
385
+ var source_img = source_context.getImageData(
386
+ copy_bits.src_pos.x, copy_bits.src_pos.y, width, height);
387
+ //source_context.putImageData(source_img, copy_bits.base.box.left, copy_bits.base.box.top);
388
+ putImageDataWithAlpha(source_context, source_img, copy_bits.base.box.left, copy_bits.base.box.top);
389
+
390
+ if (DUMP_DRAWS && this.parent.dump_id)
391
+ {
392
+ var debug_canvas = document.createElement("canvas");
393
+ debug_canvas.setAttribute('width', width);
394
+ debug_canvas.setAttribute('height', height);
395
+ debug_canvas.setAttribute('id', "copybits" + copy_bits.base.surface_id + "." + this.surfaces[copy_bits.base.surface_id].draw_count);
396
+ debug_canvas.getContext("2d").putImageData(source_img, 0, 0);
397
+ document.getElementById(this.parent.dump_id).appendChild(debug_canvas);
398
+ }
399
+
400
+
401
+ this.surfaces[copy_bits.base.surface_id].draw_count++;
402
+ return true;
403
+ }
404
+
405
+ if (msg.type == SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES)
406
+ {
407
+ this.known_unimplemented(msg.type, "Inval All Palettes");
408
+ return true;
409
+ }
410
+
411
+ if (msg.type == SPICE_MSG_DISPLAY_SURFACE_CREATE)
412
+ {
413
+ if (! ("surfaces" in this))
414
+ this.surfaces = [];
415
+
416
+ var m = new SpiceMsgSurfaceCreate(msg.data);
417
+ DEBUG > 1 && console.log(this.type + ": MsgSurfaceCreate id " + m.surface.surface_id
418
+ + "; " + m.surface.width + "x" + m.surface.height
419
+ + "; format " + m.surface.format
420
+ + "; flags " + m.surface.flags);
421
+ if (m.surface.format != SPICE_SURFACE_FMT_32_xRGB &&
422
+ m.surface.format != SPICE_SURFACE_FMT_32_ARGB)
423
+ {
424
+ this.log_warn("FIXME: cannot handle surface format " + m.surface.format + " yet.");
425
+ return false;
426
+ }
427
+
428
+ var canvas = document.createElement("canvas");
429
+ canvas.setAttribute('width', m.surface.width);
430
+ canvas.setAttribute('height', m.surface.height);
431
+ canvas.setAttribute('id', "spice_surface_" + m.surface.surface_id);
432
+ canvas.setAttribute('tabindex', m.surface.surface_id);
433
+ canvas.context = canvas.getContext("2d");
434
+
435
+ if (DUMP_CANVASES && this.parent.dump_id)
436
+ document.getElementById(this.parent.dump_id).appendChild(canvas);
437
+
438
+ m.surface.canvas = canvas;
439
+ m.surface.draw_count = 0;
440
+ this.surfaces[m.surface.surface_id] = m.surface;
441
+
442
+ if (m.surface.flags & SPICE_SURFACE_FLAGS_PRIMARY)
443
+ {
444
+ this.primary_surface = m.surface.surface_id;
445
+
446
+ /* This .save() is done entirely to enable SPICE_MSG_DISPLAY_RESET */
447
+ canvas.context.save();
448
+ document.getElementById(this.parent.screen_id).appendChild(canvas);
449
+ document.getElementById(this.parent.screen_id).setAttribute('width', m.surface.width);
450
+ document.getElementById(this.parent.screen_id).setAttribute('height', m.surface.height);
451
+ this.hook_events();
452
+ }
453
+ return true;
454
+ }
455
+
456
+ if (msg.type == SPICE_MSG_DISPLAY_SURFACE_DESTROY)
457
+ {
458
+ var m = new SpiceMsgSurfaceDestroy(msg.data);
459
+ DEBUG > 1 && console.log(this.type + ": MsgSurfaceDestroy id " + m.surface_id);
460
+ this.delete_surface(m.surface_id);
461
+ return true;
462
+ }
463
+
464
+ if (msg.type == SPICE_MSG_DISPLAY_STREAM_CREATE)
465
+ {
466
+ var m = new SpiceMsgDisplayStreamCreate(msg.data);
467
+ DEBUG > 1 && console.log(this.type + ": MsgStreamCreate id" + m.id);
468
+ if (!this.streams)
469
+ this.streams = new Array();
470
+ if (this.streams[m.id])
471
+ console.log("Stream already exists");
472
+ else
473
+ this.streams[m.id] = m;
474
+ if (m.codec_type != SPICE_VIDEO_CODEC_TYPE_MJPEG)
475
+ console.log("Unhandled stream codec: "+m.codec_type);
476
+ return true;
477
+ }
478
+
479
+ if (msg.type == SPICE_MSG_DISPLAY_STREAM_DATA)
480
+ {
481
+ var m = new SpiceMsgDisplayStreamData(msg.data);
482
+ if (!this.streams[m.base.id])
483
+ {
484
+ console.log("no stream for data");
485
+ return false;
486
+ }
487
+ if (this.streams[m.base.id].codec_type === SPICE_VIDEO_CODEC_TYPE_MJPEG)
488
+ {
489
+ var tmpstr = "data:image/jpeg,";
490
+ var img = new Image;
491
+ var i;
492
+ for (i = 0; i < m.data.length; i++)
493
+ {
494
+ tmpstr += '%';
495
+ if (m.data[i] < 16)
496
+ tmpstr += '0';
497
+ tmpstr += m.data[i].toString(16);
498
+ }
499
+ var strm_base = new SpiceMsgDisplayBase();
500
+ strm_base.surface_id = this.streams[m.base.id].surface_id;
501
+ strm_base.box = this.streams[m.base.id].dest;
502
+ strm_base.clip = this.streams[m.base.id].clip;
503
+ img.o =
504
+ { base: strm_base,
505
+ tag: "mjpeg." + m.base.id,
506
+ descriptor: null,
507
+ sc : this,
508
+ };
509
+ img.onload = handle_draw_jpeg_onload;
510
+ img.src = tmpstr;
511
+ }
512
+ return true;
513
+ }
514
+
515
+ if (msg.type == SPICE_MSG_DISPLAY_STREAM_CLIP)
516
+ {
517
+ var m = new SpiceMsgDisplayStreamClip(msg.data);
518
+ DEBUG > 1 && console.log(this.type + ": MsgStreamClip id" + m.id);
519
+ this.streams[m.id].clip = m.clip;
520
+ return true;
521
+ }
522
+
523
+ if (msg.type == SPICE_MSG_DISPLAY_STREAM_DESTROY)
524
+ {
525
+ var m = new SpiceMsgDisplayStreamDestroy(msg.data);
526
+ DEBUG > 1 && console.log(this.type + ": MsgStreamDestroy id" + m.id);
527
+ this.streams[m.id] = undefined;
528
+ return true;
529
+ }
530
+
531
+ return false;
532
+ }
533
+
534
+ SpiceDisplayConn.prototype.delete_surface = function(surface_id)
535
+ {
536
+ var canvas = document.getElementById("spice_surface_" + surface_id);
537
+ if (DUMP_CANVASES && this.parent.dump_id)
538
+ document.getElementById(this.parent.dump_id).removeChild(canvas);
539
+ if (this.primary_surface == surface_id)
540
+ {
541
+ this.unhook_events();
542
+ this.primary_surface = undefined;
543
+ document.getElementById(this.parent.screen_id).removeChild(canvas);
544
+ }
545
+
546
+ delete this.surfaces[surface_id];
547
+ }
548
+
549
+
550
+ SpiceDisplayConn.prototype.draw_copy_helper = function(o)
551
+ {
552
+
553
+ var canvas = this.surfaces[o.base.surface_id].canvas;
554
+ if (o.has_alpha)
555
+ {
556
+ /* FIXME - This is based on trial + error, not a serious thoughtful
557
+ analysis of what Spice requires. See display.js for more. */
558
+ if (this.surfaces[o.base.surface_id].format == SPICE_SURFACE_FMT_32_xRGB)
559
+ {
560
+ stripAlpha(o.image_data);
561
+ canvas.context.putImageData(o.image_data, o.base.box.left, o.base.box.top);
562
+ }
563
+ else
564
+ putImageDataWithAlpha(canvas.context, o.image_data,
565
+ o.base.box.left, o.base.box.top);
566
+ }
567
+ else
568
+ canvas.context.putImageData(o.image_data, o.base.box.left, o.base.box.top);
569
+
570
+ if (o.src_area.left > 0 || o.src_area.top > 0)
571
+ {
572
+ this.log_warn("FIXME: DrawCopy not shifting draw copies just yet...");
573
+ }
574
+
575
+ if (o.descriptor && (o.descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME))
576
+ {
577
+ if (! ("cache" in this))
578
+ this.cache = [];
579
+ this.cache[o.descriptor.id] = o.image_data;
580
+ }
581
+
582
+ if (DUMP_DRAWS && this.parent.dump_id)
583
+ {
584
+ var debug_canvas = document.createElement("canvas");
585
+ debug_canvas.setAttribute('width', o.image_data.width);
586
+ debug_canvas.setAttribute('height', o.image_data.height);
587
+ debug_canvas.setAttribute('id', o.tag + "." +
588
+ this.surfaces[o.base.surface_id].draw_count + "." +
589
+ o.base.surface_id + "@" + o.base.box.left + "x" + o.base.box.top);
590
+ debug_canvas.getContext("2d").putImageData(o.image_data, 0, 0);
591
+ document.getElementById(this.parent.dump_id).appendChild(debug_canvas);
592
+ }
593
+
594
+ this.surfaces[o.base.surface_id].draw_count++;
595
+
596
+ return true;
597
+ }
598
+
599
+
600
+ SpiceDisplayConn.prototype.log_draw = function(prefix, draw)
601
+ {
602
+ var str = prefix + "." + draw.base.surface_id + "." + this.surfaces[draw.base.surface_id].draw_count + ": ";
603
+ str += "base.box " + draw.base.box.left + ", " + draw.base.box.top + " to " +
604
+ draw.base.box.right + ", " + draw.base.box.bottom;
605
+ str += "; clip.type " + draw.base.clip.type;
606
+
607
+ if (draw.data)
608
+ {
609
+ if (draw.data.src_area)
610
+ str += "; src_area " + draw.data.src_area.left + ", " + draw.data.src_area.top + " to "
611
+ + draw.data.src_area.right + ", " + draw.data.src_area.bottom;
612
+
613
+ if (draw.data.src_bitmap && draw.data.src_bitmap != null)
614
+ {
615
+ str += "; src_bitmap id: " + draw.data.src_bitmap.descriptor.id;
616
+ str += "; src_bitmap width " + draw.data.src_bitmap.descriptor.width + ", height " + draw.data.src_bitmap.descriptor.height;
617
+ str += "; src_bitmap type " + draw.data.src_bitmap.descriptor.type + ", flags " + draw.data.src_bitmap.descriptor.flags;
618
+ if (draw.data.src_bitmap.surface_id !== undefined)
619
+ str += "; src_bitmap surface_id " + draw.data.src_bitmap.surface_id;
620
+ if (draw.data.src_bitmap.quic)
621
+ str += "; QUIC type " + draw.data.src_bitmap.quic.type +
622
+ "; width " + draw.data.src_bitmap.quic.width +
623
+ "; height " + draw.data.src_bitmap.quic.height ;
624
+ if (draw.data.src_bitmap.lz_rgb)
625
+ str += "; LZ_RGB length " + draw.data.src_bitmap.lz_rgb.length +
626
+ "; magic " + draw.data.src_bitmap.lz_rgb.magic +
627
+ "; version 0x" + draw.data.src_bitmap.lz_rgb.version.toString(16) +
628
+ "; type " + draw.data.src_bitmap.lz_rgb.type +
629
+ "; width " + draw.data.src_bitmap.lz_rgb.width +
630
+ "; height " + draw.data.src_bitmap.lz_rgb.height +
631
+ "; stride " + draw.data.src_bitmap.lz_rgb.stride +
632
+ "; top down " + draw.data.src_bitmap.lz_rgb.top_down;
633
+ }
634
+ else
635
+ str += "; src_bitmap is null";
636
+
637
+ if (draw.data.brush)
638
+ {
639
+ if (draw.data.brush.type == SPICE_BRUSH_TYPE_SOLID)
640
+ str += "; brush.color 0x" + draw.data.brush.color.toString(16);
641
+ if (draw.data.brush.type == SPICE_BRUSH_TYPE_PATTERN)
642
+ {
643
+ str += "; brush.pat ";
644
+ if (draw.data.brush.pattern.pat != null)
645
+ str += "[SpiceImage]";
646
+ else
647
+ str += "[null]";
648
+ str += " at " + draw.data.brush.pattern.pos.x + ", " + draw.data.brush.pattern.pos.y;
649
+ }
650
+ }
651
+
652
+ str += "; rop_descriptor " + draw.data.rop_descriptor;
653
+ if (draw.data.scale_mode !== undefined)
654
+ str += "; scale_mode " + draw.data.scale_mode;
655
+ str += "; mask.flags " + draw.data.mask.flags;
656
+ str += "; mask.pos " + draw.data.mask.pos.x + ", " + draw.data.mask.pos.y;
657
+ if (draw.data.mask.bitmap != null)
658
+ {
659
+ str += "; mask.bitmap width " + draw.data.mask.bitmap.descriptor.width + ", height " + draw.data.mask.bitmap.descriptor.height;
660
+ str += "; mask.bitmap type " + draw.data.mask.bitmap.descriptor.type + ", flags " + draw.data.mask.bitmap.descriptor.flags;
661
+ }
662
+ else
663
+ str += "; mask.bitmap is null";
664
+ }
665
+
666
+ console.log(str);
667
+ }
668
+
669
+ SpiceDisplayConn.prototype.hook_events = function()
670
+ {
671
+ if (this.primary_surface !== undefined)
672
+ {
673
+ var canvas = this.surfaces[this.primary_surface].canvas;
674
+ canvas.sc = this.parent;
675
+ canvas.addEventListener('mousemove', handle_mousemove);
676
+ canvas.addEventListener('mousedown', handle_mousedown);
677
+ canvas.addEventListener('contextmenu', handle_contextmenu);
678
+ canvas.addEventListener('mouseup', handle_mouseup);
679
+ canvas.addEventListener('keydown', handle_keydown);
680
+ canvas.addEventListener('keyup', handle_keyup);
681
+ canvas.addEventListener('mouseout', handle_mouseout);
682
+ canvas.addEventListener('mouseover', handle_mouseover);
683
+ canvas.addEventListener('mousewheel', handle_mousewheel);
684
+ canvas.focus();
685
+ }
686
+ }
687
+
688
+ SpiceDisplayConn.prototype.unhook_events = function()
689
+ {
690
+ if (this.primary_surface !== undefined)
691
+ {
692
+ var canvas = this.surfaces[this.primary_surface].canvas;
693
+ canvas.removeEventListener('mousemove', handle_mousemove);
694
+ canvas.removeEventListener('mousedown', handle_mousedown);
695
+ canvas.removeEventListener('contextmenu', handle_contextmenu);
696
+ canvas.removeEventListener('mouseup', handle_mouseup);
697
+ canvas.removeEventListener('keydown', handle_keydown);
698
+ canvas.removeEventListener('keyup', handle_keyup);
699
+ canvas.removeEventListener('mouseout', handle_mouseout);
700
+ canvas.removeEventListener('mouseover', handle_mouseover);
701
+ canvas.removeEventListener('mousewheel', handle_mousewheel);
702
+ }
703
+ }
704
+
705
+
706
+ SpiceDisplayConn.prototype.destroy_surfaces = function()
707
+ {
708
+ for (var s in this.surfaces)
709
+ {
710
+ this.delete_surface(this.surfaces[s].surface_id);
711
+ }
712
+
713
+ this.surfaces = undefined;
714
+ }
715
+
716
+
717
+ function handle_mouseover(e)
718
+ {
719
+ this.focus();
720
+ }
721
+
722
+ function handle_mouseout(e)
723
+ {
724
+ this.blur();
725
+ }
726
+
727
+ function handle_draw_jpeg_onload()
728
+ {
729
+ var temp_canvas = null;
730
+ var context;
731
+
732
+ /*------------------------------------------------------------
733
+ ** FIXME:
734
+ ** The helper should be extended to be able to handle actual HtmlImageElements
735
+ ** ...and the cache should be modified to do so as well
736
+ **----------------------------------------------------------*/
737
+ if (this.o.sc.surfaces[this.o.base.surface_id] === undefined)
738
+ {
739
+ // This can happen; if the jpeg image loads after our surface
740
+ // has been destroyed (e.g. open a menu, close it quickly),
741
+ // we'll find we have no surface.
742
+ DEBUG > 2 && this.o.sc.log_info("Discarding jpeg; presumed lost surface " + this.o.base.surface_id);
743
+ temp_canvas = document.createElement("canvas");
744
+ temp_canvas.setAttribute('width', this.o.base.box.right);
745
+ temp_canvas.setAttribute('height', this.o.base.box.bottom);
746
+ context = temp_canvas.getContext("2d");
747
+ }
748
+ else
749
+ context = this.o.sc.surfaces[this.o.base.surface_id].canvas.context;
750
+
751
+ if (this.alpha_img)
752
+ {
753
+ var c = document.createElement("canvas");
754
+ var t = c.getContext("2d");
755
+ c.setAttribute('width', this.alpha_img.width);
756
+ c.setAttribute('height', this.alpha_img.height);
757
+ t.putImageData(this.alpha_img, 0, 0);
758
+ t.globalCompositeOperation = 'source-in';
759
+ t.drawImage(this, 0, 0);
760
+
761
+ context.drawImage(c, this.o.base.box.left, this.o.base.box.top);
762
+
763
+ if (this.o.descriptor &&
764
+ (this.o.descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME))
765
+ {
766
+ if (! ("cache" in this.o.sc))
767
+ this.o.sc.cache = [];
768
+
769
+ this.o.sc.cache[this.o.descriptor.id] =
770
+ t.getImageData(0, 0,
771
+ this.alpha_img.width,
772
+ this.alpha_img.height);
773
+ }
774
+ }
775
+ else
776
+ {
777
+ context.drawImage(this, this.o.base.box.left, this.o.base.box.top);
778
+
779
+ if (this.o.descriptor &&
780
+ (this.o.descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME))
781
+ {
782
+ if (! ("cache" in this.o.sc))
783
+ this.o.sc.cache = [];
784
+
785
+ this.o.sc.cache[this.o.descriptor.id] =
786
+ context.getImageData(this.o.base.box.left, this.o.base.box.top,
787
+ this.o.base.box.right - this.o.base.box.left,
788
+ this.o.base.box.bottom - this.o.base.box.top);
789
+ }
790
+ }
791
+
792
+ if (temp_canvas == null)
793
+ {
794
+ if (DUMP_DRAWS && this.o.sc.parent.dump_id)
795
+ {
796
+ var debug_canvas = document.createElement("canvas");
797
+ debug_canvas.setAttribute('id', this.o.tag + "." +
798
+ this.o.sc.surfaces[this.o.base.surface_id].draw_count + "." +
799
+ this.o.base.surface_id + "@" + this.o.base.box.left + "x" + this.o.base.box.top);
800
+ debug_canvas.getContext("2d").drawImage(this, 0, 0);
801
+ document.getElementById(this.o.sc.parent.dump_id).appendChild(debug_canvas);
802
+ }
803
+
804
+ this.o.sc.surfaces[this.o.base.surface_id].draw_count++;
805
+ }
806
+ }