source: trunk/packages/sipb-xen-vnc-client/code/VncViewer.java @ 679

Last change on this file since 679 was 66, checked in by quentin, 17 years ago

Initial checkin of modified Java VNC viewer for use as remote console

File size: 25.6 KB
Line 
1//
2//  Copyright (C) 2001-2004 HorizonLive.com, Inc.  All Rights Reserved.
3//  Copyright (C) 2002 Constantin Kaplinsky.  All Rights Reserved.
4//  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
5//
6//  This is free software; you can redistribute it and/or modify
7//  it under the terms of the GNU General Public License as published by
8//  the Free Software Foundation; either version 2 of the License, or
9//  (at your option) any later version.
10//
11//  This software 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 General Public License for more details.
15//
16//  You should have received a copy of the GNU General Public License
17//  along with this software; if not, write to the Free Software
18//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
19//  USA.
20//
21
22//
23// VncViewer.java - the VNC viewer applet.  This class mainly just sets up the
24// user interface, leaving it to the VncCanvas to do the actual rendering of
25// a VNC desktop.
26//
27
28import java.awt.*;
29import java.awt.event.*;
30import java.io.*;
31import java.net.*;
32
33public class VncViewer extends java.applet.Applet
34  implements java.lang.Runnable, WindowListener {
35
36  boolean inAnApplet = true;
37  boolean inSeparateFrame = false;
38
39  //
40  // main() is called when run as a java program from the command line.
41  // It simply runs the applet inside a newly-created frame.
42  //
43
44  public static void main(String[] argv) {
45    VncViewer v = new VncViewer();
46    v.mainArgs = argv;
47    v.inAnApplet = false;
48    v.inSeparateFrame = true;
49
50    v.init();
51    v.start();
52  }
53
54  String[] mainArgs;
55
56  RfbProto rfb;
57  Thread rfbThread;
58
59  Frame vncFrame;
60  Container vncContainer;
61  ScrollPane desktopScrollPane;
62  GridBagLayout gridbag;
63  ButtonPanel buttonPanel;
64  Label connStatusLabel;
65  VncCanvas vc;
66  OptionsFrame options;
67  ClipboardFrame clipboard;
68  RecordingFrame rec;
69
70  // Control session recording.
71  Object recordingSync;
72  String sessionFileName;
73  boolean recordingActive;
74  boolean recordingStatusChanged;
75  String cursorUpdatesDef;
76  String eightBitColorsDef;
77
78  // Variables read from parameter values.
79  String socketFactory;
80  String host;
81  int port;
82  String passwordParam;
83  boolean showControls;
84  boolean offerRelogin;
85  boolean showOfflineDesktop;
86  int deferScreenUpdates;
87  int deferCursorUpdates;
88  int deferUpdateRequests;
89
90  // Reference to this applet for inter-applet communication.
91  public static java.applet.Applet refApplet;
92
93  //
94  // init()
95  //
96
97  public void init() {
98
99    readParameters();
100
101    refApplet = this;
102
103    if (inSeparateFrame) {
104      vncFrame = new Frame("TightVNC");
105      if (!inAnApplet) {
106        vncFrame.add("Center", this);
107      }
108      vncContainer = vncFrame;
109    } else {
110      vncContainer = this;
111    }
112
113    recordingSync = new Object();
114
115    options = new OptionsFrame(this);
116    clipboard = new ClipboardFrame(this);
117    if (RecordingFrame.checkSecurity())
118      rec = new RecordingFrame(this);
119
120    sessionFileName = null;
121    recordingActive = false;
122    recordingStatusChanged = false;
123    cursorUpdatesDef = null;
124    eightBitColorsDef = null;
125
126    if (inSeparateFrame)
127      vncFrame.addWindowListener(this);
128
129    rfbThread = new Thread(this);
130    rfbThread.start();
131  }
132
133  public void update(Graphics g) {
134  }
135
136  //
137  // run() - executed by the rfbThread to deal with the RFB socket.
138  //
139
140  public void run() {
141
142    gridbag = new GridBagLayout();
143    vncContainer.setLayout(gridbag);
144
145    GridBagConstraints gbc = new GridBagConstraints();
146    gbc.gridwidth = GridBagConstraints.REMAINDER;
147    gbc.anchor = GridBagConstraints.NORTHWEST;
148
149    if (showControls) {
150      buttonPanel = new ButtonPanel(this);
151      gridbag.setConstraints(buttonPanel, gbc);
152      vncContainer.add(buttonPanel);
153    }
154
155    try {
156      connectAndAuthenticate();
157      doProtocolInitialisation();
158
159      // FIXME: Use auto-scaling not only in a separate frame.
160      if (options.autoScale && inSeparateFrame) {
161        Dimension screenSize;
162        try {
163          screenSize = vncContainer.getToolkit().getScreenSize();
164        } catch (Exception e) {
165          screenSize = new Dimension(0, 0);
166        }
167        createCanvas(screenSize.width - 32, screenSize.height - 32);
168      } else {
169        createCanvas(0, 0);
170      }
171
172      gbc.weightx = 1.0;
173      gbc.weighty = 1.0;
174
175      if (inSeparateFrame) {
176
177        // Create a panel which itself is resizeable and can hold
178        // non-resizeable VncCanvas component at the top left corner.
179        Panel canvasPanel = new Panel();
180        canvasPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
181        canvasPanel.add(vc);
182
183        // Create a ScrollPane which will hold a panel with VncCanvas
184        // inside.
185        desktopScrollPane = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED);
186        gbc.fill = GridBagConstraints.BOTH;
187        gridbag.setConstraints(desktopScrollPane, gbc);
188        desktopScrollPane.add(canvasPanel);
189
190        // Finally, add our ScrollPane to the Frame window.
191        vncFrame.add(desktopScrollPane);
192        vncFrame.setTitle(rfb.desktopName);
193        vncFrame.pack();
194        vc.resizeDesktopFrame();
195
196      } else {
197
198        // Just add the VncCanvas component to the Applet.
199        gridbag.setConstraints(vc, gbc);
200        add(vc);
201        validate();
202
203      }
204
205      if (showControls)
206        buttonPanel.enableButtons();
207
208      moveFocusToDesktop();
209      processNormalProtocol();
210
211    } catch (NoRouteToHostException e) {
212      fatalError("Network error: no route to server: " + host, e);
213    } catch (UnknownHostException e) {
214      fatalError("Network error: server name unknown: " + host, e);
215    } catch (ConnectException e) {
216      fatalError("Network error: could not connect to server: " +
217                 host + ":" + port, e);
218    } catch (EOFException e) {
219      if (showOfflineDesktop) {
220        e.printStackTrace();
221        System.out.println("Network error: remote side closed connection");
222        if (vc != null) {
223          vc.enableInput(false);
224        }
225        if (inSeparateFrame) {
226          vncFrame.setTitle(rfb.desktopName + " [disconnected]");
227        }
228        if (rfb != null && !rfb.closed())
229          rfb.close();
230        if (showControls && buttonPanel != null) {
231          buttonPanel.disableButtonsOnDisconnect();
232          if (inSeparateFrame) {
233            vncFrame.pack();
234          } else {
235            validate();
236          }
237        }
238      } else {
239        fatalError("Network error: remote side closed connection", e);
240      }
241    } catch (IOException e) {
242      String str = e.getMessage();
243      if (str != null && str.length() != 0) {
244        fatalError("Network Error: " + str, e);
245      } else {
246        fatalError(e.toString(), e);
247      }
248    } catch (Exception e) {
249      String str = e.getMessage();
250      if (str != null && str.length() != 0) {
251        fatalError("Error: " + str, e);
252      } else {
253        fatalError(e.toString(), e);
254      }
255    }
256   
257  }
258
259  //
260  // Create a VncCanvas instance.
261  //
262
263  void createCanvas(int maxWidth, int maxHeight) throws IOException {
264    // Determine if Java 2D API is available and use a special
265    // version of VncCanvas if it is present.
266    vc = null;
267    try {
268      // This throws ClassNotFoundException if there is no Java 2D API.
269      Class cl = Class.forName("java.awt.Graphics2D");
270      // If we could load Graphics2D class, then we can use VncCanvas2D.
271      cl = Class.forName("VncCanvas2");
272      Class[] argClasses = { this.getClass(), Integer.TYPE, Integer.TYPE };
273      java.lang.reflect.Constructor cstr = cl.getConstructor(argClasses);
274      Object[] argObjects =
275        { this, new Integer(maxWidth), new Integer(maxHeight) };
276      vc = (VncCanvas)cstr.newInstance(argObjects);
277    } catch (Exception e) {
278      System.out.println("Warning: Java 2D API is not available");
279    }
280
281    // If we failed to create VncCanvas2D, use old VncCanvas.
282    if (vc == null)
283      vc = new VncCanvas(this, maxWidth, maxHeight);
284  }
285
286
287  //
288  // Process RFB socket messages.
289  // If the rfbThread is being stopped, ignore any exceptions,
290  // otherwise rethrow the exception so it can be handled.
291  //
292 
293  void processNormalProtocol() throws Exception {
294    try {
295      vc.processNormalProtocol();
296    } catch (Exception e) {
297      if (rfbThread == null) {
298        System.out.println("Ignoring RFB socket exceptions" +
299                           " because applet is stopping");
300      } else {
301        throw e;
302      }
303    }
304  }
305
306
307  //
308  // Connect to the RFB server and authenticate the user.
309  //
310
311  void connectAndAuthenticate() throws Exception
312  {
313    showConnectionStatus("Initializing...");
314    if (inSeparateFrame) {
315      vncFrame.pack();
316      vncFrame.show();
317    } else {
318      validate();
319    }
320
321    showConnectionStatus("Connecting to " + host + ", port " + port + "...");
322
323    rfb = new RfbProto(host, port, this);
324    showConnectionStatus("Connected to server");
325
326    rfb.readVersionMsg();
327    showConnectionStatus("RFB server supports protocol version " +
328                         rfb.serverMajor + "." + rfb.serverMinor);
329
330    rfb.writeVersionMsg();
331    showConnectionStatus("Using RFB protocol version " +
332                         rfb.clientMajor + "." + rfb.clientMinor);
333
334    int secType = rfb.negotiateSecurity();
335    int authType;
336    if (secType == RfbProto.SecTypeTight) {
337      showConnectionStatus("Enabling TightVNC protocol extensions");
338      rfb.initCapabilities();
339      rfb.setupTunneling();
340      authType = rfb.negotiateAuthenticationTight();
341    } else {
342      authType = secType;
343    }
344
345    switch (authType) {
346    case RfbProto.AuthNone:
347      showConnectionStatus("No authentication needed");
348      rfb.authenticateNone();
349      break;
350    case RfbProto.AuthVNC:
351      showConnectionStatus("Performing standard VNC authentication");
352      if (passwordParam != null) {
353        rfb.authenticateVNC(passwordParam);
354      } else {
355        String pw = askPassword();
356        rfb.authenticateVNC(pw);
357      }
358      break;
359    default:
360      throw new Exception("Unknown authentication scheme " + authType);
361    }
362  }
363
364
365  //
366  // Show a message describing the connection status.
367  // To hide the connection status label, use (msg == null).
368  //
369
370  void showConnectionStatus(String msg)
371  {
372    if (msg == null) {
373      if (vncContainer.isAncestorOf(connStatusLabel)) {
374        vncContainer.remove(connStatusLabel);
375      }
376      return;
377    }
378
379    System.out.println(msg);
380
381    if (connStatusLabel == null) {
382      connStatusLabel = new Label("Status: " + msg);
383      connStatusLabel.setFont(new Font("Helvetica", Font.PLAIN, 12));
384    } else {
385      connStatusLabel.setText("Status: " + msg);
386    }
387
388    if (!vncContainer.isAncestorOf(connStatusLabel)) {
389      GridBagConstraints gbc = new GridBagConstraints();
390      gbc.gridwidth = GridBagConstraints.REMAINDER;
391      gbc.fill = GridBagConstraints.HORIZONTAL;
392      gbc.anchor = GridBagConstraints.NORTHWEST;
393      gbc.weightx = 1.0;
394      gbc.weighty = 1.0;
395      gbc.insets = new Insets(20, 30, 20, 30);
396      gridbag.setConstraints(connStatusLabel, gbc);
397      vncContainer.add(connStatusLabel);
398    }
399
400    if (inSeparateFrame) {
401      vncFrame.pack();
402    } else {
403      validate();
404    }
405  }
406
407
408  //
409  // Show an authentication panel.
410  //
411
412  String askPassword() throws Exception
413  {
414    showConnectionStatus(null);
415
416    AuthPanel authPanel = new AuthPanel(this);
417
418    GridBagConstraints gbc = new GridBagConstraints();
419    gbc.gridwidth = GridBagConstraints.REMAINDER;
420    gbc.anchor = GridBagConstraints.NORTHWEST;
421    gbc.weightx = 1.0;
422    gbc.weighty = 1.0;
423    gbc.ipadx = 100;
424    gbc.ipady = 50;
425    gridbag.setConstraints(authPanel, gbc);
426    vncContainer.add(authPanel);
427
428    if (inSeparateFrame) {
429      vncFrame.pack();
430    } else {
431      validate();
432    }
433
434    authPanel.moveFocusToDefaultField();
435    String pw = authPanel.getPassword();
436    vncContainer.remove(authPanel);
437
438    return pw;
439  }
440
441
442  //
443  // Do the rest of the protocol initialisation.
444  //
445
446  void doProtocolInitialisation() throws IOException
447  {
448    rfb.writeClientInit();
449    rfb.readServerInit();
450
451    System.out.println("Desktop name is " + rfb.desktopName);
452    System.out.println("Desktop size is " + rfb.framebufferWidth + " x " +
453                       rfb.framebufferHeight);
454
455    setEncodings();
456
457    showConnectionStatus(null);
458  }
459
460
461  //
462  // Send current encoding list to the RFB server.
463  //
464
465  int[] encodingsSaved;
466  int nEncodingsSaved;
467
468  void setEncodings()        { setEncodings(false); }
469  void autoSelectEncodings() { setEncodings(true); }
470
471  void setEncodings(boolean autoSelectOnly) {
472    if (options == null || rfb == null || !rfb.inNormalProtocol)
473      return;
474
475    int preferredEncoding = options.preferredEncoding;
476    if (preferredEncoding == -1) {
477      long kbitsPerSecond = rfb.kbitsPerSecond();
478      if (nEncodingsSaved < 1) {
479        // Choose Tight or ZRLE encoding for the very first update.
480        System.out.println("Using Tight/ZRLE encodings");
481        preferredEncoding = RfbProto.EncodingTight;
482      } else if (kbitsPerSecond > 2000 &&
483                 encodingsSaved[0] != RfbProto.EncodingHextile) {
484        // Switch to Hextile if the connection speed is above 2Mbps.
485        System.out.println("Throughput " + kbitsPerSecond +
486                           " kbit/s - changing to Hextile encoding");
487        preferredEncoding = RfbProto.EncodingHextile;
488      } else if (kbitsPerSecond < 1000 &&
489                 encodingsSaved[0] != RfbProto.EncodingTight) {
490        // Switch to Tight/ZRLE if the connection speed is below 1Mbps.
491        System.out.println("Throughput " + kbitsPerSecond +
492                           " kbit/s - changing to Tight/ZRLE encodings");
493        preferredEncoding = RfbProto.EncodingTight;
494      } else {
495        // Don't change the encoder.
496        if (autoSelectOnly)
497          return;
498        preferredEncoding = encodingsSaved[0];
499      }
500    } else {
501      // Auto encoder selection is not enabled.
502      if (autoSelectOnly)
503        return;
504    }
505
506    int[] encodings = new int[20];
507    int nEncodings = 0;
508
509    encodings[nEncodings++] = preferredEncoding;
510    if (options.useCopyRect) {
511      encodings[nEncodings++] = RfbProto.EncodingCopyRect;
512    }
513
514    if (preferredEncoding != RfbProto.EncodingTight) {
515      encodings[nEncodings++] = RfbProto.EncodingTight;
516    }
517    if (preferredEncoding != RfbProto.EncodingZRLE) {
518      encodings[nEncodings++] = RfbProto.EncodingZRLE;
519    }
520    if (preferredEncoding != RfbProto.EncodingHextile) {
521      encodings[nEncodings++] = RfbProto.EncodingHextile;
522    }
523    if (preferredEncoding != RfbProto.EncodingZlib) {
524      encodings[nEncodings++] = RfbProto.EncodingZlib;
525    }
526    if (preferredEncoding != RfbProto.EncodingCoRRE) {
527      encodings[nEncodings++] = RfbProto.EncodingCoRRE;
528    }
529    if (preferredEncoding != RfbProto.EncodingRRE) {
530      encodings[nEncodings++] = RfbProto.EncodingRRE;
531    }
532
533    if (options.compressLevel >= 0 && options.compressLevel <= 9) {
534      encodings[nEncodings++] =
535        RfbProto.EncodingCompressLevel0 + options.compressLevel;
536    }
537    if (options.jpegQuality >= 0 && options.jpegQuality <= 9) {
538      encodings[nEncodings++] =
539        RfbProto.EncodingQualityLevel0 + options.jpegQuality;
540    }
541
542    if (options.requestCursorUpdates) {
543      encodings[nEncodings++] = RfbProto.EncodingXCursor;
544      encodings[nEncodings++] = RfbProto.EncodingRichCursor;
545      if (!options.ignoreCursorUpdates)
546        encodings[nEncodings++] = RfbProto.EncodingPointerPos;
547    }
548
549    encodings[nEncodings++] = RfbProto.EncodingLastRect;
550    encodings[nEncodings++] = RfbProto.EncodingNewFBSize;
551
552    boolean encodingsWereChanged = false;
553    if (nEncodings != nEncodingsSaved) {
554      encodingsWereChanged = true;
555    } else {
556      for (int i = 0; i < nEncodings; i++) {
557        if (encodings[i] != encodingsSaved[i]) {
558          encodingsWereChanged = true;
559          break;
560        }
561      }
562    }
563
564    if (encodingsWereChanged) {
565      try {
566        rfb.writeSetEncodings(encodings, nEncodings);
567        if (vc != null) {
568          vc.softCursorFree();
569        }
570      } catch (Exception e) {
571        e.printStackTrace();
572      }
573      encodingsSaved = encodings;
574      nEncodingsSaved = nEncodings;
575    }
576  }
577
578
579  //
580  // setCutText() - send the given cut text to the RFB server.
581  //
582
583  void setCutText(String text) {
584    try {
585      if (rfb != null && rfb.inNormalProtocol) {
586        rfb.writeClientCutText(text);
587      }
588    } catch (Exception e) {
589      e.printStackTrace();
590    }
591  }
592
593
594  //
595  // Order change in session recording status. To stop recording, pass
596  // null in place of the fname argument.
597  //
598
599  void setRecordingStatus(String fname) {
600    synchronized(recordingSync) {
601      sessionFileName = fname;
602      recordingStatusChanged = true;
603    }
604  }
605
606  //
607  // Start or stop session recording. Returns true if this method call
608  // causes recording of a new session.
609  //
610
611  boolean checkRecordingStatus() throws IOException {
612    synchronized(recordingSync) {
613      if (recordingStatusChanged) {
614        recordingStatusChanged = false;
615        if (sessionFileName != null) {
616          startRecording();
617          return true;
618        } else {
619          stopRecording();
620        }
621      }
622    }
623    return false;
624  }
625
626  //
627  // Start session recording.
628  //
629
630  protected void startRecording() throws IOException {
631    synchronized(recordingSync) {
632      if (!recordingActive) {
633        // Save settings to restore them after recording the session.
634        cursorUpdatesDef =
635          options.choices[options.cursorUpdatesIndex].getSelectedItem();
636        eightBitColorsDef =
637          options.choices[options.eightBitColorsIndex].getSelectedItem();
638        // Set options to values suitable for recording.
639        options.choices[options.cursorUpdatesIndex].select("Disable");
640        options.choices[options.cursorUpdatesIndex].setEnabled(false);
641        options.setEncodings();
642        options.choices[options.eightBitColorsIndex].select("No");
643        options.choices[options.eightBitColorsIndex].setEnabled(false);
644        options.setColorFormat();
645      } else {
646        rfb.closeSession();
647      }
648
649      System.out.println("Recording the session in " + sessionFileName);
650      rfb.startSession(sessionFileName);
651      recordingActive = true;
652    }
653  }
654
655  //
656  // Stop session recording.
657  //
658
659  protected void stopRecording() throws IOException {
660    synchronized(recordingSync) {
661      if (recordingActive) {
662        // Restore options.
663        options.choices[options.cursorUpdatesIndex].select(cursorUpdatesDef);
664        options.choices[options.cursorUpdatesIndex].setEnabled(true);
665        options.setEncodings();
666        options.choices[options.eightBitColorsIndex].select(eightBitColorsDef);
667        options.choices[options.eightBitColorsIndex].setEnabled(true);
668        options.setColorFormat();
669
670        rfb.closeSession();
671        System.out.println("Session recording stopped.");
672      }
673      sessionFileName = null;
674      recordingActive = false;
675    }
676  }
677
678
679  //
680  // readParameters() - read parameters from the html source or from the
681  // command line.  On the command line, the arguments are just a sequence of
682  // param_name/param_value pairs where the names and values correspond to
683  // those expected in the html applet tag source.
684  //
685
686  void readParameters() {
687    host = readParameter("HOST", !inAnApplet);
688    if (host == null) {
689      host = getCodeBase().getHost();
690      if (host.equals("")) {
691        fatalError("HOST parameter not specified");
692      }
693    }
694
695    String str = readParameter("PORT", true);
696    port = Integer.parseInt(str);
697
698    // Read "ENCPASSWORD" or "PASSWORD" parameter if specified.
699    readPasswordParameters();
700
701    if (inAnApplet) {
702      str = readParameter("Open New Window", false);
703      if (str != null && str.equalsIgnoreCase("Yes"))
704        inSeparateFrame = true;
705    }
706
707    // "Show Controls" set to "No" disables button panel.
708    showControls = true;
709    str = readParameter("Show Controls", false);
710    if (str != null && str.equalsIgnoreCase("No"))
711      showControls = false;
712
713    // "Offer Relogin" set to "No" disables "Login again" and "Close
714    // window" buttons under error messages in applet mode.
715    offerRelogin = true;
716    str = readParameter("Offer Relogin", false);
717    if (str != null && str.equalsIgnoreCase("No"))
718      offerRelogin = false;
719
720    // Do we continue showing desktop on remote disconnect?
721    showOfflineDesktop = false;
722    str = readParameter("Show Offline Desktop", false);
723    if (str != null && str.equalsIgnoreCase("Yes"))
724      showOfflineDesktop = true;
725
726    // Fine tuning options.
727    deferScreenUpdates = readIntParameter("Defer screen updates", 20);
728    deferCursorUpdates = readIntParameter("Defer cursor updates", 10);
729    deferUpdateRequests = readIntParameter("Defer update requests", 50);
730
731    // SocketFactory.
732    socketFactory = readParameter("SocketFactory", false);
733  }
734
735  //
736  // Read password parameters. If an "ENCPASSWORD" parameter is set,
737  // then decrypt the password into the passwordParam string. Otherwise,
738  // try to read the "PASSWORD" parameter directly to passwordParam.
739  //
740
741  private void readPasswordParameters() {
742    String encPasswordParam = readParameter("ENCPASSWORD", false);
743    if (encPasswordParam == null) {
744      passwordParam = readParameter("PASSWORD", false);
745    } else {
746      // ENCPASSWORD is hexascii-encoded. Decode.
747      byte[] pw = {0, 0, 0, 0, 0, 0, 0, 0};
748      int len = encPasswordParam.length() / 2;
749      if (len > 8)
750        len = 8;
751      for (int i = 0; i < len; i++) {
752        String hex = encPasswordParam.substring(i*2, i*2+2);
753        Integer x = new Integer(Integer.parseInt(hex, 16));
754        pw[i] = x.byteValue();
755      }
756      // Decrypt the password.
757      byte[] key = {23, 82, 107, 6, 35, 78, 88, 7};
758      DesCipher des = new DesCipher(key);
759      des.decrypt(pw, 0, pw, 0);
760      passwordParam = new String(pw);
761    }
762  }
763
764  public String readParameter(String name, boolean required) {
765    if (inAnApplet) {
766      String s = getParameter(name);
767      if ((s == null) && required) {
768        fatalError(name + " parameter not specified");
769      }
770      return s;
771    }
772
773    for (int i = 0; i < mainArgs.length; i += 2) {
774      if (mainArgs[i].equalsIgnoreCase(name)) {
775        try {
776          return mainArgs[i+1];
777        } catch (Exception e) {
778          if (required) {
779            fatalError(name + " parameter not specified");
780          }
781          return null;
782        }
783      }
784    }
785    if (required) {
786      fatalError(name + " parameter not specified");
787    }
788    return null;
789  }
790
791  int readIntParameter(String name, int defaultValue) {
792    String str = readParameter(name, false);
793    int result = defaultValue;
794    if (str != null) {
795      try {
796        result = Integer.parseInt(str);
797      } catch (NumberFormatException e) { }
798    }
799    return result;
800  }
801
802  //
803  // moveFocusToDesktop() - move keyboard focus either to VncCanvas.
804  //
805
806  void moveFocusToDesktop() {
807    if (vncContainer != null) {
808      if (vc != null && vncContainer.isAncestorOf(vc))
809        vc.requestFocus();
810    }
811  }
812
813  //
814  // disconnect() - close connection to server.
815  //
816
817  synchronized public void disconnect() {
818    System.out.println("Disconnect");
819
820    if (rfb != null && !rfb.closed())
821      rfb.close();
822    options.dispose();
823    clipboard.dispose();
824    if (rec != null)
825      rec.dispose();
826
827    if (inAnApplet) {
828      showMessage("Disconnected");
829    } else {
830      System.exit(0);
831    }
832  }
833
834  //
835  // fatalError() - print out a fatal error message.
836  // FIXME: Do we really need two versions of the fatalError() method?
837  //
838
839  synchronized public void fatalError(String str) {
840    System.out.println(str);
841
842    if (inAnApplet) {
843      // vncContainer null, applet not inited,
844      // can not present the error to the user.
845      Thread.currentThread().stop();
846    } else {
847      System.exit(1);
848    }
849  }
850
851  synchronized public void fatalError(String str, Exception e) {
852 
853    if (rfb != null && rfb.closed()) {
854      // Not necessary to show error message if the error was caused
855      // by I/O problems after the rfb.close() method call.
856      System.out.println("RFB thread finished");
857      return;
858    }
859
860    System.out.println(str);
861    e.printStackTrace();
862
863    if (rfb != null)
864      rfb.close();
865
866    if (inAnApplet) {
867      showMessage(str);
868    } else {
869      System.exit(1);
870    }
871  }
872
873  //
874  // Show message text and optionally "Relogin" and "Close" buttons.
875  //
876
877  void showMessage(String msg) {
878    vncContainer.removeAll();
879
880    Label errLabel = new Label(msg, Label.CENTER);
881    errLabel.setFont(new Font("Helvetica", Font.PLAIN, 12));
882
883    if (offerRelogin) {
884
885      Panel gridPanel = new Panel(new GridLayout(0, 1));
886      Panel outerPanel = new Panel(new FlowLayout(FlowLayout.LEFT));
887      outerPanel.add(gridPanel);
888      vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 16));
889      vncContainer.add(outerPanel);
890      Panel textPanel = new Panel(new FlowLayout(FlowLayout.CENTER));
891      textPanel.add(errLabel);
892      gridPanel.add(textPanel);
893      gridPanel.add(new ReloginPanel(this));
894
895    } else {
896
897      vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 30));
898      vncContainer.add(errLabel);
899
900    }
901
902    if (inSeparateFrame) {
903      vncFrame.pack();
904    } else {
905      validate();
906    }
907  }
908
909  //
910  // Stop the applet.
911  // Main applet thread will terminate on first exception
912  // after seeing that rfbThread has been set to null.
913  //
914
915  public void stop() {
916    System.out.println("Stopping applet");
917    rfbThread = null;
918  }
919
920  //
921  // This method is called before the applet is destroyed.
922  //
923
924  public void destroy() {
925    System.out.println("Destroying applet");
926
927    vncContainer.removeAll();
928    options.dispose();
929    clipboard.dispose();
930    if (rec != null)
931      rec.dispose();
932    if (rfb != null && !rfb.closed())
933      rfb.close();
934    if (inSeparateFrame)
935      vncFrame.dispose();
936  }
937
938  //
939  // Start/stop receiving mouse events.
940  //
941
942  public void enableInput(boolean enable) {
943    vc.enableInput(enable);
944  }
945
946  //
947  // Close application properly on window close event.
948  //
949
950  public void windowClosing(WindowEvent evt) {
951    System.out.println("Closing window");
952    if (rfb != null)
953      disconnect();
954
955    vncContainer.hide();
956
957    if (!inAnApplet) {
958      System.exit(0);
959    }
960  }
961
962  //
963  // Ignore window events we're not interested in.
964  //
965
966  public void windowActivated(WindowEvent evt) {}
967  public void windowDeactivated (WindowEvent evt) {}
968  public void windowOpened(WindowEvent evt) {}
969  public void windowClosed(WindowEvent evt) {}
970  public void windowIconified(WindowEvent evt) {}
971  public void windowDeiconified(WindowEvent evt) {}
972}
Note: See TracBrowser for help on using the repository browser.