Don't allow gtk's event loop to run while waiting for clipboard operations to complete. This prevents a whole mess of problems related to dispatching focus events in the middle of dispatching blur events, or vice versa. Bug 214583, r=blizzard. Not part of the default build.

This commit is contained in:
bryner%brianryner.com 2003-09-15 20:38:45 +00:00
Родитель f910b4d9b7
Коммит 0536e92d07
1 изменённых файлов: 191 добавлений и 7 удалений

Просмотреть файл

@ -48,6 +48,14 @@
#include <gtk/gtkclipboard.h>
#include <gtk/gtkinvisible.h>
// For manipulation of the X event queue
#include <X11/Xlib.h>
#include <gdk/gdkx.h>
#include <gtk/gtkmain.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
// Callback when someone asks us for the selection
void
invisible_selection_get_cb (GtkWidget *aWidget,
@ -70,6 +78,34 @@ ConvertHTMLtoUCS2 (guchar *data,
static void
GetHTMLCharset (guchar * data, PRInt32 dataLength, nsCString& str);
// Our own versions of gtk_clipboard_wait_for_contents and
// gtk_clipboard_wait_for_text, which don't run the event loop while
// waiting for the data. This prevents a lot of problems related to
// dispatching events at unexpected times.
static GtkSelectionData *
wait_for_contents (GtkClipboard *clipboard, GdkAtom target);
static gchar *
wait_for_text (GtkClipboard *clipboard);
static Bool
checkEventProc(Display *display, XEvent *event, XPointer arg);
static void
WaitForRetrieval(GtkClipboard *clipboard, void **transferData);
static void
clipboard_contents_received(GtkClipboard *clipboard,
GtkSelectionData *selection_data,
gpointer data);
static void
clipboard_text_received(GtkClipboard *clipboard,
const gchar *text,
gpointer data);
nsClipboard::nsClipboard()
{
mWidget = nsnull;
@ -216,10 +252,10 @@ nsClipboard::GetData(nsITransferable *aTransferable, PRInt32 aWhichClipboard)
// Special case text/unicode since we can convert any
// string into text/unicode
if (!strcmp(flavorStr, kUnicodeMime)) {
gchar *new_text = gtk_clipboard_wait_for_text(clipboard);
gchar* new_text = wait_for_text(clipboard);
if (new_text) {
// Convert utf-8 into our unicode format.
nsString ucs2string = NS_ConvertUTF8toUCS2(new_text);
NS_ConvertUTF8toUCS2 ucs2string(new_text);
data = (guchar *)ToNewUnicode(ucs2string);
length = ucs2string.Length() * 2;
g_free(new_text);
@ -237,8 +273,7 @@ nsClipboard::GetData(nsITransferable *aTransferable, PRInt32 aWhichClipboard)
// the clipboard.
GdkAtom atom = gdk_atom_intern(flavorStr, FALSE);
GtkSelectionData *selectionData;
selectionData = gtk_clipboard_wait_for_contents(clipboard,
atom);
selectionData = wait_for_contents(clipboard, atom);
if (selectionData) {
length = selectionData->length * selectionData->format / 8;
// Special case text/html since we can convert into UCS2
@ -393,9 +428,7 @@ GtkSelectionData *
nsClipboard::GetTargets(GdkAtom aWhichClipboard)
{
GtkClipboard *clipboard = gtk_clipboard_get(aWhichClipboard);
return gtk_clipboard_wait_for_contents (clipboard,
gdk_atom_intern ("TARGETS",
FALSE));
return wait_for_contents(clipboard, gdk_atom_intern("TARGETS", FALSE));
}
nsITransferable *
@ -695,3 +728,154 @@ void GetHTMLCharset(guchar * data, PRInt32 dataLength, nsCString& str)
str.Assign(NS_LITERAL_CSTRING("UNKNOWN"));
}
static void
DispatchSelectionNotifyEvent(GdkDisplay *display, GtkWidget *widget,
XEvent *xevent)
{
GdkEvent event;
event.selection.type = GDK_SELECTION_NOTIFY;
event.selection.window = widget->window;
event.selection.selection = gdk_x11_xatom_to_atom_for_display(display, xevent->xselection.selection);
event.selection.target = gdk_x11_xatom_to_atom_for_display(display, xevent->xselection.target);
event.selection.property = gdk_x11_xatom_to_atom_for_display(display, xevent->xselection.property);
event.selection.time = xevent->xselection.time;
gtk_widget_event(widget, &event);
}
static void
DispatchPropertyNotifyEvent(GdkDisplay *display, GtkWidget *widget,
XEvent *xevent)
{
if (((GdkWindowObject *) widget->window)->event_mask & GDK_PROPERTY_CHANGE_MASK) {
GdkEvent event;
event.property.type = GDK_PROPERTY_NOTIFY;
event.property.window = widget->window;
event.property.atom = gdk_x11_xatom_to_atom_for_display(display, xevent->xproperty.atom);
event.property.time = xevent->xproperty.time;
event.property.state = xevent->xproperty.state;
gtk_widget_event(widget, &event);
}
}
struct checkEventContext
{
Window window;
Atom selAtom;
};
static Bool
checkEventProc(Display *display, XEvent *event, XPointer arg)
{
checkEventContext *context = (checkEventContext *) arg;
return (event->xany.window == context->window &&
(event->xany.type == SelectionNotify ||
(event->xany.type == PropertyNotify &&
event->xproperty.atom == context->selAtom)));
}
// Idle timeout for receiving selection and property notify events (microsec)
static const int kClipboardTimeout = 500000;
static void
WaitForRetrieval(GtkClipboard *clipboard, void **transferData)
{
if (*transferData) // the request completed synchronously
return;
GdkDisplay *cbDisplay = gtk_clipboard_get_display(clipboard);
Display *xDisplay = gdk_x11_display_get_xdisplay(cbDisplay);
GtkWidget *cbWidget = NS_STATIC_CAST(GtkWidget *,
g_object_get_data(G_OBJECT(cbDisplay),
"gtk-clipboard-widget"));
checkEventContext context;
context.window = GDK_WINDOW_XWINDOW(cbWidget->window);
context.selAtom = gdk_x11_atom_to_xatom_for_display(cbDisplay,
gdk_atom_intern("GDK_SELECTION", FALSE));
// Send X events which are relevant to the ongoing selection retrieval
// to the clipboard widget. Wait until either the operation completes, or
// we hit our timeout. All other X events remain queued.
int cnumber;
#ifdef VMS
cnumber = XConnectionNumber(xDisplay);
#else
cnumber = ConnectionNumber(xDisplay);
#endif
fd_set rfds;
struct timeval tv;
FD_ZERO(&rfds);
FD_SET(cnumber++, &rfds);
do {
XEvent xevent;
while (XCheckIfEvent(xDisplay, &xevent, checkEventProc,
(XPointer) &context)) {
if (xevent.xany.type == SelectionNotify)
DispatchSelectionNotifyEvent(cbDisplay, cbWidget, &xevent);
else
DispatchPropertyNotifyEvent(cbDisplay, cbWidget, &xevent);
if (*transferData)
return;
}
tv.tv_sec = 0;
tv.tv_usec = kClipboardTimeout;
} while (select(cnumber, &rfds, NULL, NULL, &tv) == 1);
#ifdef DEBUG_CLIPBOARD
printf("exceeded clipboard timeout\n");
#endif
}
static void
clipboard_contents_received(GtkClipboard *clipboard,
GtkSelectionData *selection_data,
gpointer data)
{
GtkSelectionData **sel_copy = NS_STATIC_CAST(GtkSelectionData **, data);
*sel_copy = gtk_selection_data_copy(selection_data);
}
static GtkSelectionData *
wait_for_contents(GtkClipboard *clipboard, GdkAtom target)
{
GtkSelectionData *selection_data = NULL;
gtk_clipboard_request_contents(clipboard, target,
clipboard_contents_received,
&selection_data);
WaitForRetrieval(clipboard, (void **) &selection_data);
return selection_data;
}
static void
clipboard_text_received(GtkClipboard *clipboard,
const gchar *text,
gpointer data)
{
gchar **text_copy = NS_STATIC_CAST(gchar **, data);
*text_copy = g_strdup(text);
}
static gchar *
wait_for_text(GtkClipboard *clipboard)
{
gchar *text = NULL;
gtk_clipboard_request_text(clipboard, clipboard_text_received, &text);
WaitForRetrieval(clipboard, (void **) &text);
return text;
}