diff --git a/gtk/main.c b/gtk/main.c index 78dd8b1..c6819ff 100644 --- a/gtk/main.c +++ b/gtk/main.c @@ -243,6 +243,18 @@ sendreset(Im *im) g_signal_emit_by_name(im, "commit", commit); } +/* A password entry hidden the older way sets no purpose and signals nothing. */ +static int +ishidden(Im *im) +{ + GtkWidget *w; + + if(im->win == NULL) + return 0; + gdk_window_get_user_data(im->win, (gpointer*)&w); + return GTK_IS_ENTRY(w) && !gtk_entry_get_visibility(GTK_ENTRY(w)); +} + static gboolean kpress(GtkIMContext *ctx, GdkEventKey *ev) { @@ -264,7 +276,7 @@ kpress(GtkIMContext *ctx, GdkEventKey *ev) return simplefilter(ctx, ev, 0); key = ipckeysym(ev->keyval, gdk_keyval_to_unicode(ev->keyval)); mod = ipcmod(ev->state); - if(im->private || key == 0){ + if(im->private || ishidden(im) || key == 0){ r = simplefilter(ctx, ev, 0); /* A dead key starts a compose: pending text was typed first. */ if(im->simpleactive) diff --git a/tests/gtk_live_test.c b/tests/gtk_live_test.c index b49134a..873d54c 100644 --- a/tests/gtk_live_test.c +++ b/tests/gtk_live_test.c @@ -570,6 +570,25 @@ newwindow(int x, int y) return w; } +/* A GtkEntry gives its context a window of its own, under the toplevel's. */ +static GdkWindow* +entrywindow(GtkWidget *entry) +{ + GdkWindow *w; + GList *kids, *l; + gpointer u; + + w = NULL; + kids = gdk_window_get_children(gtk_widget_get_window(entry)); + for(l = kids; l != NULL; l = l->next){ + gdk_window_get_user_data(l->data, &u); + if(u == entry) + w = l->data; + } + g_list_free(kids); + return w; +} + #define Check(c, ...) do{ if(!(c)){ fail(__VA_ARGS__); goto out; } }while(0) int @@ -578,8 +597,8 @@ main(int argc, char **argv) Server srv; Siglog log; GtkIMContext *ctx, *oldctx, *badctx; - GtkWidget *top; - GdkWindow *win; + GtkWidget *top, *entry; + GdkWindow *win, *entrywin; GdkRectangle rect; Event e; static const GtkInputPurpose purposes[] = { @@ -757,6 +776,34 @@ main(int argc, char **argv) } g_object_set(ctx, "input-purpose", GTK_INPUT_PURPOSE_FREE_FORM, NULL); + + /* An entry hidden the older way is private too, purpose or no purpose. */ + entry = gtk_entry_new(); + gtk_container_add(GTK_CONTAINER(top), entry); + gtk_widget_show(entry); + pump(); + entrywin = entrywindow(entry); + Check(entrywin != NULL, "the test GtkEntry has no window of its own"); + first = eventcount(&srv); + gtk_im_context_set_client_window(ctx, entrywin); + /* The caret the new window invalidates must land before keys are counted. */ + Check(waitcount(&srv, first + 1) && getevent(&srv, first).type == Ecaret, + "the entry window did not renegotiate the caret"); + setpre(&srv, ""); + gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); + clearlog(&log); + first = eventcount(&srv); + Check(key(ctx, entrywin, GDK_KEY_y), "hidden-entry fallback did not commit"); + Check(eventcount(&srv) == first && strcmp(log.commit, "y") == 0, + "hidden-entry key reached daemon or did not commit"); + gtk_entry_set_visibility(GTK_ENTRY(entry), TRUE); + clearlog(&log); + first = eventcount(&srv); + Check(key(ctx, entrywin, GDK_KEY_y), "visible-entry key was not eaten"); + Check(waitcount(&srv, first + 1) && log.commit[0] == '\0', + "visible-entry key did not reach the daemon"); + gtk_im_context_set_client_window(ctx, win); + setpre(&srv, "reset"); clearlog(&log); Check(key(ctx, win, GDK_KEY_e), "reset setup key was not eaten");