xfreerdp: xfixes selection ownership notification

The X11 core protocol does not have support for selection ownership
notifications. Until now xfreerdp worked around this issue by always sending
a format list pdu to the server after sending the format data response pdu
which makes the server side think that the clients clipboard data has changed.

This workaround has some severe drawbacks:
* it causes unnecessary data transfers because even without local clipboard
  data changes the same data is always re-transferred over the channel
* with some clipboard managers (in the server sessions) you will get massive
  endless data transfer loops because these managers immediately request the
  data on clipboard changes.

The correct (core X11) way would be polling for selection ownership changes
which must include the ability to detect changes to the TIMESTAMP target if
the selection owner did not change.
The alternative to the poll based approach is using the X Fixes extension in
order to get selection ownership notifications.

This commit adds support for the XFIXES solution and also moves the complete
clipboard related event handling from xf_event.c to xf_cliprdr.c
This commit is contained in:
Norbert Federa 2014-07-07 20:26:41 +02:00
parent 190cd55e45
commit 196330111b
4 changed files with 110 additions and 66 deletions

View File

@ -148,6 +148,10 @@ set(XRENDER_FEATURE_TYPE "RECOMMENDED")
set(XRENDER_FEATURE_PURPOSE "rendering")
set(XRENDER_FEATURE_DESCRIPTION "X11 render extension")
set(XFIXES_FEATURE_TYPE "RECOMMENDED")
set(XFIXES_FEATURE_PURPOSE "X11 xfixes extension")
set(XFIXES_FEATURE_DESCRIPTION "Useful additions to the X11 core protocol")
find_feature(XShm ${XSHM_FEATURE_TYPE} ${XSHM_FEATURE_PURPOSE} ${XSHM_FEATURE_DESCRIPTION})
find_feature(Xinerama ${XINERAMA_FEATURE_TYPE} ${XINERAMA_FEATURE_PURPOSE} ${XINERAMA_FEATURE_DESCRIPTION})
find_feature(Xext ${XEXT_FEATURE_TYPE} ${XEXT_FEATURE_PURPOSE} ${XEXT_FEATURE_DESCRIPTION})
@ -155,6 +159,7 @@ find_feature(Xcursor ${XCURSOR_FEATURE_TYPE} ${XCURSOR_FEATURE_PURPOSE} ${XCURSO
find_feature(Xv ${XV_FEATURE_TYPE} ${XV_FEATURE_PURPOSE} ${XV_FEATURE_DESCRIPTION})
find_feature(Xi ${XI_FEATURE_TYPE} ${XI_FEATURE_PURPOSE} ${XI_FEATURE_DESCRIPTION})
find_feature(Xrender ${XRENDER_FEATURE_TYPE} ${XRENDER_FEATURE_PURPOSE} ${XRENDER_FEATURE_DESCRIPTION})
find_feature(Xfixes ${XFIXES_FEATURE_TYPE} ${XFIXES_FEATURE_PURPOSE} ${XFIXES_FEATURE_DESCRIPTION})
if(WITH_XINERAMA)
add_definitions(-DWITH_XINERAMA)
@ -192,6 +197,12 @@ if(WITH_XRENDER)
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${XRENDER_LIBRARIES})
endif()
if(WITH_XFIXES)
add_definitions(-DWITH_XFIXES)
include_directories(${XFIXES_INCLUDE_DIRS})
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${XFIXES_LIBRARIES})
endif()
include_directories(${CMAKE_SOURCE_DIR}/resources)
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-client)

View File

