blob: dbbf1d805fb2cf5737f134cd52650126153ec312 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossman8c3bd692018-03-22 15:58:54 +01002 * Copyright 2009-2018 Pierre Ossman for Cendio AB
Peter Åstrand (astrand)7a368c92018-09-19 12:45:17 +02003 * Copyright 2018 Peter Astrand for Cendio AB
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00004 *
5 * This is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This software is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this software; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
18 * USA.
19 */
20
Pierre Ossmana830bec2011-11-08 12:12:02 +000021#include <network/TcpSocket.h>
Pierre Ossman707fa122015-12-11 20:21:20 +010022
23#include <rfb/ComparingUpdateTracker.h>
24#include <rfb/Encoder.h>
25#include <rfb/KeyRemapper.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000026#include <rfb/LogWriter.h>
Adam Tkac5a0caed2010-04-23 13:58:10 +000027#include <rfb/Security.h>
Pierre Ossman707fa122015-12-11 20:21:20 +010028#include <rfb/ServerCore.h>
29#include <rfb/SMsgWriter.h>
30#include <rfb/VNCServerST.h>
31#include <rfb/VNCSConnectionST.h>
Pierre Ossmanc5e25602009-03-20 12:59:05 +000032#include <rfb/screenTypes.h>
Pierre Ossman2c764942011-11-14 15:54:30 +000033#include <rfb/fenceTypes.h>
Pierre Ossmanbb305ca2016-12-11 12:41:26 +010034#include <rfb/ledStates.h>
Pierre Ossmanbb305ca2016-12-11 12:41:26 +010035#define XK_LATIN1
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000036#define XK_MISCELLANY
37#define XK_XKB_KEYS
38#include <rfb/keysymdef.h>
39
40using namespace rfb;
41
42static LogWriter vlog("VNCSConnST");
43
Pierre Ossman71ca8d52017-09-15 11:03:12 +020044static Cursor emptyCursor(0, 0, Point(0, 0), NULL);
45
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000046VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
47 bool reverse)
Pierre Ossman7069bdd2015-02-06 14:41:58 +010048 : sock(s), reverseConnection(reverse),
Pierre Ossman36304752017-10-04 16:21:57 +020049 inProcessMessages(false),
Pierre Ossmanb8b1e962012-07-20 10:47:00 +000050 pendingSyncFence(false), syncFence(false), fenceFlags(0),
Pierre Ossmana99d14d2015-12-13 15:43:46 +010051 fenceDataLen(0), fenceData(NULL), congestionTimer(this),
Peter Åstrand (astrand)7a368c92018-09-19 12:45:17 +020052 losslessTimer(this), server(server_), updates(false),
Pierre Ossman671d2ef2015-06-09 16:09:06 +020053 updateRenderedCursor(false), removeRenderedCursor(false),
Pierre Ossman65e08fd2018-10-26 15:54:00 +020054 continuousUpdates(false), encodeManager(this), idleTimer(this),
55 pointerEventTime(0), clientHasCursor(false)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000056{
57 setStreams(&sock->inStream(), &sock->outStream());
58 peerEndpoint.buf = sock->getPeerEndpoint();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000059
60 // Configure the socket
61 setSocketTimeouts();
Pierre Ossman65e08fd2018-10-26 15:54:00 +020062
63 // Kick off the idle timer
64 if (rfb::Server::idleTimeout) {
65 // minimum of 15 seconds while authenticating
66 if (rfb::Server::idleTimeout < 15)
67 idleTimer.start(secsToMillis(15));
68 else
69 idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
70 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000071}
72
73
74VNCSConnectionST::~VNCSConnectionST()
75{
76 // If we reach here then VNCServerST is deleting us!
Pierre Ossman6c97fa42018-10-05 17:35:51 +020077 if (closeReason.buf)
78 vlog.info("closing %s: %s", peerEndpoint.buf, closeReason.buf);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000079
80 // Release any keys the client still had pressed
Pierre Ossman16e1dcb2017-05-16 14:33:43 +020081 while (!pressedKeys.empty()) {
82 rdr::U32 keysym, keycode;
83
84 keysym = pressedKeys.begin()->second;
85 keycode = pressedKeys.begin()->first;
86 pressedKeys.erase(pressedKeys.begin());
87
88 vlog.debug("Releasing key 0x%x / 0x%x on client disconnect",
89 keysym, keycode);
Pierre Ossmanb6843412018-10-05 17:30:52 +020090 server->keyEvent(keysym, keycode, false);
Pierre Ossman9a153b02015-08-31 10:01:14 +020091 }
Pierre Ossman16e1dcb2017-05-16 14:33:43 +020092
Pierre Ossman2c764942011-11-14 15:54:30 +000093 delete [] fenceData;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000094}
95
96
Pierre Ossman7d64b332018-10-08 15:59:02 +020097// SConnection methods
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000098
Pierre Ossman7d64b332018-10-08 15:59:02 +020099bool VNCSConnectionST::accessCheck(AccessRights ar) const
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000100{
Pierre Ossman7d64b332018-10-08 15:59:02 +0200101 // Reverse connections are user initiated, so they are implicitly
102 // allowed to bypass the query
103 if (reverseConnection)
104 ar &= ~AccessNoQuery;
105
106 return SConnection::accessCheck(ar);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000107}
108
109void VNCSConnectionST::close(const char* reason)
110{
111 // Log the reason for the close
112 if (!closeReason.buf)
Adam Tkacd36b6262009-09-04 10:57:20 +0000113 closeReason.buf = strDup(reason);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000114 else
115 vlog.debug("second close: %s (%s)", peerEndpoint.buf, reason);
116
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000117 // Just shutdown the socket and mark our state as closing. Eventually the
118 // calling code will call VNCServerST's removeSocket() method causing us to
119 // be deleted.
120 sock->shutdown();
Pierre Ossman7d64b332018-10-08 15:59:02 +0200121
122 SConnection::close(reason);
123}
124
125
126// Methods called from VNCServerST
127
128bool VNCSConnectionST::init()
129{
130 try {
131 initialiseProtocol();
132 } catch (rdr::Exception& e) {
133 close(e.str());
134 return false;
135 }
136 return true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000137}
138
139
140void VNCSConnectionST::processMessages()
141{
142 if (state() == RFBSTATE_CLOSING) return;
143 try {
144 // - Now set appropriate socket timeouts and process data
145 setSocketTimeouts();
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000146
147 inProcessMessages = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000148
Pierre Ossmana830bec2011-11-08 12:12:02 +0000149 // Get the underlying TCP layer to build large packets if we send
150 // multiple small responses.
Peter Åstrand (astrand)01dc1a62017-10-10 12:56:04 +0200151 sock->cork(true);
Pierre Ossmana830bec2011-11-08 12:12:02 +0000152
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000153 while (getInStream()->checkNoWait(1)) {
Pierre Ossmanb8b1e962012-07-20 10:47:00 +0000154 if (pendingSyncFence) {
155 syncFence = true;
156 pendingSyncFence = false;
157 }
158
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000159 processMsg();
Pierre Ossmanb8b1e962012-07-20 10:47:00 +0000160
Pierre Ossman2c764942011-11-14 15:54:30 +0000161 if (syncFence) {
162 writer()->writeFence(fenceFlags, fenceDataLen, fenceData);
163 syncFence = false;
164 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000165 }
166
Pierre Ossmana830bec2011-11-08 12:12:02 +0000167 // Flush out everything in case we go idle after this.
Peter Åstrand (astrand)01dc1a62017-10-10 12:56:04 +0200168 sock->cork(false);
Pierre Ossmana830bec2011-11-08 12:12:02 +0000169
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000170 inProcessMessages = false;
Constantin Kaplinsky2ef66952008-08-29 11:33:46 +0000171
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000172 // If there were anything requiring an update, try to send it here.
173 // We wait until now with this to aggregate responses and to give
174 // higher priority to user actions such as keyboard and pointer events.
175 writeFramebufferUpdate();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000176 } catch (rdr::EndOfStream&) {
177 close("Clean disconnection");
178 } catch (rdr::Exception &e) {
179 close(e.str());
180 }
181}
182
Pierre Ossmand408ca52016-04-29 14:26:05 +0200183void VNCSConnectionST::flushSocket()
184{
185 if (state() == RFBSTATE_CLOSING) return;
186 try {
187 setSocketTimeouts();
188 sock->outStream().flush();
Pierre Ossmana40ab202016-04-29 15:35:56 +0200189 // Flushing the socket might release an update that was previously
190 // delayed because of congestion.
191 if (sock->outStream().bufferUsage() == 0)
192 writeFramebufferUpdate();
Pierre Ossmand408ca52016-04-29 14:26:05 +0200193 } catch (rdr::Exception &e) {
194 close(e.str());
195 }
196}
197
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000198void VNCSConnectionST::pixelBufferChange()
199{
200 try {
201 if (!authenticated()) return;
Pierre Ossman6094ced2018-10-05 17:24:51 +0200202 if (cp.width && cp.height &&
203 (server->getPixelBuffer()->width() != cp.width ||
204 server->getPixelBuffer()->height() != cp.height))
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000205 {
206 // We need to clip the next update to the new size, but also add any
207 // extra bits if it's bigger. If we wanted to do this exactly, something
208 // like the code below would do it, but at the moment we just update the
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200209 // entire new size. However, we do need to clip the damagedCursorRegion
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000210 // because that might be added to updates in writeFramebufferUpdate().
211
212 //updates.intersect(server->pb->getRect());
213 //
214 //if (server->pb->width() > cp.width)
215 // updates.add_changed(Rect(cp.width, 0, server->pb->width(),
216 // server->pb->height()));
217 //if (server->pb->height() > cp.height)
218 // updates.add_changed(Rect(0, cp.height, cp.width,
219 // server->pb->height()));
220
Pierre Ossman6094ced2018-10-05 17:24:51 +0200221 damagedCursorRegion.assign_intersect(server->getPixelBuffer()->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000222
Pierre Ossman6094ced2018-10-05 17:24:51 +0200223 cp.width = server->getPixelBuffer()->width();
224 cp.height = server->getPixelBuffer()->height();
225 cp.screenLayout = server->getScreenLayout();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000226 if (state() == RFBSTATE_NORMAL) {
Pierre Ossman2ee430a2009-05-28 12:54:24 +0000227 // We should only send EDS to client asking for both
228 if (!writer()->writeExtendedDesktopSize()) {
229 if (!writer()->writeSetDesktopSize()) {
230 close("Client does not support desktop resize");
231 return;
232 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000233 }
234 }
Pierre Ossman6b2f1132016-11-30 08:03:35 +0100235
236 // Drop any lossy tracking that is now outside the framebuffer
Pierre Ossman6094ced2018-10-05 17:24:51 +0200237 encodeManager.pruneLosslessRefresh(Region(server->getPixelBuffer()->getRect()));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000238 }
239 // Just update the whole screen at the moment because we're too lazy to
240 // work out what's actually changed.
241 updates.clear();
Pierre Ossman6094ced2018-10-05 17:24:51 +0200242 updates.add_changed(server->getPixelBuffer()->getRect());
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000243 writeFramebufferUpdate();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000244 } catch(rdr::Exception &e) {
245 close(e.str());
246 }
247}
248
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000249void VNCSConnectionST::writeFramebufferUpdateOrClose()
Pierre Ossman04e62db2009-03-23 16:57:07 +0000250{
251 try {
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000252 writeFramebufferUpdate();
253 } catch(rdr::Exception &e) {
254 close(e.str());
255 }
256}
Pierre Ossman04e62db2009-03-23 16:57:07 +0000257
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000258void VNCSConnectionST::screenLayoutChangeOrClose(rdr::U16 reason)
259{
260 try {
261 screenLayoutChange(reason);
Pierre Ossmanb218ecd2017-11-16 16:43:13 +0100262 writeFramebufferUpdate();
Pierre Ossman04e62db2009-03-23 16:57:07 +0000263 } catch(rdr::Exception &e) {
264 close(e.str());
265 }
266}
267
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000268void VNCSConnectionST::bellOrClose()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000269{
270 try {
271 if (state() == RFBSTATE_NORMAL) writer()->writeBell();
272 } catch(rdr::Exception& e) {
273 close(e.str());
274 }
275}
276
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000277void VNCSConnectionST::serverCutTextOrClose(const char *str, int len)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000278{
279 try {
Pierre Ossman7d64b332018-10-08 15:59:02 +0200280 if (!accessCheck(AccessCutText)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000281 if (!rfb::Server::sendCutText) return;
282 if (state() == RFBSTATE_NORMAL)
283 writer()->writeServerCutText(str, len);
284 } catch(rdr::Exception& e) {
285 close(e.str());
286 }
287}
288
Peter Åstrandc39e0782009-01-15 12:21:42 +0000289
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000290void VNCSConnectionST::setDesktopNameOrClose(const char *name)
Peter Åstrandc39e0782009-01-15 12:21:42 +0000291{
Peter Åstrandc39e0782009-01-15 12:21:42 +0000292 try {
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000293 setDesktopName(name);
Pierre Ossmanb218ecd2017-11-16 16:43:13 +0100294 writeFramebufferUpdate();
Peter Åstrandc39e0782009-01-15 12:21:42 +0000295 } catch(rdr::Exception& e) {
296 close(e.str());
297 }
298}
299
300
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000301void VNCSConnectionST::setCursorOrClose()
302{
303 try {
304 setCursor();
Pierre Ossmanb218ecd2017-11-16 16:43:13 +0100305 writeFramebufferUpdate();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000306 } catch(rdr::Exception& e) {
307 close(e.str());
308 }
309}
310
311
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100312void VNCSConnectionST::setLEDStateOrClose(unsigned int state)
313{
314 try {
315 setLEDState(state);
Pierre Ossmanb218ecd2017-11-16 16:43:13 +0100316 writeFramebufferUpdate();
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100317 } catch(rdr::Exception& e) {
318 close(e.str());
319 }
320}
321
322
Pierre Ossmanb114cec2011-11-20 15:36:11 +0000323bool VNCSConnectionST::getComparerState()
324{
325 // We interpret a low compression level as an indication that the client
326 // wants to prioritise CPU usage over bandwidth, and hence disable the
327 // comparing update tracker.
328 return (cp.compressLevel == -1) || (cp.compressLevel > 1);
329}
330
331
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000332// renderedCursorChange() is called whenever the server-side rendered cursor
333// changes shape or position. It ensures that the next update will clean up
334// the old rendered cursor and if necessary draw the new rendered cursor.
335
336void VNCSConnectionST::renderedCursorChange()
337{
338 if (state() != RFBSTATE_NORMAL) return;
Pierre Ossman71ca8d52017-09-15 11:03:12 +0200339 // Are we switching between client-side and server-side cursor?
Pierre Ossman25db44a2017-11-16 16:40:44 +0100340 if (clientHasCursor == needRenderedCursor())
Pierre Ossman71ca8d52017-09-15 11:03:12 +0200341 setCursorOrClose();
Pierre Ossman25db44a2017-11-16 16:40:44 +0100342 bool hasRenderedCursor = !damagedCursorRegion.is_empty();
Pierre Ossman330ca422017-11-06 13:15:55 +0100343 if (hasRenderedCursor)
Pierre Ossman5c9e1e52011-11-08 10:32:05 +0000344 removeRenderedCursor = true;
Pierre Ossman6b0bc292011-12-21 13:17:54 +0000345 if (needRenderedCursor()) {
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200346 updateRenderedCursor = true;
Pierre Ossman6b0bc292011-12-21 13:17:54 +0000347 writeFramebufferUpdateOrClose();
348 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000349}
350
351// needRenderedCursor() returns true if this client needs the server-side
352// rendered cursor. This may be because it does not support local cursor or
353// because the current cursor position has not been set by this client.
354// Unfortunately we can't know for sure when the current cursor position has
355// been set by this client. We guess that this is the case when the current
356// cursor position is the same as the last pointer event from this client, or
357// if it is a very short time since this client's last pointer event (up to a
358// second). [ Ideally we should do finer-grained timing here and make the time
359// configurable, but I don't think it's that important. ]
360
361bool VNCSConnectionST::needRenderedCursor()
362{
Pierre Ossman77ede0a2016-12-05 16:57:30 +0100363 if (state() != RFBSTATE_NORMAL)
364 return false;
365
Pierre Ossman324043e2017-08-16 16:26:11 +0200366 if (!cp.supportsLocalCursorWithAlpha &&
367 !cp.supportsLocalCursor && !cp.supportsLocalXCursor)
Pierre Ossman77ede0a2016-12-05 16:57:30 +0100368 return true;
Pierre Ossman6094ced2018-10-05 17:24:51 +0200369 if (!server->getCursorPos().equals(pointerEventPos) &&
Pierre Ossman77ede0a2016-12-05 16:57:30 +0100370 (time(0) - pointerEventTime) > 0)
371 return true;
372
373 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000374}
375
376
377void VNCSConnectionST::approveConnectionOrClose(bool accept,
378 const char* reason)
379{
380 try {
381 approveConnection(accept, reason);
382 } catch (rdr::Exception& e) {
383 close(e.str());
384 }
385}
386
387
388
389// -=- Callbacks from SConnection
390
391void VNCSConnectionST::authSuccess()
392{
Pierre Ossman65e08fd2018-10-26 15:54:00 +0200393 if (rfb::Server::idleTimeout)
394 idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000395
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000396 // - Set the connection parameters appropriately
Pierre Ossman6094ced2018-10-05 17:24:51 +0200397 cp.width = server->getPixelBuffer()->width();
398 cp.height = server->getPixelBuffer()->height();
399 cp.screenLayout = server->getScreenLayout();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000400 cp.setName(server->getName());
Pierre Ossman6094ced2018-10-05 17:24:51 +0200401 cp.setLEDState(server->getLEDState());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000402
403 // - Set the default pixel format
Pierre Ossman6094ced2018-10-05 17:24:51 +0200404 cp.setPF(server->getPixelBuffer()->getPF());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000405 char buffer[256];
406 cp.pf().print(buffer, 256);
407 vlog.info("Server default pixel format %s", buffer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000408
409 // - Mark the entire display as "dirty"
Pierre Ossman6094ced2018-10-05 17:24:51 +0200410 updates.add_changed(server->getPixelBuffer()->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000411}
412
413void VNCSConnectionST::queryConnection(const char* userName)
414{
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200415 server->queryConnection(this, userName);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000416}
417
418void VNCSConnectionST::clientInit(bool shared)
419{
Pierre Ossman65e08fd2018-10-26 15:54:00 +0200420 if (rfb::Server::idleTimeout)
421 idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000422 if (rfb::Server::alwaysShared || reverseConnection) shared = true;
Pierre Ossman7d64b332018-10-08 15:59:02 +0200423 if (!accessCheck(AccessNonShared)) shared = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000424 if (rfb::Server::neverShared) shared = false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000425 SConnection::clientInit(shared);
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200426 server->clientReady(this, shared);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000427}
428
429void VNCSConnectionST::setPixelFormat(const PixelFormat& pf)
430{
431 SConnection::setPixelFormat(pf);
432 char buffer[256];
433 pf.print(buffer, 256);
434 vlog.info("Client pixel format %s", buffer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000435 setCursor();
436}
437
438void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask)
439{
Pierre Ossman65e08fd2018-10-26 15:54:00 +0200440 if (rfb::Server::idleTimeout)
441 idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
442 pointerEventTime = time(0);
Pierre Ossman7d64b332018-10-08 15:59:02 +0200443 if (!accessCheck(AccessPtrEvents)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000444 if (!rfb::Server::acceptPointerEvents) return;
Pierre Ossmanb6843412018-10-05 17:30:52 +0200445 pointerEventPos = pos;
446 server->pointerEvent(this, pointerEventPos, buttonMask);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000447}
448
449
450class VNCSConnectionSTShiftPresser {
451public:
Pierre Ossmanb6843412018-10-05 17:30:52 +0200452 VNCSConnectionSTShiftPresser(VNCServerST* server_)
453 : server(server_), pressed(false) {}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000454 ~VNCSConnectionSTShiftPresser() {
Pierre Ossman9a153b02015-08-31 10:01:14 +0200455 if (pressed) {
456 vlog.debug("Releasing fake Shift_L");
Pierre Ossmanb6843412018-10-05 17:30:52 +0200457 server->keyEvent(XK_Shift_L, 0, false);
Pierre Ossman9a153b02015-08-31 10:01:14 +0200458 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000459 }
460 void press() {
Pierre Ossman9a153b02015-08-31 10:01:14 +0200461 vlog.debug("Pressing fake Shift_L");
Pierre Ossmanb6843412018-10-05 17:30:52 +0200462 server->keyEvent(XK_Shift_L, 0, true);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000463 pressed = true;
464 }
Pierre Ossmanb6843412018-10-05 17:30:52 +0200465 VNCServerST* server;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000466 bool pressed;
467};
468
469// keyEvent() - record in the pressedKeys which keys were pressed. Allow
470// multiple down events (for autorepeat), but only allow a single up event.
Pierre Ossman5ae28212017-05-16 14:30:38 +0200471void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) {
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200472 rdr::U32 lookup;
473
Pierre Ossman65e08fd2018-10-26 15:54:00 +0200474 if (rfb::Server::idleTimeout)
475 idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
Pierre Ossman7d64b332018-10-08 15:59:02 +0200476 if (!accessCheck(AccessKeyEvents)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000477 if (!rfb::Server::acceptKeyEvents) return;
478
Pierre Ossman9a153b02015-08-31 10:01:14 +0200479 if (down)
Pierre Ossman5ae28212017-05-16 14:30:38 +0200480 vlog.debug("Key pressed: 0x%x / 0x%x", keysym, keycode);
Pierre Ossman9a153b02015-08-31 10:01:14 +0200481 else
Pierre Ossman5ae28212017-05-16 14:30:38 +0200482 vlog.debug("Key released: 0x%x / 0x%x", keysym, keycode);
Pierre Ossman9a153b02015-08-31 10:01:14 +0200483
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100484 // Avoid lock keys if we don't know the server state
Pierre Ossman6094ced2018-10-05 17:24:51 +0200485 if ((server->getLEDState() == ledUnknown) &&
Pierre Ossman5ae28212017-05-16 14:30:38 +0200486 ((keysym == XK_Caps_Lock) ||
487 (keysym == XK_Num_Lock) ||
488 (keysym == XK_Scroll_Lock))) {
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100489 vlog.debug("Ignoring lock key (e.g. caps lock)");
490 return;
491 }
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100492
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100493 // Lock key heuristics
494 // (only for clients that do not support the LED state extension)
495 if (!cp.supportsLEDState) {
496 // Always ignore ScrollLock as we don't have a heuristic
497 // for that
Pierre Ossman5ae28212017-05-16 14:30:38 +0200498 if (keysym == XK_Scroll_Lock) {
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100499 vlog.debug("Ignoring lock key (e.g. caps lock)");
500 return;
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100501 }
502
Pierre Ossman6094ced2018-10-05 17:24:51 +0200503 if (down && (server->getLEDState() != ledUnknown)) {
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100504 // CapsLock synchronisation heuristic
505 // (this assumes standard interaction between CapsLock the Shift
506 // keys and normal characters)
Pierre Ossman5ae28212017-05-16 14:30:38 +0200507 if (((keysym >= XK_A) && (keysym <= XK_Z)) ||
508 ((keysym >= XK_a) && (keysym <= XK_z))) {
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100509 bool uppercase, shift, lock;
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100510
Pierre Ossman5ae28212017-05-16 14:30:38 +0200511 uppercase = (keysym >= XK_A) && (keysym <= XK_Z);
Pierre Ossman851e6802017-09-12 16:44:44 +0200512 shift = isShiftPressed();
Pierre Ossman6094ced2018-10-05 17:24:51 +0200513 lock = server->getLEDState() & ledCapsLock;
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100514
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100515 if (lock == (uppercase == shift)) {
516 vlog.debug("Inserting fake CapsLock to get in sync with client");
Pierre Ossmanb6843412018-10-05 17:30:52 +0200517 server->keyEvent(XK_Caps_Lock, 0, true);
518 server->keyEvent(XK_Caps_Lock, 0, false);
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100519 }
520 }
521
522 // NumLock synchronisation heuristic
523 // (this is more cautious because of the differences between Unix,
524 // Windows and macOS)
Pierre Ossman5ae28212017-05-16 14:30:38 +0200525 if (((keysym >= XK_KP_Home) && (keysym <= XK_KP_Delete)) ||
526 ((keysym >= XK_KP_0) && (keysym <= XK_KP_9)) ||
527 (keysym == XK_KP_Separator) || (keysym == XK_KP_Decimal)) {
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100528 bool number, shift, lock;
529
Pierre Ossman5ae28212017-05-16 14:30:38 +0200530 number = ((keysym >= XK_KP_0) && (keysym <= XK_KP_9)) ||
531 (keysym == XK_KP_Separator) || (keysym == XK_KP_Decimal);
Pierre Ossman851e6802017-09-12 16:44:44 +0200532 shift = isShiftPressed();
Pierre Ossman6094ced2018-10-05 17:24:51 +0200533 lock = server->getLEDState() & ledNumLock;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100534
535 if (shift) {
536 // We don't know the appropriate NumLock state for when Shift
537 // is pressed as it could be one of:
538 //
539 // a) A Unix client where Shift negates NumLock
540 //
541 // b) A Windows client where Shift only cancels NumLock
542 //
543 // c) A macOS client where Shift doesn't have any effect
544 //
545 } else if (lock == (number == shift)) {
546 vlog.debug("Inserting fake NumLock to get in sync with client");
Pierre Ossmanb6843412018-10-05 17:30:52 +0200547 server->keyEvent(XK_Num_Lock, 0, true);
548 server->keyEvent(XK_Num_Lock, 0, false);
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100549 }
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100550 }
Pierre Ossman9a153b02015-08-31 10:01:14 +0200551 }
552 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000553
554 // Turn ISO_Left_Tab into shifted Tab.
Pierre Ossmanb6843412018-10-05 17:30:52 +0200555 VNCSConnectionSTShiftPresser shiftPresser(server);
Pierre Ossman5ae28212017-05-16 14:30:38 +0200556 if (keysym == XK_ISO_Left_Tab) {
Pierre Ossman851e6802017-09-12 16:44:44 +0200557 if (!isShiftPressed())
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000558 shiftPresser.press();
Pierre Ossman5ae28212017-05-16 14:30:38 +0200559 keysym = XK_Tab;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000560 }
561
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200562 // We need to be able to track keys, so generate a fake index when we
563 // aren't given a keycode
564 if (keycode == 0)
565 lookup = 0x80000000 | keysym;
566 else
567 lookup = keycode;
568
569 // We force the same keysym for an already down key for the
570 // sake of sanity
571 if (pressedKeys.find(lookup) != pressedKeys.end())
572 keysym = pressedKeys[lookup];
573
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000574 if (down) {
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200575 pressedKeys[lookup] = keysym;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000576 } else {
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200577 if (!pressedKeys.erase(lookup))
Pierre Ossman5ae28212017-05-16 14:30:38 +0200578 return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000579 }
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200580
Pierre Ossmanb6843412018-10-05 17:30:52 +0200581 server->keyEvent(keysym, keycode, down);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000582}
583
584void VNCSConnectionST::clientCutText(const char* str, int len)
585{
Pierre Ossman7d64b332018-10-08 15:59:02 +0200586 if (!accessCheck(AccessCutText)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000587 if (!rfb::Server::acceptCutText) return;
Pierre Ossmanb6843412018-10-05 17:30:52 +0200588 server->clientCutText(str, len);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000589}
590
591void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
592{
Pierre Ossmane9962f72009-04-23 12:31:42 +0000593 Rect safeRect;
594
Pierre Ossman7d64b332018-10-08 15:59:02 +0200595 if (!accessCheck(AccessView)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000596
597 SConnection::framebufferUpdateRequest(r, incremental);
598
Pierre Ossmand9a59ba2009-03-20 15:55:37 +0000599 // Check that the client isn't sending crappy requests
600 if (!r.enclosed_by(Rect(0, 0, cp.width, cp.height))) {
601 vlog.error("FramebufferUpdateRequest %dx%d at %d,%d exceeds framebuffer %dx%d",
602 r.width(), r.height(), r.tl.x, r.tl.y, cp.width, cp.height);
Pierre Ossmane9962f72009-04-23 12:31:42 +0000603 safeRect = r.intersect(Rect(0, 0, cp.width, cp.height));
604 } else {
605 safeRect = r;
Pierre Ossmand9a59ba2009-03-20 15:55:37 +0000606 }
607
Constantin Kaplinsky2ef66952008-08-29 11:33:46 +0000608 // Just update the requested region.
609 // Framebuffer update will be sent a bit later, see processMessages().
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000610 Region reqRgn(r);
Pierre Ossman1b478e52011-11-15 12:08:30 +0000611 if (!incremental || !continuousUpdates)
612 requested.assign_union(reqRgn);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000613
614 if (!incremental) {
615 // Non-incremental update - treat as if area requested has changed
616 updates.add_changed(reqRgn);
Pierre Ossman53125a72009-04-22 15:37:51 +0000617
618 // And send the screen layout to the client (which, unlike the
619 // framebuffer dimensions, the client doesn't get during init)
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000620 writer()->writeExtendedDesktopSize();
Pierre Ossman53125a72009-04-22 15:37:51 +0000621
622 // We do not send a DesktopSize since it only contains the
623 // framebuffer size (which the client already should know) and
624 // because some clients don't handle extra DesktopSize events
625 // very well.
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000626 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000627}
628
Pierre Ossman34bb0612009-03-21 21:16:14 +0000629void VNCSConnectionST::setDesktopSize(int fb_width, int fb_height,
630 const ScreenSet& layout)
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000631{
Pierre Ossman04e62db2009-03-23 16:57:07 +0000632 unsigned int result;
633
Pierre Ossman7d64b332018-10-08 15:59:02 +0200634 if (!accessCheck(AccessSetDesktopSize)) return;
Michal Srbb318b8f2014-11-24 13:18:28 +0200635 if (!rfb::Server::acceptSetDesktopSize) return;
636
Pierre Ossman07e44cc2018-10-05 17:32:57 +0200637 result = server->setDesktopSize(this, fb_width, fb_height, layout);
Pierre Ossman04e62db2009-03-23 16:57:07 +0000638 writer()->writeExtendedDesktopSize(reasonClient, result,
639 fb_width, fb_height, layout);
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000640}
641
Pierre Ossman2c764942011-11-14 15:54:30 +0000642void VNCSConnectionST::fence(rdr::U32 flags, unsigned len, const char data[])
643{
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100644 rdr::U8 type;
645
Pierre Ossman2c764942011-11-14 15:54:30 +0000646 if (flags & fenceFlagRequest) {
647 if (flags & fenceFlagSyncNext) {
Pierre Ossmanb8b1e962012-07-20 10:47:00 +0000648 pendingSyncFence = true;
Pierre Ossman2c764942011-11-14 15:54:30 +0000649
650 fenceFlags = flags & (fenceFlagBlockBefore | fenceFlagBlockAfter | fenceFlagSyncNext);
651 fenceDataLen = len;
652 delete [] fenceData;
Michal Srbf3afa242017-03-27 19:02:15 +0300653 fenceData = NULL;
Pierre Ossman2c764942011-11-14 15:54:30 +0000654 if (len > 0) {
655 fenceData = new char[len];
656 memcpy(fenceData, data, len);
657 }
658
659 return;
660 }
661
662 // We handle everything synchronously so we trivially honor these modes
663 flags = flags & (fenceFlagBlockBefore | fenceFlagBlockAfter);
664
665 writer()->writeFence(flags, len, data);
666 return;
667 }
668
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100669 if (len < 1)
670 vlog.error("Fence response of unexpected size received");
Pierre Ossman1b478e52011-11-15 12:08:30 +0000671
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100672 type = data[0];
673
674 switch (type) {
Pierre Ossman2c764942011-11-14 15:54:30 +0000675 case 0:
676 // Initial dummy fence;
677 break;
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100678 case 1:
Pierre Ossmanc09e5582015-12-11 20:23:17 +0100679 congestion.gotPong();
Pierre Ossman1b478e52011-11-15 12:08:30 +0000680 break;
Pierre Ossman2c764942011-11-14 15:54:30 +0000681 default:
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100682 vlog.error("Fence response of unexpected type received");
Pierre Ossman2c764942011-11-14 15:54:30 +0000683 }
684}
685
Pierre Ossman1b478e52011-11-15 12:08:30 +0000686void VNCSConnectionST::enableContinuousUpdates(bool enable,
687 int x, int y, int w, int h)
688{
689 Rect rect;
690
691 if (!cp.supportsFence || !cp.supportsContinuousUpdates)
692 throw Exception("Client tried to enable continuous updates when not allowed");
693
694 continuousUpdates = enable;
695
696 rect.setXYWH(x, y, w, h);
697 cuRegion.reset(rect);
698
699 if (enable) {
700 requested.clear();
Pierre Ossman1b478e52011-11-15 12:08:30 +0000701 } else {
702 writer()->writeEndOfContinuousUpdates();
703 }
704}
705
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000706// supportsLocalCursor() is called whenever the status of
707// cp.supportsLocalCursor has changed. If the client does now support local
708// cursor, we make sure that the old server-side rendered cursor is cleaned up
709// and the cursor is sent to the client.
710
711void VNCSConnectionST::supportsLocalCursor()
712{
Pierre Ossman387a4172017-11-16 16:44:36 +0100713 bool hasRenderedCursor = !damagedCursorRegion.is_empty();
714 if (hasRenderedCursor && !needRenderedCursor())
715 removeRenderedCursor = true;
716 setCursor();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000717}
718
Pierre Ossman2c764942011-11-14 15:54:30 +0000719void VNCSConnectionST::supportsFence()
720{
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100721 char type = 0;
722 writer()->writeFence(fenceFlagRequest, sizeof(type), &type);
Pierre Ossman2c764942011-11-14 15:54:30 +0000723}
724
Pierre Ossman1b478e52011-11-15 12:08:30 +0000725void VNCSConnectionST::supportsContinuousUpdates()
726{
727 // We refuse to use continuous updates if we cannot monitor the buffer
728 // usage using fences.
729 if (!cp.supportsFence)
730 return;
731
732 writer()->writeEndOfContinuousUpdates();
733}
734
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100735void VNCSConnectionST::supportsLEDState()
736{
737 writer()->writeLEDState();
738}
739
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000740
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000741bool VNCSConnectionST::handleTimeout(Timer* t)
742{
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000743 try {
Peter Åstrand (astrand)7a368c92018-09-19 12:45:17 +0200744 if ((t == &congestionTimer) ||
745 (t == &losslessTimer))
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100746 writeFramebufferUpdate();
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000747 } catch (rdr::Exception& e) {
748 close(e.str());
749 }
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000750
Pierre Ossman65e08fd2018-10-26 15:54:00 +0200751 if (t == &idleTimer)
752 close("Idle timeout");
753
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000754 return false;
755}
756
Pierre Ossman851e6802017-09-12 16:44:44 +0200757bool VNCSConnectionST::isShiftPressed()
758{
759 std::map<rdr::U32, rdr::U32>::const_iterator iter;
760
761 for (iter = pressedKeys.begin(); iter != pressedKeys.end(); ++iter) {
762 if (iter->second == XK_Shift_L)
763 return true;
764 if (iter->second == XK_Shift_R)
765 return true;
766 }
767
768 return false;
769}
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000770
Pierre Ossman1b478e52011-11-15 12:08:30 +0000771void VNCSConnectionST::writeRTTPing()
772{
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100773 char type;
Pierre Ossman1b478e52011-11-15 12:08:30 +0000774
775 if (!cp.supportsFence)
776 return;
777
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100778 congestion.updatePosition(sock->outStream().length());
Pierre Ossman1b478e52011-11-15 12:08:30 +0000779
780 // We need to make sure any old update are already processed by the
781 // time we get the response back. This allows us to reliably throttle
782 // back on client overload, as well as network overload.
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100783 type = 1;
Pierre Ossman1b478e52011-11-15 12:08:30 +0000784 writer()->writeFence(fenceFlagRequest | fenceFlagBlockBefore,
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100785 sizeof(type), &type);
Pierre Ossman1b478e52011-11-15 12:08:30 +0000786
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100787 congestion.sentPing();
Pierre Ossman1b478e52011-11-15 12:08:30 +0000788}
789
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000790bool VNCSConnectionST::isCongested()
791{
Pierre Ossmance261812018-07-17 15:01:53 +0200792 int eta;
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100793
794 congestionTimer.stop();
Pierre Ossman1b478e52011-11-15 12:08:30 +0000795
796 // Stuff still waiting in the send buffer?
Pierre Ossman352d0622016-04-29 15:50:54 +0200797 sock->outStream().flush();
Pierre Ossman8cf71632016-03-11 09:38:08 +0100798 congestion.debugTrace("congestion-trace.csv", sock->getFd());
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000799 if (sock->outStream().bufferUsage() > 0)
800 return true;
801
Pierre Ossman1b478e52011-11-15 12:08:30 +0000802 if (!cp.supportsFence)
803 return false;
804
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100805 congestion.updatePosition(sock->outStream().length());
806 if (!congestion.isCongested())
Pierre Ossman1b478e52011-11-15 12:08:30 +0000807 return false;
808
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100809 eta = congestion.getUncongestedETA();
810 if (eta >= 0)
811 congestionTimer.start(eta);
Pierre Ossman1b478e52011-11-15 12:08:30 +0000812
813 return true;
814}
815
816
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000817void VNCSConnectionST::writeFramebufferUpdate()
818{
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100819 congestion.updatePosition(sock->outStream().length());
820
Pierre Ossman2c764942011-11-14 15:54:30 +0000821 // We're in the middle of processing a command that's supposed to be
822 // synchronised. Allowing an update to slip out right now might violate
823 // that synchronisation.
824 if (syncFence)
825 return;
826
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000827 // We try to aggregate responses, so don't send out anything whilst we
828 // still have incoming messages. processMessages() will give us another
829 // chance to run once things are idle.
830 if (inProcessMessages)
831 return;
832
Pierre Ossman1b478e52011-11-15 12:08:30 +0000833 if (state() != RFBSTATE_NORMAL)
834 return;
835 if (requested.is_empty() && !continuousUpdates)
Pierre Ossmane9962f72009-04-23 12:31:42 +0000836 return;
Pierre Ossmand9a59ba2009-03-20 15:55:37 +0000837
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000838 // Check that we actually have some space on the link and retry in a
839 // bit if things are congested.
Pierre Ossmana40ab202016-04-29 15:35:56 +0200840 if (isCongested())
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000841 return;
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000842
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100843 // Updates often consists of many small writes, and in continuous
844 // mode, we will also have small fence messages around the update. We
845 // need to aggregate these in order to not clog up TCP's congestion
846 // window.
Peter Åstrand (astrand)01dc1a62017-10-10 12:56:04 +0200847 sock->cork(true);
Pierre Ossman36dadf82011-11-15 12:11:32 +0000848
Pierre Ossmane9962f72009-04-23 12:31:42 +0000849 // First take care of any updates that cannot contain framebuffer data
850 // changes.
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100851 writeNoDataUpdate();
852
853 // Then real data (if possible)
854 writeDataUpdate();
855
Peter Åstrand (astrand)01dc1a62017-10-10 12:56:04 +0200856 sock->cork(false);
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100857
858 congestion.updatePosition(sock->outStream().length());
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100859}
860
861void VNCSConnectionST::writeNoDataUpdate()
862{
863 if (!writer()->needNoDataUpdate())
864 return;
865
866 writer()->writeNoDataUpdate();
867
868 // Make sure no data update is sent until next request
869 requested.clear();
870}
871
872void VNCSConnectionST::writeDataUpdate()
873{
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100874 Region req;
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100875 UpdateInfo ui;
876 bool needNewUpdateInfo;
Pierre Ossman24684e52016-12-05 16:58:19 +0100877 const RenderedCursor *cursor;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000878
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000879 updates.enable_copyrect(cp.useCopyRect);
880
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100881 // See what the client has requested (if anything)
Pierre Ossman1b478e52011-11-15 12:08:30 +0000882 if (continuousUpdates)
883 req = cuRegion.union_(requested);
884 else
885 req = requested;
886
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100887 if (req.is_empty())
888 return;
889
890 // Get the lists of updates. Prior to exporting the data to the `ui' object,
891 // getUpdateInfo() will normalize the `updates' object such way that its
892 // `changed' and `copied' regions would not intersect.
Pierre Ossman1b478e52011-11-15 12:08:30 +0000893 updates.getUpdateInfo(&ui, req);
894 needNewUpdateInfo = false;
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000895
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000896 // If the previous position of the rendered cursor overlaps the source of the
897 // copy, then when the copy happens the corresponding rectangle in the
898 // destination will be wrong, so add it to the changed region.
899
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200900 if (!ui.copied.is_empty() && !damagedCursorRegion.is_empty()) {
901 Region bogusCopiedCursor;
902
Pierre Ossman74385d32018-03-22 16:00:18 +0100903 bogusCopiedCursor = damagedCursorRegion;
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200904 bogusCopiedCursor.translate(ui.copy_delta);
Pierre Ossman6094ced2018-10-05 17:24:51 +0200905 bogusCopiedCursor.assign_intersect(server->getPixelBuffer()->getRect());
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000906 if (!ui.copied.intersect(bogusCopiedCursor).is_empty()) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000907 updates.add_changed(bogusCopiedCursor);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000908 needNewUpdateInfo = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000909 }
910 }
911
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200912 // If we need to remove the old rendered cursor, just add the region to
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000913 // the changed region.
914
915 if (removeRenderedCursor) {
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200916 updates.add_changed(damagedCursorRegion);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000917 needNewUpdateInfo = true;
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200918 damagedCursorRegion.clear();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000919 removeRenderedCursor = false;
920 }
921
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100922 // If we need a full cursor update then make sure its entire region
923 // is marked as changed.
924
925 if (updateRenderedCursor) {
926 updates.add_changed(server->getRenderedCursor()->getEffectiveRect());
927 needNewUpdateInfo = true;
928 updateRenderedCursor = false;
929 }
930
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000931 // The `updates' object could change, make sure we have valid update info.
932
933 if (needNewUpdateInfo)
Pierre Ossman1b478e52011-11-15 12:08:30 +0000934 updates.getUpdateInfo(&ui, req);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000935
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100936 // If there are queued updates then we cannot safely send an update
937 // without risking a partially updated screen
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100938 if (!server->getPendingRegion().is_empty()) {
939 req.clear();
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100940 ui.changed.clear();
941 ui.copied.clear();
942 }
943
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100944 // Does the client need a server-side rendered cursor?
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000945
Pierre Ossman24684e52016-12-05 16:58:19 +0100946 cursor = NULL;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000947 if (needRenderedCursor()) {
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200948 Rect renderedCursorRect;
949
Pierre Ossman24684e52016-12-05 16:58:19 +0100950 cursor = server->getRenderedCursor();
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100951 renderedCursorRect = cursor->getEffectiveRect();
Pierre Ossman24684e52016-12-05 16:58:19 +0100952
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100953 // Check that we don't try to copy over the cursor area, and
954 // if that happens we need to treat it as changed so that we can
955 // re-render it
956 if (!ui.copied.intersect(renderedCursorRect).is_empty()) {
957 ui.changed.assign_union(ui.copied.intersect(renderedCursorRect));
958 ui.copied.assign_subtract(renderedCursorRect);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000959 }
960
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100961 // Track where we've rendered the cursor
962 damagedCursorRegion.assign_union(ui.changed.intersect(renderedCursorRect));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000963 }
964
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100965 // If we don't have a normal update, then try a lossless refresh
Peter Åstrand (astrand)7a368c92018-09-19 12:45:17 +0200966 if (ui.is_empty() && !writer()->needFakeUpdate()) {
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100967 writeLosslessRefresh();
968 return;
Peter Åstrand (astrand)7a368c92018-09-19 12:45:17 +0200969 }
Pierre Ossmanfdba3fe2014-01-31 13:12:18 +0100970
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100971 // We have something to send, so let's get to it
972
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100973 writeRTTPing();
Pierre Ossman1b478e52011-11-15 12:08:30 +0000974
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100975 encodeManager.writeUpdate(ui, server->getPixelBuffer(), cursor);
Pierre Ossman1b478e52011-11-15 12:08:30 +0000976
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100977 writeRTTPing();
Pierre Ossmanc0397262014-03-14 15:59:46 +0100978
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100979 // The request might be for just part of the screen, so we cannot
980 // just clear the entire update tracker.
981 updates.subtract(req);
Pierre Ossmanc0397262014-03-14 15:59:46 +0100982
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100983 requested.clear();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000984}
985
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100986void VNCSConnectionST::writeLosslessRefresh()
987{
988 Region req, pending;
989 const RenderedCursor *cursor;
990
991 int nextRefresh, nextUpdate;
Pierre Ossman812da462018-11-23 17:48:02 +0100992 size_t bandwidth, maxUpdateSize;
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100993
994 if (continuousUpdates)
995 req = cuRegion.union_(requested);
996 else
997 req = requested;
998
999 // If there are queued updates then we could not safely send an
1000 // update without risking a partially updated screen, however we
1001 // might still be able to send a lossless refresh
1002 pending = server->getPendingRegion();
1003 if (!pending.is_empty()) {
1004 UpdateInfo ui;
1005
1006 // Don't touch the updates pending in the server core
1007 req.assign_subtract(pending);
1008
1009 // Or any updates pending just for this connection
1010 updates.getUpdateInfo(&ui, req);
1011 req.assign_subtract(ui.changed);
1012 req.assign_subtract(ui.copied);
1013 }
1014
1015 // Any lossy area we can refresh?
1016 if (!encodeManager.needsLosslessRefresh(req))
1017 return;
1018
1019 // Right away? Or later?
1020 nextRefresh = encodeManager.getNextLosslessRefresh(req);
1021 if (nextRefresh > 0) {
1022 losslessTimer.start(nextRefresh);
1023 return;
1024 }
1025
1026 // Prepare the cursor in case it overlaps with a region getting
1027 // refreshed
1028 cursor = NULL;
1029 if (needRenderedCursor())
1030 cursor = server->getRenderedCursor();
1031
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001032 // FIXME: If continuous updates aren't used then the client might
1033 // be slower than frameRate in its requests and we could
1034 // afford a larger update size
1035 nextUpdate = server->msToNextUpdate();
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001036
Pierre Ossman812da462018-11-23 17:48:02 +01001037 // Don't bother if we're about to send a real update
1038 if (nextUpdate == 0)
1039 return;
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001040
Pierre Ossman812da462018-11-23 17:48:02 +01001041 // FIXME: Bandwidth estimation without congestion control
1042 bandwidth = congestion.getBandwidth();
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001043
Pierre Ossman812da462018-11-23 17:48:02 +01001044 // FIXME: Hard coded value for maximum CPU throughput
1045 if (bandwidth > 5000000)
1046 bandwidth = 5000000;
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001047
Pierre Ossman812da462018-11-23 17:48:02 +01001048 maxUpdateSize = bandwidth * nextUpdate / 1000;
1049
1050 writeRTTPing();
1051
1052 encodeManager.writeLosslessRefresh(req, server->getPixelBuffer(),
1053 cursor, maxUpdateSize);
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001054
1055 writeRTTPing();
1056
1057 requested.clear();
1058}
1059
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001060
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001061void VNCSConnectionST::screenLayoutChange(rdr::U16 reason)
1062{
1063 if (!authenticated())
1064 return;
1065
Pierre Ossman6094ced2018-10-05 17:24:51 +02001066 cp.screenLayout = server->getScreenLayout();
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001067
1068 if (state() != RFBSTATE_NORMAL)
1069 return;
1070
1071 writer()->writeExtendedDesktopSize(reason, 0, cp.width, cp.height,
1072 cp.screenLayout);
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001073}
1074
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001075
1076// setCursor() is called whenever the cursor has changed shape or pixel format.
1077// If the client supports local cursor then it will arrange for the cursor to
1078// be sent to the client.
1079
1080void VNCSConnectionST::setCursor()
1081{
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001082 if (state() != RFBSTATE_NORMAL)
1083 return;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001084
Pierre Ossman71ca8d52017-09-15 11:03:12 +02001085 // We need to blank out the client's cursor or there will be two
Pierre Ossman25db44a2017-11-16 16:40:44 +01001086 if (needRenderedCursor()) {
Pierre Ossman71ca8d52017-09-15 11:03:12 +02001087 cp.setCursor(emptyCursor);
Pierre Ossman25db44a2017-11-16 16:40:44 +01001088 clientHasCursor = false;
1089 } else {
Pierre Ossman6094ced2018-10-05 17:24:51 +02001090 cp.setCursor(*server->getCursor());
Pierre Ossman25db44a2017-11-16 16:40:44 +01001091 clientHasCursor = true;
1092 }
Pierre Ossman126e5642014-02-13 14:40:25 +01001093
Pierre Ossman8053c8e2017-02-21 12:59:04 +01001094 if (!writer()->writeSetCursorWithAlpha()) {
1095 if (!writer()->writeSetCursor()) {
1096 if (!writer()->writeSetXCursor()) {
1097 // No client support
1098 return;
1099 }
Pierre Ossman126e5642014-02-13 14:40:25 +01001100 }
1101 }
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001102}
1103
1104void VNCSConnectionST::setDesktopName(const char *name)
1105{
1106 cp.setName(name);
1107
1108 if (state() != RFBSTATE_NORMAL)
1109 return;
1110
1111 if (!writer()->writeSetDesktopName()) {
1112 fprintf(stderr, "Client does not support desktop rename\n");
1113 return;
1114 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001115}
1116
Pierre Ossmanb45a84f2016-12-12 16:59:15 +01001117void VNCSConnectionST::setLEDState(unsigned int ledstate)
1118{
1119 if (state() != RFBSTATE_NORMAL)
1120 return;
1121
1122 cp.setLEDState(ledstate);
1123
Pierre Ossmanb218ecd2017-11-16 16:43:13 +01001124 writer()->writeLEDState();
Pierre Ossmanb45a84f2016-12-12 16:59:15 +01001125}
1126
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001127void VNCSConnectionST::setSocketTimeouts()
1128{
1129 int timeoutms = rfb::Server::clientWaitTimeMillis;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001130 if (timeoutms == 0)
1131 timeoutms = -1;
1132 sock->inStream().setTimeout(timeoutms);
1133 sock->outStream().setTimeout(timeoutms);
1134}