4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
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.
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.
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
22 #include "bcclipboard.h"
23 #include "bcdisplay.h"
24 #include "bcresources.h"
25 #include "bcsignals.h"
26 #include "bcwindowbase.h"
27 #include "bcwindowbase.inc"
31 BC_Clipboard::BC_Clipboard(BC_WindowBase *window)
34 this->window = window;
35 const char *display_name = window->display_name;
38 in_display = out_display = BC_Display::get_display(display_name);
40 in_display = BC_WindowBase::init_display(display_name);
41 out_display = BC_WindowBase::init_display(display_name);
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);
55 for( int i=0; i<CLIP_BUFFERS; ++i ) {
61 BC_Clipboard::~BC_Clipboard()
63 for( int i=0; i<CLIP_BUFFERS; ++i ) {
64 delete [] data_buffer[i];
66 XDestroyWindow(in_display, in_win);
67 XCloseDisplay(in_display);
68 XDestroyWindow(out_display, out_win);
69 XCloseDisplay(out_display);
72 int BC_Clipboard::start_clipboard()
80 int BC_Clipboard::stop_clipboard()
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];
87 if( data ) XStoreBuffer(out_display, data, len, 0);
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;
100 event.type = ClientMessage;
101 ptr->message_type = completion_atom;
103 XSendEvent(display, out_win, 0, 0, &event);
105 XCloseDisplay(display);
111 void BC_Clipboard::run()
115 #ifndef SINGLE_THREAD
117 int x_fd = ConnectionNumber(out_display);
122 #ifndef SINGLE_THREAD
123 // see bcwindowevents.C regarding XNextEvent
127 FD_SET(x_fd, &x_fds);
129 tv.tv_sec = 0; tv.tv_usec = 200000;
130 select(x_fd + 1, &x_fds, 0, 0, &tv);
134 XLockDisplay(out_display);
136 while( XPending(out_display) ) {
138 XNextEvent(out_display, &event);
141 BC_Display::lock_display("BC_Clipboard::run");
143 switch( event.type ) {
145 if( event.xclient.message_type == completion_atom )
149 case SelectionRequest:
150 handle_selectionrequest(&event.xselectionrequest);
153 case SelectionClear: {
154 Atom selection = event.xselectionclear.selection;
156 selection == xa_primary ? CLIP_PRIMARY :
157 selection == clipboard ? CLIP_CLIPBOARD : -1 ;
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);
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);
175 BC_Display::unlock_display();
178 XUnlockDisplay(out_display);
183 long BC_Clipboard::from_clipboard(char *data, long maxlen, int clipboard_num)
185 if( !data || maxlen <= 0 ) return -1;
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);
194 if( bfr ) XFree(bfr);
198 long BC_Clipboard::clipboard_len(int clipboard_num)
201 long len = from_clipboard(clipboard_num, bfr, 0);
202 if( bfr ) XFree(bfr);
203 return len < 0 ? 0 : len+1;
206 long BC_Clipboard::from_clipboard(int clipboard_num, char *&bfr, long maxlen)
209 BC_Display::lock_display("BC_Clipboard::from_clipboard");
211 XLockDisplay(in_display);
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);
223 XNextEvent(in_display, &event);
224 } while( event.type != SelectionNotify && event.type != None );
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 :
234 (items*bits_per_item + 7)/8;
237 clipboard_num = CLIP_BUFFER0;
239 if( clipboard_num >= CLIP_BUFFER0 ) {
240 int idx = clipboard_num - CLIP_BUFFER0, size = 0;
241 bfr = XFetchBuffer(in_display, &size, idx);
246 BC_Display::unlock_display();
248 XUnlockDisplay(in_display);
253 int BC_Clipboard::to_clipboard(BC_WindowBase *owner, const char *data, long len, int clipboard_num)
255 if( !data || len < 0 ) return -1;
257 BC_Display::lock_display("BC_Clipboard::to_clipboard");
259 XLockDisplay(out_display);
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;
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);
279 int idx = clipboard_num - CLIP_BUFFER0;
280 XStoreBuffer(out_display, data, len, idx);
284 BC_Display::unlock_display();
286 XUnlockDisplay(out_display);
291 int BC_Clipboard::handle_request_string(XSelectionRequestEvent *xev)
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];
301 XChangeProperty(out_display, xev->requestor,
302 xev->property, string_type, 8, PropModeReplace,
303 (unsigned char*)data, len);
307 int BC_Clipboard::handle_request_targets(XSelectionRequestEvent *xev)
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);
317 void BC_Clipboard::handle_selectionrequest(XSelectionRequestEvent *xev)
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;
332 XSendEvent(out_display, xev->requestor, 0, 0, &reply);