]> source.dussan.org Git - tigervnc.git/commitdiff
Support for video area selection (revision range 2467:2563 from branches/javaviewer...
authorConstantin Kaplinsky <const@tightvnc.com>
Tue, 27 May 2008 08:38:28 +0000 (08:38 +0000)
committerConstantin Kaplinsky <const@tightvnc.com>
Tue, 27 May 2008 08:38:28 +0000 (08:38 +0000)
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@2564 3789f03b-4d11-0410-bbf8-ca57d06f2519

java/src/com/tightvnc/vncviewer/ButtonPanel.java
java/src/com/tightvnc/vncviewer/RfbProto.java
java/src/com/tightvnc/vncviewer/VncCanvas.java
java/src/com/tightvnc/vncviewer/VncViewer.java

index bd4cbca4162177bfe4a993f5580250c37fb1c181..422e6e2c53a05feb425572321d901ff30114ca26 100644 (file)
@@ -38,6 +38,10 @@ class ButtonPanel extends Panel implements ActionListener {
   Button clipboardButton;
   Button ctrlAltDelButton;
   Button refreshButton;
+  Button selectButton;
+
+  final String selectEnterLabel = "Select Video Area";
+  final String selectLeaveLabel = "Hide Selection";
 
   ButtonPanel(VncViewer v) {
     viewer = v;
@@ -69,6 +73,16 @@ class ButtonPanel extends Panel implements ActionListener {
     refreshButton.addActionListener(this);
   }
 
+  /**
+   * Add video selection button to the ButtonPanel.
+   */
+  public void addSelectButton() {
+    selectButton = new Button(selectEnterLabel);
+    selectButton.setEnabled(false);
+    add(selectButton);
+    selectButton.addActionListener(this);
+  }
+
   //
   // Enable buttons on successful connection.
   //
@@ -77,6 +91,9 @@ class ButtonPanel extends Panel implements ActionListener {
     disconnectButton.setEnabled(true);
     clipboardButton.setEnabled(true);
     refreshButton.setEnabled(true);
+    if (selectButton != null) {
+      selectButton.setEnabled(true);
+    }
   }
 
   //
@@ -94,8 +111,9 @@ class ButtonPanel extends Panel implements ActionListener {
     clipboardButton.setEnabled(false);
     ctrlAltDelButton.setEnabled(false);
     refreshButton.setEnabled(false);
-
-    validate();
+    if (selectButton != null) {
+      selectButton.setEnabled(false);
+    }
   }
 
   //
@@ -150,6 +168,17 @@ class ButtonPanel extends Panel implements ActionListener {
       } catch (IOException e) {
         e.printStackTrace();
       }
+    } else if (selectButton != null && evt.getSource() == selectButton) {
+      if (viewer.vc != null) {
+        boolean isSelecting = viewer.vc.isInSelectionMode();
+        if (!isSelecting) {
+          selectButton.setLabel(selectLeaveLabel);
+          viewer.vc.enableSelection(true);
+        } else {
+          selectButton.setLabel(selectEnterLabel);
+          viewer.vc.enableSelection(false);
+        }
+      }
     }
   }
 }
