1 /*
2 XInput2 device handling routines for x11vnc.
3
4 Copyright (C) 2009-2010 Christian Beier <dontmind@freeshell.org>
5 All rights reserved.
6
7 This file is part of x11vnc.
8
9 x11vnc is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or (at
12 your option) any later version.
13
14 x11vnc is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with x11vnc; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
22 or see <http://www.gnu.org/licenses/>.
23 */
24
25 #include <string.h>
26 #ifdef HAVE_X11
27 #include <X11/Xproto.h>
28 #include <X11/keysym.h>
29 #endif
30
31 #include "x11vnc.h"
32 #include "cursor.h"
33 #include "cleanup.h"
34 #include "win_utils.h"
35 #include "xi2_devices.h"
36
37 #ifdef HAVE_LIBXCURSOR
38 #include <X11/Xcursor/Xcursor.h>
39 #ifdef HAVE_CAIRO
40 #include <cairo.h>
41 #endif
42 #endif
43
44
45 /* does the X version we're running on support XI2? */
46 int xinput2_present;
47 int xi2_device_creation_in_progress;
48
49
50 /*
51 create MD with given name
52 returns device id, -1 on error
53 */
createMD(Display * dpy,char * name)54 int createMD(Display* dpy, char* name)
55 {
56 #ifndef HAVE_XI2
57 return -1;
58 #else
59 int dev_id = -1;
60 XErrorHandler old_handler;
61 XIAddMasterInfo c;
62 XIDeviceInfo *devinfo;
63 int num_devices, i;
64 char handle[256]; /* device name */
65 snprintf(handle, sizeof handle, "%s pointer", name);
66
67 c.type = XIAddMaster;
68 c.name = name;
69 c.send_core = 1;
70 c.enable = 1;
71
72 X_LOCK;
73
74 trapped_xerror = 0;
75 old_handler = XSetErrorHandler(trap_xerror);
76
77 XIChangeHierarchy(dpy, (XIAnyHierarchyChangeInfo*)&c, 1);
78 XSync(dpy, False);
79
80 if(trapped_xerror) {
81 XSetErrorHandler(old_handler);
82 trapped_xerror = 0;
83 X_UNLOCK;
84 return -1;
85 }
86
87 XSetErrorHandler(old_handler);
88 trapped_xerror = 0;
89
90 /* find newly created dev by name
91 FIXME: better wait for XIHierarchy event here? */
92 devinfo = XIQueryDevice(dpy, XIAllMasterDevices, &num_devices);
93 for(i = num_devices-1; i >= 0; --i)
94 if(strcmp(devinfo[i].name, handle) == 0)
95 {
96 dev_id = devinfo[i].deviceid;
97 break;
98 }
99
100 XIFreeDeviceInfo(devinfo);
101
102 X_UNLOCK;
103
104 return dev_id;
105 #endif
106 }
107
108
109
110 /*
111 remove device
112 return 1 on success, 0 on failure
113 */
removeMD(Display * dpy,int dev_id)114 int removeMD(Display* dpy, int dev_id)
115 {
116 #ifndef HAVE_XI2
117 return 0;
118 #else
119 int found = 0, res = 0;
120 XIDeviceInfo *devinfo;
121 int num_devices, i;
122
123 if(dev_id < 0)
124 return 0;
125
126 X_LOCK;
127
128 /* see if this device exists */
129 devinfo = XIQueryDevice(dpy, XIAllMasterDevices, &num_devices);
130 for(i = 0; i < num_devices; ++i)
131 if(devinfo[i].deviceid == dev_id)
132 found = 1;
133 XIFreeDeviceInfo(devinfo);
134
135 if(found) {
136 XIRemoveMasterInfo r;
137
138 /* we need to unset client pointer */
139 XISetClientPointer(dpy, None, dev_id);
140 XSync(dpy, False);
141
142 /* actually remove device pair */
143 r.type = XIRemoveMaster;
144 r.deviceid = dev_id;
145 r.return_mode = XIFloating;
146
147 res = XIChangeHierarchy(dpy, (XIAnyHierarchyChangeInfo*)&r, 1) == Success ? 1 : 0;
148 XSync(dpy, False);
149 }
150
151 X_UNLOCK;
152
153 return res;
154 #endif
155 }
156
157
removeAllMDs(Display * dpy)158 void removeAllMDs(Display *dpy)
159 {
160 /* remove all created XInput2 devices */
161 rfbClientIteratorPtr iter = rfbGetClientIterator(screen);
162 rfbClientPtr cl;
163 while((cl = rfbClientIteratorNext(iter))) {
164 ClientData *cd = (ClientData *) cl->clientData;
165 if(removeMD(dpy, cd->ptr_id))
166 rfbLog("cleanup: removed XInput2 MD for client %s.\n", cl->host);
167 }
168 rfbReleaseClientIterator(iter);
169 }
170
171
172
173
getPairedMD(Display * dpy,int dev_id)174 int getPairedMD(Display* dpy, int dev_id)
175 {
176 #ifndef HAVE_XI2
177 return -1;
178 #else
179 int paired = -1;
180 XIDeviceInfo* devinfo;
181 int devicecount = 0;
182
183 if(dev_id < 0)
184 return paired;
185
186 X_LOCK;
187
188 devinfo = XIQueryDevice(dpy, dev_id, &devicecount);
189 if(devicecount)
190 paired = devinfo->attachment;
191 XIFreeDeviceInfo(devinfo);
192
193 X_UNLOCK;
194
195 return paired;
196 #endif
197 }
198
199
200
201
202
203
204 /*
205 set cursor of pointer dev.
206 returns the cursor shape as an rfbCursorPtr
207 */
setClientCursor(Display * dpy,int dev_id,float r,float g,float b,char * label)208 rfbCursorPtr setClientCursor(Display *dpy, int dev_id, float r, float g, float b, char *label)
209 {
210 #ifndef HAVE_LIBXCURSOR
211 return NULL;
212 #else
213 #ifndef HAVE_CAIRO
214 return NULL;
215 #else
216
217 /* label setup */
218 const int idFontSize = 18;
219 const int idXOffset = 11;
220 const int idYOffset = 25;
221 const size_t textsz = 64;
222 char text[textsz];
223 int total_width, total_height;
224 cairo_surface_t* main_surface;
225 cairo_surface_t* dummy_surface;
226 cairo_surface_t* barecursor_surface;
227 cairo_t* cr;
228 cairo_text_extents_t est;
229 Cursor cursor;
230 XcursorImage *cursor_image = NULL;
231 rfbCursorPtr rfbcursor = NULL;
232
233 if(dev_id < 0)
234 return NULL;
235
236 if(label)
237 snprintf(text, textsz, "%s", label);
238 else
239 snprintf(text, textsz, "%i", (int) dev_id);
240
241 /* simple cursor w/o label */
242 barecursor_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 24, 24);
243 cr = cairo_create(barecursor_surface);
244 cairo_move_to (cr, 1, 1);
245 cairo_line_to (cr, 12, 8);
246 cairo_line_to (cr, 5, 15);
247 cairo_close_path (cr);
248 cairo_set_source_rgba(cr, r, g, b, 0.9);
249 cairo_fill_preserve (cr);
250 cairo_set_source_rgba(cr, 0, 0, 0, 0.8);
251 cairo_set_line_width (cr, 0.8);
252 cairo_stroke (cr);
253
254
255 /* get estimated text extents */
256 dummy_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 500, 10);/* ah well, but should fit */
257 cr = cairo_create(dummy_surface);
258 cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
259 cairo_set_font_size (cr, idFontSize);
260 cairo_text_extents(cr, text, &est);
261
262 /* an from these calculate our final size */
263 total_width = (int)(idXOffset + est.width + est.x_bearing);
264 total_height = (int)(idYOffset + est.height + est.y_bearing);
265
266 /* draw evrything */
267 main_surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, total_width, total_height );
268 cr = cairo_create(main_surface);
269 cairo_set_source_surface(cr, barecursor_surface, 0, 0);
270 cairo_paint (cr);
271 cairo_select_font_face (cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
272 cairo_set_font_size (cr, idFontSize);
273 cairo_set_source_rgba (cr, r, g, b, 0.8);
274 cairo_move_to(cr, idXOffset, idYOffset);
275 cairo_show_text(cr,text);
276
277 X_LOCK;
278 /* copy cairo surface to cursor image */
279 cursor_image = XcursorImageCreate(total_width, total_height);
280 /* this is important! otherwise we get badmatch, badcursor xerrrors galore... */
281 cursor_image->xhot = cursor_image->yhot = 0;
282 memcpy(cursor_image->pixels, cairo_image_surface_get_data (main_surface), sizeof(CARD32) * total_width * total_height);
283 X_UNLOCK;
284
285 /* convert to rfb cursor which we return later */
286 rfbcursor = pixels2curs(cursor_image->pixels,
287 cursor_image->width,
288 cursor_image->height,
289 cursor_image->xhot,
290 cursor_image->yhot,
291 bpp/8);
292
293 X_LOCK;
294
295 /* and display */
296 cursor = XcursorImageLoadCursor(dpy, cursor_image);
297 XIDefineCursor(dpy, dev_id, RootWindow(dpy, DefaultScreen(dpy)), cursor);
298 XFreeCursor(dpy, cursor);
299
300 /* clean up */
301 cairo_destroy(cr);
302 cairo_surface_destroy(dummy_surface);
303 cairo_surface_destroy(main_surface);
304 cairo_surface_destroy(barecursor_surface);
305 XcursorImageDestroy(cursor_image);
306
307 X_UNLOCK;
308
309 return rfbcursor;
310 #endif
311 #endif
312 }
313
314
315
316 /*
317 Sets the paired keyboard's focus to the window underneath the given pointer.
318 returns 1 on success, 0 on fail
319 */
setDeviceFocus(Display * dpy,int ptr_id)320 int setDeviceFocus(Display* dpy, int ptr_id)
321 {
322 #ifndef HAVE_XI2
323 return 0;
324 #else
325
326 XErrorHandler old_handler;
327
328 Window root_return;
329 Window child_return;
330 double root_x_return;
331 double root_y_return;
332 double win_x_return;
333 double win_y_return;
334 XIButtonState buttons_return;
335 XIModifierState modifiers_return;
336 XIGroupState group_return;
337
338 XIDeviceInfo* devinfo;
339 int devicecount = 0;
340 int paired = -1;
341
342 if(ptr_id < 0)
343 return 0;
344
345 X_LOCK;
346
347 trapped_xerror = 0;
348 old_handler = XSetErrorHandler(trap_xerror);
349
350 /* get window the pointer is in */
351 XIQueryPointer(dpy, ptr_id, rootwin, &root_return, &child_return,
352 &root_x_return, &root_y_return, &win_x_return, &win_y_return,
353 &buttons_return, &modifiers_return, &group_return);
354
355 /* get paired keyboard */
356 devinfo = XIQueryDevice(dpy, ptr_id, &devicecount);
357 if(devicecount)
358 paired = devinfo->attachment;
359 XIFreeDeviceInfo(devinfo);
360
361 XISetFocus(dpy, paired, find_client(dpy, root_return, child_return), CurrentTime);
362 XSync(dpy, False);
363
364 if(trapped_xerror) {
365 XSetErrorHandler(old_handler);
366 trapped_xerror = 0;
367 X_UNLOCK;
368 return 0;
369 }
370
371 XSetErrorHandler(old_handler);
372 trapped_xerror = 0;
373
374 X_UNLOCK;
375
376 return 1;
377 #endif
378 }
379
380
381
382 /*
383 sets the XI client pointer for client window underneath this pointer.
384 returns 1 on success, 0 on fail
385 */
setXIClientPointer(Display * dpy,int dev_id)386 int setXIClientPointer(Display* dpy, int dev_id)
387 {
388 #ifndef HAVE_XI2
389 return 0;
390 #else
391
392 XErrorHandler old_handler;
393
394 Window root_return;
395 Window child_return;
396 double root_x_return;
397 double root_y_return;
398 double win_x_return;
399 double win_y_return;
400 XIButtonState buttons_return;
401 XIModifierState modifiers_return;
402 XIGroupState group_return;
403
404 if(dev_id < 0)
405 return 0;
406
407 X_LOCK;
408
409 trapped_xerror = 0;
410 old_handler = XSetErrorHandler(trap_xerror);
411
412 /* get window the pointer is in */
413 XIQueryPointer(dpy, dev_id, rootwin, &root_return, &child_return,
414 &root_x_return, &root_y_return, &win_x_return, &win_y_return,
415 &buttons_return, &modifiers_return, &group_return);
416
417 XISetClientPointer(dpy, find_client(dpy, root_return, child_return), dev_id);
418 XSync(dpy, False);
419
420 if(trapped_xerror) {
421 XSetErrorHandler(old_handler);
422 fprintf(stderr, "got x error\n");
423 trapped_xerror = 0;
424 X_UNLOCK;
425 return 0;
426 }
427
428 XSetErrorHandler(old_handler);
429 trapped_xerror = 0;
430
431 X_UNLOCK;
432
433 return 1;
434 #endif
435 }
436