FreeRDP/client/X11/xf_event.c

1307 lines
29 KiB
C
Raw Normal View History

2011-08-07 17:52:40 +04:00
/**
2012-10-09 07:02:04 +04:00
* FreeRDP: A Remote Desktop Protocol Implementation
2011-08-07 17:52:40 +04:00
* X11 Event Handling
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2023 HP Development Company, L.P.
2011-08-07 17:52:40 +04:00
*
* 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.
*/
2022-02-16 13:20:38 +03:00
#include <freerdp/config.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <winpr/assert.h>
2014-09-12 19:13:01 +04:00
#include <freerdp/log.h>
#include <freerdp/locale/keyboard.h>
2011-08-07 17:52:40 +04:00
#include "xf_rail.h"
#include "xf_window.h"
2011-09-23 07:37:17 +04:00
#include "xf_cliprdr.h"
#include "xf_disp.h"
#include "xf_input.h"
#include "xf_gfx.h"
2022-07-04 09:52:17 +03:00
#include "xf_graphics.h"
2011-08-07 17:52:40 +04:00
#include "xf_event.h"
2013-04-26 03:52:17 +04:00
#include "xf_input.h"
2011-08-07 17:52:40 +04:00
2014-09-12 19:13:01 +04:00
#define TAG CLIENT_TAG("x11")
2019-11-06 17:24:51 +03:00
#define CLAMP_COORDINATES(x, y) \
if (x < 0) \
x = 0; \
if (y < 0) \
y = 0
2023-02-24 10:57:59 +03:00
const char* x11_event_string(int event)
2011-08-19 19:12:30 +04:00
{
2018-01-24 18:26:27 +03:00
switch (event)
{
case KeyPress:
return "KeyPress";
case KeyRelease:
return "KeyRelease";
case ButtonPress:
return "ButtonPress";
case ButtonRelease:
return "ButtonRelease";
case MotionNotify:
return "MotionNotify";
case EnterNotify:
return "EnterNotify";
case LeaveNotify:
return "LeaveNotify";
case FocusIn:
return "FocusIn";
case FocusOut:
return "FocusOut";
case KeymapNotify:
return "KeymapNotify";
case Expose:
return "Expose";
case GraphicsExpose:
return "GraphicsExpose";
case NoExpose:
return "NoExpose";
case VisibilityNotify:
return "VisibilityNotify";
case CreateNotify:
return "CreateNotify";
case DestroyNotify:
return "DestroyNotify";
case UnmapNotify:
return "UnmapNotify";
case MapNotify:
return "MapNotify";
case MapRequest:
return "MapRequest";
case ReparentNotify:
return "ReparentNotify";
case ConfigureNotify:
return "ConfigureNotify";
case ConfigureRequest:
return "ConfigureRequest";
case GravityNotify:
return "GravityNotify";
case ResizeRequest:
return "ResizeRequest";
case CirculateNotify:
return "CirculateNotify";
case CirculateRequest:
return "CirculateRequest";
case PropertyNotify:
return "PropertyNotify";
case SelectionClear:
return "SelectionClear";
case SelectionRequest:
return "SelectionRequest";
case SelectionNotify:
return "SelectionNotify";
case ColormapNotify:
return "ColormapNotify";
case ClientMessage:
return "ClientMessage";
case MappingNotify:
return "MappingNotify";
case GenericEvent:
return "GenericEvent";
default:
return "UNKNOWN";
2021-06-18 11:00:21 +03:00
}
2018-01-24 18:26:27 +03:00
}
#ifdef WITH_DEBUG_X11
#define DEBUG_X11(...) WLog_DBG(TAG, __VA_ARGS__)
#else
2019-11-06 17:24:51 +03:00
#define DEBUG_X11(...) \
do \
{ \
} while (0)
#endif
2015-05-20 20:19:50 +03:00
BOOL xf_event_action_script_init(xfContext* xfc)
{
wObject* obj;
char* xevent;
FILE* actionScript;
char buffer[1024] = { 0 };
char command[1024] = { 0 };
const rdpSettings* settings;
const char* ActionScript;
WINPR_ASSERT(xfc);
settings = xfc->common.context.settings;
WINPR_ASSERT(settings);
xfc->xevents = ArrayList_New(TRUE);
2016-07-19 17:15:38 +03:00
if (!xfc->xevents)
2015-05-20 20:19:50 +03:00
return FALSE;
obj = ArrayList_Object(xfc->xevents);
obj->fnObjectFree = free;
ActionScript = freerdp_settings_get_string(settings, FreeRDP_ActionScript);
sprintf_s(command, sizeof(command), "%s xevent", ActionScript);
actionScript = popen(command, "r");
if (!actionScript)
2015-05-20 20:19:50 +03:00
return FALSE;
while (fgets(buffer, sizeof(buffer), actionScript))
{
2020-05-18 12:18:55 +03:00
char* context = NULL;
strtok_s(buffer, "\n", &context);
xevent = _strdup(buffer);
2016-07-19 17:15:38 +03:00
if (!xevent || !ArrayList_Append(xfc->xevents, xevent))
{
pclose(actionScript);
ArrayList_Free(xfc->xevents);
xfc->xevents = NULL;
2015-05-20 20:19:50 +03:00
return FALSE;
}
}
pclose(actionScript);
2015-05-20 20:19:50 +03:00
return TRUE;
}
void xf_event_action_script_free(xfContext* xfc)
{
if (xfc->xevents)
{
ArrayList_Free(xfc->xevents);
xfc->xevents = NULL;
}
}
static BOOL xf_event_execute_action_script(xfContext* xfc, const XEvent* event)
{
int index;
int count;
char* name;
FILE* actionScript;
BOOL match = FALSE;
const char* xeventName;
const char* ActionScript;
char buffer[1024] = { 0 };
char command[1024] = { 0 };
if (!xfc->actionScriptExists || !xfc->xevents || !xfc->window)
return FALSE;
2018-01-24 18:26:27 +03:00
if (event->type > LASTEvent)
return FALSE;
2018-01-24 18:26:27 +03:00
xeventName = x11_event_string(event->type);
count = ArrayList_Count(xfc->xevents);
for (index = 0; index < count; index++)
{
2019-11-06 17:24:51 +03:00
name = (char*)ArrayList_GetItem(xfc->xevents, index);
if (_stricmp(name, xeventName) == 0)
{
match = TRUE;
break;
}
}
if (!match)
return FALSE;
ActionScript = freerdp_settings_get_string(xfc->common.context.settings, FreeRDP_ActionScript);
sprintf_s(command, sizeof(command), "%s xevent %s %lu", ActionScript, xeventName,
(unsigned long)xfc->window->handle);
actionScript = popen(command, "r");
if (!actionScript)
return FALSE;
while (fgets(buffer, sizeof(buffer), actionScript))
{
2020-05-18 12:18:55 +03:00
char* context = NULL;
strtok_s(buffer, "\n", &context);
}
pclose(actionScript);
return TRUE;
}
void xf_adjust_coordinates_to_screen(xfContext* xfc, UINT32* x, UINT32* y)
{
rdpSettings* settings;
INT64 tx, ty;
if (!xfc || !xfc->common.context.settings || !y || !x)
return;
settings = xfc->common.context.settings;
tx = *x;
ty = *y;
if (!xfc->remote_app)
{
#ifdef WITH_XRENDER
if (xf_picture_transform_required(xfc))
{
double xScalingFactor = xfc->scaledWidth / (double)settings->DesktopWidth;
double yScalingFactor = xfc->scaledHeight / (double)settings->DesktopHeight;
tx = ((tx + xfc->offset_x) * xScalingFactor);
ty = ((ty + xfc->offset_y) * yScalingFactor);
}
#endif
}
CLAMP_COORDINATES(tx, ty);
*x = tx;
*y = ty;
}
2016-07-19 17:15:38 +03:00
void xf_event_adjust_coordinates(xfContext* xfc, int* x, int* y)
{
if (!xfc || !xfc->common.context.settings || !y || !x)
2016-07-19 17:15:38 +03:00
return;
if (!xfc->remote_app)
{
#ifdef WITH_XRENDER
2022-04-28 11:49:42 +03:00
rdpSettings* settings = xfc->common.context.settings;
if (xf_picture_transform_required(xfc))
{
2016-07-19 17:15:38 +03:00
double xScalingFactor = settings->DesktopWidth / (double)xfc->scaledWidth;
double yScalingFactor = settings->DesktopHeight / (double)xfc->scaledHeight;
*x = (int)((*x - xfc->offset_x) * xScalingFactor);
*y = (int)((*y - xfc->offset_y) * yScalingFactor);
}
2016-07-19 17:15:38 +03:00
#endif
}
2016-07-19 17:15:38 +03:00
CLAMP_COORDINATES(*x, *y);
}
2022-04-28 11:49:42 +03:00
static BOOL xf_event_Expose(xfContext* xfc, const XExposeEvent* event, BOOL app)
{
int x, y;
int w, h;
rdpSettings* settings;
WINPR_ASSERT(xfc);
WINPR_ASSERT(event);
settings = xfc->common.context.settings;
WINPR_ASSERT(settings);
2016-07-19 17:15:38 +03:00
if (!app && (settings->SmartSizing || settings->MultiTouchGestures))
{
x = 0;
y = 0;
2016-07-19 17:15:38 +03:00
w = settings->DesktopWidth;
h = settings->DesktopHeight;
}
else
{
x = event->x;
y = event->y;
w = event->width;
h = event->height;
}
2014-06-05 20:36:01 +04:00
2013-02-10 22:17:08 +04:00
if (!app)
{
if (xfc->common.context.gdi->gfx)
{
xf_OutputExpose(xfc, x, y, w, h);
return TRUE;
}
xf_draw_screen(xfc, x, y, w, h);
}
else
{
xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
if (appWindow)
{
xf_UpdateWindowArea(xfc, appWindow, x, y, w, h);
}
}
return TRUE;
}
static BOOL xf_event_VisibilityNotify(xfContext* xfc, const XVisibilityEvent* event, BOOL app)
{
2019-02-07 19:53:21 +03:00
WINPR_UNUSED(app);
xfc->unobscured = event->state == VisibilityUnobscured;
return TRUE;
}
2019-11-06 17:24:51 +03:00
BOOL xf_generic_MotionNotify(xfContext* xfc, int x, int y, int state, Window window, BOOL app)
{
Window childWindow = None;
WINPR_ASSERT(xfc);
WINPR_ASSERT(xfc->common.context.settings);
rdpInput* input = xfc->common.context.input;
WINPR_ASSERT(input);
if (!xfc->common.context.settings->MouseMotion)
{
if ((state & (Button1Mask | Button2Mask | Button3Mask)) == 0)
return TRUE;
}
if (app)
{
/* make sure window exists */
if (!xf_AppWindowFromX11Window(xfc, window))
return TRUE;
2013-03-29 07:09:28 +04:00
/* Translate to desktop coordinates */
2019-11-06 17:24:51 +03:00
XTranslateCoordinates(xfc->display, window, RootWindowOfScreen(xfc->screen), x, y, &x, &y,
&childWindow);
}
xf_event_adjust_coordinates(xfc, &x, &y);
2022-01-19 15:11:34 +03:00
freerdp_client_send_button_event(&xfc->common, FALSE, PTR_FLAGS_MOVE, x, y);
if (xfc->fullscreen && !app)
{
2019-11-06 17:24:51 +03:00
XSetInputFocus(xfc->display, xfc->window->handle, RevertToPointerRoot, CurrentTime);
}
return TRUE;
2011-08-07 17:52:40 +04:00
}
BOOL xf_generic_RawMotionNotify(xfContext* xfc, int x, int y, Window window, BOOL app)
{
WINPR_ASSERT(xfc);
if (app)
{
WLog_ERR(TAG, "Relative mouse input is not supported with remoate app mode!");
return FALSE;
}
freerdp_client_send_button_event(&xfc->common, TRUE, PTR_FLAGS_MOVE, x, y);
return TRUE;
}
static BOOL xf_event_MotionNotify(xfContext* xfc, const XMotionEvent* event, BOOL app)
{
WINPR_ASSERT(xfc);
if (xfc->xi_event)
return TRUE;
if (xfc->window)
xf_floatbar_set_root_y(xfc->window->floatbar, event->y);
return xf_generic_MotionNotify(xfc, event->x, event->y, event->state, event->window, app);
}
2019-11-06 17:24:51 +03:00
BOOL xf_generic_ButtonEvent(xfContext* xfc, int x, int y, int button, Window window, BOOL app,
BOOL down)
2011-08-07 17:52:40 +04:00
{
UINT16 flags = 0;
Window childWindow = None;
WINPR_ASSERT(xfc);
for (size_t i = 0; i < ARRAYSIZE(xfc->button_map); i++)
{
const button_map* cur = &xfc->button_map[i];
if (cur->button == button)
{
flags = cur->flags;
break;
}
}
if (flags != 0)
{
if (flags & (PTR_FLAGS_WHEEL | PTR_FLAGS_HWHEEL))
{
if (down)
2022-01-19 15:11:34 +03:00
freerdp_client_send_wheel_event(&xfc->common, flags);
}
else
{
BOOL extended = FALSE;
if (flags & (PTR_XFLAGS_BUTTON1 | PTR_XFLAGS_BUTTON2))
{
extended = TRUE;
if (down)
flags |= PTR_XFLAGS_DOWN;
}
else if (flags & (PTR_FLAGS_BUTTON1 | PTR_FLAGS_BUTTON2 | PTR_FLAGS_BUTTON3))
{
if (down)
flags |= PTR_FLAGS_DOWN;
}
if (app)
{
/* make sure window exists */
if (!xf_AppWindowFromX11Window(xfc, window))
return TRUE;
/* Translate to desktop coordinates */
2019-11-06 17:24:51 +03:00
XTranslateCoordinates(xfc->display, window, RootWindowOfScreen(xfc->screen), x, y,
&x, &y, &childWindow);
}
xf_event_adjust_coordinates(xfc, &x, &y);
if (extended)
2022-01-19 15:11:34 +03:00
freerdp_client_send_extended_button_event(&xfc->common, FALSE, flags, x, y);
else
2022-01-19 15:11:34 +03:00
freerdp_client_send_button_event(&xfc->common, FALSE, flags, x, y);
}
}
return TRUE;
2011-08-07 17:52:40 +04:00
}
2022-01-19 15:27:36 +03:00
static BOOL xf_grab_mouse(xfContext* xfc)
{
WINPR_ASSERT(xfc);
if (!xfc->window)
return FALSE;
if (freerdp_settings_get_bool(xfc->common.context.settings, FreeRDP_GrabMouse))
{
XGrabPointer(xfc->display, xfc->window->handle, False,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask |
EnterWindowMask | LeaveWindowMask,
GrabModeAsync, GrabModeAsync, xfc->window->handle, None, CurrentTime);
xfc->common.mouse_grabbed = TRUE;
2022-01-19 15:27:36 +03:00
}
return TRUE;
}
static BOOL xf_grab_kbd(xfContext* xfc)
{
WINPR_ASSERT(xfc);
if (!xfc->window)
return FALSE;
XGrabKeyboard(xfc->display, xfc->window->handle, TRUE, GrabModeAsync, GrabModeAsync,
CurrentTime);
return TRUE;
}
static BOOL xf_event_ButtonPress(xfContext* xfc, const XButtonEvent* event, BOOL app)
{
2022-01-19 15:27:36 +03:00
xf_grab_mouse(xfc);
if (xfc->xi_event)
return TRUE;
return xf_generic_ButtonEvent(xfc, event->x, event->y, event->button, event->window, app, TRUE);
}
static BOOL xf_event_ButtonRelease(xfContext* xfc, const XButtonEvent* event, BOOL app)
{
2022-01-19 15:27:36 +03:00
xf_grab_mouse(xfc);
if (xfc->xi_event)
return TRUE;
return xf_generic_ButtonEvent(xfc, event->x, event->y, event->button, event->window, app,
FALSE);
}
static BOOL xf_event_KeyPress(xfContext* xfc, const XKeyEvent* event, BOOL app)
{
KeySym keysym = 0;
2022-04-27 22:02:18 +03:00
char str[256] = { 0 };
union
{
const XKeyEvent* cev;
XKeyEvent* ev;
} cnv;
cnv.cev = event;
2019-02-07 19:53:21 +03:00
WINPR_UNUSED(app);
2022-04-27 22:02:18 +03:00
XLookupString(cnv.ev, str, sizeof(str), &keysym, NULL);
xf_keyboard_key_press(xfc, event, keysym);
return TRUE;
}
static BOOL xf_event_KeyRelease(xfContext* xfc, const XKeyEvent* event, BOOL app)
{
KeySym keysym = 0;
2022-04-27 22:02:18 +03:00
char str[256] = { 0 };
union
{
const XKeyEvent* cev;
XKeyEvent* ev;
} cnv;
cnv.cev = event;
2019-02-07 19:53:21 +03:00
WINPR_UNUSED(app);
2022-04-27 22:02:18 +03:00
XLookupString(cnv.ev, str, sizeof(str), &keysym, NULL);
xf_keyboard_key_release(xfc, event, keysym);
return TRUE;
}
/* Release a key, but ignore the event in case of autorepeat.
*/
static BOOL xf_event_KeyReleaseOrIgnore(xfContext* xfc, const XKeyEvent* event, BOOL app)
{
WINPR_ASSERT(xfc);
WINPR_ASSERT(event);
if ((event->type == KeyRelease) && XEventsQueued(xfc->display, QueuedAfterReading))
{
XEvent nev = { 0 };
XPeekEvent(xfc->display, &nev);
if ((nev.type == KeyPress) && (nev.xkey.time == event->time) &&
(nev.xkey.keycode == event->keycode))
{
/* Key wasnt actually released */
return TRUE;
}
}
return xf_event_KeyRelease(xfc, event, app);
}
static BOOL xf_event_FocusIn(xfContext* xfc, const XFocusInEvent* event, BOOL app)
{
if (event->mode == NotifyGrab)
return TRUE;
xfc->focused = TRUE;
if (xfc->mouse_active && !app)
{
2022-01-19 15:27:36 +03:00
xf_grab_mouse(xfc);
if (!xf_grab_kbd(xfc))
return FALSE;
}
/* Release all keys, should already be done at FocusOut but might be missed
* if the WM decided to use an alternate event order */
if (!app)
xf_keyboard_release_all_keypress(xfc);
2022-07-04 09:52:17 +03:00
xf_pointer_update_scale(xfc);
if (app)
Multiple RAIL fixes/improvements 1. Linked Window Manager Maximize/Minimize and Restore operations to those from the Server Rail Window so that they are in sync 2. Enable things like "CTRL-ALT-DELETE" and "WindowsKey-L" to show the full desktop window again since the desktop is not actively monitored since this was still trying to draw to the rail window without updating the size of the window to accomodate the full workspace area. 3. Changed local window coordinates to be based on the visibileOffsetX/Y- while moving server window based on WindowOffsetX/Y. I have seen various issues regarding this when trying to use a maximized window where this is a disconnect between local window coordinates and remote window coordinates. This change clears these things up. 4. Commented the XShapeCombineRectangles calls - this can cause issues where the entire window is not visible and it does not currently play well with the changes from #3. The gain here is greater than the loss. 5. Draw the initial workspace correctly when running across multiple monitors. The correct size was always used, but the window was only starting on the current monitor and thus could draw the window off of the viewable area. Known Issues: Although the changes for #2 worked well in the stable branch that I developed from - the desktop window shown once the rail windows are destroyed does not respond to input unless I minimize/restore the window. Once the window starts responding to input - you can hit cancel to close the desktop window and return to your rail windows again(or launch task manager, etc.). This is still a big step in the right direction as xfreerdp is now correctly acting when the rail server stops Actively Monitoring the desktop. XShapeCombineRectangles needs to be revisited, most windows applications will give you a rectangular window anyways.
2012-08-04 02:35:17 +04:00
{
xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
2019-11-06 17:24:51 +03:00
/* Update the server with any window changes that occurred while the window was not focused.
*/
if (appWindow)
xf_rail_adjust_position(xfc, appWindow);
Multiple RAIL fixes/improvements 1. Linked Window Manager Maximize/Minimize and Restore operations to those from the Server Rail Window so that they are in sync 2. Enable things like "CTRL-ALT-DELETE" and "WindowsKey-L" to show the full desktop window again since the desktop is not actively monitored since this was still trying to draw to the rail window without updating the size of the window to accomodate the full workspace area. 3. Changed local window coordinates to be based on the visibileOffsetX/Y- while moving server window based on WindowOffsetX/Y. I have seen various issues regarding this when trying to use a maximized window where this is a disconnect between local window coordinates and remote window coordinates. This change clears these things up. 4. Commented the XShapeCombineRectangles calls - this can cause issues where the entire window is not visible and it does not currently play well with the changes from #3. The gain here is greater than the loss. 5. Draw the initial workspace correctly when running across multiple monitors. The correct size was always used, but the window was only starting on the current monitor and thus could draw the window off of the viewable area. Known Issues: Although the changes for #2 worked well in the stable branch that I developed from - the desktop window shown once the rail windows are destroyed does not respond to input unless I minimize/restore the window. Once the window starts responding to input - you can hit cancel to close the desktop window and return to your rail windows again(or launch task manager, etc.). This is still a big step in the right direction as xfreerdp is now correctly acting when the rail server stops Actively Monitoring the desktop. XShapeCombineRectangles needs to be revisited, most windows applications will give you a rectangular window anyways.
2012-08-04 02:35:17 +04:00
}
2014-03-14 05:10:22 +04:00
xf_keyboard_focus_in(xfc);
return TRUE;
}
static BOOL xf_event_FocusOut(xfContext* xfc, const XFocusOutEvent* event, BOOL app)
{
if (event->mode == NotifyUngrab)
return TRUE;
xfc->focused = FALSE;
if (event->mode == NotifyWhileGrabbed)
XUngrabKeyboard(xfc->display, CurrentTime);
xf_keyboard_release_all_keypress(xfc);
return TRUE;
}
static BOOL xf_event_MappingNotify(xfContext* xfc, const XMappingEvent* event, BOOL app)
{
2019-02-07 19:53:21 +03:00
WINPR_UNUSED(app);
if (event->request == MappingModifier)
return xf_keyboard_update_modifier_map(xfc);
return TRUE;
}
static BOOL xf_event_ClientMessage(xfContext* xfc, const XClientMessageEvent* event, BOOL app)
{
if ((event->message_type == xfc->WM_PROTOCOLS) &&
((Atom)event->data.l[0] == xfc->WM_DELETE_WINDOW))
{
if (app)
{
xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
if (appWindow)
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_CLOSE);
return TRUE;
}
else
{
2012-03-23 00:30:15 +04:00
DEBUG_X11("Main window closed");
return FALSE;
}
}
return TRUE;
2011-08-07 17:52:40 +04:00
}
static BOOL xf_event_EnterNotify(xfContext* xfc, const XEnterWindowEvent* event, BOOL app)
2011-08-07 17:52:40 +04:00
{
2013-02-10 22:17:08 +04:00
if (!app)
{
if (!xfc->window)
return FALSE;
xfc->mouse_active = TRUE;
if (xfc->fullscreen)
2019-11-06 17:24:51 +03:00
XSetInputFocus(xfc->display, xfc->window->handle, RevertToPointerRoot, CurrentTime);
if (xfc->focused)
2022-01-19 15:27:36 +03:00
xf_grab_kbd(xfc);
}
else
{
xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
/* keep track of which window has focus so that we can apply pointer updates */
xfc->appWindow = appWindow;
}
return TRUE;
2011-08-07 17:52:40 +04:00
}
static BOOL xf_event_LeaveNotify(xfContext* xfc, const XLeaveWindowEvent* event, BOOL app)
{
if (event->mode == NotifyGrab || event->mode == NotifyUngrab)
return TRUE;
2013-02-10 22:17:08 +04:00
if (!app)
2011-08-19 19:12:30 +04:00
{
xfc->mouse_active = FALSE;
XUngrabKeyboard(xfc->display, CurrentTime);
2011-08-19 19:12:30 +04:00
}
else
{
xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
/* keep track of which window has focus so that we can apply pointer updates */
if (xfc->appWindow == appWindow)
xfc->appWindow = NULL;
}
return TRUE;
}
static BOOL xf_event_ConfigureNotify(xfContext* xfc, const XConfigureEvent* event, BOOL app)
{
Window childWindow = None;
xfAppWindow* appWindow = NULL;
WINPR_ASSERT(xfc);
WINPR_ASSERT(event);
const rdpSettings* settings = xfc->common.context.settings;
WINPR_ASSERT(settings);
WLog_DBG(TAG, "x=%" PRId32 ", y=%" PRId32 ", w=%" PRId32 ", h=%" PRId32, event->x, event->y,
event->width, event->height);
2022-07-04 13:13:20 +03:00
if (!app)
{
if (!xfc->window)
return FALSE;
if (xfc->window->left != event->x)
xfc->window->left = event->x;
if (xfc->window->top != event->y)
xfc->window->top = event->y;
if (xfc->window->width != event->width || xfc->window->height != event->height)
{
xfc->window->width = event->width;
xfc->window->height = event->height;
#ifdef WITH_XRENDER
xfc->offset_x = 0;
xfc->offset_y = 0;
2016-07-19 17:15:38 +03:00
if (settings->SmartSizing || settings->MultiTouchGestures)
{
xfc->scaledWidth = xfc->window->width;
xfc->scaledHeight = xfc->window->height;
2016-07-19 17:15:38 +03:00
xf_draw_screen(xfc, 0, 0, settings->DesktopWidth, settings->DesktopHeight);
}
else
{
2016-07-19 17:15:38 +03:00
xfc->scaledWidth = settings->DesktopWidth;
xfc->scaledHeight = settings->DesktopHeight;
}
2016-07-19 17:15:38 +03:00
#endif
}
2016-07-19 17:15:38 +03:00
if (settings->DynamicResolutionUpdate)
{
int alignedWidth, alignedHeight;
alignedWidth = (xfc->window->width / 2) * 2;
alignedHeight = (xfc->window->height / 2) * 2;
/* ask the server to resize using the display channel */
xf_disp_handle_configureNotify(xfc, alignedWidth, alignedHeight);
}
}
2022-07-04 09:52:17 +03:00
else
{
2022-07-04 09:52:17 +03:00
appWindow = xf_AppWindowFromX11Window(xfc, event->window);
if (appWindow)
{
2022-07-04 09:52:17 +03:00
/*
* ConfigureNotify coordinates are expressed relative to the window parent.
* Translate these to root window coordinates.
*/
XTranslateCoordinates(xfc->display, appWindow->handle, RootWindowOfScreen(xfc->screen),
0, 0, &appWindow->x, &appWindow->y, &childWindow);
appWindow->width = event->width;
appWindow->height = event->height;
xf_AppWindowResize(xfc, appWindow);
2022-07-04 09:52:17 +03:00
/*
* Additional checks for not in a local move and not ignoring configure to send
* position update to server, also should the window not be focused then do not
* send to server yet (i.e. resizing using window decoration).
* The server will be updated when the window gets refocused.
*/
if (appWindow->decorations)
{
/* moving resizing using window decoration */
xf_rail_adjust_position(xfc, appWindow);
2022-07-04 09:52:17 +03:00
}
else
{
if ((!event->send_event || appWindow->local_move.state == LMS_NOT_ACTIVE) &&
!appWindow->rail_ignore_configure && xfc->focused)
xf_rail_adjust_position(xfc, appWindow);
}
}
}
2022-07-04 09:52:17 +03:00
return xf_pointer_update_scale(xfc);
}
static BOOL xf_event_MapNotify(xfContext* xfc, const XMapEvent* event, BOOL app)
{
WINPR_ASSERT(xfc);
2013-02-10 22:17:08 +04:00
if (!app)
gdi_send_suppress_output(xfc->common.context.gdi, FALSE);
2012-05-28 16:21:56 +04:00
else
{
xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
2012-05-28 16:21:56 +04:00
if (appWindow)
2012-05-28 16:21:56 +04:00
{
/* local restore event */
/* This is now handled as part of the PropertyNotify
* Doing this here would inhibit the ability to restore a maximized window
* that is minimized back to the maximized state
*/
// xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE);
appWindow->is_mapped = TRUE;
2012-05-28 16:21:56 +04:00
}
}
return TRUE;
}
static BOOL xf_event_UnmapNotify(xfContext* xfc, const XUnmapEvent* event, BOOL app)
{
WINPR_ASSERT(xfc);
WINPR_ASSERT(event);
if (!app)
xf_keyboard_release_all_keypress(xfc);
2013-02-10 22:17:08 +04:00
if (!app)
gdi_send_suppress_output(xfc->common.context.gdi, TRUE);
2012-05-28 16:21:56 +04:00
else
{
xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->window);
2012-05-28 16:21:56 +04:00
if (appWindow)
appWindow->is_mapped = FALSE;
}
return TRUE;
}
static BOOL xf_event_PropertyNotify(xfContext* xfc, const XPropertyEvent* event, BOOL app)
2011-09-23 07:37:17 +04:00
{
WINPR_ASSERT(xfc);
WINPR_ASSERT(event);
/*
* This section handles sending the appropriate commands to the rail server
* when the window has been minimized, maximized, restored locally
* ie. not using the buttons on the rail window itself
*/
if ((((Atom)event->atom == xfc->_NET_WM_STATE) && (event->state != PropertyDelete)) ||
(((Atom)event->atom == xfc->WM_STATE) && (event->state != PropertyDelete)))
Multiple RAIL fixes/improvements 1. Linked Window Manager Maximize/Minimize and Restore operations to those from the Server Rail Window so that they are in sync 2. Enable things like "CTRL-ALT-DELETE" and "WindowsKey-L" to show the full desktop window again since the desktop is not actively monitored since this was still trying to draw to the rail window without updating the size of the window to accomodate the full workspace area. 3. Changed local window coordinates to be based on the visibileOffsetX/Y- while moving server window based on WindowOffsetX/Y. I have seen various issues regarding this when trying to use a maximized window where this is a disconnect between local window coordinates and remote window coordinates. This change clears these things up. 4. Commented the XShapeCombineRectangles calls - this can cause issues where the entire window is not visible and it does not currently play well with the changes from #3. The gain here is greater than the loss. 5. Draw the initial workspace correctly when running across multiple monitors. The correct size was always used, but the window was only starting on the current monitor and thus could draw the window off of the viewable area. Known Issues: Although the changes for #2 worked well in the stable branch that I developed from - the desktop window shown once the rail windows are destroyed does not respond to input unless I minimize/restore the window. Once the window starts responding to input - you can hit cancel to close the desktop window and return to your rail windows again(or launch task manager, etc.). This is still a big step in the right direction as xfreerdp is now correctly acting when the rail server stops Actively Monitoring the desktop. XShapeCombineRectangles needs to be revisited, most windows applications will give you a rectangular window anyways.
2012-08-04 02:35:17 +04:00
{
unsigned long i = 0;
BOOL status = FALSE;
2018-01-24 18:26:27 +03:00
BOOL minimized = FALSE;
BOOL minimizedChanged = FALSE;
unsigned long nitems = 0;
unsigned long bytes = 0;
unsigned char* prop = NULL;
xfAppWindow* appWindow = NULL;
if (app)
{
appWindow = xf_AppWindowFromX11Window(xfc, event->window);
if (!appWindow)
return TRUE;
}
2018-01-24 18:26:27 +03:00
if ((Atom)event->atom == xfc->_NET_WM_STATE)
2016-07-19 17:15:38 +03:00
{
status = xf_GetWindowProperty(xfc, event->window, xfc->_NET_WM_STATE, 12, &nitems,
&bytes, &prop);
Multiple RAIL fixes/improvements 1. Linked Window Manager Maximize/Minimize and Restore operations to those from the Server Rail Window so that they are in sync 2. Enable things like "CTRL-ALT-DELETE" and "WindowsKey-L" to show the full desktop window again since the desktop is not actively monitored since this was still trying to draw to the rail window without updating the size of the window to accomodate the full workspace area. 3. Changed local window coordinates to be based on the visibileOffsetX/Y- while moving server window based on WindowOffsetX/Y. I have seen various issues regarding this when trying to use a maximized window where this is a disconnect between local window coordinates and remote window coordinates. This change clears these things up. 4. Commented the XShapeCombineRectangles calls - this can cause issues where the entire window is not visible and it does not currently play well with the changes from #3. The gain here is greater than the loss. 5. Draw the initial workspace correctly when running across multiple monitors. The correct size was always used, but the window was only starting on the current monitor and thus could draw the window off of the viewable area. Known Issues: Although the changes for #2 worked well in the stable branch that I developed from - the desktop window shown once the rail windows are destroyed does not respond to input unless I minimize/restore the window. Once the window starts responding to input - you can hit cancel to close the desktop window and return to your rail windows again(or launch task manager, etc.). This is still a big step in the right direction as xfreerdp is now correctly acting when the rail server stops Actively Monitoring the desktop. XShapeCombineRectangles needs to be revisited, most windows applications will give you a rectangular window anyways.
2012-08-04 02:35:17 +04:00
2018-01-24 18:26:27 +03:00
if (status)
{
2022-04-27 22:44:40 +03:00
if (appWindow)
{
appWindow->maxVert = FALSE;
appWindow->maxHorz = FALSE;
}
2018-01-24 18:26:27 +03:00
for (i = 0; i < nitems; i++)
2013-03-29 07:09:28 +04:00
{
2019-11-06 17:24:51 +03:00
if ((Atom)((UINT16**)prop)[i] ==
XInternAtom(xfc->display, "_NET_WM_STATE_MAXIMIZED_VERT", False))
2013-03-29 07:09:28 +04:00
{
2022-04-27 22:44:40 +03:00
if (appWindow)
appWindow->maxVert = TRUE;
2013-03-29 07:09:28 +04:00
}
2019-11-06 17:24:51 +03:00
if ((Atom)((UINT16**)prop)[i] ==
XInternAtom(xfc->display, "_NET_WM_STATE_MAXIMIZED_HORZ", False))
2018-01-24 18:26:27 +03:00
{
2022-04-27 22:44:40 +03:00
if (appWindow)
appWindow->maxHorz = TRUE;
2018-01-24 18:26:27 +03:00
}
2013-03-29 07:09:28 +04:00
}
2018-01-24 18:26:27 +03:00
XFree(prop);
2016-07-19 17:15:38 +03:00
}
2018-01-24 18:26:27 +03:00
}
if ((Atom)event->atom == xfc->WM_STATE)
2018-01-24 18:26:27 +03:00
{
status =
xf_GetWindowProperty(xfc, event->window, xfc->WM_STATE, 1, &nitems, &bytes, &prop);
2018-01-24 18:26:27 +03:00
if (status)
2016-07-19 17:15:38 +03:00
{
2018-01-24 18:26:27 +03:00
/* If the window is in the iconic state */
2019-11-06 17:24:51 +03:00
if (((UINT32)*prop == 3))
{
2018-01-24 18:26:27 +03:00
minimized = TRUE;
2022-04-27 22:44:40 +03:00
if (appWindow)
appWindow->minimized = TRUE;
}
2018-01-24 18:26:27 +03:00
else
{
2018-01-24 18:26:27 +03:00
minimized = FALSE;
2022-04-27 22:44:40 +03:00
if (appWindow)
appWindow->minimized = FALSE;
}
2018-01-24 18:26:27 +03:00
minimizedChanged = TRUE;
XFree(prop);
}
}
2013-03-29 07:09:28 +04:00
2018-01-24 18:26:27 +03:00
if (app)
{
2022-04-27 22:44:40 +03:00
WINPR_ASSERT(appWindow);
if (appWindow->maxVert && appWindow->maxHorz && !appWindow->minimized)
2016-07-19 17:15:38 +03:00
{
2022-06-23 08:57:38 +03:00
if (appWindow->rail_state != WINDOW_SHOW_MAXIMIZED)
{
2022-06-23 08:57:38 +03:00
appWindow->rail_state = WINDOW_SHOW_MAXIMIZED;
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MAXIMIZE);
}
2016-07-19 17:15:38 +03:00
}
else if (appWindow->minimized)
2016-07-19 17:15:38 +03:00
{
2022-06-23 08:57:38 +03:00
if (appWindow->rail_state != WINDOW_SHOW_MINIMIZED)
{
appWindow->rail_state = WINDOW_SHOW_MINIMIZED;
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_MINIMIZE);
}
2016-07-19 17:15:38 +03:00
}
else
2016-07-19 17:15:38 +03:00
{
2022-06-23 08:57:38 +03:00
if (appWindow->rail_state != WINDOW_SHOW && appWindow->rail_state != WINDOW_HIDE)
{
appWindow->rail_state = WINDOW_SHOW;
xf_rail_send_client_system_command(xfc, appWindow->windowId, SC_RESTORE);
}
2016-07-19 17:15:38 +03:00
}
}
2018-01-24 18:26:27 +03:00
else if (minimizedChanged)
gdi_send_suppress_output(xfc->common.context.gdi, minimized);
2016-07-19 17:15:38 +03:00
}
2011-09-23 07:37:17 +04:00
return TRUE;
2011-09-23 07:37:17 +04:00
}
static BOOL xf_event_suppress_events(xfContext* xfc, xfAppWindow* appWindow, const XEvent* event)
{
if (!xfc->remote_app)
return FALSE;
switch (appWindow->local_move.state)
{
case LMS_NOT_ACTIVE:
2016-07-19 17:15:38 +03:00
/* No local move in progress, nothing to do */
2019-11-06 17:24:51 +03:00
/* Prevent Configure from happening during indeterminant state of Horz or Vert Max only
*/
2016-07-19 17:15:38 +03:00
if ((event->type == ConfigureNotify) && appWindow->rail_ignore_configure)
{
appWindow->rail_ignore_configure = FALSE;
return TRUE;
}
Multiple RAIL fixes/improvements 1. Linked Window Manager Maximize/Minimize and Restore operations to those from the Server Rail Window so that they are in sync 2. Enable things like "CTRL-ALT-DELETE" and "WindowsKey-L" to show the full desktop window again since the desktop is not actively monitored since this was still trying to draw to the rail window without updating the size of the window to accomodate the full workspace area. 3. Changed local window coordinates to be based on the visibileOffsetX/Y- while moving server window based on WindowOffsetX/Y. I have seen various issues regarding this when trying to use a maximized window where this is a disconnect between local window coordinates and remote window coordinates. This change clears these things up. 4. Commented the XShapeCombineRectangles calls - this can cause issues where the entire window is not visible and it does not currently play well with the changes from #3. The gain here is greater than the loss. 5. Draw the initial workspace correctly when running across multiple monitors. The correct size was always used, but the window was only starting on the current monitor and thus could draw the window off of the viewable area. Known Issues: Although the changes for #2 worked well in the stable branch that I developed from - the desktop window shown once the rail windows are destroyed does not respond to input unless I minimize/restore the window. Once the window starts responding to input - you can hit cancel to close the desktop window and return to your rail windows again(or launch task manager, etc.). This is still a big step in the right direction as xfreerdp is now correctly acting when the rail server stops Actively Monitoring the desktop. XShapeCombineRectangles needs to be revisited, most windows applications will give you a rectangular window anyways.
2012-08-04 02:35:17 +04:00
break;
case LMS_STARTING:
2016-07-19 17:15:38 +03:00
2019-11-06 17:24:51 +03:00
/* Local move initiated by RDP server, but we have not yet seen any updates from the X
* server */
switch (event->type)
{
case ConfigureNotify:
2019-11-06 17:24:51 +03:00
/* Starting to see move events from the X server. Local move is now in progress.
*/
appWindow->local_move.state = LMS_ACTIVE;
2019-11-06 17:24:51 +03:00
/* Allow these events to be processed during move to keep our state up to date.
*/
break;
2016-07-19 17:15:38 +03:00
case ButtonPress:
case ButtonRelease:
case KeyPress:
case KeyRelease:
case UnmapNotify:
2016-07-19 17:15:38 +03:00
/*
* A button release event means the X window server did not grab the
* mouse before the user released it. In this case we must cancel the
* local move. The event will be processed below as normal, below.
*/
break;
case VisibilityNotify:
case PropertyNotify:
case Expose:
/* Allow these events to pass */
break;
2016-07-19 17:15:38 +03:00
default:
/* Eat any other events */
return TRUE;
}
2016-07-19 17:15:38 +03:00
break;
case LMS_ACTIVE:
2016-07-19 17:15:38 +03:00
/* Local move is in progress */
switch (event->type)
{
case ConfigureNotify:
case VisibilityNotify:
case PropertyNotify:
case Expose:
Multiple RAIL fixes/improvements 1. Linked Window Manager Maximize/Minimize and Restore operations to those from the Server Rail Window so that they are in sync 2. Enable things like "CTRL-ALT-DELETE" and "WindowsKey-L" to show the full desktop window again since the desktop is not actively monitored since this was still trying to draw to the rail window without updating the size of the window to accomodate the full workspace area. 3. Changed local window coordinates to be based on the visibileOffsetX/Y- while moving server window based on WindowOffsetX/Y. I have seen various issues regarding this when trying to use a maximized window where this is a disconnect between local window coordinates and remote window coordinates. This change clears these things up. 4. Commented the XShapeCombineRectangles calls - this can cause issues where the entire window is not visible and it does not currently play well with the changes from #3. The gain here is greater than the loss. 5. Draw the initial workspace correctly when running across multiple monitors. The correct size was always used, but the window was only starting on the current monitor and thus could draw the window off of the viewable area. Known Issues: Although the changes for #2 worked well in the stable branch that I developed from - the desktop window shown once the rail windows are destroyed does not respond to input unless I minimize/restore the window. Once the window starts responding to input - you can hit cancel to close the desktop window and return to your rail windows again(or launch task manager, etc.). This is still a big step in the right direction as xfreerdp is now correctly acting when the rail server stops Actively Monitoring the desktop. XShapeCombineRectangles needs to be revisited, most windows applications will give you a rectangular window anyways.
2012-08-04 02:35:17 +04:00
case GravityNotify:
/* Keep us up to date on position */
break;
2016-07-19 17:15:38 +03:00
default:
/* Any other event terminates move */
xf_rail_end_local_move(xfc, appWindow);
break;
}
2016-07-19 17:15:38 +03:00
break;
case LMS_TERMINATING:
/* Already sent RDP end move to server. Allow events to pass. */
break;
}
return FALSE;
}
BOOL xf_event_process(freerdp* instance, const XEvent* event)
{
BOOL status = TRUE;
WINPR_ASSERT(instance);
WINPR_ASSERT(event);
xfContext* xfc = (xfContext*)instance->context;
WINPR_ASSERT(xfc);
rdpSettings* settings = xfc->common.context.settings;
WINPR_ASSERT(settings);
if (xfc->remote_app)
{
xfAppWindow* appWindow = xf_AppWindowFromX11Window(xfc, event->xany.window);
if (appWindow)
{
/* Update "current" window for cursor change orders */
xfc->appWindow = appWindow;
if (xf_event_suppress_events(xfc, appWindow, event))
return TRUE;
}
}
if (xfc->window)
2018-09-18 22:25:51 +03:00
{
xfFloatbar* floatbar = xfc->window->floatbar;
if (xf_floatbar_check_event(floatbar, event))
{
xf_floatbar_event_process(floatbar, event);
return TRUE;
}
if (xf_floatbar_is_locked(floatbar))
return TRUE;
2018-09-18 22:25:51 +03:00
}
xf_event_execute_action_script(xfc, event);
2011-08-19 19:12:30 +04:00
if (event->type != MotionNotify)
2018-01-24 18:26:27 +03:00
{
2019-11-06 17:24:51 +03:00
DEBUG_X11("%s Event(%d): wnd=0x%08lX", x11_event_string(event->type), event->type,
(unsigned long)event->xany.window);
2018-01-24 18:26:27 +03:00
}
2011-08-19 19:12:30 +04:00
switch (event->type)
{
case Expose:
status = xf_event_Expose(xfc, &event->xexpose, xfc->remote_app);
break;
case VisibilityNotify:
status = xf_event_VisibilityNotify(xfc, &event->xvisibility, xfc->remote_app);
break;
case MotionNotify:
status = xf_event_MotionNotify(xfc, &event->xmotion, xfc->remote_app);
break;
case ButtonPress:
status = xf_event_ButtonPress(xfc, &event->xbutton, xfc->remote_app);
break;
case ButtonRelease:
status = xf_event_ButtonRelease(xfc, &event->xbutton, xfc->remote_app);
break;
case KeyPress:
status = xf_event_KeyPress(xfc, &event->xkey, xfc->remote_app);
break;
case KeyRelease:
status = xf_event_KeyReleaseOrIgnore(xfc, &event->xkey, xfc->remote_app);
break;
case FocusIn:
status = xf_event_FocusIn(xfc, &event->xfocus, xfc->remote_app);
break;
case FocusOut:
status = xf_event_FocusOut(xfc, &event->xfocus, xfc->remote_app);
break;
case EnterNotify:
status = xf_event_EnterNotify(xfc, &event->xcrossing, xfc->remote_app);
break;
case LeaveNotify:
status = xf_event_LeaveNotify(xfc, &event->xcrossing, xfc->remote_app);
break;
case NoExpose:
break;
case GraphicsExpose:
break;
case ConfigureNotify:
status = xf_event_ConfigureNotify(xfc, &event->xconfigure, xfc->remote_app);
break;
case MapNotify:
status = xf_event_MapNotify(xfc, &event->xmap, xfc->remote_app);
break;
case UnmapNotify:
status = xf_event_UnmapNotify(xfc, &event->xunmap, xfc->remote_app);
break;
case ReparentNotify:
break;
case MappingNotify:
status = xf_event_MappingNotify(xfc, &event->xmapping, xfc->remote_app);
break;
case ClientMessage:
status = xf_event_ClientMessage(xfc, &event->xclient, xfc->remote_app);
break;
2011-09-23 07:37:17 +04:00
case PropertyNotify:
status = xf_event_PropertyNotify(xfc, &event->xproperty, xfc->remote_app);
2011-09-23 07:37:17 +04:00
break;
default:
if (settings->SupportDisplayControl)
xf_disp_handle_xevent(xfc, event);
2017-12-21 13:04:50 +03:00
break;
}
xfWindow* window = xfc->window;
xfFloatbar* floatbar = NULL;
if (window)
floatbar = window->floatbar;
xf_cliprdr_handle_xevent(xfc, event);
if (!xf_floatbar_check_event(floatbar, event) && !xf_floatbar_is_locked(floatbar))
xf_input_handle_event(xfc, event);
XSync(xfc->display, FALSE);
return status;
}
BOOL xf_generic_RawButtonEvent(xfContext* xfc, int button, BOOL app, BOOL down)
{
UINT16 flags = 0;
size_t i;
if (app)
return FALSE;
for (i = 0; i < ARRAYSIZE(xfc->button_map); i++)
{
const button_map* cur = &xfc->button_map[i];
if (cur->button == button)
{
flags = cur->flags;
break;
}
}
if (flags != 0)
{
if (flags & (PTR_FLAGS_WHEEL | PTR_FLAGS_HWHEEL))
{
if (down)
freerdp_client_send_wheel_event(&xfc->common, flags);
}
else
{
BOOL extended = FALSE;
if (flags & (PTR_XFLAGS_BUTTON1 | PTR_XFLAGS_BUTTON2))
{
extended = TRUE;
if (down)
flags |= PTR_XFLAGS_DOWN;
}
else if (flags & (PTR_FLAGS_BUTTON1 | PTR_FLAGS_BUTTON2 | PTR_FLAGS_BUTTON3))
{
if (down)
flags |= PTR_FLAGS_DOWN;
}
if (extended)
freerdp_client_send_extended_button_event(&xfc->common, TRUE, flags, 0, 0);
else
freerdp_client_send_button_event(&xfc->common, TRUE, flags, 0, 0);
}
}
return TRUE;
}