e53cdbc9d94753be006c83b5af8ce6529127ff79
[platform/upstream/freerdp.git] / client / X11 / xf_event.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol Client
3  * X11 Event Handling
4  *
5  * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 #include <X11/Xlib.h>
21 #include <X11/Xutil.h>
22
23 #include <freerdp/kbd/kbd.h>
24 #include <freerdp/kbd/vkcodes.h>
25
26 #include "xf_rail.h"
27 #include "xf_cliprdr.h"
28
29 #include "xf_event.h"
30
31 static const char* const X11_EVENT_STRINGS[] =
32 {
33         "", "",
34         "KeyPress",
35         "KeyRelease",
36         "ButtonPress",
37         "ButtonRelease",
38         "MotionNotify",
39         "EnterNotify",
40         "LeaveNotify",
41         "FocusIn",
42         "FocusOut",
43         "KeymapNotify",
44         "Expose",
45         "GraphicsExpose",
46         "NoExpose",
47         "VisibilityNotify",
48         "CreateNotify",
49         "DestroyNotify",
50         "UnmapNotify",
51         "MapNotify",
52         "MapRequest",
53         "ReparentNotify",
54         "ConfigureNotify",
55         "ConfigureRequest",
56         "GravityNotify",
57         "ResizeRequest",
58         "CirculateNotify",
59         "CirculateRequest",
60         "PropertyNotify",
61         "SelectionClear",
62         "SelectionRequest",
63         "SelectionNotify",
64         "ColormapNotify",
65         "ClientMessage",
66         "MappingNotify",
67         "GenericEvent",
68 };
69
70 void xf_send_mouse_motion_event(rdpInput* input, boolean down, uint32 button, uint16 x, uint16 y)
71 {
72         input->MouseEvent(input, PTR_FLAGS_MOVE, x, y);
73 }
74
75 boolean xf_event_Expose(xfInfo* xfi, XEvent* event, boolean app)
76 {
77         int x, y;
78         int w, h;
79
80         x = event->xexpose.x;
81         y = event->xexpose.y;
82         w = event->xexpose.width;
83         h = event->xexpose.height;
84
85         if (app != true)
86         {
87                 XCopyArea(xfi->display, xfi->primary, xfi->window->handle, xfi->gc, x, y, w, h, x, y);
88         }
89         else
90         {
91                 xfWindow* xfw;
92                 rdpWindow* window;
93                 rdpRail* rail = ((rdpContext*) xfi->context)->rail;
94
95                 window = window_list_get_by_extra_id(rail->list, (void*) event->xexpose.window);
96
97                 if (window != NULL)
98                 {
99                         xfw = (xfWindow*) window->extra;
100                         xf_UpdateWindowArea(xfi, xfw, x, y, w, h);
101                 }
102         }
103
104         return true;
105 }
106
107 boolean xf_event_VisibilityNotify(xfInfo* xfi, XEvent* event, boolean app)
108 {
109         xfi->unobscured = event->xvisibility.state == VisibilityUnobscured;
110         return true;
111 }
112
113 boolean xf_event_MotionNotify(xfInfo* xfi, XEvent* event, boolean app)
114 {
115         rdpInput* input;
116
117         input = xfi->instance->input;
118
119         if (app != true)
120         {
121                 if (xfi->mouse_motion != true)
122                 {
123                         if ((event->xmotion.state & (Button1Mask | Button2Mask | Button3Mask)) == 0)
124                                 return true;
125                 }
126
127                 input->MouseEvent(input, PTR_FLAGS_MOVE, event->xmotion.x, event->xmotion.y);
128
129                 if (xfi->fullscreen)
130                         XSetInputFocus(xfi->display, xfi->window->handle, RevertToPointerRoot, CurrentTime);
131         }
132         else if (xfi->mouse_motion == true)
133         {
134                 rdpWindow* window;
135                 int x = event->xmotion.x;
136                 int y = event->xmotion.y;
137                 rdpRail* rail = ((rdpContext*) xfi->context)->rail;
138
139                 window = window_list_get_by_extra_id(rail->list, (void*) event->xmotion.window);
140
141                 if (window != NULL)
142                 {
143                         x += window->windowOffsetX;
144                         y += window->windowOffsetY;
145                         input->MouseEvent(input, PTR_FLAGS_MOVE, x, y);
146                 }
147         }
148
149         return true;
150 }
151
152 boolean xf_event_ButtonPress(xfInfo* xfi, XEvent* event, boolean app)
153 {
154         uint16 x, y;
155         uint16 flags;
156         boolean wheel;
157         rdpInput* input;
158
159         input = xfi->instance->input;
160
161         x = 0;
162         y = 0;
163         flags = 0;
164         wheel = false;
165
166         switch (event->xbutton.button)
167         {
168                 case 1:
169                         x = event->xbutton.x;
170                         y = event->xbutton.y;
171                         flags = PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON1;
172                         break;
173
174                 case 2:
175                         x = event->xbutton.x;
176                         y = event->xbutton.y;
177                         flags = PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON3;
178                         break;
179
180                 case 3:
181                         x = event->xbutton.x;
182                         y = event->xbutton.y;
183                         flags = PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON2;
184                         break;
185
186                 case 4:
187                         wheel = true;
188                         flags = PTR_FLAGS_WHEEL | 0x0078;
189                         break;
190
191                 case 5:
192                         wheel = true;
193                         flags = PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x0088;
194                         break;
195
196                 default:
197                         x = 0;
198                         y = 0;
199                         flags = 0;
200                         break;
201         }
202
203         if (flags != 0)
204         {
205                 if (wheel)
206                 {
207                         input->MouseEvent(input, flags, 0, 0);
208                 }
209                 else
210                 {
211                         if (app)
212                         {
213                                 rdpWindow* window;
214                                 rdpRail* rail = ((rdpContext*) xfi->context)->rail;
215
216                                 window = window_list_get_by_extra_id(rail->list, (void*) event->xbutton.window);
217
218                                 if (window != NULL)
219                                 {
220                                         x += window->windowOffsetX;
221                                         y += window->windowOffsetY;
222                                 }
223                         }
224
225                         input->MouseEvent(input, flags, x, y);
226                 }
227         }
228
229         return true;
230 }
231
232 boolean xf_event_ButtonRelease(xfInfo* xfi, XEvent* event, boolean app)
233 {
234         uint16 x, y;
235         uint16 flags;
236         rdpInput* input;
237
238         input = xfi->instance->input;
239
240         x = 0;
241         y = 0;
242         flags = 0;
243
244         switch (event->xbutton.button)
245         {
246                 case 1:
247                         x = event->xbutton.x;
248                         y = event->xbutton.y;
249                         flags = PTR_FLAGS_BUTTON1;
250                         break;
251
252                 case 2:
253                         x = event->xbutton.x;
254                         y = event->xbutton.y;
255                         flags = PTR_FLAGS_BUTTON3;
256                         break;
257
258                 case 3:
259                         x = event->xbutton.x;
260                         y = event->xbutton.y;
261                         flags = PTR_FLAGS_BUTTON2;
262                         break;
263
264                 default:
265                         flags = 0;
266                         break;
267         }
268
269         if (flags != 0)
270         {
271                 if (app)
272                 {
273                         rdpWindow* window;
274                         rdpRail* rail = ((rdpContext*) xfi->context)->rail;
275
276                         window = window_list_get_by_extra_id(rail->list, (void*) event->xbutton.window);
277
278                         if (window != NULL)
279                         {
280                                 x += window->windowOffsetX;
281                                 y += window->windowOffsetY;
282                         }
283                 }
284
285                 input->MouseEvent(input, flags, x, y);
286         }
287
288         return true;
289 }
290
291 boolean xf_event_KeyPress(xfInfo* xfi, XEvent* event, boolean app)
292 {
293         KeySym keysym;
294         char str[256];
295
296         XLookupString((XKeyEvent*) event, str, sizeof(str), &keysym, NULL);
297
298         xf_kbd_set_keypress(xfi, event->xkey.keycode, keysym);
299
300         if (xfi->fullscreen_toggle && xf_kbd_handle_special_keys(xfi, keysym))
301                 return true;
302
303         xf_kbd_send_key(xfi, true, event->xkey.keycode);
304
305         return true;
306 }
307
308 boolean xf_event_KeyRelease(xfInfo* xfi, XEvent* event, boolean app)
309 {
310         XEvent next_event;
311
312         if (XPending(xfi->display))
313         {
314                 memset(&next_event, 0, sizeof(next_event));
315                 XPeekEvent(xfi->display, &next_event);
316
317                 if (next_event.type == KeyPress)
318                 {
319                         if (next_event.xkey.keycode == event->xkey.keycode)
320                                 return true;
321                 }
322         }
323
324         xf_kbd_unset_keypress(xfi, event->xkey.keycode);
325         xf_kbd_send_key(xfi, false, event->xkey.keycode);
326
327         return true;
328 }
329
330 boolean xf_event_FocusIn(xfInfo* xfi, XEvent* event, boolean app)
331 {
332         if (event->xfocus.mode == NotifyGrab)
333                 return true;
334
335         xfi->focused = true;
336
337         if (xfi->mouse_active && (app != true))
338                 XGrabKeyboard(xfi->display, xfi->window->handle, true, GrabModeAsync, GrabModeAsync, CurrentTime);
339
340         xf_rail_send_activate(xfi, event->xany.window, true);
341         xf_kbd_focus_in(xfi);
342
343         if (xfi->remote_app != true)
344                 xf_cliprdr_check_owner(xfi);
345
346         return true;
347 }
348
349 boolean xf_event_FocusOut(xfInfo* xfi, XEvent* event, boolean app)
350 {
351         if (event->xfocus.mode == NotifyUngrab)
352                 return true;
353
354         xfi->focused = false;
355
356         if (event->xfocus.mode == NotifyWhileGrabbed)
357                 XUngrabKeyboard(xfi->display, CurrentTime);
358
359         xf_rail_send_activate(xfi, event->xany.window, false);
360
361         return true;
362 }
363
364 boolean xf_event_MappingNotify(xfInfo* xfi, XEvent* event, boolean app)
365 {
366         if (event->xmapping.request == MappingModifier)
367         {
368                 XFreeModifiermap(xfi->modifier_map);
369                 xfi->modifier_map = XGetModifierMapping(xfi->display);
370         }
371
372         return true;
373 }
374
375 boolean xf_event_ClientMessage(xfInfo* xfi, XEvent* event, boolean app)
376 {
377         if ((event->xclient.message_type == xfi->WM_PROTOCOLS)
378             && ((Atom) event->xclient.data.l[0] == xfi->WM_DELETE_WINDOW))
379         {
380                 if (app)
381                 {
382                         rdpWindow* window;
383                         rdpRail* rail = ((rdpContext*) xfi->context)->rail;
384
385                         window = window_list_get_by_extra_id(rail->list, (void*) event->xclient.window);
386
387                         if (window != NULL)
388                         {
389                                 xf_rail_send_client_system_command(xfi, window->windowId, SC_CLOSE);
390                         }
391
392                         return true;
393                 }
394                 else
395                 {
396                         return false;
397                 }
398         }
399
400         return true;
401 }
402
403 boolean xf_event_EnterNotify(xfInfo* xfi, XEvent* event, boolean app)
404 {
405         if (app != true)
406         {
407                 xfi->mouse_active = true;
408
409                 if (xfi->fullscreen)
410                         XSetInputFocus(xfi->display, xfi->window->handle, RevertToPointerRoot, CurrentTime);
411
412                 if (xfi->focused)
413                         XGrabKeyboard(xfi->display, xfi->window->handle, true, GrabModeAsync, GrabModeAsync, CurrentTime);
414         } else {
415                 // Keep track of which window has focus so that we can apply pointer updates
416                 xfWindow* xfw;
417                 rdpWindow* window;
418                 rdpRail* rail = ((rdpContext*) xfi->context)->rail;
419                 window = window_list_get_by_extra_id(rail->list, (void*) event->xexpose.window);
420                 if (window != NULL)
421                 {
422                         xfw = (xfWindow*) window->extra;
423                         xfi->window = xfw;
424                 }
425         }
426
427         return true;
428 }
429
430 boolean xf_event_LeaveNotify(xfInfo* xfi, XEvent* event, boolean app)
431 {
432         if (app != true)
433         {
434                 xfi->mouse_active = false;
435                 XUngrabKeyboard(xfi->display, CurrentTime);
436         }
437
438         return true;
439 }
440
441 boolean xf_event_ConfigureNotify(xfInfo* xfi, XEvent* event, boolean app)
442 {
443         rdpWindow* window;
444         rdpRail* rail = ((rdpContext*) xfi->context)->rail;
445
446         window = window_list_get_by_extra_id(rail->list, (void*) event->xconfigure.window);
447
448         if (window != NULL)
449         {
450                 xfWindow* xfw;
451                 uint32 left, top;
452                 uint32 right, bottom;
453                 uint32 width, height;
454                 xfw = (xfWindow*) window->extra;
455
456                 left = event->xconfigure.x;
457                 top = event->xconfigure.y;
458                 width = event->xconfigure.width;
459                 height = event->xconfigure.height;
460                 right = left + width - 1;
461                 bottom = top + height - 1;
462
463                 DEBUG_X11_LMS("ConfigureNotify: send_event=%d eventWindow=0x%X window=0x%X above=0x%X rc={l=%d t=%d r=%d b=%d} "
464                         "w=%d h=%d override_redirect=%d",
465                         event->xconfigure.send_event,
466                         (uint32) event->xconfigure.event,
467                         (uint32) event->xconfigure.window,
468                         (uint32) event->xconfigure.above,
469                         left, top, right, bottom, width, height,
470                         event->xconfigure.override_redirect);
471         }
472
473         return true;
474 }
475
476 boolean xf_event_MapNotify(xfInfo* xfi, XEvent* event, boolean app)
477 {
478         rdpWindow* window;
479         rdpRail* rail = ((rdpContext*) xfi->context)->rail;
480
481         if (app != true)
482                 return true;
483
484         window = window_list_get_by_extra_id(rail->list, (void*) event->xany.window);
485
486         if (window != NULL)
487         {
488                 /* local restore event */
489                 xf_rail_send_client_system_command(xfi, window->windowId, SC_RESTORE);
490         }
491
492         return true;
493 }
494
495 boolean xf_event_SelectionNotify(xfInfo* xfi, XEvent* event, boolean app)
496 {
497         if (xfi->remote_app != true)
498         {
499                 if (xf_cliprdr_process_selection_notify(xfi, event))
500                         return true;
501         }
502
503         return true;
504 }
505
506 boolean xf_event_SelectionRequest(xfInfo* xfi, XEvent* event, boolean app)
507 {
508         if (xfi->remote_app != true)
509         {
510                 if (xf_cliprdr_process_selection_request(xfi, event))
511                         return true;
512         }
513
514         return true;
515 }
516
517 boolean xf_event_SelectionClear(xfInfo* xfi, XEvent* event, boolean app)
518 {
519         if (xfi->remote_app != true)
520         {
521                 if (xf_cliprdr_process_selection_clear(xfi, event))
522                         return true;
523         }
524
525         return true;
526 }
527
528 boolean xf_event_PropertyNotify(xfInfo* xfi, XEvent* event, boolean app)
529 {
530         if (xfi->remote_app != true)
531         {
532                 if (xf_cliprdr_process_property_notify(xfi, event))
533                         return true;
534         }
535
536         return true;
537 }
538
539 boolean xf_event_process(freerdp* instance, XEvent* event)
540 {
541         boolean app = false;
542         boolean status = true;
543         xfInfo* xfi = ((xfContext*) instance->context)->xfi;
544
545         if (xfi->remote_app == true)
546         {
547                 app = true;
548         }
549         else
550         {
551                 if (event->xany.window != xfi->window->handle)
552                         app = true;
553         }
554
555
556         if (event->type != MotionNotify)
557                 DEBUG_X11("%s Event: wnd=0x%04X", X11_EVENT_STRINGS[event->type], (uint32) event->xany.window);
558
559         switch (event->type)
560         {
561                 case Expose:
562                         status = xf_event_Expose(xfi, event, app);
563                         break;
564
565                 case VisibilityNotify:
566                         status = xf_event_VisibilityNotify(xfi, event, app);
567                         break;
568
569                 case MotionNotify:
570                         status = xf_event_MotionNotify(xfi, event, app);
571                         break;
572
573                 case ButtonPress:
574                         status = xf_event_ButtonPress(xfi, event, app);
575                         break;
576
577                 case ButtonRelease:
578                         status = xf_event_ButtonRelease(xfi, event, app);
579                         break;
580
581                 case KeyPress:
582                         status = xf_event_KeyPress(xfi, event, app);
583                         break;
584
585                 case KeyRelease:
586                         status = xf_event_KeyRelease(xfi, event, app);
587                         break;
588
589                 case FocusIn:
590                         status = xf_event_FocusIn(xfi, event, app);
591                         break;
592
593                 case FocusOut:
594                         status = xf_event_FocusOut(xfi, event, app);
595                         break;
596
597                 case EnterNotify:
598                         status = xf_event_EnterNotify(xfi, event, app);
599                         break;
600
601                 case LeaveNotify:
602                         status = xf_event_LeaveNotify(xfi, event, app);
603                         break;
604
605                 case NoExpose:
606                         break;
607
608                 case GraphicsExpose:
609                         break;
610
611                 case ConfigureNotify:
612                         status = xf_event_ConfigureNotify(xfi, event, app);
613                         break;
614
615                 case MapNotify:
616                         status = xf_event_MapNotify(xfi, event, app);
617                         break;
618
619                 case ReparentNotify:
620                         break;
621
622                 case MappingNotify:
623                         status = xf_event_MappingNotify(xfi, event, app);
624                         break;
625
626                 case ClientMessage:
627                         status = xf_event_ClientMessage(xfi, event, app);
628                         break;
629
630                 case SelectionNotify:
631                         status = xf_event_SelectionNotify(xfi, event, app);
632                         break;
633
634                 case SelectionRequest:
635                         status = xf_event_SelectionRequest(xfi, event, app);
636                         break;
637
638                 case SelectionClear:
639                         status = xf_event_SelectionClear(xfi, event, app);
640                         break;
641
642                 case PropertyNotify:
643                         status = xf_event_PropertyNotify(xfi, event, app);
644                         break;
645
646                 default:
647                         DEBUG_X11("xf_event_process unknown event %d", event->type);
648                         break;
649         }
650
651         return status;
652 }