/** * FreeRDP: A Remote Desktop Protocol Client * X11 Event Handling * * Copyright 2011 Marc-Andre Moreau * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "xf_rail.h" #include "xf_cliprdr.h" #include "xf_event.h" uint8 X11_EVENT_STRINGS[][20] = { "", "", "KeyPress", "KeyRelease", "ButtonPress", "ButtonRelease", "MotionNotify", "EnterNotify", "LeaveNotify", "FocusIn", "FocusOut", "KeymapNotify", "Expose", "GraphicsExpose", "NoExpose", "VisibilityNotify", "CreateNotify", "DestroyNotify", "UnmapNotify", "MapNotify", "MapRequest", "ReparentNotify", "ConfigureNotify", "ConfigureRequest", "GravityNotify", "ResizeRequest", "CirculateNotify", "CirculateRequest", "PropertyNotify", "SelectionClear", "SelectionRequest", "SelectionNotify", "ColormapNotify", "ClientMessage", "MappingNotify", "GenericEvent", }; void xf_send_mouse_motion_event(rdpInput* input, boolean down, uint32 button, uint16 x, uint16 y) { input->MouseEvent(input, PTR_FLAGS_MOVE, x, y); } boolean xf_event_Expose(xfInfo* xfi, XEvent* event, boolean app) { int x, y; int w, h; x = event->xexpose.x; y = event->xexpose.y; w = event->xexpose.width; h = event->xexpose.height; if (app != True) { XCopyArea(xfi->display, xfi->primary, xfi->window->handle, xfi->gc, x, y, w, h, x, y); } else { xfWindow* xfw; rdpWindow* window; rdpRail* rail = ((rdpContext*) xfi->context)->rail; window = window_list_get_by_extra_id(rail->list, (void*) event->xexpose.window); if (window != NULL) { xfw = (xfWindow*) window->extra; xf_UpdateWindowArea(xfi, xfw, x, y, w, h); } } return True; } boolean xf_event_VisibilityNotify(xfInfo* xfi, XEvent* event, boolean app) { int x, y; int w, h; x = event->xexpose.x; y = event->xexpose.y; w = event->xexpose.width; h = event->xexpose.height; xfi->unobscured = event->xvisibility.state == VisibilityUnobscured; if (app) { xfWindow* xfw; rdpWindow* window; rdpRail* rail = ((rdpContext*) xfi->context)->rail; window = window_list_get_by_extra_id(rail->list, (void*) event->xvisibility.window); if (window != NULL) { xfw = (xfWindow*) window->extra; xf_UpdateWindowArea(xfi, xfw, x, y, w, h); } } return True; } boolean xf_event_MotionNotify(xfInfo* xfi, XEvent* event, boolean app) { rdpInput* input; input = xfi->instance->input; if (app != True) { if (xfi->mouse_motion != True) { if ((event->xmotion.state & (Button1Mask | Button2Mask | Button3Mask)) == 0) return True; } input->MouseEvent(input, PTR_FLAGS_MOVE, event->xmotion.x, event->xmotion.y); if (xfi->fullscreen) XSetInputFocus(xfi->display, xfi->window->handle, RevertToPointerRoot, CurrentTime); } else if (xfi->mouse_motion == True) { rdpWindow* window; int x = event->xmotion.x; int y = event->xmotion.y; rdpRail* rail = ((rdpContext*) xfi->context)->rail; window = window_list_get_by_extra_id(rail->list, (void*) event->xmotion.window); if (window != NULL) { x += window->windowOffsetX; y += window->windowOffsetY; input->MouseEvent(input, PTR_FLAGS_MOVE, x, y); } } return True; } boolean xf_event_ButtonPress(xfInfo* xfi, XEvent* event, boolean app) { uint16 x, y; uint16 flags; boolean wheel; rdpInput* input; input = xfi->instance->input; x = 0; y = 0; flags = 0; wheel = False; switch (event->xbutton.button) { case 1: x = event->xbutton.x; y = event->xbutton.y; flags = PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON1; break; case 2: x = event->xbutton.x; y = event->xbutton.y; flags = PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON3; break; case 3: x = event->xbutton.x; y = event->xbutton.y; flags = PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON2; break; case 4: wheel = True; flags = PTR_FLAGS_WHEEL | 0x0078; break; case 5: wheel = True; flags = PTR_FLAGS_WHEEL | PTR_FLAGS_WHEEL_NEGATIVE | 0x0088; break; default: x = 0; y = 0; flags = 0; break; } if (flags != 0) { if (wheel) { input->MouseEvent(input, flags, 0, 0); } else { if (app) { rdpWindow* window; rdpRail* rail = ((rdpContext*) xfi->context)->rail; window = window_list_get_by_extra_id(rail->list, (void*) event->xbutton.window); if (window != NULL) { x += window->windowOffsetX; y += window->windowOffsetY; } } input->MouseEvent(input, flags, x, y); } } return True; } boolean xf_event_ButtonRelease(xfInfo* xfi, XEvent* event, boolean app) { uint16 x, y; uint16 flags; rdpInput* input; input = xfi->instance->input; x = 0; y = 0; flags = 0; switch (event->xbutton.button) { case 1: x = event->xbutton.x; y = event->xbutton.y; flags = PTR_FLAGS_BUTTON1; break; case 2: x = event->xbutton.x; y = event->xbutton.y; flags = PTR_FLAGS_BUTTON3; break; case 3: x = event->xbutton.x; y = event->xbutton.y; flags = PTR_FLAGS_BUTTON2; break; default: flags = 0; break; } if (flags != 0) { if (app) { rdpWindow* window; rdpRail* rail = ((rdpContext*) xfi->context)->rail; window = window_list_get_by_extra_id(rail->list, (void*) event->xbutton.window); if (window != NULL) { x += window->windowOffsetX; y += window->windowOffsetY; } } input->MouseEvent(input, flags, x, y); } return True; } boolean xf_event_KeyPress(xfInfo* xfi, XEvent* event, boolean app) { KeySym keysym; char str[256]; XLookupString((XKeyEvent*) event, str, sizeof(str), &keysym, NULL); xf_kbd_set_keypress(xfi, event->xkey.keycode, keysym); if (xfi->fullscreen_toggle && xf_kbd_handle_special_keys(xfi, keysym)) return True; xf_kbd_send_key(xfi, True, event->xkey.keycode); return True; } boolean xf_event_KeyRelease(xfInfo* xfi, XEvent* event, boolean app) { XEvent next_event; if (XPending(xfi->display)) { memset(&next_event, 0, sizeof(next_event)); XPeekEvent(xfi->display, &next_event); if (next_event.type == KeyPress) { if (next_event.xkey.keycode == event->xkey.keycode) return True; } } xf_kbd_unset_keypress(xfi, event->xkey.keycode); xf_kbd_send_key(xfi, False, event->xkey.keycode); return True; } boolean xf_event_FocusIn(xfInfo* xfi, XEvent* event, boolean app) { if (event->xfocus.mode == NotifyGrab) return True; xfi->focused = True; if (xfi->mouse_active && (app != True)) XGrabKeyboard(xfi->display, xfi->window->handle, True, GrabModeAsync, GrabModeAsync, CurrentTime); xf_rail_send_activate(xfi, event->xany.window, True); xf_kbd_focus_in(xfi); if (xfi->remote_app != True) xf_cliprdr_check_owner(xfi); return True; } boolean xf_event_FocusOut(xfInfo* xfi, XEvent* event, boolean app) { if (event->xfocus.mode == NotifyUngrab) return True; xfi->focused = False; if (event->xfocus.mode == NotifyWhileGrabbed) XUngrabKeyboard(xfi->display, CurrentTime); xf_rail_send_activate(xfi, event->xany.window, False); return True; } boolean xf_event_MappingNotify(xfInfo* xfi, XEvent* event, boolean app) { if (event->xmapping.request == MappingModifier) { XFreeModifiermap(xfi->modifier_map); xfi->modifier_map = XGetModifierMapping(xfi->display); } return True; } boolean xf_event_ClientMessage(xfInfo* xfi, XEvent* event, boolean app) { if ((event->xclient.message_type == xfi->WM_PROTOCOLS) && ((Atom) event->xclient.data.l[0] == xfi->WM_DELETE_WINDOW)) { if (app) { rdpWindow* window; rdpRail* rail = ((rdpContext*) xfi->context)->rail; window = window_list_get_by_extra_id(rail->list, (void*) event->xclient.window); if (window != NULL) { xf_rail_send_client_system_command(xfi, window->windowId, SC_CLOSE); } return True; } else { return False; } } return True; } boolean xf_event_EnterNotify(xfInfo* xfi, XEvent* event, boolean app) { if (app != True) { xfi->mouse_active = True; if (xfi->fullscreen) XSetInputFocus(xfi->display, xfi->window->handle, RevertToPointerRoot, CurrentTime); if (xfi->focused) XGrabKeyboard(xfi->display, xfi->window->handle, True, GrabModeAsync, GrabModeAsync, CurrentTime); } return True; } boolean xf_event_LeaveNotify(xfInfo* xfi, XEvent* event, boolean app) { if (app != True) { xfi->mouse_active = False; XUngrabKeyboard(xfi->display, CurrentTime); } return True; } boolean xf_event_ConfigureNotify(xfInfo* xfi, XEvent* event, boolean app) { rdpWindow* window; rdpRail* rail = ((rdpContext*) xfi->context)->rail; window = window_list_get_by_extra_id(rail->list, (void*) event->xconfigure.window); if (window != NULL) { xfWindow* xfw; uint32 left, top; uint32 right, bottom; uint32 width, height; xfw = (xfWindow*) window->extra; left = event->xconfigure.x; top = event->xconfigure.y; width = event->xconfigure.width; height = event->xconfigure.height; right = left + width - 1; bottom = top + height - 1; 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} " "w=%d h=%d override_redirect=%d", event->xconfigure.send_event, (uint32) event->xconfigure.event, (uint32) event->xconfigure.window, (uint32) event->xconfigure.above, left, top, right, bottom, width, height, event->xconfigure.override_redirect); } return True; } boolean xf_event_MapNotify(xfInfo* xfi, XEvent* event, boolean app) { rdpWindow* window; rdpRail* rail = ((rdpContext*) xfi->context)->rail; if (app != True) return True; window = window_list_get_by_extra_id(rail->list, (void*) event->xany.window); if (window != NULL) { /* local restore event */ xf_rail_send_client_system_command(xfi, window->windowId, SC_RESTORE); } return True; } boolean xf_event_SelectionNotify(xfInfo* xfi, XEvent* event, boolean app) { if (xfi->remote_app != True) { if (xf_cliprdr_process_selection_notify(xfi, event)) return True; } return True; } boolean xf_event_SelectionRequest(xfInfo* xfi, XEvent* event, boolean app) { if (xfi->remote_app != True) { if (xf_cliprdr_process_selection_request(xfi, event)) return True; } return True; } boolean xf_event_SelectionClear(xfInfo* xfi, XEvent* event, boolean app) { if (xfi->remote_app != True) { if (xf_cliprdr_process_selection_clear(xfi, event)) return True; } return True; } boolean xf_event_PropertyNotify(xfInfo* xfi, XEvent* event, boolean app) { if (xfi->remote_app != True) { if (xf_cliprdr_process_property_notify(xfi, event)) return True; } return True; } boolean xf_event_process(freerdp* instance, XEvent* event) { boolean app = False; boolean status = True; xfInfo* xfi = ((xfContext*) instance->context)->xfi; if (xfi->remote_app == True) { app = True; } else { if (event->xany.window != xfi->window->handle) app = True; } if (event->type != MotionNotify) DEBUG_X11("%s Event: wnd=0x%04X", X11_EVENT_STRINGS[event->type], (uint32) event->xany.window); switch (event->type) { case Expose: status = xf_event_Expose(xfi, event, app); break; case VisibilityNotify: status = xf_event_VisibilityNotify(xfi, event, app); break; case MotionNotify: status = xf_event_MotionNotify(xfi, event, app); break; case ButtonPress: status = xf_event_ButtonPress(xfi, event, app); break; case ButtonRelease: status = xf_event_ButtonRelease(xfi, event, app); break; case KeyPress: status = xf_event_KeyPress(xfi, event, app); break; case KeyRelease: status = xf_event_KeyRelease(xfi, event, app); break; case FocusIn: status = xf_event_FocusIn(xfi, event, app); break; case FocusOut: status = xf_event_FocusOut(xfi, event, app); break; case EnterNotify: status = xf_event_EnterNotify(xfi, event, app); break; case LeaveNotify: status = xf_event_LeaveNotify(xfi, event, app); break; case NoExpose: break; case GraphicsExpose: break; case ConfigureNotify: status = xf_event_ConfigureNotify(xfi, event, app); break; case MapNotify: status = xf_event_MapNotify(xfi, event, app); break; case ReparentNotify: break; case MappingNotify: status = xf_event_MappingNotify(xfi, event, app); break; case ClientMessage: status = xf_event_ClientMessage(xfi, event, app); break; case SelectionNotify: status = xf_event_SelectionNotify(xfi, event, app); break; case SelectionRequest: status = xf_event_SelectionRequest(xfi, event, app); break; case SelectionClear: status = xf_event_SelectionClear(xfi, event, app); break; case PropertyNotify: status = xf_event_PropertyNotify(xfi, event, app); break; default: DEBUG_X11("xf_event_process unknown event %d", event->type); break; } return status; }