blob: 9b5294ee2cf01c7eb63494a9546bfefef11be3de [file] [log] [blame]
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00001/* Copyright (C) 2002-2004 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// XserverDesktop.cxx
20//
21
22#include <stdio.h>
23#include <strings.h>
24#include <unistd.h>
25#include <pwd.h>
26#include <sys/types.h>
27#include <sys/stat.h>
28#include <fcntl.h>
29#include <sys/utsname.h>
30#include <network/TcpSocket.h>
31#include <rfb/Exception.h>
32#include <rfb/VNCServerST.h>
33#include <rfb/HTTPServer.h>
34#include <rfb/LogWriter.h>
35#include <rfb/Configuration.h>
36#include "XserverDesktop.h"
37#include "vncExtInit.h"
38
39extern "C" {
40#define public c_public
41#define class c_class
42
43 // windowTable is in globals.h in XFree 4, but not in XFree 3 unfortunately
44extern WindowPtr *WindowTable;
45extern char *display;
46
47#include "inputstr.h"
48#include "servermd.h"
49#include "colormapst.h"
50#include "resource.h"
51#include "cursorstr.h"
52#include "windowstr.h"
53#define XK_CYRILLIC
54#include "keysym.h"
55#undef public
56#undef class
57}
58
59using namespace rfb;
60using namespace network;
61
62static LogWriter vlog("XserverDesktop");
63
64rfb::IntParameter deferUpdateTime("DeferUpdate",
65 "Time in milliseconds to defer updates",40);
66
67rfb::BoolParameter alwaysSetDeferUpdateTimer("AlwaysSetDeferUpdateTimer",
68 "Always reset the defer update timer on every change",false);
69
70static KeyCode KeysymToKeycode(KeySymsPtr keymap, KeySym ks, int* col);
71
72static rdr::U8 reverseBits[] = {
73 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0,
74 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
75 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4,
76 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
77 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc,
78 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
79 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca,
80 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
81 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6,
82 0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
83 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1,
84 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
85 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9,
86 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
87 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd,
88 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
89 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3,
90 0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
91 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7,
92 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
93 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf,
94 0x3f, 0xbf, 0x7f, 0xff
95};
96
97
98class MyHTTPServer : public rfb::HTTPServer {
99public:
100 MyHTTPServer(XserverDesktop* d) : desktop(d) {}
101 virtual ~MyHTTPServer() {}
102
103 virtual rdr::InStream* getFile(const char* name, const char** contentType) {
104 if (name[0] != '/' || strstr(name, "..") != 0) {
105 vlog.info("http request was for invalid file name");
106 return 0;
107 }
108
109 if (strcmp(name, "/") == 0) name = "/index.vnc";
110
111 CharArray httpDirStr(httpDir.getData());
112 CharArray fname(strlen(httpDirStr.buf)+strlen(name)+1);
113 sprintf(fname.buf, "%s%s", httpDirStr.buf, name);
114 int fd = open(fname.buf, O_RDONLY);
115 if (fd < 0) return 0;
116
117 rdr::InStream* is = new rdr::FdInStream(fd, -1, 0, true);
118 *contentType = guessContentType(name, *contentType);
119 if (strlen(name) > 4 && strcasecmp(&name[strlen(name)-4], ".vnc") == 0) {
120 is = new rdr::SubstitutingInStream(is, desktop, 20);
121 *contentType = "text/html";
122 }
123 return is;
124 }
125
126 XserverDesktop* desktop;
127};
128
129
130XserverDesktop::XserverDesktop(ScreenPtr pScreen_,
131 network::TcpListener* listener_,
132 network::TcpListener* httpListener_,
133 const char* name, void* fbptr)
134 : pScreen(pScreen_), deferredUpdateTimer(0), dummyTimer(0),
135 server(0), httpServer(0),
136 listener(listener_), httpListener(httpListener_),
137 cmap(0), deferredUpdateTimerSet(false),
138 grabbing(false), ignoreHooks_(false), directFbptr(fbptr != 0),
139 oldButtonMask(0), cursorX(0), cursorY(0),
140 oldCursorX(0), oldCursorY(0)
141{
142 int i;
143 format.depth = pScreen->rootDepth;
144 for (i = 0; i < screenInfo.numPixmapFormats; i++) {
145 if (screenInfo.formats[i].depth == format.depth) {
146 format.bpp = screenInfo.formats[i].bitsPerPixel;
147 break;
148 }
149 }
150 if (i == screenInfo.numPixmapFormats) {
151 fprintf(stderr,"no pixmap format for root depth???\n");
152 abort();
153 }
154 format.bigEndian = (screenInfo.imageByteOrder == MSBFirst);
155
156 VisualPtr vis;
157 for (i = 0; i < pScreen->numVisuals; i++) {
158 if (pScreen->visuals[i].vid == pScreen->rootVisual) {
159 vis = &pScreen->visuals[i];
160 break;
161 }
162 }
163 if (i == pScreen->numVisuals) {
164 fprintf(stderr,"no visual rec for root visual???\n");
165 abort();
166 }
167 format.trueColour = (vis->c_class == TrueColor);
168 if (!format.trueColour && format.bpp != 8)
169 throw rfb::Exception("X server uses unsupported visual");
170 format.redShift = ffs(vis->redMask) - 1;
171 format.greenShift = ffs(vis->greenMask) - 1;
172 format.blueShift = ffs(vis->blueMask) - 1;
173 format.redMax = vis->redMask >> format.redShift;
174 format.greenMax = vis->greenMask >> format.greenShift;
175 format.blueMax = vis->blueMask >> format.blueShift;
176
177 width_ = pScreen->width;
178 height_ = pScreen->height;
179 if (fbptr)
180 data = (rdr::U8*)fbptr;
181 else
182 data = new rdr::U8[pScreen->width * pScreen->height * (format.bpp/8)];
183 colourmap = this;
184
185 serverReset(pScreen);
186
187 server = new VNCServerST(name, this);
188 server->setPixelBuffer(this);
189
190 if (httpListener)
191 httpServer = new MyHTTPServer(this);
192}
193
194XserverDesktop::~XserverDesktop()
195{
196 if (!directFbptr)
197 delete [] data;
198 TimerFree(deferredUpdateTimer);
199 TimerFree(dummyTimer);
200 delete httpServer;
201 delete server;
202}
203
204void XserverDesktop::serverReset(ScreenPtr pScreen_)
205{
206 pScreen = pScreen_;
207 XID* ids = new XID[pScreen->maxInstalledCmaps];
208 int nmaps = (*pScreen->ListInstalledColormaps)(pScreen, ids);
209 cmap = (ColormapPtr)LookupIDByType(ids[0], RT_COLORMAP);
210 delete [] ids;
211}
212
213char* XserverDesktop::substitute(const char* varName)
214{
215 if (strcmp(varName, "$$") == 0) {
216 return rfb::strDup("$");
217 }
218 if (strcmp(varName, "$PORT") == 0) {
219 char* str = new char[10];
220 sprintf(str, "%d", listener ? listener->getMyPort() : 0);
221 return str;
222 }
223 if (strcmp(varName, "$WIDTH") == 0) {
224 char* str = new char[10];
225 sprintf(str, "%d", width());
226 return str;
227 }
228 if (strcmp(varName, "$HEIGHT") == 0) {
229 char* str = new char[10];
230 sprintf(str, "%d", height());
231 return str;
232 }
233 if (strcmp(varName, "$APPLETWIDTH") == 0) {
234 char* str = new char[10];
235 sprintf(str, "%d", width());
236 return str;
237 }
238 if (strcmp(varName, "$APPLETHEIGHT") == 0) {
239 char* str = new char[10];
240 sprintf(str, "%d", height() + 32);
241 return str;
242 }
243 if (strcmp(varName, "$DESKTOP") == 0) {
244 return rfb::strDup(server->getName());
245 }
246 if (strcmp(varName, "$DISPLAY") == 0) {
247 struct utsname uts;
248 uname(&uts);
249 char* str = new char[256];
250 strncat(str, uts.nodename, 240);
251 strcat(str, ":");
252 strncat(str, display, 10);
253 return str;
254 }
255 if (strcmp(varName, "$USER") == 0) {
256 struct passwd* user = getpwuid(getuid());
257 return rfb::strDup(user ? user->pw_name : "?");
258 }
259 return 0;
260}
261
262void XserverDesktop::setColormap(ColormapPtr cmap_)
263{
264 if (cmap != cmap_) {
265 cmap = cmap_;
266 setColourMapEntries(0, 0);
267 }
268}
269
270void XserverDesktop::setColourMapEntries(ColormapPtr pColormap, int ndef,
271 xColorItem* pdef)
272{
273 if (cmap != pColormap || ndef <= 0) return;
274
275 int first = pdef[0].pixel;
276 int n = 1;
277
278 for (int i = 1; i < ndef; i++) {
279 if (first + n == pdef[i].pixel) {
280 n++;
281 } else {
282 setColourMapEntries(first, n);
283 first = pdef[i].pixel;
284 n = 1;
285 }
286 }
287 setColourMapEntries(first, n);
288}
289
290void XserverDesktop::setColourMapEntries(int firstColour, int nColours)
291{
292 try {
293 server->setColourMapEntries(firstColour, nColours);
294 } catch (rdr::Exception& e) {
295 vlog.error("XserverDesktop::setColourMapEntries: %s",e.str());
296 }
297}
298
299void XserverDesktop::bell()
300{
301 server->bell();
302}
303
304void XserverDesktop::serverCutText(const char* str, int len)
305{
306 try {
307 server->serverCutText(str, len);
308 } catch (rdr::Exception& e) {
309 vlog.error("XserverDesktop::serverCutText: %s",e.str());
310 }
311}
312
313void XserverDesktop::setCursor(CursorPtr cursor)
314{
315 try {
316 int w = cursor->bits->width;
317 int h = cursor->bits->height;
318 rdr::U8* cursorData = new rdr::U8[w * h * (getPF().bpp / 8)];
319
320 xColorItem fg, bg;
321 fg.red = cursor->foreRed;
322 fg.green = cursor->foreGreen;
323 fg.blue = cursor->foreBlue;
324 FakeAllocColor(cmap, &fg);
325 bg.red = cursor->backRed;
326 bg.green = cursor->backGreen;
327 bg.blue = cursor->backBlue;
328 FakeAllocColor(cmap, &bg);
329 FakeFreeColor(cmap, fg.pixel);
330 FakeFreeColor(cmap, bg.pixel);
331
332 int xMaskBytesPerRow = BitmapBytePad(w);
333
334 for (int y = 0; y < h; y++) {
335 for (int x = 0; x < w; x++) {
336 int byte = y * xMaskBytesPerRow + x / 8;
337#if (BITMAP_BIT_ORDER == MSBFirst)
338 int bit = 7 - x % 8;
339#else
340 int bit = x % 8;
341#endif
342 switch (getPF().bpp) {
343 case 8:
344 ((rdr::U8*)cursorData)[y * w + x]
345 = (cursor->bits->source[byte] & (1 << bit)) ? fg.pixel : bg.pixel;
346 break;
347 case 16:
348 ((rdr::U16*)cursorData)[y * w + x]
349 = (cursor->bits->source[byte] & (1 << bit)) ? fg.pixel : bg.pixel;
350 break;
351 case 32:
352 ((rdr::U32*)cursorData)[y * w + x]
353 = (cursor->bits->source[byte] & (1 << bit)) ? fg.pixel : bg.pixel;
354 break;
355 }
356 }
357 }
358
359 int rfbMaskBytesPerRow = (w + 7) / 8;
360
361 rdr::U8* cursorMask = new rdr::U8[rfbMaskBytesPerRow * h];
362
363 for (int j = 0; j < h; j++) {
364 for (int i = 0; i < rfbMaskBytesPerRow; i++)
365#if (BITMAP_BIT_ORDER == MSBFirst)
366 cursorMask[j * rfbMaskBytesPerRow + i]
367 = cursor->bits->mask[j * xMaskBytesPerRow + i];
368#else
369 cursorMask[j * rfbMaskBytesPerRow + i]
370 = reverseBits[cursor->bits->mask[j * xMaskBytesPerRow + i]];
371#endif
372 }
373
374 server->setCursor(cursor->bits->width, cursor->bits->height,
375 cursor->bits->xhot, cursor->bits->yhot,
376 cursorData, cursorMask);
377 server->tryUpdate();
378 delete [] cursorData;
379 delete [] cursorMask;
380 } catch (rdr::Exception& e) {
381 vlog.error("XserverDesktop::setCursor: %s",e.str());
382 }
383}
384
385static void printRegion(RegionPtr reg)
386{
387 int nrects = REGION_NUM_RECTS(reg);
388
389 fprintf(stderr,"Region num rects %2d extents %3d,%3d %3dx%3d\n",nrects,
390 (REGION_EXTENTS(pScreen,reg))->x1,
391 (REGION_EXTENTS(pScreen,reg))->y1,
392 (REGION_EXTENTS(pScreen,reg))->x2-(REGION_EXTENTS(pScreen,reg))->x1,
393 (REGION_EXTENTS(pScreen,reg))->y2-(REGION_EXTENTS(pScreen,reg))->y1);
394
395 for (int i = 0; i < nrects; i++) {
396 fprintf(stderr," rect %3d,%3d %3dx%3d\n",
397 REGION_RECTS(reg)[i].x1,
398 REGION_RECTS(reg)[i].y1,
399 REGION_RECTS(reg)[i].x2-REGION_RECTS(reg)[i].x1,
400 REGION_RECTS(reg)[i].y2-REGION_RECTS(reg)[i].y1);
401 }
402}
403
404CARD32 XserverDesktop::deferredUpdateTimerCallback(OsTimerPtr timer,
405 CARD32 now, pointer arg)
406{
407 XserverDesktop* desktop = (XserverDesktop*)arg;
408 desktop->deferredUpdateTimerSet = false;
409 try {
410 desktop->server->tryUpdate();
411 } catch (rdr::Exception& e) {
412 vlog.error("XserverDesktop::deferredUpdateTimerCallback: %s",e.str());
413 }
414 return 0;
415}
416
417void XserverDesktop::add_changed(RegionPtr reg)
418{
419 if (ignoreHooks_) return;
420 if (grabbing) return;
421 try {
422 rfb::Region rfbReg;
423 rfbReg.setExtentsAndOrderedRects((ShortRect*)REGION_EXTENTS(pScreen, reg),
424 REGION_NUM_RECTS(reg),
425 (ShortRect*)REGION_RECTS(reg));
426 server->add_changed(rfbReg);
427 if (!deferredUpdateTimerSet || alwaysSetDeferUpdateTimer) {
428 deferredUpdateTimer = TimerSet(deferredUpdateTimer, 0,
429 deferUpdateTime,
430 deferredUpdateTimerCallback, this);
431 deferredUpdateTimerSet = true;
432 }
433 } catch (rdr::Exception& e) {
434 vlog.error("XserverDesktop::add_changed: %s",e.str());
435 }
436}
437
438void XserverDesktop::add_copied(RegionPtr dst, int dx, int dy)
439{
440 if (ignoreHooks_) return;
441 if (grabbing) return;
442 try {
443 rfb::Region rfbReg;
444 rfbReg.setExtentsAndOrderedRects((ShortRect*)REGION_EXTENTS(pScreen, dst),
445 REGION_NUM_RECTS(dst),
446 (ShortRect*)REGION_RECTS(dst));
447 server->add_copied(rfbReg, rfb::Point(dx, dy));
448 if (!deferredUpdateTimerSet || alwaysSetDeferUpdateTimer) {
449 deferredUpdateTimer = TimerSet(deferredUpdateTimer, 0,
450 deferUpdateTime,
451 deferredUpdateTimerCallback, this);
452 deferredUpdateTimerSet = true;
453 }
454 } catch (rdr::Exception& e) {
455 vlog.error("XserverDesktop::add_copied: %s",e.str());
456 }
457}
458
459void XserverDesktop::positionCursor()
460{
461 if (cursorX != oldCursorX || cursorY != oldCursorY) {
462 oldCursorX = cursorX;
463 oldCursorY = cursorY;
464 (*pScreen->SetCursorPosition) (pScreen, cursorX, cursorY, FALSE);
465 server->setCursorPos(cursorX, cursorY);
466 server->tryUpdate();
467 }
468}
469
470void XserverDesktop::blockHandler(fd_set* fds)
471{
472 try {
473 ScreenPtr screenWithCursor = GetCurrentRootWindow()->drawable.pScreen;
474 if (screenWithCursor == pScreen) {
475 int x, y;
476 GetSpritePosition(&x, &y);
477 if (x != cursorX || y != cursorY) {
478 cursorX = oldCursorX = x;
479 cursorY = oldCursorY = y;
480 server->setCursorPos(x, y);
481 server->tryUpdate();
482 }
483 }
484
485 if (listener)
486 FD_SET(listener->getFd(), fds);
487 if (httpListener)
488 FD_SET(httpListener->getFd(), fds);
489
490 std::list<Socket*> sockets;
491 server->getSockets(&sockets);
492 std::list<Socket*>::iterator i;
493 for (i = sockets.begin(); i != sockets.end(); i++) {
494 FD_SET((*i)->getFd(), fds);
495 }
496 if (httpServer) {
497 httpServer->getSockets(&sockets);
498 for (i = sockets.begin(); i != sockets.end(); i++) {
499 FD_SET((*i)->getFd(), fds);
500 }
501 }
502 } catch (rdr::Exception& e) {
503 vlog.error("XserverDesktop::blockHandler: %s",e.str());
504 }
505}
506
507static CARD32 dummyTimerCallback(OsTimerPtr timer, CARD32 now, pointer arg) {
508 return 0;
509}
510
511void XserverDesktop::wakeupHandler(fd_set* fds, int nfds)
512{
513 try {
514 if (nfds >= 1) {
515
516 if (listener) {
517 if (FD_ISSET(listener->getFd(), fds)) {
518 FD_CLR(listener->getFd(), fds);
519 Socket* sock = listener->accept();
520 server->addClient(sock);
521 vlog.debug("new client, sock %d",sock->getFd());
522 }
523 }
524
525 if (httpListener) {
526 if (FD_ISSET(httpListener->getFd(), fds)) {
527 FD_CLR(httpListener->getFd(), fds);
528 Socket* sock = httpListener->accept();
529 httpServer->addClient(sock);
530 vlog.debug("new http client, sock %d",sock->getFd());
531 }
532 }
533
534 std::list<Socket*> sockets;
535 server->getSockets(&sockets);
536 std::list<Socket*>::iterator i;
537 for (i = sockets.begin(); i != sockets.end(); i++) {
538 int fd = (*i)->getFd();
539 if (FD_ISSET(fd, fds)) {
540 FD_CLR(fd, fds);
541 if (!server->processSocketEvent(*i)) {
542 vlog.debug("client gone, sock %d",fd);
543 vncClientGone(fd);
544 }
545 }
546 }
547
548 if (httpServer) {
549 httpServer->getSockets(&sockets);
550 for (i = sockets.begin(); i != sockets.end(); i++) {
551 int fd = (*i)->getFd();
552 if (FD_ISSET(fd, fds)) {
553 FD_CLR(fd, fds);
554 if (!httpServer->processSocketEvent(*i)) {
555 vlog.debug("http client gone, sock %d",fd);
556 }
557 }
558 }
559 }
560
561 positionCursor();
562 }
563
564 int timeout = server->checkTimeouts();
565 if (timeout > 0) {
566 // set a dummy timer just so we are guaranteed be called again next time.
567 dummyTimer = TimerSet(dummyTimer, 0, timeout,
568 dummyTimerCallback, 0);
569 }
570
571 } catch (rdr::Exception& e) {
572 vlog.error("XserverDesktop::wakeupHandler: %s",e.str());
573 }
574}
575
576void XserverDesktop::addClient(Socket* sock, bool reverse)
577{
578 vlog.debug("new client, sock %d reverse %d",sock->getFd(),reverse);
579 server->addClient(sock, reverse);
580}
581
582void XserverDesktop::disconnectClients()
583{
584 vlog.debug("disconnecting all clients");
585 return server->closeClients("Disconnection from server end");
586}
587
588
589///////////////////////////////////////////////////////////////////////////
590//
591// SDesktop callbacks
592
593
594void XserverDesktop::pointerEvent(const Point& pos, rdr::U8 buttonMask)
595{
596 xEvent ev;
597 DevicePtr dev = LookupPointerDevice();
598
599 // SetCursorPosition seems to be very expensive (at least on XFree86 3.3.6
600 // for S3), so we delay calling it until positionCursor() is called at the
601 // end of processing a load of RFB.
602 //(*pScreen->SetCursorPosition) (pScreen, pos.x, pos.y, FALSE);
603
604 NewCurrentScreen(pScreen, pos.x, pos.y);
605
606 ev.u.u.type = MotionNotify;
607 ev.u.u.detail = 0;
608 ev.u.keyButtonPointer.rootX = pos.x;
609 ev.u.keyButtonPointer.rootY = pos.y;
610 ev.u.keyButtonPointer.time = GetTimeInMillis();
611
612 if (pos.x != cursorX || pos.y != cursorY)
613 (*dev->processInputProc)(&ev, (DeviceIntPtr)dev, 1);
614
615 for (int i = 0; i < 5; i++) {
616 if ((buttonMask ^ oldButtonMask) & (1<<i)) {
617#ifdef XINPUT
618 // God knows why but some idiot decided to conditionally move the pointer
619 // mapping out of DIX, so we guess here that if XINPUT is defined we have
620 // to do it ourselves...
621 ev.u.u.detail = ((DeviceIntPtr)dev)->button->map[i + 1];
622#else
623 ev.u.u.detail = i + 1;
624#endif
625 ev.u.u.type = (buttonMask & (1<<i)) ? ButtonPress : ButtonRelease;
626 (*dev->processInputProc)(&ev, (DeviceIntPtr)dev, 1);
627 }
628 }
629
630 cursorX = pos.x;
631 cursorY = pos.y;
632 oldButtonMask = buttonMask;
633}
634
635void XserverDesktop::clientCutText(const char* str, int len)
636{
637 vncClientCutText(str, len);
638}
639
640void XserverDesktop::grabRegion(const rfb::Region& region)
641{
642 if (directFbptr) return;
643 if (!pScreen->GetImage) {
644 vlog.error("VNC error: pScreen->GetImage == 0");
645 return;
646 }
647
648 grabbing = true;
649
650 int bytesPerPixel = format.bpp/8;
651 int bytesPerRow = pScreen->width * bytesPerPixel;
652
653 std::vector<rfb::Rect> rects;
654 std::vector<rfb::Rect>::iterator i;
655 region.get_rects(&rects);
656 for (i = rects.begin(); i != rects.end(); i++) {
657 for (int y = i->tl.y; y < i->br.y; y++) {
658 (*pScreen->GetImage) ((DrawablePtr)WindowTable[pScreen->myNum],
659 i->tl.x, y, i->width(), 1,
660 ZPixmap, (unsigned long)~0L,
661 ((char*)data
662 + y * bytesPerRow + i->tl.x * bytesPerPixel));
663 }
664 }
665 grabbing = false;
666}
667
668void XserverDesktop::lookup(int index, int* r, int* g, int* b)
669{
670 EntryPtr pent;
671 pent = (EntryPtr)&cmap->red[index];
672 if (pent->fShared) {
673 *r = pent->co.shco.red->color;
674 *g = pent->co.shco.green->color;
675 *b = pent->co.shco.blue->color;
676 } else {
677 *r = pent->co.local.red;
678 *g = pent->co.local.green;
679 *b = pent->co.local.blue;
680 }
681}
682
683//
684// Keyboard handling
685//
686
687#define IS_PRESSED(keyc, keycode) \
688 ((keyc)->down[(keycode) >> 3] & (1 << ((keycode) & 7)))
689
690// ModifierState is a class which helps simplify generating a "fake" press
691// or release of shift, ctrl, alt, etc. An instance of the class is created
692// for every modifier which may need to be pressed or released. Then either
693// press() or release() may be called to make sure that the corresponding keys
694// are in the right state. The destructor of the class automatically reverts
695// to the previous state. Each modifier may have multiple keys associated with
696// it, so in the case of a fake release, this may involve releasing more than
697// one key.
698
699class ModifierState {
700public:
701 ModifierState(DeviceIntPtr dev_, int modIndex_)
702 : dev(dev_), modIndex(modIndex_), nKeys(0), keys(0), pressed(false)
703 {
704 }
705 ~ModifierState() {
706 for (int i = 0; i < nKeys; i++)
707 generateXKeyEvent(keys[i], !pressed);
708 delete [] keys;
709 }
710 void press() {
711 KeyClassPtr keyc = dev->key;
712 if (!(keyc->state & (1<<modIndex))) {
713 tempKeyEvent(keyc->modifierKeyMap[modIndex * keyc->maxKeysPerModifier],
714 true);
715 pressed = true;
716 }
717 }
718 void release() {
719 KeyClassPtr keyc = dev->key;
720 if (keyc->state & (1<<modIndex)) {
721 for (int k = 0; k < keyc->maxKeysPerModifier; k++) {
722 int keycode
723 = keyc->modifierKeyMap[modIndex * keyc->maxKeysPerModifier + k];
724 if (keycode && IS_PRESSED(keyc, keycode))
725 tempKeyEvent(keycode, false);
726 }
727 }
728 }
729private:
730 void tempKeyEvent(int keycode, bool down) {
731 if (keycode) {
732 if (!keys) keys = new int[dev->key->maxKeysPerModifier];
733 keys[nKeys++] = keycode;
734 generateXKeyEvent(keycode, down);
735 }
736 }
737 void generateXKeyEvent(int keycode, bool down) {
738 xEvent ev;
739 ev.u.u.type = down ? KeyPress : KeyRelease;
740 ev.u.u.detail = keycode;
741 ev.u.keyButtonPointer.time = GetTimeInMillis();
742 (*dev->c_public.processInputProc)(&ev, dev, 1);
743 vlog.debug("fake keycode %d %s", keycode, down ? "down" : "up");
744 }
745 DeviceIntPtr dev;
746 int modIndex;
747 int nKeys;
748 int* keys;
749 bool pressed;
750};
751
752
753// altKeysym is a table of alternative keysyms which have the same meaning.
754
755struct altKeysym_t {
756 KeySym a, b;
757};
758
759altKeysym_t altKeysym[] = {
760 { XK_Shift_L, XK_Shift_R },
761 { XK_Control_L, XK_Control_R },
762 { XK_Meta_L, XK_Meta_R },
763 { XK_Alt_L, XK_Alt_R },
764 { XK_Super_L, XK_Super_R },
765 { XK_Hyper_L, XK_Hyper_R },
766 { XK_KP_Space, XK_space },
767 { XK_KP_Tab, XK_Tab },
768 { XK_KP_Enter, XK_Return },
769 { XK_KP_F1, XK_F1 },
770 { XK_KP_F2, XK_F2 },
771 { XK_KP_F3, XK_F3 },
772 { XK_KP_F4, XK_F4 },
773 { XK_KP_Home, XK_Home },
774 { XK_KP_Left, XK_Left },
775 { XK_KP_Up, XK_Up },
776 { XK_KP_Right, XK_Right },
777 { XK_KP_Down, XK_Down },
778 { XK_KP_Page_Up, XK_Page_Up },
779 { XK_KP_Page_Down, XK_Page_Down },
780 { XK_KP_End, XK_End },
781 { XK_KP_Begin, XK_Begin },
782 { XK_KP_Insert, XK_Insert },
783 { XK_KP_Delete, XK_Delete },
784 { XK_KP_Equal, XK_equal },
785 { XK_KP_Multiply, XK_asterisk },
786 { XK_KP_Add, XK_plus },
787 { XK_KP_Separator, XK_comma },
788 { XK_KP_Subtract, XK_minus },
789 { XK_KP_Decimal, XK_period },
790 { XK_KP_Divide, XK_slash },
791 { XK_KP_0, XK_0 },
792 { XK_KP_1, XK_1 },
793 { XK_KP_2, XK_2 },
794 { XK_KP_3, XK_3 },
795 { XK_KP_4, XK_4 },
796 { XK_KP_5, XK_5 },
797 { XK_KP_6, XK_6 },
798 { XK_KP_7, XK_7 },
799 { XK_KP_8, XK_8 },
800 { XK_KP_9, XK_9 },
801};
802
803// keyEvent() - work out the best keycode corresponding to the keysym sent by
804// the viewer. This is non-trivial because we can't assume much about the
805// local keyboard layout. We must also find out which column of the keyboard
806// mapping the keysym is in, and alter the shift state appropriately. Column 0
807// means both shift and "mode_switch" (AltGr) must be released, column 1 means
808// shift must be pressed and mode_switch released, column 2 means shift must be
809// released and mode_switch pressed, and column 3 means both shift and
810// mode_switch must be pressed.
811
812void XserverDesktop::keyEvent(rdr::U32 keysym, bool down)
813{
814 if (keysym == XK_Caps_Lock) {
815 vlog.debug("Ignoring caps lock");
816 return;
817 }
818 DeviceIntPtr dev = (DeviceIntPtr)LookupKeyboardDevice();
819 KeyClassPtr keyc = dev->key;
820 KeySymsPtr keymap = &keyc->curKeySyms;
821
822 // find which modifier Mode_switch is on.
823 int modeSwitchMapIndex = 0;
824 for (int i = 3; i < 8; i++) {
825 for (int k = 0; k < keyc->maxKeysPerModifier; k++) {
826 int keycode = keyc->modifierKeyMap[i * keyc->maxKeysPerModifier + k];
827 for (int j = 0; j < keymap->mapWidth; j++) {
828 if (keycode != 0 &&
829 keymap->map[(keycode - keymap->minKeyCode)
830 * keymap->mapWidth + j] == XK_Mode_switch)
831 {
832 modeSwitchMapIndex = i;
833 break;
834 }
835 }
836 }
837 }
838
839 int col = 0;
840 if (keyc->state & (1<<ShiftMapIndex)) col |= 1;
841 if (modeSwitchMapIndex && (keyc->state & (1<<modeSwitchMapIndex))) col |= 2;
842
843 int kc = KeysymToKeycode(keymap, keysym, &col);
844
845 // Sort out the "shifted Tab" mess. If we are sent a shifted Tab, generate a
846 // local shifted Tab regardless of what the "shifted Tab" keysym is on the
847 // local keyboard (it might be Tab, ISO_Left_Tab or HP's private BackTab
848 // keysym, and quite possibly some others too). We never get ISO_Left_Tab
849 // here because it's already been translated in VNCSConnectionST.
850 if (keysym == XK_Tab && (keyc->state & (1<<ShiftMapIndex)))
851 col |= 1;
852
853 if (kc == 0) {
854 // Not a direct match in the local keyboard mapping. Check for alternative
855 // keysyms with the same meaning.
856 for (int i = 0; i < sizeof(altKeysym) / sizeof(altKeysym_t); i++) {
857 if (keysym == altKeysym[i].a)
858 kc = KeysymToKeycode(keymap, altKeysym[i].b, &col);
859 else if (keysym == altKeysym[i].b)
860 kc = KeysymToKeycode(keymap, altKeysym[i].a, &col);
861 if (kc) break;
862 }
863 }
864
865 if (kc == 0) {
866 // Last resort - dynamically add a new key to the keyboard mapping.
867 for (kc = keymap->maxKeyCode; kc >= keymap->minKeyCode; kc--) {
868 if (!keymap->map[(kc - keymap->minKeyCode) * keymap->mapWidth]) {
869 keymap->map[(kc - keymap->minKeyCode) * keymap->mapWidth] = keysym;
870 col = 0;
871 SendMappingNotify(MappingKeyboard, kc, 1, serverClient);
872 vlog.info("Added unknown keysym 0x%x to keycode %d",keysym,kc);
873 break;
874 }
875 }
876 if (kc < keymap->minKeyCode) {
877 vlog.info("Keyboard mapping full - ignoring unknown keysym 0x%x",keysym);
878 return;
879 }
880 }
881
882 // See if it's a modifier key. If so, then don't do any auto-repeat, because
883 // the X server will translate each press into a release followed by a press.
884 for (int i = 0; i < 8; i++) {
885 for (int k = 0; k < keyc->maxKeysPerModifier; k++) {
886 if (kc == keyc->modifierKeyMap[i * keyc->maxKeysPerModifier + k] &&
887 IS_PRESSED(keyc,kc) && down)
888 return;
889 }
890 }
891
892 ModifierState shift(dev, ShiftMapIndex);
893 ModifierState modeSwitch(dev, modeSwitchMapIndex);
894 if (down) {
895 if (col & 1)
896 shift.press();
897 else
898 shift.release();
899 if (modeSwitchMapIndex) {
900 if (col & 2)
901 modeSwitch.press();
902 else
903 modeSwitch.release();
904 }
905 }
906 vlog.debug("keycode %d %s", kc, down ? "down" : "up");
907 xEvent ev;
908 ev.u.u.type = down ? KeyPress : KeyRelease;
909 ev.u.u.detail = kc;
910 ev.u.keyButtonPointer.time = GetTimeInMillis();
911 (*dev->c_public.processInputProc)(&ev, dev, 1);
912}
913
914
915void XConvertCase(KeySym sym, KeySym *lower, KeySym *upper)
916{
917 *lower = sym;
918 *upper = sym;
919 switch(sym >> 8) {
920 case 0: /* Latin 1 */
921 if ((sym >= XK_A) && (sym <= XK_Z))
922 *lower += (XK_a - XK_A);
923 else if ((sym >= XK_a) && (sym <= XK_z))
924 *upper -= (XK_a - XK_A);
925 else if ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis))
926 *lower += (XK_agrave - XK_Agrave);
927 else if ((sym >= XK_agrave) && (sym <= XK_odiaeresis))
928 *upper -= (XK_agrave - XK_Agrave);
929 else if ((sym >= XK_Ooblique) && (sym <= XK_Thorn))
930 *lower += (XK_oslash - XK_Ooblique);
931 else if ((sym >= XK_oslash) && (sym <= XK_thorn))
932 *upper -= (XK_oslash - XK_Ooblique);
933 break;
934 case 1: /* Latin 2 */
935 /* Assume the KeySym is a legal value (ignore discontinuities) */
936 if (sym == XK_Aogonek)
937 *lower = XK_aogonek;
938 else if (sym >= XK_Lstroke && sym <= XK_Sacute)
939 *lower += (XK_lstroke - XK_Lstroke);
940 else if (sym >= XK_Scaron && sym <= XK_Zacute)
941 *lower += (XK_scaron - XK_Scaron);
942 else if (sym >= XK_Zcaron && sym <= XK_Zabovedot)
943 *lower += (XK_zcaron - XK_Zcaron);
944 else if (sym == XK_aogonek)
945 *upper = XK_Aogonek;
946 else if (sym >= XK_lstroke && sym <= XK_sacute)
947 *upper -= (XK_lstroke - XK_Lstroke);
948 else if (sym >= XK_scaron && sym <= XK_zacute)
949 *upper -= (XK_scaron - XK_Scaron);
950 else if (sym >= XK_zcaron && sym <= XK_zabovedot)
951 *upper -= (XK_zcaron - XK_Zcaron);
952 else if (sym >= XK_Racute && sym <= XK_Tcedilla)
953 *lower += (XK_racute - XK_Racute);
954 else if (sym >= XK_racute && sym <= XK_tcedilla)
955 *upper -= (XK_racute - XK_Racute);
956 break;
957 case 2: /* Latin 3 */
958 /* Assume the KeySym is a legal value (ignore discontinuities) */
959 if (sym >= XK_Hstroke && sym <= XK_Hcircumflex)
960 *lower += (XK_hstroke - XK_Hstroke);
961 else if (sym >= XK_Gbreve && sym <= XK_Jcircumflex)
962 *lower += (XK_gbreve - XK_Gbreve);
963 else if (sym >= XK_hstroke && sym <= XK_hcircumflex)
964 *upper -= (XK_hstroke - XK_Hstroke);
965 else if (sym >= XK_gbreve && sym <= XK_jcircumflex)
966 *upper -= (XK_gbreve - XK_Gbreve);
967 else if (sym >= XK_Cabovedot && sym <= XK_Scircumflex)
968 *lower += (XK_cabovedot - XK_Cabovedot);
969 else if (sym >= XK_cabovedot && sym <= XK_scircumflex)
970 *upper -= (XK_cabovedot - XK_Cabovedot);
971 break;
972 case 3: /* Latin 4 */
973 /* Assume the KeySym is a legal value (ignore discontinuities) */
974 if (sym >= XK_Rcedilla && sym <= XK_Tslash)
975 *lower += (XK_rcedilla - XK_Rcedilla);
976 else if (sym >= XK_rcedilla && sym <= XK_tslash)
977 *upper -= (XK_rcedilla - XK_Rcedilla);
978 else if (sym == XK_ENG)
979 *lower = XK_eng;
980 else if (sym == XK_eng)
981 *upper = XK_ENG;
982 else if (sym >= XK_Amacron && sym <= XK_Umacron)
983 *lower += (XK_amacron - XK_Amacron);
984 else if (sym >= XK_amacron && sym <= XK_umacron)
985 *upper -= (XK_amacron - XK_Amacron);
986 break;
987 case 6: /* Cyrillic */
988 /* Assume the KeySym is a legal value (ignore discontinuities) */
989 if (sym >= XK_Serbian_DJE && sym <= XK_Serbian_DZE)
990 *lower -= (XK_Serbian_DJE - XK_Serbian_dje);
991 else if (sym >= XK_Serbian_dje && sym <= XK_Serbian_dze)
992 *upper += (XK_Serbian_DJE - XK_Serbian_dje);
993 else if (sym >= XK_Cyrillic_YU && sym <= XK_Cyrillic_HARDSIGN)
994 *lower -= (XK_Cyrillic_YU - XK_Cyrillic_yu);
995 else if (sym >= XK_Cyrillic_yu && sym <= XK_Cyrillic_hardsign)
996 *upper += (XK_Cyrillic_YU - XK_Cyrillic_yu);
997 break;
998 case 7: /* Greek */
999 /* Assume the KeySym is a legal value (ignore discontinuities) */
1000 if (sym >= XK_Greek_ALPHAaccent && sym <= XK_Greek_OMEGAaccent)
1001 *lower += (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
1002 else if (sym >= XK_Greek_alphaaccent && sym <= XK_Greek_omegaaccent &&
1003 sym != XK_Greek_iotaaccentdieresis &&
1004 sym != XK_Greek_upsilonaccentdieresis)
1005 *upper -= (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
1006 else if (sym >= XK_Greek_ALPHA && sym <= XK_Greek_OMEGA)
1007 *lower += (XK_Greek_alpha - XK_Greek_ALPHA);
1008 else if (sym >= XK_Greek_alpha && sym <= XK_Greek_omega &&
1009 sym != XK_Greek_finalsmallsigma)
1010 *upper -= (XK_Greek_alpha - XK_Greek_ALPHA);
1011 break;
1012 }
1013}
1014
1015static KeySym KeyCodetoKeySym(KeySymsPtr keymap, int keycode, int col)
1016{
1017 register int per = keymap->mapWidth;
1018 register KeySym *syms;
1019 KeySym lsym, usym;
1020
1021 if ((col < 0) || ((col >= per) && (col > 3)) ||
1022 (keycode < keymap->minKeyCode) || (keycode > keymap->maxKeyCode))
1023 return NoSymbol;
1024
1025 syms = &keymap->map[(keycode - keymap->minKeyCode) * per];
1026 if (col < 4) {
1027 if (col > 1) {
1028 while ((per > 2) && (syms[per - 1] == NoSymbol))
1029 per--;
1030 if (per < 3)
1031 col -= 2;
1032 }
1033 if ((per <= (col|1)) || (syms[col|1] == NoSymbol)) {
1034 XConvertCase(syms[col&~1], &lsym, &usym);
1035 if (!(col & 1))
1036 return lsym;
1037 // I'm commenting out this logic because it's incorrect even though it
1038 // was copied from the Xlib sources. The X protocol book quite clearly
1039 // states that where a group consists of element 1 being a non-alphabetic
1040 // keysym and element 2 being NoSymbol that you treat the second element
1041 // as being the same as the first. This also tallies with the behaviour
1042 // produced by the installed Xlib on my linux box (I believe this is
1043 // because it uses some XKB code rather than the original Xlib code -
1044 // compare XKBBind.c with KeyBind.c in lib/X11).
1045 // else if (usym == lsym)
1046 // return NoSymbol;
1047 else
1048 return usym;
1049 }
1050 }
1051 return syms[col];
1052}
1053
1054// KeysymToKeycode() - find the keycode and column corresponding to the given
1055// keysym. The value of col passed in should be the column determined from the
1056// current shift state. If the keysym can be found in that column we prefer
1057// that to finding it in a different column (which would require fake events to
1058// alter the shift state).
1059
1060static KeyCode KeysymToKeycode(KeySymsPtr keymap, KeySym ks, int* col)
1061{
1062 register int i, j;
1063
1064 j = *col;
1065 for (i = keymap->minKeyCode; i <= keymap->maxKeyCode; i++) {
1066 if (KeyCodetoKeySym(keymap, i, j) == ks)
1067 return i;
1068 }
1069
1070 for (j = 0; j < keymap->mapWidth; j++) {
1071 for (i = keymap->minKeyCode; i <= keymap->maxKeyCode; i++) {
1072 if (KeyCodetoKeySym(keymap, i, j) == ks) {
1073 *col = j;
1074 return i;
1075 }
1076 }
1077 }
1078 return 0;
1079}