You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright 2009-2013 Pierre Ossman <ossman@cendio.se> for Cendio AB
  3. * Copyright (C) 2011-2013 D. R. Commander. All Rights Reserved.
  4. * Copyright (C) 2011-2017 Brian P. Hinz
  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
  19. * USA.
  20. */
  21. //
  22. // CConn
  23. //
  24. // Methods on CConn are called from both the GUI thread and the thread which
  25. // processes incoming RFB messages ("the RFB thread"). This means we need to
  26. // be careful with synchronization here.
  27. //
  28. // Any access to writer() must not only be synchronized, but we must also make
  29. // sure that the connection is in RFBSTATE_NORMAL. We are guaranteed this for
  30. // any code called after serverInit() has been called. Since the DesktopWindow
  31. // isn't created until then, any methods called only from DesktopWindow can
  32. // assume that we are in RFBSTATE_NORMAL.
  33. package com.tigervnc.vncviewer;
  34. import java.awt.*;
  35. import java.awt.datatransfer.StringSelection;
  36. import java.awt.event.*;
  37. import java.awt.Toolkit;
  38. import java.io.IOException;
  39. import java.io.InputStream;
  40. import java.io.File;
  41. import java.io.FileInputStream;
  42. import java.io.FileNotFoundException;
  43. import java.util.jar.Attributes;
  44. import java.util.jar.Manifest;
  45. import javax.swing.*;
  46. import javax.swing.ImageIcon;
  47. import java.net.InetSocketAddress;
  48. import java.net.SocketException;
  49. import java.util.*;
  50. import java.util.prefs.*;
  51. import com.tigervnc.rdr.*;
  52. import com.tigervnc.rfb.*;
  53. import com.tigervnc.rfb.Point;
  54. import com.tigervnc.rfb.Exception;
  55. import com.tigervnc.network.Socket;
  56. import com.tigervnc.network.TcpSocket;
  57. import static com.tigervnc.vncviewer.Parameters.*;
  58. public class CConn extends CConnection implements
  59. UserPasswdGetter, FdInStreamBlockCallback, ActionListener {
  60. // 8 colours (1 bit per component)
  61. static final PixelFormat verylowColorPF =
  62. new PixelFormat(8, 3, false, true, 1, 1, 1, 2, 1, 0);
  63. // 64 colours (2 bits per component)
  64. static final PixelFormat lowColorPF =
  65. new PixelFormat(8, 6, false, true, 3, 3, 3, 4, 2, 0);
  66. // 256 colours (2-3 bits per component)
  67. static final PixelFormat mediumColorPF =
  68. new PixelFormat(8, 8, false, true, 7, 7, 3, 5, 2, 0);
  69. ////////////////////////////////////////////////////////////////////
  70. // The following methods are all called from the RFB thread
  71. public CConn(String vncServerName, Socket socket)
  72. {
  73. serverHost = null; serverPort = 0; desktop = null;
  74. pendingPFChange = false;
  75. currentEncoding = Encodings.encodingTight; lastServerEncoding = -1;
  76. formatChange = false; encodingChange = false;
  77. firstUpdate = true; pendingUpdate = false; continuousUpdates = false;
  78. forceNonincremental = true; supportsSyncFence = false;
  79. setShared(shared.getValue());
  80. sock = socket;
  81. upg = this;
  82. int encNum = Encodings.encodingNum(preferredEncoding.getValue());
  83. if (encNum != -1)
  84. currentEncoding = encNum;
  85. cp.supportsLocalCursor = true;
  86. if (VncViewer.os.contains("windows"))
  87. // JRE on Windows does not support alpha cursor
  88. cp.supportsLocalCursorWithAlpha = false;
  89. else
  90. cp.supportsLocalCursorWithAlpha = true;
  91. cp.supportsDesktopResize = true;
  92. cp.supportsExtendedDesktopSize = true;
  93. cp.supportsDesktopRename = true;
  94. cp.supportsSetDesktopSize = false;
  95. cp.supportsClientRedirect = true;
  96. if (customCompressLevel.getValue())
  97. cp.compressLevel = compressLevel.getValue();
  98. else
  99. cp.compressLevel = -1;
  100. if (!noJpeg.getValue())
  101. cp.qualityLevel = qualityLevel.getValue();
  102. else
  103. cp.qualityLevel = -1;
  104. if (sock == null) {
  105. setServerName(Hostname.getHost(vncServerName));
  106. setServerPort(Hostname.getPort(vncServerName));
  107. try {
  108. if (tunnel.getValue() || !via.getValue().isEmpty()) {
  109. int localPort = TcpSocket.findFreeTcpPort();
  110. if (localPort == 0)
  111. throw new Exception("Could not obtain free TCP port");
  112. Tunnel.createTunnel(this, localPort);
  113. sock = new TcpSocket("localhost", localPort);
  114. } else {
  115. sock = new TcpSocket(getServerName(), getServerPort());
  116. }
  117. } catch (java.lang.Exception e) {
  118. throw new Exception(e.getMessage());
  119. }
  120. vlog.info("connected to host "+getServerName()+" port "+getServerPort());
  121. } else {
  122. String name = sock.getPeerEndpoint();
  123. if (listenMode.getValue())
  124. vlog.info("Accepted connection from " + name);
  125. else
  126. vlog.info("connected to host "+Hostname.getHost(name)+" port "+Hostname.getPort(name));
  127. }
  128. // See callback below
  129. sock.inStream().setBlockCallback(this);
  130. setStreams(sock.inStream(), sock.outStream());
  131. initialiseProtocol();
  132. OptionsDialog.addCallback("handleOptions", this);
  133. }
  134. public void refreshFramebuffer()
  135. {
  136. forceNonincremental = true;
  137. // Without fences, we cannot safely trigger an update request directly
  138. // but must wait for the next update to arrive.
  139. if (supportsSyncFence)
  140. requestNewUpdate();
  141. }
  142. public String connectionInfo() {
  143. String info = new String("Desktop name: %s%n"+
  144. "Host: %s:%d%n"+
  145. "Size: %dx%d%n"+
  146. "Pixel format: %s%n"+
  147. " (server default: %s)%n"+
  148. "Requested encoding: %s%n"+
  149. "Last used encoding: %s%n"+
  150. "Line speed estimate: %d kbit/s%n"+
  151. "Protocol version: %d.%d%n"+
  152. "Security method: %s [%s]%n");
  153. String infoText =
  154. String.format(info, cp.name(),
  155. sock.getPeerName(), sock.getPeerPort(),
  156. cp.width, cp.height,
  157. cp.pf().print(),
  158. serverPF.print(),
  159. Encodings.encodingName(currentEncoding),
  160. Encodings.encodingName(lastServerEncoding),
  161. sock.inStream().kbitsPerSecond(),
  162. cp.majorVersion, cp.minorVersion,
  163. Security.secTypeName(csecurity.getType()),
  164. csecurity.description());
  165. return infoText;
  166. }
  167. // The RFB core is not properly asynchronous, so it calls this callback
  168. // whenever it needs to block to wait for more data. Since FLTK is
  169. // monitoring the socket, we just make sure FLTK gets to run.
  170. public void blockCallback() {
  171. try {
  172. synchronized(this) {
  173. wait(1);
  174. }
  175. } catch (java.lang.InterruptedException e) {
  176. throw new Exception(e.getMessage());
  177. }
  178. }
  179. // getUserPasswd() is called by the CSecurity object when it needs us to read
  180. // a password from the user.
  181. public final boolean getUserPasswd(StringBuffer user, StringBuffer passwd) {
  182. String title = ("VNC Authentication ["
  183. +csecurity.description() + "]");
  184. String passwordFileStr = passwordFile.getValue();
  185. PasswdDialog dlg;
  186. if (user == null && !passwordFileStr.equals("")) {
  187. InputStream fp = null;
  188. try {
  189. fp = new FileInputStream(passwordFileStr);
  190. } catch(FileNotFoundException e) {
  191. throw new Exception("Opening password file failed");
  192. }
  193. byte[] obfPwd = new byte[256];
  194. try {
  195. fp.read(obfPwd);
  196. fp.close();
  197. } catch(IOException e) {
  198. throw new Exception("Failed to read VncPasswd file");
  199. }
  200. String PlainPasswd = VncAuth.unobfuscatePasswd(obfPwd);
  201. passwd.append(PlainPasswd);
  202. passwd.setLength(PlainPasswd.length());
  203. return true;
  204. }
  205. if (user == null) {
  206. dlg = new PasswdDialog(title, (user == null), (passwd == null));
  207. } else {
  208. if ((passwd == null) && sendLocalUsername.getValue()) {
  209. user.append((String)System.getProperties().get("user.name"));
  210. return true;
  211. }
  212. dlg = new PasswdDialog(title, sendLocalUsername.getValue(),
  213. (passwd == null));
  214. }
  215. dlg.showDialog();
  216. if (user != null) {
  217. if (sendLocalUsername.getValue()) {
  218. user.append((String)System.getProperties().get("user.name"));
  219. } else {
  220. user.append(dlg.userEntry.getText());
  221. }
  222. }
  223. if (passwd != null)
  224. passwd.append(new String(dlg.passwdEntry.getPassword()));
  225. return true;
  226. }
  227. ////////////////////// CConnection callback methods //////////////////////
  228. // serverInit() is called when the serverInit message has been received. At
  229. // this point we create the desktop window and display it. We also tell the
  230. // server the pixel format and encodings to use and request the first update.
  231. public void serverInit()
  232. {
  233. super.serverInit();
  234. // If using AutoSelect with old servers, start in FullColor
  235. // mode. See comment in autoSelectFormatAndEncoding.
  236. if (cp.beforeVersion(3, 8) && autoSelect.getValue())
  237. fullColor.setParam(true);
  238. serverPF = cp.pf();
  239. desktop = new DesktopWindow(cp.width, cp.height, cp.name(), serverPF, this);
  240. fullColorPF = desktop.getPreferredPF();
  241. // Force a switch to the format and encoding we'd like
  242. formatChange = true; encodingChange = true;
  243. // And kick off the update cycle
  244. requestNewUpdate();
  245. // This initial update request is a bit of a corner case, so we need
  246. // to help out setting the correct format here.
  247. assert(pendingPFChange);
  248. cp.setPF(pendingPF);
  249. pendingPFChange = false;
  250. }
  251. // setDesktopSize() is called when the desktop size changes (including when
  252. // it is set initially).
  253. public void setDesktopSize(int w, int h)
  254. {
  255. super.setDesktopSize(w, h);
  256. resizeFramebuffer();
  257. }
  258. // setExtendedDesktopSize() is a more advanced version of setDesktopSize()
  259. public void setExtendedDesktopSize(int reason, int result, int w, int h,
  260. ScreenSet layout)
  261. {
  262. super.setExtendedDesktopSize(reason, result, w, h, layout);
  263. if ((reason == screenTypes.reasonClient) &&
  264. (result != screenTypes.resultSuccess)) {
  265. vlog.error("SetDesktopSize failed: " + result);
  266. return;
  267. }
  268. resizeFramebuffer();
  269. }
  270. // clientRedirect() migrates the client to another host/port
  271. public void clientRedirect(int port, String host, String x509subject)
  272. {
  273. try {
  274. sock.close();
  275. sock = new TcpSocket(host, port);
  276. vlog.info("Redirected to "+host+":"+port);
  277. setServerName(host);
  278. setServerPort(port);
  279. sock.inStream().setBlockCallback(this);
  280. setStreams(sock.inStream(), sock.outStream());
  281. if (desktop != null)
  282. desktop.dispose();
  283. initialiseProtocol();
  284. } catch (java.lang.Exception e) {
  285. throw new Exception(e.getMessage());
  286. }
  287. }
  288. // setName() is called when the desktop name changes
  289. public void setName(String name)
  290. {
  291. super.setName(name);
  292. if (desktop != null)
  293. desktop.setName(name);
  294. }
  295. // framebufferUpdateStart() is called at the beginning of an update.
  296. // Here we try to send out a new framebuffer update request so that the
  297. // next update can be sent out in parallel with us decoding the current
  298. // one.
  299. public void framebufferUpdateStart()
  300. {
  301. ModifiablePixelBuffer pb;
  302. PlatformPixelBuffer ppb;
  303. super.framebufferUpdateStart();
  304. // Note: This might not be true if sync fences are supported
  305. pendingUpdate = false;
  306. requestNewUpdate();
  307. // We might still be rendering the previous update
  308. pb = getFramebuffer();
  309. assert(pb != null);
  310. ppb = (PlatformPixelBuffer)pb;
  311. assert(ppb != null);
  312. //FIXME
  313. }
  314. // framebufferUpdateEnd() is called at the end of an update.
  315. // For each rectangle, the FdInStream will have timed the speed
  316. // of the connection, allowing us to select format and encoding
  317. // appropriately, and then request another incremental update.
  318. public void framebufferUpdateEnd()
  319. {
  320. super.framebufferUpdateEnd();
  321. desktop.updateWindow();
  322. if (firstUpdate) {
  323. // We need fences to make extra update requests and continuous
  324. // updates "safe". See fence() for the next step.
  325. if (cp.supportsFence)
  326. writer().writeFence(fenceTypes.fenceFlagRequest | fenceTypes.fenceFlagSyncNext, 0, null);
  327. firstUpdate = false;
  328. }
  329. // A format change has been scheduled and we are now past the update
  330. // with the old format. Time to active the new one.
  331. if (pendingPFChange) {
  332. cp.setPF(pendingPF);
  333. pendingPFChange = false;
  334. }
  335. // Compute new settings based on updated bandwidth values
  336. if (autoSelect.getValue())
  337. autoSelectFormatAndEncoding();
  338. }
  339. // The rest of the callbacks are fairly self-explanatory...
  340. public void setColourMapEntries(int firstColor, int nColors, int[] rgbs)
  341. {
  342. vlog.error("Invalid SetColourMapEntries from server!");
  343. }
  344. public void bell()
  345. {
  346. if (acceptBell.getValue())
  347. desktop.getToolkit().beep();
  348. }
  349. public void serverCutText(String str, int len)
  350. {
  351. StringSelection buffer;
  352. if (!acceptClipboard.getValue())
  353. return;
  354. ClipboardDialog.serverCutText(str);
  355. }
  356. public void dataRect(Rect r, int encoding)
  357. {
  358. sock.inStream().startTiming();
  359. if (encoding != Encodings.encodingCopyRect)
  360. lastServerEncoding = encoding;
  361. super.dataRect(r, encoding);
  362. sock.inStream().stopTiming();
  363. }
  364. public void setCursor(int width, int height, Point hotspot,
  365. byte[] data)
  366. {
  367. desktop.setCursor(width, height, hotspot, data);
  368. }
  369. public void fence(int flags, int len, byte[] data)
  370. {
  371. // can't call super.super.fence(flags, len, data);
  372. cp.supportsFence = true;
  373. if ((flags & fenceTypes.fenceFlagRequest) != 0) {
  374. // We handle everything synchronously so we trivially honor these modes
  375. flags = flags & (fenceTypes.fenceFlagBlockBefore | fenceTypes.fenceFlagBlockAfter);
  376. writer().writeFence(flags, len, data);
  377. return;
  378. }
  379. if (len == 0) {
  380. // Initial probe
  381. if ((flags & fenceTypes.fenceFlagSyncNext) != 0) {
  382. supportsSyncFence = true;
  383. if (cp.supportsContinuousUpdates) {
  384. vlog.info("Enabling continuous updates");
  385. continuousUpdates = true;
  386. writer().writeEnableContinuousUpdates(true, 0, 0, cp.width, cp.height);
  387. }
  388. }
  389. } else {
  390. // Pixel format change
  391. MemInStream memStream = new MemInStream(data, 0, len);
  392. PixelFormat pf = new PixelFormat();
  393. pf.read(memStream);
  394. cp.setPF(pf);
  395. }
  396. }
  397. ////////////////////// Internal methods //////////////////////
  398. private void resizeFramebuffer()
  399. {
  400. if (desktop == null)
  401. return;
  402. if (continuousUpdates)
  403. writer().writeEnableContinuousUpdates(true, 0, 0, cp.width, cp.height);
  404. desktop.resizeFramebuffer(cp.width, cp.height);
  405. }
  406. // autoSelectFormatAndEncoding() chooses the format and encoding appropriate
  407. // to the connection speed:
  408. //
  409. // First we wait for at least one second of bandwidth measurement.
  410. //
  411. // Above 16Mbps (i.e. LAN), we choose the second highest JPEG quality,
  412. // which should be perceptually lossless.
  413. //
  414. // If the bandwidth is below that, we choose a more lossy JPEG quality.
  415. //
  416. // If the bandwidth drops below 256 Kbps, we switch to palette mode.
  417. //
  418. // Note: The system here is fairly arbitrary and should be replaced
  419. // with something more intelligent at the server end.
  420. //
  421. private void autoSelectFormatAndEncoding()
  422. {
  423. long kbitsPerSecond = sock.inStream().kbitsPerSecond();
  424. long timeWaited = sock.inStream().timeWaited();
  425. boolean newFullColor = fullColor.getValue();
  426. int newQualityLevel = qualityLevel.getValue();
  427. // Always use Tight
  428. if (currentEncoding != Encodings.encodingTight) {
  429. currentEncoding = Encodings.encodingTight;
  430. encodingChange = true;
  431. }
  432. // Check that we have a decent bandwidth measurement
  433. if ((kbitsPerSecond == 0) || (timeWaited < 100))
  434. return;
  435. // Select appropriate quality level
  436. if (!noJpeg.getValue()) {
  437. if (kbitsPerSecond > 16000)
  438. newQualityLevel = 8;
  439. else
  440. newQualityLevel = 6;
  441. if (newQualityLevel != qualityLevel.getValue()) {
  442. vlog.info("Throughput "+kbitsPerSecond+
  443. " kbit/s - changing to quality "+newQualityLevel);
  444. cp.qualityLevel = newQualityLevel;
  445. qualityLevel.setParam(newQualityLevel);
  446. encodingChange = true;
  447. }
  448. }
  449. if (cp.beforeVersion(3, 8)) {
  450. // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with
  451. // cursors "asynchronously". If this happens in the middle of a
  452. // pixel format change, the server will encode the cursor with
  453. // the old format, but the client will try to decode it
  454. // according to the new format. This will lead to a
  455. // crash. Therefore, we do not allow automatic format change for
  456. // old servers.
  457. return;
  458. }
  459. // Select best color level
  460. newFullColor = (kbitsPerSecond > 256);
  461. if (newFullColor != fullColor.getValue()) {
  462. vlog.info("Throughput "+kbitsPerSecond+
  463. " kbit/s - full color is now "+
  464. (newFullColor ? "enabled" : "disabled"));
  465. fullColor.setParam(newFullColor);
  466. formatChange = true;
  467. }
  468. }
  469. // checkEncodings() sends a setEncodings message if one is needed.
  470. private void checkEncodings()
  471. {
  472. if (encodingChange && (writer() != null)) {
  473. vlog.info("Using " + Encodings.encodingName(currentEncoding) +
  474. " encoding");
  475. writer().writeSetEncodings(currentEncoding, true);
  476. encodingChange = false;
  477. }
  478. }
  479. // requestNewUpdate() requests an update from the server, having set the
  480. // format and encoding appropriately.
  481. private void requestNewUpdate()
  482. {
  483. if (formatChange) {
  484. PixelFormat pf;
  485. /* Catch incorrect requestNewUpdate calls */
  486. assert(!pendingUpdate || supportsSyncFence);
  487. if (fullColor.getValue()) {
  488. pf = fullColorPF;
  489. } else {
  490. if (lowColorLevel.getValue() == 0) {
  491. pf = verylowColorPF;
  492. } else if (lowColorLevel.getValue() == 1) {
  493. pf = lowColorPF;
  494. } else {
  495. pf = mediumColorPF;
  496. }
  497. }
  498. if (supportsSyncFence) {
  499. // We let the fence carry the pixel format and switch once we
  500. // get the response back. That way we will be synchronised with
  501. // when the server switches.
  502. MemOutStream memStream = new MemOutStream();
  503. pf.write(memStream);
  504. writer().writeFence(fenceTypes.fenceFlagRequest | fenceTypes.fenceFlagSyncNext,
  505. memStream.length(), (byte[])memStream.data());
  506. } else {
  507. // New requests are sent out at the start of processing the last
  508. // one, so we cannot switch our internal format right now (doing so
  509. // would mean misdecoding the current update).
  510. pendingPFChange = true;
  511. pendingPF = pf;
  512. }
  513. String str = pf.print();
  514. vlog.info("Using pixel format " + str);
  515. writer().writeSetPixelFormat(pf);
  516. formatChange = false;
  517. }
  518. checkEncodings();
  519. if (forceNonincremental || !continuousUpdates) {
  520. pendingUpdate = true;
  521. writer().writeFramebufferUpdateRequest(new Rect(0, 0, cp.width, cp.height),
  522. !forceNonincremental);
  523. }
  524. forceNonincremental = false;
  525. }
  526. public void handleOptions()
  527. {
  528. // Checking all the details of the current set of encodings is just
  529. // a pain. Assume something has changed, as resending the encoding
  530. // list is cheap. Avoid overriding what the auto logic has selected
  531. // though.
  532. if (!autoSelect.getValue()) {
  533. int encNum = Encodings.encodingNum(preferredEncoding.getValue());
  534. if (encNum != -1)
  535. this.currentEncoding = encNum;
  536. }
  537. this.cp.supportsLocalCursor = true;
  538. if (customCompressLevel.getValue())
  539. this.cp.compressLevel = compressLevel.getValue();
  540. else
  541. this.cp.compressLevel = -1;
  542. if (!noJpeg.getValue() && !autoSelect.getValue())
  543. this.cp.qualityLevel = qualityLevel.getValue();
  544. else
  545. this.cp.qualityLevel = -1;
  546. this.encodingChange = true;
  547. // Format changes refreshes the entire screen though and are therefore
  548. // very costly. It's probably worth the effort to see if it is necessary
  549. // here.
  550. PixelFormat pf;
  551. if (fullColor.getValue()) {
  552. pf = fullColorPF;
  553. } else {
  554. if (lowColorLevel.getValue() == 0)
  555. pf = verylowColorPF;
  556. else if (lowColorLevel.getValue() == 1)
  557. pf = lowColorPF;
  558. else
  559. pf = mediumColorPF;
  560. }
  561. if (!pf.equal(this.cp.pf())) {
  562. this.formatChange = true;
  563. // Without fences, we cannot safely trigger an update request directly
  564. // but must wait for the next update to arrive.
  565. if (this.supportsSyncFence)
  566. this.requestNewUpdate();
  567. }
  568. }
  569. ////////////////////////////////////////////////////////////////////
  570. // The following methods are all called from the GUI thread
  571. // close() shuts down the socket, thus waking up the RFB thread.
  572. public void close() {
  573. if (closeListener != null) {
  574. embed.setParam(true);
  575. JFrame f =
  576. (JFrame)SwingUtilities.getAncestorOfClass(JFrame.class, desktop);
  577. if (f != null)
  578. f.dispatchEvent(new WindowEvent(f, WindowEvent.WINDOW_CLOSING));
  579. }
  580. shuttingDown = true;
  581. try {
  582. if (sock != null)
  583. sock.shutdown();
  584. } catch (java.lang.Exception e) {
  585. throw new Exception(e.getMessage());
  586. }
  587. }
  588. // writeClientCutText() is called from the clipboard dialog
  589. public void writeClientCutText(String str, int len) {
  590. if (state() != RFBSTATE_NORMAL || shuttingDown)
  591. return;
  592. writer().writeClientCutText(str, len);
  593. }
  594. // this is a special ActionListener passed in by the
  595. // Java Plug-in software to control applet's close behavior
  596. public void setCloseListener(ActionListener cl) {
  597. closeListener = cl;
  598. }
  599. public void actionPerformed(ActionEvent e) {}
  600. public Socket getSocket() {
  601. return sock;
  602. }
  603. ////////////////////////////////////////////////////////////////////
  604. // The following methods are called from both RFB and GUI threads
  605. // the following never change so need no synchronization:
  606. // access to desktop by different threads is specified in DesktopWindow
  607. // the following need no synchronization:
  608. public static UserPasswdGetter upg;
  609. // shuttingDown is set by the GUI thread and only ever tested by the RFB
  610. // thread after the window has been destroyed.
  611. boolean shuttingDown = false;
  612. // reading and writing int and boolean is atomic in java, so no
  613. // synchronization of the following flags is needed:
  614. // All menu, options, about and info stuff is done in the GUI thread (apart
  615. // from when constructed).
  616. // the following are only ever accessed by the GUI thread:
  617. private String serverHost;
  618. private int serverPort;
  619. private Socket sock;
  620. protected DesktopWindow desktop;
  621. private PixelFormat serverPF;
  622. private PixelFormat fullColorPF;
  623. private boolean pendingPFChange;
  624. private PixelFormat pendingPF;
  625. private int currentEncoding, lastServerEncoding;
  626. private boolean formatChange;
  627. private boolean encodingChange;
  628. private boolean firstUpdate;
  629. private boolean pendingUpdate;
  630. private boolean continuousUpdates;
  631. private boolean forceNonincremental;
  632. private boolean supportsSyncFence;
  633. public ActionListener closeListener = null;
  634. static LogWriter vlog = new LogWriter("CConn");
  635. }