x11: Introduce GdkX11PendingSelectionNotify

This object tracks the SelectionNotifyEvent that has to be sent in
response to a SelectionRequest.

Currently it just looks like code reshuffling, but it's a prerequisite
for handling MULTIPLE, which requires to only send the notify after
every stream has writtten at least once.

But anyway, code is cleaner now, so it's a win!
This commit is contained in:
Benjamin Otte 2017-12-01 02:53:47 +01:00
parent bcc0d4b5f0
commit ea18793965
3 changed files with 175 additions and 105 deletions

View File

@ -92,8 +92,6 @@ gdk_x11_clipboard_default_output_done (GObject *clipboard,
static void
gdk_x11_clipboard_default_output_handler (GdkX11Clipboard *cb,
const char *target,
const char *encoding,
int format,
GOutputStream *stream)
{
gdk_clipboard_write_async (GDK_CLIPBOARD (cb),
@ -232,7 +230,7 @@ handle_text_list (GdkX11Clipboard *cb,
g_object_unref (converter);
g_object_unref (stream);
gdk_x11_clipboard_default_output_handler (cb, "text/plain;charset=utf-8", encoding, format, converter_stream);
gdk_x11_clipboard_default_output_handler (cb, "text/plain;charset=utf-8", converter_stream);
}
static void
@ -242,7 +240,7 @@ handle_utf8 (GdkX11Clipboard *cb,
int format,
GOutputStream *stream)
{
gdk_x11_clipboard_default_output_handler (cb, "text/plain;charset=utf-8", encoding, format, stream);
gdk_x11_clipboard_default_output_handler (cb, "text/plain;charset=utf-8", stream);
}
static GInputStream *
@ -480,16 +478,15 @@ gdk_x11_clipboard_claim_remote (GdkX11Clipboard *cb,
}
static void
gdk_x11_clipboard_request_selection (GdkX11Clipboard *cb,
Window requestor,
const char *target,
const char *property,
gulong timestamp)
gdk_x11_clipboard_request_selection (GdkX11Clipboard *cb,
GdkX11PendingSelectionNotify *notify,
Window requestor,
const char *target,
const char *property,
gulong timestamp)
{
const char *type, *mime_type;
MimeTypeHandleFunc handler_func = NULL;
const char *mime_type;
GdkDisplay *display;
gint format;
gsize i;
display = gdk_clipboard_get_display (GDK_CLIPBOARD (cb));
@ -497,9 +494,21 @@ gdk_x11_clipboard_request_selection (GdkX11Clipboard *cb,
if (mime_type)
{
handler_func = gdk_x11_clipboard_default_output_handler;
type = target;
format = 8;
if (gdk_content_formats_contain_mime_type (gdk_clipboard_get_formats (GDK_CLIPBOARD (cb)), mime_type))
{
GOutputStream *stream;
stream = gdk_x11_selection_output_stream_new (display,
notify,
requestor,
cb->selection,
target,
property,
target,
8,
timestamp);
gdk_x11_clipboard_default_output_handler (cb, target, stream);
}
}
else
{
@ -508,65 +517,30 @@ gdk_x11_clipboard_request_selection (GdkX11Clipboard *cb,
if (g_str_equal (target, special_targets[i].x_target) &&
special_targets[i].handler)
{
GOutputStream *stream;
if (special_targets[i].mime_type)
mime_type = gdk_intern_mime_type (special_targets[i].mime_type);
handler_func = special_targets[i].handler;
type = special_targets[i].type;
format = special_targets[i].format;
break;
stream = gdk_x11_selection_output_stream_new (display,
notify,
requestor,
cb->selection,
target,
property,
special_targets[i].type,
special_targets[i].format,
timestamp);
special_targets[i].handler (cb,
target,
special_targets[i].type,
special_targets[i].format,
stream);
return;
}
}
}
if (handler_func == NULL ||
(mime_type && !gdk_content_formats_contain_mime_type (gdk_clipboard_get_formats (GDK_CLIPBOARD (cb)), mime_type)))
{
Display *xdisplay = gdk_x11_display_get_xdisplay (display);
XSelectionEvent xreply;
int error;
xreply.type = SelectionNotify;
xreply.serial = 0;
xreply.send_event = True;
xreply.requestor = requestor;
xreply.selection = cb->xselection;
xreply.target = gdk_x11_get_xatom_by_name_for_display (display, target);
xreply.property = None;
xreply.time = timestamp;
GDK_NOTE(CLIPBOARD, g_printerr ("%s%s: Sending SelectionNotify rejecting request\n",
cb->selection, target));
gdk_x11_display_error_trap_push (display);
if (XSendEvent (xdisplay, xreply.requestor, False, NoEventMask, (XEvent*) & xreply) == 0)
{
GDK_NOTE(CLIPBOARD, g_printerr ("%s:%s: failed to XSendEvent()\n",
cb->selection, target));
g_warning ("failed to XSendEvent()");
}
XSync (xdisplay, False);
error = gdk_x11_display_error_trap_pop (display);
if (error != Success)
{
GDK_NOTE(CLIPBOARD, g_printerr ("%s:%s: X error during write: %d\n",
cb->selection, target, error));
}
}
else
{
GOutputStream *stream;
stream = gdk_x11_selection_output_stream_new (display,
requestor,
cb->selection,
target,
property,
type,
format,
timestamp);
handler_func (cb, target, type, format, stream);
}
gdk_x11_pending_selection_notify_send (notify, display, FALSE);
}
static GdkFilterReturn
@ -604,6 +578,7 @@ gdk_x11_clipboard_filter_event (GdkXEvent *xev,
case SelectionRequest:
{
GdkX11PendingSelectionNotify *notify;
const char *target, *property;
if (xevent->xselectionrequest.selection != cb->xselection)
@ -630,7 +605,16 @@ gdk_x11_clipboard_filter_event (GdkXEvent *xev,
GDK_NOTE(CLIPBOARD, g_printerr ("%s: got SelectionRequest for %s @ %s\n",
cb->selection, target, property));
notify = gdk_x11_pending_selection_notify_new (xevent->xselectionrequest.requestor,
xevent->xselectionrequest.selection,
xevent->xselectionrequest.target,
xevent->xselectionrequest.property ? xevent->xselectionrequest.property
: xevent->xselectionrequest.target,
xevent->xselectionrequest.time);
gdk_x11_clipboard_request_selection (cb,
notify,
xevent->xselectionrequest.requestor,
target,
property,

View File

@ -29,10 +29,11 @@
#include "gdkx11property.h"
#include "gdkx11window.h"
typedef struct GdkX11SelectionOutputStreamPrivate GdkX11SelectionOutputStreamPrivate;
typedef struct _GdkX11SelectionOutputStreamPrivate GdkX11SelectionOutputStreamPrivate;
struct GdkX11SelectionOutputStreamPrivate {
struct _GdkX11SelectionOutputStreamPrivate {
GdkDisplay *display;
GdkX11PendingSelectionNotify *notify;
Window xwindow;
char *selection;
Atom xselection;
@ -52,11 +53,17 @@ struct GdkX11SelectionOutputStreamPrivate {
GTask *pending_task;
guint started : 1;
guint incr : 1;
guint delete_pending : 1;
};
struct _GdkX11PendingSelectionNotify
{
gsize n_pending;
XSelectionEvent xevent;
};
G_DEFINE_TYPE_WITH_PRIVATE (GdkX11SelectionOutputStream, gdk_x11_selection_output_stream, G_TYPE_OUTPUT_STREAM);
static GdkFilterReturn
@ -80,7 +87,7 @@ gdk_x11_selection_output_stream_needs_flush_unlocked (GdkX11SelectionOutputStrea
{
GdkX11SelectionOutputStreamPrivate *priv = gdk_x11_selection_output_stream_get_instance_private (stream);
if (priv->data->len == 0)
if (priv->data->len == 0 && priv->notify == NULL)
return FALSE;
if (g_output_stream_is_closing (G_OUTPUT_STREAM (stream)))
@ -148,7 +155,7 @@ gdk_x11_selection_output_stream_perform_flush (GdkX11SelectionOutputStream *stre
element_size = get_element_size (priv->format);
n_elements = priv->data->len / element_size;
if (!priv->started && !g_output_stream_is_closing (G_OUTPUT_STREAM (stream)))
if (priv->notify && !g_output_stream_is_closing (G_OUTPUT_STREAM (stream)))
{
XWindowAttributes attrs;
@ -190,30 +197,10 @@ gdk_x11_selection_output_stream_perform_flush (GdkX11SelectionOutputStream *stre
priv->flush_requested = FALSE;
}
if (!priv->started)
if (priv->notify)
{
XSelectionEvent xevent;
xevent.type = SelectionNotify;
xevent.serial = 0;
xevent.send_event = True;
xevent.requestor = priv->xwindow;
xevent.selection = priv->xselection;
xevent.target = priv->xtarget;
xevent.property = priv->xproperty;
xevent.time = priv->timestamp;
if (XSendEvent (xdisplay, priv->xwindow, False, NoEventMask, (XEvent*) & xevent) == 0)
{
GDK_NOTE(SELECTION, g_printerr ("%s:%s: failed to XSendEvent()\n",
priv->selection, priv->target));
g_warning ("failed to XSendEvent()");
}
XSync (xdisplay, False);
GDK_NOTE(SELECTION, g_printerr ("%s:%s: sent SelectionNotify for %s on %s\n",
priv->selection, priv->target, priv->target, priv->property));
priv->started = TRUE;
gdk_x11_pending_selection_notify_send (priv->notify, priv->display, TRUE);
priv->notify = NULL;
}
priv->delete_pending = TRUE;
@ -475,6 +462,9 @@ gdk_x11_selection_output_stream_finalize (GObject *object)
GdkX11SelectionOutputStream *stream = GDK_X11_SELECTION_OUTPUT_STREAM (object);
GdkX11SelectionOutputStreamPrivate *priv = gdk_x11_selection_output_stream_get_instance_private (stream);
/* not sending a notify is terrible */
g_assert (priv->notify == NULL);
g_byte_array_unref (priv->data);
g_cond_clear (&priv->cond);
g_mutex_clear (&priv->mutex);
@ -550,14 +540,15 @@ gdk_x11_selection_output_stream_filter_event (GdkXEvent *xev,
}
GOutputStream *
gdk_x11_selection_output_stream_new (GdkDisplay *display,
Window window,
const char *selection,
const char *target,
const char *property,
const char *type,
int format,
gulong timestamp)
gdk_x11_selection_output_stream_new (GdkDisplay *display,
GdkX11PendingSelectionNotify *notify,
Window window,
const char *selection,
const char *target,
const char *property,
const char *type,
int format,
gulong timestamp)
{
GdkX11SelectionOutputStream *stream;
GdkX11SelectionOutputStreamPrivate *priv;
@ -567,6 +558,7 @@ gdk_x11_selection_output_stream_new (GdkDisplay *display,
priv->display = display;
GDK_X11_DISPLAY (display)->streams = g_slist_prepend (GDK_X11_DISPLAY (display)->streams, stream);
priv->notify = notify;
priv->xwindow = window;
priv->selection = g_strdup (selection);
priv->xselection = gdk_x11_get_xatom_by_name_for_display (display, priv->selection);
@ -580,6 +572,86 @@ gdk_x11_selection_output_stream_new (GdkDisplay *display,
priv->timestamp = timestamp;
gdk_window_add_filter (NULL, gdk_x11_selection_output_stream_filter_event, stream);
return G_OUTPUT_STREAM (stream);
}
GdkX11PendingSelectionNotify *
gdk_x11_pending_selection_notify_new (Window window,
Atom selection,
Atom target,
Atom property,
Time timestamp)
{
GdkX11PendingSelectionNotify *pending;
pending = g_slice_new0 (GdkX11PendingSelectionNotify);
pending->n_pending = 1;
pending->xevent.type = SelectionNotify;
pending->xevent.serial = 0;
pending->xevent.send_event = True;
pending->xevent.requestor = window;
pending->xevent.selection = selection;
pending->xevent.target = target;
pending->xevent.property = property;
pending->xevent.time = timestamp;
return pending;
}
void
gdk_x11_pending_selection_notify_require (GdkX11PendingSelectionNotify *notify,
guint n_sends)
{
notify->n_pending += n_sends;
}
void
gdk_x11_pending_selection_notify_send (GdkX11PendingSelectionNotify *notify,
GdkDisplay *display,
gboolean success)
{
Display *xdisplay;
int error;
notify->n_pending--;
if (notify->n_pending)
{
GDK_NOTE(SELECTION, g_printerr ("%s:%s: not sending SelectionNotify yet, %zu streams still pending\n",
gdk_x11_get_xatom_name_for_display (display, notify->xevent.selection),
gdk_x11_get_xatom_name_for_display (display, notify->xevent.target),
notify->n_pending));
return;
}
GDK_NOTE(SELECTION, g_printerr ("%s:%s: sending SelectionNotify reporting %s\n",
gdk_x11_get_xatom_name_for_display (display, notify->xevent.selection),
gdk_x11_get_xatom_name_for_display (display, notify->xevent.target),
success ? "success" : "failure"));
if (!success)
notify->xevent.property = None;
xdisplay = gdk_x11_display_get_xdisplay (display);
gdk_x11_display_error_trap_push (display);
if (XSendEvent (xdisplay, notify->xevent.requestor, False, NoEventMask, (XEvent*) &notify->xevent) == 0)
{
GDK_NOTE(SELECTION, g_printerr ("%s:%s: failed to XSendEvent()\n",
gdk_x11_get_xatom_name_for_display (display, notify->xevent.selection),
gdk_x11_get_xatom_name_for_display (display, notify->xevent.target)));
}
XSync (xdisplay, False);
error = gdk_x11_display_error_trap_pop (display);
if (error != Success)
{
GDK_NOTE(SELECTION, g_printerr ("%s:%s: X error during write: %d\n",
gdk_x11_get_xatom_name_for_display (display, notify->xevent.selection),
gdk_x11_get_xatom_name_for_display (display, notify->xevent.target),
error));
}
}

View File

@ -36,6 +36,8 @@ G_BEGIN_DECLS
#define GDK_IS_X11_SELECTION_OUTPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDK_TYPE_X11_SELECTION_OUTPUT_STREAM))
#define GDK_X11_SELECTION_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_X11_SELECTION_OUTPUT_STREAM, GdkX11SelectionOutputStreamClass))
typedef struct _GdkX11PendingSelectionNotify GdkX11PendingSelectionNotify;
typedef struct GdkX11SelectionOutputStream GdkX11SelectionOutputStream;
typedef struct GdkX11SelectionOutputStreamClass GdkX11SelectionOutputStreamClass;
@ -53,6 +55,7 @@ struct GdkX11SelectionOutputStreamClass
GType gdk_x11_selection_output_stream_get_type (void) G_GNUC_CONST;
GOutputStream * gdk_x11_selection_output_stream_new (GdkDisplay *display,
GdkX11PendingSelectionNotify *notify,
Window window,
const char *selection,
const char *target,
@ -61,6 +64,17 @@ GOutputStream * gdk_x11_selection_output_stream_new (GdkDisplay
int format,
gulong timestamp);
GdkX11PendingSelectionNotify *
gdk_x11_pending_selection_notify_new (Window window,
Atom selection,
Atom target,
Atom property,
Time timestamp);
void gdk_x11_pending_selection_notify_require (GdkX11PendingSelectionNotify *notify,
guint n_sends);
void gdk_x11_pending_selection_notify_send (GdkX11PendingSelectionNotify *notify,
GdkDisplay *display,
gboolean success);
G_END_DECLS