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
116 int x_fd = ConnectionNumber(out_display);
120 #ifndef SINGLE_THREAD
121 // see bcwindowevents.C regarding XNextEvent
124 FD_SET(x_fd, &x_fds);
126 tv.tv_sec = 0; tv.tv_usec = 200000;
127 select(x_fd + 1, &x_fds, 0, 0, &tv);
128 XLockDisplay(out_display);
130 while( XPending(out_display) ) {
132 XNextEvent(out_display, &event);
135 BC_Display::lock_display("BC_Clipboard::run");
137 switch( event.type ) {
139 if( event.xclient.message_type == completion_atom )
143 case SelectionRequest:
144 handle_selectionrequest(&event.xselectionrequest);
147 case SelectionClear: {
148 Atom selection = event.xselectionclear.selection;
150 selection == xa_primary ? CLIP_PRIMARY :
151 selection == clipboard ? CLIP_CLIPBOARD : -1 ;
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);
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);
169 BC_Display::unlock_display();
172 XUnlockDisplay(out_display);
177 long BC_Clipboard::from_clipboard(char *data, long maxlen, int clipboard_num)
179 if( !data || maxlen <= 0 ) return -1;
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);
188 if( bfr ) XFree(bfr);
192 long BC_Clipboard::clipboard_len(int clipboard_num)
195 long len = from_clipboard(clipboard_num, bfr, 0);
196 if( bfr ) XFree(bfr);
197 return len < 0 ? 0 : len+1;
200 long BC_Clipboard::from_clipboard(int clipboard_num, char *&bfr, long maxlen)
203 BC_Display::lock_display("BC_Clipboard::from_clipboard");
205 XLockDisplay(in_display);
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);
217 XNextEvent(in_display, &event);
218 } while( event.type != SelectionNotify && event.type != None );
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 :
228 (items*bits_per_item + 7)/8;
231 clipboard_num = CLIP_BUFFER0;
233 if( clipboard_num >= CLIP_BUFFER0 ) {
234 int idx = clipboard_num - CLIP_BUFFER0, size = 0;
235 bfr = XFetchBuffer(in_display, &size, idx);
240 BC_Display::unlock_display();
242 XUnlockDisplay(in_display);
247 int BC_Clipboard::to_clipboard(BC_WindowBase *owner, const char *data, long len, int clipboard_num)
249 if( !data || len < 0 ) return -1;
251 BC_Display::lock_display("BC_Clipboard::to_clipboard");
253 XLockDisplay(out_display);
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;
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);
273 int idx = clipboard_num - CLIP_BUFFER0;
274 XStoreBuffer(out_display, data, len, idx);
278 BC_Display::unlock_display();
280 XUnlockDisplay(out_display);
285 int BC_Clipboard::handle_request_string(XSelectionRequestEvent *xev)
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];
295 XChangeProperty(out_display, xev->requestor,
296 xev->property, string_type, 8, PropModeReplace,
297 (unsigned char*)data, len);
301 int BC_Clipboard::handle_request_targets(XSelectionRequestEvent *xev)
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);
311 void BC_Clipboard::handle_selectionrequest(XSelectionRequestEvent *xev)
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;
326 XSendEvent(out_display, xev->requestor, 0, 0, &reply);