diff --git a/gdk/x11/gdkclipboard-x11.c b/gdk/x11/gdkclipboard-x11.c index 817792e5a1..78edb2a10f 100644 --- a/gdk/x11/gdkclipboard-x11.c +++ b/gdk/x11/gdkclipboard-x11.c @@ -92,12 +92,12 @@ gdk_x11_clipboard_default_output_done (GObject *clipboard, } static void -gdk_x11_clipboard_default_output_handler (GdkX11Clipboard *cb, - const char *target, - GOutputStream *stream) +gdk_x11_clipboard_default_output_handler (GOutputStream *stream, + const char *mime_type, + gpointer user_data) { - gdk_clipboard_write_async (GDK_CLIPBOARD (cb), - g_intern_string (target), + gdk_clipboard_write_async (GDK_CLIPBOARD (user_data), + mime_type, stream, G_PRIORITY_DEFAULT, NULL, @@ -106,108 +106,6 @@ gdk_x11_clipboard_default_output_handler (GdkX11Clipboard *cb, g_object_unref (stream); } -static void -handle_targets_done (GObject *stream, - GAsyncResult *result, - gpointer user_data) -{ - GError *error = NULL; - gsize bytes_written; - - if (!g_output_stream_write_all_finish (G_OUTPUT_STREAM (stream), result, &bytes_written, &error)) - { - GDK_NOTE(CLIPBOARD, g_printerr ("---: failed to send targets after %zu bytes: %s\n", - bytes_written, error->message)); - g_error_free (error); - } - - g_free (user_data); -} - -static Atom * -gdk_x11_clipboard_formats_to_atoms (GdkDisplay *display, - gboolean include_special, - GdkContentFormats *formats, - gsize *n_atoms); - -static void -handle_targets (GdkX11Clipboard *cb, - const char *target, - const char *encoding, - int format, - GOutputStream *stream) -{ - GdkClipboard *clipboard = GDK_CLIPBOARD (cb); - Atom *atoms; - gsize n_atoms; - - atoms = gdk_x11_clipboard_formats_to_atoms (gdk_clipboard_get_display (clipboard), - TRUE, - gdk_clipboard_get_formats (clipboard), - &n_atoms); - print_atoms (cb, "sending targets", atoms, n_atoms); - g_output_stream_write_all_async (stream, - atoms, - n_atoms * sizeof (Atom), - G_PRIORITY_DEFAULT, - NULL, - handle_targets_done, - atoms); - g_object_unref (stream); -} - -static void -handle_timestamp_done (GObject *stream, - GAsyncResult *result, - gpointer user_data) -{ - GError *error = NULL; - gsize bytes_written; - - if (!g_output_stream_write_all_finish (G_OUTPUT_STREAM (stream), result, &bytes_written, &error)) - { - GDK_NOTE(CLIPBOARD, g_printerr ("---: failed to send timestamp after %zu bytes: %s\n", - bytes_written, error->message)); - g_error_free (error); - } - - g_slice_free (gulong, user_data); -} - -static void -handle_timestamp (GdkX11Clipboard *cb, - const char *target, - const char *encoding, - int format, - GOutputStream *stream) -{ - gulong *timestamp; - - timestamp = g_slice_new (gulong); - *timestamp = cb->timestamp; - - g_output_stream_write_all_async (stream, - timestamp, - sizeof (gulong), - G_PRIORITY_DEFAULT, - NULL, - handle_timestamp_done, - timestamp); - g_object_unref (stream); -} - -static void -handle_save_targets (GdkX11Clipboard *cb, - const char *target, - const char *encoding, - int format, - GOutputStream *stream) -{ - /* Don't do anything */ - - g_object_unref (stream); -} - static GInputStream * text_list_convert (GdkX11Clipboard *cb, GInputStream *stream, @@ -228,37 +126,6 @@ text_list_convert (GdkX11Clipboard *cb, return converter_stream; } -static void -handle_text_list (GdkX11Clipboard *cb, - const char *target, - const char *encoding, - int format, - GOutputStream *stream) -{ - GOutputStream *converter_stream; - GConverter *converter; - - converter = gdk_x11_text_list_converter_to_utf8_new (gdk_clipboard_get_display (GDK_CLIPBOARD (cb)), - encoding, - format); - converter_stream = g_converter_output_stream_new (stream, converter); - - g_object_unref (converter); - g_object_unref (stream); - - gdk_x11_clipboard_default_output_handler (cb, "text/plain;charset=utf-8", converter_stream); -} - -static void -handle_utf8 (GdkX11Clipboard *cb, - const char *target, - const char *encoding, - int format, - GOutputStream *stream) -{ - gdk_x11_clipboard_default_output_handler (cb, "text/plain;charset=utf-8", stream); -} - static GInputStream * no_convert (GdkX11Clipboard *cb, GInputStream *stream, @@ -268,23 +135,20 @@ no_convert (GdkX11Clipboard *cb, return stream; } -typedef void (* MimeTypeHandleFunc) (GdkX11Clipboard *, const char *, const char *, int, GOutputStream *); - static const struct { const char *x_target; const char *mime_type; GInputStream * (* convert) (GdkX11Clipboard *, GInputStream *, const char *, int); const char *type; gint format; - MimeTypeHandleFunc handler; } special_targets[] = { - { "UTF8_STRING", "text/plain;charset=utf-8", no_convert, "UTF8_STRING", 8, handle_utf8 }, - { "COMPOUND_TEXT", "text/plain;charset=utf-8", text_list_convert, "COMPOUND_TEXT", 8, handle_text_list }, - { "TEXT", "text/plain;charset=utf-8", text_list_convert, "STRING", 8, handle_text_list }, - { "STRING", "text/plain;charset=utf-8", text_list_convert, "STRING", 8, handle_text_list }, - { "TARGETS", NULL, NULL, "ATOM", 32, handle_targets }, - { "TIMESTAMP", NULL, NULL, "INTEGER", 32, handle_timestamp }, - { "SAVE_TARGETS", NULL, NULL, "NULL", 32, handle_save_targets } + { "UTF8_STRING", "text/plain;charset=utf-8", no_convert, "UTF8_STRING", 8 }, + { "COMPOUND_TEXT", "text/plain;charset=utf-8", text_list_convert, "COMPOUND_TEXT", 8 }, + { "TEXT", "text/plain;charset=utf-8", text_list_convert, "STRING", 8 }, + { "STRING", "text/plain;charset=utf-8", text_list_convert, "STRING", 8 }, + { "TARGETS", NULL, NULL, "ATOM", 32 }, + { "TIMESTAMP", NULL, NULL, "INTEGER", 32 }, + { "SAVE_TARGETS", NULL, NULL, "NULL", 32 } }; GSList * @@ -313,7 +177,7 @@ gdk_x11_clipboard_formats_to_targets (GdkContentFormats *formats) return g_slist_reverse (targets); } -static Atom * +Atom * gdk_x11_clipboard_formats_to_atoms (GdkDisplay *display, gboolean include_special, GdkContentFormats *formats, @@ -332,8 +196,7 @@ gdk_x11_clipboard_formats_to_atoms (GdkDisplay *display, if (special_targets[i].mime_type != NULL) continue; - if (special_targets[i].handler) - targets = g_slist_prepend (targets, (gpointer) g_intern_string (special_targets[i].x_target)); + targets = g_slist_prepend (targets, (gpointer) g_intern_string (special_targets[i].x_target)); } } @@ -509,164 +372,6 @@ gdk_x11_clipboard_claim_remote (GdkX11Clipboard *cb, gdk_x11_clipboard_request_targets (cb); } -static gboolean -gdk_x11_clipboard_request_selection (GdkX11Clipboard *cb, - GdkX11PendingSelectionNotify *notify, - Window requestor, - const char *target, - const char *property, - gulong timestamp) -{ - const char *mime_type; - GdkDisplay *display; - gsize i; - - display = gdk_clipboard_get_display (GDK_CLIPBOARD (cb)); - mime_type = gdk_intern_mime_type (target); - - if (mime_type) - { - 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); - return TRUE; - } - } - else if (g_str_equal (target, "MULTIPLE")) - { - gulong n_atoms; - gulong nbytes; - Atom prop_type; - gint prop_format; - Atom *atoms = NULL; - int error; - - error = XGetWindowProperty (gdk_x11_display_get_xdisplay (display), - requestor, - gdk_x11_get_xatom_by_name_for_display (display, property), - 0, 0x1FFFFFFF, False, - AnyPropertyType, - &prop_type, &prop_format, - &n_atoms, &nbytes, (guchar **) &atoms); - if (error != Success) - { - GDK_NOTE(CLIPBOARD, g_printerr ("%s: XGetProperty() during MULTIPLE failed with %d\n", - cb->selection, error)); - } - else if (prop_format != 32 || - prop_type != gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR")) - { - GDK_NOTE(CLIPBOARD, g_printerr ("%s: XGetProperty() type/format should be ATOM_PAIR/32 but is %s/%d\n", - cb->selection, gdk_x11_get_xatom_name_for_display (display, prop_type), prop_format)); - } - else if (n_atoms < 2) - { - print_atoms (cb, "ignoring MULTIPLE request with too little elements", atoms, n_atoms); - } - else - { - gulong i; - - print_atoms (cb, "MULTIPLE request", atoms, n_atoms); - if (n_atoms % 2) - { - GDK_NOTE(CLIPBOARD, g_printerr ("%s: Number of atoms is uneven at %lu, ignoring last element\n", - cb->selection, n_atoms)); - n_atoms &= ~1; - } - - gdk_x11_pending_selection_notify_require (notify, n_atoms / 2); - - for (i = 0; i < n_atoms / 2; i++) - { - gboolean success; - - if (atoms[2 * i] == None || atoms[2 * i + 1] == None) - { - success = FALSE; - GDK_NOTE(CLIPBOARD, g_printerr ("%s: None not allowed as atom in MULTIPLE request\n", - cb->selection)); - gdk_x11_pending_selection_notify_send (notify, display, FALSE); - } - else if (atoms[2 * i] == gdk_x11_get_xatom_by_name_for_display (display, "MULTIPLE")) - { - success = FALSE; - GDK_NOTE(CLIPBOARD, g_printerr ("%s: MULTIPLE as target in MULTIPLE request would cause recursion\n", - cb->selection)); - gdk_x11_pending_selection_notify_send (notify, display, FALSE); - } - else - { - success = gdk_x11_clipboard_request_selection (cb, - notify, - requestor, - gdk_x11_get_xatom_name_for_display (display, atoms[2 * i]), - gdk_x11_get_xatom_name_for_display (display, atoms[2 * i + 1]), - timestamp); - } - - if (!success) - atoms[2 * i + 1] = None; - } - } - - XChangeProperty (gdk_x11_display_get_xdisplay (display), - requestor, - gdk_x11_get_xatom_by_name_for_display (display, property), - prop_type, 32, - PropModeReplace, (guchar *)atoms, n_atoms); - - if (atoms) - XFree (atoms); - - gdk_x11_pending_selection_notify_send (notify, display, TRUE); - return TRUE; - } - else - { - for (i = 0; i < G_N_ELEMENTS (special_targets); i++) - { - 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); - 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 TRUE; - } - } - } - - gdk_x11_pending_selection_notify_send (notify, display, FALSE); - return FALSE; -} - static GdkFilterReturn gdk_x11_clipboard_filter_event (GdkXEvent *xev, GdkEvent *gdkevent, @@ -732,7 +437,6 @@ gdk_x11_clipboard_filter_event (GdkXEvent *xev, case SelectionRequest: { - GdkX11PendingSelectionNotify *notify; const char *target, *property; if (xevent->xselectionrequest.selection != cb->xselection) @@ -760,19 +464,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, - xevent->xselectionrequest.time); + gdk_x11_selection_output_streams_create (display, + gdk_clipboard_get_formats (GDK_CLIPBOARD (cb)), + xevent->xselectionrequest.requestor, + xevent->xselectionrequest.selection, + xevent->xselectionrequest.target, + xevent->xselectionrequest.property ? xevent->xselectionrequest.property + : xevent->xselectionrequest.target, + xevent->xselectionrequest.time, + gdk_x11_clipboard_default_output_handler, + cb); return GDK_FILTER_REMOVE; } diff --git a/gdk/x11/gdkclipboard-x11.h b/gdk/x11/gdkclipboard-x11.h index 0c0322dbc6..e4b82ee622 100644 --- a/gdk/x11/gdkclipboard-x11.h +++ b/gdk/x11/gdkclipboard-x11.h @@ -37,6 +37,10 @@ GdkClipboard * gdk_x11_clipboard_new (GdkDisplay const gchar *selection); GSList * gdk_x11_clipboard_formats_to_targets (GdkContentFormats *formats); +Atom * gdk_x11_clipboard_formats_to_atoms (GdkDisplay *display, + gboolean include_special, + GdkContentFormats *formats, + gsize *n_atoms); G_END_DECLS diff --git a/gdk/x11/gdkselectionoutputstream-x11.c b/gdk/x11/gdkselectionoutputstream-x11.c index 5a42421906..221900424b 100644 --- a/gdk/x11/gdkselectionoutputstream-x11.c +++ b/gdk/x11/gdkselectionoutputstream-x11.c @@ -23,12 +23,15 @@ #include "gdkselectionoutputstream-x11.h" +#include "gdkclipboard-x11.h" #include "gdkdisplay-x11.h" #include "gdkintl.h" +#include "gdktextlistconverter-x11.h" #include "gdkx11display.h" #include "gdkx11property.h" #include "gdkx11window.h" +typedef struct _GdkX11PendingSelectionNotify GdkX11PendingSelectionNotify; typedef struct _GdkX11SelectionOutputStreamPrivate GdkX11SelectionOutputStreamPrivate; struct _GdkX11SelectionOutputStreamPrivate { @@ -66,6 +69,84 @@ struct _GdkX11PendingSelectionNotify G_DEFINE_TYPE_WITH_PRIVATE (GdkX11SelectionOutputStream, gdk_x11_selection_output_stream, G_TYPE_OUTPUT_STREAM); +static 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; +} + +static void +gdk_x11_pending_selection_notify_require (GdkX11PendingSelectionNotify *notify, + guint n_sends) +{ + notify->n_pending += n_sends; +} + +static 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)); + } +} + static GdkFilterReturn gdk_x11_selection_output_stream_filter_event (GdkXEvent *xevent, GdkEvent *gdkevent, @@ -539,7 +620,7 @@ gdk_x11_selection_output_stream_filter_event (GdkXEvent *xev, } } -GOutputStream * +static GOutputStream * gdk_x11_selection_output_stream_new (GdkDisplay *display, GdkX11PendingSelectionNotify *notify, Window window, @@ -576,82 +657,386 @@ gdk_x11_selection_output_stream_new (GdkDisplay *display, return G_OUTPUT_STREAM (stream); } -GdkX11PendingSelectionNotify * -gdk_x11_pending_selection_notify_new (Window window, - Atom selection, - Atom target, - Atom property, - Time timestamp) +static void +print_atoms (GdkDisplay *display, + const char *selection, + const char *prefix, + const Atom *atoms, + gsize n_atoms) { - GdkX11PendingSelectionNotify *pending; + GDK_NOTE(CLIPBOARD, + gsize i; + + g_printerr ("%s: %s [ ", selection, prefix); + for (i = 0; i < n_atoms; i++) + { + g_printerr ("%s%s", i > 0 ? ", " : "", gdk_x11_get_xatom_name_for_display (display , atoms[i])); + } + g_printerr (" ]\n"); + ); +} - pending = g_slice_new0 (GdkX11PendingSelectionNotify); - pending->n_pending = 1; +static void +handle_targets_done (GObject *stream, + GAsyncResult *result, + gpointer user_data) +{ + GError *error = NULL; + gsize bytes_written; - 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; + if (!g_output_stream_write_all_finish (G_OUTPUT_STREAM (stream), result, &bytes_written, &error)) + { + GDK_NOTE(CLIPBOARD, g_printerr ("---: failed to send targets after %zu bytes: %s\n", + bytes_written, error->message)); + g_error_free (error); + } - return pending; + g_free (user_data); +} + +static void +handle_targets (GOutputStream *stream, + GdkDisplay *display, + GdkContentFormats *formats, + const char *target, + const char *encoding, + int format, + gulong timestamp, + GdkX11SelectionOutputHandler handler, + gpointer user_data) +{ + Atom *atoms; + gsize n_atoms; + + atoms = gdk_x11_clipboard_formats_to_atoms (display, + TRUE, + formats, + &n_atoms); + print_atoms (display, "---", "sending targets", atoms, n_atoms); + g_output_stream_write_all_async (stream, + atoms, + n_atoms * sizeof (Atom), + G_PRIORITY_DEFAULT, + NULL, + handle_targets_done, + atoms); + g_object_unref (stream); +} + +static void +handle_timestamp_done (GObject *stream, + GAsyncResult *result, + gpointer user_data) +{ + GError *error = NULL; + gsize bytes_written; + + if (!g_output_stream_write_all_finish (G_OUTPUT_STREAM (stream), result, &bytes_written, &error)) + { + GDK_NOTE(CLIPBOARD, g_printerr ("---: failed to send timestamp after %zu bytes: %s\n", + bytes_written, error->message)); + g_error_free (error); + } + + g_slice_free (gulong, user_data); +} + +static void +handle_timestamp (GOutputStream *stream, + GdkDisplay *display, + GdkContentFormats *formats, + const char *target, + const char *encoding, + int format, + gulong timestamp, + GdkX11SelectionOutputHandler handler, + gpointer user_data) +{ + gulong *time_; + + time_ = g_slice_new (gulong); + *time_ = timestamp; + + g_output_stream_write_all_async (stream, + time_, + sizeof (gulong), + G_PRIORITY_DEFAULT, + NULL, + handle_timestamp_done, + time_); + g_object_unref (stream); +} + +static void +handle_save_targets (GOutputStream *stream, + GdkDisplay *display, + GdkContentFormats *formats, + const char *target, + const char *encoding, + int format, + gulong timestamp, + GdkX11SelectionOutputHandler handler, + gpointer user_data) +{ + /* Don't do anything */ + + g_object_unref (stream); +} + +static void +handle_text_list (GOutputStream *stream, + GdkDisplay *display, + GdkContentFormats *formats, + const char *target, + const char *encoding, + int format, + gulong timestamp, + GdkX11SelectionOutputHandler handler, + gpointer user_data) +{ + GOutputStream *converter_stream; + GConverter *converter; + + converter = gdk_x11_text_list_converter_to_utf8_new (display, + encoding, + format); + converter_stream = g_converter_output_stream_new (stream, converter); + + g_object_unref (converter); + g_object_unref (stream); + + handler (converter_stream, gdk_intern_mime_type ("text/plain;charset=utf-8"), user_data); +} + +static void +handle_utf8 (GOutputStream *stream, + GdkDisplay *display, + GdkContentFormats *formats, + const char *target, + const char *encoding, + int format, + gulong timestamp, + GdkX11SelectionOutputHandler handler, + gpointer user_data) +{ + handler (stream, gdk_intern_mime_type ("text/plain;charset=utf-8"), user_data); +} + +typedef void (* MimeTypeHandleFunc) (GOutputStream *, GdkDisplay *, GdkContentFormats *, const char *, const char *, int, gulong, GdkX11SelectionOutputHandler, gpointer); + +static const struct { + const char *x_target; + const char *mime_type; + const char *type; + gint format; + MimeTypeHandleFunc handler; +} special_targets[] = { + { "UTF8_STRING", "text/plain;charset=utf-8", "UTF8_STRING", 8, handle_utf8 }, + { "COMPOUND_TEXT", "text/plain;charset=utf-8", "COMPOUND_TEXT", 8, handle_text_list }, + { "TEXT", "text/plain;charset=utf-8", "STRING", 8, handle_text_list }, + { "STRING", "text/plain;charset=utf-8", "STRING", 8, handle_text_list }, + { "TARGETS", NULL, "ATOM", 32, handle_targets }, + { "TIMESTAMP", NULL, "INTEGER", 32, handle_timestamp }, + { "SAVE_TARGETS", NULL, "NULL", 32, handle_save_targets } +}; + +static gboolean +gdk_x11_selection_output_streams_request (GdkDisplay *display, + GdkX11PendingSelectionNotify *notify, + GdkContentFormats *formats, + Window requestor, + Atom xselection, + Atom xtarget, + Atom xproperty, + gulong timestamp, + GdkX11SelectionOutputHandler handler, + gpointer user_data) +{ + const char *mime_type, *selection, *target, *property; + gsize i; + + selection = gdk_x11_get_xatom_name_for_display (display, xselection); + target = gdk_x11_get_xatom_name_for_display (display, xtarget); + property = gdk_x11_get_xatom_name_for_display (display, xproperty); + mime_type = gdk_intern_mime_type (target); + + if (mime_type) + { + if (gdk_content_formats_contain_mime_type (formats, mime_type)) + { + GOutputStream *stream; + + stream = gdk_x11_selection_output_stream_new (display, + notify, + requestor, + selection, + target, + property, + target, + 8, + timestamp); + handler (stream, target, user_data); + return TRUE; + } + } + else if (g_str_equal (target, "MULTIPLE")) + { + gulong n_atoms; + gulong nbytes; + Atom prop_type; + gint prop_format; + Atom *atoms = NULL; + int error; + + error = XGetWindowProperty (gdk_x11_display_get_xdisplay (display), + requestor, + xproperty, + 0, 0x1FFFFFFF, False, + AnyPropertyType, + &prop_type, &prop_format, + &n_atoms, &nbytes, (guchar **) &atoms); + if (error != Success) + { + GDK_NOTE(SELECTION, g_printerr ("%s: XGetProperty() during MULTIPLE failed with %d\n", + selection, error)); + } + else if (prop_format != 32 || + prop_type != gdk_x11_get_xatom_by_name_for_display (display, "ATOM_PAIR")) + { + GDK_NOTE(SELECTION, g_printerr ("%s: XGetProperty() type/format should be ATOM_PAIR/32 but is %s/%d\n", + selection, gdk_x11_get_xatom_name_for_display (display, prop_type), prop_format)); + } + else if (n_atoms < 2) + { + print_atoms (display, selection, "ignoring MULTIPLE request with too little elements", atoms, n_atoms); + } + else + { + gulong i; + + print_atoms (display, selection, "MULTIPLE request", atoms, n_atoms); + if (n_atoms % 2) + { + GDK_NOTE(SELECTION, g_printerr ("%s: Number of atoms is uneven at %lu, ignoring last element\n", + selection, n_atoms)); + n_atoms &= ~1; + } + + gdk_x11_pending_selection_notify_require (notify, n_atoms / 2); + + for (i = 0; i < n_atoms / 2; i++) + { + gboolean success; + + if (atoms[2 * i] == None || atoms[2 * i + 1] == None) + { + success = FALSE; + GDK_NOTE(SELECTION, g_printerr ("%s: None not allowed as atom in MULTIPLE request\n", + selection)); + gdk_x11_pending_selection_notify_send (notify, display, FALSE); + } + else if (atoms[2 * i] == gdk_x11_get_xatom_by_name_for_display (display, "MULTIPLE")) + { + success = FALSE; + GDK_NOTE(SELECTION, g_printerr ("%s: MULTIPLE as target in MULTIPLE request would cause recursion\n", + selection)); + gdk_x11_pending_selection_notify_send (notify, display, FALSE); + } + else + { + success = gdk_x11_selection_output_streams_request (display, + notify, + formats, + requestor, + xselection, + atoms[2 * i], + atoms[2 * i + 1], + timestamp, + handler, + user_data); + } + + if (!success) + atoms[2 * i + 1] = None; + } + } + + XChangeProperty (gdk_x11_display_get_xdisplay (display), + requestor, + xproperty, + prop_type, 32, + PropModeReplace, (guchar *)atoms, n_atoms); + + if (atoms) + XFree (atoms); + + gdk_x11_pending_selection_notify_send (notify, display, TRUE); + return TRUE; + } + else + { + for (i = 0; i < G_N_ELEMENTS (special_targets); i++) + { + 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); + stream = gdk_x11_selection_output_stream_new (display, + notify, + requestor, + selection, + target, + property, + special_targets[i].type, + special_targets[i].format, + timestamp); + special_targets[i].handler (stream, + display, + formats, + target, + special_targets[i].type, + special_targets[i].format, + timestamp, + handler, + user_data); + return TRUE; + } + } + } + + gdk_x11_pending_selection_notify_send (notify, display, FALSE); + return FALSE; } void -gdk_x11_pending_selection_notify_require (GdkX11PendingSelectionNotify *notify, - guint n_sends) +gdk_x11_selection_output_streams_create (GdkDisplay *display, + GdkContentFormats *formats, + Window requestor, + Atom selection, + Atom target, + Atom property, + gulong timestamp, + GdkX11SelectionOutputHandler handler, + gpointer user_data) { - notify->n_pending += n_sends; + GdkX11PendingSelectionNotify *notify; + + notify = gdk_x11_pending_selection_notify_new (requestor, + selection, + target, + property, + timestamp); + gdk_x11_selection_output_streams_request (display, + notify, + formats, + requestor, + selection, + target, + property, + timestamp, + handler, + user_data); } - -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 7b4c09cfdd..03a0d30029 100644 --- a/gdk/x11/gdkselectionoutputstream-x11.h +++ b/gdk/x11/gdkselectionoutputstream-x11.h @@ -36,11 +36,11 @@ 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; +typedef void (* GdkX11SelectionOutputHandler) (GOutputStream *stream, const char *mime_type, gpointer user_data); + struct GdkX11SelectionOutputStream { GOutputStream parent_instance; @@ -54,27 +54,15 @@ 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, - const char *property, - const char *type, - int format, - gulong timestamp); - -GdkX11PendingSelectionNotify * - gdk_x11_pending_selection_notify_new (Window window, +void gdk_x11_selection_output_streams_create (GdkDisplay *display, + GdkContentFormats *formats, + Window requestor, 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); + gulong timestamp, + GdkX11SelectionOutputHandler handler, + gpointer user_data); G_END_DECLS