blob: 3b4448b41ebd96059384e6bc21d095315f1fefef [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossmanc5e25602009-03-20 12:59:05 +00002 * Copyright 2009 Pierre Ossman for Cendio AB
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00003 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19
20#include <rfb/VNCSConnectionST.h>
21#include <rfb/LogWriter.h>
22#include <rfb/secTypes.h>
Pierre Ossmanc5e25602009-03-20 12:59:05 +000023#include <rfb/screenTypes.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000024#include <rfb/ServerCore.h>
25#include <rfb/ComparingUpdateTracker.h>
26#include <rfb/KeyRemapper.h>
27#define XK_MISCELLANY
28#define XK_XKB_KEYS
29#include <rfb/keysymdef.h>
30
31using namespace rfb;
32
33static LogWriter vlog("VNCSConnST");
34
35VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
36 bool reverse)
37 : SConnection(server_->securityFactory, reverse), sock(s), server(server_),
38 updates(false), image_getter(server->useEconomicTranslate),
39 drawRenderedCursor(false), removeRenderedCursor(false),
40 pointerEventTime(0), accessRights(AccessDefault),
Pierre Ossmanf99c5712009-03-13 14:41:27 +000041 startTime(time(0))
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000042{
43 setStreams(&sock->inStream(), &sock->outStream());
44 peerEndpoint.buf = sock->getPeerEndpoint();
45 VNCServerST::connectionsLog.write(1,"accepted: %s", peerEndpoint.buf);
46
47 // Configure the socket
48 setSocketTimeouts();
49 lastEventTime = time(0);
50
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000051 server->clients.push_front(this);
52}
53
54
55VNCSConnectionST::~VNCSConnectionST()
56{
57 // If we reach here then VNCServerST is deleting us!
58 VNCServerST::connectionsLog.write(1,"closed: %s (%s)",
59 peerEndpoint.buf,
60 (closeReason.buf) ? closeReason.buf : "");
61
62 // Release any keys the client still had pressed
63 std::set<rdr::U32>::iterator i;
64 for (i=pressedKeys.begin(); i!=pressedKeys.end(); i++)
65 server->desktop->keyEvent(*i, false);
66 if (server->pointerClient == this)
67 server->pointerClient = 0;
68
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000069 // Remove this client from the server
70 server->clients.remove(this);
71
72}
73
74
75// Methods called from VNCServerST
76
77bool VNCSConnectionST::init()
78{
79 try {
80 initialiseProtocol();
81 } catch (rdr::Exception& e) {
82 close(e.str());
83 return false;
84 }
85 return true;
86}
87
88void VNCSConnectionST::close(const char* reason)
89{
90 // Log the reason for the close
91 if (!closeReason.buf)
92 closeReason.buf = strDup(reason);
93 else
94 vlog.debug("second close: %s (%s)", peerEndpoint.buf, reason);
95
96 if (authenticated()) {
97 server->lastDisconnectTime = time(0);
98 }
99
100 // Just shutdown the socket and mark our state as closing. Eventually the
101 // calling code will call VNCServerST's removeSocket() method causing us to
102 // be deleted.
103 sock->shutdown();
104 setState(RFBSTATE_CLOSING);
105}
106
107
108void VNCSConnectionST::processMessages()
109{
110 if (state() == RFBSTATE_CLOSING) return;
111 try {
112 // - Now set appropriate socket timeouts and process data
113 setSocketTimeouts();
114 bool clientsReadyBefore = server->clientsReadyForUpdate();
115
116 while (getInStream()->checkNoWait(1)) {
117 processMsg();
118 }
119
Constantin Kaplinsky2ef66952008-08-29 11:33:46 +0000120 // If there were update requests, try to send a framebuffer update.
Pierre Ossman02e43d72009-03-05 11:57:11 +0000121 // We don't send updates immediately on requests as this way, we
122 // give higher priority to user actions such as keyboard and
123 // pointer events.
Constantin Kaplinsky2ef66952008-08-29 11:33:46 +0000124 if (!requested.is_empty()) {
125 writeFramebufferUpdate();
126 }
127
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000128 if (!clientsReadyBefore && !requested.is_empty())
129 server->desktop->framebufferUpdateRequest();
130 } catch (rdr::EndOfStream&) {
131 close("Clean disconnection");
132 } catch (rdr::Exception &e) {
133 close(e.str());
134 }
135}
136
137void VNCSConnectionST::writeFramebufferUpdateOrClose()
138{
139 try {
140 writeFramebufferUpdate();
141 } catch(rdr::Exception &e) {
142 close(e.str());
143 }
144}
145
146void VNCSConnectionST::pixelBufferChange()
147{
148 try {
149 if (!authenticated()) return;
150 if (cp.width && cp.height && (server->pb->width() != cp.width ||
151 server->pb->height() != cp.height))
152 {
153 // We need to clip the next update to the new size, but also add any
154 // extra bits if it's bigger. If we wanted to do this exactly, something
155 // like the code below would do it, but at the moment we just update the
156 // entire new size. However, we do need to clip the renderedCursorRect
157 // because that might be added to updates in writeFramebufferUpdate().
158
159 //updates.intersect(server->pb->getRect());
160 //
161 //if (server->pb->width() > cp.width)
162 // updates.add_changed(Rect(cp.width, 0, server->pb->width(),
163 // server->pb->height()));
164 //if (server->pb->height() > cp.height)
165 // updates.add_changed(Rect(0, cp.height, cp.width,
166 // server->pb->height()));
167
168 renderedCursorRect = renderedCursorRect.intersect(server->pb->getRect());
169
170 cp.width = server->pb->width();
171 cp.height = server->pb->height();
172 if (state() == RFBSTATE_NORMAL) {
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000173 if (!writer()->writeSetDesktopSize() &&
174 !writer()->writeExtendedDesktopSize()) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000175 close("Client does not support desktop resize");
176 return;
177 }
178 }
179 }
180 // Just update the whole screen at the moment because we're too lazy to
181 // work out what's actually changed.
182 updates.clear();
183 updates.add_changed(server->pb->getRect());
184 vlog.debug("pixel buffer changed - re-initialising image getter");
185 image_getter.init(server->pb, cp.pf(), writer());
186 if (writer()->needFakeUpdate())
187 writeFramebufferUpdate();
188 } catch(rdr::Exception &e) {
189 close(e.str());
190 }
191}
192
193void VNCSConnectionST::setColourMapEntriesOrClose(int firstColour,int nColours)
194{
195 try {
196 setColourMapEntries(firstColour, nColours);
197 } catch(rdr::Exception& e) {
198 close(e.str());
199 }
200}
201
202void VNCSConnectionST::bell()
203{
204 try {
205 if (state() == RFBSTATE_NORMAL) writer()->writeBell();
206 } catch(rdr::Exception& e) {
207 close(e.str());
208 }
209}
210
211void VNCSConnectionST::serverCutText(const char *str, int len)
212{
213 try {
214 if (!(accessRights & AccessCutText)) return;
215 if (!rfb::Server::sendCutText) return;
216 if (state() == RFBSTATE_NORMAL)
217 writer()->writeServerCutText(str, len);
218 } catch(rdr::Exception& e) {
219 close(e.str());
220 }
221}
222
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000223
224void VNCSConnectionST::setDesktopName(const char *name)
225{
226 cp.setName(name);
227 try {
228 if (state() == RFBSTATE_NORMAL) {
229 if (!writer()->writeSetDesktopName()) {
230 fprintf(stderr, "Client does not support desktop rename\n");
231 }
232 }
233 } catch(rdr::Exception& e) {
234 close(e.str());
235 }
236}
237
238
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000239void VNCSConnectionST::setCursorOrClose()
240{
241 try {
242 setCursor();
243 } catch(rdr::Exception& e) {
244 close(e.str());
245 }
246}
247
248
249int VNCSConnectionST::checkIdleTimeout()
250{
251 int idleTimeout = rfb::Server::idleTimeout;
252 if (idleTimeout == 0) return 0;
253 if (state() != RFBSTATE_NORMAL && idleTimeout < 15)
254 idleTimeout = 15; // minimum of 15 seconds while authenticating
255 time_t now = time(0);
256 if (now < lastEventTime) {
257 // Someone must have set the time backwards. Set lastEventTime so that the
258 // idleTimeout will count from now.
259 vlog.info("Time has gone backwards - resetting idle timeout");
260 lastEventTime = now;
261 }
262 int timeLeft = lastEventTime + idleTimeout - now;
263 if (timeLeft < -60) {
264 // Our callback is over a minute late - someone must have set the time
265 // forwards. Set lastEventTime so that the idleTimeout will count from
266 // now.
267 vlog.info("Time has gone forwards - resetting idle timeout");
268 lastEventTime = now;
269 return secsToMillis(idleTimeout);
270 }
271 if (timeLeft <= 0) {
272 close("Idle timeout");
273 return 0;
274 }
275 return secsToMillis(timeLeft);
276}
277
278// renderedCursorChange() is called whenever the server-side rendered cursor
279// changes shape or position. It ensures that the next update will clean up
280// the old rendered cursor and if necessary draw the new rendered cursor.
281
282void VNCSConnectionST::renderedCursorChange()
283{
284 if (state() != RFBSTATE_NORMAL) return;
285 removeRenderedCursor = true;
286 if (needRenderedCursor())
287 drawRenderedCursor = true;
288}
289
290// needRenderedCursor() returns true if this client needs the server-side
291// rendered cursor. This may be because it does not support local cursor or
292// because the current cursor position has not been set by this client.
293// Unfortunately we can't know for sure when the current cursor position has
294// been set by this client. We guess that this is the case when the current
295// cursor position is the same as the last pointer event from this client, or
296// if it is a very short time since this client's last pointer event (up to a
297// second). [ Ideally we should do finer-grained timing here and make the time
298// configurable, but I don't think it's that important. ]
299
300bool VNCSConnectionST::needRenderedCursor()
301{
302 return (state() == RFBSTATE_NORMAL
303 && (!cp.supportsLocalCursor && !cp.supportsLocalXCursor
304 || (!server->cursorPos.equals(pointerEventPos) &&
305 (time(0) - pointerEventTime) > 0)));
306}
307
308
309void VNCSConnectionST::approveConnectionOrClose(bool accept,
310 const char* reason)
311{
312 try {
313 approveConnection(accept, reason);
314 } catch (rdr::Exception& e) {
315 close(e.str());
316 }
317}
318
319
320
321// -=- Callbacks from SConnection
322
323void VNCSConnectionST::authSuccess()
324{
325 lastEventTime = time(0);
326
327 server->startDesktop();
328
329 // - Set the connection parameters appropriately
330 cp.width = server->pb->width();
331 cp.height = server->pb->height();
332 cp.setName(server->getName());
333
334 // - Set the default pixel format
335 cp.setPF(server->pb->getPF());
336 char buffer[256];
337 cp.pf().print(buffer, 256);
338 vlog.info("Server default pixel format %s", buffer);
339 image_getter.init(server->pb, cp.pf(), 0);
340
341 // - Mark the entire display as "dirty"
342 updates.add_changed(server->pb->getRect());
343 startTime = time(0);
344}
345
346void VNCSConnectionST::queryConnection(const char* userName)
347{
348 // - Authentication succeeded - clear from blacklist
349 CharArray name; name.buf = sock->getPeerAddress();
350 server->blHosts->clearBlackmark(name.buf);
351
352 // - Special case to provide a more useful error message
353 if (rfb::Server::neverShared && !rfb::Server::disconnectClients &&
354 server->authClientCount() > 0) {
355 approveConnection(false, "The server is already in use");
356 return;
357 }
358
359 // - Does the client have the right to bypass the query?
360 if (reverseConnection ||
361 !(rfb::Server::queryConnect || sock->requiresQuery()) ||
362 (accessRights & AccessNoQuery))
363 {
364 approveConnection(true);
365 return;
366 }
367
368 // - Get the server to display an Accept/Reject dialog, if required
369 // If a dialog is displayed, the result will be PENDING, and the
370 // server will call approveConnection at a later time
371 CharArray reason;
372 VNCServerST::queryResult qr = server->queryConnection(sock, userName,
373 &reason.buf);
374 if (qr == VNCServerST::PENDING)
375 return;
376
377 // - If server returns ACCEPT/REJECT then pass result to SConnection
378 approveConnection(qr == VNCServerST::ACCEPT, reason.buf);
379}
380
381void VNCSConnectionST::clientInit(bool shared)
382{
383 lastEventTime = time(0);
384 if (rfb::Server::alwaysShared || reverseConnection) shared = true;
385 if (rfb::Server::neverShared) shared = false;
386 if (!shared) {
387 if (rfb::Server::disconnectClients) {
388 // - Close all the other connected clients
389 vlog.debug("non-shared connection - closing clients");
390 server->closeClients("Non-shared connection requested", getSock());
391 } else {
392 // - Refuse this connection if there are existing clients, in addition to
393 // this one
394 if (server->authClientCount() > 1) {
395 close("Server is already in use");
396 return;
397 }
398 }
399 }
400 SConnection::clientInit(shared);
401}
402
403void VNCSConnectionST::setPixelFormat(const PixelFormat& pf)
404{
405 SConnection::setPixelFormat(pf);
406 char buffer[256];
407 pf.print(buffer, 256);
408 vlog.info("Client pixel format %s", buffer);
409 image_getter.init(server->pb, pf, writer());
410 setCursor();
411}
412
413void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask)
414{
415 pointerEventTime = lastEventTime = time(0);
416 server->lastUserInputTime = lastEventTime;
417 if (!(accessRights & AccessPtrEvents)) return;
418 if (!rfb::Server::acceptPointerEvents) return;
419 if (!server->pointerClient || server->pointerClient == this) {
420 pointerEventPos = pos;
421 if (buttonMask)
422 server->pointerClient = this;
423 else
424 server->pointerClient = 0;
425 server->desktop->pointerEvent(pointerEventPos, buttonMask);
426 }
427}
428
429
430class VNCSConnectionSTShiftPresser {
431public:
432 VNCSConnectionSTShiftPresser(SDesktop* desktop_)
433 : desktop(desktop_), pressed(false) {}
434 ~VNCSConnectionSTShiftPresser() {
435 if (pressed) { desktop->keyEvent(XK_Shift_L, false); }
436 }
437 void press() {
438 desktop->keyEvent(XK_Shift_L, true);
439 pressed = true;
440 }
441 SDesktop* desktop;
442 bool pressed;
443};
444
445// keyEvent() - record in the pressedKeys which keys were pressed. Allow
446// multiple down events (for autorepeat), but only allow a single up event.
447void VNCSConnectionST::keyEvent(rdr::U32 key, bool down) {
448 lastEventTime = time(0);
449 server->lastUserInputTime = lastEventTime;
450 if (!(accessRights & AccessKeyEvents)) return;
451 if (!rfb::Server::acceptKeyEvents) return;
452
453 // Remap the key if required
454 if (server->keyRemapper)
455 key = server->keyRemapper->remapKey(key);
456
457 // Turn ISO_Left_Tab into shifted Tab.
458 VNCSConnectionSTShiftPresser shiftPresser(server->desktop);
459 if (key == XK_ISO_Left_Tab) {
460 if (pressedKeys.find(XK_Shift_L) == pressedKeys.end() &&
461 pressedKeys.find(XK_Shift_R) == pressedKeys.end())
462 shiftPresser.press();
463 key = XK_Tab;
464 }
465
466 if (down) {
467 pressedKeys.insert(key);
468 } else {
469 if (!pressedKeys.erase(key)) return;
470 }
471 server->desktop->keyEvent(key, down);
472}
473
474void VNCSConnectionST::clientCutText(const char* str, int len)
475{
476 if (!(accessRights & AccessCutText)) return;
477 if (!rfb::Server::acceptCutText) return;
478 server->desktop->clientCutText(str, len);
479}
480
481void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
482{
483 if (!(accessRights & AccessView)) return;
484
485 SConnection::framebufferUpdateRequest(r, incremental);
486
Constantin Kaplinsky2ef66952008-08-29 11:33:46 +0000487 // Just update the requested region.
488 // Framebuffer update will be sent a bit later, see processMessages().
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000489 Region reqRgn(r);
490 requested.assign_union(reqRgn);
491
492 if (!incremental) {
493 // Non-incremental update - treat as if area requested has changed
494 updates.add_changed(reqRgn);
495 server->comparer->add_changed(reqRgn);
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000496 // And update the clients view of screen layout
497 writer()->writeSetDesktopSize();
498 writer()->writeExtendedDesktopSize();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000499 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000500}
501
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000502void VNCSConnectionST::setDesktopSize(int fb_width, int fb_height)
503{
504 vlog.info("Rejecting client request to change desktop size");
505 writer()->writeExtendedDesktopSize(resultProhibited);
506}
507
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000508void VNCSConnectionST::setInitialColourMap()
509{
510 setColourMapEntries(0, 0);
511}
512
513// supportsLocalCursor() is called whenever the status of
514// cp.supportsLocalCursor has changed. If the client does now support local
515// cursor, we make sure that the old server-side rendered cursor is cleaned up
516// and the cursor is sent to the client.
517
518void VNCSConnectionST::supportsLocalCursor()
519{
520 if (cp.supportsLocalCursor || cp.supportsLocalXCursor) {
521 removeRenderedCursor = true;
522 drawRenderedCursor = false;
523 setCursor();
524 }
525}
526
527void VNCSConnectionST::writeSetCursorCallback()
528{
529 if (cp.supportsLocalXCursor) {
530 Pixel pix0, pix1;
531 rdr::U8Array bitmap(server->cursor.getBitmap(&pix0, &pix1));
532 if (bitmap.buf) {
533 // The client supports XCursor and the cursor only has two
534 // colors. Use the XCursor encoding.
535 writer()->writeSetXCursor(server->cursor.width(),
536 server->cursor.height(),
537 server->cursor.hotspot.x,
538 server->cursor.hotspot.y,
539 bitmap.buf, server->cursor.mask.buf);
540 return;
541 } else {
542 // More than two colors
543 if (!cp.supportsLocalCursor) {
544 // FIXME: We could reduce to two colors.
545 vlog.info("Unable to send multicolor cursor: RichCursor not supported by client");
546 return;
547 }
548 }
549 }
550
551 // Use RichCursor
552 rdr::U8* transData = writer()->getImageBuf(server->cursor.area());
553 image_getter.translatePixels(server->cursor.data, transData,
554 server->cursor.area());
555 writer()->writeSetCursor(server->cursor.width(),
556 server->cursor.height(),
557 server->cursor.hotspot,
558 transData, server->cursor.mask.buf);
559}
560
561
562void VNCSConnectionST::writeFramebufferUpdate()
563{
564 if (state() != RFBSTATE_NORMAL || requested.is_empty()) return;
565
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000566 updates.enable_copyrect(cp.useCopyRect);
567
Pierre Ossman02e43d72009-03-05 11:57:11 +0000568 server->checkUpdate();
Constantin Kaplinskya09dc142008-12-18 12:08:15 +0000569
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000570 // Get the lists of updates. Prior to exporting the data to the `ui' object,
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000571 // getUpdateInfo() will normalize the `updates' object such way that its
Pierre Ossman02e43d72009-03-05 11:57:11 +0000572 // `changed' and `copied' regions would not intersect.
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000573
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000574 UpdateInfo ui;
575 updates.getUpdateInfo(&ui, requested);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000576 bool needNewUpdateInfo = false;
577
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000578 // If the previous position of the rendered cursor overlaps the source of the
579 // copy, then when the copy happens the corresponding rectangle in the
580 // destination will be wrong, so add it to the changed region.
581
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000582 if (!ui.copied.is_empty() && !renderedCursorRect.is_empty()) {
583 Rect bogusCopiedCursor = (renderedCursorRect.translate(ui.copy_delta)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000584 .intersect(server->pb->getRect()));
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000585 if (!ui.copied.intersect(bogusCopiedCursor).is_empty()) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000586 updates.add_changed(bogusCopiedCursor);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000587 needNewUpdateInfo = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000588 }
589 }
590
591 // If we need to remove the old rendered cursor, just add the rectangle to
592 // the changed region.
593
594 if (removeRenderedCursor) {
595 updates.add_changed(renderedCursorRect);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000596 needNewUpdateInfo = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000597 renderedCursorRect.clear();
598 removeRenderedCursor = false;
599 }
600
601 // Return if there is nothing to send the client.
602
603 if (updates.is_empty() && !writer()->needFakeUpdate() && !drawRenderedCursor)
604 return;
605
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000606 // The `updates' object could change, make sure we have valid update info.
607
608 if (needNewUpdateInfo)
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000609 updates.getUpdateInfo(&ui, requested);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000610
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000611 // If the client needs a server-side rendered cursor, work out the cursor
612 // rectangle. If it's empty then don't bother drawing it, but if it overlaps
613 // with the update region, we need to draw the rendered cursor regardless of
614 // whether it has changed.
615
616 if (needRenderedCursor()) {
617 renderedCursorRect
618 = (server->renderedCursor.getRect(server->renderedCursorTL)
619 .intersect(requested.get_bounding_rect()));
620
621 if (renderedCursorRect.is_empty()) {
622 drawRenderedCursor = false;
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000623 } else if (!ui.changed.union_(ui.copied)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000624 .intersect(renderedCursorRect).is_empty()) {
625 drawRenderedCursor = true;
626 }
627
628 // We could remove the new cursor rect from updates here. It's not clear
629 // whether this is worth it. If we do remove it, then we won't draw over
630 // the same bit of screen twice, but we have the overhead of a more complex
631 // region.
632
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000633 //if (drawRenderedCursor) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000634 // updates.subtract(renderedCursorRect);
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000635 // updates.getUpdateInfo(&ui, requested);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000636 //}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000637 }
638
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000639 if (!ui.is_empty() || writer()->needFakeUpdate() || drawRenderedCursor) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000640 // Compute the number of rectangles. Tight encoder makes the things more
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000641 // complicated as compared to the original VNC4.
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000642 writer()->setupCurrentEncoder();
Constantin Kaplinsky1a845d02007-08-31 21:06:53 +0000643 int nRects = (ui.copied.numRects() +
Constantin Kaplinsky1a845d02007-08-31 21:06:53 +0000644 (drawRenderedCursor ? 1 : 0));
Constantin Kaplinsky651606d2007-10-17 17:40:23 +0000645
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000646 std::vector<Rect> rects;
647 std::vector<Rect>::const_iterator i;
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000648 ui.changed.get_rects(&rects);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000649 for (i = rects.begin(); i != rects.end(); i++) {
650 if (i->width() && i->height())
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000651 nRects += writer()->getNumRects(*i);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000652 }
653
654 writer()->writeFramebufferUpdateStart(nRects);
655 Region updatedRegion;
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000656 writer()->writeRects(ui, &image_getter, &updatedRegion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000657 updates.subtract(updatedRegion);
658 if (drawRenderedCursor)
659 writeRenderedCursorRect();
660 writer()->writeFramebufferUpdateEnd();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000661 requested.clear();
662 }
663}
664
665
666// writeRenderedCursorRect() writes a single rectangle drawing the rendered
667// cursor on the client.
668
669void VNCSConnectionST::writeRenderedCursorRect()
670{
671 image_getter.setPixelBuffer(&server->renderedCursor);
672 image_getter.setOffset(server->renderedCursorTL);
673
674 Rect actual;
675 writer()->writeRect(renderedCursorRect, &image_getter, &actual);
676
677 image_getter.setPixelBuffer(server->pb);
678 image_getter.setOffset(Point(0,0));
679
680 drawRenderedCursor = false;
681}
682
683void VNCSConnectionST::setColourMapEntries(int firstColour, int nColours)
684{
685 if (!readyForSetColourMapEntries) return;
686 if (server->pb->getPF().trueColour) return;
687
688 image_getter.setColourMapEntries(firstColour, nColours, writer());
689
690 if (cp.pf().trueColour) {
691 updates.add_changed(server->pb->getRect());
692 }
693}
694
695
696// setCursor() is called whenever the cursor has changed shape or pixel format.
697// If the client supports local cursor then it will arrange for the cursor to
698// be sent to the client.
699
700void VNCSConnectionST::setCursor()
701{
702 if (state() != RFBSTATE_NORMAL || !cp.supportsLocalCursor) return;
703 writer()->cursorChange(this);
704 if (writer()->needFakeUpdate())
705 writeFramebufferUpdate();
706}
707
708void VNCSConnectionST::setSocketTimeouts()
709{
710 int timeoutms = rfb::Server::clientWaitTimeMillis;
711 soonestTimeout(&timeoutms, secsToMillis(rfb::Server::idleTimeout));
712 if (timeoutms == 0)
713 timeoutms = -1;
714 sock->inStream().setTimeout(timeoutms);
715 sock->outStream().setTimeout(timeoutms);
716}
717
718char* VNCSConnectionST::getStartTime()
719{
720 char* result = ctime(&startTime);
721 result[24] = '\0';
722 return result;
723}
724
725void VNCSConnectionST::setStatus(int status)
726{
727 switch (status) {
728 case 0:
729 accessRights = accessRights | AccessPtrEvents | AccessKeyEvents | AccessView;
730 break;
731 case 1:
732 accessRights = accessRights & !(AccessPtrEvents | AccessKeyEvents) | AccessView;
733 break;
734 case 2:
735 accessRights = accessRights & !(AccessPtrEvents | AccessKeyEvents | AccessView);
736 break;
737 }
738 framebufferUpdateRequest(server->pb->getRect(), false);
739}
740int VNCSConnectionST::getStatus()
741{
742 if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0007)
743 return 0;
744 if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0001)
745 return 1;
746 if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0000)
747 return 2;
748 return 4;
749}
750