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