blob: c60d08132d846e0bb2248f0a203e0f36a1a4d06f [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.
3 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19
20#include <x0vncserver/XDesktop.h>
21
22#include <X11/XKBlib.h>
23#ifdef HAVE_XTEST
24#include <X11/extensions/XTest.h>
25#endif
26#ifdef HAVE_XDAMAGE
27#include <X11/extensions/Xdamage.h>
28#endif
29#ifdef HAVE_XFIXES
30#include <X11/extensions/Xfixes.h>
31#endif
32
33#include <x0vncserver/Geometry.h>
34#include <x0vncserver/XPixelBuffer.h>
35
36using namespace rfb;
37
38extern const unsigned short code_map_qnum_to_xorgevdev[];
39extern const unsigned int code_map_qnum_to_xorgevdev_len;
40
41extern const unsigned short code_map_qnum_to_xorgkbd[];
42extern const unsigned int code_map_qnum_to_xorgkbd_len;
43
44extern rfb::BoolParameter useShm;
45extern rfb::BoolParameter useOverlay;
46extern 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.
194 ImageFactory factory((bool)useShm, (bool)useOverlay);
195
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
210 server->setLEDState(ledState);
211
212 running = true;
213}
214
215void XDesktop::stop() {
216 running = false;
217
218#ifdef HAVE_XDAMAGE
219 if (haveDamage)
220 XDamageDestroy(dpy, damage);
221#endif
222
223 delete pb;
224 pb = 0;
225}
226
227bool XDesktop::isRunning() {
228 return running;
229}
230
231void XDesktop::pointerEvent(const Point& pos, int buttonMask) {
232#ifdef HAVE_XTEST
233 if (!haveXtest) return;
234 XTestFakeMotionEvent(dpy, DefaultScreen(dpy),
235 geometry->offsetLeft() + pos.x,
236 geometry->offsetTop() + pos.y,
237 CurrentTime);
238 if (buttonMask != oldButtonMask) {
239 for (int i = 0; i < maxButtons; i++) {
240 if ((buttonMask ^ oldButtonMask) & (1<<i)) {
241 if (buttonMask & (1<<i)) {
242 XTestFakeButtonEvent(dpy, i+1, True, CurrentTime);
243 } else {
244 XTestFakeButtonEvent(dpy, i+1, False, CurrentTime);
245 }
246 }
247 }
248 }
249 oldButtonMask = buttonMask;
250#endif
251}
252
253#ifdef HAVE_XTEST
254KeyCode XDesktop::XkbKeysymToKeycode(Display* dpy, KeySym keysym) {
255 XkbDescPtr xkb;
256 XkbStateRec state;
257 unsigned keycode;
258
259 xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
260 if (!xkb)
261 return 0;
262
263 XkbGetState(dpy, XkbUseCoreKbd, &state);
264
265 for (keycode = xkb->min_key_code;
266 keycode <= xkb->max_key_code;
267 keycode++) {
268 KeySym cursym;
269 unsigned int mods, out_mods;
270 // XkbStateFieldFromRec() doesn't work properly because
271 // state.lookup_mods isn't properly updated, so we do this manually
272 mods = XkbBuildCoreState(XkbStateMods(&state), state.group);
273 XkbTranslateKeyCode(xkb, keycode, mods, &out_mods, &cursym);
274 if (cursym == keysym)
275 break;
276 }
277
278 if (keycode > xkb->max_key_code)
279 keycode = 0;
280
281 XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
282
283 return keycode;
284}
285#endif
286
287void XDesktop::keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down) {
288#ifdef HAVE_XTEST
289 int keycode = 0;
290
291 if (!haveXtest)
292 return;
293
294 // Use scan code if provided and mapping exists
295 if (codeMap && rawKeyboard && xtcode < codeMapLen)
296 keycode = codeMap[xtcode];
297
298 if (!keycode) {
299 if (pressedKeys.find(keysym) != pressedKeys.end())
300 keycode = pressedKeys[keysym];
301 else {
302 // XKeysymToKeycode() doesn't respect state, so we have to use
303 // something slightly more complex
304 keycode = XkbKeysymToKeycode(dpy, keysym);
305 }
306 }
307
308 if (!keycode)
309 return;
310
311 if (down)
312 pressedKeys[keysym] = keycode;
313 else
314 pressedKeys.erase(keysym);
315
316 XTestFakeKeyEvent(dpy, keycode, down, CurrentTime);
317#endif
318}
319
320void XDesktop::clientCutText(const char* str, int len) {
321}
322
323
324bool XDesktop::handleGlobalEvent(XEvent* ev) {
325 if (ev->type == xkbEventBase + XkbEventCode) {
326 XkbEvent *kb = (XkbEvent *)ev;
327
328 if (kb->any.xkb_type != XkbIndicatorStateNotify)
329 return false;
330
331 vlog.debug("Got indicator update, mask is now 0x%x", kb->indicators.state);
332
333 ledState = 0;
334 for (int i = 0; i < XDESKTOP_N_LEDS; i++) {
335 if (kb->indicators.state & ledMasks[i])
336 ledState |= 1u << i;
337 }
338
339 if (running)
340 server->setLEDState(ledState);
341
342 return true;
343#ifdef HAVE_XDAMAGE
344 } else if (ev->type == xdamageEventBase) {
345 XDamageNotifyEvent* dev;
346 Rect rect;
347
348 if (!running)
349 return true;
350
351 dev = (XDamageNotifyEvent*)ev;
352 rect.setXYWH(dev->area.x, dev->area.y, dev->area.width, dev->area.height);
353 server->add_changed(rect);
354
355 return true;
356#endif
357#ifdef HAVE_XFIXES
358 } else if (ev->type == xfixesEventBase + XFixesCursorNotify) {
359 XFixesCursorNotifyEvent* cev;
360 XFixesCursorImage *cim;
361
362 if (!running)
363 return true;
364
365 cev = (XFixesCursorNotifyEvent*)ev;
366
367 if (cev->subtype != XFixesDisplayCursorNotify)
368 return false;
369
370 cim = XFixesGetCursorImage(dpy);
371 if (cim == NULL)
372 return false;
373
374 // Copied from XserverDesktop::setCursor() in
375 // unix/xserver/hw/vnc/XserverDesktop.cc and adapted to
376 // handle long -> U32 conversion for 64-bit Xlib
377 rdr::U8* cursorData;
378 rdr::U8 *out;
379 const unsigned long *pixels;
380
381 cursorData = new rdr::U8[cim->width * cim->height * 4];
382
383 // Un-premultiply alpha
384 pixels = cim->pixels;
385 out = cursorData;
386 for (int y = 0; y < cim->height; y++) {
387 for (int x = 0; x < cim->width; x++) {
388 rdr::U8 alpha;
389 rdr::U32 pixel = *pixels++;
390 rdr::U8 *in = (rdr::U8 *) &pixel;
391
392 alpha = in[3];
393 if (alpha == 0)
394 alpha = 1; // Avoid division by zero
395
396 *out++ = (unsigned)*in++ * 255/alpha;
397 *out++ = (unsigned)*in++ * 255/alpha;
398 *out++ = (unsigned)*in++ * 255/alpha;
399 *out++ = *in++;
400 }
401 }
402
403 try {
404 server->setCursor(cim->width, cim->height, Point(cim->xhot, cim->yhot),
405 cursorData);
406 } catch (rdr::Exception& e) {
407 vlog.error("XserverDesktop::setCursor: %s",e.str());
408 }
409
410 delete [] cursorData;
411 XFree(cim);
412 return true;
413#endif
414 }
415
416 return false;
417}
418