mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-26 05:31:07 +00:00
x11: Refactor code
This is in preparation for DND. It moves a lot of code from gdkclipboard-x11.c to gdkselectionoutputstream-x11.c to untangle it from GdkX11Clipboard usage.
This commit is contained in:
parent
80dcdd3df6
commit
5df527edaf
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user