~ctf/unity-settings-daemon/bug1389099_mic_volume_icons

« back to all changes in this revision

Viewing changes to plugins/clipboard/gsd-clipboard-manager.c

  • Committer: Package Import Robot
  • Author(s): Robert Ancell
  • Date: 2014-02-07 11:44:36 UTC
  • Revision ID: package-import@ubuntu.com-20140207114436-7t5u3yvwc4ul7w3e
Tags: upstream-14.04.0
ImportĀ upstreamĀ versionĀ 14.04.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 
2
 *
 
3
 * Copyright (C) 2007 Matthias Clasen
 
4
 * Copyright (C) 2007 Anders Carlsson
 
5
 * Copyright (C) 2007 Rodrigo Moya
 
6
 * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
 
7
 *
 
8
 * This program is free software; you can redistribute it and/or modify
 
9
 * it under the terms of the GNU General Public License as published by
 
10
 * the Free Software Foundation; either version 2 of the License, or
 
11
 * (at your option) any later version.
 
12
 *
 
13
 * This program is distributed in the hope that it will be useful,
 
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
 * GNU General Public License for more details.
 
17
 *
 
18
 * You should have received a copy of the GNU General Public License
 
19
 * along with this program; if not, write to the Free Software
 
20
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
21
 *
 
22
 */
 
23
 
 
24
#include "config.h"
 
25
 
 
26
#include <sys/types.h>
 
27
#include <sys/wait.h>
 
28
#include <stdlib.h>
 
29
#include <stdio.h>
 
30
#include <unistd.h>
 
31
#include <string.h>
 
32
#include <errno.h>
 
33
 
 
34
#include <locale.h>
 
35
 
 
36
#include <glib.h>
 
37
#include <glib/gi18n.h>
 
38
#include <gdk/gdk.h>
 
39
#include <gdk/gdkx.h>
 
40
#include <gtk/gtk.h>
 
41
#include <X11/Xlib.h>
 
42
#include <X11/Xatom.h>
 
43
 
 
44
#include "xutils.h"
 
45
#include "list.h"
 
46
 
 
47
#include "gnome-settings-profile.h"
 
48
#include "gsd-clipboard-manager.h"
 
49
 
 
50
#define GSD_CLIPBOARD_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_CLIPBOARD_MANAGER, GsdClipboardManagerPrivate))
 
51
 
 
52
struct GsdClipboardManagerPrivate
 
53
{
 
54
        guint    start_idle_id;
 
55
        Display *display;
 
56
        Window   window;
 
57
        Time     timestamp;
 
58
 
 
59
        List    *contents;
 
60
        List    *conversions;
 
61
 
 
62
        Window   requestor;
 
63
        Atom     property;
 
64
        Time     time;
 
65
};
 
66
 
 
67
typedef struct
 
68
{
 
69
        unsigned char *data;
 
70
        int            length;
 
71
        Atom           target;
 
72
        Atom           type;
 
73
        int            format;
 
74
        int            refcount;
 
75
} TargetData;
 
76
 
 
77
typedef struct
 
78
{
 
79
        Atom        target;
 
80
        TargetData *data;
 
81
        Atom        property;
 
82
        Window      requestor;
 
83
        int         offset;
 
84
} IncrConversion;
 
85
 
 
86
static void     gsd_clipboard_manager_class_init  (GsdClipboardManagerClass *klass);
 
87
static void     gsd_clipboard_manager_init        (GsdClipboardManager      *clipboard_manager);
 
88
static void     gsd_clipboard_manager_finalize    (GObject                  *object);
 
89
 
 
90
static void     clipboard_manager_watch_cb        (GsdClipboardManager *manager,
 
91
                                                   Window               window,
 
92
                                                   Bool                 is_start,
 
93
                                                   long                 mask,
 
94
                                                   void                *cb_data);
 
95
 
 
96
G_DEFINE_TYPE (GsdClipboardManager, gsd_clipboard_manager, G_TYPE_OBJECT)
 
97
 
 
98
static gpointer manager_object = NULL;
 
99
 
 
100
/* We need to use reference counting for the target data, since we may
 
101
 * need to keep the data around after loosing the CLIPBOARD ownership
 
102
 * to complete incremental transfers.
 
103
 */
 
104
static TargetData *
 
105
target_data_ref (TargetData *data)
 
106
{
 
107
        data->refcount++;
 
108
        return data;
 
109
}
 
110
 
 
111
static void
 
112
target_data_unref (TargetData *data)
 
113
{
 
114
        data->refcount--;
 
115
        if (data->refcount == 0) {
 
116
                free (data->data);
 
117
                free (data);
 
118
        }
 
119
}
 
120
 
 
121
static void
 
122
conversion_free (IncrConversion *rdata)
 
123
{
 
124
        if (rdata->data) {
 
125
                target_data_unref (rdata->data);
 
126
        }
 
127
        free (rdata);
 
128
}
 
129
 
 
130
static void
 
131
send_selection_notify (GsdClipboardManager *manager,
 
132
                       Bool                 success)
 
