Source code: com/idanim/idvnc/idvncviewer/vncCanvas.java
1 //
2 // Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
3 //
4 // This is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This software is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this software; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 // USA.
18 //
19
20 package com.idanim.idvnc.idvncviewer;
21
22 import java.awt.*;
23 import java.awt.image.*;
24 import java.io.*;
25
26
27 //
28 // vncCanvas is a subclass of Canvas which draws a VNC desktop on it.
29 //
30
31 class vncCanvas extends Canvas
32 {
33 vncviewer v;
34 rfbProto rfb;
35 ColorModel cm;
36 Color[] colors;
37 Image rawPixelsImage;
38 animatedMemoryImageSource amis;
39 byte[] pixels;
40 Graphics sg, sg2;
41 Image paintImage;
42 Graphics pig, pig2;
43 boolean needToResetClip;
44
45 vncCanvas(vncviewer v1) throws IOException {
46 v = v1;
47 rfb = v.rfb;
48
49 cm = new DirectColorModel(8, 7, (7 << 3), (3 << 6));
50
51 rfb.writeSetPixelFormat(8, 8, false, true, 7, 7, 3, 0, 3, 6);
52
53 colors = new Color[256];
54
55 for (int i = 0; i < 256; i++) {
56 colors[i] = new Color(cm.getRGB(i));
57 }
58
59 pixels = new byte[rfb.framebufferWidth * rfb.framebufferHeight];
60
61 amis = new animatedMemoryImageSource(rfb.framebufferWidth,
62 rfb.framebufferHeight, cm, pixels);
63 rawPixelsImage = createImage(amis);
64
65 paintImage = v.createImage(rfb.framebufferWidth, rfb.framebufferHeight);
66
67 pig = paintImage.getGraphics();
68 }
69
70 public Dimension preferredSize() {
71 return new Dimension(rfb.framebufferWidth, rfb.framebufferHeight);
72 }
73
74 public Dimension minimumSize() {
75 return new Dimension(rfb.framebufferWidth, rfb.framebufferHeight);
76 }
77
78 public void update(Graphics g) {
79 }
80
81 public void paint(Graphics g) {
82 g.drawImage(paintImage, 0, 0, this);
83 }
84
85 //
86 // processNormalProtocol() - executed by the rfbThread to deal with the
87 // RFB socket.
88 //
89
90 public void processNormalProtocol() throws IOException {
91
92 rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth,
93 rfb.framebufferHeight, false);
94
95 sg = getGraphics();
96
97 needToResetClip = false;
98
99 //
100 // main dispatch loop
101 //
102
103 while (true) {
104 int msgType = rfb.readServerMessageType();
105
106 switch (msgType) {
107 case rfbProto.FramebufferUpdate:
108 rfb.readFramebufferUpdate();
109
110 for (int i = 0; i < rfb.updateNRects; i++) {
111 rfb.readFramebufferUpdateRectHdr();
112
113 if (needToResetClip &&
114 (rfb.updateRectEncoding != rfbProto.EncodingRaw)) {
115 try {
116 sg.setClip(0, 0, rfb.framebufferWidth, rfb.framebufferHeight);
117 pig.setClip(0, 0, rfb.framebufferWidth, rfb.framebufferHeight);
118 } catch (NoSuchMethodError e) {
119 }
120 needToResetClip = false;
121 }
122
123 switch (rfb.updateRectEncoding) {
124
125 case rfbProto.EncodingRaw:
126 drawRawRect(rfb.updateRectX, rfb.updateRectY,
127 rfb.updateRectW, rfb.updateRectH);
128 break;
129
130 case rfbProto.EncodingCopyRect:
131 rfb.readCopyRect();
132 pig.copyArea(rfb.copyRectSrcX, rfb.copyRectSrcY,
133 rfb.updateRectW, rfb.updateRectH,
134 rfb.updateRectX - rfb.copyRectSrcX,
135 rfb.updateRectY - rfb.copyRectSrcY);
136 if (v.options.copyRectFast) {
137 sg.copyArea(rfb.copyRectSrcX, rfb.copyRectSrcY,
138 rfb.updateRectW, rfb.updateRectH,
139 rfb.updateRectX - rfb.copyRectSrcX,
140 rfb.updateRectY - rfb.copyRectSrcY);
141 } else {
142 sg.drawImage(paintImage, 0, 0, this);
143 }
144 break;
145
146 case rfbProto.EncodingRRE:
147 {
148 int nSubrects = rfb.is.readInt();
149 int bg = rfb.is.read();
150 int pixel, x, y, w, h;
151 sg.translate(rfb.updateRectX, rfb.updateRectY);
152 sg.setColor(colors[bg]);
153 sg.fillRect(0, 0, rfb.updateRectW, rfb.updateRectH);
154 pig.translate(rfb.updateRectX, rfb.updateRectY);
155 pig.setColor(colors[bg]);
156 pig.fillRect(0, 0, rfb.updateRectW, rfb.updateRectH);
157 for (int j = 0; j < nSubrects; j++) {
158 pixel = rfb.is.read();
159 x = rfb.is.readUnsignedShort();
160 y = rfb.is.readUnsignedShort();
161 w = rfb.is.readUnsignedShort();
162 h = rfb.is.readUnsignedShort();
163 sg.setColor(colors[pixel]);
164 sg.fillRect(x, y, w, h);
165 pig.setColor(colors[pixel]);
166 pig.fillRect(x, y, w, h);
167 }
168 sg.translate(-rfb.updateRectX, -rfb.updateRectY);
169 pig.translate(-rfb.updateRectX, -rfb.updateRectY);
170 break;
171 }
172
173 case rfbProto.EncodingCoRRE:
174 {
175 int nSubrects = rfb.is.readInt();
176 int bg = rfb.is.read();
177 int pixel, x, y, w, h;
178
179 sg.translate(rfb.updateRectX, rfb.updateRectY);
180 sg.setColor(colors[bg]);
181 sg.fillRect(0, 0, rfb.updateRectW, rfb.updateRectH);
182 pig.translate(rfb.updateRectX, rfb.updateRectY);
183 pig.setColor(colors[bg]);
184 pig.fillRect(0, 0, rfb.updateRectW, rfb.updateRectH);
185
186 for (int j = 0; j < nSubrects; j++) {
187 pixel = rfb.is.read();
188 x = rfb.is.read();
189 y = rfb.is.read();
190 w = rfb.is.read();
191 h = rfb.is.read();
192
193 sg.setColor(colors[pixel]);
194 sg.fillRect(x, y, w, h);
195 pig.setColor(colors[pixel]);
196 pig.fillRect(x, y, w, h);
197 }
198 sg.translate(-rfb.updateRectX, -rfb.updateRectY);
199 pig.translate(-rfb.updateRectX, -rfb.updateRectY);
200
201 break;
202 }
203
204 case rfbProto.EncodingHextile:
205 {
206 int bg = 0, fg = 0, sx, sy, sw, sh;
207
208 for (int ty = rfb.updateRectY;
209 ty < rfb.updateRectY + rfb.updateRectH;
210 ty += 16) {
211 for (int tx = rfb.updateRectX;
212 tx < rfb.updateRectX + rfb.updateRectW;
213 tx += 16) {
214
215 int tw = 16, th = 16;
216
217 if (rfb.updateRectX + rfb.updateRectW - tx < 16)
218 tw = rfb.updateRectX + rfb.updateRectW - tx;
219 if (rfb.updateRectY + rfb.updateRectH - ty < 16)
220 th = rfb.updateRectY + rfb.updateRectH - ty;
221
222 int subencoding = rfb.is.read();
223
224 if ((subencoding & rfbProto.HextileRaw) != 0) {
225 drawRawRect(tx, ty, tw, th);
226 continue;
227 }
228
229 if (needToResetClip) {
230 try {
231 sg.setClip(0, 0,
232 rfb.framebufferWidth, rfb.framebufferHeight);
233 pig.setClip(0, 0,
234 rfb.framebufferWidth, rfb.framebufferHeight);
235 } catch (NoSuchMethodError e) {
236 }
237 needToResetClip = false;
238 }
239
240 if ((subencoding & rfbProto.HextileBackgroundSpecified) != 0)
241 bg = rfb.is.read();
242
243 sg.setColor(colors[bg]);
244 sg.fillRect(tx, ty, tw, th);
245 pig.setColor(colors[bg]);
246 pig.fillRect(tx, ty, tw, th);
247
248 if ((subencoding & rfbProto.HextileForegroundSpecified) != 0)
249 fg = rfb.is.read();
250
251 if ((subencoding & rfbProto.HextileAnySubrects) == 0)
252 continue;
253
254 int nSubrects = rfb.is.read();
255
256 sg.translate(tx, ty);
257 pig.translate(tx, ty);
258
259 if ((subencoding & rfbProto.HextileSubrectsColoured) != 0) {
260
261 for (int j = 0; j < nSubrects; j++) {
262 fg = rfb.is.read();
263 int b1 = rfb.is.read();
264 int b2 = rfb.is.read();
265 sx = b1 >> 4;
266 sy = b1 & 0xf;
267 sw = (b2 >> 4) + 1;
268 sh = (b2 & 0xf) + 1;
269
270 sg.setColor(colors[fg]);
271 sg.fillRect(sx, sy, sw, sh);
272 pig.setColor(colors[fg]);
273 pig.fillRect(sx, sy, sw, sh);
274 }
275
276 } else {
277
278 sg.setColor(colors[fg]);
279 pig.setColor(colors[fg]);
280
281 for (int j = 0; j < nSubrects; j++) {
282 int b1 = rfb.is.read();
283 int b2 = rfb.is.read();
284 sx = b1 >> 4;
285 sy = b1 & 0xf;
286 sw = (b2 >> 4) + 1;
287 sh = (b2 & 0xf) + 1;
288
289 sg.fillRect(sx, sy, sw, sh);
290 pig.fillRect(sx, sy, sw, sh);
291 }
292 }
293
294 sg.translate(-tx, -ty);
295 pig.translate(-tx, -ty);
296 }
297 }
298 break;
299 }
300
301 default:
302 throw new IOException("Unknown RFB rectangle encoding " +
303 rfb.updateRectEncoding);
304 }
305 }
306 rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth,
307 rfb.framebufferHeight, true);
308 break;
309
310 case rfbProto.SetColourMapEntries:
311 throw new IOException("Can't handle SetColourMapEntries message");
312
313 case rfbProto.Bell:
314 System.out.print((char)7);
315 break;
316
317 case rfbProto.ServerCutText:
318 String s = rfb.readServerCutText();
319 v.clipboard.setCutText(s);
320 break;
321
322 default:
323 throw new IOException("Unknown RFB message type " + msgType);
324 }
325 }
326 }
327
328
329 //
330 // Draw a raw rectangle.
331 //
332
333 void drawRawRect(int x, int y, int w, int h) throws IOException {
334 if (v.options.drawEachPixelForRawRects) {
335 for (int j = y; j < (y + h); j++) {
336 for (int k = x; k < (x + w); k++) {
337 int pixel = rfb.is.read();
338 sg.setColor(colors[pixel]);
339 sg.fillRect(k, j, 1, 1);
340 pig.setColor(colors[pixel]);
341 pig.fillRect(k, j, 1, 1);
342 }
343 }
344 return;
345 }
346
347 for (int j = y; j < (y + h); j++) {
348 rfb.is.readFully(pixels, j * rfb.framebufferWidth + x, w);
349 }
350
351 amis.newPixels(x, y, w, h);
352
353 try {
354 sg.setClip(x, y, w, h);
355 pig.setClip(x, y, w, h);
356 needToResetClip = true;
357 } catch (NoSuchMethodError e) {
358 sg2 = sg.create();
359 sg.clipRect(x, y, w, h);
360 pig2 = pig.create();
361 pig.clipRect(x, y, w, h);
362 }
363
364 sg.drawImage(rawPixelsImage, 0, 0, this);
365 pig.drawImage(rawPixelsImage, 0, 0, this);
366
367 if (sg2 != null) {
368 sg.dispose(); // reclaims resources more quickly
369 sg = sg2;
370 sg2 = null;
371 pig.dispose();
372 pig = pig2;
373 pig2 = null;
374 }
375 }
376
377
378 //
379 // Handle events.
380 //
381 // Because of a "feature" in the AWT implementation over X, the vncCanvas
382 // sometimes loses focus and the only way to get it back is to call
383 // requestFocus() explicitly. However we need to be careful when calling
384 // requestFocus() on Windows or other click-to-type systems. What we do is
385 // call requestFocus() whenever there is mouse movement over the window,
386 // AND the focus is already in the applet.
387 //
388
389 public boolean handleEvent(Event evt) {
390 if ((rfb != null) && rfb.inNormalProtocol) {
391 try {
392 switch (evt.id) {
393 case Event.MOUSE_MOVE:
394 case Event.MOUSE_DOWN:
395 case Event.MOUSE_DRAG:
396 case Event.MOUSE_UP:
397 if (v.gotFocus) {
398 requestFocus();
399 }
400 rfb.writePointerEvent(evt);
401 break;
402 case Event.KEY_PRESS:
403 case Event.KEY_RELEASE:
404 case Event.KEY_ACTION:
405 case Event.KEY_ACTION_RELEASE:
406 rfb.writeKeyEvent(evt);
407 break;
408 }
409 } catch (Exception e) {
410 e.printStackTrace();
411 }
412 return true;
413 }
414 return false;
415 }
416 }