version update
[goodguy/cinelerra.git] / cinelerra-5.1 / guicast / bcclipboard.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bcclipboard.h"
23 #include "bcdisplay.h"
24 #include "bcresources.h"
25 #include "bcsignals.h"
26 #include "bcwindowbase.h"
27 #include "bcwindowbase.inc"
28 #include <string.h>
29 #include <unistd.h>
30
31 BC_Clipboard::BC_Clipboard(BC_WindowBase *window)
32  : Thread(1, 0, 0)
33 {
34         this->window = window;
35         const char *display_name = window->display_name;
36
37 #ifdef SINGLE_THREAD
38         in_display = out_display = BC_Display::get_display(display_name);
39 #else
40         in_display = BC_WindowBase::init_display(display_name);
41         out_display = BC_WindowBase::init_display(display_name);
42 #endif
43
44         completion_atom = XInternAtom(out_display, "BC_CLOSE_EVENT", False);
45         xa_primary = XA_PRIMARY;
46         clipboard = XInternAtom(out_display, "CLIPBOARD", False);
47         targets = XInternAtom(out_display, "TARGETS", False);
48         string_type = !BC_Resources::locale_utf8 ? XA_STRING :
49                  XInternAtom(out_display, "UTF8_STRING", False);
50         in_win = XCreateSimpleWindow(in_display,
51                 DefaultRootWindow(in_display), 0, 0, 1, 1, 0, 0, 0);
52         out_win = XCreateSimpleWindow(out_display,
53                 DefaultRootWindow(out_display), 0, 0, 1, 1, 0, 0, 0);
54
55         for( int i=0; i<CLIP_BUFFERS; ++i ) {
56                 data_buffer[i] = 0;
57                 data_length[i] = 0;
58         }
59 }
60
61 BC_Clipboard::~BC_Clipboard()
62 {
63         for( int i=0; i<CLIP_BUFFERS; ++i ) {
64                 delete [] data_buffer[i];
65         }
66         XDestroyWindow(in_display, in_win);
67         XCloseDisplay(in_display);
68         XDestroyWindow(out_display, out_win);
69         XCloseDisplay(out_display);
70 }
71
72 int BC_Clipboard::start_clipboard()
73 {
74 #ifndef SINGLE_THREAD
75         Thread::start();
76 #endif
77         return 0;
78 }
79
80 int BC_Clipboard::stop_clipboard()
81 {
82 // if closing clipboard with selection, move data to CUT_BUFFER0
83         char *data = 0;  int len = 0;
84         for( int i=0; !data && i<CLIP_BUFFERS; ++i ) {
85                 data = data_buffer[i];  len = data_length[i];
86         }
87         if( data ) XStoreBuffer(out_display, data, len, 0);
88 #ifdef SINGLE_THREAD
89         XFlush(in_display);
90 #else
91         XFlush(in_display);
92         XFlush(out_display);
93 #endif
94 // Must use a different display handle to send events.
95         const char *display_name = window->display_name;
96         Display *display = BC_WindowBase::init_display(display_name);
97         XEvent event;  memset(&event, 0, sizeof(event));
98         XClientMessageEvent *ptr = (XClientMessageEvent*)&event;
99
100         event.type = ClientMessage;
101         ptr->message_type = completion_atom;
102         ptr->format = 32;
103         XSendEvent(display, out_win, 0, 0, &event);
104         XFlush(display);
105         XCloseDisplay(display);
106
107         Thread::join();
108         return 0;
109 }
110
111 void BC_Clipboard::run()
112 {
113         XEvent event;
114         int done = 0;
115 #ifndef SINGLE_THREAD
116 #ifndef NO_XSELECT
117         int x_fd = ConnectionNumber(out_display);
118 #endif
119 #endif
120
121         while(!done) {
122 #ifndef SINGLE_THREAD
123 // see bcwindowevents.C regarding XNextEvent
124 #ifndef NO_XSELECT
125                 fd_set x_fds;
126                 FD_ZERO(&x_fds);
127                 FD_SET(x_fd, &x_fds);
128                 struct timeval tv;
129                 tv.tv_sec = 0;  tv.tv_usec = 200000;
130                 select(x_fd + 1, &x_fds, 0, 0, &tv);
131 #else
132                 usleep(100000);
133 #endif
134                 XLockDisplay(out_display);
135
136                 while( XPending(out_display) ) {
137 #endif
138                         XNextEvent(out_display, &event);
139
140 #ifdef SINGLE_THREAD
141                         BC_Display::lock_display("BC_Clipboard::run");
142 #endif
143                         switch( event.type ) {
144                         case ClientMessage:
145                                 if( event.xclient.message_type == completion_atom )
146                                         done = 1;
147                                 break;
148
149                         case SelectionRequest:
150                                 handle_selectionrequest(&event.xselectionrequest);
151                                 break;
152
153                         case SelectionClear: {
154                                 Atom selection = event.xselectionclear.selection;
155                                 int idx =
156                                         selection == xa_primary ? CLIP_PRIMARY :
157                                         selection == clipboard  ? CLIP_CLIPBOARD : -1 ;
158                                 if( idx < 0 ) break;
159                                 delete [] data_buffer[idx];
160                                 data_buffer[idx] = 0;
161                                 data_length[idx] = 0;
162                                 Window win = event.xselectionclear.window;
163 #ifndef SINGLE_THREAD
164                                 XUnlockDisplay(out_display);
165 #endif
166                                 window->lock_window("BC_Clipboard::run");
167                                 window->do_selection_clear(win);
168                                 window->unlock_window();
169 #ifndef SINGLE_THREAD
170                                 XLockDisplay(out_display);
171 #endif
172                                 break; }
173                         }
174 #ifdef SINGLE_THREAD
175                         BC_Display::unlock_display();
176 #else
177                 }
178                 XUnlockDisplay(out_display);
179 #endif
180         }
181 }
182
183 long BC_Clipboard::from_clipboard(char *data, long maxlen, int clipboard_num)
184 {
185         if( !data || maxlen <= 0 ) return -1;
186         data[0] = 0;
187         char *bfr;
188         long len = from_clipboard(clipboard_num, bfr, maxlen);
189         if( len >= maxlen ) len = maxlen-1;
190         if( bfr && len >= 0 ) {
191                 strncpy(data, bfr, len);
192                 data[len] = 0;
193         }
194         if( bfr ) XFree(bfr);
195         return len;
196 }
197
198 long BC_Clipboard::clipboard_len(int clipboard_num)
199 {
200         char *bfr;
201         long len = from_clipboard(clipboard_num, bfr, 0);
202         if( bfr ) XFree(bfr);
203         return len < 0 ? 0 : len+1;
204 }
205
206 long BC_Clipboard::from_clipboard(int clipboard_num, char *&bfr, long maxlen)
207 {
208 #ifdef SINGLE_THREAD
209         BC_Display::lock_display("BC_Clipboard::from_clipboard");
210 #else
211         XLockDisplay(in_display);
212 #endif
213
214         bfr = 0;
215         long len = 0;
216         if( clipboard_num < CLIP_BUFFER0 ) {
217                 Atom selection = clipboard_num == CLIP_PRIMARY ? xa_primary : clipboard;
218                 Atom target = string_type, property = selection;
219                 XConvertSelection(in_display, selection, target, property, in_win, CurrentTime);
220
221                 XEvent event;
222                 do {
223                         XNextEvent(in_display, &event);
224                 } while( event.type != SelectionNotify && event.type != None );
225
226                 if( event.type == SelectionNotify && property == event.xselection.property ) {
227                         unsigned long size = 0, items = 0;
228                         Atom prop_type = 0;  int bits_per_item = 0;
229                         XGetWindowProperty(in_display, in_win, property, 0, (maxlen+3)/4,
230                                 False, AnyPropertyType, &prop_type, &bits_per_item,
231                                 &items, &size, (unsigned char**)&bfr);
232                         len = !prop_type ? -1 :
233                                 !maxlen ? size :
234                                 (items*bits_per_item + 7)/8;
235                 }
236                 else
237                         clipboard_num = CLIP_BUFFER0;
238         }
239         if( clipboard_num >= CLIP_BUFFER0 ) {
240                 int idx = clipboard_num - CLIP_BUFFER0, size = 0;
241                 bfr = XFetchBuffer(in_display, &size, idx);
242                 len = size;
243         }
244
245 #ifdef SINGLE_THREAD
246         BC_Display::unlock_display();
247 #else
248         XUnlockDisplay(in_display);
249 #endif
250         return len;
251 }
252
253 int BC_Clipboard::to_clipboard(BC_WindowBase *owner, const char *data, long len, int clipboard_num)
254 {
255         if( !data || len < 0 ) return -1;
256 #ifdef SINGLE_THREAD
257         BC_Display::lock_display("BC_Clipboard::to_clipboard");
258 #else
259         XLockDisplay(out_display);
260 #endif
261
262         if( clipboard_num < CLIP_BUFFER0 ) {
263                 char *bfr = data_buffer[clipboard_num];
264                 if( data_length[clipboard_num] != len ) {
265                         delete [] bfr;  bfr = new char[len];
266                         data_buffer[clipboard_num] = bfr;
267                         data_length[clipboard_num] = len;
268                 }
269                 memcpy(bfr, data, len);
270                 Atom selection = clipboard_num == CLIP_PRIMARY ? xa_primary : clipboard;
271 // this is not supposed to be necessary according to the man page
272                 Window cur = XGetSelectionOwner(out_display, selection);
273                 if( cur != owner->win && cur != None )
274                         XSetSelectionOwner(out_display, selection, None, CurrentTime);
275                 XSetSelectionOwner(out_display, selection, owner->win, CurrentTime);
276                 XFlush(out_display);
277         }
278         else {
279                 int idx = clipboard_num - CLIP_BUFFER0;
280                 XStoreBuffer(out_display, data, len, idx);
281         }
282
283 #ifdef SINGLE_THREAD
284         BC_Display::unlock_display();
285 #else
286         XUnlockDisplay(out_display);
287 #endif
288         return 0;
289 }
290
291 int BC_Clipboard::handle_request_string(XSelectionRequestEvent *xev)
292 {
293         int idx =
294                 xev->selection == xa_primary ? CLIP_PRIMARY :
295                 xev->selection == clipboard  ? CLIP_CLIPBOARD : -1 ;
296         if( idx < 0 ) return 0;
297         char *data = data_buffer[idx];
298         if( !data ) return 0;
299         int len = data_length[idx];
300
301         XChangeProperty(out_display, xev->requestor,
302                 xev->property, string_type, 8, PropModeReplace,
303                 (unsigned char*)data, len);
304         return 1;
305 }
306
307 int BC_Clipboard::handle_request_targets(XSelectionRequestEvent *xev)
308 {
309         Atom target_atoms[] = { targets, string_type };
310         int ntarget_atoms = sizeof(target_atoms)/sizeof(target_atoms[0]);
311         XChangeProperty(out_display, xev->requestor,
312                 xev->property, XA_ATOM, 32, PropModeReplace,
313                 (unsigned char*)target_atoms, ntarget_atoms);
314         return 1;
315 }
316
317 void BC_Clipboard::handle_selectionrequest(XSelectionRequestEvent *xev)
318 {
319         XEvent reply;  memset(&reply, 0, sizeof(reply));
320 // 'None' tells the client that the request was denied
321         reply.xselection.property  =
322             (xev->target == string_type && handle_request_string(xev)) ||
323             (xev->target == targets && handle_request_targets(xev)) ?
324                 xev->property : None;
325         reply.xselection.type      = SelectionNotify;
326         reply.xselection.display   = xev->display;
327         reply.xselection.requestor = xev->requestor;
328         reply.xselection.selection = xev->selection;
329         reply.xselection.target    = xev->target;
330         reply.xselection.time      = xev->time;
331
332         XSendEvent(out_display, xev->requestor, 0, 0, &reply);
333         XFlush(out_display);
334 }
335