133
{
 
134
        XSelectionEvent notify;
 
135
 
 
136
        notify.type = SelectionNotify;
 
137
        notify.serial = 0;
 
138
        notify.send_event = True;
 
139
        notify.display = manager->priv->display;
 
140
        notify.requestor = manager->priv->requestor;
 
141
        notify.selection = XA_CLIPBOARD_MANAGER;
 
142
        notify.target = XA_SAVE_TARGETS;
 
143
        notify.property = success ? manager->priv->property : None;
 
144
        notify.time = manager->priv->time;
 
145
 
 
146
        gdk_error_trap_push ();
 
147
 
 
148
        XSendEvent (manager->priv->display,
 
149
                    manager->priv->requestor,
 
150
                    False,
 
151
                    NoEventMask,
 
152
                    (XEvent *)&notify);
 
153
        XSync (manager->priv->display, False);
 
154
 
 
155
        gdk_error_trap_pop_ignored ();
 
156
}
 
157
 
 
158
static void
 
159
finish_selection_request (GsdClipboardManager *manager,
 
160
                          XEvent              *xev,
 
161
                          Bool                 success)
 
162
{
 
163
        XSelectionEvent notify;
 
164
 
 
165
        notify.type = SelectionNotify;
 
166
        notify.serial = 0;
 
167
        notify.send_event = True;
 
168
        notify.display = xev->xselectionrequest.display;
 
169
        notify.requestor = xev->xselectionrequest.requestor;
 
170
        notify.selection = xev->xselectionrequest.selection;
 
171
        notify.target = xev->xselectionrequest.target;
 
172
        notify.property = success ? xev->xselectionrequest.property : None;
 
173
        notify.time = xev->xselectionrequest.time;
 
174
 
 
175
        gdk_error_trap_push ();
 
176
 
 
177
        XSendEvent (xev->xselectionrequest.display,
 
178
                    xev->xselectionrequest.requestor,
 
179
                    False, NoEventMask, (XEvent *) &notify);
 
180
        XSync (manager->priv->display, False);
 
181
 
 
182
        gdk_error_trap_pop_ignored ();
 
183
}
 
184
 
 
185
static int
 
186
clipboard_bytes_per_item (int format)
 
187
{
 
188
        switch (format) {
 
189
        case 8: return sizeof (char);
 
190
        case 16: return sizeof (short);
 
191
        case 32: return sizeof (long);
 
192
        default: ;
 
193
        }
 
194
 
 
195
        return 0;
 
196
}
 
197
 
 
198
static void
 
199
save_targets (GsdClipboardManager *manager,
 
200
              Atom                *save_targets,
 
201
              int                  nitems)
 
202
{
 
203
        int         nout, i;
 
204
        Atom       *multiple;
 
205
        TargetData *tdata;
 
206
 
 
207
        multiple = (Atom *) malloc (2 * nitems * sizeof (Atom));
 
208
 
 
209
        nout = 0;
 
210
        for (i = 0; i < nitems; i++) {
 
211
                if (save_targets[i] != XA_TARGETS &&
 
212
                    save_targets[i] != XA_MULTIPLE &&
 
213
                    save_targets[i] != XA_DELETE &&
 
214
                    save_targets[i] != XA_INSERT_PROPERTY &&
 
215
                    save_targets[i] != XA_INSERT_SELECTION &&
 
216
                    save_targets[i] != XA_PIXMAP) {
 
217
                        tdata = (TargetData *) malloc (sizeof (TargetData));
 
218
                        tdata->data = NULL;
 
219
                        tdata->length = 0;
 
220
                        tdata->target = save_targets[i];
 
221
                        tdata->type = None;
 
222
                        tdata->format = 0;
 
223
                        tdata->refcount = 1;
 
224
                        manager->priv->contents = list_prepend (manager->priv->contents, tdata);
 
225
 
 
226
                        multiple[nout++] = save_targets[i];
 
227
                        multiple[nout++] = save_targets[i];
 
228
                }
 
229
        }
 
230
 
 
231
        XFree (save_targets);
 
232
 
 
233
        XChangeProperty (manager->priv->display, manager->priv->window,
 
234
                         XA_MULTIPLE, XA_ATOM_PAIR,
 
235
                         32, PropModeReplace, (const unsigned char *) multiple, nout);
 
236
        free (multiple);
 
237
 
 
238
        XConvertSelection (manager->priv->display, XA_CLIPBOARD,
 
239
                           XA_MULTIPLE, XA_MULTIPLE,
 
240
                           manager->priv->window, manager->priv->time);
 
241
}
 
242
 
 
243
static int
 
244
find_content_target (TargetData *tdata,
 
245
                     Atom        target)
 
246
{
 
247
        return tdata->target == target;
 
248
}
 
249
 
 
250
static int
 
251
find_content_type (TargetData *tdata,
 
252
                   Atom        type)
 
253
{
 
254
        return tdata->type == type;
 
255
}
 
256
 
 
257
static int
 
258
find_conversion_requestor (IncrConversion *rdata,
 
259
                           XEvent         *xev)
 
260
{
 
261
        return (rdata->requestor == xev->xproperty.window &&
 
262
                rdata->property == xev->xproperty.atom);
 
263
}
 
264
 
 
265
static void
 
266
get_property (TargetData          *tdata,
 
267
              GsdClipboardManager *manager)
 