@ -25,6 +25,10 @@
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#ifdef WITH_XFIXES
#include <X11/extensions/Xfixes.h>
#endif
#include <winpr/crt.h>
#include <winpr/stream.h>
@ -88,6 +92,11 @@ struct clipboard_context
BOOL incr_starts;
BYTE* incr_data;
int incr_data_length;
/* X Fixes extension */
int xfixes_event_base;
int xfixes_error_base;
BOOL xfixes_supported;
};
void xf_cliprdr_init(xfContext* xfc, rdpChannels* channels)
@ -121,6 +130,30 @@ void xf_cliprdr_init(xfContext* xfc, rdpChannels* channels)
XSelectInput(xfc->display, cb->root_window, PropertyChangeMask);
#ifdef WITH_XFIXES
if (XFixesQueryExtension(xfc->display, &cb->xfixes_event_base, &cb->xfixes_error_base))
{
int xfmajor, xfminor;
if (XFixesQueryVersion(xfc->display, &xfmajor, &xfminor))
{
DEBUG_X11_CLIPRDR("Found X Fixes extension version %d.%d", xfmajor, xfminor);
XFixesSelectSelectionInput(xfc->display, cb->root_window,
cb->clipboard_atom, XFixesSetSelectionOwnerNotifyMask);
cb->xfixes_supported = TRUE;
}
else
{
fprintf(stderr, "%s: Error querying X Fixes extension version\n", __FUNCTION__);
}
}
else
{
fprintf(stderr, "%s: Error loading X Fixes extension\n", __FUNCTION__);
}
#else
fprintf(stderr, "Warning: Using clipboard redirection without XFIXES extension is strongly discouraged!\n");
#endif
n = 0;
cb->format_mappings[n].target_format = XInternAtom(xfc->display, "_FREERDP_RAW", FALSE);
cb->format_mappings[n].format_id = CB_FORMAT_RAW;
@ -718,8 +751,11 @@ static void xf_cliprdr_process_requested_data(xfContext* xfc, BOOL has_data, BYT
else
xf_cliprdr_send_null_data_response(xfc);
/* Resend the format list, otherwise the server won't request again for the next paste */
xf_cliprdr_send_format_list(xfc);
if (!cb->xfixes_supported)
{
/* Resend the format list, otherwise the server won't request again for the next paste */
xf_cliprdr_send_format_list(xfc);
}
}
static BOOL xf_cliprdr_get_requested_data(xfContext* xfc, Atom target)
@ -1077,7 +1113,7 @@ void xf_process_cliprdr_event(xfContext* xfc, wMessage* event)
}
}
BOOL xf_cliprdr_process_selection_notify(xfContext* xfc, XEvent* xevent)
static BOOL xf_cliprdr_process_selection_notify(xfContext* xfc, XEvent* xevent)
{
clipboardContext* cb = (clipboardContext*) xfc->clipboard_context;
@ -1101,7 +1137,7 @@ BOOL xf_cliprdr_process_selection_notify(xfContext* xfc, XEvent* xevent)
}
}
BOOL xf_cliprdr_process_selection_request(xfContext* xfc, XEvent* xevent)
static BOOL xf_cliprdr_process_selection_request(xfContext* xfc, XEvent* xevent)
{
int i;
int fmt;
@ -1218,7 +1254,7 @@ BOOL xf_cliprdr_process_selection_request(xfContext* xfc, XEvent* xevent)
return TRUE;
}
BOOL xf_cliprdr_process_selection_clear(xfContext* xfc, XEvent* xevent)
static BOOL xf_cliprdr_process_selection_clear(xfContext* xfc, XEvent* xevent)
{
clipboardContext* cb = (clipboardContext*) xfc->clipboard_context;
@ -1230,7 +1266,7 @@ BOOL xf_cliprdr_process_selection_clear(xfContext* xfc, XEvent* xevent)
return TRUE;
}
BOOL xf_cliprdr_process_property_notify(xfContext* xfc, XEvent* xevent)
static BOOL xf_cliprdr_process_property_notify(xfContext* xfc, XEvent* xevent)
{
clipboardContext* cb = (clipboardContext*) xfc->clipboard_context;
@ -1257,7 +1293,7 @@ BOOL xf_cliprdr_process_property_notify(xfContext* xfc, XEvent* xevent)
return TRUE;
}
void xf_cliprdr_check_owner(xfContext* xfc)
static void xf_cliprdr_check_owner(xfContext* xfc)
{
Window owner;
clipboardContext* cb = (clipboardContext*) xfc->clipboard_context;
@ -1274,3 +1310,53 @@ void xf_cliprdr_check_owner(xfContext* xfc)
}
}
void xf_cliprdr_handle_xevent(xfContext* xfc, XEvent* event)
{
clipboardContext* cb;
if (!xfc || !event)
return;
if (!(cb = (clipboardContext*) xfc->clipboard_context))
return;
#ifdef WITH_XFIXES
if (cb->xfixes_supported && event->type == XFixesSelectionNotify + cb->xfixes_event_base)
{
XFixesSelectionNotifyEvent* se = (XFixesSelectionNotifyEvent*) event;
if (se->subtype == XFixesSetSelectionOwnerNotify)
{
if (se->selection != cb->clipboard_atom)
return;
if (XGetSelectionOwner(xfc->display, se->selection) == xfc->drawable)
return;
cb->owner = None;
xf_cliprdr_check_owner(xfc);
}
return;
}
#endif
switch (event->type)
{
case SelectionNotify:
xf_cliprdr_process_selection_notify(xfc, event);
break;
case SelectionRequest:
xf_cliprdr_process_selection_request(xfc, event);
break;
case SelectionClear:
xf_cliprdr_process_selection_clear(xfc, event);
break;
case PropertyNotify:
xf_cliprdr_process_property_notify(xfc, event);
break;
case FocusIn:
if (!cb->xfixes_supported)
{
xf_cliprdr_check_owner(xfc);
}
break;
}
}

