1d2a58fdeb2dc90d486a58dc707e97d2e24f253c
[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_window.h"
28 #include "xf_cliprdr.h"
29
30 #include "xf_event.h"
31
32 static const char* const X11_EVENT_STRINGS[] =
33 {
34         "", "",
35         "KeyPress",
36         "KeyRelease",
37         "ButtonPress",
38         "ButtonRelease",
39         "MotionNotify",
40         "EnterNotify",
41         "LeaveNotify",
42         "FocusIn",
43         "FocusOut",
44         "KeymapNotify",
45         "Expose",
46         "GraphicsExpose",
47         "NoExpose",
48         "VisibilityNotify",
49         "CreateNotify",
50         "DestroyNotify",
51         "UnmapNotify",
52         "MapNotify",
53         "MapRequest",
54         "ReparentNotify",
55         "ConfigureNotify",
56         "ConfigureRequest",
57         "GravityNotify",
58         "ResizeRequest",
59         "CirculateNotify",
60         "CirculateRequest",
61         "PropertyNotify",
62         "SelectionClear",
63         "SelectionRequest",
64         "SelectionNotify",
65         "ColormapNotify",
66         "ClientMessage",
67         "MappingNotify",
68         "GenericEvent",
69 };
70
71 void xf_send_mouse_motion_event(rdpInput* input, boolean down, uint32 button, uint16 x, uint16 y)
72 {
73         input->MouseEvent(input, PTR_FLAGS_MOVE, x, y);
74 }
75
76 boolean xf_event_Expose(xfInfo* xfi, XEvent* event, boolean app)
77 {
78         int x, y;
79         int w, h;
80
81         x = event->xexpose.x;
82         y = event->xexpose.y;
83         w = event->xexpose.width;
84         h = event->xexpose.height;
85
86         if (app != true)
87         {
88                 XCopyArea(xfi->display, xfi->primary, xfi->window->handle, xfi->gc, x, y, w, h, x, y);
89         }
90         else
91         {
92                 xfWindow* xfw;
93                 rdpWindow* window;
94                 rdpRail* rail = ((rdpContext*) xfi->context)->rail;
95
96                 window = window_list_get_by_extra_id(rail->list, (void*) event->xexpose.window);
97
98                 if (window != NULL)
99                 {
100                         xfw = (xfWindow*) window->extra;
101                         xf_UpdateWindowArea(xfi, xfw, x, y, w, h);
102                 }
103         }
104
105         return true;
106 }
107
108 boolean xf_event_VisibilityNotify(xfInfo* xfi, XEvent* event, boolean app)
109 {
110         xfi->unobscured = event->xvisibility.state == VisibilityUnobscured;
111         return true;
112 }
113
114 boolean xf_event_MotionNotify(xfInfo* xfi, XEvent* event, boolean app)
115 {
116         rdpInput* input;
117
118         input = xfi->instance->input;
119
120         if (app != true)
121         {
122                 if (xfi->mouse_motion != true)
123                 {
124                         if ((event->xmotion.state & (Button1Mask | Button2Mask | Button3Mask)) == 0)
125                                 return true;
126                 }
127
128                 input->MouseEvent(input, PTR_FLAGS_MOVE, event->xmotion.x, event->xmotion.y);
129
130                 if (xfi->fullscreen)
131                         XSetInputFocus(xfi->display, xfi->window->handle, RevertToPointerRoot, CurrentTime);
132         }
133         else if (xfi->mouse_motion == true)
134         {
135                 rdpWindow* window;
136                 int x = event->xmotion.x;
137                 int y = event->xmotion.y;
138                 rdpRail* rail = ((rdpContext*) xfi->context)->rail;
139
140                 window = window_list_get_by_extra_id(rail->list, (void*) event->xmotion.window);
141
142                 if (window != NULL)
143                 {
144                         x += window->windowOffsetX;
145                         y += window->windowOffsetY;
146                         input->MouseEvent(input, PTR_FLAGS_MOVE, x, y);
147                 }
148         }
149
150         return true;
151 }
152
153 boolean xf_event_ButtonPress(xfInfo* xfi, XEvent* event, boolean app)
154 {
155         uint16 x, y;
156         uint16 flags;
157         boolean wheel;
158         rdpInput* input;
159
160         input = xfi->instance->input;
161
162         x = 0;
163         y = 0;
164         flags = 0;
165         wheel = false;
166
167         switch (event->xbutton.button)
168         {
169                 case 1:
170                         x = event->xbutton.x;
171                         y = event->xbutton.y;
172                         flags = PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON1;
173                         break;
174
175                 case 2:
176                         x = event->xbutton.x;
177                         y = event->xbutton.y;
178                         flags = PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON3;
179                         break;
180
181                 case 3:
182                         x = event->xbutton.x;
183                         y = event->xbutton.y;
184                         flags = PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON2;
185                         break;
186
187                 case 4:
188                         wheel = true;
189                         flags = PTR_FLAGS_WHEEL | 0x0078;
190                         break;
191
192                 case 5:
193                         wheel = true;
194                         flags = PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x0088;
195                         break;
196
197                 default:
198                         x = 0;
199                         y = 0;
200                         flags = 0;
201                         break;
202         }
203
204         if (flags != 0)
205         {
206                 if (wheel)
207                 {
208                         input->MouseEvent(input, flags, 0, 0);
209                 }
210                 else
211                 {
212                         if (app)
213                         {
214                                 rdpWindow* window;
215                                 rdpRail* rail = ((rdpContext*) xfi->context)->rail;
216
217                                 window = window_list_get_by_extra_id(rail->list, (void*) event->xbutton.window);
218
219                                 if (window != NULL)
220                                 {
221                                         x += window->windowOffsetX;
222                                         y += window->windowOffsetY;
223                                 }
224                         }
225
226                         input->MouseEvent(input, flags, x, y);
227                 }
228         }
229
230         return true;
231 }
232
233 boolean xf_event_ButtonRelease(xfInfo* xfi, XEvent* event, boolean app)
234 {
235         uint16 x, y;
236         uint16 flags;
237         rdpInput* input;
238
239         input = xfi->instance->input;
240
241         x = 0;
242         y = 0;
243         flags = 0;
244
245         switch (event->xbutton.button)
246         {
247                 case 1:
248                         x = event->xbutton.x;
249                         y = event->xbutton.y;
250                         flags = PTR_FLAGS_BUTTON1;
251                         break;
252
253                 case 2:
254                         x = event->xbutton.x;
255                         y = event->xbutton.y;
256                         flags = PTR_FLAGS_BUTTON3;
257                         break;
258
259                 case 3:
260                         x = event->xbutton.x;
261                         y = event->xbutton.y;
262                         flags = PTR_FLAGS_BUTTON2;
263                         break;
264
265                 default:
266                         flags = 0;
267                         break;
268         }
269
270         if (flags != 0)
271         {
272                 if (app)
273                 {
274                         rdpWindow* window;
275                         rdpRail* rail = ((rdpContext*) xfi->context)->rail;
276
277                         window = window_list_get_by_extra_id(rail->list, (void*) event->xbutton.window);
278
279                         if (window != NULL)
280                         {
281                                 x += window->windowOffsetX;
282                                 y += window->windowOffsetY;
283                         }
284                 }
285
286                 input->MouseEvent(input, flags, x, y);
287         }
288
289         return true;
290 }
291
292 boolean xf_event_KeyPress(xfInfo* xfi, XEvent* event, boolean app)
293 {
294         KeySym keysym;
295         char str[256];
296
297         XLookupString((XKeyEvent*) event, str, sizeof(str), &keysym, NULL);
298
299         xf_kbd_set_keypress(xfi, event->xkey.keycode, keysym);
300
301         if (xfi->fullscreen_toggle && xf_kbd_handle_special_keys(xfi, keysym))
302                 return true;
303
304         xf_kbd_send_key(xfi, true, event->xkey.keycode);
305
306         return true;
307 }
308
309 boolean xf_event_KeyRelease(xfInfo* xfi, XEvent* event, boolean app)
310 {
311         XEvent next_event;
312
313         if (XPending(xfi->display))
314         {
315                 memset(&next_event, 0, sizeof(next_event));
316                 XPeekEvent(xfi->display, &next_event);
317
318                 if (next_event.type == KeyPress)
319                 {
320                         if (next_event.xkey.keycode == event->xkey.keycode)
321                                 return true;
322                 }
323         }
324
325         xf_kbd_unset_keypress(xfi, event->xkey.keycode);
326         xf_kbd_send_key(xfi, false, event->xkey.keycode);
327
328         return true;
329 }
330
331 boolean xf_event_FocusIn(xfInfo* xfi, XEvent* event, boolean app)
332 {
333         if (event->xfocus.mode == NotifyGrab)
334                 return true;
335
336         xfi->focused = true;
337
338         if (xfi->mouse_active && (app != true))
339                 XGrabKeyboard(xfi->display, xfi->window->handle, true, GrabModeAsync, GrabModeAsync, CurrentTime);
340
341         xf_rail_send_activate(xfi, event->xany.window, true);
342         xf_kbd_focus_in(xfi);
343
344         if (app != true)
345                 xf_cliprdr_check_owner(xfi);
346
347         return true;
348 }
349
350 boolean xf_event_FocusOut(xfInfo* xfi, XEvent* event, boolean app)
351 {
352         if (event->xfocus.mode == NotifyUngrab)
353                 return true;
354
355         xfi->focused = false;
356
357         if (event->xfocus.mode == NotifyWhileGrabbed)
358                 XUngrabKeyboard(xfi->display, CurrentTime);
359
360         xf_rail_send_activate(xfi, event->xany.window, false);
361
362         return true;
363 }
364
365 boolean xf_event_MappingNotify(xfInfo* xfi, XEvent* event, boolean app)
366 {
367         if (event->xmapping.request == MappingModifier)
368         {
369                 XFreeModifiermap(xfi->modifier_map);
370                 xfi->modifier_map = XGetModifierMapping(xfi->display);
371         }
372
373         return true;
374 }
375
376 boolean xf_event_ClientMessage(xfInfo* xfi, XEvent* event, boolean app)
377 {
378         if ((event->xclient.message_type == xfi->WM_PROTOCOLS)
379             && ((Atom) event->xclient.data.l[0] == xfi->WM_DELETE_WINDOW))
380         {
381                 if (app)
382                 {
383                         rdpWindow* window;
384                         rdpRail* rail = ((rdpContext*) xfi->context)->rail;
385
386                         window = window_list_get_by_extra_id(rail->list, (void*) event->xclient.window);
387
388                         if (window != NULL)
389                         {
390                                 xf_rail_send_client_system_command(xfi, window->windowId, SC_CLOSE);
391                         }
392
393                         return true;
394                 }
395                 else
396                 {
397                         return false;
398                 }
399         }
400
401         return true;
402 }
403
404 boolean xf_event_EnterNotify(xfInfo* xfi, XEvent* event, boolean app)
405 {
406         if (app != true)
407         {
408                 xfi->mouse_active = true;
409
410                 if (xfi->fullscreen)
411                         XSetInputFocus(xfi->display, xfi->window->handle, RevertToPointerRoot, CurrentTime);
412
413                 if (xfi->focused)
414                         XGrabKeyboard(xfi->display, xfi->window->handle, true, GrabModeAsync, GrabModeAsync, CurrentTime);
415         } else {
416                 // Keep track of which window has focus so that we can apply pointer updates
417                 xfWindow* xfw;
418                 rdpWindow* window;
419                 rdpRail* rail = ((rdpContext*) xfi->context)->rail;
420                 window = window_list_get_by_extra_id(rail->list, (void*) event->xexpose.window);
421                 if (window != NULL)
422                 {
423                         xfw = (xfWindow*) window->extra;
424                         xfi->window = xfw;
425                 }
426         }
427
428         return true;
429 }
430
431 boolean xf_event_LeaveNotify(xfInfo* xfi, XEvent* event, boolean app)
432 {
433         if (app != true)
434         {
435                 xfi->mouse_active = false;
436                 XUngrabKeyboard(xfi->display, CurrentTime);
437         }
438
439         return true;
440 }
441
442 boolean xf_event_ConfigureNotify(xfInfo* xfi, XEvent* event, boolean app)
443 {
444         rdpWindow* window;
445         rdpRail* rail = ((rdpContext*) xfi->context)->rail;
446
447         window = window_list_get_by_extra_id(rail->list, (void*) event->xconfigure.window);
448
449         if (window != NULL)
450         {
451                 xfWindow* xfw;
452                 xfw = (xfWindow*) window->extra;
453
454                 // ConfigureNotify coordinates are expressed relative to the window parent.
455                 // Translate these to root window coordinates.
456                 Window childWindow;
457                 XTranslateCoordinates(xfi->display, xfw->handle, 
458                         RootWindowOfScreen(xfi->screen),
459                         0, 0, &xfw->left, &xfw->top, &childWindow);
460
461                 xfw->width = event->xconfigure.width;
462                 xfw->height = event->xconfigure.height;
463                 xfw->right = xfw->left + xfw->width - 1;
464                 xfw->bottom = xfw->top + xfw->height - 1;
465
466                 DEBUG_X11_LMS("window=0x%X rc={l=%d t=%d r=%d b=%d} w=%u h=%u send_event=%d",
467                         (uint32) xfw->handle, 
468                         xfw->left, xfw->top, xfw->right, xfw->bottom, 
469                         xfw->width, xfw->height, event->xconfigure.send_event);
470
471                 if (app && ! event->xconfigure.send_event)
472                         xf_rail_adjust_position(xfi, window);
473         }
474
475         return True;
476 }
477
478 boolean xf_event_MapNotify(xfInfo* xfi, XEvent* event, boolean app)
479 {
480         rdpWindow* window;
481         rdpRail* rail = ((rdpContext*) xfi->context)->rail;
482
483         if (app != true)
484                 return true;
485
486         window = window_list_get_by_extra_id(rail->list, (void*) event->xany.window);
487
488         if (window != NULL)
489         {
490                 /* local restore event */
491                 xf_rail_send_client_system_command(xfi, window->windowId, SC_RESTORE);
492                 xfWindow *xfw = (xfWindow*) window->extra;
493                 xfw->is_mapped = true;
494         }
495
496         return true;
497 }
498
499 boolean xf_event_UnmapNotify(xfInfo* xfi, XEvent* event, boolean app)
500 {
501         rdpWindow* window;
502         rdpRail* rail = ((rdpContext*) xfi->context)->rail;
503
504         if (app != true)
505                 return true;
506
507         window = window_list_get_by_extra_id(rail->list, (void*) event->xany.window);
508
509         if (window != NULL)
510         {
511                 xfWindow *xfw = (xfWindow*) window->extra;
512                 xfw->is_mapped = false;
513         }
514
515         return true;
516 }
517
518 boolean xf_event_SelectionNotify(xfInfo* xfi, XEvent* event, boolean app)
519 {
520         if (app != true)
521         {
522                 if (xf_cliprdr_process_selection_notify(xfi, event))
523                         return true;
524         }
525
526         return true;
527 }
528
529 boolean xf_event_SelectionRequest(xfInfo* xfi, XEvent* event, boolean app)
530 {
531         if (app != true)
532         {
533                 if (xf_cliprdr_process_selection_request(xfi, event))
534                         return true;
535         }
536
537         return true;
538 }
539
540 boolean xf_event_SelectionClear(xfInfo* xfi, XEvent* event, boolean app)
541 {
542         if (app != true)
543         {
544                 if (xf_cliprdr_process_selection_clear(xfi, event))
545                         return true;
546         }
547
548         return true;
549 }
550
551 boolean xf_event_PropertyNotify(xfInfo* xfi, XEvent* event, boolean app)
552 {
553         if (app != true)
554         {
555                 if (xf_cliprdr_process_property_notify(xfi, event))
556                         return true;
557         }
558
559         return true;
560 }
561
562 boolean xf_event_suppress_events(xfInfo *xfi, rdpWindow *window, XEvent*event)
563 {
564         if (! xfi->remote_app)
565                 return false;
566
567         switch (xfi->window->local_move.state)
568         {
569                 case LMS_NOT_ACTIVE:
570                         // No local move in progress, nothing to do
571                         break;
572                 case LMS_STARTING:
573                         // Local move initiated by RDP server, but we
574                         // have not yet seen any updates from the X server
575                         switch(event->type)
576                         {
577                                 case ConfigureNotify:
578                                         // Starting to see move events 
579                                         // from the X server. Local 
580                                         // move is now in progress.
581                                         xfi->window->local_move.state = LMS_ACTIVE;
582
583                                         // Allow these events to be processed during move to keep
584                                         // our state up to date.
585                                         break;
586                                 case ButtonPress:
587                                 case ButtonRelease:
588                                 case KeyPress:
589                                 case KeyRelease:
590                                 case UnmapNotify:
591                                         // A button release event means the X 
592                                         // window server did not grab the
593                                         // mouse before the user released it.  
594                                         // In this case we must cancel the 
595                                         // local move. The event will be 
596                                         // processed below as normal, below.
597                                         break;
598                                 case VisibilityNotify:
599                                 case PropertyNotify:
600                                 case Expose:
601                                         // Allow these events to pass
602                                         break;
603                                 default:
604                                         // Eat any other events 
605                                         return true;
606                         }
607                         break;
608
609                 case LMS_ACTIVE:
610                         // Local move is in progress
611                         switch(event->type)
612                         {
613                                 case ConfigureNotify:
614                                 case VisibilityNotify:
615                                 case PropertyNotify:
616                                 case Expose:
617                                         // Keep us up to date on position
618                                         break;
619                                 default:
620                                         // Any other event terminates move
621                                         xf_rail_end_local_move(xfi, window);
622                                         break;
623                         }
624                         break;
625
626                 case LMS_TERMINATING:
627                         // Already sent RDP end move to sever
628                         // Allow events to pass.
629                         break;
630         }       
631
632         return false;
633 }
634
635
636 boolean xf_event_process(freerdp* instance, XEvent* event)
637 {
638         boolean status = true;
639         xfInfo* xfi = ((xfContext*) instance->context)->xfi;
640         rdpRail* rail = ((rdpContext*) xfi->context)->rail;
641         rdpWindow* window;
642
643         if (xfi->remote_app)
644         {
645                 window = window_list_get_by_extra_id(
646                         rail->list, (void*) event->xexpose.window);
647                 if (window) 
648                 {
649                         // Update "current" window for cursor change orders
650                         xfi->window = (xfWindow *) window->extra;
651
652                         if (xf_event_suppress_events(xfi, window, event))
653                                 return true;
654                 }
655         }
656
657         if (event->type != MotionNotify)
658                 DEBUG_X11("%s Event: wnd=0x%04X", X11_EVENT_STRINGS[event->type], (uint32) event->xany.window);
659
660         switch (event->type)
661         {
662                 case Expose:
663                         status = xf_event_Expose(xfi, event, xfi->remote_app);
664                         break;
665
666                 case VisibilityNotify:
667                         status = xf_event_VisibilityNotify(xfi, event, xfi->remote_app);
668                         break;
669
670                 case MotionNotify:
671                         status = xf_event_MotionNotify(xfi, event, xfi->remote_app);
672                         break;
673
674                 case ButtonPress:
675                         status = xf_event_ButtonPress(xfi, event, xfi->remote_app);
676                         break;
677
678                 case ButtonRelease:
679                         status = xf_event_ButtonRelease(xfi, event, xfi->remote_app);
680                         break;
681
682                 case KeyPress:
683                         status = xf_event_KeyPress(xfi, event, xfi->remote_app);
684                         break;
685
686                 case KeyRelease:
687                         status = xf_event_KeyRelease(xfi, event, xfi->remote_app);
688                         break;
689
690                 case FocusIn:
691                         status = xf_event_FocusIn(xfi, event, xfi->remote_app);
692                         break;
693
694                 case FocusOut:
695                         status = xf_event_FocusOut(xfi, event, xfi->remote_app);
696                         break;
697
698                 case EnterNotify:
699                         status = xf_event_EnterNotify(xfi, event, xfi->remote_app);
700                         break;
701
702                 case LeaveNotify:
703                         status = xf_event_LeaveNotify(xfi, event, xfi->remote_app);
704                         break;
705
706                 case NoExpose:
707                         break;
708
709                 case GraphicsExpose:
710                         break;
711
712                 case ConfigureNotify:
713                         status = xf_event_ConfigureNotify(xfi, event, xfi->remote_app);
714                         break;
715
716                 case MapNotify:
717                         status = xf_event_MapNotify(xfi, event, xfi->remote_app);
718                         break;
719
720                 case UnmapNotify:
721                         status = xf_event_UnmapNotify(xfi, event, xfi->remote_app);
722                         break;
723
724                 case ReparentNotify:
725                         break;
726
727                 case MappingNotify:
728                         status = xf_event_MappingNotify(xfi, event, xfi->remote_app);
729                         break;
730
731                 case ClientMessage:
732                         status = xf_event_ClientMessage(xfi, event, xfi->remote_app);
733                         break;
734
735                 case SelectionNotify:
736                         status = xf_event_SelectionNotify(xfi, event, xfi->remote_app);
737                         break;
738
739                 case SelectionRequest:
740                         status = xf_event_SelectionRequest(xfi, event, xfi->remote_app);
741                         break;
742
743                 case SelectionClear:
744                         status = xf_event_SelectionClear(xfi, event, xfi->remote_app);
745                         break;
746
747                 case PropertyNotify:
748                         status = xf_event_PropertyNotify(xfi, event, xfi->remote_app);
749                         break;
750         }
751
752         return status;
753 }