268
{
 
269
        Atom           type;
 
270
        int            format;
 
271
        unsigned long  length;
 
272
        unsigned long  remaining;
 
273
        unsigned char *data;
 
274
 
 
275
        XGetWindowProperty (manager->priv->display,
 
276
                            manager->priv->window,
 
277
                            tdata->target,
 
278
                            0,
 
279
                            0x1FFFFFFF,
 
280
                            True,
 
281
                            AnyPropertyType,
 
282
                            &type,
 
283
                            &format,
 
284
                            &length,
 
285
                            &remaining,
 
286
                            &data);
 
287
 
 
288
        if (type == None) {
 
289
                manager->priv->contents = list_remove (manager->priv->contents, tdata);
 
290
                free (tdata);
 
291
        } else if (type == XA_INCR) {
 
292
                tdata->type = type;
 
293
                tdata->length = 0;
 
294
                XFree (data);
 
295
        } else {
 
296
                tdata->type = type;
 
297
                tdata->data = data;
 
298
                tdata->length = length * clipboard_bytes_per_item (format);
 
299
                tdata->format = format;
 
300
        }
 
301
}
 
302
 
 
303
static Bool
 
304
receive_incrementally (GsdClipboardManager *manager,
 
305
                       XEvent              *xev)
 
306
{
 
307
        List          *list;
 
308
        TargetData    *tdata;
 
309
        Atom           type;
 
310
        int            format;
 
311
        unsigned long  length, nitems, remaining;
 
312
        unsigned char *data;
 
313
 
 
314
        if (xev->xproperty.window != manager->priv->window)
 
315
                return False;
 
316
 
 
317
        list = list_find (manager->priv->contents,
 
318
                          (ListFindFunc) find_content_target, (void *) xev->xproperty.atom);
 
319
 
 
320
        if (!list)
 
321
                return False;
 
322
 
 
323
        tdata = (TargetData *) list->data;
 
324
 
 
325
        if (tdata->type != XA_INCR)
 
326
                return False;
 
327
 
 
328
        XGetWindowProperty (xev->xproperty.display,
 
329
                            xev->xproperty.window,
 
330
                            xev->xproperty.atom,
 
331
                            0, 0x1FFFFFFF, True, AnyPropertyType,
 
332
                            &type, &format, &nitems, &remaining, &data);
 
333
 
 
334
        length = nitems * clipboard_bytes_per_item (format);
 
335
        if (length == 0) {
 
336
                tdata->type = type;
 
337
                tdata->format = format;
 
338
 
 
339
                if (!list_find (manager->priv->contents,
 
340
                                (ListFindFunc) find_content_type, (void *)XA_INCR)) {
 
341
                        /* all incremental transfers done */
 
342
                        send_selection_notify (manager, True);
 
343
                        manager->priv->requestor = None;
 
344
                }
 
345
 
 
346
                XFree (data);
 
347
        } else {
 
348
                if (!tdata->data) {
 
349
                        tdata->data = data;
 
350
                        tdata->length = length;
 
351
                } else {
 
352
                        tdata->data = realloc (tdata->data, tdata->length + length + 1);
 
353
                        memcpy (tdata->data + tdata->length, data, length + 1);
 
354
                        tdata->length += length;
 
355
                        XFree (data);
 
356
                }
 
357
        }
 
358
 
 
359
        return True;
 
360
}
 
361
 
 
362
static Bool
 
363
send_incrementally (GsdClipboardManager *manager,
 
364
                    XEvent              *xev)
 
365
{
 
366
        List           *list;
 
367
        IncrConversion *rdata;
 
368
        unsigned long   length;
 
369
        unsigned long   items;
 
370
        unsigned char  *data;
 
371
 
 
372
        list = list_find (manager->priv->conversions,
 
373
                          (ListFindFunc) find_conversion_requestor, xev);
 
374
        if (list == NULL)
 
375
                return False;
 
376
 
 
377
        rdata = (IncrConversion *) list->data;
 
378
 
 
379
        data = rdata->data->data + rdata->offset;
 
380
        length = rdata->data->length - rdata->offset;
 
381
        if (length > SELECTION_MAX_SIZE)
 
382
                length = SELECTION_MAX_SIZE;
 
383
 
 
384
        rdata->offset += length;
 
385
 
 
386
        items = length / clipboard_bytes_per_item (rdata->data->format);
 
387
        XChangeProperty (manager->priv->display, rdata->requestor,
 
388
                         rdata->property, rdata->data->type,
 
389
                         rdata->data->format, PropModeAppend,
 
390
                         data, items);
 
391
 
 
392
        if (length == 0) {
 
393
                clipboard_manager_watch_cb (manager,
 
394
                                            rdata->requestor,
 
395
                                            False,
 
396
                                            PropertyChangeMask,
 
397
                                            NULL);
 
398
 
 
399
                manager->priv->conversions = list_remove (manager->priv->conversions, rdata);
 
400
                conversion_free (rdata);
 
401
        }
 
402
 
 
403
        return True;
 
404
}
 
405
 
 
406
static void
 
407
convert_clipboard_manager (GsdClipboardManager *manager,
 
408
                           XEvent              *xev)
 
