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