1 #include "unix/guts.h"
2 #include "Application.h"
3 #include "Clipboard.h"
4 #include "Icon.h"
5 
6 #define WIN PComponent(application)-> handle
7 
8 #define CF_NAME(x)   (guts. clipboard_formats[(x)*3])
9 #define CF_TYPE(x)   (guts. clipboard_formats[(x)*3+1])
10 #define CF_FORMAT(x) (guts. clipboard_formats[(x)*3+2])
11 #define CF_ASSIGN(i,a,b,c) CF_NAME(i)=(a);CF_TYPE(i)=(b);CF_FORMAT(i)=((Atom)c)
12 
13 Bool
prima_init_clipboard_subsystem(char * error_buf)14 prima_init_clipboard_subsystem(char * error_buf)
15 {
16 	guts. clipboards = hash_create();
17 
18 	if ( !(guts. clipboard_formats = malloc( cfCOUNT * 3 * sizeof(Atom)))) {
19 		sprintf( error_buf, "No memory");
20 		return false;
21 	}
22 	guts. clipboard_formats_count = cfCOUNT;
23 #if (cfText != 0) || (cfBitmap != 1) || (cfUTF8 != 2)
24 #error broken clipboard type formats
25 #endif
26 
27 	CF_ASSIGN(cfText, XA_STRING, XA_STRING, 8);
28 	CF_ASSIGN(cfUTF8, UTF8_STRING, UTF8_STRING, 8);
29 	CF_ASSIGN(cfBitmap, XA_PIXMAP, XA_PIXMAP, CF_32);
30 	CF_ASSIGN(cfTargets, CF_TARGETS, XA_ATOM, CF_32);
31 
32 	/* XXX - bitmaps and indexed pixmaps may have the associated colormap or pixel values
33 	CF_ASSIGN(cfPalette, XA_COLORMAP, XA_ATOM, CF_32);
34 	CF_ASSIGN(cfForeground, CF_FOREGROUND, CF_PIXEL, CF_32);
35 	CF_ASSIGN(cfBackground, CF_BACKGROUND, CF_PIXEL, CF_32);
36 	*/
37 
38 	guts. clipboard_event_timeout = 2000;
39 	return true;
40 }
41 
42 PList
apc_get_standard_clipboards(void)43 apc_get_standard_clipboards( void)
44 {
45 	PList l = plist_create( 4, 1);
46 	if (!l) return NULL;
47 	list_add( l, (Handle)duplicate_string( "Primary"));
48 	list_add( l, (Handle)duplicate_string( "Secondary"));
49 	list_add( l, (Handle)duplicate_string( "Clipboard"));
50 	list_add( l, (Handle)duplicate_string( "XdndSelection"));
51 	return l;
52 }
53 
54 Bool
apc_clipboard_create(Handle self)55 apc_clipboard_create( Handle self)
56 {
57 	PClipboard c = (PClipboard)self;
58 	int i;
59 	DEFCC;
60 
61 	if ( strcmp(c->name, "XdndSelection") != 0 ) {
62 		char *name, *x;
63 		name = x = duplicate_string( c-> name);
64 		while (*x) {
65 			*x = toupper(*x);
66 			x++;
67 		}
68 		XX-> selection = XInternAtom( DISP, name, false);
69 		free( name);
70 	} else {
71 		XX-> selection = XdndSelection;
72 	}
73 
74 	if ( hash_fetch( guts.clipboards, &XX->selection, sizeof(XX->selection))) {
75 		warn("This clipboard is already present");
76 		return false;
77 	}
78 
79 	if ( !( XX-> internal = malloc( sizeof( ClipboardDataItem) * cfCOUNT))) {
80 		warn("Not enough memory");
81 		return false;
82 	}
83 	if ( !( XX-> external = malloc( sizeof( ClipboardDataItem) * cfCOUNT))) {
84 		free( XX-> internal);
85 		warn("Not enough memory");
86 		return false;
87 	}
88 	bzero( XX-> internal, sizeof( ClipboardDataItem) * cfCOUNT);
89 	bzero( XX-> external, sizeof( ClipboardDataItem) * cfCOUNT);
90 
91 	XX->internal[cfTargets].name = CF_NAME(cfTargets);
92 
93 	for ( i = 0; i < cfCOUNT; i++)
94 		XX->internal[i].immediate = XX->external[i].immediate = true;
95 
96 	hash_store( guts.clipboards, &XX->selection, sizeof(XX->selection), (void*)self);
97 
98 	if ( XX-> selection == XdndSelection )
99 		guts. xdnd_clipboard = self;
100 
101 	return true;
102 }
103 
104 static void
clipboard_free_data(void * data,int size,Handle id)105 clipboard_free_data( void * data, int size, Handle id)
106 {
107 	if ( size <= 0) {
108 		if ( size == 0 && data != NULL) free( data);
109 		return;
110 	}
111 	if ( id == cfBitmap) {
112 		int i;
113 		Pixmap * p = (Pixmap*) data;
114 		for ( i = 0; i < size/sizeof(Pixmap); i++, p++)
115 			if ( *p)
116 				XFreePixmap( DISP, *p);
117 	}
118 	free( data);
119 }
120 
121 /*
122 	each clipboard type can be represented by a set of
123 	X properties pairs, where each is X name and X type.
124 	get_typename() returns such pairs by the index.
125 */
126 static Atom
get_typename(Handle id,int index,Atom * type)127 get_typename( Handle id, int index, Atom * type)
128 {
129 	if ( type) *type = None;
130 	switch ( id) {
131 	case cfText:
132 		if ( index > 1) return None;
133 		if ( index == 1) {
134 			if ( type) *type = PLAINTEXT_MIME;
135 			return PLAINTEXT_MIME;
136 		}
137 	case cfUTF8:
138 		if ( index > 1) return None;
139 		if ( index == 1) {
140 			if ( type) *type = UTF8_MIME;
141 			return UTF8_MIME;
142 		}
143 	case cfBitmap:
144 		if ( index > 1) return None;
145 		if ( index == 1) {
146 			if ( type) *type = XA_BITMAP;
147 			return XA_BITMAP;
148 		}
149 	case cfTargets:
150 		if ( index > 1) return None;
151 		if ( index == 1) {
152 			if ( type) *type = CF_TARGETS;
153 			return CF_NAME(id);
154 		}
155 	}
156 	if ( index > 0) return None;
157 	if ( type) *type = CF_TYPE(id);
158 	return CF_NAME(id);
159 }
160 
161 void
prima_clipboard_kill_item(PClipboardDataItem item,Handle id)162 prima_clipboard_kill_item( PClipboardDataItem item, Handle id)
163 {
164 	item += id;
165 	clipboard_free_data( item-> data, item-> size, id);
166 	if ( item-> image ) unprotect_object( item-> image );
167 	item-> image = NULL_HANDLE;
168 	item-> data = NULL;
169 	item-> size = 0;
170 	item-> name = get_typename( id, 0, NULL);
171 	item-> immediate = true;
172 }
173 
174 /*
175 	Deletes a transfer record from pending xfer chain.
176 */
177 static void
delete_xfer(PClipboardSysData cc,ClipboardXfer * xfer)178 delete_xfer( PClipboardSysData cc, ClipboardXfer * xfer)
179 {
180 	ClipboardXferKey key;
181 	CLIPBOARD_XFER_KEY( key, xfer-> requestor, xfer-> property);
182 	if ( guts. clipboard_xfers) {
183 		IV refcnt;
184 		hash_delete( guts. clipboard_xfers, key, sizeof( key), false);
185 		refcnt = PTR2IV( hash_fetch( guts. clipboard_xfers, &xfer-> requestor, sizeof(XWindow)));
186 		if ( --refcnt == 0) {
187 			XSelectInput( DISP, xfer-> requestor, 0);
188 			hash_delete( guts. clipboard_xfers, &xfer-> requestor, sizeof(XWindow), false);
189 		} else {
190 			if ( refcnt < 0) refcnt = 0;
191 			hash_store( guts. clipboard_xfers, &xfer-> requestor, sizeof(XWindow), INT2PTR(void*, refcnt));
192 		}
193 	}
194 	if ( cc-> xfers)
195 		list_delete( cc-> xfers, ( Handle) xfer);
196 	if ( xfer-> data_detached && xfer-> data_master)
197 		clipboard_free_data( xfer-> data, xfer-> size, xfer-> id);
198 	free( xfer);
199 }
200 
201 Bool
apc_clipboard_destroy(Handle self)202 apc_clipboard_destroy( Handle self)
203 {
204 	DEFCC;
205 	int i;
206 
207 	if ( guts. xdnd_clipboard == self )
208 		guts. xdnd_clipboard = NULL_HANDLE;
209 
210 	if (XX-> selection == None) return true;
211 
212 	if ( XX-> xfers) {
213 		for ( i = 0; i < XX-> xfers-> count; i++)
214 			delete_xfer( XX, ( ClipboardXfer*) XX-> xfers-> items[i]);
215 		plist_destroy( XX-> xfers);
216 	}
217 
218 	for ( i = 0; i < guts. clipboard_formats_count; i++) {
219 		if ( XX-> external) prima_clipboard_kill_item( XX-> external, i);
220 		if ( XX-> internal) prima_clipboard_kill_item( XX-> internal, i);
221 	}
222 
223 	free( XX-> external);
224 	free( XX-> internal);
225 	hash_delete( guts.clipboards, &XX->selection, sizeof(XX->selection), false);
226 
227 	XX-> selection = None;
228 	return true;
229 }
230 
231 Bool
apc_clipboard_open(Handle self)232 apc_clipboard_open( Handle self)
233 {
234 	DEFCC;
235 	if ( XX-> xdnd_receiving ) return true;
236 	if ( XX-> opened) return false;
237 	XX-> opened = true;
238 
239 	if ( !XX-> inside_event) XX-> need_write = false;
240 
241 	return true;
242 }
243 
244 Bool
apc_clipboard_close(Handle self)245 apc_clipboard_close( Handle self)
246 {
247 	DEFCC;
248 	if ( XX-> xdnd_receiving ) return true; /* XXX */
249 	if ( !XX-> opened) return false;
250 	XX-> opened = false;
251 
252 	/* check if UTF8 is present and Text is not, and downgrade */
253 	if ( XX-> need_write &&
254 		XX-> internal[cfUTF8]. size > 0 &&
255 		XX-> internal[cfText]. size == 0) {
256 		Byte * src = XX-> internal[cfUTF8]. data;
257 		int len    = utf8_length( src, src + XX-> internal[cfUTF8]. size);
258 		if (( XX-> internal[cfText]. data = malloc( len))) {
259 			STRLEN charlen;
260 			U8 *dst, *end = src + XX-> internal[cfUTF8]. size;
261 			dst = XX-> internal[cfText]. data;
262 			XX-> internal[cfText]. size = len;
263 			while ( len--) {
264 				register UV u = prima_utf8_uvchr_end(src, end, &charlen);
265 				*(dst++) = ( u < 0x7f) ? u : '?'; /* XXX employ $LANG and iconv() */
266 				src += charlen;
267 			}
268 		}
269 	}
270 
271 
272 	if ( !XX-> inside_event) {
273 		int i;
274 		for ( i = 0; i < guts. clipboard_formats_count; i++)
275 			prima_clipboard_kill_item( XX-> external, i);
276 		if ( XX-> need_write && (!XX->xdnd_receiving || XX->xdnd_sending))
277 			if ( XGetSelectionOwner( DISP, XX-> selection) != WIN)
278 				XSetSelectionOwner( DISP, XX-> selection, WIN, CurrentTime);
279 	}
280 
281 	return true;
282 }
283 
284 /*
285 	Detaches data for pending transfers from XX, so eventual changes
286 	to XX->internal would not affect them. detach_xfers() should be
287 	called before clipboard_kill_item(XX-> internal), otherwise
288 	there's a chance of coredump.
289 */
290 void
prima_detach_xfers(PClipboardSysData XX,Handle id,Bool clear_original_data)291 prima_detach_xfers( PClipboardSysData XX, Handle id, Bool clear_original_data)
292 {
293 	int i, got_master = 0, got_anything = 0;
294 	if ( !XX-> xfers) return;
295 	for ( i = 0; i < XX-> xfers-> count; i++) {
296 		ClipboardXfer * x = ( ClipboardXfer *) XX-> xfers-> items[i];
297 		if ( x-> data_detached || x-> id != id) continue;
298 		got_anything = 1;
299 		if ( !got_master) {
300 			x-> data_master = true;
301 			got_master = 1;
302 		}
303 		x-> data_detached = true;
304 	}
305 	if ( got_anything && clear_original_data) {
306 		XX-> internal[id]. data = NULL;
307 		XX-> internal[id]. size = 0;
308 		XX-> internal[id]. name = get_typename( id, 0, NULL);
309 	}
310 }
311 
312 Bool
apc_clipboard_clear(Handle self)313 apc_clipboard_clear( Handle self)
314 {
315 	DEFCC;
316 	int i;
317 
318 	for ( i = 0; i < guts. clipboard_formats_count; i++) {
319 		prima_detach_xfers( XX, i, true);
320 		prima_clipboard_kill_item( XX-> internal, i);
321 		prima_clipboard_kill_item( XX-> external, i);
322 	}
323 
324 	if ( XX-> inside_event) {
325 		XX-> need_write = true;
326 	} else if ( !XX->xdnd_receiving || XX->xdnd_sending) {
327 		XWindow owner = XGetSelectionOwner( DISP, XX-> selection);
328 		XX-> need_write = false;
329 		if ( owner != None && owner != WIN)
330 			XSetSelectionOwner( DISP, XX-> selection, None, CurrentTime);
331 	}
332 
333 	return true;
334 }
335 
336 typedef struct {
337 	Atom selection;
338 	long mask;
339 } SelectionProcData;
340 
341 #define SELECTION_NOTIFY_MASK 1
342 #define PROPERTY_NOTIFY_MASK  2
343 
344 static int
selection_filter(Display * disp,XEvent * ev,SelectionProcData * data)345 selection_filter( Display * disp, XEvent * ev, SelectionProcData * data)
346 {
347 	switch ( ev-> type) {
348 	case PropertyNotify:
349 		return (data-> mask & PROPERTY_NOTIFY_MASK) && (data-> selection == ev-> xproperty. atom);
350 	case SelectionRequest:
351 	case SelectionClear:
352 	case MappingNotify:
353 		return true;
354 	case SelectionNotify:
355 		return (data-> mask & SELECTION_NOTIFY_MASK) && (data-> selection == ev-> xselection. selection);
356 	case ClientMessage:
357 		if ( ev-> xclient. window == WIN ||
358 			ev-> xclient. window == guts. root ||
359 			ev-> xclient. window == None) return true;
360 		if ( hash_fetch( guts.windows, (void*)&ev-> xclient. window,
361 			sizeof(ev-> xclient. window))) return false;
362 		return true;
363 	}
364 	return false;
365 }
366 
367 #define CFDATA_NONE            0
368 #define CFDATA_NOT_ACQUIRED  (-1)
369 #define CFDATA_ERROR         (-2)
370 
371 int
prima_read_property(XWindow window,Atom property,Atom * type,int * format,unsigned long * size,unsigned char ** data,Bool delete_property)372 prima_read_property( XWindow window, Atom property, Atom * type, int * format,
373 	unsigned long * size, unsigned char ** data, Bool delete_property)
374 {
375 	int ret = ( *size > 0) ? RPS_PARTIAL : RPS_ERROR;
376 	unsigned char * prop, *a1;
377 	unsigned long n, left, offs = 0, new_size, big_offs = *size;
378 
379 	XCHECKPOINT;
380 	Cdebug("clipboard: read_property: %s\n", XGetAtomName(DISP, property));
381 	while ( 1) {
382 		if ( XGetWindowProperty( DISP, window, property,
383 			offs, guts. limits. request_length - 4, false,
384 			AnyPropertyType,
385 			type, format, &n, &left, &prop) != Success) {
386 			if ( delete_property )
387 				XDeleteProperty( DISP, window, property);
388 			Cdebug("clipboard:fail\n");
389 			return ret;
390 		}
391 		XCHECKPOINT;
392 		Cdebug("clipboard: type=0x%x(%s) fmt=%d n=%d left=%d\n",
393 				*type, XGetAtomName(DISP,*type), *format, n, left);
394 
395 		if ( *format == 32) *format = CF_32;
396 
397 		if ( *type == 0 ) return RPS_NODATA;
398 
399 		new_size = n * *format / 8;
400 
401 		if ( new_size > 0) {
402 			if ( !( a1 = realloc( *data, big_offs + offs * 4 + new_size))) {
403 				warn("Not enough memory: %ld bytes\n", offs * 4 + new_size);
404 				if ( delete_property )
405 					XDeleteProperty( DISP, window, property);
406 				XFree( prop);
407 				return ret;
408 			}
409 			*data = a1;
410 			memcpy( *data + big_offs + offs * 4, prop, new_size);
411 			*size = big_offs + (offs * 4) + new_size;
412 			if ( *size > INT_MAX) *size = INT_MAX;
413 			offs += new_size / 4;
414 			ret = RPS_PARTIAL;
415 		}
416 		XFree( prop);
417 		if ( left <= 0 || *size == INT_MAX || n * *format == 0) break;
418 	}
419 
420 	if ( delete_property )
421 		XDeleteProperty( DISP, window, property);
422 	XCHECKPOINT;
423 
424 	return RPS_OK;
425 }
426 
427 static Bool
query_datum(Handle self,Handle id,Atom query_target,Atom query_type)428 query_datum( Handle self, Handle id, Atom query_target, Atom query_type)
429 {
430 	DEFCC;
431 	XEvent ev;
432 	Atom type;
433 	int format, rps;
434 	SelectionProcData spd;
435 	unsigned long size = 0, incr = 0, old_size, delay;
436 	long timestamp;
437 	unsigned char * data;
438 	struct timeval start_time, timeout;
439 	XWindow window;
440 
441 	/* init */
442 	if ( query_target == None) return false;
443 	window = XX-> xdnd_receiving ? PWidget(guts.xdndr_receiver)->handle : WIN;
444 	if ( window == None ) return false; /* don't operate on XDND clipboard outside the drag */
445 	timestamp = XX-> xdnd_receiving ? guts.xdndr_timestamp : guts.last_time;
446 
447 	data = malloc(0);
448 	XX-> external[id]. size = CFDATA_ERROR;
449 	gettimeofday( &start_time, NULL);
450 	XCHECKPOINT;
451 
452 	Cdebug("clipboard:convert %s from %08x on %s\n", XGetAtomName( DISP, query_target), window, XGetAtomName(DISP, XX->selection));
453 	XDeleteProperty( DISP, WIN, XX-> selection);
454 	XConvertSelection( DISP, XX-> selection, query_target, XX-> selection, window, timestamp);
455 	XFlush( DISP);
456 	XCHECKPOINT;
457 
458 	/* wait for SelectionNotify */
459 	spd. selection = XX-> selection;
460 	spd. mask = SELECTION_NOTIFY_MASK;
461 	while ( 1) {
462 		Bool ok;
463 
464 		ok = XCheckIfEvent( DISP, &ev, (XIfEventProcType)selection_filter, (char*)&spd);
465 		if ( !ok ) {
466 			gettimeofday( &timeout, NULL);
467 			delay = (( timeout. tv_sec - start_time. tv_sec) * 1000 +
468 				( timeout. tv_usec - start_time. tv_usec) / 1000);
469 			if ( delay > guts. clipboard_event_timeout) {
470 				Cdebug("clipboard:selection timeout\n");
471 				goto FAIL;
472 			}
473 			XFlush( DISP);
474 			continue;
475 		}
476 		gettimeofday( &timeout, NULL);
477 		delay = 2 * (( timeout. tv_sec - start_time. tv_sec) * 1000 +
478 			( timeout. tv_usec - start_time. tv_usec) / 1000);
479 		if ( ev. type != SelectionNotify) {
480 			prima_handle_event( &ev, NULL);
481 			continue;
482 		}
483 		if ( ev. xselection. property == None) goto FAIL;
484 		Cdebug("clipboard:read SelectionNotify  %s %s\n",
485 				XGetAtomName(DISP, ev. xselection. property),
486 				XGetAtomName(DISP, ev. xselection. target));
487 		if ( prima_read_property( window, ev. xselection. property, &type, &format, &size, &data, 1) > RPS_PARTIAL)
488 			goto FAIL;
489 		XFlush( DISP);
490 		break;
491 	}
492 	XCHECKPOINT;
493 
494 	if ( type != XA_INCR) { /* ordinary, single-property selection */
495 		if ( format != CF_FORMAT(id) || type != query_type) {
496 			if ( format != CF_FORMAT(id))
497 				Cdebug("clipboard: id=%d: formats mismatch: got %d, want %d\n", id, format, CF_FORMAT(id));
498 			if ( type != query_type)
499 				Cdebug("clipboard: id=%d: types mismatch: got %s, want %s\n", id,
500 						XGetAtomName(DISP,type), XGetAtomName(DISP,query_type));
501 			return false;
502 		}
503 		XX-> external[id]. size = size;
504 		XX-> external[id]. data = data;
505 		XX-> external[id]. name = query_target;
506 		return true;
507 	}
508 
509 	/* setup INCR */
510 	if ( format != CF_32 || size < 4) goto FAIL;
511 	incr = (unsigned long) *(( Atom*) data);
512 	if ( incr == 0) goto FAIL;
513 	size = 0;
514 	spd. mask = PROPERTY_NOTIFY_MASK;
515 
516 	while ( 1) {
517 		/* wait for PropertyNotify */
518 		while ( XCheckIfEvent( DISP, &ev, (XIfEventProcType)selection_filter, (char*)&spd) == False) {
519 			gettimeofday( &timeout, NULL);
520 			if ((( timeout. tv_sec - start_time. tv_sec) * 1000 +
521 				( timeout. tv_usec - start_time. tv_usec) / 1000) > delay)
522 				goto END_LOOP;
523 		}
524 		if ( ev. type != PropertyNotify) {
525 			prima_handle_event( &ev, NULL);
526 			continue;
527 		}
528 		if ( ev. xproperty. state != PropertyNewValue) continue;
529 		start_time = timeout;
530 		old_size = size;
531 
532 		rps = prima_read_property( window, ev. xproperty. atom, &type, &format, &size, &data, 1);
533 		XFlush( DISP);
534 		if ( rps == RPS_NODATA) continue;
535 		if ( rps == RPS_ERROR) goto FAIL;
536 		if ( format != CF_FORMAT(id) || type != CF_TYPE(id)) return false;
537 		if ( size > incr ||                       /* read all in INCR */
538 			rps == RPS_PARTIAL ||                /* failed somewhere */
539 			( size == incr && old_size == size)  /* wait for empty PropertyNotify otherwise */
540 			) break;
541 	}
542 END_LOOP:
543 	XCHECKPOINT;
544 
545 	XX-> external[id]. size   = size;
546 	XX-> external[id]. data   = data;
547 	XX-> external[id]. name   = query_target;
548 	return true;
549 
550 FAIL:
551 	XCHECKPOINT;
552 	free( data);
553 	return false;
554 }
555 
556 static Bool
query_data(Handle self,Handle id)557 query_data( Handle self, Handle id)
558 {
559 	DEFCC;
560 	Atom name, type;
561 	int index = 0;
562 	Bool filter_by_targets = id != cfTargets && XX-> external[cfTargets]. size > 0;
563 
564 	/* query all types */
565 	while (( name = get_typename( id, index++, &type)) != None) {
566 		if ( filter_by_targets ) {
567 			Bool found = false;
568 			int i, length = XX->external[cfTargets].size;
569 			Atom * data   = (Atom*) XX->external[cfTargets].data;
570 			for ( i = 0; i < length / sizeof(Atom); i++) {
571 				if ( data[i] != name) continue;
572 				found = true;
573 				break;
574 			}
575 			if ( !found ) continue;
576 		}
577 
578 		if ( query_datum( self, id, name, type)) return true;
579 	}
580 	return false;
581 }
582 
583 static Atom
find_atoms(Atom * data,int length,int id)584 find_atoms( Atom * data, int length, int id)
585 {
586 	int i, index = 0;
587 	Atom name;
588 
589 	while (( name = get_typename( id, index++, NULL)) != None) {
590 		for ( i = 0; i < length / sizeof(Atom); i++) {
591 			if ( data[i] == name)
592 				return name;
593 		}
594 	}
595 	return None;
596 }
597 
598 void
prima_clipboard_query_targets(Handle self)599 prima_clipboard_query_targets( Handle self)
600 {
601 	DEFCC;
602 
603 	if ( !XX->xdnd_receiving ) {
604 		if ( XX-> external[cfTargets]. size != 0)
605 			return;
606 		query_data( self, cfTargets);
607 	}
608 
609 	/* read TARGETS, which is an array of ATOMs */
610 	if ( XX-> external[cfTargets].size > 0) {
611 		int i, size = XX-> external[cfTargets].size;
612 		Atom * data = ( Atom*)(XX-> external[cfTargets]. data);
613 		Atom ret;
614 
615 		Cdebug("clipboard targets:");
616 		for ( i = 0; i < size/sizeof(Atom); i++)
617 			Cdebug("%s\n", XGetAtomName( DISP, data[i]));
618 
619 		/* find our index for TARGETS[i], assign CFDATA_NOT_ACQUIRED to it */
620 		for ( i = 0; i < guts. clipboard_formats_count; i++) {
621 			if ( i == cfTargets) continue;
622 			ret = find_atoms( data, size, i);
623 			if (
624 				XX-> external[i]. size == 0 ||
625 				XX-> external[i]. size == CFDATA_ERROR
626 			) {
627 				XX-> external[i]. size = CFDATA_NOT_ACQUIRED;
628 				XX-> external[i]. name = ret;
629 			}
630 		}
631 	}
632 }
633 
634 Bool
apc_clipboard_has_format(Handle self,Handle id)635 apc_clipboard_has_format( Handle self, Handle id)
636 {
637 	DEFCC;
638 	if ( id >= guts. clipboard_formats_count) return false;
639 
640 	if ( XX-> inside_event) {
641 		return XX-> internal[id]. size > 0 || !XX->internal[id].immediate || XX-> external[id]. size > 0;
642 	} else {
643 		if ( XX-> internal[id]. size > 0 || !XX->internal[id].immediate) return true;
644 		prima_clipboard_query_targets(self);
645 		if ( XX-> external[cfTargets]. size > 0) {
646 			return find_atoms(
647 				(Atom*) XX-> external[cfTargets]. data,
648 				XX-> external[cfTargets]. size, id
649 			) != None;
650 		}
651 
652 		if ( XX-> external[id]. size > 0 ||
653 			XX-> external[id]. size == CFDATA_NOT_ACQUIRED)
654 			return true;
655 
656 		if ( XX-> external[id]. size == CFDATA_ERROR)
657 			return false;
658 
659 		/* selection owner does not support TARGETS, so peek */
660 		if ( XX-> external[cfTargets]. size == 0 && XX-> internal[id]. size == 0)
661 			return query_data( self, id);
662 	}
663 	return false;
664 }
665 
666 PList
apc_clipboard_get_formats(Handle self)667 apc_clipboard_get_formats( Handle self)
668 {
669 	DEFCC;
670 	int id;
671 	PList list = plist_create(guts.clipboard_formats_count, 8);
672 	Byte visited[1024]; /* 8K formats */
673 
674 	bzero( visited, sizeof(visited));
675 
676 	if ( !XX-> inside_event ) {
677 		int i, j, size;
678 		Atom * data;
679 
680 		prima_clipboard_query_targets(self);
681 		size = XX-> external[cfTargets].size;
682 		data = ( Atom*)(XX-> external[cfTargets]. data);
683 		if ( size > 0 && data != NULL ) {
684 			/* TARGETS supported */
685 			for ( i = 0; i < size/sizeof(Atom); i++, data++) {
686 				Atom atom = None;
687 				char *name = NULL;
688 				/* try to map back f.ex. text/plain to Text */
689 				for ( j = 0; j < guts.clipboard_formats_count; j++) {
690 					if (*data == XX->external[j].name) {
691 						atom = CF_NAME(j);
692 						if (atom == XA_STRING )
693 							name = "Text";
694 						else if ( atom == XA_BITMAP)
695 							name = "Image";
696 						else if ( atom == UTF8_STRING )
697 							name = "UTF8";
698 					}
699 					if ( atom != None || name != NULL ) {
700 						int ofs = j >> 3;
701 						if ( ofs < 1024 ) visited[ofs] |= 1 << (j & 7);
702 					}
703 				}
704 				if ( atom == None ) atom = *data;
705 				if ( name == NULL ) name = XGetAtomName(DISP, atom);
706 				list_add( list, (Handle) duplicate_string(name));
707 			}
708 		}
709 	}
710 
711 	for ( id = 0; id < guts. clipboard_formats_count; id++) {
712 		int ofs = id >> 3;
713 		int was_visited = ( ofs < 1024 ) ? visited[ofs] & (1 << (id & 7)) : 0;
714 		if (XX-> internal[id]. size > 0 || !XX->internal[id]. immediate || XX-> external[id]. size > 0) {
715 			char * name;
716 			if ( was_visited ) continue;
717 			switch ( id ) {
718 			case cfText:
719 				name = "Text";
720 				break;
721 			case cfUTF8:
722 				name = "UTF8";
723 				break;
724 			case cfBitmap:
725 				name = "Image";
726 				break;
727 			default:
728 				name = XGetAtomName(DISP, XX->internal[id].name);
729 			}
730 			list_add( list, (Handle) duplicate_string(name));
731 		}
732 	}
733 
734 	return list;
735 }
736 
737 static Bool
738 fill_target( Handle self, Atom target );
739 
740 static Bool
741 fill_bitmap( Handle self );
742 
743 Bool
apc_clipboard_get_data(Handle self,Handle id,PClipboardDataRec c)744 apc_clipboard_get_data( Handle self, Handle id, PClipboardDataRec c)
745 {
746 	DEFCC;
747 	STRLEN size;
748 	unsigned char * data;
749 	Bool imm;
750 
751 	if ( id >= guts. clipboard_formats_count) return false;
752 
753 	if ( !XX-> inside_event) {
754 		if ( XX-> internal[id]. size == 0) {
755 			if ( XX-> external[id]. size == CFDATA_NOT_ACQUIRED) {
756 				if ( !query_data( self, id)) return false;
757 			}
758 			if ( XX-> external[id]. size == CFDATA_ERROR) return false;
759 		}
760 	}
761 	if ( XX-> internal[id]. size == CFDATA_ERROR) return false;
762 
763 	if ( XX-> internal[id]. size > 0 || !XX-> internal[id]. immediate) {
764 		size = XX-> internal[id]. size;
765 		data = XX-> internal[id]. data;
766 		imm  = XX-> internal[id]. immediate;
767 	} else {
768 		size = XX-> external[id]. size;
769 		data = XX-> external[id]. data;
770 		imm  = true;
771 	}
772 	if ( size == 0 || data == NULL) {
773 		Bool ret;
774 		if ( imm ) return false;
775 		if ( id == cfBitmap ) {
776 			ret = fill_bitmap(self);
777 		} else {
778 			Atom name, type;
779 			int index = 0;
780 			while (( name = get_typename( id, index++, &type)) != None) {
781 				ret = fill_target(self, name);
782 				if ( ret ) break;
783 			}
784 		}
785 
786 		if ( !ret ) return false;
787 		size = XX-> internal[id]. size;
788 		data = XX-> internal[id]. data;
789 	}
790 
791 	switch ( id) {
792 	case cfBitmap:
793 		if ( XX-> internal[cfBitmap].image) {
794 			c->image = XX->internal[cfBitmap].image;
795 		} else if ( XX->external[cfBitmap].size > 0 ) {
796 			XWindow foo;
797 			Pixmap px = *(( Pixmap*)( XX-> external[id]. data ));
798 			unsigned int dummy, x, y, d;
799 			int bar;
800 			if ( !XGetGeometry( DISP, px, &foo, &bar, &bar, &x, &y, &dummy, &d))
801 				return false;
802 			c->image = (Handle) create_object("Prima::Image", "iii",
803 				"width", x,
804 				"height", y,
805 				"type", (d == 1) ? imBW : guts.qdepth
806 			);
807 			if ( !prima_std_query_image( c->image, px)) {
808 				Object_destroy(c->image);
809 				return false;
810 			}
811 		}
812 		break;
813 	case cfText:
814 	case cfUTF8: {
815 		void * ret = malloc( size);
816 		if ( !ret) {
817 			warn("Not enough memory: %d bytes\n", (int)size);
818 			return false;
819 		}
820 		memcpy( ret, data, size);
821 		c-> data   = ret;
822 		c-> length = size;
823 		break;}
824 	default: {
825 		void * ret = malloc( size);
826 		if ( !ret) {
827 			warn("Not enough memory: %d bytes\n", (int)size);
828 			return false;
829 		}
830 		memcpy( ret, data, size);
831 		c-> data = ( Byte * ) ret;
832 		c-> length = size;
833 		break;}
834 	}
835 	return true;
836 }
837 
838 Bool
apc_clipboard_set_data(Handle self,Handle id,PClipboardDataRec c)839 apc_clipboard_set_data( Handle self, Handle id, PClipboardDataRec c)
840 {
841 	DEFCC;
842 	if ( id >= guts. clipboard_formats_count) return false;
843 
844 	if ( id >= cfTargets && id < cfCOUNT ) return false;
845 	prima_detach_xfers( XX, id, true);
846 	prima_clipboard_kill_item( XX-> internal, id);
847 
848 	switch ( id) {
849 	case cfBitmap:
850 		if (( XX-> internal[id]. image = c-> image) != NULL_HANDLE) {
851 			protect_object( XX-> internal[id]. image );
852 			XX-> internal[id]. immediate = false;
853 		}
854 		break;
855 	default:
856 		if ( c-> length < 0 ) {
857 			XX-> internal[id]. immediate = false;
858 		} else {
859 			if ( !( XX-> internal[id]. data = malloc( c-> length)))
860 				return false;
861 			XX-> internal[id]. size = c-> length;
862 			memcpy( XX-> internal[id]. data, c-> data, c-> length);
863 		}
864 		break;
865 	}
866 	XX-> need_write = true;
867 	return true;
868 }
869 
870 static Bool
expand_clipboards(Handle self,int keyLen,void * key,void * dummy)871 expand_clipboards( Handle self, int keyLen, void * key, void * dummy)
872 {
873 	DEFCC;
874 	PClipboardDataItem f;
875 
876 	if ( !( f = realloc( XX-> internal,
877 		sizeof( ClipboardDataItem) * guts. clipboard_formats_count))) {
878 		guts. clipboard_formats_count--;
879 		return true;
880 	}
881 	f[ guts. clipboard_formats_count-1].size      = 0;
882 	f[ guts. clipboard_formats_count-1].data      = NULL;
883 	f[ guts. clipboard_formats_count-1].name      = CF_NAME(guts. clipboard_formats_count-1);
884 	f[ guts. clipboard_formats_count-1].immediate = true;
885 	f[ guts. clipboard_formats_count-1].image     = NULL_HANDLE;
886 	XX-> internal = f;
887 	if ( !( f = realloc( XX-> external,
888 		sizeof( ClipboardDataItem) * guts. clipboard_formats_count))) {
889 		guts. clipboard_formats_count--;
890 		return true;
891 	}
892 	f[ guts. clipboard_formats_count-1].size      = 0;
893 	f[ guts. clipboard_formats_count-1].data      = NULL;
894 	f[ guts. clipboard_formats_count-1].name      = CF_NAME(guts. clipboard_formats_count-1);
895 	f[ guts. clipboard_formats_count-1].immediate = true;       /* unused */
896 	f[ guts. clipboard_formats_count-1].image     = NULL_HANDLE;  /* unused */
897 	XX-> external = f;
898 	return false;
899 }
900 
901 Handle
apc_clipboard_register_format(Handle self,const char * format)902 apc_clipboard_register_format( Handle self, const char* format)
903 {
904 	int i;
905 	Atom x = XInternAtom( DISP, format, false);
906 	Atom *f;
907 
908 	for ( i = 0; i < guts. clipboard_formats_count; i++) {
909 		if ( x == CF_NAME(i))
910 			return i;
911 	}
912 
913 	if ( !( f = realloc( guts. clipboard_formats,
914 		sizeof( Atom) * 3 * ( guts. clipboard_formats_count + 1))))
915 		return false;
916 
917 	guts. clipboard_formats = f;
918 	CF_ASSIGN( guts. clipboard_formats_count, x, x, 8);
919 	guts. clipboard_formats_count++;
920 
921 	if ( hash_first_that( guts. clipboards, (void*)expand_clipboards, NULL, NULL, NULL))
922 		return -1;
923 
924 	return guts. clipboard_formats_count - 1;
925 }
926 
927 Bool
apc_clipboard_deregister_format(Handle self,Handle id)928 apc_clipboard_deregister_format( Handle self, Handle id)
929 {
930 	return true;
931 }
932 
933 ApiHandle
apc_clipboard_get_handle(Handle self)934 apc_clipboard_get_handle( Handle self)
935 {
936 	return C(self)-> selection;
937 }
938 
939 Bool
apc_clipboard_is_dnd(Handle self)940 apc_clipboard_is_dnd( Handle self)
941 {
942 	return guts. xdnd_clipboard == self;
943 }
944 
945 static Bool
delete_xfers(Handle self,int keyLen,void * key,XWindow * window)946 delete_xfers( Handle self, int keyLen, void * key, XWindow * window)
947 {
948 	DEFCC;
949 	if ( XX-> xfers) {
950 		int i;
951 		for ( i = 0; i < XX-> xfers-> count; i++)
952 			delete_xfer( XX, ( ClipboardXfer*) XX-> xfers-> items[i]);
953 	}
954 	hash_delete( guts. clipboard_xfers, window, sizeof( XWindow), false);
955 	return false;
956 }
957 
958 int
prima_clipboard_fill_targets(Handle self)959 prima_clipboard_fill_targets( Handle self)
960 {
961 	DEFCC;
962 	int i, count = 0, have_utf8 = 0, have_plaintext = 0;
963 	Atom * ci;
964 	prima_detach_xfers( XX, cfTargets, true);
965 	prima_clipboard_kill_item( XX-> internal, cfTargets);
966 
967 	for ( i = 0; i < guts. clipboard_formats_count; i++) {
968 		if ( i != cfTargets && (XX-> internal[i]. size > 0 || !XX-> internal[i]. immediate)) {
969 			count++;
970 			if ( i == cfUTF8) {
971 				count++;
972 				have_utf8 = 1;
973 			}
974 			if ( i == cfText) {
975 				count++;
976 				have_plaintext = 1;
977 			}
978 		}
979 	}
980 	if ( count == 0 ) return 0;
981 
982 	if (( XX-> internal[cfTargets]. data = malloc( count * sizeof( Atom)))) {
983 		Cdebug("clipboard: fill targets: ", guts. clipboard_formats_count);
984 		XX-> internal[cfTargets]. size = count * sizeof( Atom);
985 		ci = (Atom*)XX-> internal[cfTargets]. data;
986 		for ( i = 0; i < guts. clipboard_formats_count; i++) {
987 			if ( i != cfTargets && ( XX-> internal[i]. size > 0 || !XX-> internal[i]. immediate)) {
988 				*(ci++) = CF_NAME(i);
989 				Cdebug("%s ", XGetAtomName(DISP, CF_NAME(i)));
990 			}
991 		}
992 		if ( have_utf8) {
993 			*(ci++) = UTF8_MIME;
994 			Cdebug("UTF8_MIME ");
995 		}
996 		if ( have_plaintext) {
997 			*(ci++) = PLAINTEXT_MIME;
998 			Cdebug("PLAINTEXT_MIME ");
999 		}
1000 		Cdebug("\n");
1001 	}
1002 	return count;
1003 }
1004 
1005 static Bool
fill_bitmap(Handle self)1006 fill_bitmap( Handle self )
1007 {
1008 	Pixmap px;
1009 	PClipboardDataItem c;
1010 
1011 	c = &C(self)->internal[cfBitmap];
1012 	if ( !c-> image) return false;
1013 
1014 	px = prima_std_pixmap( c-> image, CACHE_LOW_RES);
1015 	if ( !px) return false;
1016 
1017 	if ( !( c-> data = malloc( sizeof( px)))) {
1018 		XFreePixmap( DISP, px);
1019 		return false;
1020 	}
1021 
1022 	c->immediate = true;
1023 	c->size = sizeof(px);
1024 	memcpy( c->data, &px, sizeof(px));
1025 
1026 	return true;
1027 }
1028 
1029 static Bool
fill_target(Handle self,Atom target)1030 fill_target( Handle self, Atom target )
1031 {
1032 	PClipboardSysData CC = C(self);
1033 
1034 	Bool event  = CC-> inside_event;
1035 	Bool flag   = exception_block(true);
1036 	Event ev    = { cmClipboard };
1037 	int h_stage = PObject(self)->stage;
1038 
1039 	CC-> inside_event = true;
1040 	CC-> opened       = true;
1041 	protect_object(self);
1042 
1043 	ev.gen.p = XGetAtomName(DISP, target);
1044 	CComponent(self)->message(self, &ev);
1045 
1046 	unprotect_object(self);
1047 	exception_block(flag);
1048 	if ( h_stage == csDead ) return false;
1049 
1050 	CC-> opened       = false;
1051 	CC-> inside_event = event;
1052 	if ( exception_charged()) return false;
1053 
1054 	return true;
1055 }
1056 
1057 static void
handle_selection_request(XEvent * ev,XWindow win,Handle self)1058 handle_selection_request( XEvent *ev, XWindow win, Handle self)
1059 {
1060 	XEvent xe;
1061 	int i, id = -1;
1062 	Atom
1063 		prop   = ev-> xselectionrequest. property,
1064 		target = ev-> xselectionrequest. target;
1065 	self = ( Handle) hash_fetch( guts. clipboards, &ev-> xselectionrequest. selection, sizeof( Atom));
1066 
1067 	guts. last_time = ev-> xselectionrequest. time;
1068 	xe. type      = SelectionNotify;
1069 	xe. xselection. send_event = true;
1070 	xe. xselection. serial    = ev-> xselectionrequest. serial;
1071 	xe. xselection. display   = ev-> xselectionrequest. display;
1072 	xe. xselection. requestor = ev-> xselectionrequest. requestor;
1073 	xe. xselection. selection = ev-> xselectionrequest. selection;
1074 	xe. xselection. target    = target;
1075 	xe. xselection. property  = None;
1076 	xe. xselection. time      = ev-> xselectionrequest. time;
1077 
1078 	Cdebug("clipboard: from %08x %s at %s\n", ev-> xselectionrequest. requestor,
1079 		XGetAtomName( DISP, ev-> xselectionrequest. target),
1080 		XGetAtomName( DISP, ev-> xselectionrequest. property)
1081 	);
1082 
1083 	if ( self) {
1084 		PClipboardSysData CC = C(self);
1085 		int format, utf8_mime = 0, plaintext_mime = 0;
1086 
1087 		for ( i = 0; i < guts. clipboard_formats_count; i++) {
1088 			if ( xe. xselection. target == CC-> internal[i]. name) {
1089 				id = i;
1090 				break;
1091 			} else if ( i == cfUTF8 && xe. xselection. target == UTF8_MIME) {
1092 				id = i;
1093 				utf8_mime = 1;
1094 				break;
1095 			} else if ( i == cfText && xe. xselection. target == PLAINTEXT_MIME) {
1096 				id = i;
1097 				plaintext_mime = 1;
1098 				break;
1099 			}
1100 		}
1101 		if ( id < 0) goto SEND_EMPTY;
1102 		for ( i = 0; i < guts. clipboard_formats_count; i++)
1103 			prima_clipboard_kill_item( CC-> external, i);
1104 
1105 		CC-> target = xe. xselection. target;
1106 		CC-> need_write = false;
1107 
1108 		if ( !CC-> internal[id]. immediate && id != cfTargets ) {
1109 			Bool ret;
1110 			ret = ( id == cfBitmap ) ? fill_bitmap(self) : fill_target(self, target);
1111 			if ( !ret ) goto SEND_EMPTY;
1112 		}
1113 
1114 		format = CF_FORMAT(id);
1115 		target = CF_TYPE( id);
1116 		if ( utf8_mime) target = UTF8_MIME;
1117 		if ( plaintext_mime) target = PLAINTEXT_MIME;
1118 
1119 		if ( id == cfTargets)
1120 			prima_clipboard_fill_targets(self);
1121 
1122 		if ( CC-> internal[id]. size > 0) {
1123 			Atom incr;
1124 			int mode = PropModeReplace;
1125 			unsigned char * data = CC-> internal[id]. data;
1126 			unsigned long size = CC-> internal[id]. size * 8 / format;
1127 			if ( CC-> internal[id]. size > guts. limits. request_length - 4) {
1128 				int ok = 0;
1129 				int reqlen = guts. limits. request_length - 4;
1130 				/* INCR */
1131 				if ( !guts. clipboard_xfers)
1132 					guts. clipboard_xfers = hash_create();
1133 				if ( !CC-> xfers)
1134 					CC-> xfers = plist_create( 1, 1);
1135 				if ( CC-> xfers && guts. clipboard_xfers) {
1136 					ClipboardXfer * x = malloc( sizeof( ClipboardXfer));
1137 					if ( x) {
1138 						IV refcnt;
1139 						ClipboardXferKey key;
1140 
1141 						bzero( x, sizeof( ClipboardXfer));
1142 						list_add( CC-> xfers, ( Handle) x);
1143 						x-> size = CC-> internal[id]. size;
1144 						x-> data = CC-> internal[id]. data;
1145 						x-> blocks = ( x-> size / reqlen ) + ( x-> size % reqlen) ? 1 : 0;
1146 						x-> requestor = xe. xselection. requestor;
1147 						x-> property  = prop;
1148 						x-> target    = xe. xselection. target;
1149 						x-> self      = self;
1150 						x-> format    = format;
1151 						x-> id        = id;
1152 						gettimeofday( &x-> time, NULL);
1153 
1154 						CLIPBOARD_XFER_KEY( key, x-> requestor, x-> property);
1155 						hash_store( guts. clipboard_xfers, key, sizeof(key), (void*) x);
1156 						refcnt = PTR2IV( hash_fetch( guts. clipboard_xfers, &x-> requestor, sizeof( XWindow)));
1157 						if ( refcnt++ == 0)
1158 							XSelectInput( DISP, x-> requestor, PropertyChangeMask|StructureNotifyMask);
1159 						hash_store( guts. clipboard_xfers, &x-> requestor, sizeof(XWindow), INT2PTR( void*, refcnt));
1160 
1161 						format = CF_32;
1162 						size = 1;
1163 						incr = ( Atom) CC-> internal[id]. size;
1164 						data = ( unsigned char*) &incr;
1165 						ok = 1;
1166 						target = XA_INCR;
1167 						Cdebug("clipboard: init INCR for %08x %d\n", x-> requestor, x-> property);
1168 					}
1169 				}
1170 				if ( !ok) size = reqlen;
1171 			}
1172 
1173 			if ( format == CF_32) format = 32;
1174 			XChangeProperty(
1175 				xe. xselection. display,
1176 				xe. xselection. requestor,
1177 				prop, target, format, mode, data, size);
1178 			Cdebug("clipboard: store prop %s f=%d s=%d\n", XGetAtomName( DISP, prop), format, size);
1179 			xe. xselection. property = prop;
1180 		}
1181 
1182 		/* content of PIXMAP or BITMAP is seemingly gets invalidated
1183 			after the selection transfer, unlike the string data format */
1184 		if ( id == cfBitmap) {
1185 			bzero( CC-> internal[id].data, CC-> internal[id].size);
1186 			bzero( CC-> external[id].data, CC-> external[id].size);
1187 			prima_clipboard_kill_item( CC-> internal, id);
1188 			prima_clipboard_kill_item( CC-> external, id);
1189 		}
1190 	}
1191 SEND_EMPTY:
1192 
1193 	XSendEvent( xe.xselection.display, xe.xselection.requestor, false, 0, &xe);
1194 	XFlush( DISP);
1195 	Cdebug("clipboard:id %d, SelectionNotify to %08x , %s %s\n", id, xe.xselection.requestor,
1196 		xe. xselection. property ? XGetAtomName( DISP, xe. xselection. property) : "None",
1197 		XGetAtomName( DISP, xe. xselection. target));
1198 	exception_check_raise();
1199 }
1200 
1201 static void
handle_selection_clear(XEvent * ev,XWindow win,Handle self)1202 handle_selection_clear( XEvent *ev, XWindow win, Handle self)
1203 {
1204 	guts. last_time = ev-> xselectionclear. time;
1205 	if ( XGetSelectionOwner( DISP, ev-> xselectionclear. selection) != WIN) {
1206 		Handle c = ( Handle) hash_fetch( guts. clipboards,
1207 			&ev-> xselectionclear. selection, sizeof( Atom));
1208 		guts. last_time = ev-> xselectionclear. time;
1209 		if (c) {
1210 			int i;
1211 			C(c)-> selection_owner = NULL_HANDLE;
1212 			for ( i = 0; i < guts. clipboard_formats_count; i++) {
1213 				prima_detach_xfers( C(c), i, true);
1214 				prima_clipboard_kill_item( C(c)-> external, i);
1215 				prima_clipboard_kill_item( C(c)-> internal, i);
1216 			}
1217 		}
1218 	}
1219 }
1220 
1221 static void
handle_property_notify(XEvent * ev,XWindow win,Handle self)1222 handle_property_notify( XEvent *ev, XWindow win, Handle self)
1223 {
1224 	unsigned long offs, size, reqlen = guts. limits. request_length - 4, format;
1225 	ClipboardXfer * x = ( ClipboardXfer *) self;
1226 	PClipboardSysData CC = C(x-> self);
1227 
1228 	if ( ev-> xproperty. state != PropertyDelete)
1229 		return;
1230 
1231 	offs = x-> offset * reqlen;
1232 	if ( offs >= x-> size) { /* clear termination */
1233 		size = 0;
1234 		offs = 0;
1235 	} else {
1236 		size = x-> size - offs;
1237 		if ( size > reqlen) size = reqlen;
1238 	}
1239 	Cdebug("clipboard: put %d %d in %08x %d\n", x-> offset, size, x-> requestor, x-> property);
1240 	if ( x-> format > 8)  size /= 2;
1241 	if ( x-> format > 16) size /= 2;
1242 	format = ( x-> format == CF_32) ? 32 : x-> format;
1243 	XChangeProperty( DISP, x-> requestor, x-> property, x-> target,
1244 		format, PropModeReplace,
1245 		x-> data + offs, size);
1246 	XFlush( DISP);
1247 	x-> offset++;
1248 	if ( size == 0) delete_xfer( CC, x);
1249 }
1250 
1251 static void
handle_destroy_notify(XEvent * ev,XWindow win,Handle self)1252 handle_destroy_notify( XEvent *ev, XWindow win, Handle self)
1253 {
1254 	Cdebug("clipboard: destroy xfers at %08x\n", ev-> xdestroywindow. window);
1255 	hash_first_that( guts. clipboards, (void*)delete_xfers, (void*) &ev-> xdestroywindow. window, NULL, NULL);
1256 	XFlush( DISP);
1257 }
1258 
1259 void
prima_handle_selection_event(XEvent * ev,XWindow win,Handle self)1260 prima_handle_selection_event( XEvent *ev, XWindow win, Handle self)
1261 {
1262 	XCHECKPOINT;
1263 	switch ( ev-> type) {
1264 	case SelectionRequest:
1265 		handle_selection_request(ev, win, self);
1266 		break;
1267 	case SelectionClear:
1268 		handle_selection_clear(ev, win, self);
1269 		break;
1270 	case PropertyNotify:
1271 		handle_property_notify(ev, win, self);
1272 		break;
1273 	case DestroyNotify:
1274 		handle_destroy_notify(ev, win, self);
1275 		break;
1276 	}
1277 	XCHECKPOINT;
1278 }
1279 
1280