blob: 07b3a9c19b10b27052cb09750ccd7bceafcfb6f4 [file] [log] [blame]
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +02001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 * Copyright (C) 2004-2008 Constantin Kaplinsky. All Rights Reserved.
Peter Åstrand (astrand)3a1db162017-10-16 11:11:45 +02003 * Copyright 2017 Peter Astrand <astrand@cendio.se> for Cendio AB
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +02004 *
5 * This is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This software is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this software; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
18 * USA.
19 */
20
21#include <x0vncserver/XDesktop.h>
22
23#include <X11/XKBlib.h>
24#ifdef HAVE_XTEST
25#include <X11/extensions/XTest.h>
26#endif
27#ifdef HAVE_XDAMAGE
28#include <X11/extensions/Xdamage.h>
29#endif
30#ifdef HAVE_XFIXES
31#include <X11/extensions/Xfixes.h>
32#endif
33
34#include <x0vncserver/Geometry.h>
35#include <x0vncserver/XPixelBuffer.h>
36
37using namespace rfb;
38
39extern const unsigned short code_map_qnum_to_xorgevdev[];
40extern const unsigned int code_map_qnum_to_xorgevdev_len;
41
42extern const unsigned short code_map_qnum_to_xorgkbd[];
43extern const unsigned int code_map_qnum_to_xorgkbd_len;
44
45extern rfb::BoolParameter useShm;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +020046extern rfb::BoolParameter rawKeyboard;
47
48static rfb::LogWriter vlog("XDesktop");
49
50// order is important as it must match RFB extension
51static const char * ledNames[XDESKTOP_N_LEDS] = {
52 "Scroll Lock", "Num Lock", "Caps Lock"
53};
54
55XDesktop::XDesktop(Display* dpy_, Geometry *geometry_)
56 : dpy(dpy_), geometry(geometry_), pb(0), server(0),
57 oldButtonMask(0), haveXtest(false), haveDamage(false),
58 maxButtons(0), running(false), ledMasks(), ledState(0),
59 codeMap(0), codeMapLen(0)
60{
61 int major, minor;
62
63 int xkbOpcode, xkbErrorBase;
64
65 major = XkbMajorVersion;
66 minor = XkbMinorVersion;
67 if (!XkbQueryExtension(dpy, &xkbOpcode, &xkbEventBase,
68 &xkbErrorBase, &major, &minor)) {
69 vlog.error("XKEYBOARD extension not present");
70 throw Exception();
71 }
72
73 XkbSelectEvents(dpy, XkbUseCoreKbd, XkbIndicatorStateNotifyMask,
74 XkbIndicatorStateNotifyMask);
75
76 // figure out bit masks for the indicators we are interested in
77 for (int i = 0; i < XDESKTOP_N_LEDS; i++) {
78 Atom a;
79 int shift;
80 Bool on;
81
82 a = XInternAtom(dpy, ledNames[i], True);
83 if (!a || !XkbGetNamedIndicator(dpy, a, &shift, &on, NULL, NULL))
84 continue;
85
86 ledMasks[i] = 1u << shift;
87 vlog.debug("Mask for '%s' is 0x%x", ledNames[i], ledMasks[i]);
88 if (on)
89 ledState |= 1u << i;
90 }
91
92 // X11 unfortunately uses keyboard driver specific keycodes and provides no
93 // direct way to query this, so guess based on the keyboard mapping
94 XkbDescPtr desc = XkbGetKeyboard(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
95 if (desc && desc->names) {
96 char *keycodes = XGetAtomName(dpy, desc->names->keycodes);
97
98 if (keycodes) {
99 if (strncmp("evdev", keycodes, strlen("evdev")) == 0) {
100 codeMap = code_map_qnum_to_xorgevdev;
101 codeMapLen = code_map_qnum_to_xorgevdev_len;
102 vlog.info("Using evdev codemap\n");
103 } else if (strncmp("xfree86", keycodes, strlen("xfree86")) == 0) {
104 codeMap = code_map_qnum_to_xorgkbd;
105 codeMapLen = code_map_qnum_to_xorgkbd_len;
106 vlog.info("Using xorgkbd codemap\n");
107 } else {
108 vlog.info("Unknown keycode '%s', no codemap\n", keycodes);
109 }
110 XFree(keycodes);
111 } else {
112 vlog.debug("Unable to get keycode map\n");
113 }
114
115 XkbFreeKeyboard(desc, XkbAllComponentsMask, True);
116 }
117
118#ifdef HAVE_XTEST
119 int xtestEventBase;
120 int xtestErrorBase;
121
122 if (XTestQueryExtension(dpy, &xtestEventBase,
123 &xtestErrorBase, &major, &minor)) {
124 XTestGrabControl(dpy, True);
125 vlog.info("XTest extension present - version %d.%d",major,minor);
126 haveXtest = true;
127 } else {
128#endif
129 vlog.info("XTest extension not present");
130 vlog.info("Unable to inject events or display while server is grabbed");
131#ifdef HAVE_XTEST
132 }
133#endif
134
135#ifdef HAVE_XDAMAGE
136 int xdamageErrorBase;
137
138 if (XDamageQueryExtension(dpy, &xdamageEventBase, &xdamageErrorBase)) {
139 haveDamage = true;
140 } else {
141#endif
142 vlog.info("DAMAGE extension not present");
143 vlog.info("Will have to poll screen for changes");
144#ifdef HAVE_XDAMAGE
145 }
146#endif
147
148#ifdef HAVE_XFIXES
149 int xfixesErrorBase;
150
151 if (XFixesQueryExtension(dpy, &xfixesEventBase, &xfixesErrorBase)) {
152 XFixesSelectCursorInput(dpy, DefaultRootWindow(dpy),
153 XFixesDisplayCursorNotifyMask);
154 } else {
155#endif
156 vlog.info("XFIXES extension not present");
157 vlog.info("Will not be able to display cursors");
158#ifdef HAVE_XFIXES
159 }
160#endif
161
162 TXWindow::setGlobalEventHandler(this);
163}
164
165XDesktop::~XDesktop() {
166 stop();
167}
168
169
170void XDesktop::poll() {
171 if (pb and not haveDamage)
172 pb->poll(server);
173 if (running) {
174 Window root, child;
175 int x, y, wx, wy;
176 unsigned int mask;
177 XQueryPointer(dpy, DefaultRootWindow(dpy), &root, &child,
178 &x, &y, &wx, &wy, &mask);
179 server->setCursorPos(rfb::Point(x, y));
180 }
181 }
182
183
184void XDesktop::start(VNCServer* vs) {
185
186 // Determine actual number of buttons of the X pointer device.
187 unsigned char btnMap[8];
188 int numButtons = XGetPointerMapping(dpy, btnMap, 8);
189 maxButtons = (numButtons > 8) ? 8 : numButtons;
190 vlog.info("Enabling %d button%s of X pointer device",
191 maxButtons, (maxButtons != 1) ? "s" : "");
192
193 // Create an ImageFactory instance for producing Image objects.
Peter Åstrand (astrand)dcd0b132017-10-16 15:18:00 +0200194 ImageFactory factory((bool)useShm);
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200195
196 // Create pixel buffer and provide it to the server object.
197 pb = new XPixelBuffer(dpy, factory, geometry->getRect());
198 vlog.info("Allocated %s", pb->getImage()->classDesc());
199
200 server = (VNCServerST *)vs;
201 server->setPixelBuffer(pb);
202
203#ifdef HAVE_XDAMAGE
204 if (haveDamage) {
205 damage = XDamageCreate(dpy, DefaultRootWindow(dpy),
206 XDamageReportRawRectangles);
207 }
208#endif
209
Peter Åstrand (astrand)3abc7d42017-10-11 15:12:10 +0200210#ifdef HAVE_XFIXES
211 setCursor();
212#endif
213
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200214 server->setLEDState(ledState);
215
216 running = true;
217}
218
219void XDesktop::stop() {
220 running = false;
221
222#ifdef HAVE_XDAMAGE
223 if (haveDamage)
224 XDamageDestroy(dpy, damage);
225#endif
226
227 delete pb;
228 pb = 0;
229}
230
231bool XDesktop::isRunning() {
232 return running;
233}
234
235void XDesktop::pointerEvent(const Point& pos, int buttonMask) {
236#ifdef HAVE_XTEST
237 if (!haveXtest) return;
238 XTestFakeMotionEvent(dpy, DefaultScreen(dpy),
239 geometry->offsetLeft() + pos.x,
240 geometry->offsetTop() + pos.y,
241 CurrentTime);
242 if (buttonMask != oldButtonMask) {
243 for (int i = 0; i < maxButtons; i++) {
244 if ((buttonMask ^ oldButtonMask) & (1<<i)) {
245 if (buttonMask & (1<<i)) {
246 XTestFakeButtonEvent(dpy, i+1, True, CurrentTime);
247 } else {
248 XTestFakeButtonEvent(dpy, i+1, False, CurrentTime);
249 }
250 }
251 }
252 }
253 oldButtonMask = buttonMask;
254#endif
255}
256
257#ifdef HAVE_XTEST
258KeyCode XDesktop::XkbKeysymToKeycode(Display* dpy, KeySym keysym) {
259 XkbDescPtr xkb;
260 XkbStateRec state;
261 unsigned keycode;
262
263 xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
264 if (!xkb)
265 return 0;
266
267 XkbGetState(dpy, XkbUseCoreKbd, &state);
268
269 for (keycode = xkb->min_key_code;
270 keycode <= xkb->max_key_code;
271 keycode++) {
272 KeySym cursym;
273 unsigned int mods, out_mods;
274 // XkbStateFieldFromRec() doesn't work properly because
275 // state.lookup_mods isn't properly updated, so we do this manually
276 mods = XkbBuildCoreState(XkbStateMods(&state), state.group);
277 XkbTranslateKeyCode(xkb, keycode, mods, &out_mods, &cursym);
278 if (cursym == keysym)
279 break;
280 }
281
282 if (keycode > xkb->max_key_code)
283 keycode = 0;
284
285 XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
286
287 return keycode;
288}
289#endif
290
291void XDesktop::keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down) {
292#ifdef HAVE_XTEST
293 int keycode = 0;
294
295 if (!haveXtest)
296 return;
297
298 // Use scan code if provided and mapping exists
299 if (codeMap && rawKeyboard && xtcode < codeMapLen)
300 keycode = codeMap[xtcode];
301
302 if (!keycode) {
303 if (pressedKeys.find(keysym) != pressedKeys.end())
304 keycode = pressedKeys[keysym];
305 else {
306 // XKeysymToKeycode() doesn't respect state, so we have to use
307 // something slightly more complex
308 keycode = XkbKeysymToKeycode(dpy, keysym);
309 }
310 }
311
312 if (!keycode)
313 return;
314
315 if (down)
316 pressedKeys[keysym] = keycode;
317 else
318 pressedKeys.erase(keysym);
319
320 XTestFakeKeyEvent(dpy, keycode, down, CurrentTime);
321#endif
322}
323
324void XDesktop::clientCutText(const char* str, int len) {
325}
326
327
328bool XDesktop::handleGlobalEvent(XEvent* ev) {
329 if (ev->type == xkbEventBase + XkbEventCode) {
330 XkbEvent *kb = (XkbEvent *)ev;
331
332 if (kb->any.xkb_type != XkbIndicatorStateNotify)
333 return false;
334
335 vlog.debug("Got indicator update, mask is now 0x%x", kb->indicators.state);
336
337 ledState = 0;
338 for (int i = 0; i < XDESKTOP_N_LEDS; i++) {
339 if (kb->indicators.state & ledMasks[i])
340 ledState |= 1u << i;
341 }
342
343 if (running)
344 server->setLEDState(ledState);
345
346 return true;
347#ifdef HAVE_XDAMAGE
348 } else if (ev->type == xdamageEventBase) {
349 XDamageNotifyEvent* dev;
350 Rect rect;
351
352 if (!running)
353 return true;
354
355 dev = (XDamageNotifyEvent*)ev;
356 rect.setXYWH(dev->area.x, dev->area.y, dev->area.width, dev->area.height);
357 server->add_changed(rect);
358
359 return true;
360#endif
361#ifdef HAVE_XFIXES
362 } else if (ev->type == xfixesEventBase + XFixesCursorNotify) {
363 XFixesCursorNotifyEvent* cev;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200364
365 if (!running)
366 return true;
367
368 cev = (XFixesCursorNotifyEvent*)ev;
369
370 if (cev->subtype != XFixesDisplayCursorNotify)
371 return false;
372
Peter Åstrand (astrand)3abc7d42017-10-11 15:12:10 +0200373 return setCursor();
374#endif
375 }
376
377 return false;
378}
379
380bool XDesktop::setCursor()
381{
382 XFixesCursorImage *cim;
383
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200384 cim = XFixesGetCursorImage(dpy);
385 if (cim == NULL)
386 return false;
387
388 // Copied from XserverDesktop::setCursor() in
389 // unix/xserver/hw/vnc/XserverDesktop.cc and adapted to
390 // handle long -> U32 conversion for 64-bit Xlib
391 rdr::U8* cursorData;
392 rdr::U8 *out;
393 const unsigned long *pixels;
394
395 cursorData = new rdr::U8[cim->width * cim->height * 4];
396
397 // Un-premultiply alpha
398 pixels = cim->pixels;
399 out = cursorData;
400 for (int y = 0; y < cim->height; y++) {
401 for (int x = 0; x < cim->width; x++) {
402 rdr::U8 alpha;
403 rdr::U32 pixel = *pixels++;
404 rdr::U8 *in = (rdr::U8 *) &pixel;
405
406 alpha = in[3];
407 if (alpha == 0)
408 alpha = 1; // Avoid division by zero
409
410 *out++ = (unsigned)*in++ * 255/alpha;
411 *out++ = (unsigned)*in++ * 255/alpha;
412 *out++ = (unsigned)*in++ * 255/alpha;
413 *out++ = *in++;
414 }
415 }
416
417 try {
418 server->setCursor(cim->width, cim->height, Point(cim->xhot, cim->yhot),
419 cursorData);
420 } catch (rdr::Exception& e) {
421 vlog.error("XserverDesktop::setCursor: %s",e.str());
422 }
423
424 delete [] cursorData;
425 XFree(cim);
426 return true;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200427}