add to set transient_for at main_win
[framework/uifw/cbhm.git] / src / xcnphandler.c
1 #include "common.h"
2 #include "cbhm_main.h"
3 #include "xcnphandler.h"
4 #include "storage.h"
5 #include "clipdrawer.h"
6
7 static Ecore_Event_Handler *xsel_clear_handler = NULL;
8 static Ecore_Event_Handler *xsel_request_handler = NULL;
9 static Ecore_Event_Handler *xsel_notify_handler = NULL;
10 static Ecore_Event_Handler *xclient_msg_handler = NULL;
11 static Ecore_Event_Handler *xfocus_out_handler = NULL;
12
13 char *g_lastest_content = NULL;
14 int g_history_pos = 0;
15
16 int xcnp_init(void *data)
17 {
18         struct appdata *ad = data;
19         DTRACE("xcnp_init().. start!\n");
20
21         if(!_cbhm_init(ad))
22         {
23                 DTRACE("Failed - _cbhm_init()..!\n");
24                 return NULL;
25         }
26
27         //Adding Event Handlers
28         xsel_clear_handler = ecore_event_handler_add(ECORE_X_EVENT_SELECTION_CLEAR, _xsel_clear_cb, ad);
29         xsel_request_handler = ecore_event_handler_add(ECORE_X_EVENT_SELECTION_REQUEST, _xsel_request_cb, ad);
30         xsel_notify_handler = ecore_event_handler_add(ECORE_X_EVENT_SELECTION_NOTIFY, _xsel_notify_cb, ad);
31         xclient_msg_handler = ecore_event_handler_add(ECORE_X_EVENT_CLIENT_MESSAGE, _xclient_msg_cb, ad);
32         xfocus_out_handler = ecore_event_handler_add(ECORE_X_EVENT_WINDOW_FOCUS_OUT, _xfocus_out_cb, ad);
33
34         if(!xsel_clear_handler)
35                 DTRACE("Failed to add ECORE_X_EVENT_SELECTION_CLEAR handler\n");
36         if(!xsel_request_handler)
37                 DTRACE("Failed to add ECORE_X_EVENT_SELECTION_REQUEST handler\n");
38         if(!xsel_notify_handler)
39                 DTRACE("Failed to add ECORE_X_EVENT_SELECTION_NOTIFY handler\n");
40         if(!xclient_msg_handler)
41                 DTRACE("Failed to add ECORE_X_EVENT_CLIENT_MESSAGE handler\n");
42         if(!xfocus_out_handler)
43                 DTRACE("Failed to add ECORE_X_EVENT_WINDOW_FOCUS_OUT handler\n");
44
45         return TRUE;
46 }
47
48 int xcnp_shutdown()
49 {
50         //Removing Event Handlers
51         ecore_event_handler_del(xsel_clear_handler);
52         ecore_event_handler_del(xsel_request_handler);
53         ecore_event_handler_del(xsel_notify_handler);
54         ecore_event_handler_del(xclient_msg_handler);
55         ecore_event_handler_del(xfocus_out_handler);
56
57         xsel_clear_handler = NULL;
58         xsel_request_handler = NULL;
59         xsel_notify_handler = NULL;
60         xclient_msg_handler = NULL;
61         xfocus_out_handler = NULL;
62
63         _cbhm_fini();
64
65         return TRUE;
66 }
67
68 static int _init_atoms()
69 {
70         /* all atoms are global variables */
71         atomPrimary = XA_PRIMARY; 
72         atomSecondary = XA_SECONDARY; 
73 //      atomTarget = XA_STRING;
74         atomClipboard = XInternAtom(g_disp, ATOM_CLIPBOARD_NAME, False);
75         atomCBHM = XInternAtom (g_disp, ATOM_CLIPBOARD_MANAGER_NAME, False);
76         atomCBOut = XInternAtom(g_disp, ATOM_CBHM_OUTBUF, False);
77         atomInc = XInternAtom(g_disp, "INCR", False);
78         atomTargets = XInternAtom(g_disp, "TARGETS", False);
79         atomUTF8String = XInternAtom(g_disp, "UTF8_STRING", False);
80         atomHtmltext = XInternAtom(g_disp, "text/html;charset=utf-8", False);
81
82         return TRUE;
83 }
84
85 static void _set_cbhmwin_prop()
86 {
87         Atom atomCbhmWin = XInternAtom(g_disp, "CBHM_XWIN", False);
88         XChangeProperty(g_disp, g_rootwin, atomCbhmWin, XA_WINDOW, 
89                                         32, PropModeReplace,
90                                         (unsigned char *)&g_evtwin, (int) 1);
91 }
92
93 int increment_current_history_position()
94 {
95         int pos = g_history_pos+1;
96         if (pos >= HISTORY_QUEUE_MAX_ITEMS)
97                 pos = 0;
98         g_history_pos = pos;
99         return pos;
100 }
101
102 int get_current_history_position()
103 {
104         int pos = g_history_pos-1;
105         if (pos < 0)
106                 pos = HISTORY_QUEUE_MAX_ITEMS-1;
107         
108         return pos;
109 }
110
111 int add_to_storage_buffer(void *data, char *src, int len)
112 {
113         struct appdata *ad = data;
114
115         if (len <= 0)
116                 return -1;
117
118         if (g_lastest_content == NULL)
119                 g_lastest_content = malloc(sizeof(char)*(HISTORY_QUEUE_ITEM_SIZE));
120         if (g_history_pos >= HISTORY_QUEUE_MAX_ITEMS)
121                 g_history_pos = 0;
122
123         // FIXME: remove g_lasteset_content
124         //strcpy(g_lastest_content, src);
125         memcpy(g_lastest_content, src, len);
126         g_lastest_content[len] = '\0';
127         adding_item_to_storage(g_history_pos, g_lastest_content);
128         increment_current_history_position();
129
130         int nserial = 0;
131         nserial = get_storage_serial_code();
132         Atom atomCbhmSerial = XInternAtom(g_disp, "CBHM_SERIAL_NUMBER", False);
133         XChangeProperty(g_disp, g_evtwin, atomCbhmSerial, XA_INTEGER,
134                                         32, PropModeReplace,
135                                         (unsigned char *)&nserial, (int) 1);
136         XFlush(g_disp);
137
138         clipdrawer_update_contents(ad);
139
140         return 0;
141 }
142
143 int print_storage_buffer()
144 {
145         int pos;
146         int i = 0;
147         for (i = 0; i < HISTORY_QUEUE_MAX_ITEMS; i++)
148         {
149                 pos = get_current_history_position()+i;
150                 if (pos > HISTORY_QUEUE_MAX_ITEMS-1)
151                         pos = pos-HISTORY_QUEUE_MAX_ITEMS;
152                 DTRACE("%d: %s\n", i, get_item_contents_by_pos(pos) != NULL ? get_item_contents_by_pos(pos) : "NULL");
153         }
154 }
155
156 int send_convert_selection()
157 {
158         XConvertSelection(g_disp, atomClipboard, atomUTF8String, atomCBOut, g_evtwin, CurrentTime);
159         DTRACE("sent convert selection\n");
160         return 0;
161 }
162
163 int set_clipboard_manager_owner()
164 {
165         XSetSelectionOwner(g_disp, atomCBHM, g_evtwin, CurrentTime);
166         Ecore_X_Window selowner_window = XGetSelectionOwner(g_disp, atomCBHM);
167         DTRACE("g_evtwin = 0x%x, setted clipboard manager owner is = 0x%x\n", g_evtwin, selowner_window);
168         return 0;
169 }
170
171 int set_selection_owner()
172 {
173         XSetSelectionOwner(g_disp, atomClipboard, g_evtwin, CurrentTime);
174         Ecore_X_Window selowner_window = XGetSelectionOwner(g_disp, atomClipboard);
175         DTRACE("evtwin = 0x%x, setted selection owner is = 0x%x\n", g_evtwin, selowner_window);
176         return 0;
177 }
178
179 int get_selection_content(void *data)
180 {
181         Atom cbtype;
182         int cbformat;
183         unsigned long cbsize, cbitems;
184         unsigned char *cbbuf;
185         struct appdata *ad = data;
186         const char *unesc;
187         size_t unesc_len = 0;
188         int i;
189
190         XGetWindowProperty(g_disp, g_evtwin, atomCBOut, 0, 0, False,
191                                            AnyPropertyType, &cbtype, &cbformat, &cbitems, &cbsize, &cbbuf);
192         XFree(cbbuf);
193
194         if (cbtype == atomInc)
195         {
196                 XDeleteProperty(g_disp, g_evtwin, atomCBOut);
197                 XFlush(g_disp);
198                 DTRACE("INCR \n");
199                 return -1;
200         }
201
202         DTRACE("cbsize = %d\n", cbsize);
203
204         if (cbformat != 8)
205         {
206                 DTRACE("There're nothing to read = %d\n", cbformat);
207                 return -2;
208         }
209
210         XGetWindowProperty(g_disp, g_evtwin, atomCBOut, 0, (long) cbsize, False,
211                                            AnyPropertyType, &cbtype, &cbformat, &cbitems, &cbsize, &cbbuf);
212         XDeleteProperty(g_disp, g_evtwin, atomCBOut);
213
214         unesc = clipdrawer_get_plain_string_from_escaped(cbbuf);
215         if (unesc != NULL)
216         {
217                 unesc_len = strlen(unesc);
218                 // FIXME: invent more clever way to right trim the string
219                 for (i = unesc_len-1; i > 0; i--)
220                 {
221                         // avoid control characters
222                         if (unesc[i] >= 0x01 && unesc[i] <= 0x1F)
223                                 continue;
224                         else
225                         {
226                                 DTRACE("before right trim len = %d\n", unesc_len);
227                                 unesc_len = i+1;
228                                 DTRACE("after right trim len = %d\n", unesc_len);
229                                 break;
230                         }
231                 }
232         }
233         else
234                 unesc_len = 0;
235
236 //      add_to_storage_buffer(ad, cbbuf, cbitems);
237 //      DTRACE("len = %ld, data = %s\n", cbitems, cbbuf);
238
239 /*
240         if (cbbuf != NULL)
241         {
242                 unesc_len = strlen(cbbuf);
243                 // FIXME: invent more clever way to right trim the string
244                 for (i = unesc_len-1; i > 0; i--)
245                 {
246                         // avoid control characters
247                         if (cbbuf[i] >= 0x01 && cbbuf[i] <= 0x1F)
248                                 continue;
249                         else
250                         {
251                                 DTRACE("before right trim len = %d\n", unesc_len);
252                                 unesc_len = i+1;
253                                 DTRACE("after right trim len = %d\n", unesc_len);
254                                 break;
255                         }
256                 }
257         }
258         else
259                 unesc_len = 0;
260
261         if (!strncmp(cbbuf, "file://", 7) && 
262                 (strcasestr(cbbuf,".png") || strcasestr(cbbuf,".jpg") || strcasestr(cbbuf,".bmp")) &&
263                 check_regular_file(cbbuf+7))
264         {
265                 DTRACE("clipdrawer add path = %s\n", cbbuf+7);
266                 clipdrawer_add_item(cbbuf+7, GI_IMAGE);
267         }
268         else
269         {
270                 add_to_storage_buffer(ad, cbbuf, unesc_len);
271                 clipdrawer_add_item(cbbuf, GI_TEXT);
272         }
273         DTRACE("len = %ld, data = %s\n", unesc_len, cbbuf);
274 */
275
276
277         /* FIXME : it needs two verification. 
278                1. does the file exist?
279                2. dose the file wanted type? */
280         if (!strncmp(unesc, "file://", 7) && 
281                 (strcasestr(unesc,".png") || strcasestr(unesc,".jpg") || strcasestr(unesc,".bmp")) &&
282                 check_regular_file(unesc+7))
283         {
284                 DTRACE("clipdrawer add path = %s\n", unesc+7);
285                 clipdrawer_add_item(unesc+7, GI_IMAGE);
286         }
287         else
288         {
289                 add_to_storage_buffer(ad, unesc, unesc_len);
290                 clipdrawer_add_item(unesc, GI_TEXT);
291         }
292         DTRACE("len = %ld, data = %s\n", unesc_len, unesc);
293         free(unesc);
294
295         DTRACE("\n");
296         print_storage_buffer();
297         DTRACE("\n");
298
299         XFree(cbbuf);
300
301         return 0;
302 }
303
304 int processing_selection_request(Ecore_X_Event_Selection_Request *ev)
305 {
306         XEvent req_evt;
307         int req_size;
308         Ecore_X_Window req_win;
309         Atom req_atom;
310
311         req_size = XExtendedMaxRequestSize(g_disp) / 4;
312         if (!req_size)
313         {
314                 req_size = XMaxRequestSize(g_disp) / 4;
315         }
316
317     req_win = ev->requestor;
318         req_atom = ev->property;
319
320         DTRACE("## wanted target = %d\n", ev->target);
321         DTRACE("## wanted target = %s\n", XGetAtomName(g_disp, ev->target));
322         DTRACE("## req target atom name = %s\n", XGetAtomName(g_disp, ev->target));
323
324         /* TODO : if there are request which cbhm doesn't understand,
325            then reply None property to requestor */
326         /* TODO : add image type */
327         if (ev->target == atomTargets) 
328         {
329 //        Atom types[2] = { atomTargets, atomUTF8String };
330         Atom types[3] = { atomTargets, atomUTF8String, atomHtmltext };
331
332         // send all (not using INCR) 
333         XChangeProperty(g_disp, req_win, req_atom, XA_ATOM,
334                                                 32, PropModeReplace, (unsigned char *) types,
335                                                 (int) (sizeof(types) / sizeof(Atom)));
336                 DTRACE("target matched\n");
337     }
338     else
339         {
340                 DTRACE("target mismatched. trying to txt\n");
341
342                 int txt_len;
343                 if (g_lastest_content != NULL)
344                         txt_len = strlen(g_lastest_content);
345                 else
346                         txt_len = 0;
347
348                 if (txt_len > req_size) 
349                 {
350                         // INCR 
351                 }
352                 else 
353                 {
354                         // send all (not using INCR) 
355                         XChangeProperty(g_disp, req_win, req_atom, atomUTF8String, 
356                                                         8, PropModeReplace, (unsigned char *) g_lastest_content, (int) txt_len);
357                 }
358                 DTRACE("txt target, len = %d\n", txt_len);
359                 if (txt_len > 0 && g_lastest_content != NULL)
360                         DTRACE("txt target, content = %s\n", g_lastest_content);
361         }
362
363 /*
364         DTRACE("wanted window = 0x%x\n", req_win);
365         DTRACE("wanted target = %s\n", XGetAtomName(g_disp, ev->target));
366 */
367         DTRACE("selection target = %s\n", XGetAtomName(g_disp, ev->selection));
368         DTRACE("getted atom name = %s\n", XGetAtomName(g_disp, req_atom));
369         DTRACE("req target atom name = %s\n", XGetAtomName(g_disp, ev->target));
370
371     req_evt.xselection.property = req_atom;
372     req_evt.xselection.type = SelectionNotify;
373     req_evt.xselection.display = g_disp;
374     req_evt.xselection.requestor = req_win;
375     req_evt.xselection.selection = ev->selection;
376     req_evt.xselection.target = ev->target;
377     req_evt.xselection.time = ev->time;
378
379     XSendEvent(g_disp, ev->requestor, 0, 0, &req_evt);
380     XFlush(g_disp);
381
382         return 0;
383 }
384
385 static int _cbhm_init(void *data)
386 {
387         struct appdata *ad = data;
388         //Set default data structure
389         //Control that the libraries are properly initialized
390         if (!ecore_init()) return EXIT_FAILURE;
391         if (!ecore_evas_init()) return EXIT_FAILURE;
392         if (!edje_init()) return EXIT_FAILURE;
393
394         // do not x init in this module, it disconnect previous e17's X connection
395         //ecore_x_init(NULL);
396
397         g_disp = ecore_x_display_get();
398
399         if( !g_disp )
400         {
401                 DTRACE("Failed to get display\n");
402                 return -1;
403         }
404
405         g_rootwin = DefaultRootWindow(g_disp);
406         g_evtwin = ecore_x_window_new(g_rootwin, 0, 0, 19, 19);
407         ecore_x_netwm_name_set(g_evtwin, CLIPBOARD_MANAGER_WINDOW_TITLE_STRING);
408
409         XSelectInput(g_disp, g_evtwin, PropertyChangeMask);
410         XSelectInput(g_disp, g_rootwin, StructureNotifyMask);
411 //      ecore_x_window_show(g_evtwin);
412         ecore_x_flush();
413
414         _set_cbhmwin_prop();
415     _init_atoms();
416         init_storage();
417
418         DTRACE("_cbhm_init ok\n");
419
420         set_clipboard_manager_owner();
421         send_convert_selection();
422         set_selection_owner();
423
424         return TRUE;
425 }
426
427 static void _cbhm_fini()
428 {
429         close_storage();
430
431         return;
432 }
433
434 static int _xsel_clear_cb(void *data, int ev_type, void *event)
435 {
436         struct appdata *ad = data;
437
438         Ecore_X_Event_Selection_Clear *ev = (Ecore_X_Event_Selection_Clear *)event;
439
440         if (ev->selection != ECORE_X_SELECTION_CLIPBOARD)
441                 return TRUE;
442         
443         DTRACE("XE:SelectionClear\n");
444
445         send_convert_selection();
446         ecore_x_flush();
447         /* TODO : set selection request is should after convert selection
448          * is done */
449         set_selection_owner();
450
451         return TRUE;
452 }
453
454 static int _xsel_request_cb(void *data, int ev_type, void *event)
455 {
456         Ecore_X_Event_Selection_Request *ev = (Ecore_X_Event_Selection_Request *)event;
457
458         if (ev->selection != atomClipboard)
459                 return TRUE;
460
461         DTRACE("XE:SelectionRequest\n");
462
463         processing_selection_request(ev);
464
465         return TRUE;
466 }
467
468 static int _xsel_notify_cb(void *data, int ev_type, void *event)
469 {
470         struct appdata *ad = data;
471
472         Ecore_X_Event_Selection_Notify *ev = (Ecore_X_Event_Selection_Notify *)event;
473         Ecore_X_Selection_Data_Text *text_data = NULL;
474
475         if (ev->selection != ECORE_X_SELECTION_CLIPBOARD)
476                 return TRUE;
477         
478         DTRACE("XE:SelectionNotify\n");
479
480         text_data = ev->data;
481         DTRACE("content type = %d\n", text_data->data.content);
482         get_selection_content(ad);
483
484         if (text_data->data.content != ECORE_X_SELECTION_CONTENT_TEXT)
485         {
486                 DTRACE("content isn't text\n");
487         }
488
489         return TRUE;
490 }
491
492 static int _xclient_msg_cb(void *data, int ev_type, void *event)
493 {
494         struct appdata *ad = data;
495
496         Ecore_X_Event_Client_Message *ev = (Ecore_X_Event_Client_Message*)event;
497
498         Atom atomCBHM_MSG = XInternAtom(g_disp, "CBHM_MSG", False);
499         Atom atomCBHM_cRAW = XInternAtom(g_disp, "CBHM_cRAW", False);
500         Atom atomXKey_MSG = XInternAtom(g_disp, "_XKEY_COMPOSITION", False);
501         char atomname[10];
502         Atom cbhm_atoms[HISTORY_QUEUE_MAX_ITEMS];
503         Ecore_X_Window reqwin = ev->win;
504         int i, pos;
505
506         if (ev->message_type == atomXKey_MSG)
507         {
508                 DTRACE("XE:ClientMessage for Screen capture\n");
509
510                 capture_current_screen(ad);
511
512                 return TRUE;
513         }
514
515         if (ev->message_type != atomCBHM_MSG)
516                 return -1;
517
518         DTRACE("XE:ClientMessage for CBHM\n");
519
520         DTRACE("## %s\n", ev->data.b);
521
522         if (strcmp("get count", ev->data.b) == 0)
523         {
524                 int icount = get_item_counts();
525                 char countbuf[10];
526                 DTRACE("## cbhm count : %d\n", icount);
527                 sprintf(countbuf, "%d", icount);
528                 sprintf(atomname, "CBHM_cCOUNT");
529                 cbhm_atoms[0] = XInternAtom(g_disp, atomname, False);
530                 XChangeProperty(g_disp, reqwin, cbhm_atoms[0], atomUTF8String, 
531                                                 8, PropModeReplace, 
532                                                 (unsigned char *) countbuf, 
533                                                 (int) strlen(countbuf));
534         
535         }
536         else if (strncmp("get #", ev->data.b, 5) == 0)
537         {
538                 // FIXME : handle greater than 9
539                 int num = ev->data.b[5] - '0';
540                 int cur = get_current_history_position();
541                 num = cur + num - 1;
542                 if (num > HISTORY_QUEUE_MAX_ITEMS-1)
543                         num = num-HISTORY_QUEUE_MAX_ITEMS;
544
545                 if (num >= 0 && num < HISTORY_QUEUE_MAX_ITEMS)
546                 {
547                         DTRACE("## pos : #%d\n", num);
548                         // FIXME : handle with correct atom
549                         sprintf(atomname, "CBHM_c%d", num);
550                         cbhm_atoms[0] = XInternAtom(g_disp, atomname, False);
551                         if (get_item_contents_by_pos(num) != NULL)
552                         {
553                                 XChangeProperty(g_disp, reqwin, cbhm_atoms[0], atomUTF8String, 
554                                                                 8, PropModeReplace, 
555                                                                 (unsigned char *) get_item_contents_by_pos(num), 
556                                                                 (int) strlen(get_item_contents_by_pos(num)));
557                         }
558                 }
559         }
560         else if (strcmp("get all", ev->data.b) == 0)
561         {
562 //              print_history_buffer();
563                 pos = get_current_history_position();
564                 for (i = 0; i < 5; i++)
565                 {
566                         DTRACE("## %d -> %d\n", i, pos);
567                         sprintf(atomname, "CBHM_c%d", i);
568                         cbhm_atoms[i] = XInternAtom(g_disp, atomname, False);
569                         if (get_item_contents_by_pos(pos) != NULL)
570                         {
571                                 XChangeProperty(g_disp, reqwin, cbhm_atoms[i], atomUTF8String, 
572                                                                 8, PropModeReplace, 
573                                                                 (unsigned char *) get_item_contents_by_pos(pos) , 
574                                                                 (int) strlen(get_item_contents_by_pos(pos)));
575                         }
576                         pos--;
577                         if (pos < 0)
578                                 pos = HISTORY_QUEUE_MAX_ITEMS-1;
579                 }
580         }
581         else if (strcmp("get raw", ev->data.b) == 0)
582         {
583                 if (get_storage_start_addr != NULL)
584                 {
585                         XChangeProperty(g_disp, reqwin, atomCBHM_cRAW, atomUTF8String, 
586                                                         8, PropModeReplace, 
587                                                         (unsigned char *) get_storage_start_addr(),
588                                                         (int) get_total_storage_size());
589                 }
590         }
591         else if (strcmp("show", ev->data.b) == 0)
592         {
593                 clipdrawer_activate_view(ad);
594         }
595
596         XFlush(g_disp);
597
598         return TRUE;
599 }
600
601 static int _xfocus_out_cb(void *data, int ev_type, void *event)
602 {
603         struct appdata *ad = data;
604
605         DTRACE("XE:FOCUS OUT\n");
606
607         clipdrawer_lower_view(ad);
608
609         return TRUE;
610 }
611
612 void set_transient_for(void *data)
613 {
614         struct appdata *ad = data;
615
616         Ecore_X_Window xwin_active = None;
617         Atom atomActive = XInternAtom(g_disp, "_NET_ACTIVE_WINDOW", False);
618
619         if (ecore_x_window_prop_window_get(DefaultRootWindow(g_disp), 
620                                                                            atomActive, &xwin_active, 1) != -1)
621         {
622                 ecore_x_icccm_transient_for_set (elm_win_xwindow_get(ad->win_main), xwin_active);
623                 DTRACE("Success to set transient_for active window = 0x%X\n", xwin_active);
624         }
625         else
626         {
627                 DTRACE("Failed to find active window for transient_for\n");
628         }
629 }
630
631 void unset_transient_for(void *data)
632 {
633         struct appdata *ad = data;
634
635         ecore_x_icccm_transient_for_unset(elm_win_xwindow_get(ad->win_main));
636 }
637
638 static Ecore_X_Window get_selection_secondary_target_win()
639 {
640         Atom actual_type;
641         int actual_format;
642         unsigned long nitems, bytes_after;
643         unsigned char *prop_return = NULL;
644         Atom atomCbhmXTarget = XInternAtom(g_disp, "CBHM_XTARGET", False);
645         static Ecore_X_Window xtarget = None;
646         if (xtarget != None)
647                 return xtarget;
648
649         if(Success == 
650            XGetWindowProperty(g_disp, DefaultRootWindow(g_disp), atomCbhmXTarget, 
651                                                   0, sizeof(Ecore_X_Window), False, XA_WINDOW, 
652                                                   &actual_type, &actual_format, &nitems, &bytes_after, &prop_return) && 
653            prop_return)
654         {
655                 xtarget = *(Ecore_X_Window*)prop_return;
656                 XFree(prop_return);
657                 fprintf(stderr, "## find clipboard secondary target at root\n");
658         }
659         return xtarget;
660 }
661
662 int set_selection_secondary_data(char *sdata)
663 {
664         Ecore_X_Window setwin = get_selection_secondary_target_win();
665         if (setwin == None)
666                 return 0;
667
668         if (sdata == NULL)
669                 return 0;
670
671         int slen = strlen(sdata);
672
673         fprintf(stderr, "## cbhm xwin = 0x%x, d = %s\n", setwin, sdata);
674
675         ecore_x_selection_secondary_set(setwin, sdata, slen);
676 }