adds keepAlive feature requested in #3482254 to java viewer

git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4857 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/java/com/tigervnc/vncviewer/CConn.java b/java/com/tigervnc/vncviewer/CConn.java
index b20b82e..965bcb2 100644
--- a/java/com/tigervnc/vncviewer/CConn.java
+++ b/java/com/tigervnc/vncviewer/CConn.java
@@ -49,7 +49,8 @@
 import java.net.InetSocketAddress;
 import java.net.URL;
 import java.net.SocketException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Iterator;
 
 import com.tigervnc.rdr.*;
 import com.tigervnc.rfb.*;
@@ -227,6 +228,18 @@
     setServerName(serverHost);
     setStreams(sock.inStream(), sock.outStream());
     initialiseProtocol();
+    int delay = viewer.keepAliveTimeout.getValue();
+    if (delay > 0) {
+      ActionListener keepAliveListener = new ActionListener() {
+        public void actionPerformed(ActionEvent evt) {
+          synchronized (writer()) {
+            writer().writeFramebufferUpdateRequest(new Rect(0,0,0,0), true);
+          }
+        }
+      };
+      keepAliveTimer = new Timer(1000*delay, keepAliveListener);
+      keepAliveTimer.start();
+    }
   }
 
   public void refreshFramebuffer()
@@ -249,6 +262,8 @@
   // deleteWindow() is called when the user closes the desktop or menu windows.
 
   void deleteWindow() {
+    if (keepAliveTimer != null)
+      keepAliveTimer.stop();
     if (viewport != null)
       viewport.dispose();
     viewport = null;
@@ -465,6 +480,8 @@
       }
 
       firstUpdate = false;
+      if (keepAliveTimer != null)
+        keepAliveTimer.restart();
     }
 
     // A format change has been scheduled and we are now past the update
@@ -779,6 +796,8 @@
     }
 
     forceNonincremental = false;
+    if (keepAliveTimer != null)
+      keepAliveTimer.restart();
   }
 
 
@@ -1217,6 +1236,8 @@
   synchronized public void writeClientCutText(String str, int len) {
     if (state() != RFBSTATE_NORMAL) return;
     writer().writeClientCutText(str,len);
+    if (keepAliveTimer != null)
+      keepAliveTimer.restart();
   }
 
   synchronized public void writeKeyEvent(int keysym, boolean down) {
@@ -1388,6 +1409,8 @@
     if ((m & Event.META_MASK) != (pressedModifiers & Event.META_MASK))
       writeKeyEvent(Keysyms.Meta_L, (m & Event.META_MASK) != 0);
     pressedModifiers = m;
+    if (keepAliveTimer != null)
+      keepAliveTimer.restart();
   }
 
 
@@ -1421,6 +1444,7 @@
   ImageIcon logo = new ImageIcon(cl.getResource("com/tigervnc/vncviewer/tigervnc.png"));
   public static UserPasswdGetter upg;
   public UserMsgBox msg;
+  Timer keepAliveTimer;
 
   // shuttingDown is set by the GUI thread and only ever tested by the RFB
   // thread after the window has been destroyed.
diff --git a/java/com/tigervnc/vncviewer/VncViewer.java b/java/com/tigervnc/vncviewer/VncViewer.java
index 91504d2..6a06284 100644
--- a/java/com/tigervnc/vncviewer/VncViewer.java
+++ b/java/com/tigervnc/vncviewer/VncViewer.java
@@ -331,6 +331,14 @@
 			                    "Use specified compression level "+
 			                    "0 = Low, 6 = High",
 			                    1);
+  IntParameter keepAliveTimeout
+  = new IntParameter("KeepAliveTimeout",
+			               "Send dummy framebuffer update requests if no "+
+                     "network activity has occurred for more than "+
+                     "the specified number of seconds. This is to "+
+                     "prevent VPNs or other applications from "+
+                     "disconnecting due to inactivity. A value of "+
+                     "0 disables this feature.", 0);
   BoolParameter noJpeg
   = new BoolParameter("NoJPEG",
                           "Disable lossy JPEG compression in Tight encoding.", false);