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