blob: e021aa6ff1c3a86f5d5b55a378d75a78ef3e659d [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),
Pierre Ossmanf99c5712009-03-13 14:41:27 +000039 startTime(time(0))
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000040{
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
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000049 server->clients.push_front(this);
50}
51
52
53VNCSConnectionST::~VNCSConnectionST()
54{
55 // If we reach here then VNCServerST is deleting us!
56 VNCServerST::connectionsLog.write(1,"closed: %s (%s)",
57 peerEndpoint.buf,
58 (closeReason.buf) ? closeReason.buf : "");
59
60 // Release any keys the client still had pressed
61 std::set<rdr::U32>::iterator i;
62 for (i=pressedKeys.begin(); i!=pressedKeys.end(); i++)
63 server->desktop->keyEvent(*i, false);
64 if (server->pointerClient == this)
65 server->pointerClient = 0;
66
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000067 // Remove this client from the server
68 server->clients.remove(this);
69
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
94 if (authenticated()) {
95 server->lastDisconnectTime = time(0);
96 }
97
98 // 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.
101 sock->shutdown();
102 setState(RFBSTATE_CLOSING);
103}
104
105
106void VNCSConnectionST::processMessages()
107{
108 if (state() == RFBSTATE_CLOSING) return;
109 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
Constantin Kaplinsky2ef66952008-08-29 11:33:46 +0000118 // If there were update requests, try to send a framebuffer update.
Pierre Ossman02e43d72009-03-05 11:57:11 +0000119 // We don't send updates immediately on requests as this way, we
120 // give higher priority to user actions such as keyboard and
121 // pointer events.
Constantin Kaplinsky2ef66952008-08-29 11:33:46 +0000122 if (!requested.is_empty()) {
123 writeFramebufferUpdate();
124 }
125
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000126 if (!clientsReadyBefore && !requested.is_empty())
127 server->desktop->framebufferUpdateRequest();
128 } catch (rdr::EndOfStream&) {
129 close("Clean disconnection");
130 } catch (rdr::Exception &e) {
131 close(e.str());
132 }
133}
134
135void VNCSConnectionST::writeFramebufferUpdateOrClose()
136{
137 try {
138 writeFramebufferUpdate();
139 } catch(rdr::Exception &e) {
140 close(e.str());
141 }
142}
143
144void VNCSConnectionST::pixelBufferChange()
145{
146 try {
147 if (!authenticated()) return;
148 if (cp.width && cp.height && (server->pb->width() != cp.width ||
149 server->pb->height() != cp.height))
150 {
151 // We need to clip the next update to the new size, but also add any
152 // extra bits if it's bigger. If we wanted to do this exactly, something
153 // like the code below would do it, but at the moment we just update the
154 // entire new size. However, we do need to clip the renderedCursorRect
155 // because that might be added to updates in writeFramebufferUpdate().
156
157 //updates.intersect(server->pb->getRect());
158 //
159 //if (server->pb->width() > cp.width)
160 // updates.add_changed(Rect(cp.width, 0, server->pb->width(),
161 // server->pb->height()));
162 //if (server->pb->height() > cp.height)
163 // updates.add_changed(Rect(0, cp.height, cp.width,
164 // server->pb->height()));
165
166 renderedCursorRect = renderedCursorRect.intersect(server->pb->getRect());
167
168 cp.width = server->pb->width();
169 cp.height = server->pb->height();
170 if (state() == RFBSTATE_NORMAL) {
171 if (!writer()->writeSetDesktopSize()) {
172 close("Client does not support desktop resize");
173 return;
174 }
175 }
176 }
177 // Just update the whole screen at the moment because we're too lazy to
178 // work out what's actually changed.
179 updates.clear();
180 updates.add_changed(server->pb->getRect());
181 vlog.debug("pixel buffer changed - re-initialising image getter");
182 image_getter.init(server->pb, cp.pf(), writer());
183 if (writer()->needFakeUpdate())
184 writeFramebufferUpdate();
185 } catch(rdr::Exception &e) {
186 close(e.str());
187 }
188}
189
190void VNCSConnectionST::setColourMapEntriesOrClose(int firstColour,int nColours)
191{
192 try {
193 setColourMapEntries(firstColour, nColours);
194 } catch(rdr::Exception& e) {
195 close(e.str());
196 }
197}
198
199void VNCSConnectionST::bell()
200{
201 try {
202 if (state() == RFBSTATE_NORMAL) writer()->writeBell();
203 } catch(rdr::Exception& e) {
204 close(e.str());
205 }
206}
207
208void VNCSConnectionST::serverCutText(const char *str, int len)
209{
210 try {
211 if (!(accessRights & AccessCutText)) return;
212 if (!rfb::Server::sendCutText) return;
213 if (state() == RFBSTATE_NORMAL)
214 writer()->writeServerCutText(str, len);
215 } catch(rdr::Exception& e) {
216 close(e.str());
217 }
218}
219
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000220
221void VNCSConnectionST::setDesktopName(const char *name)
222{
223 cp.setName(name);
224 try {
225 if (state() == RFBSTATE_NORMAL) {
226 if (!writer()->writeSetDesktopName()) {
227 fprintf(stderr, "Client does not support desktop rename\n");
228 }
229 }
230 } catch(rdr::Exception& e) {
231 close(e.str());
232 }
233}
234
235
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000236void VNCSConnectionST::setCursorOrClose()
237{
238 try {
239 setCursor();
240 } catch(rdr::Exception& e) {
241 close(e.str());
242 }
243}
244
245
246int VNCSConnectionST::checkIdleTimeout()
247{
248 int idleTimeout = rfb::Server::idleTimeout;
249 if (idleTimeout == 0) return 0;
250 if (state() != RFBSTATE_NORMAL && idleTimeout < 15)
251 idleTimeout = 15; // minimum of 15 seconds while authenticating
252 time_t now = time(0);
253 if (now < lastEventTime) {
254 // Someone must have set the time backwards. Set lastEventTime so that the
255 // idleTimeout will count from now.
256 vlog.info("Time has gone backwards - resetting idle timeout");
257 lastEventTime = now;
258 }
259 int timeLeft = lastEventTime + idleTimeout - now;
260 if (timeLeft < -60) {
261 // Our callback is over a minute late - someone must have set the time
262 // forwards. Set lastEventTime so that the idleTimeout will count from
263 // now.
264 vlog.info("Time has gone forwards - resetting idle timeout");
265 lastEventTime = now;
266 return secsToMillis(idleTimeout);
267 }
268 if (timeLeft <= 0) {
269 close("Idle timeout");
270 return 0;
271 }
272 return secsToMillis(timeLeft);
273}
274
275// renderedCursorChange() is called whenever the server-side rendered cursor
276// changes shape or position. It ensures that the next update will clean up
277// the old rendered cursor and if necessary draw the new rendered cursor.
278
279void VNCSConnectionST::renderedCursorChange()
280{
281 if (state() != RFBSTATE_NORMAL) return;
282 removeRenderedCursor = true;
283 if (needRenderedCursor())
284 drawRenderedCursor = true;
285}
286
287// needRenderedCursor() returns true if this client needs the server-side
288// rendered cursor. This may be because it does not support local cursor or
289// because the current cursor position has not been set by this client.
290// Unfortunately we can't know for sure when the current cursor position has
291// been set by this client. We guess that this is the case when the current
292// cursor position is the same as the last pointer event from this client, or
293// if it is a very short time since this client's last pointer event (up to a
294// second). [ Ideally we should do finer-grained timing here and make the time
295// configurable, but I don't think it's that important. ]
296
297bool VNCSConnectionST::needRenderedCursor()
298{
299 return (state() == RFBSTATE_NORMAL
300 && (!cp.supportsLocalCursor && !cp.supportsLocalXCursor
301 || (!server->cursorPos.equals(pointerEventPos) &&
302 (time(0) - pointerEventTime) > 0)));
303}
304
305
306void VNCSConnectionST::approveConnectionOrClose(bool accept,
307 const char* reason)
308{
309 try {
310 approveConnection(accept, reason);
311 } catch (rdr::Exception& e) {
312 close(e.str());
313 }
314}
315
316
317
318// -=- Callbacks from SConnection
319
320void VNCSConnectionST::authSuccess()
321{
322 lastEventTime = time(0);
323
324 server->startDesktop();
325
326 // - Set the connection parameters appropriately
327 cp.width = server->pb->width();
328 cp.height = server->pb->height();
329 cp.setName(server->getName());
330
331 // - Set the default pixel format
332 cp.setPF(server->pb->getPF());
333 char buffer[256];
334 cp.pf().print(buffer, 256);
335 vlog.info("Server default pixel format %s", buffer);
336 image_getter.init(server->pb, cp.pf(), 0);
337
338 // - Mark the entire display as "dirty"
339 updates.add_changed(server->pb->getRect());
340 startTime = time(0);
341}
342
343void VNCSConnectionST::queryConnection(const char* userName)
344{
345 // - Authentication succeeded - clear from blacklist
346 CharArray name; name.buf = sock->getPeerAddress();
347 server->blHosts->clearBlackmark(name.buf);
348
349 // - Special case to provide a more useful error message
350 if (rfb::Server::neverShared && !rfb::Server::disconnectClients &&
351 server->authClientCount() > 0) {
352 approveConnection(false, "The server is already in use");
353 return;
354 }
355
356 // - Does the client have the right to bypass the query?
357 if (reverseConnection ||
358 !(rfb::Server::queryConnect || sock->requiresQuery()) ||
359 (accessRights & AccessNoQuery))
360 {
361 approveConnection(true);
362 return;
363 }
364
365 // - Get the server to display an Accept/Reject dialog, if required
366 // If a dialog is displayed, the result will be PENDING, and the
367 // server will call approveConnection at a later time
368 CharArray reason;
369 VNCServerST::queryResult qr = server->queryConnection(sock, userName,
370 &reason.buf);
371 if (qr == VNCServerST::PENDING)
372 return;
373
374 // - If server returns ACCEPT/REJECT then pass result to SConnection
375 approveConnection(qr == VNCServerST::ACCEPT, reason.buf);
376}
377
378void VNCSConnectionST::clientInit(bool shared)
379{
380 lastEventTime = time(0);
381 if (rfb::Server::alwaysShared || reverseConnection) shared = true;
382 if (rfb::Server::neverShared) shared = false;
383 if (!shared) {
384 if (rfb::Server::disconnectClients) {
385 // - Close all the other connected clients
386 vlog.debug("non-shared connection - closing clients");
387 server->closeClients("Non-shared connection requested", getSock());
388 } else {
389 // - Refuse this connection if there are existing clients, in addition to
390 // this one
391 if (server->authClientCount() > 1) {
392 close("Server is already in use");
393 return;
394 }
395 }
396 }
397 SConnection::clientInit(shared);
398}
399
400void VNCSConnectionST::setPixelFormat(const PixelFormat& pf)
401{
402 SConnection::setPixelFormat(pf);
403 char buffer[256];
404 pf.print(buffer, 256);
405 vlog.info("Client pixel format %s", buffer);
406 image_getter.init(server->pb, pf, writer());
407 setCursor();
408}
409
410void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask)
411{
412 pointerEventTime = lastEventTime = time(0);
413 server->lastUserInputTime = lastEventTime;
414 if (!(accessRights & AccessPtrEvents)) return;
415 if (!rfb::Server::acceptPointerEvents) return;
416 if (!server->pointerClient || server->pointerClient == this) {
417 pointerEventPos = pos;
418 if (buttonMask)
419 server->pointerClient = this;
420 else
421 server->pointerClient = 0;
422 server->desktop->pointerEvent(pointerEventPos, buttonMask);
423 }
424}
425
426
427class VNCSConnectionSTShiftPresser {
428public:
429 VNCSConnectionSTShiftPresser(SDesktop* desktop_)
430 : desktop(desktop_), pressed(false) {}
431 ~VNCSConnectionSTShiftPresser() {
432 if (pressed) { desktop->keyEvent(XK_Shift_L, false); }
433 }
434 void press() {
435 desktop->keyEvent(XK_Shift_L, true);
436 pressed = true;
437 }
438 SDesktop* desktop;
439 bool pressed;
440};
441
442// keyEvent() - record in the pressedKeys which keys were pressed. Allow
443// multiple down events (for autorepeat), but only allow a single up event.
444void VNCSConnectionST::keyEvent(rdr::U32 key, bool down) {
445 lastEventTime = time(0);
446 server->lastUserInputTime = lastEventTime;
447 if (!(accessRights & AccessKeyEvents)) return;
448 if (!rfb::Server::acceptKeyEvents) return;
449
450 // Remap the key if required
451 if (server->keyRemapper)
452 key = server->keyRemapper->remapKey(key);
453
454 // Turn ISO_Left_Tab into shifted Tab.
455 VNCSConnectionSTShiftPresser shiftPresser(server->desktop);
456 if (key == XK_ISO_Left_Tab) {
457 if (pressedKeys.find(XK_Shift_L) == pressedKeys.end() &&
458 pressedKeys.find(XK_Shift_R) == pressedKeys.end())
459 shiftPresser.press();
460 key = XK_Tab;
461 }
462
463 if (down) {
464 pressedKeys.insert(key);
465 } else {
466 if (!pressedKeys.erase(key)) return;
467 }
468 server->desktop->keyEvent(key, down);
469}
470
471void VNCSConnectionST::clientCutText(const char* str, int len)
472{
473 if (!(accessRights & AccessCutText)) return;
474 if (!rfb::Server::acceptCutText) return;
475 server->desktop->clientCutText(str, len);
476}
477
478void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
479{
480 if (!(accessRights & AccessView)) return;
481
482 SConnection::framebufferUpdateRequest(r, incremental);
483
Constantin Kaplinsky2ef66952008-08-29 11:33:46 +0000484 // Just update the requested region.
485 // Framebuffer update will be sent a bit later, see processMessages().
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000486 Region reqRgn(r);
487 requested.assign_union(reqRgn);
488
489 if (!incremental) {
490 // Non-incremental update - treat as if area requested has changed
491 updates.add_changed(reqRgn);
492 server->comparer->add_changed(reqRgn);
493 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000494}
495
496void VNCSConnectionST::setInitialColourMap()
497{
498 setColourMapEntries(0, 0);
499}
500
501// supportsLocalCursor() is called whenever the status of
502// cp.supportsLocalCursor has changed. If the client does now support local
503// cursor, we make sure that the old server-side rendered cursor is cleaned up
504// and the cursor is sent to the client.
505
506void VNCSConnectionST::supportsLocalCursor()
507{
508 if (cp.supportsLocalCursor || cp.supportsLocalXCursor) {
509 removeRenderedCursor = true;
510 drawRenderedCursor = false;
511 setCursor();
512 }
513}
514
515void VNCSConnectionST::writeSetCursorCallback()
516{
517 if (cp.supportsLocalXCursor) {
518 Pixel pix0, pix1;
519 rdr::U8Array bitmap(server->cursor.getBitmap(&pix0, &pix1));
520 if (bitmap.buf) {
521 // The client supports XCursor and the cursor only has two
522 // colors. Use the XCursor encoding.
523 writer()->writeSetXCursor(server->cursor.width(),
524 server->cursor.height(),
525 server->cursor.hotspot.x,
526 server->cursor.hotspot.y,
527 bitmap.buf, server->cursor.mask.buf);
528 return;
529 } else {
530 // More than two colors
531 if (!cp.supportsLocalCursor) {
532 // FIXME: We could reduce to two colors.
533 vlog.info("Unable to send multicolor cursor: RichCursor not supported by client");
534 return;
535 }
536 }
537 }
538
539 // Use RichCursor
540 rdr::U8* transData = writer()->getImageBuf(server->cursor.area());
541 image_getter.translatePixels(server->cursor.data, transData,
542 server->cursor.area());
543 writer()->writeSetCursor(server->cursor.width(),
544 server->cursor.height(),
545 server->cursor.hotspot,
546 transData, server->cursor.mask.buf);
547}
548
549
550void VNCSConnectionST::writeFramebufferUpdate()
551{
552 if (state() != RFBSTATE_NORMAL || requested.is_empty()) return;
553
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000554 updates.enable_copyrect(cp.useCopyRect);
555
Pierre Ossman02e43d72009-03-05 11:57:11 +0000556 server->checkUpdate();
Constantin Kaplinskya09dc142008-12-18 12:08:15 +0000557
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000558 // Get the lists of updates. Prior to exporting the data to the `ui' object,
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000559 // getUpdateInfo() will normalize the `updates' object such way that its
Pierre Ossman02e43d72009-03-05 11:57:11 +0000560 // `changed' and `copied' regions would not intersect.
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000561
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000562 UpdateInfo ui;
563 updates.getUpdateInfo(&ui, requested);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000564 bool needNewUpdateInfo = false;
565
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000566 // If the previous position of the rendered cursor overlaps the source of the
567 // copy, then when the copy happens the corresponding rectangle in the
568 // destination will be wrong, so add it to the changed region.
569
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000570 if (!ui.copied.is_empty() && !renderedCursorRect.is_empty()) {
571 Rect bogusCopiedCursor = (renderedCursorRect.translate(ui.copy_delta)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000572 .intersect(server->pb->getRect()));
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000573 if (!ui.copied.intersect(bogusCopiedCursor).is_empty()) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000574 updates.add_changed(bogusCopiedCursor);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000575 needNewUpdateInfo = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000576 }
577 }
578
579 // If we need to remove the old rendered cursor, just add the rectangle to
580 // the changed region.
581
582 if (removeRenderedCursor) {
583 updates.add_changed(renderedCursorRect);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000584 needNewUpdateInfo = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000585 renderedCursorRect.clear();
586 removeRenderedCursor = false;
587 }
588
589 // Return if there is nothing to send the client.
590
591 if (updates.is_empty() && !writer()->needFakeUpdate() && !drawRenderedCursor)
592 return;
593
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000594 // The `updates' object could change, make sure we have valid update info.
595
596 if (needNewUpdateInfo)
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000597 updates.getUpdateInfo(&ui, requested);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000598
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000599 // If the client needs a server-side rendered cursor, work out the cursor
600 // rectangle. If it's empty then don't bother drawing it, but if it overlaps
601 // with the update region, we need to draw the rendered cursor regardless of
602 // whether it has changed.
603
604 if (needRenderedCursor()) {
605 renderedCursorRect
606 = (server->renderedCursor.getRect(server->renderedCursorTL)
607 .intersect(requested.get_bounding_rect()));
608
609 if (renderedCursorRect.is_empty()) {
610 drawRenderedCursor = false;
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000611 } else if (!ui.changed.union_(ui.copied)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000612 .intersect(renderedCursorRect).is_empty()) {
613 drawRenderedCursor = true;
614 }
615
616 // We could remove the new cursor rect from updates here. It's not clear
617 // whether this is worth it. If we do remove it, then we won't draw over
618 // the same bit of screen twice, but we have the overhead of a more complex
619 // region.
620
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000621 //if (drawRenderedCursor) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000622 // updates.subtract(renderedCursorRect);
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000623 // updates.getUpdateInfo(&ui, requested);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000624 //}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000625 }
626
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000627 if (!ui.is_empty() || writer()->needFakeUpdate() || drawRenderedCursor) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000628 // Compute the number of rectangles. Tight encoder makes the things more
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000629 // complicated as compared to the original VNC4.
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000630 writer()->setupCurrentEncoder();
Constantin Kaplinsky1a845d02007-08-31 21:06:53 +0000631 int nRects = (ui.copied.numRects() +
Constantin Kaplinsky1a845d02007-08-31 21:06:53 +0000632 (drawRenderedCursor ? 1 : 0));
Constantin Kaplinsky651606d2007-10-17 17:40:23 +0000633
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000634 std::vector<Rect> rects;
635 std::vector<Rect>::const_iterator i;
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000636 ui.changed.get_rects(&rects);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000637 for (i = rects.begin(); i != rects.end(); i++) {
638 if (i->width() && i->height())
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000639 nRects += writer()->getNumRects(*i);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000640 }
641
642 writer()->writeFramebufferUpdateStart(nRects);
643 Region updatedRegion;
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000644 writer()->writeRects(ui, &image_getter, &updatedRegion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000645 updates.subtract(updatedRegion);
646 if (drawRenderedCursor)
647 writeRenderedCursorRect();
648 writer()->writeFramebufferUpdateEnd();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000649 requested.clear();
650 }
651}
652
653
654// writeRenderedCursorRect() writes a single rectangle drawing the rendered
655// cursor on the client.
656
657void VNCSConnectionST::writeRenderedCursorRect()
658{
659 image_getter.setPixelBuffer(&server->renderedCursor);
660 image_getter.setOffset(server->renderedCursorTL);
661
662 Rect actual;
663 writer()->writeRect(renderedCursorRect, &image_getter, &actual);
664
665 image_getter.setPixelBuffer(server->pb);
666 image_getter.setOffset(Point(0,0));
667
668 drawRenderedCursor = false;
669}
670
671void VNCSConnectionST::setColourMapEntries(int firstColour, int nColours)
672{
673 if (!readyForSetColourMapEntries) return;
674 if (server->pb->getPF().trueColour) return;
675
676 image_getter.setColourMapEntries(firstColour, nColours, writer());
677
678 if (cp.pf().trueColour) {
679 updates.add_changed(server->pb->getRect());
680 }
681}
682
683
684// setCursor() is called whenever the cursor has changed shape or pixel format.
685// If the client supports local cursor then it will arrange for the cursor to
686// be sent to the client.
687
688void VNCSConnectionST::setCursor()
689{
690 if (state() != RFBSTATE_NORMAL || !cp.supportsLocalCursor) return;
691 writer()->cursorChange(this);
692 if (writer()->needFakeUpdate())
693 writeFramebufferUpdate();
694}
695
696void VNCSConnectionST::setSocketTimeouts()
697{
698 int timeoutms = rfb::Server::clientWaitTimeMillis;
699 soonestTimeout(&timeoutms, secsToMillis(rfb::Server::idleTimeout));
700 if (timeoutms == 0)
701 timeoutms = -1;
702 sock->inStream().setTimeout(timeoutms);
703 sock->outStream().setTimeout(timeoutms);
704}
705
706char* VNCSConnectionST::getStartTime()
707{
708 char* result = ctime(&startTime);
709 result[24] = '\0';
710 return result;
711}
712
713void VNCSConnectionST::setStatus(int status)
714{
715 switch (status) {
716 case 0:
717 accessRights = accessRights | AccessPtrEvents | AccessKeyEvents | AccessView;
718 break;
719 case 1:
720 accessRights = accessRights & !(AccessPtrEvents | AccessKeyEvents) | AccessView;
721 break;
722 case 2:
723 accessRights = accessRights & !(AccessPtrEvents | AccessKeyEvents | AccessView);
724 break;
725 }
726 framebufferUpdateRequest(server->pb->getRect(), false);
727}
728int VNCSConnectionST::getStatus()
729{
730 if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0007)
731 return 0;
732 if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0001)
733 return 1;
734 if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0000)
735 return 2;
736 return 4;
737}
738