3 * Guacamole - Clientless Remote Desktop
4 * Copyright (C) 2010 Michael Jumper
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.
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.
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/>.
20 function Layer(width, height) {
23 var display = document.createElement("canvas");
24 var displayContext = display.getContext("2d");
26 function resize(newWidth, newHeight) {
27 display.style.position = "absolute";
28 display.style.left = "0px";
29 display.style.top = "0px";
31 display.width = newWidth;
32 display.height = newHeight;
38 display.resize = function(newWidth, newHeight) {
39 if (newWidth != width || newHeight != height)
40 resize(newWidth, newHeight);
43 function fitRect(x, y, w, h) {
49 // Determine max width
52 resizeWidth = opBoundX;
56 // Determine max height
58 if (opBoundY > height)
59 resizeHeight = opBoundY;
61 resizeHeight = height;
63 // Resize if necessary
64 if (resizeWidth != width || resizeHeight != height)
65 resize(resizeWidth, resizeHeight);
69 resize(width, height);
71 var readyHandler = null;
72 var updates = new Array();
75 function Update(updateHandler) {
77 this.setHandler = function(handler) {
78 updateHandler = handler;
81 this.hasHandler = function() {
82 return updateHandler != null;
85 this.handle = function() {
91 display.setAutosize = function(flag) {
95 function reserveJob(handler) {
97 // If no pending updates, just call (if available) and exit
98 if (display.isReady() && handler != null) {
103 // If updates are pending/executing, schedule a pending update
104 // and return a reference to it.
105 var update = new Update(handler);
106 updates.push(update);
111 function handlePendingUpdates() {
113 // Draw all pending updates.
115 while ((update = updates[0]) != null && update.hasHandler()) {
120 // If done with updates, call ready handler
121 if (display.isReady() && readyHandler != null)
126 display.isReady = function() {
127 return updates.length == 0;
130 display.setReadyHandler = function(handler) {
131 readyHandler = handler;
135 display.drawImage = function(x, y, image) {
136 reserveJob(function() {
137 if (autosize != 0) fitRect(x, y, image.width, image.height);
138 displayContext.drawImage(image, x, y);
143 display.draw = function(x, y, url) {
144 var update = reserveJob(null);
146 var image = new Image();
147 image.onload = function() {
149 update.setHandler(function() {
150 if (autosize != 0) fitRect(x, y, image.width, image.height);
151 displayContext.drawImage(image, x, y);
154 // As this update originally had no handler and may have blocked
155 // other updates, handle any blocked updates.
156 handlePendingUpdates();
163 // Run arbitrary function as soon as currently pending operations complete.
164 // Future operations will not block this function from being called (unlike
165 // the ready handler, which requires no pending updates)
166 display.sync = function(handler) {
170 display.copyRect = function(srcLayer, srcx, srcy, w, h, x, y) {
172 function doCopyRect() {
173 if (autosize != 0) fitRect(x, y, w, h);
174 displayContext.drawImage(srcLayer, srcx, srcy, w, h, x, y, w, h);
177 // If we ARE the source layer, no need to sync.
178 // Syncing would result in deadlock.
179 if (display === srcLayer)
180 reserveJob(doCopyRect);
182 // Otherwise synchronize copy operation with source layer
184 var update = reserveJob(null);
185 srcLayer.sync(function() {
187 update.setHandler(doCopyRect);
189 // As this update originally had no handler and may have blocked
190 // other updates, handle any blocked updates.
191 handlePendingUpdates();
198 display.clearRect = function(x, y, w, h) {
199 reserveJob(function() {
200 if (autosize != 0) fitRect(x, y, w, h);
201 displayContext.clearRect(x, y, w, h);
205 display.filter = function(filter) {
206 reserveJob(function() {
207 var imageData = displayContext.getImageData(0, 0, width, height);
208 filter(imageData.data, width, height);
209 displayContext.putImageData(imageData, 0, 0);
213 var compositeOperation = {
214 /* 0x0 NOT IMPLEMENTED */
215 0x1: "destination-in",
216 0x2: "destination-out",
217 /* 0x3 NOT IMPLEMENTED */
219 /* 0x5 NOT IMPLEMENTED */
221 /* 0x7 NOT IMPLEMENTED */
223 0x9: "destination-atop",
225 0xB: "destination-over",
227 /* 0xD NOT IMPLEMENTED */
232 display.setChannelMask = function(mask) {
233 reserveJob(function() {
234 displayContext.globalCompositeOperation = compositeOperation[mask];