diff --git a/gdk/x11/gdkclipboard-x11.c b/gdk/x11/gdkclipboard-x11.c index 06f9e98f91..d3513f5ab2 100644 --- a/gdk/x11/gdkclipboard-x11.c +++ b/gdk/x11/gdkclipboard-x11.c @@ -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, diff --git a/gdk/x11/gdkselectionoutputstream-x11.c b/gdk/x11/gdkselectionoutputstream-x11.c index 00e60513d5..5a42421906 100644 --- a/gdk/x11/gdkselectionoutputstream-x11.c +++ b/gdk/x11/gdkselectionoutputstream-x11.c @@ -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*) ¬ify->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)); + } +} + + diff --git a/gdk/x11/gdkselectionoutputstream-x11.h b/gdk/x11/gdkselectionoutputstream-x11.h index 874d35b849..7b4c09cfdd 100644 --- a/gdk/x11/gdkselectionoutputstream-x11.h +++ b/gdk/x11/gdkselectionoutputstream-x11.h @@ -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