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