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