blob: 7c922cfe444a2ecb846a18e7019dd114a2f74b64 [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),
Constantin Kaplinskydafbb012007-04-05 08:43:25 +000038 autoUpdatesActive(false),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000039 pointerEventTime(0), accessRights(AccessDefault),
40 startTime(time(0)), m_pFileTransfer(0)
41{
42 setStreams(&sock->inStream(), &sock->outStream());
43 peerEndpoint.buf = sock->getPeerEndpoint();
44 VNCServerST::connectionsLog.write(1,"accepted: %s", peerEndpoint.buf);
45
46 // Configure the socket
47 setSocketTimeouts();
48 lastEventTime = time(0);
49
50 // Add this client to the VNCServerST
51 if (server->m_pFTManager != NULL) {
52 SFileTransfer *pFT = server->m_pFTManager->createObject(sock);
53 if (pFT != NULL) {
54 m_pFileTransfer = pFT;
55 }
56 }
57
58 server->clients.push_front(this);
59}
60
61
62VNCSConnectionST::~VNCSConnectionST()
63{
64 // If we reach here then VNCServerST is deleting us!
65 VNCServerST::connectionsLog.write(1,"closed: %s (%s)",
66 peerEndpoint.buf,
67 (closeReason.buf) ? closeReason.buf : "");
68
69 // Release any keys the client still had pressed
70 std::set<rdr::U32>::iterator i;
71 for (i=pressedKeys.begin(); i!=pressedKeys.end(); i++)
72 server->desktop->keyEvent(*i, false);
73 if (server->pointerClient == this)
74 server->pointerClient = 0;
75
76 if (m_pFileTransfer)
77 server->m_pFTManager->destroyObject(m_pFileTransfer);
78
79 // Remove this client from the server
80 server->clients.remove(this);
81
82}
83
84
85// Methods called from VNCServerST
86
87bool VNCSConnectionST::init()
88{
89 try {
Constantin Kaplinsky82328312008-04-24 08:44:24 +000090 setProtocolOptions(server->isVideoSelectionEnabled());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000091 initialiseProtocol();
92 } catch (rdr::Exception& e) {
93 close(e.str());
94 return false;
95 }
96 return true;
97}
98
99void VNCSConnectionST::close(const char* reason)
100{
101 // Log the reason for the close
102 if (!closeReason.buf)
103 closeReason.buf = strDup(reason);
104 else
105 vlog.debug("second close: %s (%s)", peerEndpoint.buf, reason);
106
107 if (authenticated()) {
108 server->lastDisconnectTime = time(0);
109 }
110
111 // Just shutdown the socket and mark our state as closing. Eventually the
112 // calling code will call VNCServerST's removeSocket() method causing us to
113 // be deleted.
114 sock->shutdown();
115 setState(RFBSTATE_CLOSING);
116}
117
118
119void VNCSConnectionST::processMessages()
120{
121 if (state() == RFBSTATE_CLOSING) return;
122 try {
123 // - Now set appropriate socket timeouts and process data
124 setSocketTimeouts();
125 bool clientsReadyBefore = server->clientsReadyForUpdate();
126
127 while (getInStream()->checkNoWait(1)) {
128 processMsg();
129 }
130
131 if (!clientsReadyBefore && !requested.is_empty())
132 server->desktop->framebufferUpdateRequest();
133 } catch (rdr::EndOfStream&) {
134 close("Clean disconnection");
135 } catch (rdr::Exception &e) {
136 close(e.str());
137 }
138}
139
140void VNCSConnectionST::writeFramebufferUpdateOrClose()
141{
142 try {
143 writeFramebufferUpdate();
144 } catch(rdr::Exception &e) {
145 close(e.str());
146 }
147}
148
149void VNCSConnectionST::pixelBufferChange()
150{
151 try {
152 if (!authenticated()) return;
153 if (cp.width && cp.height && (server->pb->width() != cp.width ||
154 server->pb->height() != cp.height))
155 {
156 // We need to clip the next update to the new size, but also add any
157 // extra bits if it's bigger. If we wanted to do this exactly, something
158 // like the code below would do it, but at the moment we just update the
159 // entire new size. However, we do need to clip the renderedCursorRect
160 // because that might be added to updates in writeFramebufferUpdate().
161
162 //updates.intersect(server->pb->getRect());
163 //
164 //if (server->pb->width() > cp.width)
165 // updates.add_changed(Rect(cp.width, 0, server->pb->width(),
166 // server->pb->height()));
167 //if (server->pb->height() > cp.height)
168 // updates.add_changed(Rect(0, cp.height, cp.width,
169 // server->pb->height()));
170
171 renderedCursorRect = renderedCursorRect.intersect(server->pb->getRect());
172
173 cp.width = server->pb->width();
174 cp.height = server->pb->height();
175 if (state() == RFBSTATE_NORMAL) {
176 if (!writer()->writeSetDesktopSize()) {
177 close("Client does not support desktop resize");
178 return;
179 }
180 }
181 }
182 // Just update the whole screen at the moment because we're too lazy to
183 // work out what's actually changed.
184 updates.clear();
185 updates.add_changed(server->pb->getRect());
186 vlog.debug("pixel buffer changed - re-initialising image getter");
187 image_getter.init(server->pb, cp.pf(), writer());
188 if (writer()->needFakeUpdate())
189 writeFramebufferUpdate();
190 } catch(rdr::Exception &e) {
191 close(e.str());
192 }
193}
194
195void VNCSConnectionST::setColourMapEntriesOrClose(int firstColour,int nColours)
196{
197 try {
198 setColourMapEntries(firstColour, nColours);
199 } catch(rdr::Exception& e) {
200 close(e.str());
201 }
202}
203
204void VNCSConnectionST::bell()
205{
206 try {
207 if (state() == RFBSTATE_NORMAL) writer()->writeBell();
208 } catch(rdr::Exception& e) {
209 close(e.str());
210 }
211}
212
213void VNCSConnectionST::serverCutText(const char *str, int len)
214{
215 try {
216 if (!(accessRights & AccessCutText)) return;
217 if (!rfb::Server::sendCutText) return;
218 if (state() == RFBSTATE_NORMAL)
219 writer()->writeServerCutText(str, len);
220 } catch(rdr::Exception& e) {
221 close(e.str());
222 }
223}
224
225void VNCSConnectionST::setCursorOrClose()
226{
227 try {
228 setCursor();
229 } catch(rdr::Exception& e) {
230 close(e.str());
231 }
232}
233
234
235int VNCSConnectionST::checkIdleTimeout()
236{
237 int idleTimeout = rfb::Server::idleTimeout;
238 if (idleTimeout == 0) return 0;
239 if (state() != RFBSTATE_NORMAL && idleTimeout < 15)
240 idleTimeout = 15; // minimum of 15 seconds while authenticating
241 time_t now = time(0);
242 if (now < lastEventTime) {
243 // Someone must have set the time backwards. Set lastEventTime so that the
244 // idleTimeout will count from now.
245 vlog.info("Time has gone backwards - resetting idle timeout");
246 lastEventTime = now;
247 }
248 int timeLeft = lastEventTime + idleTimeout - now;
249 if (timeLeft < -60) {
250 // Our callback is over a minute late - someone must have set the time
251 // forwards. Set lastEventTime so that the idleTimeout will count from
252 // now.
253 vlog.info("Time has gone forwards - resetting idle timeout");
254 lastEventTime = now;
255 return secsToMillis(idleTimeout);
256 }
257 if (timeLeft <= 0) {
258 close("Idle timeout");
259 return 0;
260 }
261 return secsToMillis(timeLeft);
262}
263
264// renderedCursorChange() is called whenever the server-side rendered cursor
265// changes shape or position. It ensures that the next update will clean up
266// the old rendered cursor and if necessary draw the new rendered cursor.
267
268void VNCSConnectionST::renderedCursorChange()
269{
270 if (state() != RFBSTATE_NORMAL) return;
271 removeRenderedCursor = true;
272 if (needRenderedCursor())
273 drawRenderedCursor = true;
274}
275
276// needRenderedCursor() returns true if this client needs the server-side
277// rendered cursor. This may be because it does not support local cursor or
278// because the current cursor position has not been set by this client.
279// Unfortunately we can't know for sure when the current cursor position has
280// been set by this client. We guess that this is the case when the current
281// cursor position is the same as the last pointer event from this client, or
282// if it is a very short time since this client's last pointer event (up to a
283// second). [ Ideally we should do finer-grained timing here and make the time
284// configurable, but I don't think it's that important. ]
285
286bool VNCSConnectionST::needRenderedCursor()
287{
288 return (state() == RFBSTATE_NORMAL
289 && (!cp.supportsLocalCursor && !cp.supportsLocalXCursor
290 || (!server->cursorPos.equals(pointerEventPos) &&
291 (time(0) - pointerEventTime) > 0)));
292}
293
294
295void VNCSConnectionST::approveConnectionOrClose(bool accept,
296 const char* reason)
297{
298 try {
299 approveConnection(accept, reason);
300 } catch (rdr::Exception& e) {
301 close(e.str());
302 }
303}
304
305
306
307// -=- Callbacks from SConnection
308
309void VNCSConnectionST::authSuccess()
310{
311 lastEventTime = time(0);
312
313 server->startDesktop();
314
315 // - Set the connection parameters appropriately
316 cp.width = server->pb->width();
317 cp.height = server->pb->height();
318 cp.setName(server->getName());
319
320 // - Set the default pixel format
321 cp.setPF(server->pb->getPF());
322 char buffer[256];
323 cp.pf().print(buffer, 256);
324 vlog.info("Server default pixel format %s", buffer);
325 image_getter.init(server->pb, cp.pf(), 0);
326
327 // - Mark the entire display as "dirty"
328 updates.add_changed(server->pb->getRect());
329 startTime = time(0);
330}
331
332void VNCSConnectionST::queryConnection(const char* userName)
333{
334 // - Authentication succeeded - clear from blacklist
335 CharArray name; name.buf = sock->getPeerAddress();
336 server->blHosts->clearBlackmark(name.buf);
337
338 // - Special case to provide a more useful error message
339 if (rfb::Server::neverShared && !rfb::Server::disconnectClients &&
340 server->authClientCount() > 0) {
341 approveConnection(false, "The server is already in use");
342 return;
343 }
344
345 // - Does the client have the right to bypass the query?
346 if (reverseConnection ||
347 !(rfb::Server::queryConnect || sock->requiresQuery()) ||
348 (accessRights & AccessNoQuery))
349 {
350 approveConnection(true);
351 return;
352 }
353
354 // - Get the server to display an Accept/Reject dialog, if required
355 // If a dialog is displayed, the result will be PENDING, and the
356 // server will call approveConnection at a later time
357 CharArray reason;
358 VNCServerST::queryResult qr = server->queryConnection(sock, userName,
359 &reason.buf);
360 if (qr == VNCServerST::PENDING)
361 return;
362
363 // - If server returns ACCEPT/REJECT then pass result to SConnection
364 approveConnection(qr == VNCServerST::ACCEPT, reason.buf);
365}
366
367void VNCSConnectionST::clientInit(bool shared)
368{
369 lastEventTime = time(0);
370 if (rfb::Server::alwaysShared || reverseConnection) shared = true;
371 if (rfb::Server::neverShared) shared = false;
372 if (!shared) {
373 if (rfb::Server::disconnectClients) {
374 // - Close all the other connected clients
375 vlog.debug("non-shared connection - closing clients");
376 server->closeClients("Non-shared connection requested", getSock());
377 } else {
378 // - Refuse this connection if there are existing clients, in addition to
379 // this one
380 if (server->authClientCount() > 1) {
381 close("Server is already in use");
382 return;
383 }
384 }
385 }
386 SConnection::clientInit(shared);
387}
388
389void VNCSConnectionST::setPixelFormat(const PixelFormat& pf)
390{
391 SConnection::setPixelFormat(pf);
392 char buffer[256];
393 pf.print(buffer, 256);
394 vlog.info("Client pixel format %s", buffer);
395 image_getter.init(server->pb, pf, writer());
396 setCursor();
397}
398
399void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask)
400{
401 pointerEventTime = lastEventTime = time(0);
402 server->lastUserInputTime = lastEventTime;
403 if (!(accessRights & AccessPtrEvents)) return;
404 if (!rfb::Server::acceptPointerEvents) return;
405 if (!server->pointerClient || server->pointerClient == this) {
406 pointerEventPos = pos;
407 if (buttonMask)
408 server->pointerClient = this;
409 else
410 server->pointerClient = 0;
411 server->desktop->pointerEvent(pointerEventPos, buttonMask);
412 }
413}
414
415
416class VNCSConnectionSTShiftPresser {
417public:
418 VNCSConnectionSTShiftPresser(SDesktop* desktop_)
419 : desktop(desktop_), pressed(false) {}
420 ~VNCSConnectionSTShiftPresser() {
421 if (pressed) { desktop->keyEvent(XK_Shift_L, false); }
422 }
423 void press() {
424 desktop->keyEvent(XK_Shift_L, true);
425 pressed = true;
426 }
427 SDesktop* desktop;
428 bool pressed;
429};
430
431// keyEvent() - record in the pressedKeys which keys were pressed. Allow
432// multiple down events (for autorepeat), but only allow a single up event.
433void VNCSConnectionST::keyEvent(rdr::U32 key, bool down) {
434 lastEventTime = time(0);
435 server->lastUserInputTime = lastEventTime;
436 if (!(accessRights & AccessKeyEvents)) return;
437 if (!rfb::Server::acceptKeyEvents) return;
438
439 // Remap the key if required
440 if (server->keyRemapper)
441 key = server->keyRemapper->remapKey(key);
442
443 // Turn ISO_Left_Tab into shifted Tab.
444 VNCSConnectionSTShiftPresser shiftPresser(server->desktop);
445 if (key == XK_ISO_Left_Tab) {
446 if (pressedKeys.find(XK_Shift_L) == pressedKeys.end() &&
447 pressedKeys.find(XK_Shift_R) == pressedKeys.end())
448 shiftPresser.press();
449 key = XK_Tab;
450 }
451
452 if (down) {
453 pressedKeys.insert(key);
454 } else {
455 if (!pressedKeys.erase(key)) return;
456 }
457 server->desktop->keyEvent(key, down);
458}
459
460void VNCSConnectionST::clientCutText(const char* str, int len)
461{
462 if (!(accessRights & AccessCutText)) return;
463 if (!rfb::Server::acceptCutText) return;
464 server->desktop->clientCutText(str, len);
465}
466
467void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
468{
469 if (!(accessRights & AccessView)) return;
470
471 SConnection::framebufferUpdateRequest(r, incremental);
472
473 Region reqRgn(r);
474 requested.assign_union(reqRgn);
475
476 if (!incremental) {
477 // Non-incremental update - treat as if area requested has changed
478 updates.add_changed(reqRgn);
479 server->comparer->add_changed(reqRgn);
480 }
481
482 writeFramebufferUpdate();
483}
484
Constantin Kaplinskydafbb012007-04-05 08:43:25 +0000485void VNCSConnectionST::enableContinuousUpdates(const Rect& r)
486{
487 // TightVNC-specific EnableContinuousUpdates message is very much like
488 // incremental FramebufferUpdateRequest. So here we copy some code from
489 // VNCSConnectionST::framebufferUpdateRequest().
490
491 if (!(accessRights & AccessView)) return;
492
493 SConnection::framebufferUpdateRequest(r, true);
494
495 autoUpdatesActive = true;
496 autoUpdatedRect = r;
497
498 Region reqRgn(autoUpdatedRect);
499 requested.assign_union(reqRgn);
500
501 writeFramebufferUpdate();
502}
503
504void VNCSConnectionST::disableContinuousUpdates()
505{
506 autoUpdatesActive = false;
507 autoUpdatedRect.clear();
508
509 writeFramebufferUpdate();
510
511 // FIXME: Send EndOfContinuousUpdates message.
512}
513
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000514void VNCSConnectionST::setInitialColourMap()
515{
516 setColourMapEntries(0, 0);
517}
518
519// supportsLocalCursor() is called whenever the status of
520// cp.supportsLocalCursor has changed. If the client does now support local
521// cursor, we make sure that the old server-side rendered cursor is cleaned up
522// and the cursor is sent to the client.
523
524void VNCSConnectionST::supportsLocalCursor()
525{
526 if (cp.supportsLocalCursor || cp.supportsLocalXCursor) {
527 removeRenderedCursor = true;
528 drawRenderedCursor = false;
529 setCursor();
530 }
531}
532
533void VNCSConnectionST::writeSetCursorCallback()
534{
535 if (cp.supportsLocalXCursor) {
536 Pixel pix0, pix1;
537 rdr::U8Array bitmap(server->cursor.getBitmap(&pix0, &pix1));
538 if (bitmap.buf) {
539 // The client supports XCursor and the cursor only has two
540 // colors. Use the XCursor encoding.
541 writer()->writeSetXCursor(server->cursor.width(),
542 server->cursor.height(),
543 server->cursor.hotspot.x,
544 server->cursor.hotspot.y,
545 bitmap.buf, server->cursor.mask.buf);
546 return;
547 } else {
548 // More than two colors
549 if (!cp.supportsLocalCursor) {
550 // FIXME: We could reduce to two colors.
551 vlog.info("Unable to send multicolor cursor: RichCursor not supported by client");
552 return;
553 }
554 }
555 }
556
557 // Use RichCursor
558 rdr::U8* transData = writer()->getImageBuf(server->cursor.area());
559 image_getter.translatePixels(server->cursor.data, transData,
560 server->cursor.area());
561 writer()->writeSetCursor(server->cursor.width(),
562 server->cursor.height(),
563 server->cursor.hotspot,
564 transData, server->cursor.mask.buf);
565}
566
567
568void VNCSConnectionST::writeFramebufferUpdate()
569{
570 if (state() != RFBSTATE_NORMAL || requested.is_empty()) return;
571
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000572 updates.enable_copyrect(cp.useCopyRect);
573
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000574 server->checkUpdate();
575
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000576 // Get the lists of updates. Prior to exporting the data to the `ui' object,
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000577 // getUpdateInfo() will normalize the `updates' object such way that its
Constantin Kaplinsky1a845d02007-08-31 21:06:53 +0000578 // `changed', `copied' and `video_area' regions would not intersect.
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000579
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000580 UpdateInfo ui;
581 updates.getUpdateInfo(&ui, requested);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000582 bool needNewUpdateInfo = false;
583
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000584 // If the previous position of the rendered cursor overlaps the source of the
585 // copy, then when the copy happens the corresponding rectangle in the
586 // destination will be wrong, so add it to the changed region.
587
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000588 if (!ui.copied.is_empty() && !renderedCursorRect.is_empty()) {
589 Rect bogusCopiedCursor = (renderedCursorRect.translate(ui.copy_delta)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000590 .intersect(server->pb->getRect()));
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000591 if (!ui.copied.intersect(bogusCopiedCursor).is_empty()) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000592 updates.add_changed(bogusCopiedCursor);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000593 needNewUpdateInfo = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000594 }
595 }
596
597 // If we need to remove the old rendered cursor, just add the rectangle to
598 // the changed region.
599
600 if (removeRenderedCursor) {
601 updates.add_changed(renderedCursorRect);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000602 needNewUpdateInfo = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000603 renderedCursorRect.clear();
604 removeRenderedCursor = false;
605 }
606
607 // Return if there is nothing to send the client.
608
609 if (updates.is_empty() && !writer()->needFakeUpdate() && !drawRenderedCursor)
610 return;
611
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000612 // The `updates' object could change, make sure we have valid update info.
613
614 if (needNewUpdateInfo)
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000615 updates.getUpdateInfo(&ui, requested);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000616
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000617 // If the client needs a server-side rendered cursor, work out the cursor
618 // rectangle. If it's empty then don't bother drawing it, but if it overlaps
619 // with the update region, we need to draw the rendered cursor regardless of
620 // whether it has changed.
621
622 if (needRenderedCursor()) {
623 renderedCursorRect
624 = (server->renderedCursor.getRect(server->renderedCursorTL)
625 .intersect(requested.get_bounding_rect()));
626
627 if (renderedCursorRect.is_empty()) {
628 drawRenderedCursor = false;
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000629 } else if (!ui.changed.union_(ui.copied)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000630 .intersect(renderedCursorRect).is_empty()) {
631 drawRenderedCursor = true;
632 }
633
634 // We could remove the new cursor rect from updates here. It's not clear
635 // whether this is worth it. If we do remove it, then we won't draw over
636 // the same bit of screen twice, but we have the overhead of a more complex
637 // region.
638
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000639 //if (drawRenderedCursor) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000640 // updates.subtract(renderedCursorRect);
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000641 // updates.getUpdateInfo(&ui, requested);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000642 //}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000643 }
644
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000645 if (!ui.is_empty() || writer()->needFakeUpdate() || drawRenderedCursor) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000646 // Compute the number of rectangles. Tight encoder makes the things more
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000647 // complicated as compared to the original VNC4.
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000648 writer()->setupCurrentEncoder();
Constantin Kaplinsky1a845d02007-08-31 21:06:53 +0000649 int nRects = (ui.copied.numRects() +
Constantin Kaplinsky1a845d02007-08-31 21:06:53 +0000650 (drawRenderedCursor ? 1 : 0));
Constantin Kaplinsky651606d2007-10-17 17:40:23 +0000651 if (!ui.video_area.is_empty()) {
652 if (writer()->canUseJpegEncoder(server->getPixelBuffer())) {
653 nRects++;
654 } else {
655 nRects += writer()->getNumRects(ui.video_area);
656 }
657 }
658
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000659 std::vector<Rect> rects;
660 std::vector<Rect>::const_iterator i;
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000661 ui.changed.get_rects(&rects);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000662 for (i = rects.begin(); i != rects.end(); i++) {
663 if (i->width() && i->height())
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000664 nRects += writer()->getNumRects(*i);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000665 }
666
667 writer()->writeFramebufferUpdateStart(nRects);
Constantin Kaplinsky651606d2007-10-17 17:40:23 +0000668 if (!ui.video_area.is_empty()) {
669 if (writer()->canUseJpegEncoder(server->getPixelBuffer())) {
670 writer()->writeJpegRect(server->getPixelBuffer(), ui.video_area);
671 } else {
672 Rect actual;
673 writer()->writeRect(ui.video_area, &image_getter, &actual);
674 }
675 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000676 Region updatedRegion;
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000677 writer()->writeRects(ui, &image_getter, &updatedRegion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000678 updates.subtract(updatedRegion);
679 if (drawRenderedCursor)
680 writeRenderedCursorRect();
681 writer()->writeFramebufferUpdateEnd();
Constantin Kaplinskydafbb012007-04-05 08:43:25 +0000682 resetRequestedRegion();
683 }
684}
685
686void VNCSConnectionST::resetRequestedRegion()
687{
688 if (autoUpdatesActive) {
689 requested.reset(autoUpdatedRect);
690 } else {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000691 requested.clear();
692 }
693}
694
695
696// writeRenderedCursorRect() writes a single rectangle drawing the rendered
697// cursor on the client.
698
699void VNCSConnectionST::writeRenderedCursorRect()
700{
701 image_getter.setPixelBuffer(&server->renderedCursor);
702 image_getter.setOffset(server->renderedCursorTL);
703
704 Rect actual;
705 writer()->writeRect(renderedCursorRect, &image_getter, &actual);
706
707 image_getter.setPixelBuffer(server->pb);
708 image_getter.setOffset(Point(0,0));
709
710 drawRenderedCursor = false;
711}
712
713void VNCSConnectionST::setColourMapEntries(int firstColour, int nColours)
714{
715 if (!readyForSetColourMapEntries) return;
716 if (server->pb->getPF().trueColour) return;
717
718 image_getter.setColourMapEntries(firstColour, nColours, writer());
719
720 if (cp.pf().trueColour) {
721 updates.add_changed(server->pb->getRect());
722 }
723}
724
725
726// setCursor() is called whenever the cursor has changed shape or pixel format.
727// If the client supports local cursor then it will arrange for the cursor to
728// be sent to the client.
729
730void VNCSConnectionST::setCursor()
731{
732 if (state() != RFBSTATE_NORMAL || !cp.supportsLocalCursor) return;
733 writer()->cursorChange(this);
734 if (writer()->needFakeUpdate())
735 writeFramebufferUpdate();
736}
737
738void VNCSConnectionST::setSocketTimeouts()
739{
740 int timeoutms = rfb::Server::clientWaitTimeMillis;
741 soonestTimeout(&timeoutms, secsToMillis(rfb::Server::idleTimeout));
742 if (timeoutms == 0)
743 timeoutms = -1;
744 sock->inStream().setTimeout(timeoutms);
745 sock->outStream().setTimeout(timeoutms);
746}
747
748char* VNCSConnectionST::getStartTime()
749{
750 char* result = ctime(&startTime);
751 result[24] = '\0';
752 return result;
753}
754
755void VNCSConnectionST::setStatus(int status)
756{
757 switch (status) {
758 case 0:
759 accessRights = accessRights | AccessPtrEvents | AccessKeyEvents | AccessView;
760 break;
761 case 1:
762 accessRights = accessRights & !(AccessPtrEvents | AccessKeyEvents) | AccessView;
763 break;
764 case 2:
765 accessRights = accessRights & !(AccessPtrEvents | AccessKeyEvents | AccessView);
766 break;
767 }
768 framebufferUpdateRequest(server->pb->getRect(), false);
769}
770int VNCSConnectionST::getStatus()
771{
772 if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0007)
773 return 0;
774 if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0001)
775 return 1;
776 if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0000)
777 return 2;
778 return 4;
779}
780
781bool VNCSConnectionST::processFTMsg(int type)
782{
783 if (m_pFileTransfer != NULL)
784 return m_pFileTransfer->processMessages(type);
785 else
786 return false;
787}