From a9191cd79daa06ab039fdcdcdc1f2965ada28204 Mon Sep 17 00:00:00 2001 From: Anders Carlsson Date: Fri, 3 Jun 2005 08:29:45 +0000 Subject: [PATCH] xutils.c Add clipboard manager files. 2005-06-03 Anders Carlsson * Makefile.am: * clipboard-manager.c: * clipboard-manager.h: * list.c: * list.h: * xutils.c * xutils.h: Add clipboard manager files. * gnome-settings-daemon.c: (clipboard_manager_terminate_cb), (clipboard_manager_event_filter), (clipboard_manager_watch_cb), (finalize), (gnome_settings_daemon_new): Use clipboard manager for persistent clipboard storage. --- gnome-settings-daemon/ChangeLog | 16 + gnome-settings-daemon/Makefile.am | 6 + gnome-settings-daemon/clipboard-manager.c | 923 ++++++++++++++++++ gnome-settings-daemon/clipboard-manager.h | 51 + gnome-settings-daemon/gnome-settings-daemon.c | 67 +- gnome-settings-daemon/list.c | 150 +++ gnome-settings-daemon/list.h | 57 ++ gnome-settings-daemon/xutils.c | 117 +++ gnome-settings-daemon/xutils.h | 50 + 9 files changed, 1435 insertions(+), 2 deletions(-) create mode 100644 gnome-settings-daemon/clipboard-manager.c create mode 100644 gnome-settings-daemon/clipboard-manager.h create mode 100644 gnome-settings-daemon/list.c create mode 100644 gnome-settings-daemon/list.h create mode 100644 gnome-settings-daemon/xutils.c create mode 100644 gnome-settings-daemon/xutils.h diff --git a/gnome-settings-daemon/ChangeLog b/gnome-settings-daemon/ChangeLog index 5b6304f9a..ef845574a 100644 --- a/gnome-settings-daemon/ChangeLog +++ b/gnome-settings-daemon/ChangeLog @@ -1,3 +1,19 @@ +2005-06-03 Anders Carlsson + + * Makefile.am: + * clipboard-manager.c: + * clipboard-manager.h: + * list.c: + * list.h: + * xutils.c + * xutils.h: + Add clipboard manager files. + + * gnome-settings-daemon.c: (clipboard_manager_terminate_cb), + (clipboard_manager_event_filter), (clipboard_manager_watch_cb), + (finalize), (gnome_settings_daemon_new): + Use clipboard manager for persistent clipboard storage. + 2005-05-31 Rodrigo Moya * GNOME_Settings_Daemon.server.in: added name and description diff --git a/gnome-settings-daemon/Makefile.am b/gnome-settings-daemon/Makefile.am index 95ea97ea8..a105a392b 100644 --- a/gnome-settings-daemon/Makefile.am +++ b/gnome-settings-daemon/Makefile.am @@ -55,6 +55,12 @@ gnome_settings_daemon_SOURCES = \ reaper.h \ gnome-settings-xmodmap.c \ gnome-settings-xmodmap.h \ + list.c \ + list.h \ + xutils.c \ + xutils.h \ + clipboard-manager.c \ + clipboard-manager.h \ $(CORBA_GENERATED) # $(AccessX_files) diff --git a/gnome-settings-daemon/clipboard-manager.c b/gnome-settings-daemon/clipboard-manager.c new file mode 100644 index 000000000..6dd269179 --- /dev/null +++ b/gnome-settings-daemon/clipboard-manager.c @@ -0,0 +1,923 @@ +/* + * Copyright © 2004 Red Hat, Inc. + * Copyright © 2004 Nokia Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Red Hat not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Red Hat makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT + * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Authors: Matthias Clasen, Red Hat, Inc. + * Anders Carlsson, Imendio AB + */ + +#include +#include +#include + +#include +#include + +#include "clipboard-manager.h" +#include "xutils.h" +#include "list.h" + + +struct _ClipboardManager +{ + Display *display; + Window window; + Time timestamp; + + ClipboardTerminateFunc terminate; + ClipboardWatchFunc watch; + void *cb_data; + + ClipboardErrorTrapPushFunc error_trap_push; + ClipboardErrorTrapPopFunc error_trap_pop; + + List *contents; + List *conversions; + + Window requestor; + Atom property; + Time time; +}; + +typedef struct +{ + unsigned char *data; + int length; + Atom target; + Atom type; + int format; + int refcount; +} TargetData; + +typedef struct +{ + Atom target; + TargetData *data; + Atom property; + Window requestor; + int offset; +} IncrConversion; + + +/* We need to use reference counting for the target data, since we may + * need to keep the data around after loosing the CLIPBOARD ownership + * to complete incremental transfers. + */ +static TargetData * +target_data_ref (TargetData *data) +{ + data->refcount++; + return data; +} + +static void +target_data_unref (TargetData *data) +{ + data->refcount--; + if (data->refcount == 0) + { + free (data->data); + free (data); + } +} + +static void +conversion_free (IncrConversion *rdata) +{ + if (rdata->data) + target_data_unref (rdata->data); + free (rdata); +} + +static void +send_selection_notify (ClipboardManager *manager, + Bool success) +{ + XSelectionEvent notify; + + notify.type = SelectionNotify; + notify.serial = 0; + notify.send_event = True; + notify.display = manager->display; + notify.requestor = manager->requestor; + notify.selection = XA_CLIPBOARD_MANAGER; + notify.target = XA_SAVE_TARGETS; + notify.property = success ? manager->property : None; + notify.time = manager->time; + + manager->error_trap_push (); + + XSendEvent (manager->display, manager->requestor, + False, NoEventMask, (XEvent *)¬ify); + XSync (manager->display, False); + + manager->error_trap_pop (); +} + +static void +finish_selection_request (ClipboardManager *manager, + XEvent *xev, + Bool success) +{ + XSelectionEvent notify; + + notify.type = SelectionNotify; + notify.serial = 0; + notify.send_event = True; + notify.display = xev->xselectionrequest.display; + notify.requestor = xev->xselectionrequest.requestor; + notify.selection = xev->xselectionrequest.selection; + notify.target = xev->xselectionrequest.target; + notify.property = success ? xev->xselectionrequest.property : None; + notify.time = xev->xselectionrequest.time; + + manager->error_trap_push (); + + XSendEvent (xev->xselectionrequest.display, + xev->xselectionrequest.requestor, + False, NoEventMask, (XEvent *)¬ify); + XSync (manager->display, False); + + manager->error_trap_pop (); +} + +static int +clipboard_bytes_per_item (int format) +{ + switch (format) + { + case 8: + return sizeof (char); + break; + case 16: + return sizeof (short); + break; + case 32: + return sizeof (long); + break; + default: ; + } + return 0; +} + +static void +save_targets (ClipboardManager *manager, + Atom *save_targets, + int nitems) +{ + int nout, i; + Atom *multiple; + TargetData *tdata; + + multiple = (Atom *) malloc (2 * nitems * sizeof (Atom)); + + nout = 0; + for (i = 0; i < nitems; i++) + { + if (save_targets[i] != XA_TARGETS && + save_targets[i] != XA_MULTIPLE && + save_targets[i] != XA_DELETE && + save_targets[i] != XA_INSERT_PROPERTY && + save_targets[i] != XA_INSERT_SELECTION && + save_targets[i] != XA_PIXMAP) + { + + tdata = (TargetData *) malloc (sizeof (TargetData)); + tdata->data = NULL; + tdata->length = 0; + tdata->target = save_targets[i]; + tdata->type = None; + tdata->format = 0; + tdata->refcount = 1; + manager->contents = list_prepend (manager->contents, tdata); + + multiple[nout++] = save_targets[i]; + multiple[nout++] = save_targets[i]; + } + } + + XFree (save_targets); + + XChangeProperty (manager->display, manager->window, + XA_MULTIPLE, XA_ATOM_PAIR, + 32, PropModeReplace, (char *)multiple, nout); + free (multiple); + + XConvertSelection (manager->display, XA_CLIPBOARD, + XA_MULTIPLE, XA_MULTIPLE, + manager->window, manager->time); +} + +static int +find_content_target (TargetData *tdata, + Atom target) +{ + return tdata->target == target; +} + +static int +find_content_type (TargetData *tdata, + Atom type) +{ + return tdata->type == type; +} + +static int +find_conversion_requestor (IncrConversion *rdata, + XEvent *xev) +{ + return (rdata->requestor == xev->xproperty.window && + rdata->property == xev->xproperty.atom); +} + +static void +get_property (TargetData *tdata, + ClipboardManager *manager) +{ + Atom type; + int format; + unsigned long length; + unsigned long remaining; + unsigned char *data; + + XGetWindowProperty (manager->display, + manager->window, + tdata->target, + 0, 0x1FFFFFFF, True, AnyPropertyType, + &type, &format, &length, &remaining, + &data); + + if (type == None) + { + manager->contents = list_remove (manager->contents, tdata); + free (tdata); + } + else if (type == XA_INCR) + { + tdata->type = type; + tdata->length = 0; + XFree (data); + } + else + { + tdata->type = type; + tdata->data = data; + tdata->length = length * clipboard_bytes_per_item (format); + tdata->format = format; + } +} + +static Bool +receive_incrementally (ClipboardManager *manager, + XEvent *xev) +{ + List *list; + TargetData *tdata; + Atom type; + int format; + unsigned long length, nitems, remaining; + unsigned char *data; + + if (xev->xproperty.window != manager->window) + return False; + + list = list_find (manager->contents, + (ListFindFunc)find_content_target, (void *)xev->xproperty.atom); + + if (!list) + return False; + + tdata = (TargetData *)list->data; + + if (tdata->type != XA_INCR) + return False; + + XGetWindowProperty (xev->xproperty.display, + xev->xproperty.window, + xev->xproperty.atom, + 0, 0x1FFFFFFF, True, AnyPropertyType, + &type, &format, &nitems, &remaining, &data); + + length = nitems * clipboard_bytes_per_item (format); + + if (length == 0) + { + tdata->type = type; + tdata->format = format; + + if (!list_find (manager->contents, + (ListFindFunc)find_content_type, (void *)XA_INCR)) + { + /* all incremental transfers done */ + send_selection_notify (manager, True); + manager->requestor = None; + } + + XFree (data); + } + else + { + if (!tdata->data) + { + tdata->data = data; + tdata->length = length; + } + else + { + tdata->data = realloc (tdata->data, tdata->length + length + 1); + memcpy (tdata->data + tdata->length, data, length + 1); + tdata->length += length; + XFree (data); + } + } + + return True; +} + +static Bool +send_incrementally (ClipboardManager *manager, + XEvent *xev) +{ + List *list; + IncrConversion *rdata; + unsigned long length, items; + unsigned char *data; + + list = list_find (manager->conversions, + (ListFindFunc)find_conversion_requestor, xev); + + if (list == NULL) + return False; + + rdata = (IncrConversion *)list->data; + + data = rdata->data->data + rdata->offset; + length = rdata->data->length - rdata->offset; + if (length > SELECTION_MAX_SIZE) + length = SELECTION_MAX_SIZE; + + rdata->offset += length; + + items = length / clipboard_bytes_per_item (rdata->data->format); + XChangeProperty (manager->display, rdata->requestor, + rdata->property, rdata->data->type, + rdata->data->format, PropModeAppend, + data, items); + + if (length == 0) + { + manager->conversions = list_remove (manager->conversions, rdata); + conversion_free (rdata); + } + + return True; +} + +static void +convert_clipboard_manager (ClipboardManager *manager, + XEvent *xev) +{ + Atom type = None; + int format; + unsigned long nitems, remaining; + Atom *targets = NULL; + + if (xev->xselectionrequest.target == XA_SAVE_TARGETS) + { + if (manager->requestor != None || manager->contents != NULL) + { + /* We're in the middle of a conversion request, or own + * the CLIPBOARD already + */ + finish_selection_request (manager, xev, False); + } + else + { + manager->error_trap_push (); + + manager->watch (xev->xselectionrequest.requestor, True, StructureNotifyMask, manager->cb_data); + XSelectInput (manager->display, xev->xselectionrequest.requestor, + StructureNotifyMask); + XSync (manager->display, False); + + if (manager->error_trap_pop () != Success) + return; + + manager->error_trap_push (); + + if (xev->xselectionrequest.property != None) + { + XGetWindowProperty (manager->display, xev->xselectionrequest.requestor, + xev->xselectionrequest.property, + 0, 0x1FFFFFFF, False, XA_ATOM, + &type, &format, &nitems, &remaining, + (unsigned char **)&targets); + + if (manager->error_trap_pop () != Success) + { + if (targets) + XFree (targets); + + return; + } + } + + manager->requestor = xev->xselectionrequest.requestor; + manager->property = xev->xselectionrequest.property; + manager->time = xev->xselectionrequest.time; + + if (type == None) + XConvertSelection (manager->display, XA_CLIPBOARD, + XA_TARGETS, XA_TARGETS, + manager->window, manager->time); + else + save_targets (manager, targets, nitems); + } + } + else if (xev->xselectionrequest.target == XA_TIMESTAMP) + { + XChangeProperty (manager->display, + xev->xselectionrequest.requestor, + xev->xselectionrequest.property, + XA_INTEGER, 32, PropModeReplace, + (unsigned char *)&manager->timestamp, 1); + + finish_selection_request (manager, xev, True); + } + else if (xev->xselectionrequest.target == XA_TARGETS) + { + int n_targets = 0; + Atom targets[3]; + + targets[n_targets++] = XA_TARGETS; + targets[n_targets++] = XA_TIMESTAMP; + targets[n_targets++] = XA_SAVE_TARGETS; + + XChangeProperty (manager->display, + xev->xselectionrequest.requestor, + xev->xselectionrequest.property, + XA_ATOM, 32, PropModeReplace, + (unsigned char *)targets, n_targets); + + finish_selection_request (manager, xev, True); + } + else + { + finish_selection_request (manager, xev, False); + } +} + +static void +convert_clipboard_target (IncrConversion *rdata, + ClipboardManager *manager) +{ + TargetData *tdata; + Atom *targets; + int n_targets; + List *list; + unsigned long items; + XWindowAttributes atts; + + if (rdata->target == XA_TARGETS) + { + n_targets = list_length (manager->contents) + 2; + targets = (Atom *) malloc (n_targets * sizeof (Atom)); + + n_targets = 0; + + targets[n_targets++] = XA_TARGETS; + targets[n_targets++] = XA_MULTIPLE; + + for (list = manager->contents; list; list = list->next) + { + tdata = (TargetData *)list->data; + targets[n_targets++] = tdata->target; + } + + XChangeProperty (manager->display, rdata->requestor, + rdata->property, + XA_ATOM, 32, PropModeReplace, + (unsigned char *)targets, n_targets); + free (targets); + } + else + { + /* Convert from stored CLIPBOARD data */ + list = list_find (manager->contents, + (ListFindFunc)find_content_target, (void *)rdata->target); + + /* We got a target that we don't support */ + if (!list) + return; + + tdata = (TargetData *)list->data; + + if (tdata->type == XA_INCR) + { + /* we haven't completely received this target yet + */ + rdata->property = None; + return; + } + + rdata->data = target_data_ref (tdata); + items = tdata->length / clipboard_bytes_per_item (tdata->format); + if (tdata->length <= SELECTION_MAX_SIZE) + XChangeProperty (manager->display, rdata->requestor, + rdata->property, + tdata->type, tdata->format, PropModeReplace, + tdata->data, items); + else + { + /* start incremental transfer + */ + rdata->offset = 0; + + manager->error_trap_push (); + + XGetWindowAttributes (manager->display, rdata->requestor, &atts); + XSelectInput (manager->display, rdata->requestor, + atts.your_event_mask | PropertyChangeMask); + + XChangeProperty (manager->display, rdata->requestor, + rdata->property, + XA_INCR, 32, PropModeReplace, + (unsigned char *)&items, 1); + + XSync (manager->display, False); + + manager->error_trap_pop (); + } + } +} + +static void +collect_incremental (IncrConversion *rdata, + ClipboardManager *manager) +{ + if (rdata->offset >= 0) + manager->conversions = list_prepend (manager->conversions, rdata); + else + { + if (rdata->data) + { + target_data_unref (rdata->data); + rdata->data = NULL; + } + free (rdata); + } +} + +static void +convert_clipboard (ClipboardManager *manager, + XEvent *xev) +{ + List *list, *conversions; + IncrConversion *rdata; + Atom type; + int i, format; + unsigned long nitems, remaining; + Atom *multiple; + + conversions = NULL; + type = None; + + if (xev->xselectionrequest.target == XA_MULTIPLE) + { + + XGetWindowProperty (xev->xselectionrequest.display, + xev->xselectionrequest.requestor, + xev->xselectionrequest.property, + 0, 0x1FFFFFFF, False, XA_ATOM_PAIR, + &type, &format, &nitems, &remaining, + (unsigned char **)&multiple); + + + + if (type != XA_ATOM_PAIR) + return; + + for (i = 0; i < nitems; i += 2) + { + rdata = (IncrConversion *) malloc (sizeof (IncrConversion)); + rdata->requestor = xev->xselectionrequest.requestor; + rdata->target = multiple[i]; + rdata->property = multiple[i+1]; + rdata->data = NULL; + rdata->offset = -1; + conversions = list_prepend (conversions, rdata); + } + } + else + { + multiple = NULL; + + rdata = (IncrConversion *) malloc (sizeof (IncrConversion)); + rdata->requestor = xev->xselectionrequest.requestor; + rdata->target = xev->xselectionrequest.target; + rdata->property = xev->xselectionrequest.property; + rdata->data = NULL; + rdata->offset = -1; + conversions = list_prepend (conversions, rdata); + } + + list_foreach (conversions, (Callback)convert_clipboard_target, manager); + + if (conversions->next == NULL && + ((IncrConversion *)conversions->data)->property == None) + { + finish_selection_request (manager, xev, False); + } + else + { + if (multiple) + { + i = 0; + for (list = conversions; list; list = list->next) + { + rdata = (IncrConversion *)list->data; + multiple[i++] = rdata->target; + multiple[i++] = rdata->property; + } + XChangeProperty (xev->xselectionrequest.display, + xev->xselectionrequest.requestor, + xev->xselectionrequest.property, + XA_ATOM_PAIR, 32, PropModeReplace, + (unsigned char *)multiple, nitems); + } + finish_selection_request (manager, xev, True); + } + + list_foreach (conversions, (Callback)collect_incremental, manager); + list_free (conversions); + + if (multiple) + free (multiple); +} + +Bool +clipboard_manager_process_event (ClipboardManager *manager, + XEvent *xev) +{ + Atom type; + int format; + unsigned long nitems; + unsigned long remaining; + Atom *targets; + + targets = NULL; + + switch (xev->xany.type) + { + case DestroyNotify: + if (xev->xdestroywindow.window == manager->requestor) + { + list_foreach (manager->contents, (Callback)target_data_unref, NULL); + list_free (manager->contents); + manager->contents = NULL; + + manager->watch (manager->requestor, False, 0, manager->cb_data); + manager->requestor = None; + } + break; + case PropertyNotify: + + if (xev->xproperty.state == PropertyNewValue) + return receive_incrementally (manager, xev); + else + return send_incrementally (manager, xev); + break; + + case SelectionClear: + if (xev->xany.window != manager->window) + return False; + + if (xev->xselectionclear.selection == XA_CLIPBOARD_MANAGER) + { + /* We lost the manager selection */ + if (manager->contents) + { + list_foreach (manager->contents, (Callback)target_data_unref, NULL); + list_free (manager->contents); + manager->contents = NULL; + + XSetSelectionOwner (manager->display, + XA_CLIPBOARD, + None, manager->time); + } + manager->terminate (manager->cb_data); + + return True; + } + if (xev->xselectionclear.selection == XA_CLIPBOARD) + { + /* We lost the clipboard selection */ + list_foreach (manager->contents, (Callback)target_data_unref, NULL); + list_free (manager->contents); + manager->contents = NULL; + manager->watch (manager->requestor, False, 0, manager->cb_data); + manager->requestor = None; + + return True; + } + break; + + case SelectionNotify: + if (xev->xany.window != manager->window) + return False; + + if (xev->xselection.selection == XA_CLIPBOARD) + { + /* a CLIPBOARD conversion is done */ + if (xev->xselection.property == XA_TARGETS) + { + XGetWindowProperty (xev->xselection.display, + xev->xselection.requestor, + xev->xselection.property, + 0, 0x1FFFFFFF, True, XA_ATOM, + &type, &format, &nitems, &remaining, + (unsigned char **)&targets); + + save_targets (manager, targets, nitems); + } + else if (xev->xselection.property == XA_MULTIPLE) + { + List *tmp; + + tmp = list_copy (manager->contents); + list_foreach (tmp, (Callback)get_property, manager); + list_free (tmp); + + manager->time = xev->xselection.time; + XSetSelectionOwner (manager->display, XA_CLIPBOARD, + manager->window, manager->time); + + if (manager->property != None) + XChangeProperty (manager->display, manager->requestor, + manager->property, + XA_ATOM, 32, PropModeReplace, + (unsigned char *)&XA_NULL, 1); + + if (!list_find (manager->contents, + (ListFindFunc)find_content_type, (void *)XA_INCR)) + { + /* all transfers done */ + send_selection_notify (manager, True); + manager->watch (manager->requestor, False, 0, manager->cb_data); + manager->requestor = None; + } + } + else if (xev->xselection.property == None) + { + send_selection_notify (manager, False); + manager->watch (manager->requestor, False, 0, manager->cb_data); + manager->requestor = None; + } + + return True; + } + break; + + case SelectionRequest: + if (xev->xany.window != manager->window) + return False; + + if (xev->xselectionrequest.selection == XA_CLIPBOARD_MANAGER) + { + convert_clipboard_manager (manager, xev); + return True; + } + else if (xev->xselectionrequest.selection == XA_CLIPBOARD) + { + convert_clipboard (manager, xev); + return True; + } + break; + + default: ; + } + + return False; +} + +Bool +clipboard_manager_check_running (Display *display) +{ + init_atoms (display); + + if (XGetSelectionOwner (display, XA_CLIPBOARD_MANAGER)) + return True; + else + return False; +} + +ClipboardManager * +clipboard_manager_new (Display *display, + ClipboardErrorTrapPushFunc error_trap_push_cb, + ClipboardErrorTrapPopFunc error_trap_pop_cb, + ClipboardTerminateFunc terminate, + ClipboardWatchFunc watch, + void *cb_data) +{ + ClipboardManager *manager; + XClientMessageEvent xev; + + init_atoms (display); + + manager = malloc (sizeof *manager); + if (!manager) + return NULL; + + manager->display = display; + + manager->error_trap_push = error_trap_push_cb; + manager->error_trap_pop = error_trap_pop_cb; + + manager->terminate = terminate; + manager->watch = watch; + manager->cb_data = cb_data; + + manager->contents = NULL; + manager->conversions = NULL; + + manager->requestor = None; + + manager->window = XCreateSimpleWindow (display, + DefaultRootWindow (display), + 0, 0, 10, 10, 0, + WhitePixel (display, DefaultScreen (display)), + WhitePixel (display, DefaultScreen (display))); + + manager->watch (manager->window, True, PropertyChangeMask, manager->cb_data); + XSelectInput (display, manager->window, PropertyChangeMask); + manager->timestamp = get_server_time (display, manager->window); + + XSetSelectionOwner (display, XA_CLIPBOARD_MANAGER, + manager->window, manager->timestamp); + + /* Check to see if we managed to claim the selection. If not, + * we treat it as if we got it then immediately lost it + */ + + if (XGetSelectionOwner (display, XA_CLIPBOARD_MANAGER) == manager->window) + { + xev.type = ClientMessage; + xev.window = DefaultRootWindow (display); + xev.message_type = XA_MANAGER; + xev.format = 32; + xev.data.l[0] = manager->timestamp; + xev.data.l[1] = XA_CLIPBOARD_MANAGER; + xev.data.l[2] = manager->window; + xev.data.l[3] = 0; /* manager specific data */ + xev.data.l[4] = 0; /* manager specific data */ + + XSendEvent (display, DefaultRootWindow (display), + False, StructureNotifyMask, (XEvent *)&xev); + } + else + { + manager->watch (manager->window, False, 0, manager->cb_data); + manager->terminate (manager->cb_data); + free (manager); + manager = NULL; + } + + return manager; +} + +void +clipboard_manager_destroy (ClipboardManager *manager) +{ + if (manager) + { + manager->watch (manager->window, False, 0, manager->cb_data); + XDestroyWindow (manager->display, manager->window); + + list_foreach (manager->conversions, (Callback)conversion_free, NULL); + list_free (manager->conversions); + + list_foreach (manager->contents, (Callback)target_data_unref, NULL); + list_free (manager->contents); + + free (manager); + manager = NULL; + } +} + diff --git a/gnome-settings-daemon/clipboard-manager.h b/gnome-settings-daemon/clipboard-manager.h new file mode 100644 index 000000000..77ae12708 --- /dev/null +++ b/gnome-settings-daemon/clipboard-manager.h @@ -0,0 +1,51 @@ +/* + * Copyright © 2004 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Red Hat not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Red Hat makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT + * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: Matthias Clasen, Red Hat, Inc. + */ +#ifndef CLIPBOARD_MANAGER_H +#define CLIPBOARD_MANAGER_H + +#include + +typedef struct _ClipboardManager ClipboardManager; +typedef void (*ClipboardTerminateFunc) (void *data); +typedef void (*ClipboardWatchFunc) (Window window, + Bool is_start, + long mask, + void *cb_data); + +typedef void (*ClipboardErrorTrapPushFunc) (void); +typedef int (*ClipboardErrorTrapPopFunc) (void); + +ClipboardManager *clipboard_manager_new (Display *display, + ClipboardErrorTrapPushFunc error_trap_push_cb, + ClipboardErrorTrapPopFunc error_trap_pop_cb, + ClipboardTerminateFunc terminate_cb, + ClipboardWatchFunc watch_cb, + void *cb_data); + +void clipboard_manager_destroy (ClipboardManager *manager); +Bool clipboard_manager_process_event (ClipboardManager *manager, + XEvent *xev); +Bool clipboard_manager_check_running (Display *display); + + +#endif /* CLIPBOARD_MANAGER_H */ diff --git a/gnome-settings-daemon/gnome-settings-daemon.c b/gnome-settings-daemon/gnome-settings-daemon.c index d70430f40..cc22c9ba2 100644 --- a/gnome-settings-daemon/gnome-settings-daemon.c +++ b/gnome-settings-daemon/gnome-settings-daemon.c @@ -61,6 +61,8 @@ #include "GNOME_SettingsDaemon.h" +#include "clipboard-manager.h" + static GObjectClass *parent_class = NULL; struct _GnomeSettingsDaemonPrivate { @@ -69,6 +71,7 @@ struct _GnomeSettingsDaemonPrivate { static GSList *directories = NULL; XSettingsManager **managers = NULL; +static ClipboardManager *clipboard_manager; typedef struct DirElement { @@ -136,6 +139,53 @@ config_notify (GConfClient *client, } } +static void +clipboard_manager_terminate_cb (void *data) +{ + /* Do nothing */ +} + +static GdkFilterReturn +clipboard_manager_event_filter (GdkXEvent *xevent, + GdkEvent *event, + gpointer data) +{ + if (clipboard_manager_process_event (clipboard_manager, + (XEvent *)xevent)) + return GDK_FILTER_REMOVE; + else + return GDK_FILTER_CONTINUE; +} + +static void +clipboard_manager_watch_cb (Window window, + Bool is_start, + long mask, + void *cb_data) +{ + GdkWindow *gdkwin; + GdkDisplay *display; + + display = gdk_display_get_default (); + gdkwin = gdk_window_lookup_for_display (display, window); + + if (is_start) + { + if (!gdkwin) + gdkwin = gdk_window_foreign_new_for_display (display, window); + else + g_object_ref (gdkwin); + + gdk_window_add_filter (gdkwin, clipboard_manager_event_filter, NULL); + } + else + { + g_assert (gdkwin); + gdk_window_remove_filter (gdkwin, clipboard_manager_event_filter, NULL); + g_object_unref (gdkwin); + } +} + static void terminate_cb (void *data) { @@ -189,6 +239,8 @@ finalize (GObject *object) for (i = 0; managers && managers [i]; i++) xsettings_manager_destroy (managers [i]); + clipboard_manager_destroy (clipboard_manager); + g_free (daemon->private); daemon->private = NULL; @@ -241,7 +293,8 @@ gnome_settings_daemon_new (void) fprintf (stderr, "You can only run one xsettings manager at a time; exiting\n"); exit (1); } - + + if (!terminated) { managers = g_new (XSettingsManager *, n_screens + 1); @@ -267,6 +320,16 @@ gnome_settings_daemon_new (void) managers [i] = NULL; } + if (!clipboard_manager_check_running (GDK_DISPLAY_XDISPLAY (display))) + { + clipboard_manager = clipboard_manager_new (GDK_DISPLAY_XDISPLAY (display), + gdk_error_trap_push, + gdk_error_trap_pop, + clipboard_manager_terminate_cb, + clipboard_manager_watch_cb, + NULL); + } + /* We use GConfClient not GConfClient because a cache isn't useful * for us */ @@ -316,7 +379,7 @@ gnome_settings_daemon_new (void) g_error_free (error); } } - + for (i = 0; i < n_screens; i++) { GdkScreen *screen; diff --git a/gnome-settings-daemon/list.c b/gnome-settings-daemon/list.c new file mode 100644 index 000000000..477eead77 --- /dev/null +++ b/gnome-settings-daemon/list.c @@ -0,0 +1,150 @@ +/* + * Copyright © 2004 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Red Hat not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Red Hat makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT + * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: Matthias Clasen, Red Hat, Inc. + */ + +#include +#include + + +void +list_foreach (List *list, + Callback func, + void *user_data) +{ + while (list) + { + func (list->data, user_data); + + list = list->next; + } +} + +List * +list_prepend (List *list, + void *data) +{ + List *link; + + link = (List *) malloc (sizeof (List)); + link->next = list; + link->data = data; + + return link; +} + +void +list_free (List *list) +{ + while (list) + { + List *next = list->next; + + free (list); + + list = next; + } +} + +List * +list_find (List *list, + ListFindFunc func, + void *user_data) +{ + List *tmp; + + for (tmp = list; tmp; tmp = tmp->next) + { + if ((*func) (tmp->data, user_data)) + break; + } + + return tmp; +} + +List * +list_remove (List *list, + void *data) +{ + List *tmp, *prev; + + prev = NULL; + for (tmp = list; tmp; tmp = tmp->next) + { + if (tmp->data == data) + { + if (prev) + prev->next = tmp->next; + else + list = tmp->next; + + free (tmp); + break; + } + + prev = tmp; + } + + return list; +} + +int +list_length (List *list) +{ + List *tmp; + int length; + + length = 0; + for (tmp = list; tmp; tmp = tmp->next) + length++; + + return length; +} + +List * +list_copy (List *list) +{ + List *new_list = NULL; + + if (list) + { + List *last; + + new_list = (List *) malloc (sizeof (List)); + new_list->data = list->data; + new_list->next = NULL; + + last = new_list; + list = list->next; + + while (list) + { + last->next = (List *) malloc (sizeof (List)); + last = last->next; + last->data = list->data; + list = list->next; + } + + last->next = NULL; + } + + return new_list; +} diff --git a/gnome-settings-daemon/list.h b/gnome-settings-daemon/list.h new file mode 100644 index 000000000..158b77937 --- /dev/null +++ b/gnome-settings-daemon/list.h @@ -0,0 +1,57 @@ +/* + * Copyright © 2004 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Red Hat not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Red Hat makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT + * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: Matthias Clasen, Red Hat, Inc. + */ +#ifndef LIST_H +#define LIST_H + + +typedef struct _List List; +typedef void (*Callback) (void *data, + void *user_data); + + +struct _List +{ + void *data; + + List *next; +}; + +typedef int (*ListFindFunc) (void *data, + void *user_data); + +void list_foreach (List *list, + Callback func, + void *user_data); +List *list_prepend (List *list, + void *data); +void list_free (List *list); +List *list_find (List *list, + ListFindFunc func, + void *user_data); +List *list_remove (List *list, + void *data); +int list_length (List *list); + +List *list_copy (List *list); + +#endif /* LIST_H */ diff --git a/gnome-settings-daemon/xutils.c b/gnome-settings-daemon/xutils.c new file mode 100644 index 000000000..4e48b98f0 --- /dev/null +++ b/gnome-settings-daemon/xutils.c @@ -0,0 +1,117 @@ +/* + * Copyright © 2004 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Red Hat not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Red Hat makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT + * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: Matthias Clasen, Red Hat, Inc. + */ + +#include + +#include "xutils.h" + +Atom XA_ATOM_PAIR; +Atom XA_CLIPBOARD_MANAGER; +Atom XA_CLIPBOARD; +Atom XA_DELETE; +Atom XA_INCR; +Atom XA_INSERT_PROPERTY; +Atom XA_INSERT_SELECTION; +Atom XA_MANAGER; +Atom XA_MULTIPLE; +Atom XA_NULL; +Atom XA_SAVE_TARGETS; +Atom XA_TARGETS; +Atom XA_TIMESTAMP; + +unsigned long SELECTION_MAX_SIZE = 0; + + +void +init_atoms (Display *display) +{ + unsigned long max_request_size; + + if (SELECTION_MAX_SIZE > 0) + return; + + XA_ATOM_PAIR = XInternAtom (display, "ATOM_PAIR", False); + XA_CLIPBOARD_MANAGER = XInternAtom (display, "CLIPBOARD_MANAGER", False); + XA_CLIPBOARD = XInternAtom (display, "CLIPBOARD", False); + XA_DELETE = XInternAtom (display, "DELETE", False); + XA_INCR = XInternAtom (display, "INCR", False); + XA_INSERT_PROPERTY = XInternAtom (display, "INSERT_PROPERTY", False); + XA_INSERT_SELECTION = XInternAtom (display, "INSERT_SELECTION", False); + XA_MANAGER = XInternAtom (display, "MANAGER", False); + XA_MULTIPLE = XInternAtom (display, "MULTIPLE", False); + XA_NULL = XInternAtom (display, "NULL", False); + XA_SAVE_TARGETS = XInternAtom (display, "SAVE_TARGETS", False); + XA_TARGETS = XInternAtom (display, "TARGETS", False); + XA_TIMESTAMP = XInternAtom (display, "TIMESTAMP", False); + + max_request_size = XExtendedMaxRequestSize (display); + if (max_request_size == 0) + max_request_size = XMaxRequestSize (display); + + SELECTION_MAX_SIZE = max_request_size - 100; + if (SELECTION_MAX_SIZE > 262144) + SELECTION_MAX_SIZE = 262144; +} + +typedef struct +{ + Window window; + Atom timestamp_prop_atom; +} TimeStampInfo; + +static Bool +timestamp_predicate (Display *display, + XEvent *xevent, + XPointer arg) +{ + TimeStampInfo *info = (TimeStampInfo *)arg; + + if (xevent->type == PropertyNotify && + xevent->xproperty.window == info->window && + xevent->xproperty.atom == info->timestamp_prop_atom) + return True; + + return False; +} + +Time +get_server_time (Display *display, + Window window) +{ + unsigned char c = 'a'; + XEvent xevent; + TimeStampInfo info; + + info.timestamp_prop_atom = XInternAtom (display, "_TIMESTAMP_PROP", False); + info.window = window; + + XChangeProperty (display, window, + info.timestamp_prop_atom, info.timestamp_prop_atom, + 8, PropModeReplace, &c, 1); + + XIfEvent (display, &xevent, + timestamp_predicate, (XPointer)&info); + + return xevent.xproperty.time; +} + diff --git a/gnome-settings-daemon/xutils.h b/gnome-settings-daemon/xutils.h new file mode 100644 index 000000000..143fe443a --- /dev/null +++ b/gnome-settings-daemon/xutils.h @@ -0,0 +1,50 @@ +/* + * Copyright © 2004 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Red Hat not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Red Hat makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT + * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: Matthias Clasen, Red Hat, Inc. + */ +#ifndef X_UTILS_H +#define X_UTILS_H + +#include + + +extern Atom XA_ATOM_PAIR; +extern Atom XA_CLIPBOARD_MANAGER; +extern Atom XA_CLIPBOARD; +extern Atom XA_DELETE; +extern Atom XA_INCR; +extern Atom XA_INSERT_PROPERTY; +extern Atom XA_INSERT_SELECTION; +extern Atom XA_MANAGER; +extern Atom XA_MULTIPLE; +extern Atom XA_NULL; +extern Atom XA_SAVE_TARGETS; +extern Atom XA_TARGETS; +extern Atom XA_TIMESTAMP; + +extern unsigned long SELECTION_MAX_SIZE; + +void init_atoms (Display *display); + +Time get_server_time (Display *display, + Window window); + +#endif /* X_UTILS_H */