blob: 748796bee3b7c36ebe9963077abd7a884fd0da3c [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
Pierre Ossmance4722f2017-11-08 16:00:05 +010045BoolParameter useShm("UseSHM", "Use MIT-SHM extension if available", true);
46BoolParameter rawKeyboard("RawKeyboard",
47 "Send keyboard events straight through and "
48 "avoid mapping them to the current keyboard "
49 "layout", false);
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +020050
51static rfb::LogWriter vlog("XDesktop");
52
53// order is important as it must match RFB extension
54static const char * ledNames[XDESKTOP_N_LEDS] = {
55 "Scroll Lock", "Num Lock", "Caps Lock"
56};
57
58XDesktop::XDesktop(Display* dpy_, Geometry *geometry_)
Pierre Ossman07580a82018-03-07 15:33:42 +010059 : dpy(dpy_), geometry(geometry_), pb(0), server(0),
60 oldButtonMask(0), haveXtest(false), haveDamage(false),
61 maxButtons(0), running(false), ledMasks(), ledState(0),
62 codeMap(0), codeMapLen(0)
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +020063{
Pierre Ossman07580a82018-03-07 15:33:42 +010064 int major, minor;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +020065
Pierre Ossman07580a82018-03-07 15:33:42 +010066 int xkbOpcode, xkbErrorBase;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +020067
Pierre Ossman07580a82018-03-07 15:33:42 +010068 major = XkbMajorVersion;
69 minor = XkbMinorVersion;
70 if (!XkbQueryExtension(dpy, &xkbOpcode, &xkbEventBase,
71 &xkbErrorBase, &major, &minor)) {
72 vlog.error("XKEYBOARD extension not present");
73 throw Exception();
74 }
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +020075
Pierre Ossman07580a82018-03-07 15:33:42 +010076 XkbSelectEvents(dpy, XkbUseCoreKbd, XkbIndicatorStateNotifyMask,
77 XkbIndicatorStateNotifyMask);
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +020078
Pierre Ossman07580a82018-03-07 15:33:42 +010079 // figure out bit masks for the indicators we are interested in
80 for (int i = 0; i < XDESKTOP_N_LEDS; i++) {
81 Atom a;
82 int shift;
83 Bool on;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +020084
Pierre Ossman07580a82018-03-07 15:33:42 +010085 a = XInternAtom(dpy, ledNames[i], True);
86 if (!a || !XkbGetNamedIndicator(dpy, a, &shift, &on, NULL, NULL))
87 continue;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +020088
Pierre Ossman07580a82018-03-07 15:33:42 +010089 ledMasks[i] = 1u << shift;
90 vlog.debug("Mask for '%s' is 0x%x", ledNames[i], ledMasks[i]);
91 if (on)
92 ledState |= 1u << i;
93 }
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +020094
Pierre Ossman07580a82018-03-07 15:33:42 +010095 // X11 unfortunately uses keyboard driver specific keycodes and provides no
96 // direct way to query this, so guess based on the keyboard mapping
97 XkbDescPtr desc = XkbGetKeyboard(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
98 if (desc && desc->names) {
99 char *keycodes = XGetAtomName(dpy, desc->names->keycodes);
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200100
Pierre Ossman07580a82018-03-07 15:33:42 +0100101 if (keycodes) {
102 if (strncmp("evdev", keycodes, strlen("evdev")) == 0) {
103 codeMap = code_map_qnum_to_xorgevdev;
104 codeMapLen = code_map_qnum_to_xorgevdev_len;
105 vlog.info("Using evdev codemap\n");
106 } else if (strncmp("xfree86", keycodes, strlen("xfree86")) == 0) {
107 codeMap = code_map_qnum_to_xorgkbd;
108 codeMapLen = code_map_qnum_to_xorgkbd_len;
109 vlog.info("Using xorgkbd codemap\n");
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200110 } else {
Pierre Ossman07580a82018-03-07 15:33:42 +0100111 vlog.info("Unknown keycode '%s', no codemap\n", keycodes);
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200112 }
Pierre Ossman07580a82018-03-07 15:33:42 +0100113 XFree(keycodes);
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200114 } else {
Pierre Ossman07580a82018-03-07 15:33:42 +0100115 vlog.debug("Unable to get keycode map\n");
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200116 }
Pierre Ossman07580a82018-03-07 15:33:42 +0100117
118 XkbFreeKeyboard(desc, XkbAllComponentsMask, True);
119 }
120
121#ifdef HAVE_XTEST
122 int xtestEventBase;
123 int xtestErrorBase;
124
125 if (XTestQueryExtension(dpy, &xtestEventBase,
126 &xtestErrorBase, &major, &minor)) {
127 XTestGrabControl(dpy, True);
128 vlog.info("XTest extension present - version %d.%d",major,minor);
129 haveXtest = true;
130 } else {
131#endif
132 vlog.info("XTest extension not present");
133 vlog.info("Unable to inject events or display while server is grabbed");
134#ifdef HAVE_XTEST
135 }
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200136#endif
137
138#ifdef HAVE_XDAMAGE
Pierre Ossman07580a82018-03-07 15:33:42 +0100139 int xdamageErrorBase;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200140
Pierre Ossman07580a82018-03-07 15:33:42 +0100141 if (XDamageQueryExtension(dpy, &xdamageEventBase, &xdamageErrorBase)) {
142 haveDamage = true;
143 } else {
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200144#endif
Pierre Ossman07580a82018-03-07 15:33:42 +0100145 vlog.info("DAMAGE extension not present");
146 vlog.info("Will have to poll screen for changes");
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200147#ifdef HAVE_XDAMAGE
Pierre Ossman07580a82018-03-07 15:33:42 +0100148 }
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200149#endif
150
151#ifdef HAVE_XFIXES
Pierre Ossman07580a82018-03-07 15:33:42 +0100152 int xfixesErrorBase;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200153
Pierre Ossman07580a82018-03-07 15:33:42 +0100154 if (XFixesQueryExtension(dpy, &xfixesEventBase, &xfixesErrorBase)) {
155 XFixesSelectCursorInput(dpy, DefaultRootWindow(dpy),
156 XFixesDisplayCursorNotifyMask);
157 } else {
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200158#endif
Pierre Ossman07580a82018-03-07 15:33:42 +0100159 vlog.info("XFIXES extension not present");
160 vlog.info("Will not be able to display cursors");
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200161#ifdef HAVE_XFIXES
Pierre Ossman07580a82018-03-07 15:33:42 +0100162 }
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200163#endif
164
Pierre Ossman07580a82018-03-07 15:33:42 +0100165 TXWindow::setGlobalEventHandler(this);
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200166}
167
168XDesktop::~XDesktop() {
Pierre Ossman07580a82018-03-07 15:33:42 +0100169 if (running)
170 stop();
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200171}
172
173
174void XDesktop::poll() {
Pierre Ossman07580a82018-03-07 15:33:42 +0100175 if (pb and not haveDamage)
176 pb->poll(server);
177 if (running) {
178 Window root, child;
179 int x, y, wx, wy;
180 unsigned int mask;
181 XQueryPointer(dpy, DefaultRootWindow(dpy), &root, &child,
182 &x, &y, &wx, &wy, &mask);
183 server->setCursorPos(rfb::Point(x, y));
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200184 }
Pierre Ossman07580a82018-03-07 15:33:42 +0100185}
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200186
187
188void XDesktop::start(VNCServer* vs) {
189
Pierre Ossman07580a82018-03-07 15:33:42 +0100190 // Determine actual number of buttons of the X pointer device.
191 unsigned char btnMap[8];
192 int numButtons = XGetPointerMapping(dpy, btnMap, 8);
193 maxButtons = (numButtons > 8) ? 8 : numButtons;
194 vlog.info("Enabling %d button%s of X pointer device",
195 maxButtons, (maxButtons != 1) ? "s" : "");
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200196
Pierre Ossman07580a82018-03-07 15:33:42 +0100197 // Create an ImageFactory instance for producing Image objects.
198 ImageFactory factory((bool)useShm);
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200199
Pierre Ossman07580a82018-03-07 15:33:42 +0100200 // Create pixel buffer and provide it to the server object.
201 pb = new XPixelBuffer(dpy, factory, geometry->getRect());
202 vlog.info("Allocated %s", pb->getImage()->classDesc());
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200203
Pierre Ossman07580a82018-03-07 15:33:42 +0100204 server = (VNCServerST *)vs;
205 server->setPixelBuffer(pb);
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200206
207#ifdef HAVE_XDAMAGE
Pierre Ossman07580a82018-03-07 15:33:42 +0100208 if (haveDamage) {
209 damage = XDamageCreate(dpy, DefaultRootWindow(dpy),
210 XDamageReportRawRectangles);
211 }
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200212#endif
213
Peter Åstrand (astrand)3abc7d42017-10-11 15:12:10 +0200214#ifdef HAVE_XFIXES
Pierre Ossman07580a82018-03-07 15:33:42 +0100215 setCursor();
Peter Åstrand (astrand)3abc7d42017-10-11 15:12:10 +0200216#endif
217
Pierre Ossman07580a82018-03-07 15:33:42 +0100218 server->setLEDState(ledState);
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200219
Pierre Ossman07580a82018-03-07 15:33:42 +0100220 running = true;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200221}
222
223void XDesktop::stop() {
Pierre Ossman07580a82018-03-07 15:33:42 +0100224 running = false;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200225
226#ifdef HAVE_XDAMAGE
Pierre Ossman07580a82018-03-07 15:33:42 +0100227 if (haveDamage)
228 XDamageDestroy(dpy, damage);
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200229#endif
230
Pierre Ossman07580a82018-03-07 15:33:42 +0100231 server->setPixelBuffer(0);
232 server = 0;
Michal Srb18a77072017-09-29 14:47:56 +0200233
Pierre Ossman07580a82018-03-07 15:33:42 +0100234 delete pb;
235 pb = 0;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200236}
237
238bool XDesktop::isRunning() {
Pierre Ossman07580a82018-03-07 15:33:42 +0100239 return running;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200240}
241
242void XDesktop::pointerEvent(const Point& pos, int buttonMask) {
243#ifdef HAVE_XTEST
Pierre Ossman07580a82018-03-07 15:33:42 +0100244 if (!haveXtest) return;
245 XTestFakeMotionEvent(dpy, DefaultScreen(dpy),
246 geometry->offsetLeft() + pos.x,
247 geometry->offsetTop() + pos.y,
248 CurrentTime);
249 if (buttonMask != oldButtonMask) {
250 for (int i = 0; i < maxButtons; i++) {
251 if ((buttonMask ^ oldButtonMask) & (1<<i)) {
252 if (buttonMask & (1<<i)) {
253 XTestFakeButtonEvent(dpy, i+1, True, CurrentTime);
254 } else {
255 XTestFakeButtonEvent(dpy, i+1, False, CurrentTime);
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200256 }
257 }
258 }
Pierre Ossman07580a82018-03-07 15:33:42 +0100259 }
260 oldButtonMask = buttonMask;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200261#endif
262}
263
264#ifdef HAVE_XTEST
265KeyCode XDesktop::XkbKeysymToKeycode(Display* dpy, KeySym keysym) {
Pierre Ossman07580a82018-03-07 15:33:42 +0100266 XkbDescPtr xkb;
267 XkbStateRec state;
268 unsigned keycode;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200269
Pierre Ossman07580a82018-03-07 15:33:42 +0100270 xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
271 if (!xkb)
272 return 0;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200273
Pierre Ossman07580a82018-03-07 15:33:42 +0100274 XkbGetState(dpy, XkbUseCoreKbd, &state);
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200275
Pierre Ossman07580a82018-03-07 15:33:42 +0100276 for (keycode = xkb->min_key_code;
277 keycode <= xkb->max_key_code;
278 keycode++) {
279 KeySym cursym;
280 unsigned int mods, out_mods;
281 // XkbStateFieldFromRec() doesn't work properly because
282 // state.lookup_mods isn't properly updated, so we do this manually
283 mods = XkbBuildCoreState(XkbStateMods(&state), state.group);
284 XkbTranslateKeyCode(xkb, keycode, mods, &out_mods, &cursym);
285 if (cursym == keysym)
286 break;
287 }
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200288
Pierre Ossman07580a82018-03-07 15:33:42 +0100289 if (keycode > xkb->max_key_code)
290 keycode = 0;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200291
Pierre Ossman07580a82018-03-07 15:33:42 +0100292 XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200293
Pierre Ossman07580a82018-03-07 15:33:42 +0100294 return keycode;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200295}
296#endif
297
298void XDesktop::keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down) {
299#ifdef HAVE_XTEST
Pierre Ossman07580a82018-03-07 15:33:42 +0100300 int keycode = 0;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200301
Pierre Ossman07580a82018-03-07 15:33:42 +0100302 if (!haveXtest)
303 return;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200304
Pierre Ossman07580a82018-03-07 15:33:42 +0100305 // Use scan code if provided and mapping exists
306 if (codeMap && rawKeyboard && xtcode < codeMapLen)
307 keycode = codeMap[xtcode];
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200308
Pierre Ossman07580a82018-03-07 15:33:42 +0100309 if (!keycode) {
310 if (pressedKeys.find(keysym) != pressedKeys.end())
311 keycode = pressedKeys[keysym];
312 else {
313 // XKeysymToKeycode() doesn't respect state, so we have to use
314 // something slightly more complex
315 keycode = XkbKeysymToKeycode(dpy, keysym);
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200316 }
Pierre Ossman07580a82018-03-07 15:33:42 +0100317 }
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200318
Pierre Ossman07580a82018-03-07 15:33:42 +0100319 if (!keycode)
320 return;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200321
Pierre Ossman07580a82018-03-07 15:33:42 +0100322 if (down)
323 pressedKeys[keysym] = keycode;
324 else
325 pressedKeys.erase(keysym);
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200326
Pierre Ossman07580a82018-03-07 15:33:42 +0100327 XTestFakeKeyEvent(dpy, keycode, down, CurrentTime);
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200328#endif
329}
330
331void XDesktop::clientCutText(const char* str, int len) {
332}
333
334
335bool XDesktop::handleGlobalEvent(XEvent* ev) {
Pierre Ossman07580a82018-03-07 15:33:42 +0100336 if (ev->type == xkbEventBase + XkbEventCode) {
337 XkbEvent *kb = (XkbEvent *)ev;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200338
Pierre Ossman07580a82018-03-07 15:33:42 +0100339 if (kb->any.xkb_type != XkbIndicatorStateNotify)
340 return false;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200341
Pierre Ossman07580a82018-03-07 15:33:42 +0100342 vlog.debug("Got indicator update, mask is now 0x%x", kb->indicators.state);
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200343
Pierre Ossman07580a82018-03-07 15:33:42 +0100344 ledState = 0;
345 for (int i = 0; i < XDESKTOP_N_LEDS; i++) {
346 if (kb->indicators.state & ledMasks[i])
347 ledState |= 1u << i;
Peter Åstrand (astrand)3abc7d42017-10-11 15:12:10 +0200348 }
349
Pierre Ossman07580a82018-03-07 15:33:42 +0100350 if (running)
351 server->setLEDState(ledState);
352
353 return true;
354#ifdef HAVE_XDAMAGE
355 } else if (ev->type == xdamageEventBase) {
356 XDamageNotifyEvent* dev;
357 Rect rect;
358
359 if (!running)
360 return true;
361
362 dev = (XDamageNotifyEvent*)ev;
363 rect.setXYWH(dev->area.x, dev->area.y, dev->area.width, dev->area.height);
364 server->add_changed(rect);
365
366 return true;
367#endif
368#ifdef HAVE_XFIXES
369 } else if (ev->type == xfixesEventBase + XFixesCursorNotify) {
370 XFixesCursorNotifyEvent* cev;
371
372 if (!running)
373 return true;
374
375 cev = (XFixesCursorNotifyEvent*)ev;
376
377 if (cev->subtype != XFixesDisplayCursorNotify)
378 return false;
379
380 return setCursor();
381#endif
382 }
383
384 return false;
Peter Åstrand (astrand)3abc7d42017-10-11 15:12:10 +0200385}
386
387bool XDesktop::setCursor()
388{
Pierre Ossman07580a82018-03-07 15:33:42 +0100389 XFixesCursorImage *cim;
Peter Åstrand (astrand)3abc7d42017-10-11 15:12:10 +0200390
Pierre Ossman07580a82018-03-07 15:33:42 +0100391 cim = XFixesGetCursorImage(dpy);
392 if (cim == NULL)
393 return false;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200394
Pierre Ossman07580a82018-03-07 15:33:42 +0100395 // Copied from XserverDesktop::setCursor() in
396 // unix/xserver/hw/vnc/XserverDesktop.cc and adapted to
397 // handle long -> U32 conversion for 64-bit Xlib
398 rdr::U8* cursorData;
399 rdr::U8 *out;
400 const unsigned long *pixels;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200401
Pierre Ossman07580a82018-03-07 15:33:42 +0100402 cursorData = new rdr::U8[cim->width * cim->height * 4];
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200403
Pierre Ossman07580a82018-03-07 15:33:42 +0100404 // Un-premultiply alpha
405 pixels = cim->pixels;
406 out = cursorData;
407 for (int y = 0; y < cim->height; y++) {
408 for (int x = 0; x < cim->width; x++) {
409 rdr::U8 alpha;
410 rdr::U32 pixel = *pixels++;
411 rdr::U8 *in = (rdr::U8 *) &pixel;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200412
Pierre Ossman07580a82018-03-07 15:33:42 +0100413 alpha = in[3];
414 if (alpha == 0)
415 alpha = 1; // Avoid division by zero
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200416
Pierre Ossman07580a82018-03-07 15:33:42 +0100417 *out++ = (unsigned)*in++ * 255/alpha;
418 *out++ = (unsigned)*in++ * 255/alpha;
419 *out++ = (unsigned)*in++ * 255/alpha;
420 *out++ = *in++;
421 }
422 }
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200423
Pierre Ossman07580a82018-03-07 15:33:42 +0100424 try {
425 server->setCursor(cim->width, cim->height, Point(cim->xhot, cim->yhot),
426 cursorData);
427 } catch (rdr::Exception& e) {
428 vlog.error("XserverDesktop::setCursor: %s",e.str());
429 }
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200430
Pierre Ossman07580a82018-03-07 15:33:42 +0100431 delete [] cursorData;
432 XFree(cim);
433 return true;
Peter Åstrand (astrand)3112f502017-10-10 12:27:38 +0200434}