409
{
 
410
        Atom          type = None;
 
411
        int           format;
 
412
        unsigned long nitems;
 
413
        unsigned long remaining;
 
414
        Atom         *targets = NULL;
 
415
 
 
416
        if (xev->xselectionrequest.target == XA_SAVE_TARGETS) {
 
417
                if (manager->priv->requestor != None || manager->priv->contents != NULL) {
 
418
                        /* We're in the middle of a conversion request, or own
 
419
                         * the CLIPBOARD already
 
420
                         */
 
421
                        finish_selection_request (manager, xev, False);
 
422
                } else {
 
423
                        gdk_error_trap_push ();
 
424
 
 
425
                        clipboard_manager_watch_cb (manager,
 
426
                                                    xev->xselectionrequest.requestor,
 
427
                                                    True,
 
428
                                                    StructureNotifyMask,
 
429
                                                    NULL);
 
430
                        XSelectInput (manager->priv->display,
 
431
                                      xev->xselectionrequest.requestor,
 
432
                                      StructureNotifyMask);
 
433
                        XSync (manager->priv->display, False);
 
434
 
 
435
                        if (gdk_error_trap_pop () != Success)
 
436
                                return;
 
437
 
 
438
                        gdk_error_trap_push ();
 
439
 
 
440
                        if (xev->xselectionrequest.property != None) {
 
441
                                XGetWindowProperty (manager->priv->display,
 
442
                                                    xev->xselectionrequest.requestor,
 
443
                                                    xev->xselectionrequest.property,
 
444
                                                    0, 0x1FFFFFFF, False, XA_ATOM,
 
445
                                                    &type, &format, &nitems, &remaining,
 
446
                                                    (unsigned char **) &targets);
 
447
 
 
448
                                if (gdk_error_trap_pop () != Success) {
 
449
                                        if (targets)
 
450
                                                XFree (targets);
 
451
 
 
452
                                        return;
 
453
                                }
 
454
                        }
 
455
 
 
456
                        manager->priv->requestor = xev->xselectionrequest.requestor;
 
457
                        manager->priv->property = xev->xselectionrequest.property;
 
458
                        manager->priv->time = xev->xselectionrequest.time;
 
459
 
 
460
                        if (type == None)
 
461
                                XConvertSelection (manager->priv->display, XA_CLIPBOARD,
 
462
                                                   XA_TARGETS, XA_TARGETS,
 
463
                                                   manager->priv->window, manager->priv->time);
 
464
                        else
 
465
                                save_targets (manager, targets, nitems);
 
466
                }
 
467
        } else if (xev->xselectionrequest.target == XA_TIMESTAMP) {
 
468
                XChangeProperty (manager->priv->display,
 
469
                                 xev->xselectionrequest.requestor,
 
470
                                 xev->xselectionrequest.property,
 
471
                                 XA_INTEGER, 32, PropModeReplace,
 
472
                                 (unsigned char *) &manager->priv->timestamp, 1);
 
473
 
 
474
                finish_selection_request (manager, xev, True);
 
475
        } else if (xev->xselectionrequest.target == XA_TARGETS) {
 
476
                int  n_targets = 0;
 
477
                Atom targets[3];
 
478
 
 
479
                targets[n_targets++] = XA_TARGETS;
 
480
                targets[n_targets++] = XA_TIMESTAMP;
 
481
                targets[n_targets++] = XA_SAVE_TARGETS;
 
482
 
 
483
                XChangeProperty (manager->priv->display,
 
484
                                 xev->xselectionrequest.requestor,
 
485
                                 xev->xselectionrequest.property,
 
486
                                 XA_ATOM, 32, PropModeReplace,
 
487
                                 (unsigned char *) targets, n_targets);
 
488
 
 
489
                finish_selection_request (manager, xev, True);
 
490
        } else
 
491
                finish_selection_request (manager, xev, False);
 
492
}
 
493
 
 
494
static void
 
495
convert_clipboard_target (IncrConversion      *rdata,
 
496
                          GsdClipboardManager *manager)
 
497
{
 
498
        TargetData       *tdata;
 
499
        Atom             *targets;
 
500
        int               n_targets;
 
501
        List             *list;
 
502
        unsigned long     items;
 
503
        XWindowAttributes atts;
 
504
 
 
505
        if (rdata->target == XA_TARGETS) {
 
506
                n_targets = list_length (manager->priv->contents) + 2;
 
507
                targets = (Atom *) malloc (n_targets * sizeof (Atom));
 
508
 
 
509
                n_targets = 0;
 
510
 
 
511
                targets[n_targets++] = XA_TARGETS;
 
512
                targets[n_targets++] = XA_MULTIPLE;
 
513
 
 
514
                for (list = manager->priv->contents; list; list = list->next) {
 
515
                        tdata = (TargetData *) list->data;
 
516
                        targets[n_targets++] = tdata->target;
 
517
                }
 
518
 
 
519
                XChangeProperty (manager->priv->display, rdata->requestor,
 
520
                                 rdata->property,
 
521
                                 XA_ATOM, 32, PropModeReplace,
 
522
                                 (unsigned char *) targets, n_targets);
 
523
                free (targets);
 
524
        } else  {
 
525
                /* Convert from stored CLIPBOARD data */
 
526
                list = list_find (manager->priv->contents,
 
527
                                  (ListFindFunc) find_content_target, (void *) rdata->target);
 
528
 
 
529
                /* We got a target that we don't support */
 
530
                if (!list)
 
531
                        return;
 
532
 
 
533
                tdata = (TargetData *)list->data;
 
534
                if (tdata->type == XA_INCR) {
 
535
                        /* we haven't completely received this target yet  */
 
536
                        rdata->property = None;
 
537
                        return;
 
538
                }
 
539
 
 
540
                rdata->data = target_data_ref (tdata);
 
541
                items = tdata->length / clipboard_bytes_per_item (tdata->format);
 
542
                if (tdata->length <= SELECTION_MAX_SIZE)
 
543
                        XChangeProperty (manager->priv->display, rdata->requestor,
 
544
                                         rdata->property,
 
545
                                         tdata->type, tdata->format, PropModeReplace,
 
546
                                         tdata->data, items);
 
547
                else {
 
548
                        /* start incremental transfer */
 
549
                        rdata->offset = 0;
 
550
 
 
551
                        gdk_error_trap_push ();
 
552
 
 
553
                        XGetWindowAttributes (manager->priv->display, rdata->requestor, &atts);
 
554
 
 
555
                        clipboard_manager_watch_cb (manager,
 
556
                                                    rdata->requestor,
 
557
                                                    True,
 
558
                                                    PropertyChangeMask,
 
559
                                                    NULL);
 
560
 
 
561
                        XSelectInput (manager->priv->display, rdata->requestor,
 
562
                                      atts.your_event_mask | PropertyChangeMask);
 
563
 
 
564
                        XChangeProperty (manager->priv->display, rdata->requestor,
 
565
                                         rdata->property,
 
566
                                         XA_INCR, 32, PropModeReplace,
 
567
                                         (unsigned char *) &items, 1);
 
568
 
 
569
                        XSync (manager->priv->display, False);
 
570
 
 
571
                        gdk_error_trap_pop_ignored ();
 
572
                }
 
573
        }
 
574
}
 
