Removed keymap (merged into keyboard), fixed JS semicolons, etc.
[guacamole-common-js.git] / src / main / resources / layer.js
1
2 /*
3  *  Guacamole - Clientless Remote Desktop
4  *  Copyright (C) 2010  Michael Jumper
5  *
6  *  This program is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU Affero General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU Affero General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Affero General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 function Layer(width, height) {
21
22     // Off-screen buffer
23     var display = document.createElement("canvas");
24     var displayContext = display.getContext("2d");
25
26     function resize(newWidth, newHeight) {
27         display.style.position = "absolute";
28         display.style.left = "0px";
29         display.style.top = "0px";
30
31         display.width = newWidth;
32         display.height = newHeight;
33
34         width = newWidth;
35         height = newHeight;
36     }
37
38     display.resize = function(newWidth, newHeight) {
39         if (newWidth != width || newHeight != height)
40             resize(newWidth, newHeight);
41     };
42
43     function fitRect(x, y, w, h) {
44         
45         // Calculate bounds
46         var opBoundX = w + x;
47         var opBoundY = h + y;
48         
49         // Determine max width
50         var resizeWidth;
51         if (opBoundX > width)
52             resizeWidth = opBoundX;
53         else
54             resizeWidth = width;
55
56         // Determine max height
57         var resizeHeight;
58         if (opBoundY > height)
59             resizeHeight = opBoundY;
60         else
61             resizeHeight = height;
62
63         // Resize if necessary
64         if (resizeWidth != width || resizeHeight != height)
65             resize(resizeWidth, resizeHeight);
66
67     }
68
69     resize(width, height);
70
71     var updates = new Array();
72     var autosize = 0;
73
74     function Update(updateHandler) {
75
76         this.setHandler = function(handler) {
77             updateHandler = handler;
78         };
79
80         this.hasHandler = function() {
81             return updateHandler != null;
82         };
83
84         this.handle = function() {
85             updateHandler();
86         };
87
88     }
89
90     display.setAutosize = function(flag) {
91         autosize = flag;
92     };
93
94     function reserveJob(handler) {
95         
96         // If no pending updates, just call (if available) and exit
97         if (display.isReady() && handler != null) {
98             handler();
99             return null;
100         }
101
102         // If updates are pending/executing, schedule a pending update
103         // and return a reference to it.
104         var update = new Update(handler);
105         updates.push(update);
106         return update;
107         
108     }
109
110     function handlePendingUpdates() {
111
112         // Draw all pending updates.
113         var update;
114         while ((update = updates[0]) != null && update.hasHandler()) {
115             update.handle();
116             updates.shift();
117         }
118
119     }
120
121     display.isReady = function() {
122         return updates.length == 0;
123     };
124
125
126     display.drawImage = function(x, y, image) {
127         reserveJob(function() {
128             if (autosize != 0) fitRect(x, y, image.width, image.height);
129             displayContext.drawImage(image, x, y);
130         });
131     };
132
133
134     display.draw = function(x, y, url) {
135         var update = reserveJob(null);
136
137         var image = new Image();
138         image.onload = function() {
139
140             update.setHandler(function() {
141                 if (autosize != 0) fitRect(x, y, image.width, image.height);
142                 displayContext.drawImage(image, x, y);
143             });
144
145             // As this update originally had no handler and may have blocked
146             // other updates, handle any blocked updates.
147             handlePendingUpdates();
148
149         };
150         image.src = url;
151
152     };
153
154     // Run arbitrary function as soon as currently pending operations complete.
155     // Future operations will not block this function from being called (unlike
156     // the ready handler, which requires no pending updates)
157     display.sync = function(handler) {
158         reserveJob(handler);
159     };
160
161     display.copyRect = function(srcLayer, srcx, srcy, w, h, x, y) {
162
163         function doCopyRect() {
164             if (autosize != 0) fitRect(x, y, w, h);
165             displayContext.drawImage(srcLayer, srcx, srcy, w, h, x, y, w, h);
166         }
167
168         // If we ARE the source layer, no need to sync.
169         // Syncing would result in deadlock.
170         if (display === srcLayer)
171             reserveJob(doCopyRect);
172
173         // Otherwise synchronize copy operation with source layer
174         else {
175             var update = reserveJob(null);
176             srcLayer.sync(function() {
177                 
178                 update.setHandler(doCopyRect);
179
180                 // As this update originally had no handler and may have blocked
181                 // other updates, handle any blocked updates.
182                 handlePendingUpdates();
183
184             });
185         }
186
187     };
188
189     display.clearRect = function(x, y, w, h) {
190         reserveJob(function() {
191             if (autosize != 0) fitRect(x, y, w, h);
192             displayContext.clearRect(x, y, w, h);
193         });
194     };
195
196     display.filter = function(filter) {
197         reserveJob(function() {
198             var imageData = displayContext.getImageData(0, 0, width, height);
199             filter(imageData.data, width, height);
200             displayContext.putImageData(imageData, 0, 0);
201         });
202     };
203
204     var compositeOperation = {
205      /* 0x0 NOT IMPLEMENTED */
206         0x1: "destination-in",
207         0x2: "destination-out",
208      /* 0x3 NOT IMPLEMENTED */
209         0x4: "source-in",
210      /* 0x5 NOT IMPLEMENTED */
211         0x6: "source-atop",
212      /* 0x7 NOT IMPLEMENTED */
213         0x8: "source-out",
214         0x9: "destination-atop",
215         0xA: "xor",
216         0xB: "destination-over",
217         0xC: "copy",
218      /* 0xD NOT IMPLEMENTED */
219         0xE: "source-over",
220         0xF: "lighter"
221     };
222
223     display.setChannelMask = function(mask) {
224         reserveJob(function() {
225             displayContext.globalCompositeOperation = compositeOperation[mask];
226         });
227     };
228
229     return display;
230
231 }
232