View File

@ -26,10 +26,5 @@
void xf_cliprdr_init(xfContext* xfc, rdpChannels* channels);
void xf_cliprdr_uninit(xfContext* xfc);
void xf_process_cliprdr_event(xfContext* xfc, wMessage* event);
BOOL xf_cliprdr_process_selection_notify(xfContext* xfc, XEvent* xevent);
BOOL xf_cliprdr_process_selection_request(xfContext* xfc, XEvent* xevent);
BOOL xf_cliprdr_process_selection_clear(xfContext* xfc, XEvent* xevent);
BOOL xf_cliprdr_process_property_notify(xfContext* xfc, XEvent* xevent);
void xf_cliprdr_check_owner(xfContext* xfc);
void xf_cliprdr_handle_xevent(xfContext* xfc, XEvent* event);
#endif /* __XF_CLIPRDR_H */

View File

@ -545,9 +545,6 @@ static BOOL xf_event_FocusIn(xfContext* xfc, XEvent* event, BOOL app)
xf_keyboard_focus_in(xfc);
if (!app)
xf_cliprdr_check_owner(xfc);
return TRUE;
}
@ -793,39 +790,6 @@ static BOOL xf_event_UnmapNotify(xfContext* xfc, XEvent* event, BOOL app)
return TRUE;
}
static BOOL xf_event_SelectionNotify(xfContext* xfc, XEvent* event, BOOL app)
{
if (!app)
{
if (xf_cliprdr_process_selection_notify(xfc, event))
return TRUE;
}
return TRUE;
}
static BOOL xf_event_SelectionRequest(xfContext* xfc, XEvent* event, BOOL app)
{
if (!app)
{
if (xf_cliprdr_process_selection_request(xfc, event))
return TRUE;
}
return TRUE;
}
static BOOL xf_event_SelectionClear(xfContext* xfc, XEvent* event, BOOL app)
{
if (!app)
{
if (xf_cliprdr_process_selection_clear(xfc, event))
return TRUE;
}
return TRUE;
}
static BOOL xf_event_PropertyNotify(xfContext* xfc, XEvent* event, BOOL app)
{
/*
@ -922,11 +886,6 @@ static BOOL xf_event_PropertyNotify(xfContext* xfc, XEvent* event, BOOL app)
}
}
}
else
{
if (xf_cliprdr_process_property_notify(xfc, event))
return TRUE;
}
return TRUE;
}
@ -1112,24 +1071,17 @@ BOOL xf_event_process(freerdp* instance, XEvent* event)
status = xf_event_ClientMessage(xfc, event, xfc->remote_app);
break;
case SelectionNotify:
status = xf_event_SelectionNotify(xfc, event, xfc->remote_app);
break;
case SelectionRequest:
status = xf_event_SelectionRequest(xfc, event, xfc->remote_app);
break;
case SelectionClear:
status = xf_event_SelectionClear(xfc, event, xfc->remote_app);
break;
case PropertyNotify:
status = xf_event_PropertyNotify(xfc, event, xfc->remote_app);
break;
}
if (!xfc->remote_app)
{
xf_cliprdr_handle_xevent(xfc, event);
}
xf_input_handle_event(xfc, event);
XSync(xfc->display, FALSE);