575
 
 
576
static void
 
577
collect_incremental (IncrConversion      *rdata,
 
578
                     GsdClipboardManager *manager)
 
579
{
 
580
        if (rdata->offset >= 0)
 
581
                manager->priv->conversions = list_prepend (manager->priv->conversions, rdata);
 
582
        else {
 
583
                if (rdata->data) {
 
584
                        target_data_unref (rdata->data);
 
585
                        rdata->data = NULL;
 
586
                }
 
587
                free (rdata);
 
588
        }
 
589
}
 
590
 
 
591
static void
 
592
convert_clipboard (GsdClipboardManager *manager,
 
593
                   XEvent              *xev)
 
594
{
 
595
        List           *list;
 
596
        List           *conversions;
 
597
        IncrConversion *rdata;
 
598
        Atom            type;
 
599
        int             i;
 
600
        int             format;
 
601
        unsigned long   nitems;
 
602
        unsigned long   remaining;
 
603
        Atom           *multiple;
 
604
 
 
605
        conversions = NULL;
 
606
        type = None;
 
607
 
 
608
        if (xev->xselectionrequest.target == XA_MULTIPLE) {
 
609
                XGetWindowProperty (xev->xselectionrequest.display,
 
610
                                    xev->xselectionrequest.requestor,
 
611
                                    xev->xselectionrequest.property,
 
612
                                    0, 0x1FFFFFFF, False, XA_ATOM_PAIR,
 
613
                                    &type, &format, &nitems, &remaining,
 
614
                                    (unsigned char **) &multiple);
 
615
 
 
616
                if (type != XA_ATOM_PAIR || nitems == 0) {
 
617
                        if (multiple)
 
618
                                free (multiple);
 
619
                        return;
 
620
                }
 
621
 
 
622
                for (i = 0; i < nitems; i += 2) {
 
623
                        rdata = (IncrConversion *) malloc (sizeof (IncrConversion));
 
624
                        rdata->requestor = xev->xselectionrequest.requestor;
 
625
                        rdata->target = multiple[i];
 
626
                        rdata->property = multiple[i+1];
 
627
                        rdata->data = NULL;
 
628
                        rdata->offset = -1;
 
629
                        conversions = list_prepend (conversions, rdata);
 
630
                }
 
631
        } else {
 
632
                multiple = NULL;
 
633
 
 
634
                rdata = (IncrConversion *) malloc (sizeof (IncrConversion));
 
635
                rdata->requestor = xev->xselectionrequest.requestor;
 
636
                rdata->target = xev->xselectionrequest.target;
 
637
                rdata->property = xev->xselectionrequest.property;
 
638
                rdata->data = NULL;
 
639
                rdata->offset = -1;
 
640
                conversions = list_prepend (conversions, rdata);
 
641
        }
 
642
 
 
643
        list_foreach (conversions, (Callback) convert_clipboard_target, manager);
 
644
 
 
645
        if (conversions->next == NULL &&
 
646
            ((IncrConversion *) conversions->data)->property == None) {
 
647
                finish_selection_request (manager, xev, False);
 
648
        } else {
 
649
                if (multiple) {
 
650
                        i = 0;
 
651
                        for (list = conversions; list; list = list->next) {
 
652
                                rdata = (IncrConversion *)list->data;
 
653
                                multiple[i++] = rdata->target;
 
654
                                multiple[i++] = rdata->property;
 
655
                        }
 
656
                        XChangeProperty (xev->xselectionrequest.display,
 
657
                                         xev->xselectionrequest.requestor,
 
658
                                         xev->xselectionrequest.property,
 
659
                                         XA_ATOM_PAIR, 32, PropModeReplace,
 
660
                                         (unsigned char *) multiple, nitems);
 
661
                }
 
662
                finish_selection_request (manager, xev, True);
 
663
        }
 
664
 
 
665
        list_foreach (conversions, (Callback) collect_incremental, manager);
 
666
        list_free (conversions);
 
667
 
 
668
        if (multiple)
 
669
                free (multiple);
 
670
}
 
