diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index c6ce7b628e9..a975322e9ce 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -117,7 +117,15 @@ GHOST_SystemX11( m_net_fullscreen= XInternAtom(m_display, "_NET_WM_STATE_FULLSCREEN", False); m_motif= XInternAtom(m_display, "_MOTIF_WM_HINTS", False); - + m_targets= XInternAtom(m_display, "TARGETS", False); + m_string= XInternAtom(m_display, "STRING", False); + m_compound_text= XInternAtom(m_display, "COMPOUND_TEXT", False); + m_text= XInternAtom(m_display, "TEXT", False); + m_clipboard= XInternAtom(m_display, "CLIPBOARD", False); + m_primary= XInternAtom(m_display, "PRIMARY", False); + m_xclip_out= XInternAtom(m_display, "XCLIP_OUT", False); + m_incr= XInternAtom(m_display, "INCR", False); + m_utf8_string= XInternAtom(m_display, "UTF8_STRING", False); // compute the initial time timeval tv; @@ -1004,144 +1012,298 @@ convertXKey( #undef GXMAP - GHOST_TUns8* -GHOST_SystemX11:: -getClipboard(bool selection -) const { - //Flag - //0 = Regular clipboard 1 = selection - static Atom Primary_atom, clip_String, compound_text, a_text, a_string; - Atom rtype; - Window m_window, owner; - unsigned char *data, *tmp_data; - int bits, count; - unsigned long len, bytes; - XEvent xevent; - +/* from xclip.c xcout() v0.11 */ + +#define XCLIB_XCOUT_NONE 0 /* no context */ +#define XCLIB_XCOUT_SENTCONVSEL 1 /* sent a request */ +#define XCLIB_XCOUT_INCR 2 /* in an incr loop */ +#define XCLIB_XCOUT_FALLBACK 3 /* STRING failed, need fallback to UTF8 */ +#define XCLIB_XCOUT_FALLBACK_UTF8 4 /* UTF8 failed, move to compouned */ +#define XCLIB_XCOUT_FALLBACK_COMP 5 /* compouned failed, move to text. */ +#define XCLIB_XCOUT_FALLBACK_TEXT 6 + +// Retrieves the contents of a selections. +void GHOST_SystemX11::getClipboard_xcout(XEvent evt, + Atom sel, Atom target, unsigned char **txt, + unsigned long *len, unsigned int *context) const +{ + Atom pty_type; + int pty_format; + unsigned char *buffer; + unsigned long pty_size, pty_items; + unsigned char *ltxt= *txt; + vector & win_vec = m_windowManager->getWindows(); vector::iterator win_it = win_vec.begin(); GHOST_WindowX11 * window = static_cast(*win_it); - m_window = window->getXWindow(); + Window win = window->getXWindow(); - clip_String = XInternAtom(m_display, "_BLENDER_STRING", False); - compound_text = XInternAtom(m_display, "COMPOUND_TEXT", False); - a_text= XInternAtom(m_display, "TEXT", False); - a_string= XInternAtom(m_display, "STRING", False); + switch (*context) { + // There is no context, do an XConvertSelection() + case XCLIB_XCOUT_NONE: + // Initialise return length to 0 + if (*len > 0) { + free(*txt); + *len = 0; + } - //lets check the owner and if it is us then return the static buffer - if(!selection) { - Primary_atom = XInternAtom(m_display, "CLIPBOARD", False); - owner = XGetSelectionOwner(m_display, Primary_atom); - if (owner == m_window) { - data = (unsigned char*) malloc(strlen(txt_cut_buffer)+1); - strcpy((char*)data, txt_cut_buffer); - return (GHOST_TUns8*)data; - } else if (owner == None) { - return NULL; - } - } else { - Primary_atom = XInternAtom(m_display, "PRIMARY", False); - owner = XGetSelectionOwner(m_display, Primary_atom); - if (owner == m_window) { - data = (unsigned char*) malloc(strlen(txt_select_buffer)+1); - strcpy((char*)data, txt_select_buffer); - return (GHOST_TUns8*)data; - } else if (owner == None) { - return NULL; - } - } + // Send a selection request + XConvertSelection(m_display, sel, target, m_xclip_out, win, CurrentTime); + *context = XCLIB_XCOUT_SENTCONVSEL; + return; - if(!Primary_atom) { - return NULL; - } - - XDeleteProperty(m_display, m_window, Primary_atom); - XConvertSelection(m_display, Primary_atom, compound_text, clip_String, m_window, CurrentTime); //XA_STRING - XFlush(m_display); + case XCLIB_XCOUT_SENTCONVSEL: + if (evt.type != SelectionNotify) + return; - //This needs to change so we do not wait for ever or check owner first - count= 1; - while(1) { - XNextEvent(m_display, &xevent); - if(xevent.type == SelectionNotify) { - if (xevent.xselection.property == None) { - /* Ok, the client can't convert the property - * to some that we can handle, try other types.. - */ - if (count == 1) { - XConvertSelection(m_display, Primary_atom, a_text, clip_String, m_window, CurrentTime); - count++; - } - else if (count == 2) { - XConvertSelection(m_display, Primary_atom, a_string, clip_String, m_window, CurrentTime); - count++; - } - else { - /* Ok, the owner of the selection can't - * convert the data to something that we can - * handle. - */ - return(NULL); - } + if (target == m_utf8_string && evt.xselection.property == None) { + *context= XCLIB_XCOUT_FALLBACK_UTF8; + return; + } + else if (target == m_compound_text && evt.xselection.property == None) { + *context= XCLIB_XCOUT_FALLBACK_COMP; + return; + } + else if (target == m_text && evt.xselection.property == None) { + *context= XCLIB_XCOUT_FALLBACK_TEXT; + return; + } + + // find the size and format of the data in property + XGetWindowProperty(m_display, win, m_xclip_out, 0, 0, False, + AnyPropertyType, &pty_type, &pty_format, + &pty_items, &pty_size, &buffer); + XFree(buffer); + + if (pty_type == m_incr) { + // start INCR mechanism by deleting property + XDeleteProperty(m_display, win, m_xclip_out); + XFlush(m_display); + *context = XCLIB_XCOUT_INCR; + return; + } + + // if it's not incr, and not format == 8, then there's + // nothing in the selection (that xclip understands, anyway) + + if (pty_format != 8) { + *context = XCLIB_XCOUT_NONE; + return; + } + + // not using INCR mechanism, just read the property + XGetWindowProperty(m_display, win, m_xclip_out, 0, (long) pty_size, + False, AnyPropertyType, &pty_type, + &pty_format, &pty_items, &pty_size, &buffer); + + // finished with property, delete it + XDeleteProperty(m_display, win, m_xclip_out); + + // copy the buffer to the pointer for returned data + ltxt = (unsigned char *) malloc(pty_items); + memcpy(ltxt, buffer, pty_items); + + // set the length of the returned data + *len = pty_items; + *txt = ltxt; + + // free the buffer + XFree(buffer); + + *context = XCLIB_XCOUT_NONE; + + // complete contents of selection fetched, return 1 + return; + + case XCLIB_XCOUT_INCR: + // To use the INCR method, we basically delete the + // property with the selection in it, wait for an + // event indicating that the property has been created, + // then read it, delete it, etc. + + // make sure that the event is relevant + if (evt.type != PropertyNotify) + return; + + // skip unless the property has a new value + if (evt.xproperty.state != PropertyNewValue) + return; + + // check size and format of the property + XGetWindowProperty(m_display, win, m_xclip_out, 0, 0, False, + AnyPropertyType, &pty_type, &pty_format, + &pty_items, &pty_size, (unsigned char **) &buffer); + + if (pty_format != 8) { + // property does not contain text, delete it + // to tell the other X client that we have read + // it and to send the next property + XFree(buffer); + XDeleteProperty(m_display, win, m_xclip_out); + return; + } + + if (pty_size == 0) { + // no more data, exit from loop + XFree(buffer); + XDeleteProperty(m_display, win, m_xclip_out); + *context = XCLIB_XCOUT_NONE; + + // this means that an INCR transfer is now + // complete, return 1 + return; + } + + XFree(buffer); + + // if we have come this far, the propery contains + // text, we know the size. + XGetWindowProperty(m_display, win, m_xclip_out, 0, (long) pty_size, + False, AnyPropertyType, &pty_type, &pty_format, + &pty_items, &pty_size, (unsigned char **) &buffer); + + // allocate memory to accommodate data in *txt + if (*len == 0) { + *len = pty_items; + ltxt = (unsigned char *) malloc(*len); } else { - if(XGetWindowProperty(m_display, m_window, xevent.xselection.property , 0L, 4096L, False, AnyPropertyType, &rtype, &bits, &len, &bytes, &data) == Success) { - if (data) { - if (bits == 8 && (rtype == compound_text || rtype == a_text || rtype == a_string)) { - tmp_data = (unsigned char*) malloc(strlen((char*)data)+1); - strcpy((char*)tmp_data, (char*)data); - } - else - tmp_data= NULL; - - XFree(data); - return (GHOST_TUns8*)tmp_data; - } - } - return(NULL); + *len += pty_items; + ltxt = (unsigned char *) realloc(ltxt, *len); } - } - } -} - void -GHOST_SystemX11:: -putClipboard( -GHOST_TInt8 *buffer, bool selection) const -{ - static Atom Primary_atom; - Window m_window, owner; - - if(!buffer) {return;} - - if(!selection) { - Primary_atom = XInternAtom(m_display, "CLIPBOARD", False); - if(txt_cut_buffer) { free((void*)txt_cut_buffer); } - - txt_cut_buffer = (char*) malloc(strlen(buffer)+1); - strcpy(txt_cut_buffer, buffer); - } else { - Primary_atom = XInternAtom(m_display, "PRIMARY", False); - if(txt_select_buffer) { free((void*)txt_select_buffer); } - - txt_select_buffer = (char*) malloc(strlen(buffer)+1); - strcpy(txt_select_buffer, buffer); - } - - vector & win_vec = m_windowManager->getWindows(); - vector::iterator win_it = win_vec.begin(); - GHOST_WindowX11 * window = static_cast(*win_it); - m_window = window->getXWindow(); + // add data to ltxt + memcpy(<xt[*len - pty_items], buffer, pty_items); - if(!Primary_atom) { - return; + *txt = ltxt; + XFree(buffer); + + // delete property to get the next item + XDeleteProperty(m_display, win, m_xclip_out); + XFlush(m_display); + return; } - - XSetSelectionOwner(m_display, Primary_atom, m_window, CurrentTime); - owner = XGetSelectionOwner(m_display, Primary_atom); - if (owner != m_window) - fprintf(stderr, "failed to own primary\n"); - return; } +GHOST_TUns8 *GHOST_SystemX11::getClipboard(bool selection) const +{ + Atom sseln; + Atom target= m_string; + Window owner; + + // from xclip.c doOut() v0.11 + unsigned char *sel_buf; + unsigned long sel_len= 0; + XEvent evt; + unsigned int context= XCLIB_XCOUT_NONE; + + if (selection == True) + sseln= m_primary; + else + sseln= m_clipboard; + + vector & win_vec = m_windowManager->getWindows(); + vector::iterator win_it = win_vec.begin(); + GHOST_WindowX11 * window = static_cast(*win_it); + Window win = window->getXWindow(); + + /* check if we are the owner. */ + owner= XGetSelectionOwner(m_display, sseln); + if (owner == win) { + if (sseln == m_clipboard) { + sel_buf= (unsigned char *)malloc(strlen(txt_cut_buffer)+1); + strcpy((char *)sel_buf, txt_cut_buffer); + return((GHOST_TUns8*)sel_buf); + } + else { + sel_buf= (unsigned char *)malloc(strlen(txt_select_buffer)+1); + strcpy((char *)sel_buf, txt_select_buffer); + return((GHOST_TUns8*)sel_buf); + } + } + else if (owner == None) + return(NULL); + + while (1) { + /* only get an event if xcout() is doing something */ + if (context != XCLIB_XCOUT_NONE) + XNextEvent(m_display, &evt); + + /* fetch the selection, or part of it */ + getClipboard_xcout(evt, sseln, target, &sel_buf, &sel_len, &context); + + /* fallback is needed. set XA_STRING to target and restart the loop. */ + if (context == XCLIB_XCOUT_FALLBACK) { + context= XCLIB_XCOUT_NONE; + target= m_string; + continue; + } + else if (context == XCLIB_XCOUT_FALLBACK_UTF8) { + /* utf8 fail, move to compouned text. */ + context= XCLIB_XCOUT_NONE; + target= m_compound_text; + continue; + } + else if (context == XCLIB_XCOUT_FALLBACK_COMP) { + /* compouned text faile, move to text. */ + context= XCLIB_XCOUT_NONE; + target= m_text; + continue; + } + + /* only continue if xcout() is doing something */ + if (context == XCLIB_XCOUT_NONE) + break; + } + + if (sel_len) { + /* only print the buffer out, and free it, if it's not + * empty + */ + unsigned char *tmp_data = (unsigned char*) malloc(sel_len+1); + memcpy((char*)tmp_data, (char*)sel_buf, sel_len); + tmp_data[sel_len] = '\0'; + + if (sseln == m_string) + XFree(sel_buf); + else + free(sel_buf); + + return (GHOST_TUns8*)tmp_data; + } + return(NULL); +} + +void GHOST_SystemX11::putClipboard(GHOST_TInt8 *buffer, bool selection) const +{ + Window m_window, owner; + + vector & win_vec = m_windowManager->getWindows(); + vector::iterator win_it = win_vec.begin(); + GHOST_WindowX11 * window = static_cast(*win_it); + m_window = window->getXWindow(); + + if (buffer) { + if (selection == False) { + XSetSelectionOwner(m_display, m_clipboard, m_window, CurrentTime); + owner= XGetSelectionOwner(m_display, m_clipboard); + if (txt_cut_buffer) + free((void*)txt_cut_buffer); + + txt_cut_buffer = (char*) malloc(strlen(buffer)+1); + strcpy(txt_cut_buffer, buffer); + } else { + XSetSelectionOwner(m_display, m_primary, m_window, CurrentTime); + owner= XGetSelectionOwner(m_display, m_primary); + if (txt_select_buffer) + free((void*)txt_select_buffer); + + txt_select_buffer = (char*) malloc(strlen(buffer)+1); + strcpy(txt_select_buffer, buffer); + } + + if (owner != m_window) + fprintf(stderr, "failed to own primary\n"); + } +} diff --git a/intern/ghost/intern/GHOST_SystemX11.h b/intern/ghost/intern/GHOST_SystemX11.h index 6eacd88b8c2..711a188ffe9 100644 --- a/intern/ghost/intern/GHOST_SystemX11.h +++ b/intern/ghost/intern/GHOST_SystemX11.h @@ -199,20 +199,25 @@ public: prepareNdofInfo( volatile GHOST_TEventNDOFData *current_values ); - + + /* Helped function for get data from the clipboard. */ + void getClipboard_xcout(XEvent evt, Atom sel, Atom target, + unsigned char **txt, unsigned long *len, + unsigned int *context) const; + /** * Returns unsinged char from CUT_BUFFER0 * @param selection Get selection, X11 only feature * @return Returns the Clipboard indicated by Flag */ - GHOST_TUns8* getClipboard(bool selection) const; + GHOST_TUns8 *getClipboard(bool selection) const; /** * Puts buffer to system clipboard * @param buffer The buffer to copy to the clipboard * @param selection Set the selection into the clipboard, X11 only feature */ - virtual void putClipboard(GHOST_TInt8 *buffer, bool selection) const; + void putClipboard(GHOST_TInt8 *buffer, bool selection) const; /** * Atom used for ICCCM, WM-spec and Motif. @@ -231,6 +236,17 @@ public: Atom m_wm_protocols; Atom m_delete_window_atom; + /* Atoms for Selection, copy & paste. */ + Atom m_targets; + Atom m_string; + Atom m_compound_text; + Atom m_text; + Atom m_clipboard; + Atom m_primary; + Atom m_xclip_out; + Atom m_incr; + Atom m_utf8_string; + private : Display * m_display; diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 02c803963eb..c38f147a480 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -651,7 +651,8 @@ static void ui_but_copy_paste(bContext *C, uiBut *but, uiHandleButtonData *data, if(mode=='v') { /* extract first line from clipboard in case of multi-line copies */ - char *p = WM_clipboard_text_get(0); + char *p, *pbuf= WM_clipboard_text_get(0); + p= pbuf; if(p) { int i = 0; while (*p && *p!='\r' && *p!='\n' && i