index 89a4caebdad79e02ed82130487b4bdcbe868a4d9..39656fc99e54332c4db2268a31545b9928eb8182 100644 (file)
@@ -98,10 +98,10 @@ class RfbProto {
     ClientCutText            = 6;
 
   // Non-standard client-to-server messages
-  final static int
-    EnableContinuousUpdates = 150;
-  final static String
-    SigEnableContinuousUpdates = "CUC_ENCU";
+  final static int EnableContinuousUpdates = 150;
+  final static int VideoRectangleSelection = 151;
+  final static String SigEnableContinuousUpdates = "CUC_ENCU";
+  final static String SigVideoRectangleSelection = "VRECTSEL";
 
   // Supported encodings and pseudo-encodings
   final static int
@@ -499,6 +499,9 @@ class RfbProto {
     clientMsgCaps.add(EnableContinuousUpdates, TightVncVendor,
                       SigEnableContinuousUpdates,
                       "Enable/disable continuous updates");
+    clientMsgCaps.add(VideoRectangleSelection, TightVncVendor,
+                      SigVideoRectangleSelection,
+                      "Select a rectangle to be treated as video");
 
     // Supported encoding types
     encodingCaps.add(EncodingCopyRect, StandardVendor,
@@ -1357,6 +1360,42 @@ class RfbProto {
     return continuousUpdatesActive;
   }
 
+  /**
+   * Send a rectangle selection to be treated as video by the server (but
+   * only if VideoRectangleSelection message is supported by the server).
+   * @param rect specifies coordinates and size of the rectangule.
+   * @throws java.io.IOException
+   */
+  void trySendVideoSelection(Rectangle rect) throws IOException
+  {
+    if (!clientMsgCaps.isEnabled(VideoRectangleSelection)) {
+      System.out.println("Video area selection is not supported by the server");
+      return;
+    }
+
+    int x = rect.x;
+    int y = rect.y;
+    int w = rect.width;
+    int h = rect.height;
+    byte[] b = new byte[10];
+
+    b[0] = (byte) VideoRectangleSelection;
+    b[1] = (byte) 0; // reserved
+    b[2] = (byte) ((x >> 8) & 0xff);
+    b[3] = (byte) (x & 0xff);
+    b[4] = (byte) ((y >> 8) & 0xff);
+    b[5] = (byte) (y & 0xff);
+    b[6] = (byte) ((w >> 8) & 0xff);
+    b[7] = (byte) (w & 0xff);
+    b[8] = (byte) ((h >> 8) & 0xff);
+    b[9] = (byte) (h & 0xff);
+
+    os.write(b);
+
+    System.out.println("Video rectangle selection message sent");
+  }
+
 
   //
   // Compress and write the data into the recorded session file. This
index 4f8122ea939c3c678ee0d6687816746be510c378..332efa38a4fce077f8268292ab2d4741a80e9b42 100644 (file)
@@ -121,13 +121,16 @@ class VncCanvas extends Canvas
 
     setPixelFormat();
 
+    resetSelection();
+
     inputEnabled = false;
     if (!viewer.options.viewOnly)
       enableInput(true);
 
-    // Keyboard listener is enabled even in view-only mode, to catch
-    // 'r' or 'R' key presses used to request screen update.
+    // Enable mouse and keyboard event listeners.
     addKeyListener(this);
+    addMouseListener(this);
+    addMouseMotionListener(this);
   }
 
   public VncCanvas(VncViewer v) throws IOException {
@@ -173,6 +176,17 @@ class VncCanvas extends Canvas
        g.drawImage(softCursor, x0, y0, null);
       }
     }
+    if (isInSelectionMode()) {
+      Rectangle r = getSelection(true);
+      if (r.width > 0 && r.height > 0) {
+        // Don't forget to correct the coordinates for the right and bottom
+        // borders, so that the borders are the part of the selection.
+        r.width -= 1;
+        r.height -= 1;
+        g.setXORMode(Color.yellow);
+        g.drawRect(r.x, r.y, r.width, r.height);
+      }
+    }
   }
 
   public void paintScaledFrameBuffer(Graphics g) {
@@ -213,16 +227,12 @@ class VncCanvas extends Canvas
   public synchronized void enableInput(boolean enable) {
     if (enable && !inputEnabled) {
       inputEnabled = true;
-      addMouseListener(this);
-      addMouseMotionListener(this);
       if (viewer.showControls) {
        viewer.buttonPanel.enableRemoteAccessControls(true);
       }
       createSoftCursor();      // scaled cursor
     } else if (!enable && inputEnabled) {
       inputEnabled = false;
-      removeMouseListener(this);
-      removeMouseMotionListener(this);
       if (viewer.showControls) {
        viewer.buttonPanel.enableRemoteAccessControls(false);
       }
@@ -1589,7 +1599,19 @@ class VncCanvas extends Canvas
     processLocalMouseEvent(evt, true);
   }
 
-  public void processLocalKeyEvent(KeyEvent evt) {
+  //
+  // Ignored events.
+  //
+
+  public void mouseClicked(MouseEvent evt) {}
+  public void mouseEntered(MouseEvent evt) {}
+  public void mouseExited(MouseEvent evt) {}
+
+  //
+  // Actual event processing.
+  //
+
+  private void processLocalKeyEvent(KeyEvent evt) {
     if (viewer.rfb != null && rfb.inNormalProtocol) {
       if (!inputEnabled) {
        if ((evt.getKeyChar() == 'r' || evt.getKeyChar() == 'R') &&
@@ -1619,34 +1641,36 @@ class VncCanvas extends Canvas
     evt.consume();
   }
 
-  public void processLocalMouseEvent(MouseEvent evt, boolean moved) {
+  private void processLocalMouseEvent(MouseEvent evt, boolean moved) {
     if (viewer.rfb != null && rfb.inNormalProtocol) {
-      if (moved) {
-       softCursorMove(evt.getX(), evt.getY());
-      }
-      if (rfb.framebufferWidth != scaledWidth) {
-        int sx = (evt.getX() * 100 + scalingFactor/2) / scalingFactor;
-        int sy = (evt.getY() * 100 + scalingFactor/2) / scalingFactor;
-        evt.translatePoint(sx - evt.getX(), sy - evt.getY());
-      }
-      synchronized(rfb) {
-       try {
-         rfb.writePointerEvent(evt);
-       } catch (Exception e) {
-         e.printStackTrace();
-       }
-       rfb.notify();
+      if (!inSelectionMode) {
+        if (inputEnabled) {
+          sendMouseEvent(evt, moved);
+        }
+      } else {
+        handleSelectionMouseEvent(evt);
       }
     }
   }
 
-  //
-  // Ignored events.
-  //
-
-  public void mouseClicked(MouseEvent evt) {}
-  public void mouseEntered(MouseEvent evt) {}
-  public void mouseExited(MouseEvent evt) {}
+  private void sendMouseEvent(MouseEvent evt, boolean moved) {
+    if (moved) {
+      softCursorMove(evt.getX(), evt.getY());
+    }
+    if (rfb.framebufferWidth != scaledWidth) {
+      int sx = (evt.getX() * 100 + scalingFactor/2) / scalingFactor;
+      int sy = (evt.getY() * 100 + scalingFactor/2) / scalingFactor;
+      evt.translatePoint(sx - evt.getX(), sy - evt.getY());
+    }
+    synchronized(rfb) {
+      try {
+        rfb.writePointerEvent(evt);
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+      rfb.notify();
+    }
+  }
 
   //
   // Reset update statistics.
@@ -1914,4 +1938,162 @@ class VncCanvas extends Canvas
              cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight);
     }
   }
+
+  //////////////////////////////////////////////////////////////////
+  //
+  // Support for selecting a rectangular video area.
+  //
+
+  /** This flag is false in normal operation, and true in the selection mode. */
+  private boolean inSelectionMode;
+
+  /** The point where the selection was started. */
+  private Point selectionStart;
+
+  /** The second point of the selection. */
+  private Point selectionEnd;
+
+  /**
+   * We change cursor when enabling the selection mode. In this variable, we
+   * save the original cursor so we can restore it on returning to the normal
+   * mode.
+   */
+  private Cursor savedCursor;
+
+  /**
+   * Initialize selection-related varibles.
+   */
+  private synchronized void resetSelection() {
+    inSelectionMode = false;
+    selectionStart = new Point(0, 0);
+    selectionEnd = new Point(0, 0);
+
+    savedCursor = getCursor();
+  }
+
+  /**
+   * Check current state of the selection mode.
+   * @return true in the selection mode, false otherwise.
+   */
+  public boolean isInSelectionMode() {
+    return inSelectionMode;
+  }
+
+  /**
+   * Get current selection.
+   * @param useScreenCoords use screen coordinates if true, or framebuffer
+   * coordinates if false. This makes difference when scaling factor is not 100.
+   * @return The selection as a {@link Rectangle}.
+   */
+  private synchronized Rectangle getSelection(boolean useScreenCoords) {
+    int x = selectionStart.x;
+    int y = selectionStart.y;
+    int w = selectionEnd.x - selectionStart.x;
+    int h = selectionEnd.y - selectionStart.y;
+    // Make x and y point to the upper left corner of the selection.
+    boolean horizSwap = false;
+    boolean vertSwap = false;
+    if (w < 0) {
+      w = -w;
+      x = x - w;
+      horizSwap = true;
+    }
+    if (h < 0) {
+      h = -h;
+      y = y - h;
+      vertSwap = true;
+    }
+    // Make sure the borders are included in the selection.
+    if (w > 0 && h > 0) {
+      w += 1;
+      h += 1;
+    }
+    // Translate from screen coordinates to framebuffer coordinates.
+    if (rfb.framebufferWidth != scaledWidth) {
+      x = (x * 100 + scalingFactor/2) / scalingFactor;
+      y = (y * 100 + scalingFactor/2) / scalingFactor;
+      w = (w * 100 + scalingFactor/2) / scalingFactor;
+      h = (h * 100 + scalingFactor/2) / scalingFactor;
+    }
+    // Make width a multiple of 16.
+    int widthCorrection = w % 16;
+    if (widthCorrection >= 8) {
+      widthCorrection -= 16;
+    }
+    w -= widthCorrection;
+    if (horizSwap) {
+      x += widthCorrection;
+    }
+    // Make height a multiple of 8.
+    int heightCorrection = h % 8;
+    if (heightCorrection >= 4) {
+      heightCorrection -= 8;
+    }
+    h -= heightCorrection;
+    if (vertSwap) {
+      y += heightCorrection;
+    }
+    // Translate the selection back to screen coordinates if requested.
+    int clipWidth = rfb.framebufferWidth;
+    int clipHeight = rfb.framebufferHeight;
+    if (useScreenCoords && rfb.framebufferWidth != scaledWidth) {
+      x = (x * scalingFactor + 50) / 100;
+      y = (y * scalingFactor + 50) / 100;
+      w = (w * scalingFactor + 50) / 100;
+      h = (h * scalingFactor + 50) / 100;
+      clipWidth = scaledWidth;
+      clipHeight = scaledHeight;
+    }
+    // Clip the selection to screen/framebuffer and return the result.
+    Rectangle selection = new Rectangle(x, y, w, h);
+    Rectangle clip = new Rectangle(0, 0, clipWidth, clipHeight);
+    return selection.intersection(clip);
+  }
+
+  /**
+   * Enable or disable the selection mode.
+   * @param enable enables the selection mode if true, disables if fasle.
+   */
+  public synchronized void enableSelection(boolean enable) {
+    if (enable && !inSelectionMode) {
+      // Enter the selection mode.
+      inSelectionMode = true;
+      savedCursor = getCursor();
+      setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
+      repaint();
+    } else if (!enable && inSelectionMode) {
+      // Leave the selection mode.
+      inSelectionMode = false;
+      setCursor(savedCursor);
+      repaint();
+    }
+  }
+
+  /**
+   * Process mouse events in the selection mode.
+   * 
+   * @param evt mouse event that was originally passed to
+   *   {@link MouseListener} or {@link MouseMotionListener}.
+   */
+  private synchronized void handleSelectionMouseEvent(MouseEvent evt) {
+    int id = evt.getID();
+    boolean button1 = (evt.getModifiers() & InputEvent.BUTTON1_MASK) != 0;
+
+    if (id == MouseEvent.MOUSE_PRESSED && button1) {
+      selectionStart = selectionEnd = evt.getPoint();
+      repaint();
+    }
+    if (id == MouseEvent.MOUSE_DRAGGED && button1) {
+      selectionEnd = evt.getPoint();
+      repaint();
+    }
+    if (id == MouseEvent.MOUSE_RELEASED && button1) {
+      try {
+        rfb.trySendVideoSelection(getSelection(false));
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
+    }
+  }
+
 }
index 6fa77f856ee027cd6553330849a13849afab2a73..c6fa157968a04beeab763f3042a6fb1387edd202 100644 (file)
@@ -160,6 +160,11 @@ public class VncViewer extends java.applet.Applet
       connectAndAuthenticate();
       doProtocolInitialisation();
 
+      if (showControls &&
+          rfb.clientMsgCaps.isEnabled(RfbProto.VideoRectangleSelection)) {
+        buttonPanel.addSelectButton();
+      }
+
       // FIXME: Use auto-scaling not only in a separate frame.
       if (options.autoScale && inSeparateFrame) {
        Dimension screenSize;
@@ -206,8 +211,9 @@ public class VncViewer extends java.applet.Applet
 
       }
 
-      if (showControls)
-       buttonPanel.enableButtons();
+      if (showControls) {
+        buttonPanel.enableButtons();
+      }
 
       moveFocusToDesktop();
       processNormalProtocol();