build version update
[goodguy/history.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         int x_fd = ConnectionNumber(out_display);
117 #endif
118
119         while(!done) {
120 #ifndef SINGLE_THREAD
121 // see bcwindowevents.C regarding XNextEvent
122                 fd_set x_fds;
123                 FD_ZERO(&x_fds);
124                 FD_SET(x_fd, &x_fds);
125                 struct timeval tv;
126                 tv.tv_sec = 0;  tv.tv_usec = 200000;
127                 select(x_fd + 1, &x_fds, 0, 0, &tv);
128                 XLockDisplay(out_display);
129
130                 while( XPending(out_display) ) {
131 #endif
132                         XNextEvent(out_display, &event);
133
134 #ifdef SINGLE_THREAD
135                         BC_Display::lock_display("BC_Clipboard::run");
136 #endif
137                         switch( event.type ) {
138                         case ClientMessage:
139                                 if( event.xclient.message_type == completion_atom )
140                                         done = 1;
141                                 break;
142
143                         case SelectionRequest:
144                                 handle_selectionrequest(&event.xselectionrequest);
145                                 break;
146
147                         case SelectionClear: {
148                                 Atom selection = event.xselectionclear.selection;
149                                 int idx =
150                                         selection == xa_primary ? CLIP_PRIMARY :
151                                         selection == clipboard  ? CLIP_CLIPBOARD : -1 ;
152                                 if( idx < 0 ) break;
153                                 delete [] data_buffer[idx];
154                                 data_buffer[idx] = 0;
155                                 data_length[idx] = 0;
156                                 Window win = event.xselectionclear.window;
157 #ifndef SINGLE_THREAD
158                                 XUnlockDisplay(out_display);
159 #endif
160                                 window->lock_window("BC_Clipboard::run");
161                                 window->do_selection_clear(win);
162                                 window->unlock_window();
163 #ifndef SINGLE_THREAD
164                                 XLockDisplay(out_display);
165 #endif
166                                 break; }
167                         }
168 #ifdef SINGLE_THREAD
169                         BC_Display::unlock_display();
170 #else
171                 }
172                 XUnlockDisplay(out_display);
173 #endif
174         }
175 }
176
177 long BC_Clipboard::from_clipboard(char *data, long maxlen, int clipboard_num)
178 {
179         if( !data || maxlen <= 0 ) return -1;
180         data[0] = 0;
181         char *bfr;
182         long len = from_clipboard(clipboard_num, bfr, maxlen);
183         if( len >= maxlen ) len = maxlen-1;
184         if( bfr && len >= 0 ) {
185                 strncpy(data, bfr, len);
186                 data[len] = 0;
187         }
188         if( bfr ) XFree(bfr);
189         return len;
190 }
191
192 long BC_Clipboard::clipboard_len(int clipboard_num)
193 {
194         char *bfr;
195         long len = from_clipboard(clipboard_num, bfr, 0);
196         if( bfr ) XFree(bfr);
197         return len < 0 ? 0 : len+1;
198 }
199
200 long BC_Clipboard::from_clipboard(int clipboard_num, char *&bfr, long maxlen)
201 {
202 #ifdef SINGLE_THREAD
203         BC_Display::lock_display("BC_Clipboard::from_clipboard");
204 #else
205         XLockDisplay(in_display);
206 #endif
207
208         bfr = 0;
209         long len = 0;
210         if( clipboard_num < CLIP_BUFFER0 ) {
211                 Atom selection = clipboard_num == CLIP_PRIMARY ? xa_primary : clipboard;
212                 Atom target = string_type, property = selection;
213                 XConvertSelection(in_display, selection, target, property, in_win, CurrentTime);
214
215                 XEvent event;
216                 do {
217                         XNextEvent(in_display, &event);
218                 } while( event.type != SelectionNotify && event.type != None );
219
220                 if( event.type == SelectionNotify && property == event.xselection.property ) {
221                         unsigned long size = 0, items = 0;
222                         Atom prop_type = 0;  int bits_per_item = 0;
223                         XGetWindowProperty(in_display, in_win, property, 0, (maxlen+3)/4,
224                                 False, AnyPropertyType, &prop_type, &bits_per_item,
225                                 &items, &size, (unsigned char**)&bfr);
226                         len = !prop_type ? -1 :
227                                 !maxlen ? size :
228                                 (items*bits_per_item + 7)/8;
229                 }
230                 else
231                         clipboard_num = CLIP_BUFFER0;
232         }
233         if( clipboard_num >= CLIP_BUFFER0 ) {
234                 int idx = clipboard_num - CLIP_BUFFER0, size = 0;
235                 bfr = XFetchBuffer(in_display, &size, idx);
236                 len = size;
237         }
238
239 #ifdef SINGLE_THREAD
240         BC_Display::unlock_display();
241 #else
242         XUnlockDisplay(in_display);
243 #endif
244         return len;
245 }
246
247 int BC_Clipboard::to_clipboard(BC_WindowBase *owner, const char *data, long len, int clipboard_num)
248 {
249         if( !data || len < 0 ) return -1;
250 #ifdef SINGLE_THREAD
251         BC_Display::lock_display("BC_Clipboard::to_clipboard");
252 #else
253         XLockDisplay(out_display);
254 #endif
255
256         if( clipboard_num < CLIP_BUFFER0 ) {
257                 char *bfr = data_buffer[clipboard_num];
258                 if( data_length[clipboard_num] != len ) {
259                         delete [] bfr;  bfr = new char[len];
260                         data_buffer[clipboard_num] = bfr;
261                         data_length[clipboard_num] = len;
262                 }
263                 memcpy(bfr, data, len);
264                 Atom selection = clipboard_num == CLIP_PRIMARY ? xa_primary : clipboard;
265 // this is not supposed to be necessary according to the man page
266                 Window cur = XGetSelectionOwner(out_display, selection);
267                 if( cur != owner->win && cur != None )
268                         XSetSelectionOwner(out_display, selection, None, CurrentTime);
269                 XSetSelectionOwner(out_display, selection, owner->win, CurrentTime);
270                 XFlush(out_display);
271         }
272         else {
273                 int idx = clipboard_num - CLIP_BUFFER0;
274                 XStoreBuffer(out_display, data, len, idx);
275         }
276
277 #ifdef SINGLE_THREAD
278         BC_Display::unlock_display();
279 #else
280         XUnlockDisplay(out_display);
281 #endif
282         return 0;
283 }
284
285 int BC_Clipboard::handle_request_string(XSelectionRequestEvent *xev)
286 {
287         int idx =
288                 xev->selection == xa_primary ? CLIP_PRIMARY :
289                 xev->selection == clipboard  ? CLIP_CLIPBOARD : -1 ;
290         if( idx < 0 ) return 0;
291         char *data = data_buffer[idx];
292         if( !data ) return 0;
293         int len = data_length[idx];
294
295         XChangeProperty(out_display, xev->requestor,
296                 xev->property, string_type, 8, PropModeReplace,
297                 (unsigned char*)data, len);
298         return 1;
299 }
300
301 int BC_Clipboard::handle_request_targets(XSelectionRequestEvent *xev)
302 {
303         Atom target_atoms[] = { targets, string_type };
304         int ntarget_atoms = sizeof(target_atoms)/sizeof(target_atoms[0]);
305         XChangeProperty(out_display, xev->requestor,
306                 xev->property, XA_ATOM, 32, PropModeReplace,
307                 (unsigned char*)target_atoms, ntarget_atoms);
308         return 1;
309 }
310
311 void BC_Clipboard::handle_selectionrequest(XSelectionRequestEvent *xev)
312 {
313         XEvent reply;  memset(&reply, 0, sizeof(reply));
314 // 'None' tells the client that the request was denied
315         reply.xselection.property  =
316             (xev->target == string_type && handle_request_string(xev)) ||
317             (xev->target == targets && handle_request_targets(xev)) ?
318                 xev->property : None;
319         reply.xselection.type      = SelectionNotify;
320         reply.xselection.display   = xev->display;
321         reply.xselection.requestor = xev->requestor;
322         reply.xselection.selection = xev->selection;
323         reply.xselection.target    = xev->target;
324         reply.xselection.time      = xev->time;
325
326         XSendEvent(out_display, xev->requestor, 0, 0, &reply);
327         XFlush(out_display);
328 }
329