diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/Fl.cxx | 67 | ||||
| -rw-r--r-- | src/Fl_cocoa.mm | 38 | ||||
| -rw-r--r-- | src/Fl_win32.cxx | 55 | ||||
| -rw-r--r-- | src/Fl_x.cxx | 172 |
5 files changed, 329 insertions, 7 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d95078f68..14915be9a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -239,6 +239,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 --git a/src/Fl.cxx b/src/Fl.cxx index a0fcbd9f3..268232309 100644 --- a/src/Fl.cxx +++ b/src/Fl.cxx @@ -435,6 +435,70 @@ static char in_idle; #endif //////////////////////////////////////////////////////////////// +// Clipboard notifications + +struct Clipboard_Notify { + Fl_Clipboard_Notify_Handler handler; + void *data; + struct Clipboard_Notify *next; +}; + +static struct Clipboard_Notify *clip_notify_list = NULL; + +extern void fl_clipboard_notify_change(); // in Fl_<platform>.cxx + +void Fl::add_clipboard_notify(Fl_Clipboard_Notify_Handler h, void *data) { + struct Clipboard_Notify *node; + + remove_clipboard_notify(h); + + node = new Clipboard_Notify; + + node->handler = h; + node->data = data; + node->next = clip_notify_list; + + clip_notify_list = node; + + fl_clipboard_notify_change(); +} + +void Fl::remove_clipboard_notify(Fl_Clipboard_Notify_Handler h) { + struct Clipboard_Notify *node, **prev; + + node = clip_notify_list; + prev = &clip_notify_list; + while (node != NULL) { + if (node->handler == h) { + *prev = node->next; + delete node; + + fl_clipboard_notify_change(); + + return; + } + + prev = &node->next; + node = node->next; + } +} + +bool fl_clipboard_notify_empty(void) { + return clip_notify_list == NULL; +} + +void fl_trigger_clipboard_notify(int source) { + struct Clipboard_Notify *node, *next; + + node = clip_notify_list; + while (node != NULL) { + next = node->next; + node->handler(source, node->data); + node = next; + } +} + +//////////////////////////////////////////////////////////////// // wait/run/check/ready: void (*Fl::idle)(); // see Fl::add_idle.cxx for the add/remove functions @@ -1358,6 +1422,7 @@ int Fl::handle_(int e, Fl_Window* window) // hide() destroys the X window, it does not do unmap! #if defined(WIN32) +extern void fl_clipboard_notify_untarget(HWND wnd); extern void fl_update_clipboard(void); #elif USE_XFT extern void fl_destroy_xft_draw(Window); @@ -1408,6 +1473,8 @@ void Fl_Window::hide() { // to destroy the window that owns the selection. if (GetClipboardOwner()==ip->xid) fl_update_clipboard(); + // Make sure we unlink this window from the clipboard chain + fl_clipboard_notify_untarget(ip->xid); // Send a message to myself so that I'll get out of the event loop... PostMessage(ip->xid, WM_APP, 0, 0); if (ip->private_dc) fl_release_dc(ip->xid, ip->private_dc); diff --git a/src/Fl_cocoa.mm b/src/Fl_cocoa.mm index 3c8d22da8..7b042d324 100644 --- a/src/Fl_cocoa.mm +++ b/src/Fl_cocoa.mm @@ -90,6 +90,7 @@ static void createAppleMenu(void); static Fl_Region MacRegionMinusRect(Fl_Region r, int x,int y,int w,int h); static void cocoaMouseHandler(NSEvent *theEvent); static int calc_mac_os_version(); +static void clipboard_check(void); Fl_Display_Device *Fl_Display_Device::_display = new Fl_Display_Device(new Fl_Quartz_Graphics_Driver); // the platform display @@ -1109,16 +1110,21 @@ static void cocoaMouseHandler(NSEvent *theEvent) fl_unlock_function(); return reply; } -/** - * Cocoa organizes the Z depth of windows on a global priority. FLTK however - * expects the window manager to organize Z level by application. The trickery - * below will change Z order during activation and deactivation. - */ - (void)applicationDidBecomeActive:(NSNotification *)notify { - fl_lock_function(); Fl_X *x; FLWindow *top = 0, *topModal = 0, *topNonModal = 0; + + fl_lock_function(); + + // update clipboard status + clipboard_check(); + + /** + * Cocoa organizes the Z depth of windows on a global priority. FLTK however + * expects the window manager to organize Z level by application. The trickery + * below will change Z order during activation and deactivation. + */ for (x = Fl_X::first;x;x = x->next) { FLWindow *cw = x->xid; Fl_Window *win = x->w; @@ -2720,6 +2726,26 @@ static void allocatePasteboard() { PasteboardCreate(kPasteboardClipboard, &myPasteboard); } +extern void fl_trigger_clipboard_notify(int source); + +void fl_clipboard_notify_change() { + // No need to do anything here... +} + +static void clipboard_check(void) +{ + PasteboardSyncFlags flags; + + allocatePasteboard(); + flags = PasteboardSynchronize(myPasteboard); + + if (!(flags & kPasteboardModified)) + return; + if (flags & kPasteboardClientIsOwner) + return; + + fl_trigger_clipboard_notify(1); +} /* * create a selection diff --git a/src/Fl_win32.cxx b/src/Fl_win32.cxx index 02cecd949..ce480b5d7 100644 --- a/src/Fl_win32.cxx +++ b/src/Fl_win32.cxx @@ -634,6 +634,38 @@ void Fl::paste(Fl_Widget &receiver, int clipboard) { } } +static HWND clipboard_wnd = 0; +static HWND next_clipboard_wnd = 0; + +static bool initial_clipboard = true; + +void fl_clipboard_notify_change() { + // No need to do anything here... +} + +void fl_clipboard_notify_target(HWND wnd) { + if (clipboard_wnd) + return; + + // We get one fake WM_DRAWCLIPBOARD immediately, which we therefore + // need to ignore. + initial_clipboard = true; + + clipboard_wnd = wnd; + next_clipboard_wnd = SetClipboardViewer(wnd); +} + +void fl_clipboard_notify_untarget(HWND wnd) { + if (wnd != clipboard_wnd) + return; + + ChangeClipboardChain(wnd, next_clipboard_wnd); + clipboard_wnd = next_clipboard_wnd = 0; + + if (Fl::first_window()) + fl_clipboard_notify_target(fl_xid(Fl::first_window())); +} + //////////////////////////////////////////////////////////////// char fl_is_ime = 0; void fl_get_codepage() @@ -1211,6 +1243,27 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPar Fl::handle(FL_SCREEN_CONFIGURATION_CHANGED, NULL); return 0; + case WM_CHANGECBCHAIN: + if ((hWnd == clipboard_wnd) && + (next_clipboard_wnd == (HWND)wParam)) { + next_clipboard_wnd = (HWND)lParam; + return 0; + } + break; + + case WM_DRAWCLIPBOARD: + // When the clipboard moves between two FLTK windows, + // fl_i_own_selection will temporarily be false as we are + // processing this message. Hence the need to use fl_find(). + if (!initial_clipboard && !fl_find(GetClipboardOwner())) + fl_trigger_clipboard_notify(1); + initial_clipboard = false; + + if (next_clipboard_wnd) + SendMessage(next_clipboard_wnd, WM_DRAWCLIPBOARD, wParam, lParam); + + return 0; + default: if (Fl::handle(0,0)) return 0; break; @@ -1647,6 +1700,8 @@ Fl_X* Fl_X::make(Fl_Window* w) { x->next = Fl_X::first; Fl_X::first = x; + fl_clipboard_notify_target(x->xid); + x->wait_for_expose = 1; if (fl_show_iconic) {showit = 0; fl_show_iconic = 0;} if (showit) { diff --git a/src/Fl_x.cxx b/src/Fl_x.cxx index d0067a200..aba288f81 100644 --- a/src/Fl_x.cxx +++ b/src/Fl_x.cxx @@ -53,6 +53,12 @@ static XRRUpdateConfiguration_type XRRUpdateConfiguration_f; static int randrEventBase; // base of RandR-defined events #endif +# if HAVE_XFIXES +# include <X11/extensions/Xfixes.h> +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 @@ -306,6 +312,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; @@ -608,6 +617,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); @@ -655,6 +667,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); @@ -843,6 +864,107 @@ void Fl::copy(const char *stuff, int len, int clipboard) { } //////////////////////////////////////////////////////////////// +// 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 @@ -1005,7 +1127,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; @@ -1021,6 +1142,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; i<count; i++) { @@ -1061,6 +1195,9 @@ int fl_handle(const XEvent& thisevent) buffer[bytesread] = 0; convert_crlf(buffer, bytesread); } + + if (!fl_selection_requestor) return 0; + Fl::e_text = buffer ? (char*)buffer : (char *)""; Fl::e_length = bytesread; int old_event = Fl::e_number; @@ -1081,6 +1218,7 @@ int fl_handle(const XEvent& thisevent) case SelectionClear: { int clipboard = fl_xevent->xselectionclear.selection == CLIPBOARD; fl_i_own_selection[clipboard] = 0; + poll_clipboard_owner(); return 1;} case SelectionRequest: { @@ -1295,6 +1433,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: @@ -1663,6 +1804,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); } @@ -1982,6 +2142,16 @@ void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap) 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(); |
