blob: 37fb74472eccdfca8807771e37a57464b93b7fda [file] [log] [blame]
Constantin Kaplinskyde179d42006-04-16 06:53:44 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 *
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00003 * 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>
Constantin Kaplinskyde179d42006-04-16 06:53:44 +000024#include <rfb/KeyRemapper.h>
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000025#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)
Constantin Kaplinskyde179d42006-04-16 06:53:44 +000035 : SConnection(server_->securityFactory, reverse), sock(s), server(server_),
36 updates(false), image_getter(server->useEconomicTranslate),
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000037 drawRenderedCursor(false), removeRenderedCursor(false),
Oleg Sheikin4b0304f2005-12-09 10:59:12 +000038 pointerEventTime(0), accessRights(AccessDefault),
39 startTime(time(0))
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000040{
41 setStreams(&sock->inStream(), &sock->outStream());
42 peerEndpoint.buf = sock->getPeerEndpoint();
43 VNCServerST::connectionsLog.write(1,"accepted: %s", peerEndpoint.buf);
44
Constantin Kaplinskyde179d42006-04-16 06:53:44 +000045 // Configure the socket
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000046 setSocketTimeouts();
47 lastEventTime = time(0);
48
Constantin Kaplinskyde179d42006-04-16 06:53:44 +000049 // Add this client to the VNCServerST
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000050 server->clients.push_front(this);
51}
52
53
54VNCSConnectionST::~VNCSConnectionST()
55{
56 // If we reach here then VNCServerST is deleting us!
57 VNCServerST::connectionsLog.write(1,"closed: %s (%s)",
Constantin Kaplinskyde179d42006-04-16 06:53:44 +000058 peerEndpoint.buf,
59 (closeReason.buf) ? closeReason.buf : "");
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000060
61 // Release any keys the client still had pressed
62 std::set<rdr::U32>::iterator i;
63 for (i=pressedKeys.begin(); i!=pressedKeys.end(); i++)
64 server->desktop->keyEvent(*i, false);
65 if (server->pointerClient == this)
66 server->pointerClient = 0;
67
68 // Remove this client from the server
69 server->clients.remove(this);
70}
71
72
73// Methods called from VNCServerST
74
75bool VNCSConnectionST::init()
76{
77 try {
78 initialiseProtocol();
79 } catch (rdr::Exception& e) {
80 close(e.str());
81 return false;
82 }
83 return true;
84}
85
86void VNCSConnectionST::close(const char* reason)
87{
88 // Log the reason for the close
89 if (!closeReason.buf)
90 closeReason.buf = strDup(reason);
91 else
92 vlog.debug("second close: %s (%s)", peerEndpoint.buf, reason);
93
Peter Åstrand43aa1a12005-02-21 09:58:31 +000094 if (authenticated()) {
95 server->lastDisconnectTime = time(0);
96 }
97
Constantin Kaplinskyde179d42006-04-16 06:53:44 +000098 // Just shutdown the socket and mark our state as closing. Eventually the
99 // calling code will call VNCServerST's removeSocket() method causing us to
100 // be deleted.
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000101 sock->shutdown();
102 setState(RFBSTATE_CLOSING);
103}
104
105
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000106void VNCSConnectionST::processMessages()
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000107{
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000108 if (state() == RFBSTATE_CLOSING) return;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000109 try {
110 // - Now set appropriate socket timeouts and process data
111 setSocketTimeouts();
112 bool clientsReadyBefore = server->clientsReadyForUpdate();
113
114 while (getInStream()->checkNoWait(1)) {
115 processMsg();
116 }
117
118 if (!clientsReadyBefore && !requested.is_empty())
119 server->desktop->framebufferUpdateRequest();
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000120 } catch (rdr::EndOfStream&) {
121 close("Clean disconnection");
122 } catch (rdr::Exception &e) {
123 close(e.str());
124 }
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000125}
126
127void VNCSConnectionST::writeFramebufferUpdateOrClose()
128{
129 try {
130 writeFramebufferUpdate();
131 } catch(rdr::Exception &e) {
132 close(e.str());
133 }
134}
135
136void VNCSConnectionST::pixelBufferChange()
137{
138 try {
139 if (!authenticated()) return;
140 if (cp.width && cp.height && (server->pb->width() != cp.width ||
141 server->pb->height() != cp.height))
142 {
143 // We need to clip the next update to the new size, but also add any
144 // extra bits if it's bigger. If we wanted to do this exactly, something
145 // like the code below would do it, but at the moment we just update the
146 // entire new size. However, we do need to clip the renderedCursorRect
147 // because that might be added to updates in writeFramebufferUpdate().
148
149 //updates.intersect(server->pb->getRect());
150 //
151 //if (server->pb->width() > cp.width)
152 // updates.add_changed(Rect(cp.width, 0, server->pb->width(),
153 // server->pb->height()));
154 //if (server->pb->height() > cp.height)
155 // updates.add_changed(Rect(0, cp.height, cp.width,
156 // server->pb->height()));
157
158 renderedCursorRect = renderedCursorRect.intersect(server->pb->getRect());
159
160 cp.width = server->pb->width();
161 cp.height = server->pb->height();
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000162 if (state() == RFBSTATE_NORMAL) {
163 if (!writer()->writeSetDesktopSize()) {
164 close("Client does not support desktop resize");
165 return;
166 }
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000167 }
168 }
169 // Just update the whole screen at the moment because we're too lazy to
170 // work out what's actually changed.
171 updates.clear();
172 updates.add_changed(server->pb->getRect());
173 vlog.debug("pixel buffer changed - re-initialising image getter");
174 image_getter.init(server->pb, cp.pf(), writer());
175 if (writer()->needFakeUpdate())
176 writeFramebufferUpdate();
177 } catch(rdr::Exception &e) {
178 close(e.str());
179 }
180}
181
182void VNCSConnectionST::setColourMapEntriesOrClose(int firstColour,int nColours)
183{
184 try {
185 setColourMapEntries(firstColour, nColours);
186 } catch(rdr::Exception& e) {
187 close(e.str());
188 }
189}
190
191void VNCSConnectionST::bell()
192{
193 try {
194 if (state() == RFBSTATE_NORMAL) writer()->writeBell();
195 } catch(rdr::Exception& e) {
196 close(e.str());
197 }
198}
199
200void VNCSConnectionST::serverCutText(const char *str, int len)
201{
202 try {
203 if (!(accessRights & AccessCutText)) return;
204 if (!rfb::Server::sendCutText) return;
205 if (state() == RFBSTATE_NORMAL)
206 writer()->writeServerCutText(str, len);
207 } catch(rdr::Exception& e) {
208 close(e.str());
209 }
210}
211
212void VNCSConnectionST::setCursorOrClose()
213{
214 try {
215 setCursor();
216 } catch(rdr::Exception& e) {
217 close(e.str());
218 }
219}
220
221
222int VNCSConnectionST::checkIdleTimeout()
223{
224 int idleTimeout = rfb::Server::idleTimeout;
225 if (idleTimeout == 0) return 0;
226 if (state() != RFBSTATE_NORMAL && idleTimeout < 15)
227 idleTimeout = 15; // minimum of 15 seconds while authenticating
228 time_t now = time(0);
229 if (now < lastEventTime) {
230 // Someone must have set the time backwards. Set lastEventTime so that the
231 // idleTimeout will count from now.
232 vlog.info("Time has gone backwards - resetting idle timeout");
233 lastEventTime = now;
234 }
235 int timeLeft = lastEventTime + idleTimeout - now;
236 if (timeLeft < -60) {
237 // Our callback is over a minute late - someone must have set the time
238 // forwards. Set lastEventTime so that the idleTimeout will count from
239 // now.
240 vlog.info("Time has gone forwards - resetting idle timeout");
241 lastEventTime = now;
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000242 return secsToMillis(idleTimeout);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000243 }
244 if (timeLeft <= 0) {
245 close("Idle timeout");
246 return 0;
247 }
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000248 return secsToMillis(timeLeft);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000249}
250
251// renderedCursorChange() is called whenever the server-side rendered cursor
252// changes shape or position. It ensures that the next update will clean up
253// the old rendered cursor and if necessary draw the new rendered cursor.
254
255void VNCSConnectionST::renderedCursorChange()
256{
257 if (state() != RFBSTATE_NORMAL) return;
258 removeRenderedCursor = true;
259 if (needRenderedCursor())
260 drawRenderedCursor = true;
261}
262
263// needRenderedCursor() returns true if this client needs the server-side
264// rendered cursor. This may be because it does not support local cursor or
265// because the current cursor position has not been set by this client.
266// Unfortunately we can't know for sure when the current cursor position has
267// been set by this client. We guess that this is the case when the current
268// cursor position is the same as the last pointer event from this client, or
269// if it is a very short time since this client's last pointer event (up to a
270// second). [ Ideally we should do finer-grained timing here and make the time
271// configurable, but I don't think it's that important. ]
272
273bool VNCSConnectionST::needRenderedCursor()
274{
275 return (state() == RFBSTATE_NORMAL
Peter Åstrand1e9d32f2005-02-16 13:00:38 +0000276 && (!cp.supportsLocalCursor && !cp.supportsLocalXCursor
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000277 || (!server->cursorPos.equals(pointerEventPos) &&
278 (time(0) - pointerEventTime) > 0)));
279}
280
281
282void VNCSConnectionST::approveConnectionOrClose(bool accept,
283 const char* reason)
284{
285 try {
286 approveConnection(accept, reason);
287 } catch (rdr::Exception& e) {
288 close(e.str());
289 }
290}
291
292
293
294// -=- Callbacks from SConnection
295
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000296void VNCSConnectionST::authSuccess()
297{
298 lastEventTime = time(0);
299
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000300 server->startDesktop();
301
302 // - Set the connection parameters appropriately
303 cp.width = server->pb->width();
304 cp.height = server->pb->height();
305 cp.setName(server->getName());
306
307 // - Set the default pixel format
308 cp.setPF(server->pb->getPF());
309 char buffer[256];
310 cp.pf().print(buffer, 256);
311 vlog.info("Server default pixel format %s", buffer);
312 image_getter.init(server->pb, cp.pf(), 0);
313
314 // - Mark the entire display as "dirty"
315 updates.add_changed(server->pb->getRect());
Oleg Sheikin4b0304f2005-12-09 10:59:12 +0000316 startTime = time(0);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000317}
318
319void VNCSConnectionST::queryConnection(const char* userName)
320{
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000321 // - Authentication succeeded - clear from blacklist
322 CharArray name; name.buf = sock->getPeerAddress();
323 server->blHosts->clearBlackmark(name.buf);
324
325 // - Special case to provide a more useful error message
326 if (rfb::Server::neverShared && !rfb::Server::disconnectClients &&
327 server->authClientCount() > 0) {
328 approveConnection(false, "The server is already in use");
329 return;
330 }
331
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000332 // - Does the client have the right to bypass the query?
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000333 if (reverseConnection ||
334 !(rfb::Server::queryConnect || sock->requiresQuery()) ||
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000335 (accessRights & AccessNoQuery))
336 {
337 approveConnection(true);
338 return;
339 }
340
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000341 // - Get the server to display an Accept/Reject dialog, if required
342 // If a dialog is displayed, the result will be PENDING, and the
343 // server will call approveConnection at a later time
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000344 CharArray reason;
345 VNCServerST::queryResult qr = server->queryConnection(sock, userName,
346 &reason.buf);
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000347 if (qr == VNCServerST::PENDING)
348 return;
349
350 // - If server returns ACCEPT/REJECT then pass result to SConnection
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000351 approveConnection(qr == VNCServerST::ACCEPT, reason.buf);
352}
353
354void VNCSConnectionST::clientInit(bool shared)
355{
356 lastEventTime = time(0);
357 if (rfb::Server::alwaysShared || reverseConnection) shared = true;
358 if (rfb::Server::neverShared) shared = false;
359 if (!shared) {
360 if (rfb::Server::disconnectClients) {
361 // - Close all the other connected clients
362 vlog.debug("non-shared connection - closing clients");
363 server->closeClients("Non-shared connection requested", getSock());
364 } else {
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000365 // - Refuse this connection if there are existing clients, in addition to
366 // this one
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000367 if (server->authClientCount() > 1) {
368 close("Server is already in use");
369 return;
370 }
371 }
372 }
373 SConnection::clientInit(shared);
374}
375
376void VNCSConnectionST::setPixelFormat(const PixelFormat& pf)
377{
378 SConnection::setPixelFormat(pf);
379 char buffer[256];
380 pf.print(buffer, 256);
381 vlog.info("Client pixel format %s", buffer);
382 image_getter.init(server->pb, pf, writer());
383 setCursor();
384}
385
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000386void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask)
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000387{
388 pointerEventTime = lastEventTime = time(0);
Peter Åstrand43aa1a12005-02-21 09:58:31 +0000389 server->lastUserInputTime = lastEventTime;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000390 if (!(accessRights & AccessPtrEvents)) return;
391 if (!rfb::Server::acceptPointerEvents) return;
392 if (!server->pointerClient || server->pointerClient == this) {
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000393 pointerEventPos = pos;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000394 if (buttonMask)
395 server->pointerClient = this;
396 else
397 server->pointerClient = 0;
398 server->desktop->pointerEvent(pointerEventPos, buttonMask);
399 }
400}
401
402
403class VNCSConnectionSTShiftPresser {
404public:
405 VNCSConnectionSTShiftPresser(SDesktop* desktop_)
406 : desktop(desktop_), pressed(false) {}
407 ~VNCSConnectionSTShiftPresser() {
408 if (pressed) { desktop->keyEvent(XK_Shift_L, false); }
409 }
410 void press() {
411 desktop->keyEvent(XK_Shift_L, true);
412 pressed = true;
413 }
414 SDesktop* desktop;
415 bool pressed;
416};
417
418// keyEvent() - record in the pressedKeys which keys were pressed. Allow
419// multiple down events (for autorepeat), but only allow a single up event.
420void VNCSConnectionST::keyEvent(rdr::U32 key, bool down) {
421 lastEventTime = time(0);
Peter Åstrand43aa1a12005-02-21 09:58:31 +0000422 server->lastUserInputTime = lastEventTime;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000423 if (!(accessRights & AccessKeyEvents)) return;
424 if (!rfb::Server::acceptKeyEvents) return;
425
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000426 // Remap the key if required
427 if (server->keyRemapper)
428 key = server->keyRemapper->remapKey(key);
429
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000430 // Turn ISO_Left_Tab into shifted Tab.
431 VNCSConnectionSTShiftPresser shiftPresser(server->desktop);
432 if (key == XK_ISO_Left_Tab) {
433 if (pressedKeys.find(XK_Shift_L) == pressedKeys.end() &&
434 pressedKeys.find(XK_Shift_R) == pressedKeys.end())
435 shiftPresser.press();
436 key = XK_Tab;
437 }
438
439 if (down) {
440 pressedKeys.insert(key);
441 } else {
442 if (!pressedKeys.erase(key)) return;
443 }
444 server->desktop->keyEvent(key, down);
445}
446
447void VNCSConnectionST::clientCutText(const char* str, int len)
448{
449 if (!(accessRights & AccessCutText)) return;
450 if (!rfb::Server::acceptCutText) return;
451 server->desktop->clientCutText(str, len);
452}
453
454void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
455{
456 if (!(accessRights & AccessView)) return;
457
458 SConnection::framebufferUpdateRequest(r, incremental);
459
460 Region reqRgn(r);
461 requested.assign_union(reqRgn);
462
463 if (!incremental) {
464 // Non-incremental update - treat as if area requested has changed
465 updates.add_changed(reqRgn);
466 server->comparer->add_changed(reqRgn);
467 }
468
469 writeFramebufferUpdate();
470}
471
472void VNCSConnectionST::setInitialColourMap()
473{
474 setColourMapEntries(0, 0);
475}
476
477// supportsLocalCursor() is called whenever the status of
478// cp.supportsLocalCursor has changed. If the client does now support local
479// cursor, we make sure that the old server-side rendered cursor is cleaned up
480// and the cursor is sent to the client.
481
482void VNCSConnectionST::supportsLocalCursor()
483{
Peter Åstrand1e9d32f2005-02-16 13:00:38 +0000484 if (cp.supportsLocalCursor || cp.supportsLocalXCursor) {
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000485 removeRenderedCursor = true;
486 drawRenderedCursor = false;
487 setCursor();
488 }
489}
490
491void VNCSConnectionST::writeSetCursorCallback()
492{
Peter Åstrand1e9d32f2005-02-16 13:00:38 +0000493 if (cp.supportsLocalXCursor) {
494 Pixel pix0, pix1;
495 rdr::U8Array bitmap(server->cursor.getBitmap(&pix0, &pix1));
496 if (bitmap.buf) {
497 // The client supports XCursor and the cursor only has two
498 // colors. Use the XCursor encoding.
499 writer()->writeSetXCursor(server->cursor.width(),
500 server->cursor.height(),
501 server->cursor.hotspot.x,
502 server->cursor.hotspot.y,
503 bitmap.buf, server->cursor.mask.buf);
504 return;
505 } else {
506 // More than two colors
507 if (!cp.supportsLocalCursor) {
508 // FIXME: We could reduce to two colors.
509 vlog.info("Unable to send multicolor cursor: RichCursor not supported by client");
510 return;
511 }
512 }
513 }
514
515 // Use RichCursor
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000516 rdr::U8* transData = writer()->getImageBuf(server->cursor.area());
517 image_getter.translatePixels(server->cursor.data, transData,
Peter Åstrand1e9d32f2005-02-16 13:00:38 +0000518 server->cursor.area());
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000519 writer()->writeSetCursor(server->cursor.width(),
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000520 server->cursor.height(),
521 server->cursor.hotspot,
522 transData, server->cursor.mask.buf);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000523}
524
525
526void VNCSConnectionST::writeFramebufferUpdate()
527{
528 if (state() != RFBSTATE_NORMAL || requested.is_empty()) return;
529
530 server->checkUpdate();
531
532 // If the previous position of the rendered cursor overlaps the source of the
533 // copy, then when the copy happens the corresponding rectangle in the
534 // destination will be wrong, so add it to the changed region.
535
536 if (!updates.get_copied().is_empty() && !renderedCursorRect.is_empty()) {
537 Rect bogusCopiedCursor = (renderedCursorRect.translate(updates.get_delta())
538 .intersect(server->pb->getRect()));
539 if (!updates.get_copied().intersect(bogusCopiedCursor).is_empty()) {
540 updates.add_changed(bogusCopiedCursor);
541 }
542 }
543
544 // If we need to remove the old rendered cursor, just add the rectangle to
545 // the changed region.
546
547 if (removeRenderedCursor) {
548 updates.add_changed(renderedCursorRect);
549 renderedCursorRect.clear();
550 removeRenderedCursor = false;
551 }
552
553 // Return if there is nothing to send the client.
554
555 if (updates.is_empty() && !writer()->needFakeUpdate() && !drawRenderedCursor)
556 return;
557
558 // If the client needs a server-side rendered cursor, work out the cursor
559 // rectangle. If it's empty then don't bother drawing it, but if it overlaps
560 // with the update region, we need to draw the rendered cursor regardless of
561 // whether it has changed.
562
563 if (needRenderedCursor()) {
564 renderedCursorRect
565 = (server->renderedCursor.getRect(server->renderedCursorTL)
566 .intersect(requested.get_bounding_rect()));
567
568 if (renderedCursorRect.is_empty()) {
569 drawRenderedCursor = false;
570 } else if (!updates.get_changed().union_(updates.get_copied())
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000571 .intersect(renderedCursorRect).is_empty()) {
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000572 drawRenderedCursor = true;
573 }
574
575 // We could remove the new cursor rect from updates here. It's not clear
576 // whether this is worth it. If we do remove it, then we won't draw over
577 // the same bit of screen twice, but we have the overhead of a more complex
578 // region.
579
580 //if (drawRenderedCursor)
581 // updates.subtract(renderedCursorRect);
582 }
583
584 UpdateInfo update;
585 updates.enable_copyrect(cp.useCopyRect);
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000586 updates.getUpdateInfo(&update, requested);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000587 if (!update.is_empty() || writer()->needFakeUpdate() || drawRenderedCursor) {
Peter Åstrand016dc192005-02-10 16:41:06 +0000588 // Compute the number of rectangles. Tight encoder makes the things more
589 // complicated as compared to the original RealVNC.
590 writer()->setupCurrentEncoder();
591 int nRects = update.copied.numRects() + (drawRenderedCursor ? 1 : 0);
592 std::vector<Rect> rects;
593 std::vector<Rect>::const_iterator i;
594 update.changed.get_rects(&rects);
595 for (i = rects.begin(); i != rects.end(); i++) {
596 if (i->width() && i->height())
597 nRects += writer()->getNumRects(*i);
598 }
599
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000600 writer()->writeFramebufferUpdateStart(nRects);
601 Region updatedRegion;
602 writer()->writeRects(update, &image_getter, &updatedRegion);
603 updates.subtract(updatedRegion);
604 if (drawRenderedCursor)
605 writeRenderedCursorRect();
606 writer()->writeFramebufferUpdateEnd();
607 requested.clear();
608 }
609}
610
611
612// writeRenderedCursorRect() writes a single rectangle drawing the rendered
613// cursor on the client.
614
615void VNCSConnectionST::writeRenderedCursorRect()
616{
617 image_getter.setPixelBuffer(&server->renderedCursor);
618 image_getter.setOffset(server->renderedCursorTL);
619
620 Rect actual;
621 writer()->writeRect(renderedCursorRect, &image_getter, &actual);
622
623 image_getter.setPixelBuffer(server->pb);
624 image_getter.setOffset(Point(0,0));
625
626 drawRenderedCursor = false;
627}
628
629void VNCSConnectionST::setColourMapEntries(int firstColour, int nColours)
630{
631 if (!readyForSetColourMapEntries) return;
632 if (server->pb->getPF().trueColour) return;
633
634 image_getter.setColourMapEntries(firstColour, nColours, writer());
635
636 if (cp.pf().trueColour) {
637 updates.add_changed(server->pb->getRect());
638 }
639}
640
641
642// setCursor() is called whenever the cursor has changed shape or pixel format.
643// If the client supports local cursor then it will arrange for the cursor to
644// be sent to the client.
645
646void VNCSConnectionST::setCursor()
647{
648 if (state() != RFBSTATE_NORMAL || !cp.supportsLocalCursor) return;
649 writer()->cursorChange(this);
650 if (writer()->needFakeUpdate())
651 writeFramebufferUpdate();
652}
653
654void VNCSConnectionST::setSocketTimeouts()
655{
656 int timeoutms = rfb::Server::clientWaitTimeMillis;
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000657 soonestTimeout(&timeoutms, secsToMillis(rfb::Server::idleTimeout));
658 if (timeoutms == 0)
659 timeoutms = -1;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000660 sock->inStream().setTimeout(timeoutms);
661 sock->outStream().setTimeout(timeoutms);
662}
Oleg Sheikinff43bfd2005-12-07 08:02:52 +0000663
664char* VNCSConnectionST::getStartTime()
665{
Constantin Kaplinsky64929742006-01-26 14:32:31 +0000666 // FIXME: Using ctime() is not thread-safe.
667 // Also, it's not good to return the pointer instead of copying.
Oleg Sheikinff43bfd2005-12-07 08:02:52 +0000668 char* result = ctime(&startTime);
669 result[24] = '\0';
Constantin Kaplinsky64929742006-01-26 14:32:31 +0000670 return result;
Oleg Sheikinff43bfd2005-12-07 08:02:52 +0000671}
Oleg Sheikin4b0304f2005-12-09 10:59:12 +0000672
673void VNCSConnectionST::setStatus(int status)
674{
Constantin Kaplinsky64929742006-01-26 14:32:31 +0000675 // FIXME: What do numbers mean?
Oleg Sheikin4b0304f2005-12-09 10:59:12 +0000676 switch (status) {
677 case 0:
678 accessRights = accessRights | AccessPtrEvents | AccessKeyEvents | AccessView;
679 break;
680 case 1:
681 accessRights = accessRights & !(AccessPtrEvents | AccessKeyEvents) | AccessView;
682 break;
683 case 2:
684 accessRights = accessRights & !(AccessPtrEvents | AccessKeyEvents | AccessView);
685 break;
686 }
Oleg Sheikind87a7a72006-01-12 15:27:04 +0000687 framebufferUpdateRequest(server->pb->getRect(), false);
Oleg Sheikin4b0304f2005-12-09 10:59:12 +0000688}
Constantin Kaplinsky64929742006-01-26 14:32:31 +0000689
Oleg Sheikin4b0304f2005-12-09 10:59:12 +0000690int VNCSConnectionST::getStatus()
691{
Constantin Kaplinsky64929742006-01-26 14:32:31 +0000692 // FIXME: What do numbers mean?
Oleg Sheikin4b0304f2005-12-09 10:59:12 +0000693 if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0007)
694 return 0;
695 if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0001)
696 return 1;
697 if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0000)
698 return 2;
699 return 4;
Dennis Syrovatskyea354642005-12-18 13:10:26 +0000700}
701
702bool VNCSConnectionST::processFTMsg(int type)
703{
704 return false;
Constantin Kaplinsky4cea5e72006-01-13 10:52:39 +0000705}