summaryrefslogtreecommitdiffstats
path: root/java/com/tigervnc/vncviewer/Viewport.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/tigervnc/vncviewer/Viewport.java')
-rw-r--r--java/com/tigervnc/vncviewer/Viewport.java558
1 files changed, 401 insertions, 157 deletions
diff --git a/java/com/tigervnc/vncviewer/Viewport.java b/java/com/tigervnc/vncviewer/Viewport.java
index 3a5fb54c..bf07d2d5 100644
--- a/java/com/tigervnc/vncviewer/Viewport.java
+++ b/java/com/tigervnc/vncviewer/Viewport.java
@@ -1,6 +1,8 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright (C) 2011-2015 Brian P. Hinz
- * Copyright (C) 2012-2013 D. R. Commander. All Rights Reserved.
+ * Copyright (C) 2006 Constantin Kaplinsky. All Rights Reserved.
+ * Copyright (C) 2009 Paul Donohue. All Rights Reserved.
+ * Copyright (C) 2010, 2012-2013 D. R. Commander. All Rights Reserved.
+ * Copyright (C) 2011-2014 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,201 +20,443 @@
* USA.
*/
-package com.tigervnc.vncviewer;
+//
+// DesktopWindow is an AWT Canvas representing a VNC desktop.
+//
+// Methods on DesktopWindow are called from both the GUI thread and the thread
+// which processes incoming RFB messages ("the RFB thread"). This means we
+// need to be careful with synchronization here.
+//
+package com.tigervnc.vncviewer;
+import java.awt.*;
import java.awt.Color;
+import java.awt.color.ColorSpace;
import java.awt.event.*;
-import java.awt.Dimension;
-import java.awt.Event;
-import java.awt.GraphicsConfiguration;
-import java.awt.GraphicsDevice;
-import java.awt.GraphicsEnvironment;
-import java.awt.Image;
-import java.awt.Insets;
-import java.awt.Window;
-import java.lang.reflect.*;
+import java.awt.geom.AffineTransform;
+import java.awt.image.*;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.Clipboard;
+import java.io.BufferedReader;
+import java.nio.*;
import javax.swing.*;
-import com.tigervnc.rfb.*;
-import java.lang.Exception;
-import java.awt.Rectangle;
+import javax.imageio.*;
+import java.io.*;
-import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER;
-import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER;
-import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED;
-import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED;
+import com.tigervnc.rfb.*;
+import com.tigervnc.rfb.Cursor;
+import com.tigervnc.rfb.Point;
import static com.tigervnc.vncviewer.Parameters.*;
-public class Viewport extends JFrame
-{
- public Viewport(String name, CConn cc_) {
+class Viewport extends JPanel implements MouseListener,
+ MouseMotionListener, MouseWheelListener, KeyListener {
+
+ static LogWriter vlog = new LogWriter("Viewport");
+
+ public Viewport(int w, int h, PixelFormat serverPF, CConn cc_)
+ {
cc = cc_;
- setTitle(name+" - TigerVNC");
- setFocusable(false);
- setFocusTraversalKeysEnabled(false);
- if (!VncViewer.os.startsWith("mac os x"))
- setIconImage(VncViewer.frameIcon);
- UIManager.getDefaults().put("ScrollPane.ancestorInputMap",
- new UIDefaults.LazyInputMap(new Object[]{}));
- sp = new JScrollPane();
- sp.getViewport().setBackground(Color.BLACK);
- sp.setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
- getContentPane().add(sp);
- if (VncViewer.os.startsWith("mac os x")) {
- if (!noLionFS.getValue())
- enableLionFS();
- }
- addWindowFocusListener(new WindowAdapter() {
- public void windowGainedFocus(WindowEvent e) {
- if (isVisible())
- sp.getViewport().getView().requestFocusInWindow();
+ setScaledSize(cc.cp.width, cc.cp.height);
+ frameBuffer = createFramebuffer(serverPF, w, h);
+ assert(frameBuffer != null);
+ setBackground(Color.BLACK);
+
+ cc.setFramebuffer(frameBuffer);
+ OptionsDialog.addCallback("handleOptions", this);
+
+ addMouseListener(this);
+ addMouseWheelListener(this);
+ addMouseMotionListener(this);
+ addKeyListener(this);
+ addFocusListener(new FocusAdapter() {
+ public void focusGained(FocusEvent e) {
+ ClipboardDialog.clientCutText();
}
- public void windowLostFocus(WindowEvent e) {
+ public void focusLost(FocusEvent e) {
cc.releaseDownKeys();
}
});
- addWindowListener(new WindowAdapter() {
- public void windowClosing(WindowEvent e) {
- cc.close();
+ setFocusTraversalKeysEnabled(false);
+ setFocusable(true);
+
+ // Send a fake pointer event so that the server will stop rendering
+ // a server-side cursor. Ideally we'd like to send the actual pointer
+ // position, but we can't really tell when the window manager is done
+ // placing us so we don't have a good time for that.
+ cc.writer().writePointerEvent(new Point(w/2, h/2), 0);
+ }
+
+ // Most efficient format (from Viewport's point of view)
+ public PixelFormat getPreferredPF()
+ {
+ return frameBuffer.getPF();
+ }
+
+ // Copy the areas of the framebuffer that have been changed (damaged)
+ // to the displayed window.
+ public void updateWindow() {
+ Rect r = frameBuffer.getDamage();
+ if (!r.is_empty()) {
+ if (image == null)
+ image = (BufferedImage)createImage(frameBuffer.width(), frameBuffer.height());
+ image.getRaster().setDataElements(r.tl.x, r.tl.y, frameBuffer.getBuffer(r));
+ if (cc.cp.width != scaledWidth ||
+ cc.cp.height != scaledHeight) {
+ AffineTransform t = new AffineTransform();
+ t.scale((double)scaleRatioX, (double)scaleRatioY);
+ Rectangle s = new Rectangle(r.tl.x, r.tl.y, r.width(), r.height());
+ s = t.createTransformedShape(s).getBounds();
+ paintImmediately(s.x, s.y, s.width, s.height);
+ } else {
+ paintImmediately(r.tl.x, r.tl.y, r.width(), r.height());
}
- });
- addComponentListener(new ComponentAdapter() {
- public void componentResized(ComponentEvent e) {
- String scaleString = scalingFactor.getValue();
- if (scaleString.equalsIgnoreCase("Auto") ||
- scaleString.equalsIgnoreCase("FixedRatio")) {
- if ((sp.getSize().width != cc.desktop.scaledWidth) ||
- (sp.getSize().height != cc.desktop.scaledHeight)) {
- cc.desktop.setScaledSize();
- sp.setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_NEVER);
- sp.setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_NEVER);
- sp.validate();
- if (getExtendedState() != JFrame.MAXIMIZED_BOTH &&
- !fullScreen.getValue()) {
- sp.setSize(new Dimension(cc.desktop.scaledWidth,
- cc.desktop.scaledHeight));
- int w = cc.desktop.scaledWidth + getInsets().left +
- getInsets().right;
- int h = cc.desktop.scaledHeight + getInsets().top +
- getInsets().bottom;
- if (scaleString.equalsIgnoreCase("FixedRatio"))
- setSize(w, h);
- }
+ }
+ }
+
+ static final int[] dotcursor_xpm = {
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0x00000000,
+ 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0x00000000,
+ 0x00000000, 0xff000000, 0xff000000, 0xff000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ };
+
+ public void setCursor(int width, int height, Point hotspot,
+ byte[] data, byte[] mask)
+ {
+
+ int mask_len = ((width+7)/8) * height;
+ int i;
+
+ for (i = 0; i < mask_len; i++)
+ if ((mask[i] & 0xff) != 0) break;
+
+ if ((i == mask_len) && dotWhenNoCursor.getValue()) {
+ vlog.debug("cursor is empty - using dot");
+ cursor = new BufferedImage(5, 5, BufferedImage.TYPE_INT_ARGB_PRE);
+ cursor.setRGB(0, 0, 5, 5, dotcursor_xpm, 0, 5);
+ cursorHotspot.x = cursorHotspot.y = 3;
+ } else {
+ if ((width == 0) || (height == 0)) {
+ cursor = new BufferedImage(tk.getBestCursorSize(0, 0).width,
+ tk.getBestCursorSize(0, 0).height,
+ BufferedImage.TYPE_INT_ARGB_PRE);
+ cursorHotspot.x = cursorHotspot.y = 0;
+ } else {
+ ByteBuffer buffer = ByteBuffer.allocate(width*height*4);
+ ByteBuffer in, o, m;
+ int m_width;
+
+ PixelFormat pf;
+
+ pf = cc.cp.pf();
+
+ in = (ByteBuffer)ByteBuffer.wrap(data).mark();
+ o = (ByteBuffer)buffer.duplicate().mark();
+ m = ByteBuffer.wrap(mask);
+ m_width = (width+7)/8;
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ // NOTE: BufferedImage needs ARGB, rather than RGBA
+ if ((m.get((m_width*y)+(x/8)) & 0x80>>(x%8)) != 0)
+ o.put((byte)255);
+ else
+ o.put((byte)0);
+
+ pf.rgbFromBuffer(o, in.duplicate(), 1);
+
+ o.position(o.reset().position() + 4).mark();
+ in.position(in.position() + pf.bpp/8);
}
- } else {
- sp.setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_AS_NEEDED);
- sp.setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_AS_NEEDED);
- sp.validate();
- }
- if (cc.desktop.cursor != null) {
- Cursor cursor = cc.desktop.cursor;
- cc.setCursor(cursor.width(),cursor.height(),cursor.hotspot,
- cursor.data, cursor.mask);
}
+
+ IntBuffer rgb =
+ IntBuffer.allocate(width*height).put(buffer.asIntBuffer());
+ cursor = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB_PRE);
+ cursor.setRGB(0, 0, width, height, rgb.array(), 0, width);
+
+ cursorHotspot = hotspot;
+
}
- });
+ }
+
+ int cw = (int)Math.floor((float)cursor.getWidth() * scaleRatioX);
+ int ch = (int)Math.floor((float)cursor.getHeight() * scaleRatioY);
+
+ int x = (int)Math.floor((float)cursorHotspot.x * scaleRatioX);
+ int y = (int)Math.floor((float)cursorHotspot.y * scaleRatioY);
+
+ java.awt.Cursor softCursor;
+
+ Dimension cs = tk.getBestCursorSize(cw, ch);
+ if (cs.width != cw && cs.height != ch) {
+ cw = Math.min(cw, cs.width);
+ ch = Math.min(ch, cs.height);
+ x = (int)Math.min(x, Math.max(cs.width - 1, 0));
+ y = (int)Math.min(y, Math.max(cs.height - 1, 0));
+ BufferedImage scaledImage =
+ new BufferedImage(cs.width, cs.height, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g2 = scaledImage.createGraphics();
+ g2.setRenderingHint(RenderingHints.KEY_RENDERING,
+ RenderingHints.VALUE_RENDER_QUALITY);
+ g2.drawImage(cursor,
+ 0, 0, cw, ch,
+ 0, 0, cursor.getWidth(), cursor.getHeight(), null);
+ g2.dispose();
+ java.awt.Point hs = new java.awt.Point(x, y);
+ softCursor = tk.createCustomCursor(scaledImage, hs, "softCursor");
+ scaledImage.flush();
+ } else {
+ java.awt.Point hs = new java.awt.Point(x, y);
+ softCursor = tk.createCustomCursor(cursor, hs, "softCursor");
+ }
+
+ cursor.flush();
+
+ setCursor(softCursor);
+
}
- public void setName(String name) {
- setTitle(name + "- TigerVNC");
+ public void resize(int x, int y, int w, int h) {
+ if ((w != frameBuffer.width()) || (h != frameBuffer.height())) {
+ vlog.debug("Resizing framebuffer from "+frameBuffer.width()+"x"+
+ frameBuffer.height()+" to "+w+"x"+h);
+ frameBuffer = createFramebuffer(frameBuffer.getPF(), w, h);
+ assert(frameBuffer != null);
+ cc.setFramebuffer(frameBuffer);
+ image = null;
+ }
+ setScaledSize(w, h);
}
- boolean lionFSSupported() { return canDoLionFS; }
+ private PlatformPixelBuffer createFramebuffer(PixelFormat pf, int w, int h)
+ {
+ PlatformPixelBuffer fb;
- void enableLionFS() {
- try {
- String version = System.getProperty("os.version");
- int firstDot = version.indexOf('.');
- int lastDot = version.lastIndexOf('.');
- if (lastDot > firstDot && lastDot >= 0) {
- version = version.substring(0, version.indexOf('.', firstDot + 1));
- }
- double v = Double.parseDouble(version);
- if (v < 10.7)
- throw new Exception("Operating system version is " + v);
-
- Class fsuClass = Class.forName("com.apple.eawt.FullScreenUtilities");
- Class argClasses[] = new Class[]{Window.class, Boolean.TYPE};
- Method setWindowCanFullScreen =
- fsuClass.getMethod("setWindowCanFullScreen", argClasses);
- setWindowCanFullScreen.invoke(fsuClass, this, true);
-
- canDoLionFS = true;
- } catch (Exception e) {
- vlog.debug("Could not enable OS X 10.7+ full-screen mode: " +
- e.getMessage());
- }
+ fb = new JavaPixelBuffer(w, h);
+
+ return fb;
+ }
+
+ //
+ // Callback methods to determine geometry of our Component.
+ //
+
+ public Dimension getPreferredSize() {
+ return new Dimension(scaledWidth, scaledHeight);
}
- public void toggleLionFS() {
- try {
- Class appClass = Class.forName("com.apple.eawt.Application");
- Method getApplication = appClass.getMethod("getApplication",
- (Class[])null);
- Object app = getApplication.invoke(appClass);
- Method requestToggleFullScreen =
- appClass.getMethod("requestToggleFullScreen", Window.class);
- requestToggleFullScreen.invoke(app, this);
- } catch (Exception e) {
- vlog.debug("Could not toggle OS X 10.7+ full-screen mode: " +
- e.getMessage());
+ public Dimension getMinimumSize() {
+ return new Dimension(scaledWidth, scaledHeight);
+ }
+
+ public Dimension getMaximumSize() {
+ return new Dimension(scaledWidth, scaledHeight);
+ }
+
+ public void paintComponent(Graphics g) {
+ Graphics2D g2 = (Graphics2D)g;
+ if (cc.cp.width != scaledWidth ||
+ cc.cp.height != scaledHeight) {
+ g2.setRenderingHint(RenderingHints.KEY_RENDERING,
+ RenderingHints.VALUE_RENDER_QUALITY);
+ g2.drawImage(image, 0, 0, scaledWidth, scaledHeight, null);
+ } else {
+ g2.drawImage(image, 0, 0, null);
}
+ g2.dispose();
}
- public JViewport getViewport() {
- return sp.getViewport();
+ // Mouse-Motion callback function
+ private void mouseMotionCB(MouseEvent e) {
+ if (!viewOnly.getValue() &&
+ e.getX() >= 0 && e.getX() <= scaledWidth &&
+ e.getY() >= 0 && e.getY() <= scaledHeight)
+ cc.writePointerEvent(translateMouseEvent(e));
}
+ public void mouseDragged(MouseEvent e) { mouseMotionCB(e); }
+ public void mouseMoved(MouseEvent e) { mouseMotionCB(e); }
- public void setGeometry(int x, int y, int w, int h) {
- pack();
- if (!fullScreen.getValue())
- setLocation(x, y);
+ // Mouse callback function
+ private void mouseCB(MouseEvent e) {
+ if (!viewOnly.getValue())
+ if ((e.getID() == MouseEvent.MOUSE_RELEASED) ||
+ (e.getX() >= 0 && e.getX() <= scaledWidth &&
+ e.getY() >= 0 && e.getY() <= scaledHeight))
+ cc.writePointerEvent(translateMouseEvent(e));
+ }
+ public void mouseReleased(MouseEvent e) { mouseCB(e); }
+ public void mousePressed(MouseEvent e) { mouseCB(e); }
+ public void mouseClicked(MouseEvent e) {}
+ public void mouseEntered(MouseEvent e) {
+ if (embed.getValue())
+ requestFocus();
}
+ public void mouseExited(MouseEvent e) {}
- public Dimension getScreenSize() {
- return getScreenBounds().getSize();
+ // MouseWheel callback function
+ private void mouseWheelCB(MouseWheelEvent e) {
+ if (!viewOnly.getValue())
+ cc.writeWheelEvent(e);
}
- public Rectangle getScreenBounds() {
- GraphicsEnvironment ge =
- GraphicsEnvironment.getLocalGraphicsEnvironment();
- Rectangle r = new Rectangle();
- setMaximizedBounds(null);
- if (fullScreenAllMonitors.getValue()) {
- for (GraphicsDevice gd : ge.getScreenDevices())
- for (GraphicsConfiguration gc : gd.getConfigurations())
- r = r.union(gc.getBounds());
- Rectangle mb = new Rectangle(r);
- mb.grow(getInsets().left, getInsets().bottom);
- setMaximizedBounds(mb);
+ public void mouseWheelMoved(MouseWheelEvent e) {
+ mouseWheelCB(e);
+ }
+
+ private static final Integer keyEventLock = 0;
+
+ // Handle the key-typed event.
+ public void keyTyped(KeyEvent e) { }
+
+ // Handle the key-released event.
+ public void keyReleased(KeyEvent e) {
+ synchronized(keyEventLock) {
+ cc.writeKeyEvent(e);
+ }
+ }
+
+ // Handle the key-pressed event.
+ public void keyPressed(KeyEvent e)
+ {
+ if (e.getKeyCode() == MenuKey.getMenuKeyCode()) {
+ java.awt.Point pt = e.getComponent().getMousePosition();
+ if (pt != null) {
+ F8Menu menu = new F8Menu(cc);
+ menu.show(e.getComponent(), (int)pt.getX(), (int)pt.getY());
+ }
+ return;
+ }
+ int ctrlAltShiftMask = Event.SHIFT_MASK | Event.CTRL_MASK | Event.ALT_MASK;
+ if ((e.getModifiers() & ctrlAltShiftMask) == ctrlAltShiftMask) {
+ switch (e.getKeyCode()) {
+ case KeyEvent.VK_A:
+ VncViewer.showAbout(this);
+ return;
+ case KeyEvent.VK_F:
+ if (cc.desktop.fullscreen_active())
+ cc.desktop.fullscreen_on();
+ else
+ cc.desktop.fullscreen_off();
+ return;
+ case KeyEvent.VK_H:
+ cc.refresh();
+ return;
+ case KeyEvent.VK_I:
+ cc.showInfo();
+ return;
+ case KeyEvent.VK_O:
+ OptionsDialog.showDialog(this);
+ return;
+ case KeyEvent.VK_W:
+ VncViewer.newViewer();
+ return;
+ case KeyEvent.VK_LEFT:
+ case KeyEvent.VK_RIGHT:
+ case KeyEvent.VK_UP:
+ case KeyEvent.VK_DOWN:
+ return;
+ }
+ }
+ if ((e.getModifiers() & Event.META_MASK) == Event.META_MASK) {
+ switch (e.getKeyCode()) {
+ case KeyEvent.VK_COMMA:
+ case KeyEvent.VK_N:
+ case KeyEvent.VK_W:
+ case KeyEvent.VK_I:
+ case KeyEvent.VK_R:
+ case KeyEvent.VK_L:
+ case KeyEvent.VK_F:
+ case KeyEvent.VK_Z:
+ case KeyEvent.VK_T:
+ return;
+ }
+ }
+ synchronized(keyEventLock) {
+ cc.writeKeyEvent(e);
+ }
+ }
+
+ public void setScaledSize(int width, int height)
+ {
+ assert(width != 0 && height != 0);
+ String scaleString = scalingFactor.getValue();
+ if (remoteResize.getValue()) {
+ scaledWidth = width;
+ scaledHeight = height;
+ scaleRatioX = 1.00f;
+ scaleRatioY = 1.00f;
} else {
- GraphicsDevice gd = ge.getDefaultScreenDevice();
- GraphicsConfiguration gc = gd.getDefaultConfiguration();
- r = gc.getBounds();
+ if (scaleString.matches("^[0-9]+$")) {
+ int scalingFactor = Integer.parseInt(scaleString);
+ scaledWidth =
+ (int)Math.floor((float)width * (float)scalingFactor/100.0);
+ scaledHeight =
+ (int)Math.floor((float)height * (float)scalingFactor/100.0);
+ } else if (scaleString.equalsIgnoreCase("Auto")) {
+ scaledWidth = width;
+ scaledHeight = height;
+ } else {
+ float widthRatio = (float)width / (float)cc.cp.width;
+ float heightRatio = (float)height / (float)cc.cp.height;
+ float ratio = Math.min(widthRatio, heightRatio);
+ scaledWidth = (int)Math.floor(cc.cp.width * ratio);
+ scaledHeight = (int)Math.floor(cc.cp.height * ratio);
+ }
+ scaleRatioX = (float)scaledWidth / (float)cc.cp.width;
+ scaleRatioY = (float)scaledHeight / (float)cc.cp.height;
}
- return r;
+ if (scaledWidth != getWidth() || scaledHeight != getHeight())
+ setSize(new Dimension(scaledWidth, scaledHeight));
}
- public static Window getFullScreenWindow() {
- GraphicsEnvironment ge =
- GraphicsEnvironment.getLocalGraphicsEnvironment();
- GraphicsDevice gd = ge.getDefaultScreenDevice();
- Window fullScreenWindow = gd.getFullScreenWindow();
- return fullScreenWindow;
+ private MouseEvent translateMouseEvent(MouseEvent e)
+ {
+ if (cc.cp.width != scaledWidth ||
+ cc.cp.height != scaledHeight) {
+ int sx = (scaleRatioX == 1.00) ?
+ e.getX() : (int)Math.floor(e.getX() / scaleRatioX);
+ int sy = (scaleRatioY == 1.00) ?
+ e.getY() : (int)Math.floor(e.getY() / scaleRatioY);
+ e.translatePoint(sx - e.getX(), sy - e.getY());
+ }
+ return e;
}
- public static void setFullScreenWindow(Window fullScreenWindow) {
- GraphicsEnvironment ge =
- GraphicsEnvironment.getLocalGraphicsEnvironment();
- GraphicsDevice gd = ge.getDefaultScreenDevice();
- if (gd.isFullScreenSupported())
- gd.setFullScreenWindow(fullScreenWindow);
+ public void handleOptions()
+ {
+ /*
+ setScaledSize(cc.cp.width, cc.cp.height);
+ if (!oldSize.equals(new Dimension(scaledWidth, scaledHeight))) {
+ // Re-layout the DesktopWindow when the scaled size changes.
+ // Ideally we'd do this with a ComponentListener, but unfortunately
+ // sometimes a spurious resize event is triggered on the viewport
+ // when the DesktopWindow is manually resized via the drag handles.
+ if (cc.desktop != null && cc.desktop.isVisible()) {
+ JScrollPane scroll = (JScrollPane)((JViewport)getParent()).getParent();
+ scroll.setViewportBorder(BorderFactory.createEmptyBorder(0,0,0,0));
+ cc.desktop.pack();
+ }
+ */
}
- CConn cc;
- JScrollPane sp;
- boolean canDoLionFS;
- static LogWriter vlog = new LogWriter("Viewport");
-}
+ // access to cc by different threads is specified in CConn
+ private CConn cc;
+ private BufferedImage image;
+ // access to the following must be synchronized:
+ public PlatformPixelBuffer frameBuffer;
+
+ static Toolkit tk = Toolkit.getDefaultToolkit();
+
+ public int scaledWidth = 0, scaledHeight = 0;
+ float scaleRatioX, scaleRatioY;
+
+ BufferedImage cursor;
+ Point cursorHotspot = new Point();
+
+}