blob: f53caec0f3fc3abdcfd14ad78a1abd0b9783dc0c [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)) {
Peter Ã…strand0aafa762005-03-10 14:45:22 +0000617 // Do not use the pointer mapping. Treat VNC buttons as logical
618 // buttons.
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000619 ev.u.u.detail = i + 1;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000620 ev.u.u.type = (buttonMask & (1<<i)) ? ButtonPress : ButtonRelease;
621 (*dev->processInputProc)(&ev, (DeviceIntPtr)dev, 1);
622 }
623 }
624
625 cursorX = pos.x;
626 cursorY = pos.y;
627 oldButtonMask = buttonMask;
628}
629
630void XserverDesktop::clientCutText(const char* str, int len)
631{
632 vncClientCutText(str, len);
633}
634
635void XserverDesktop::grabRegion(const rfb::Region& region)
636{
637 if (directFbptr) return;
638 if (!pScreen->GetImage) {
639 vlog.error("VNC error: pScreen->GetImage == 0");
640 return;
641 }
642
643 grabbing = true;
644
645 int bytesPerPixel = format.bpp/8;
646 int bytesPerRow = pScreen->width * bytesPerPixel;
647
648 std::vector<rfb::Rect> rects;
649 std::vector<rfb::Rect>::iterator i;
650 region.get_rects(&rects);
651 for (i = rects.begin(); i != rects.end(); i++) {
652 for (int y = i->tl.y; y < i->br.y; y++) {
653 (*pScreen->GetImage) ((DrawablePtr)WindowTable[pScreen->myNum],
654 i->tl.x, y, i->width(), 1,
655 ZPixmap, (unsigned long)~0L,
656 ((char*)data
657 + y * bytesPerRow + i->tl.x * bytesPerPixel));
658 }
659 }
660 grabbing = false;
661}
662
663void XserverDesktop::lookup(int index, int* r, int* g, int* b)
664{
665 EntryPtr pent;
666 pent = (EntryPtr)&cmap->red[index];
667 if (pent->fShared) {
668 *r = pent->co.shco.red->color;
669 *g = pent->co.shco.green->color;
670 *b = pent->co.shco.blue->color;
671 } else {
672 *r = pent->co.local.red;
673 *g = pent->co.local.green;
674 *b = pent->co.local.blue;
675 }
676}
677
678//
679// Keyboard handling
680//
681
682#define IS_PRESSED(keyc, keycode) \
683 ((keyc)->down[(keycode) >> 3] & (1 << ((keycode) & 7)))
684
685// ModifierState is a class which helps simplify generating a "fake" press
686// or release of shift, ctrl, alt, etc. An instance of the class is created
687// for every modifier which may need to be pressed or released. Then either
688// press() or release() may be called to make sure that the corresponding keys
689// are in the right state. The destructor of the class automatically reverts
690// to the previous state. Each modifier may have multiple keys associated with
691// it, so in the case of a fake release, this may involve releasing more than
692// one key.
693
694class ModifierState {
695public:
696 ModifierState(DeviceIntPtr dev_, int modIndex_)
697 : dev(dev_), modIndex(modIndex_), nKeys(0), keys(0), pressed(false)
698 {
699 }
700 ~ModifierState() {
701 for (int i = 0; i < nKeys; i++)
702 generateXKeyEvent(keys[i], !pressed);
703 delete [] keys;
704 }
705 void press() {
706 KeyClassPtr keyc = dev->key;
707 if (!(keyc->state & (1<<modIndex))) {
708 tempKeyEvent(keyc->modifierKeyMap[modIndex * keyc->maxKeysPerModifier],
709 true);
710 pressed = true;
711 }
712 }
713 void release() {
714 KeyClassPtr keyc = dev->key;
715 if (keyc->state & (1<<modIndex)) {
716 for (int k = 0; k < keyc->maxKeysPerModifier; k++) {
717 int keycode
718 = keyc->modifierKeyMap[modIndex * keyc->maxKeysPerModifier + k];
719 if (keycode && IS_PRESSED(keyc, keycode))
720 tempKeyEvent(keycode, false);
721 }
722 }
723 }
724private:
725 void tempKeyEvent(int keycode, bool down) {
726 if (keycode) {
727 if (!keys) keys = new int[dev->key->maxKeysPerModifier];
728 keys[nKeys++] = keycode;
729 generateXKeyEvent(keycode, down);
730 }
731 }
732 void generateXKeyEvent(int keycode, bool down) {
733 xEvent ev;
734 ev.u.u.type = down ? KeyPress : KeyRelease;
735 ev.u.u.detail = keycode;
736 ev.u.keyButtonPointer.time = GetTimeInMillis();
737 (*dev->c_public.processInputProc)(&ev, dev, 1);
738 vlog.debug("fake keycode %d %s", keycode, down ? "down" : "up");
739 }
740 DeviceIntPtr dev;
741 int modIndex;
742 int nKeys;
743 int* keys;
744 bool pressed;
745};
746
747
748// altKeysym is a table of alternative keysyms which have the same meaning.
749
750struct altKeysym_t {
751 KeySym a, b;
752};
753
754altKeysym_t altKeysym[] = {
755 { XK_Shift_L, XK_Shift_R },
756 { XK_Control_L, XK_Control_R },
757 { XK_Meta_L, XK_Meta_R },
758 { XK_Alt_L, XK_Alt_R },
759 { XK_Super_L, XK_Super_R },
760 { XK_Hyper_L, XK_Hyper_R },
761 { XK_KP_Space, XK_space },
762 { XK_KP_Tab, XK_Tab },
763 { XK_KP_Enter, XK_Return },
764 { XK_KP_F1, XK_F1 },
765 { XK_KP_F2, XK_F2 },
766 { XK_KP_F3, XK_F3 },
767 { XK_KP_F4, XK_F4 },
768 { XK_KP_Home, XK_Home },
769 { XK_KP_Left, XK_Left },
770 { XK_KP_Up, XK_Up },
771 { XK_KP_Right, XK_Right },
772 { XK_KP_Down, XK_Down },
773 { XK_KP_Page_Up, XK_Page_Up },
774 { XK_KP_Page_Down, XK_Page_Down },
775 { XK_KP_End, XK_End },
776 { XK_KP_Begin, XK_Begin },
777 { XK_KP_Insert, XK_Insert },
778 { XK_KP_Delete, XK_Delete },
779 { XK_KP_Equal, XK_equal },
780 { XK_KP_Multiply, XK_asterisk },
781 { XK_KP_Add, XK_plus },
782 { XK_KP_Separator, XK_comma },
783 { XK_KP_Subtract, XK_minus },
784 { XK_KP_Decimal, XK_period },
785 { XK_KP_Divide, XK_slash },
786 { XK_KP_0, XK_0 },
787 { XK_KP_1, XK_1 },
788 { XK_KP_2, XK_2 },
789 { XK_KP_3, XK_3 },
790 { XK_KP_4, XK_4 },
791 { XK_KP_5, XK_5 },
792 { XK_KP_6, XK_6 },
793 { XK_KP_7, XK_7 },
794 { XK_KP_8, XK_8 },
795 { XK_KP_9, XK_9 },
796};
797
798// keyEvent() - work out the best keycode corresponding to the keysym sent by
799// the viewer. This is non-trivial because we can't assume much about the
800// local keyboard layout. We must also find out which column of the keyboard
801// mapping the keysym is in, and alter the shift state appropriately. Column 0
802// means both shift and "mode_switch" (AltGr) must be released, column 1 means
803// shift must be pressed and mode_switch released, column 2 means shift must be
804// released and mode_switch pressed, and column 3 means both shift and
805// mode_switch must be pressed.
806
807void XserverDesktop::keyEvent(rdr::U32 keysym, bool down)
808{
809 if (keysym == XK_Caps_Lock) {
810 vlog.debug("Ignoring caps lock");
811 return;
812 }
813 DeviceIntPtr dev = (DeviceIntPtr)LookupKeyboardDevice();
814 KeyClassPtr keyc = dev->key;
815 KeySymsPtr keymap = &keyc->curKeySyms;
816
817 // find which modifier Mode_switch is on.
818 int modeSwitchMapIndex = 0;
819 for (int i = 3; i < 8; i++) {
820 for (int k = 0; k < keyc->maxKeysPerModifier; k++) {
821 int keycode = keyc->modifierKeyMap[i * keyc->maxKeysPerModifier + k];
822 for (int j = 0; j < keymap->mapWidth; j++) {
823 if (keycode != 0 &&
824 keymap->map[(keycode - keymap->minKeyCode)
825 * keymap->mapWidth + j] == XK_Mode_switch)
826 {
827 modeSwitchMapIndex = i;
828 break;
829 }
830 }
831 }
832 }
833
834 int col = 0;
835 if (keyc->state & (1<<ShiftMapIndex)) col |= 1;
836 if (modeSwitchMapIndex && (keyc->state & (1<<modeSwitchMapIndex))) col |= 2;
837
838 int kc = KeysymToKeycode(keymap, keysym, &col);
839
840 // Sort out the "shifted Tab" mess. If we are sent a shifted Tab, generate a
841 // local shifted Tab regardless of what the "shifted Tab" keysym is on the
842 // local keyboard (it might be Tab, ISO_Left_Tab or HP's private BackTab
843 // keysym, and quite possibly some others too). We never get ISO_Left_Tab
844 // here because it's already been translated in VNCSConnectionST.
845 if (keysym == XK_Tab && (keyc->state & (1<<ShiftMapIndex)))
846 col |= 1;
847
848 if (kc == 0) {
849 // Not a direct match in the local keyboard mapping. Check for alternative
850 // keysyms with the same meaning.
851 for (int i = 0; i < sizeof(altKeysym) / sizeof(altKeysym_t); i++) {
852 if (keysym == altKeysym[i].a)
853 kc = KeysymToKeycode(keymap, altKeysym[i].b, &col);
854 else if (keysym == altKeysym[i].b)
855 kc = KeysymToKeycode(keymap, altKeysym[i].a, &col);
856 if (kc) break;
857 }
858 }
859
860 if (kc == 0) {
861 // Last resort - dynamically add a new key to the keyboard mapping.
862 for (kc = keymap->maxKeyCode; kc >= keymap->minKeyCode; kc--) {
863 if (!keymap->map[(kc - keymap->minKeyCode) * keymap->mapWidth]) {
864 keymap->map[(kc - keymap->minKeyCode) * keymap->mapWidth] = keysym;
865 col = 0;
866 SendMappingNotify(MappingKeyboard, kc, 1, serverClient);
867 vlog.info("Added unknown keysym 0x%x to keycode %d",keysym,kc);
868 break;
869 }
870 }
871 if (kc < keymap->minKeyCode) {
872 vlog.info("Keyboard mapping full - ignoring unknown keysym 0x%x",keysym);
873 return;
874 }
875 }
876
877 // See if it's a modifier key. If so, then don't do any auto-repeat, because
878 // the X server will translate each press into a release followed by a press.
879 for (int i = 0; i < 8; i++) {
880 for (int k = 0; k < keyc->maxKeysPerModifier; k++) {
881 if (kc == keyc->modifierKeyMap[i * keyc->maxKeysPerModifier + k] &&
882 IS_PRESSED(keyc,kc) && down)
883 return;
884 }
885 }
886
887 ModifierState shift(dev, ShiftMapIndex);
888 ModifierState modeSwitch(dev, modeSwitchMapIndex);
889 if (down) {
890 if (col & 1)
891 shift.press();
892 else
893 shift.release();
894 if (modeSwitchMapIndex) {
895 if (col & 2)
896 modeSwitch.press();
897 else
898 modeSwitch.release();
899 }
900 }
901 vlog.debug("keycode %d %s", kc, down ? "down" : "up");
902 xEvent ev;
903 ev.u.u.type = down ? KeyPress : KeyRelease;
904 ev.u.u.detail = kc;
905 ev.u.keyButtonPointer.time = GetTimeInMillis();
906 (*dev->c_public.processInputProc)(&ev, dev, 1);
907}
908
909
910void XConvertCase(KeySym sym, KeySym *lower, KeySym *upper)
911{
912 *lower = sym;
913 *upper = sym;
914 switch(sym >> 8) {
915 case 0: /* Latin 1 */
916 if ((sym >= XK_A) && (sym <= XK_Z))
917 *lower += (XK_a - XK_A);
918 else if ((sym >= XK_a) && (sym <= XK_z))
919 *upper -= (XK_a - XK_A);
920 else if ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis))
921 *lower += (XK_agrave - XK_Agrave);
922 else if ((sym >= XK_agrave) && (sym <= XK_odiaeresis))
923 *upper -= (XK_agrave - XK_Agrave);
924 else if ((sym >= XK_Ooblique) && (sym <= XK_Thorn))
925 *lower += (XK_oslash - XK_Ooblique);
926 else if ((sym >= XK_oslash) && (sym <= XK_thorn))
927 *upper -= (XK_oslash - XK_Ooblique);
928 break;
929 case 1: /* Latin 2 */
930 /* Assume the KeySym is a legal value (ignore discontinuities) */
931 if (sym == XK_Aogonek)
932 *lower = XK_aogonek;
933 else if (sym >= XK_Lstroke && sym <= XK_Sacute)
934 *lower += (XK_lstroke - XK_Lstroke);
935 else if (sym >= XK_Scaron && sym <= XK_Zacute)
936 *lower += (XK_scaron - XK_Scaron);
937 else if (sym >= XK_Zcaron && sym <= XK_Zabovedot)
938 *lower += (XK_zcaron - XK_Zcaron);
939 else if (sym == XK_aogonek)
940 *upper = XK_Aogonek;
941 else if (sym >= XK_lstroke && sym <= XK_sacute)
942 *upper -= (XK_lstroke - XK_Lstroke);
943 else if (sym >= XK_scaron && sym <= XK_zacute)
944 *upper -= (XK_scaron - XK_Scaron);
945 else if (sym >= XK_zcaron && sym <= XK_zabovedot)
946 *upper -= (XK_zcaron - XK_Zcaron);
947 else if (sym >= XK_Racute && sym <= XK_Tcedilla)
948 *lower += (XK_racute - XK_Racute);
949 else if (sym >= XK_racute && sym <= XK_tcedilla)
950 *upper -= (XK_racute - XK_Racute);
951 break;
952 case 2: /* Latin 3 */
953 /* Assume the KeySym is a legal value (ignore discontinuities) */
954 if (sym >= XK_Hstroke && sym <= XK_Hcircumflex)
955 *lower += (XK_hstroke - XK_Hstroke);
956 else if (sym >= XK_Gbreve && sym <= XK_Jcircumflex)
957 *lower += (XK_gbreve - XK_Gbreve);
958 else if (sym >= XK_hstroke && sym <= XK_hcircumflex)
959 *upper -= (XK_hstroke - XK_Hstroke);
960 else if (sym >= XK_gbreve && sym <= XK_jcircumflex)
961 *upper -= (XK_gbreve - XK_Gbreve);
962 else if (sym >= XK_Cabovedot && sym <= XK_Scircumflex)
963 *lower += (XK_cabovedot - XK_Cabovedot);
964 else if (sym >= XK_cabovedot && sym <= XK_scircumflex)
965 *upper -= (XK_cabovedot - XK_Cabovedot);
966 break;
967 case 3: /* Latin 4 */
968 /* Assume the KeySym is a legal value (ignore discontinuities) */
969 if (sym >= XK_Rcedilla && sym <= XK_Tslash)
970 *lower += (XK_rcedilla - XK_Rcedilla);
971 else if (sym >= XK_rcedilla && sym <= XK_tslash)
972 *upper -= (XK_rcedilla - XK_Rcedilla);
973 else if (sym == XK_ENG)
974 *lower = XK_eng;
975 else if (sym == XK_eng)
976 *upper = XK_ENG;
977 else if (sym >= XK_Amacron && sym <= XK_Umacron)
978 *lower += (XK_amacron - XK_Amacron);
979 else if (sym >= XK_amacron && sym <= XK_umacron)
980 *upper -= (XK_amacron - XK_Amacron);
981 break;
982 case 6: /* Cyrillic */
983 /* Assume the KeySym is a legal value (ignore discontinuities) */
984 if (sym >= XK_Serbian_DJE && sym <= XK_Serbian_DZE)
985 *lower -= (XK_Serbian_DJE - XK_Serbian_dje);
986 else if (sym >= XK_Serbian_dje && sym <= XK_Serbian_dze)
987 *upper += (XK_Serbian_DJE - XK_Serbian_dje);
988 else if (sym >= XK_Cyrillic_YU && sym <= XK_Cyrillic_HARDSIGN)
989 *lower -= (XK_Cyrillic_YU - XK_Cyrillic_yu);
990 else if (sym >= XK_Cyrillic_yu && sym <= XK_Cyrillic_hardsign)
991 *upper += (XK_Cyrillic_YU - XK_Cyrillic_yu);
992 break;
993 case 7: /* Greek */
994 /* Assume the KeySym is a legal value (ignore discontinuities) */
995 if (sym >= XK_Greek_ALPHAaccent && sym <= XK_Greek_OMEGAaccent)
996 *lower += (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
997 else if (sym >= XK_Greek_alphaaccent && sym <= XK_Greek_omegaaccent &&
998 sym != XK_Greek_iotaaccentdieresis &&
999 sym != XK_Greek_upsilonaccentdieresis)
1000 *upper -= (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent);
1001 else if (sym >= XK_Greek_ALPHA && sym <= XK_Greek_OMEGA)
1002 *lower += (XK_Greek_alpha - XK_Greek_ALPHA);
1003 else if (sym >= XK_Greek_alpha && sym <= XK_Greek_omega &&
1004 sym != XK_Greek_finalsmallsigma)
1005 *upper -= (XK_Greek_alpha - XK_Greek_ALPHA);
1006 break;
1007 }
1008}
1009
1010static KeySym KeyCodetoKeySym(KeySymsPtr keymap, int keycode, int col)
1011{
1012 register int per = keymap->mapWidth;
1013 register KeySym *syms;
1014 KeySym lsym, usym;
1015
1016 if ((col < 0) || ((col >= per) && (col > 3)) ||
1017 (keycode < keymap->minKeyCode) || (keycode > keymap->maxKeyCode))
1018 return NoSymbol;
1019
1020 syms = &keymap->map[(keycode - keymap->minKeyCode) * per];
1021 if (col < 4) {
1022 if (col > 1) {
1023 while ((per > 2) && (syms[per - 1] == NoSymbol))
1024 per--;
1025 if (per < 3)
1026 col -= 2;
1027 }
1028 if ((per <= (col|1)) || (syms[col|1] == NoSymbol)) {
1029 XConvertCase(syms[col&~1], &lsym, &usym);
1030 if (!(col & 1))
1031 return lsym;
1032 // I'm commenting out this logic because it's incorrect even though it
1033 // was copied from the Xlib sources. The X protocol book quite clearly
1034 // states that where a group consists of element 1 being a non-alphabetic
1035 // keysym and element 2 being NoSymbol that you treat the second element
1036 // as being the same as the first. This also tallies with the behaviour
1037 // produced by the installed Xlib on my linux box (I believe this is
1038 // because it uses some XKB code rather than the original Xlib code -
1039 // compare XKBBind.c with KeyBind.c in lib/X11).
1040 // else if (usym == lsym)
1041 // return NoSymbol;
1042 else
1043 return usym;
1044 }
1045 }
1046 return syms[col];
1047}
1048
1049// KeysymToKeycode() - find the keycode and column corresponding to the given
1050// keysym. The value of col passed in should be the column determined from the
1051// current shift state. If the keysym can be found in that column we prefer
1052// that to finding it in a different column (which would require fake events to
1053// alter the shift state).
1054
1055static KeyCode KeysymToKeycode(KeySymsPtr keymap, KeySym ks, int* col)
1056{
1057 register int i, j;
1058
1059 j = *col;
1060 for (i = keymap->minKeyCode; i <= keymap->maxKeyCode; i++) {
1061 if (KeyCodetoKeySym(keymap, i, j) == ks)
1062 return i;
1063 }
1064
1065 for (j = 0; j < keymap->mapWidth; j++) {
1066 for (i = keymap->minKeyCode; i <= keymap->maxKeyCode; i++) {
1067 if (KeyCodetoKeySym(keymap, i, j) == ks) {
1068 *col = j;
1069 return i;
1070 }
1071 }
1072 }
1073 return 0;
1074}