diff -up fltk-1.3.2/CMakeLists.txt.clp-x11 fltk-1.3.2/CMakeLists.txt --- fltk-1.3.2/CMakeLists.txt.clp-x11 2012-09-13 16:19:01.000000000 +0200 +++ fltk-1.3.2/CMakeLists.txt 2013-01-30 15:56:25.810663430 +0100 @@ -515,6 +515,20 @@ else() endif(OPTION_USE_XINERAMA) ####################################################################### +if(X11_Xfixes_FOUND) + option(OPTION_USE_XFIXES "use lib XFIXES" ON) +endif(X11_Xfixes_FOUND) + +if(OPTION_USE_XFIXES) + set(HAVE_XFIXES ${X11_Xfixes_FOUND}) + include_directories(${X11_Xfixes_INCLUDE_PATH}) + list(APPEND FLTK_LDLIBS -lXfixes) + set(FLTK_XFIXES_FOUND TRUE) +else() + set(FLTK_XFIXES_FOUND FALSE) +endif(OPTION_USE_XFIXES) + +####################################################################### if(X11_Xft_FOUND) option(OPTION_USE_XFT "use lib Xft" ON) endif(X11_Xft_FOUND) diff -up fltk-1.3.2/configh.cmake.in.clp-x11 fltk-1.3.2/configh.cmake.in --- fltk-1.3.2/configh.cmake.in.clp-x11 2011-07-19 06:49:30.000000000 +0200 +++ fltk-1.3.2/configh.cmake.in 2013-01-30 15:56:25.810663430 +0100 @@ -108,6 +108,14 @@ #define USE_XDBE HAVE_XDBE /* + * HAVE_XFIXES: + * + * Do we have the X fixes extension? + */ + +#cmakedefine01 HAVE_XFIXES + +/* * __APPLE_QUARTZ__: * * If __APPLE_QUARTZ__ is defined, FLTK will be diff -up fltk-1.3.2/configh.in.clp-x11 fltk-1.3.2/configh.in --- fltk-1.3.2/configh.in.clp-x11 2011-10-04 11:21:47.000000000 +0200 +++ fltk-1.3.2/configh.in 2013-01-30 15:56:25.810663430 +0100 @@ -108,6 +108,14 @@ #define USE_XDBE HAVE_XDBE /* + * HAVE_XFIXES: + * + * Do we have the X fixes extension? + */ + +#define HAVE_XFIXES 0 + +/* * __APPLE_QUARTZ__: * * All Apple implementations are now based on Quartz and Cocoa, diff -up fltk-1.3.2/configure.in.clp-x11 fltk-1.3.2/configure.in --- fltk-1.3.2/configure.in.clp-x11 2013-01-30 15:56:25.802663573 +0100 +++ fltk-1.3.2/configure.in 2013-01-30 15:56:25.810663430 +0100 @@ -999,6 +999,16 @@ case $uname_GUI in LIBS="-lXext $LIBS") fi + dnl Check for the Xfixes extension unless disabled... + AC_ARG_ENABLE(xfixes, [ --enable-xfixes turn on Xfixes support [default=yes]]) + + if test x$enable_xfixes != xno; then + AC_CHECK_HEADER(X11/extensions/Xfixes.h, AC_DEFINE(HAVE_XFIXES),, + [#include ]) + AC_CHECK_LIB(Xfixes, XFixesQueryExtension, + LIBS="-lXfixes $LIBS") + fi + dnl Check for overlay visuals... AC_PATH_PROG(XPROP, xprop) AC_CACHE_CHECK(for X overlay visuals, ac_cv_have_overlay, diff -up fltk-1.3.2/fluid/CMakeLists.txt.clp-x11 fltk-1.3.2/fluid/CMakeLists.txt diff -up fltk-1.3.2/src/CMakeLists.txt.clp-x11 fltk-1.3.2/src/CMakeLists.txt --- fltk-1.3.2/src/CMakeLists.txt.clp-x11 2013-01-30 16:06:00.785430590 +0100 +++ fltk-1.3.2/src/CMakeLists.txt 2013-01-30 16:06:17.883126642 +0100 @@ -243,6 +243,10 @@ if(HAVE_XINERAMA) target_link_libraries(fltk ${X11_Xinerama_LIB}) endif(HAVE_XINERAMA) +if(HAVE_XFIXES) + target_link_libraries(fltk ${X11_Xfixes_LIB}) +endif(HAVE_XFIXES) + if(USE_XFT) target_link_libraries(fltk ${X11_Xft_LIB}) endif(USE_XFT) diff -up fltk-1.3.2/src/Fl_x.cxx.clp-x11 fltk-1.3.2/src/Fl_x.cxx --- fltk-1.3.2/src/Fl_x.cxx.clp-x11 2013-01-30 15:56:25.793663733 +0100 +++ fltk-1.3.2/src/Fl_x.cxx 2013-01-30 16:03:37.355981103 +0100 @@ -53,6 +53,12 @@ static XRRUpdateConfiguration_type XRRUp static int randrEventBase; // base of RandR-defined events #endif +# if HAVE_XFIXES +# include +static int xfixes_event_base = 0; +static bool have_xfixes = false; +# endif + static Fl_Xlib_Graphics_Driver fl_xlib_driver; static Fl_Display_Device fl_xlib_display(&fl_xlib_driver); Fl_Display_Device *Fl_Display_Device::_display = &fl_xlib_display;// the platform display @@ -307,6 +313,9 @@ static Atom WM_PROTOCOLS; static Atom fl_MOTIF_WM_HINTS; static Atom TARGETS; static Atom CLIPBOARD; +static Atom TIMESTAMP; +static Atom PRIMARY_TIMESTAMP; +static Atom CLIPBOARD_TIMESTAMP; Atom fl_XdndAware; Atom fl_XdndSelection; Atom fl_XdndEnter; @@ -667,6 +676,9 @@ void fl_open_display(Display* d) { fl_MOTIF_WM_HINTS = XInternAtom(d, "_MOTIF_WM_HINTS", 0); TARGETS = XInternAtom(d, "TARGETS", 0); CLIPBOARD = XInternAtom(d, "CLIPBOARD", 0); + TIMESTAMP = XInternAtom(d, "TIMESTAMP", 0); + PRIMARY_TIMESTAMP = XInternAtom(d, "PRIMARY_TIMESTAMP", 0); + CLIPBOARD_TIMESTAMP = XInternAtom(d, "CLIPBOARD_TIMESTAMP", 0); fl_XdndAware = XInternAtom(d, "XdndAware", 0); fl_XdndSelection = XInternAtom(d, "XdndSelection", 0); fl_XdndEnter = XInternAtom(d, "XdndEnter", 0); @@ -713,6 +725,15 @@ void fl_open_display(Display* d) { #if !USE_COLORMAP Fl::visual(FL_RGB); #endif + +#if HAVE_XFIXES + int error_base; + if (XFixesQueryExtension(fl_display, &xfixes_event_base, &error_base)) + have_xfixes = true; + else + have_xfixes = false; +#endif + #if USE_XRANDR void *libxrandr_addr = dlopen("libXrandr.so.2", RTLD_LAZY); if (!libxrandr_addr) libxrandr_addr = dlopen("libXrandr.so", RTLD_LAZY); @@ -901,6 +922,107 @@ void Fl::copy(const char *stuff, int len } //////////////////////////////////////////////////////////////// +// Code for tracking clipboard changes: + +static Time primary_timestamp = -1; +static Time clipboard_timestamp = -1; + +extern bool fl_clipboard_notify_empty(void); +extern void fl_trigger_clipboard_notify(int source); + +static void poll_clipboard_owner(void) { + Window xid; + +#if HAVE_XFIXES + // No polling needed with Xfixes + if (have_xfixes) + return; +#endif + + // No one is interested, so no point polling + if (fl_clipboard_notify_empty()) + return; + + // We need a window for this to work + if (!Fl::first_window()) + return; + xid = fl_xid(Fl::first_window()); + if (!xid) + return; + + // Request an update of the selection time for both the primary and + // clipboard selections. Magic continues when we get a SelectionNotify. + if (!fl_i_own_selection[0]) + XConvertSelection(fl_display, XA_PRIMARY, TIMESTAMP, PRIMARY_TIMESTAMP, + xid, fl_event_time); + if (!fl_i_own_selection[1]) + XConvertSelection(fl_display, CLIPBOARD, TIMESTAMP, CLIPBOARD_TIMESTAMP, + xid, fl_event_time); +} + +static void clipboard_timeout(void *data) +{ + // No one is interested, so stop polling + if (fl_clipboard_notify_empty()) + return; + + poll_clipboard_owner(); + + Fl::repeat_timeout(0.5, clipboard_timeout); +} + +static void handle_clipboard_timestamp(int clipboard, Time time) +{ + Time *timestamp; + + timestamp = clipboard ? &clipboard_timestamp : &primary_timestamp; + +#if HAVE_XFIXES + if (!have_xfixes) +#endif + { + // Initial scan, just store the value + if (*timestamp == (Time)-1) { + *timestamp = time; + return; + } + } + + // Same selection + if (time == *timestamp) + return; + + *timestamp = time; + + // The clipboard change is the event that caused us to request + // the clipboard data, so use that time as the latest event. + if (time > fl_event_time) + fl_event_time = time; + + // Something happened! Let's tell someone! + fl_trigger_clipboard_notify(clipboard); +} + +void fl_clipboard_notify_change() { + // Reset the timestamps if we've going idle so that you don't + // get a bogus immediate trigger next time they're activated. + if (fl_clipboard_notify_empty()) { + primary_timestamp = -1; + clipboard_timestamp = -1; + } else { +#if HAVE_XFIXES + if (!have_xfixes) +#endif + { + poll_clipboard_owner(); + + if (!Fl::has_timeout(clipboard_timeout)) + Fl::add_timeout(0.5, clipboard_timeout); + } + } +} + +//////////////////////////////////////////////////////////////// const XEvent* fl_xevent; // the current x event ulong fl_event_time; // the last timestamp from an x event @@ -1024,7 +1141,6 @@ int fl_handle(const XEvent& thisevent) return 0; case SelectionNotify: { - if (!fl_selection_requestor) return 0; static unsigned char* buffer = 0; if (buffer) {XFree(buffer); buffer = 0;} long bytesread = 0; @@ -1040,6 +1156,19 @@ int fl_handle(const XEvent& thisevent) bytesread/4, 65536, 1, 0, &actual, &format, &count, &remaining, &portion)) break; // quit on error + + if ((fl_xevent->xselection.property == PRIMARY_TIMESTAMP) || + (fl_xevent->xselection.property == CLIPBOARD_TIMESTAMP)) { + if (portion && format == 32 && count == 1) { + Time t = *(unsigned int*)portion; + if (fl_xevent->xselection.property == CLIPBOARD_TIMESTAMP) + handle_clipboard_timestamp(1, t); + else + handle_clipboard_timestamp(0, t); + } + return true; + } + if (actual == TARGETS || actual == XA_ATOM) { Atom type = XA_STRING; for (unsigned i = 0; ixselectionclear.selection == CLIPBOARD; fl_i_own_selection[clipboard] = 0; + poll_clipboard_owner(); return 1;} case SelectionRequest: { @@ -1308,6 +1441,9 @@ int fl_handle(const XEvent& thisevent) case FocusIn: if (fl_xim_ic) XSetICFocus(fl_xim_ic); event = FL_FOCUS; + // If the user has toggled from another application to this one, + // then it's a good time to check for clipboard changes. + poll_clipboard_owner(); break; case FocusOut: @@ -1676,6 +1812,25 @@ int fl_handle(const XEvent& thisevent) } } +#if HAVE_XFIXES + switch (xevent.type - xfixes_event_base) { + case XFixesSelectionNotify: { + // Someone feeding us bogus events? + if (!have_xfixes) + return true; + + XFixesSelectionNotifyEvent *selection_notify = (XFixesSelectionNotifyEvent *)&xevent; + + if ((selection_notify->selection == XA_PRIMARY) && !fl_i_own_selection[0]) + handle_clipboard_timestamp(0, selection_notify->selection_timestamp); + else if ((selection_notify->selection == CLIPBOARD) && !fl_i_own_selection[1]) + handle_clipboard_timestamp(1, selection_notify->selection_timestamp); + + return true; + } + } +#endif + return Fl::handle(event, window); } @@ -1995,6 +2150,16 @@ void Fl_X::make_xid(Fl_Window* win, XVis XChangeProperty(fl_display, xp->xid, net_wm_type, XA_ATOM, 32, PropModeReplace, (unsigned char*)&net_wm_type_kind, 1); } +#if HAVE_XFIXES + // register for clipboard change notifications + if (have_xfixes && !win->parent()) { + XFixesSelectSelectionInput(fl_display, xp->xid, XA_PRIMARY, + XFixesSetSelectionOwnerNotifyMask); + XFixesSelectSelectionInput(fl_display, xp->xid, CLIPBOARD, + XFixesSetSelectionOwnerNotifyMask); + } +#endif + XMapWindow(fl_display, xp->xid); if (showit) { win->set_visible(); diff -up fltk-1.3.2/test/CMakeLists.txt.clp-x11 fltk-1.3.2/test/CMakeLists.txt