summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/Fl.cxx67
-rw-r--r--src/Fl_cocoa.mm38
-rw-r--r--src/Fl_win32.cxx55
-rw-r--r--src/Fl_x.cxx172
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();