Initial commit - from Precise source
[freerdp-ubuntu-pcb-backport.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         if (app)
342                 xf_rail_send_activate(xfi, event->xany.window, true);
343
344         xf_kbd_focus_in(xfi);
345
346         if (app != true)
347                 xf_cliprdr_check_owner(xfi);
348
349         return true;
350 }
351
352 boolean xf_event_FocusOut(xfInfo* xfi, XEvent* event, boolean app)
353 {
354         if (event->xfocus.mode == NotifyUngrab)
355                 return true;
356
357         xfi->focused = false;
358
359         if (event->xfocus.mode == NotifyWhileGrabbed)
360                 XUngrabKeyboard(xfi->display, CurrentTime);
361
362         if (app)
363                 xf_rail_send_activate(xfi, event->xany.window, false);
364
365         return true;
366 }
367
368 boolean xf_event_MappingNotify(xfInfo* xfi, XEvent* event, boolean app)
369 {
370         if (event->xmapping.request == MappingModifier)
371         {
372                 XFreeModifiermap(xfi->modifier_map);
373                 xfi->modifier_map = XGetModifierMapping(xfi->display);
374         }
375
376         return true;
377 }
378
379 boolean xf_event_ClientMessage(xfInfo* xfi, XEvent* event, boolean app)
380 {
381         if ((event->xclient.message_type == xfi->WM_PROTOCOLS)
382             && ((Atom) event->xclient.data.l[0] == xfi->WM_DELETE_WINDOW))
383         {
384                 if (app)
385                 {
386                         rdpWindow* window;
387                         rdpRail* rail = ((rdpContext*) xfi->context)->rail;
388
389                         window = window_list_get_by_extra_id(rail->list, (void*) event->xclient.window);
390
391                         if (window != NULL)
392                         {
393                                 xf_rail_send_client_system_command(xfi, window->windowId, SC_CLOSE);
394                         }
395
396                         return true;
397                 }
398                 else
399                 {
400                         return false;
401                 }
402         }
403
404         return true;
405 }
406
407 boolean xf_event_EnterNotify(xfInfo* xfi, XEvent* event, boolean app)
408 {
409         if (app != true)
410         {
411                 xfi->mouse_active = true;
412
413                 if (xfi->fullscreen)
414                         XSetInputFocus(xfi->display, xfi->window->handle, RevertToPointerRoot, CurrentTime);
415
416                 if (xfi->focused)
417                         XGrabKeyboard(xfi->display, xfi->window->handle, true, GrabModeAsync, GrabModeAsync, CurrentTime);
418         }
419         else
420         {
421                 /* keep track of which window has focus so that we can apply pointer updates */
422
423                 xfWindow* xfw;
424                 rdpWindow* window;
425                 rdpRail* rail = ((rdpContext*) xfi->context)->rail;
426                 window = window_list_get_by_extra_id(rail->list, (void*) event->xexpose.window);
427
428                 if (window != NULL)
429                 {
430                         xfw = (xfWindow*) window->extra;
431                         xfi->window = xfw;
432                 }
433         }
434
435         return true;
436 }
437
438 boolean xf_event_LeaveNotify(xfInfo* xfi, XEvent* event, boolean app)
439 {
440         if (app != true)
441         {
442                 xfi->mouse_active = false;
443                 XUngrabKeyboard(xfi->display, CurrentTime);
444         }
445
446         return true;
447 }
448
449 boolean xf_event_ConfigureNotify(xfInfo* xfi, XEvent* event, boolean app)
450 {
451         rdpWindow* window;
452         rdpRail* rail = ((rdpContext*) xfi->context)->rail;
453
454         window = window_list_get_by_extra_id(rail->list, (void*) event->xconfigure.window);
455
456         if (window != NULL)
457         {
458                 xfWindow* xfw;
459                 Window childWindow;
460                 xfw = (xfWindow*) window->extra;
461
462                 /*
463                  * ConfigureNotify coordinates are expressed relative to the window parent.
464                  * Translate these to root window coordinates.
465                  */
466
467                 XTranslateCoordinates(xfi->display, xfw->handle, 
468                         RootWindowOfScreen(xfi->screen),
469                         0, 0, &xfw->left, &xfw->top, &childWindow);
470
471                 xfw->width = event->xconfigure.width;
472                 xfw->height = event->xconfigure.height;
473                 xfw->right = xfw->left + xfw->width - 1;
474                 xfw->bottom = xfw->top + xfw->height - 1;
475
476                 DEBUG_X11_LMS("window=0x%X rc={l=%d t=%d r=%d b=%d} w=%u h=%u send_event=%d",
477                         (uint32) xfw->handle, xfw->left, xfw->top, xfw->right, xfw->bottom,
478                         xfw->width, xfw->height, event->xconfigure.send_event);
479
480                 if (app && ! event->xconfigure.send_event)
481                         xf_rail_adjust_position(xfi, window);
482         }
483
484         return True;
485 }
486
487 boolean xf_event_MapNotify(xfInfo* xfi, XEvent* event, boolean app)
488 {
489         rdpWindow* window;
490         rdpRail* rail = ((rdpContext*) xfi->context)->rail;
491
492         if (app != true)
493                 return true;
494
495         window = window_list_get_by_extra_id(rail->list, (void*) event->xany.window);
496
497         if (window != NULL)
498         {
499                 /* local restore event */
500                 xf_rail_send_client_system_command(xfi, window->windowId, SC_RESTORE);
501                 xfWindow *xfw = (xfWindow*) window->extra;
502                 xfw->is_mapped = true;
503         }
504
505         return true;
506 }
507
508 boolean xf_event_UnmapNotify(xfInfo* xfi, XEvent* event, boolean app)
509 {
510         rdpWindow* window;
511         rdpRail* rail = ((rdpContext*) xfi->context)->rail;
512
513         if (app != true)
514                 return true;
515
516         window = window_list_get_by_extra_id(rail->list, (void*) event->xany.window);
517
518         if (window != NULL)
519         {
520                 xfWindow *xfw = (xfWindow*) window->extra;
521                 xfw->is_mapped = false;
522         }
523
524         return true;
525 }
526
527 boolean xf_event_SelectionNotify(xfInfo* xfi, XEvent* event, boolean app)
528 {
529         if (app != true)
530         {
531                 if (xf_cliprdr_process_selection_notify(xfi, event))
532                         return true;
533         }
534
535         return true;
536 }
537
538 boolean xf_event_SelectionRequest(xfInfo* xfi, XEvent* event, boolean app)
539 {
540         if (app != true)
541         {
542                 if (xf_cliprdr_process_selection_request(xfi, event))
543                         return true;
544         }
545
546         return true;
547 }
548
549 boolean xf_event_SelectionClear(xfInfo* xfi, XEvent* event, boolean app)
550 {
551         if (app != true)
552         {
553                 if (xf_cliprdr_process_selection_clear(xfi, event))
554                         return true;
555         }
556
557         return true;
558 }
559
560 boolean xf_event_PropertyNotify(xfInfo* xfi, XEvent* event, boolean app)
561 {
562         if (app != true)
563         {
564                 if (xf_cliprdr_process_property_notify(xfi, event))
565                         return true;
566         }
567
568         return true;
569 }
570
571 boolean xf_event_suppress_events(xfInfo *xfi, rdpWindow *window, XEvent*event)
572 {
573         if (! xfi->remote_app)
574                 return false;
575
576         switch (xfi->window->local_move.state)
577         {
578                 case LMS_NOT_ACTIVE:
579                         // No local move in progress, nothing to do
580                         break;
581                 case LMS_STARTING:
582                         // Local move initiated by RDP server, but we
583                         // have not yet seen any updates from the X server
584                         switch(event->type)
585                         {
586                                 case ConfigureNotify:
587                                         // Starting to see move events 
588                                         // from the X server. Local 
589                                         // move is now in progress.
590                                         xfi->window->local_move.state = LMS_ACTIVE;
591
592                                         // Allow these events to be processed during move to keep
593                                         // our state up to date.
594                                         break;
595                                 case ButtonPress:
596                                 case ButtonRelease:
597                                 case KeyPress:
598                                 case KeyRelease:
599                                 case UnmapNotify:
600                                         // A button release event means the X 
601                                         // window server did not grab the
602                                         // mouse before the user released it.  
603                                         // In this case we must cancel the 
604                                         // local move. The event will be 
605                                         // processed below as normal, below.
606                                         break;
607                                 case VisibilityNotify:
608                                 case PropertyNotify:
609                                 case Expose:
610                                         // Allow these events to pass
611                                         break;
612                                 default:
613                                         // Eat any other events 
614                                         return true;
615                         }
616                         break;
617
618                 case LMS_ACTIVE:
619                         // Local move is in progress
620                         switch(event->type)
621                         {
622                                 case ConfigureNotify:
623                                 case VisibilityNotify:
624                                 case PropertyNotify:
625                                 case Expose:
626                                         // Keep us up to date on position
627                                         break;
628                                 default:
629                                         // Any other event terminates move
630                                         xf_rail_end_local_move(xfi, window);
631                                         break;
632                         }
633                         break;
634
635                 case LMS_TERMINATING:
636                         // Already sent RDP end move to sever
637                         // Allow events to pass.
638                         break;
639         }       
640
641         return false;
642 }
643
644
645 boolean xf_event_process(freerdp* instance, XEvent* event)
646 {
647         boolean status = true;
648         xfInfo* xfi = ((xfContext*) instance->context)->xfi;
649         rdpRail* rail = ((rdpContext*) xfi->context)->rail;
650         rdpWindow* window;
651
652         if (xfi->remote_app)
653         {
654                 window = window_list_get_by_extra_id(
655                         rail->list, (void*) event->xexpose.window);
656                 if (window) 
657                 {
658                         // Update "current" window for cursor change orders
659                         xfi->window = (xfWindow *) window->extra;
660
661                         if (xf_event_suppress_events(xfi, window, event))
662                                 return true;
663                 }
664         }
665
666         if (event->type != MotionNotify)
667                 DEBUG_X11("%s Event: wnd=0x%04X", X11_EVENT_STRINGS[event->type], (uint32) event->xany.window);
668
669         switch (event->type)
670         {
671                 case Expose:
672                         status = xf_event_Expose(xfi, event, xfi->remote_app);
673                         break;
674
675                 case VisibilityNotify:
676                         status = xf_event_VisibilityNotify(xfi, event, xfi->remote_app);
677                         break;
678
679                 case MotionNotify:
680                         status = xf_event_MotionNotify(xfi, event, xfi->remote_app);
681                         break;
682
683                 case ButtonPress:
684                         status = xf_event_ButtonPress(xfi, event, xfi->remote_app);
685                         break;
686
687                 case ButtonRelease:
688                         status = xf_event_ButtonRelease(xfi, event, xfi->remote_app);
689                         break;
690
691                 case KeyPress:
692                         status = xf_event_KeyPress(xfi, event, xfi->remote_app);
693                         break;
694
695                 case KeyRelease:
696                         status = xf_event_KeyRelease(xfi, event, xfi->remote_app);
697                         break;
698
699                 case FocusIn:
700                         status = xf_event_FocusIn(xfi, event, xfi->remote_app);
701                         break;
702
703                 case FocusOut:
704                         status = xf_event_FocusOut(xfi, event, xfi->remote_app);
705                         break;
706
707                 case EnterNotify:
708                         status = xf_event_EnterNotify(xfi, event, xfi->remote_app);
709                         break;
710
711                 case LeaveNotify:
712                         status = xf_event_LeaveNotify(xfi, event, xfi->remote_app);
713                         break;
714
715                 case NoExpose:
716                         break;
717
718                 case GraphicsExpose:
719                         break;
720
721                 case ConfigureNotify:
722                         status = xf_event_ConfigureNotify(xfi, event, xfi->remote_app);
723                         break;
724
725                 case MapNotify:
726                         status = xf_event_MapNotify(xfi, event, xfi->remote_app);
727                         break;
728
729                 case UnmapNotify:
730                         status = xf_event_UnmapNotify(xfi, event, xfi->remote_app);
731                         break;
732
733                 case ReparentNotify:
734                         break;
735
736                 case MappingNotify:
737                         status = xf_event_MappingNotify(xfi, event, xfi->remote_app);
738                         break;
739
740                 case ClientMessage:
741                         status = xf_event_ClientMessage(xfi, event, xfi->remote_app);
742                         break;
743
744                 case SelectionNotify:
745                         status = xf_event_SelectionNotify(xfi, event, xfi->remote_app);
746                         break;
747
748                 case SelectionRequest:
749                         status = xf_event_SelectionRequest(xfi, event, xfi->remote_app);
750                         break;
751
752                 case SelectionClear:
753                         status = xf_event_SelectionClear(xfi, event, xfi->remote_app);
754                         break;
755
756                 case PropertyNotify:
757                         status = xf_event_PropertyNotify(xfi, event, xfi->remote_app);
758                         break;
759         }
760
761         return status;
762 }