671
 
 
672
static Bool
 
673
clipboard_manager_process_event (GsdClipboardManager *manager,
 
674
                                 XEvent              *xev)
 
675
{
 
676
        Atom          type;
 
677
        int           format;
 
678
        unsigned long nitems;
 
679
        unsigned long remaining;
 
680
        Atom         *targets;
 
681
 
 
682
        targets = NULL;
 
683
 
 
684
        switch (xev->xany.type) {
 
685
        case DestroyNotify:
 
686
                if (xev->xdestroywindow.window == manager->priv->requestor) {
 
687
                        list_foreach (manager->priv->contents, (Callback)target_data_unref, NULL);
 
688
                        list_free (manager->priv->contents);
 
689
                        manager->priv->contents = NULL;
 
690
 
 
691
                        clipboard_manager_watch_cb (manager,
 
692
                                                    manager->priv->requestor,
 
693
                                                    False,
 
694
                                                    0,
 
695
                                                    NULL);
 
696
                        manager->priv->requestor = None;
 
697
                }
 
698
                break;
 
699
        case PropertyNotify:
 
700
                if (xev->xproperty.state == PropertyNewValue) {
 
701
                        return receive_incrementally (manager, xev);
 
702
                } else {
 
703
                        return send_incrementally (manager, xev);
 
704
                }
 
705
 
 
706
        case SelectionClear:
 
707
                if (xev->xany.window != manager->priv->window)
 
708
                        return False;
 
709
 
 
710
                if (xev->xselectionclear.selection == XA_CLIPBOARD_MANAGER) {
 
711
                        /* We lost the manager selection */
 
712
                        if (manager->priv->contents) {
 
713
                                list_foreach (manager->priv->contents, (Callback)target_data_unref, NULL);
 
714
                                list_free (manager->priv->contents);
 
715
                                manager->priv->contents = NULL;
 
716
 
 
717
                                XSetSelectionOwner (manager->priv->display,
 
718
                                                    XA_CLIPBOARD,
 
719
                                                    None, manager->priv->time);
 
720
                        }
 
721
 
 
722
                        return True;
 
723
                }
 
724
                if (xev->xselectionclear.selection == XA_CLIPBOARD) {
 
725
                        /* We lost the clipboard selection */
 
726
                        list_foreach (manager->priv->contents, (Callback)target_data_unref, NULL);
 
727
                        list_free (manager->priv->contents);
 
728
                        manager->priv->contents = NULL;
 
729
                        clipboard_manager_watch_cb (manager,
 
730
                                                    manager->priv->requestor,
 
731
                                                    False,
 
732
                                                    0,
 
733
                                                    NULL);
 
734
                        manager->priv->requestor = None;
 
735
 
 
736
                        return True;
 
737
                }
 
738
                break;
 
739
 
 
740
        case SelectionNotify:
 
741
                if (xev->xany.window != manager->priv->window)
 
742
                        return False;
 
743
 
 
744
                if (xev->xselection.selection == XA_CLIPBOARD) {
 
745
                        /* a CLIPBOARD conversion is done */
 
746
                        if (xev->xselection.property == XA_TARGETS) {
 
747
                                XGetWindowProperty (xev->xselection.display,
 
748
                                                    xev->xselection.requestor,
 
749
                                                    xev->xselection.property,
 
750
                                                    0, 0x1FFFFFFF, True, XA_ATOM,
 
751
                                                    &type, &format, &nitems, &remaining,
 
752
                                                    (unsigned char **) &targets);
 
753
 
 
754
                                save_targets (manager, targets, nitems);
 
755
                        } else if (xev->xselection.property == XA_MULTIPLE) {
 
756
                                List *tmp;
 
757
 
 
758
                                tmp = list_copy (manager->priv->contents);
 
759
                                list_foreach (tmp, (Callback) get_property, manager);
 
760
                                list_free (tmp);
 
761
 
 
762
                                manager->priv->time = xev->xselection.time;
 
763
                                XSetSelectionOwner (manager->priv->display, XA_CLIPBOARD,
 
764
                                                    manager->priv->window, manager->priv->time);
 
765
 
 
766
                                if (manager->priv->property != None)
 
767
                                        XChangeProperty (manager->priv->display,
 
768
                                                         manager->priv->requestor,
 
769
                                                         manager->priv->property,
 
770
                                                         XA_ATOM, 32, PropModeReplace,
 
771
                                                         (unsigned char *)&XA_NULL, 1);
 
772
 
 
773
                                if (!list_find (manager->priv->contents,
 
774
                                                (ListFindFunc)find_content_type, (void *)XA_INCR)) {
 
775
                                        /* all transfers done */
 
776
                                        send_selection_notify (manager, True);
 
777
                                        clipboard_manager_watch_cb (manager,
 
778
                                                                    manager->priv->requestor,
 
779
                                                                    False,
 
780
                                                                    0,
 
781
                                                                    NULL);
 
782
                                        manager->priv->requestor = None;
 
783
                                }
 
784
                        }
 
785
                        else if (xev->xselection.property == None) {
 
786
                                send_selection_notify (manager, False);
 
787
                                clipboard_manager_watch_cb (manager,
 
788
                                                            manager->priv->requestor,
 
789
                                                            False,
 
790
                                                            0,
 
791
                                                            NULL);
 
792
                                manager->priv->requestor = None;
 
793
                        }
 
794
 
 
795
                        return True;
 
796
                }
 
797
                break;
 
798
 
 
799
        case SelectionRequest:
 
800
                if (xev->xany.window != manager->priv->window) {
 
801
                        return False;
 
802
                }
 
803
 
 
804
                if (xev->xselectionrequest.selection == XA_CLIPBOARD_MANAGER) {
 
805
                        convert_clipboard_manager (manager, xev);
 
806
                        return True;
 
807
                } else if (xev->xselectionrequest.selection == XA_CLIPBOARD) {
 
808
                        convert_clipboard (manager, xev);
 
809
                        return True;
 
810
                }
 
811
                break;
 
812
 
 
813
        default: ;
 
814
        }
 
815
 
 
816
        return False;
 
817
}
 
818
 
 
819
static GdkFilterReturn
 
820
clipboard_manager_event_filter (GdkXEvent           *xevent,
 
821
                                GdkEvent            *event,
 
822
                                GsdClipboardManager *manager)
 
823
{
 
824
        if (clipboard_manager_process_event (manager, (XEvent *)xevent)) {
 
825
                return GDK_FILTER_REMOVE;
 
826
        } else {
 
827
                return GDK_FILTER_CONTINUE;
 
828
        }
 
829
}
 
830
 
 
831
static void
 
832
clipboard_manager_watch_cb (GsdClipboardManager *manager,
 
833
                            Window               window,
 
834
                            Bool                 is_start,
 
835
                            long                 mask,
 
836
                            void                *cb_data)
 
837
{
 
838
        GdkWindow  *gdkwin;
 
839
        GdkDisplay *display;
 
840
 
 
841
        display = gdk_display_get_default ();
 
842
        gdkwin = gdk_x11_window_lookup_for_display (display, window);
 
843
 
 
844
        if (is_start) {
 
845
                if (gdkwin == NULL) {
 
846
                        gdkwin = gdk_x11_window_foreign_new_for_display (display, window);
 
847
                } else {
 
848
                        g_object_ref (gdkwin);
 
849
                }
 
850
 
 
851
                gdk_window_add_filter (gdkwin,
 
852
                                       (GdkFilterFunc)clipboard_manager_event_filter,
 
853
                                       manager);
 
854
        } else {
 
855
                if (gdkwin == NULL) {
 
856
                        return;
 
857
                }
 
858
                gdk_window_remove_filter (gdkwin,
 
859
                                          (GdkFilterFunc)clipboard_manager_event_filter,
 
860
                                          manager);
 
861
                g_object_unref (gdkwin);
 
862
        }
 
863
}
 
864
 
 
865
static gboolean
 
866
start_clipboard_idle_cb (GsdClipboardManager *manager)
 
867
{
 
868
        XClientMessageEvent xev;
 
869
 
 
870
 
 
871
        gnome_settings_profile_start (NULL);
 
872
 
 
873
        init_atoms (manager->priv->display);
 
874
 
 
875
        /* check if there is a clipboard manager running */
 
876
        if (XGetSelectionOwner (manager->priv->display, XA_CLIPBOARD_MANAGER)) {
 
877
                g_warning ("Clipboard manager is already running.");
 
878
                return FALSE;
 
879
        }
 
880
 
 
881
        manager->priv->contents = NULL;
 
882
        manager->priv->conversions = NULL;
 
883
        manager->priv->requestor = None;
 
884
 
 
885
        manager->priv->window = XCreateSimpleWindow (manager->priv->display,
 
886
                                                     DefaultRootWindow (manager->priv->display),
 
887
                                                     0, 0, 10, 10, 0,
 
888
                                                     WhitePixel (manager->priv->display,
 
889
                                                                 DefaultScreen (manager->priv->display)),
 
890
                                                     WhitePixel (manager->priv->display,
 
891
                                                                 DefaultScreen (manager->priv->display)));
 
892
        clipboard_manager_watch_cb (manager,
 
893
                                    manager->priv->window,
 
894
                                    True,
 
895
                                    PropertyChangeMask,
 
896
                                    NULL);
 
897
        XSelectInput (manager->priv->display,
 
898
                      manager->priv->window,
 
899
                      PropertyChangeMask);
 
900
        manager->priv->timestamp = get_server_time (manager->priv->display, manager->priv->window);
 
901
 
 
902
        XSetSelectionOwner (manager->priv->display,
 
903
                            XA_CLIPBOARD_MANAGER,
 
904
                            manager->priv->window,
 
905
                            manager->priv->timestamp);
 
906
 
 
907
        /* Check to see if we managed to claim the selection. If not,
 
908
         * we treat it as if we got it then immediately lost it
 
909
         */
 
910
        if (XGetSelectionOwner (manager->priv->display, XA_CLIPBOARD_MANAGER) == manager->priv->window) {
 
911
                xev.type = ClientMessage;
 
912
                xev.window = DefaultRootWindow (manager->priv->display);
 
913
                xev.message_type = XA_MANAGER;
 
914
                xev.format = 32;
 
915
                xev.data.l[0] = manager->priv->timestamp;
 
916
                xev.data.l[1] = XA_CLIPBOARD_MANAGER;
 
917
                xev.data.l[2] = manager->priv->window;
 
918
                xev.data.l[3] = 0;      /* manager specific data */
 
919
                xev.data.l[4] = 0;      /* manager specific data */
 
920
 
 
921
                XSendEvent (manager->priv->display,
 
922
                            DefaultRootWindow (manager->priv->display),
 
923
                            False,
 
924
                            StructureNotifyMask,
 
925
                            (XEvent *)&xev);
 
926
        } else {
 
927
                clipboard_manager_watch_cb (manager,
 
928
                                            manager->priv->window,
 
929
                                            False,
 
930
                                            0,
 
931
                                            NULL);
 
932
                /* FIXME: manager->priv->terminate (manager->priv->cb_data); */
 
933
        }
 
934
 
 
935
        gnome_settings_profile_end (NULL);
 
936
 
 
937
        manager->priv->start_idle_id = 0;
 
938
 
 
939
        return FALSE;
 
940
}
 
941
 
 
942
gboolean
 
943
gsd_clipboard_manager_start (GsdClipboardManager *manager,
 
944
                             GError             **error)
 
945
{
 
946
        gnome_settings_profile_start (NULL);
 
947
 
 
948
        manager->priv->start_idle_id = g_idle_add ((GSourceFunc) start_clipboard_idle_cb, manager);
 
949
 
 
950
        gnome_settings_profile_end (NULL);
 
951
 
 
952
        return TRUE;
 
953
}
 
954
 
 
955
void
 
956
gsd_clipboard_manager_stop (GsdClipboardManager *manager)
 
957
{
 
958
        g_debug ("Stopping clipboard manager");
 
959
 
 
960
        if (manager->priv->window != None) {
 
961
                clipboard_manager_watch_cb (manager,
 
962
                                            manager->priv->window,
 
963
                                            FALSE,
 
964
                                            0,
 
965
                                            NULL);
 
966
                XDestroyWindow (manager->priv->display, manager->priv->window);
 
967
                manager->priv->window = None;
 
968
        }
 
969
 
 
970
        if (manager->priv->conversions != NULL) {
 
971
                list_foreach (manager->priv->conversions, (Callback) conversion_free, NULL);
 
972
                list_free (manager->priv->conversions);
 
973
                manager->priv->conversions = NULL;
 
974
        }
 
975
 
 
976
        if (manager->priv->contents != NULL) {
 
977
                list_foreach (manager->priv->contents, (Callback) target_data_unref, NULL);
 
978
                list_free (manager->priv->contents);
 
979
                manager->priv->contents = NULL;
 
980
        }
 
981
}
 
982
 
 
983
static GObject *
 
984
gsd_clipboard_manager_constructor (GType                  type,
 
985
                                   guint                  n_construct_properties,
 
986
                                   GObjectConstructParam *construct_properties)
 
987
{
 
988
        GsdClipboardManager      *clipboard_manager;
 
989
 
 
990
        clipboard_manager = GSD_CLIPBOARD_MANAGER (G_OBJECT_CLASS (gsd_clipboard_manager_parent_class)->constructor (type,
 
991
                                                                                                      n_construct_properties,
 
992
                                                                                                      construct_properties));
 
993
 
 
994
        return G_OBJECT (clipboard_manager);
 
995
}
 
996
 
 
997
static void
 
998
gsd_clipboard_manager_class_init (GsdClipboardManagerClass *klass)
 
999
{
 
1000
        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
 
1001
 
 
1002
        object_class->constructor = gsd_clipboard_manager_constructor;
 
1003
        object_class->finalize = gsd_clipboard_manager_finalize;
 
1004
 
 
1005
        g_type_class_add_private (klass, sizeof (GsdClipboardManagerPrivate));
 
1006
}
 
1007
 
 
1008
static void
 
1009
gsd_clipboard_manager_init (GsdClipboardManager *manager)
 
1010
{
 
1011
        manager->priv = GSD_CLIPBOARD_MANAGER_GET_PRIVATE (manager);
 
1012
 
 
1013
        manager->priv->display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
 
1014
 
 
1015
}
 
1016
 
 
1017
static void
 
1018
gsd_clipboard_manager_finalize (GObject *object)
 
1019
{
 
1020
        GsdClipboardManager *clipboard_manager;
 
1021
 
 
1022
        g_return_if_fail (object != NULL);
 
1023
        g_return_if_fail (GSD_IS_CLIPBOARD_MANAGER (object));
 
1024
 
 
1025
        clipboard_manager = GSD_CLIPBOARD_MANAGER (object);
 
1026
 
 
1027
        g_return_if_fail (clipboard_manager->priv != NULL);
 
1028
 
 
1029
        if (clipboard_manager->priv->start_idle_id !=0)
 
1030
                g_source_remove (clipboard_manager->priv->start_idle_id);
 
1031
 
 
1032
        G_OBJECT_CLASS (gsd_clipboard_manager_parent_class)->finalize (object);
 
1033
}
 
1034
 
 
1035
GsdClipboardManager *
 
1036
gsd_clipboard_manager_new (void)
 
1037
{
 
1038
        if (manager_object != NULL) {
 
1039
                g_object_ref (manager_object);
 
1040
        } else {
 
1041
                manager_object = g_object_new (GSD_TYPE_CLIPBOARD_MANAGER, NULL);
 
1042
                g_object_add_weak_pointer (manager_object,
 
1043
                                           (gpointer *) &manager_object);
 
1044
        }
 
1045
 
 
1046
        return GSD_CLIPBOARD_MANAGER (manager_object);
 
1047
}