Get rid of getopt_long and improve --help output.
[pinentry.git] / gtk+-2 / pinentry-gtk-2.c
1 /* pinentry-gtk-2.c
2    Copyright (C) 1999 Robert Bihlmeyer <robbe@orcus.priv.at>
3    Copyright (C) 2001, 2002, 2007 g10 Code GmbH
4    Copyright (C) 2004 by Albrecht DreƟ <albrecht.dress@arcor.de>
5
6    pinentry-gtk-2 is a pinentry application for the Gtk+-2 widget set.
7    It tries to follow the Gnome Human Interface Guide as close as
8    possible.
9
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 #include <gtk/gtk.h>
28 #include <assert.h>
29 #include <math.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #ifdef HAVE_GETOPT_H
35 #include <getopt.h>
36 #else
37 #include "getopt.h"
38 #endif                          /* HAVE_GETOPT_H */
39
40 #include "gtksecentry.h"
41 #include "pinentry.h"
42
43 #ifdef FALLBACK_CURSES
44 #include "pinentry-curses.h"
45 #endif
46
47 \f
48 #define PGMNAME "pinentry-gtk2"
49
50 #ifndef VERSION
51 #  define VERSION
52 #endif
53
54 static pinentry_t pinentry;
55 static int grab_failed;
56 static int passphrase_ok;
57 typedef enum { CONFIRM_CANCEL, CONFIRM_OK, CONFIRM_NOTOK } confirm_value_t;
58 static confirm_value_t confirm_value;
59
60 static GtkWidget *entry;
61 static GtkWidget *repeat_entry;
62 static GtkWidget *error_label;
63 static GtkWidget *qualitybar;
64 #ifdef ENABLE_ENHANCED
65 static GtkWidget *insure;
66 static GtkWidget *time_out;
67 #endif
68 static GtkTooltips *tooltips;
69 static gboolean got_input;
70
71 /* Gnome hig small and large space in pixels.  */
72 #define HIG_SMALL      6
73 #define HIG_LARGE     12
74
75 /* The text shown in the quality bar when no text is shown.  This is not
76  * the empty string, because with an empty string the height of
77  * the quality bar is less than with a non-empty string.  This results
78  * in ugly layout changes when the text changes from non-empty to empty
79  * and vice versa.  */
80 #define QUALITYBAR_EMPTY_TEXT " "
81
82 \f
83 /* Constrain size of the window the window should not shrink beyond
84    the requisition, and should not grow vertically.  */
85 static void
86 constrain_size (GtkWidget *win, GtkRequisition *req, gpointer data)
87 {
88   static gint width, height;
89   GdkGeometry geo;
90
91   if (req->width == width && req->height == height)
92     return;
93   width = req->width;
94   height = req->height;
95   geo.min_width = width;
96   /* This limit is arbitrary, but INT_MAX breaks other things */
97   geo.max_width = 10000;
98   geo.min_height = geo.max_height = height;
99   gtk_window_set_geometry_hints (GTK_WINDOW (win), NULL, &geo,
100                                  GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
101 }
102
103
104 /* Realize the window as transient if we grab the keyboard.  This
105    makes the window a modal dialog to the root window, which helps the
106    window manager.  See the following quote from:
107    http://standards.freedesktop.org/wm-spec/wm-spec-1.4.html#id2512420
108
109    Implementing enhanced support for application transient windows
110
111    If the WM_TRANSIENT_FOR property is set to None or Root window, the
112    window should be treated as a transient for all other windows in
113    the same group. It has been noted that this is a slight ICCCM
114    violation, but as this behavior is pretty standard for many
115    toolkits and window managers, and is extremely unlikely to break
116    anything, it seems reasonable to document it as standard.  */
117
118 static void
119 make_transient (GtkWidget *win, GdkEvent *event, gpointer data)
120 {
121   GdkScreen *screen;
122   GdkWindow *root;
123
124   if (! pinentry->grab)
125     return;
126
127   /* Make window transient for the root window.  */
128   screen = gdk_screen_get_default ();
129   root = gdk_screen_get_root_window (screen);
130   gdk_window_set_transient_for (win->window, root);
131 }
132
133
134 /* Grab the keyboard for maximum security */
135 static int
136 grab_keyboard (GtkWidget *win, GdkEvent *event, gpointer data)
137 {
138   if (! pinentry->grab)
139     return FALSE;
140
141   if (gdk_keyboard_grab (win->window, FALSE, gdk_event_get_time (event)))
142     {
143       g_critical ("could not grab keyboard");
144       grab_failed = 1;
145       gtk_main_quit ();
146     }
147   return FALSE;
148 }
149
150
151 /* Remove grab.  */
152 static int
153 ungrab_keyboard (GtkWidget *win, GdkEvent *event, gpointer data)
154 {
155   gdk_keyboard_ungrab (gdk_event_get_time (event));
156   /* Unmake window transient for the root window.  */
157   /* gdk_window_set_transient_for cannot be used with parent = NULL to
158      unset transient hint (unlike gtk_ version which can).  Replacement
159      code is taken from gtk_window_transient_parent_unrealized.  */
160   gdk_property_delete (win->window,
161                        gdk_atom_intern_static_string ("WM_TRANSIENT_FOR"));
162   return FALSE;
163 }
164
165
166 static int
167 delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
168 {
169   pinentry->close_button = 1;
170   gtk_main_quit ();
171   return TRUE;
172 }
173
174
175 static void
176 button_clicked (GtkWidget *widget, gpointer data)
177 {
178   if (data)
179     {
180       const char *s, *s2;
181
182       /* Okay button or enter used in text field.  */
183 #ifdef ENABLE_ENHANCED
184       /* FIXME: This is not compatible with assuan.  We can't just
185          print stuff on stdout.  */
186       /* if (pinentry->enhanced) */
187       /*   printf ("Options: %s\nTimeout: %d\n\n", */
188       /*        gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (insure)) */
189       /*        ? "insure" : "", */
190       /*        gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (time_out))); */
191 #endif
192
193       s = gtk_secure_entry_get_text (GTK_SECURE_ENTRY (entry));
194       if (!s)
195         s = "";
196
197       if (pinentry->repeat_passphrase && repeat_entry)
198         {
199           s2 = gtk_secure_entry_get_text (GTK_SECURE_ENTRY (repeat_entry));
200           if (!s2)
201             s2 = "";
202           if (strcmp (s, s2))
203             {
204               gtk_label_set_text (GTK_LABEL (error_label),
205                                   pinentry->repeat_error_string?
206                                   pinentry->repeat_error_string:
207                                   "not correctly repeated");
208               gtk_widget_grab_focus (entry);
209               return; /* again */
210             }
211           pinentry->repeat_okay = 1;
212         }
213
214       passphrase_ok = 1;
215       pinentry_setbufferlen (pinentry, strlen (s) + 1);
216       if (pinentry->pin)
217         strcpy (pinentry->pin, s);
218     }
219   gtk_main_quit ();
220 }
221
222
223 static void
224 enter_callback (GtkWidget *widget, GtkWidget *anentry)
225 {
226   button_clicked (widget, "ok");
227 }
228
229
230 static void
231 confirm_button_clicked (GtkWidget *widget, gpointer data)
232 {
233   confirm_value = (int)(long) data;
234   gtk_main_quit ();
235 }
236
237
238 static gchar *
239 pinentry_utf8_validate (gchar *text)
240 {
241   gchar *result;
242
243   if (!text)
244     return NULL;
245
246   if (g_utf8_validate (text, -1, NULL))
247     return g_strdup (text);
248
249   /* Failure: Assume that it was encoded in the current locale and
250      convert it to utf-8.  */
251   result = g_locale_to_utf8 (text, -1, NULL, NULL, NULL);
252   if (!result)
253     {
254       gchar *p;
255
256       result = p = g_strdup (text);
257       while (!g_utf8_validate (p, -1, (const gchar **) &p))
258         *p = '?';
259     }
260   return result;
261 }
262
263
264 /* Handler called for "changed".   We use it to update the quality
265    indicator.  */
266 static void
267 changed_text_handler (GtkWidget *widget)
268 {
269   char textbuf[50];
270   const char *s;
271   int length;
272   int percent;
273   GdkColor color = { 0, 0, 0, 0};
274
275   got_input = TRUE;
276
277   if (pinentry->repeat_passphrase && repeat_entry)
278     {
279       gtk_secure_entry_set_text (GTK_SECURE_ENTRY (repeat_entry), "");
280       gtk_label_set_text (GTK_LABEL (error_label), "");
281     }
282
283   if (!qualitybar || !pinentry->quality_bar)
284     return;
285
286   s = gtk_secure_entry_get_text (GTK_SECURE_ENTRY (widget));
287   if (!s)
288     s = "";
289   length = strlen (s);
290   percent = length? pinentry_inq_quality (pinentry, s, length) : 0;
291   if (!length)
292     {
293       strcpy(textbuf, QUALITYBAR_EMPTY_TEXT);
294       color.red = 0xffff;
295     }
296   else if (percent < 0)
297     {
298       snprintf (textbuf, sizeof textbuf, "(%d%%)", -percent);
299       color.red = 0xffff;
300       percent = -percent;
301     }
302   else
303     {
304       snprintf (textbuf, sizeof textbuf, "%d%%", percent);
305       color.green = 0xffff;
306     }
307   gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (qualitybar),
308                                  (double)percent/100.0);
309   gtk_progress_bar_set_text (GTK_PROGRESS_BAR (qualitybar), textbuf);
310   gtk_widget_modify_bg (qualitybar, GTK_STATE_PRELIGHT, &color);
311 }
312
313
314 static gboolean
315 timeout_cb (gpointer data)
316 {
317   (void)data;
318   if (!got_input)
319     gtk_main_quit ();
320   return FALSE;
321 }
322
323
324 static GtkWidget *
325 create_window (int confirm_mode)
326 {
327   GtkWidget *w;
328   GtkWidget *win, *box;
329   GtkWidget *wvbox, *chbox, *bbox;
330   GtkAccelGroup *acc;
331   gchar *msg;
332
333   tooltips = gtk_tooltips_new ();
334
335   /* FIXME: check the grabbing code against the one we used with the
336      old gpg-agent */
337   win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
338   acc = gtk_accel_group_new ();
339
340   g_signal_connect (G_OBJECT (win), "delete_event",
341                     G_CALLBACK (delete_event), NULL);
342
343 #if 0
344   g_signal_connect (G_OBJECT (win), "destroy", G_CALLBACK (gtk_main_quit),
345                     NULL);
346 #endif
347   g_signal_connect (G_OBJECT (win), "size-request",
348                     G_CALLBACK (constrain_size), NULL);
349   if (!confirm_mode)
350     {
351       if (pinentry->grab)
352         g_signal_connect (G_OBJECT (win),
353                           "realize", G_CALLBACK (make_transient), NULL);
354
355       /* We need to grab the keyboard when its visible! not when its
356          mapped (there is a difference)  */
357       g_object_set (G_OBJECT(win), "events",
358                     GDK_VISIBILITY_NOTIFY_MASK | GDK_STRUCTURE_MASK, NULL);
359
360       g_signal_connect (G_OBJECT (win),
361                         pinentry->grab
362                         ? "visibility-notify-event"
363                         : "focus-in-event",
364                         G_CALLBACK (grab_keyboard), NULL);
365       g_signal_connect (G_OBJECT (win),
366                         pinentry->grab ? "unmap-event" : "focus-out-event",
367                         G_CALLBACK (ungrab_keyboard), NULL);
368     }
369   gtk_window_add_accel_group (GTK_WINDOW (win), acc);
370
371   wvbox = gtk_vbox_new (FALSE, HIG_LARGE * 2);
372   gtk_container_add (GTK_CONTAINER (win), wvbox);
373   gtk_container_set_border_width (GTK_CONTAINER (wvbox), HIG_LARGE);
374
375   chbox = gtk_hbox_new (FALSE, HIG_LARGE);
376   gtk_box_pack_start (GTK_BOX (wvbox), chbox, FALSE, FALSE, 0);
377
378   w = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION,
379                                                GTK_ICON_SIZE_DIALOG);
380   gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.0);
381   gtk_box_pack_start (GTK_BOX (chbox), w, FALSE, FALSE, 0);
382
383   box = gtk_vbox_new (FALSE, HIG_SMALL);
384   gtk_box_pack_start (GTK_BOX (chbox), box, TRUE, TRUE, 0);
385
386   if (pinentry->title)
387     {
388       msg = pinentry_utf8_validate (pinentry->title);
389       gtk_window_set_title (GTK_WINDOW(win), msg);
390     }
391   if (pinentry->description)
392     {
393       msg = pinentry_utf8_validate (pinentry->description);
394       w = gtk_label_new (msg);
395       g_free (msg);
396       gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5);
397       gtk_label_set_line_wrap (GTK_LABEL (w), TRUE);
398       gtk_box_pack_start (GTK_BOX (box), w, TRUE, FALSE, 0);
399     }
400   if (!confirm_mode && (pinentry->error || pinentry->repeat_passphrase))
401     {
402       /* With the repeat passphrase option we need to create the label
403          in any case so that it may later be updated by the error
404          message.  */
405       GdkColor color = { 0, 0xffff, 0, 0 };
406
407       if (pinentry->error)
408         msg = pinentry_utf8_validate (pinentry->error);
409       else
410         msg = "";
411       error_label = gtk_label_new (msg);
412       if (pinentry->error)
413         g_free (msg);
414       gtk_misc_set_alignment (GTK_MISC (error_label), 0.0, 0.5);
415       gtk_label_set_line_wrap (GTK_LABEL (error_label), TRUE);
416       gtk_box_pack_start (GTK_BOX (box), error_label, TRUE, FALSE, 0);
417       gtk_widget_modify_fg (error_label, GTK_STATE_NORMAL, &color);
418     }
419
420   qualitybar = NULL;
421
422   if (!confirm_mode)
423     {
424       int nrow;
425       GtkWidget* table;
426
427       nrow = 1;
428       if (pinentry->quality_bar)
429         nrow++;
430       if (pinentry->repeat_passphrase)
431         nrow++;
432
433       table = gtk_table_new (nrow, 2, FALSE);
434       nrow = 0;
435       gtk_box_pack_start (GTK_BOX (box), table, FALSE, FALSE, 0);
436
437       if (pinentry->prompt)
438         {
439           msg = pinentry_utf8_validate (pinentry->prompt);
440           w = gtk_label_new_with_mnemonic (msg);
441           g_free (msg);
442           gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5);
443           gtk_table_attach (GTK_TABLE (table), w, 0, 1, nrow, nrow+1,
444                             GTK_FILL, GTK_FILL, 4, 0);
445         }
446
447       entry = gtk_secure_entry_new ();
448       gtk_widget_set_size_request (entry, 200, -1);
449       g_signal_connect (G_OBJECT (entry), "activate",
450                         G_CALLBACK (enter_callback), entry);
451       g_signal_connect (G_OBJECT (entry), "changed",
452                         G_CALLBACK (changed_text_handler), entry);
453       gtk_table_attach (GTK_TABLE (table), entry, 1, 2, nrow, nrow+1,
454                         GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
455       gtk_widget_grab_focus (entry);
456       gtk_widget_show (entry);
457       nrow++;
458
459       if (pinentry->quality_bar)
460         {
461           msg = pinentry_utf8_validate (pinentry->quality_bar);
462           w = gtk_label_new (msg);
463           g_free (msg);
464           gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5);
465           gtk_table_attach (GTK_TABLE (table), w, 0, 1, nrow, nrow+1,
466                             GTK_FILL, GTK_FILL, 4, 0);
467           qualitybar = gtk_progress_bar_new();
468           gtk_widget_add_events (qualitybar,
469                                  GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
470           gtk_progress_bar_set_text (GTK_PROGRESS_BAR (qualitybar),
471                                      QUALITYBAR_EMPTY_TEXT);
472           gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (qualitybar), 0.0);
473           if (pinentry->quality_bar_tt)
474             gtk_tooltips_set_tip (GTK_TOOLTIPS (tooltips), qualitybar,
475                                   pinentry->quality_bar_tt, "");
476           gtk_table_attach (GTK_TABLE (table), qualitybar, 1, 2, nrow, nrow+1,
477                             GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
478           nrow++;
479         }
480
481
482       if (pinentry->repeat_passphrase)
483         {
484           msg = pinentry_utf8_validate (pinentry->repeat_passphrase);
485           w = gtk_label_new (msg);
486           g_free (msg);
487           gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5);
488           gtk_table_attach (GTK_TABLE (table), w, 0, 1, nrow, nrow+1,
489                             GTK_FILL, GTK_FILL, 4, 0);
490
491           repeat_entry = gtk_secure_entry_new ();
492           gtk_widget_set_size_request (repeat_entry, 200, -1);
493           g_signal_connect (G_OBJECT (entry), "activate",
494                             G_CALLBACK (enter_callback), repeat_entry);
495           gtk_table_attach (GTK_TABLE (table), repeat_entry, 1, 2, nrow, nrow+1,
496                             GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
497           gtk_widget_grab_focus (entry);
498           gtk_widget_show (entry);
499           nrow++;
500         }
501
502
503 #ifdef ENABLE_ENHANCED
504       if (pinentry->enhanced)
505         {
506           GtkWidget *sbox = gtk_hbox_new (FALSE, HIG_SMALL);
507           gtk_box_pack_start (GTK_BOX (box), sbox, FALSE, FALSE, 0);
508
509           w = gtk_label_new ("Forget secret after");
510           gtk_box_pack_start (GTK_BOX (sbox), w, FALSE, FALSE, 0);
511           gtk_widget_show (w);
512
513           time_out = gtk_spin_button_new
514             (GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, HUGE_VAL, 1, 60, 60)),
515              2, 0);
516           gtk_box_pack_start (GTK_BOX (sbox), time_out, FALSE, FALSE, 0);
517           gtk_widget_show (time_out);
518
519           w = gtk_label_new ("seconds");
520           gtk_box_pack_start (GTK_BOX (sbox), w, FALSE, FALSE, 0);
521           gtk_widget_show (w);
522           gtk_widget_show (sbox);
523
524           insure = gtk_check_button_new_with_label ("ask before giving out "
525                                                     "secret");
526           gtk_box_pack_start (GTK_BOX (box), insure, FALSE, FALSE, 0);
527           gtk_widget_show (insure);
528         }
529 #endif
530     }
531
532   bbox = gtk_hbutton_box_new ();
533   gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
534   gtk_box_set_spacing (GTK_BOX (bbox), 6);
535   gtk_box_pack_start (GTK_BOX (wvbox), bbox, TRUE, FALSE, 0);
536
537   if (!pinentry->one_button)
538     {
539       if (pinentry->cancel)
540         {
541           msg = pinentry_utf8_validate (pinentry->cancel);
542           w = gtk_button_new_with_mnemonic (msg);
543           g_free (msg);
544         }
545       else if (pinentry->default_cancel)
546         {
547           GtkWidget *image;
548
549           msg = pinentry_utf8_validate (pinentry->default_cancel);
550           w = gtk_button_new_with_mnemonic (msg);
551           g_free (msg);
552           image = gtk_image_new_from_stock (GTK_STOCK_CANCEL,
553                                             GTK_ICON_SIZE_BUTTON);
554           if (image)
555             gtk_button_set_image (GTK_BUTTON (w), image);
556         }
557       else
558           w = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
559       gtk_container_add (GTK_CONTAINER (bbox), w);
560       g_signal_connect (G_OBJECT (w), "clicked",
561                         G_CALLBACK (confirm_mode ? confirm_button_clicked
562                                     : button_clicked),
563                         (gpointer) CONFIRM_CANCEL);
564       GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
565     }
566
567   if (confirm_mode && !pinentry->one_button && pinentry->notok)
568     {
569       msg = pinentry_utf8_validate (pinentry->notok);
570       w = gtk_button_new_with_mnemonic (msg);
571       g_free (msg);
572
573       gtk_container_add (GTK_CONTAINER (bbox), w);
574       g_signal_connect (G_OBJECT (w), "clicked",
575                         G_CALLBACK (confirm_button_clicked),
576                         (gpointer) CONFIRM_NOTOK);
577       GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
578     }
579
580   if (pinentry->ok)
581     {
582       msg = pinentry_utf8_validate (pinentry->ok);
583       w = gtk_button_new_with_mnemonic (msg);
584       g_free (msg);
585     }
586   else if (pinentry->default_ok)
587     {
588       GtkWidget *image;
589
590       msg = pinentry_utf8_validate (pinentry->default_ok);
591       w = gtk_button_new_with_mnemonic (msg);
592       g_free (msg);
593       image = gtk_image_new_from_stock (GTK_STOCK_OK,
594                                         GTK_ICON_SIZE_BUTTON);
595       if (image)
596         gtk_button_set_image (GTK_BUTTON (w), image);
597     }
598   else
599     w = gtk_button_new_from_stock (GTK_STOCK_OK);
600   gtk_container_add (GTK_CONTAINER(bbox), w);
601   if (!confirm_mode)
602     {
603       g_signal_connect (G_OBJECT (w), "clicked",
604                         G_CALLBACK (button_clicked), "ok");
605       GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
606       gtk_widget_grab_default (w);
607       g_signal_connect_object (G_OBJECT (entry), "focus_in_event",
608                                 G_CALLBACK (gtk_widget_grab_default),
609                                G_OBJECT (w), 0);
610     }
611   else
612     {
613       g_signal_connect (G_OBJECT (w), "clicked",
614                         G_CALLBACK(confirm_button_clicked),
615                         (gpointer) CONFIRM_OK);
616       GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
617     }
618
619   gtk_window_set_position (GTK_WINDOW (win), GTK_WIN_POS_CENTER);
620   gtk_window_set_keep_above (GTK_WINDOW (win), TRUE);
621   gtk_widget_show_all (win);
622   gtk_window_present (GTK_WINDOW (win));  /* Make sure it has the focus.  */
623
624   if (pinentry->timeout > 0)
625     g_timeout_add (pinentry->timeout*1000, timeout_cb, NULL);
626
627   return win;
628 }
629
630
631 static int
632 gtk_cmd_handler (pinentry_t pe)
633 {
634   GtkWidget *w;
635   int want_pass = !!pe->pin;
636
637   got_input = FALSE;
638   pinentry = pe;
639   confirm_value = CONFIRM_CANCEL;
640   passphrase_ok = 0;
641   w = create_window (want_pass ? 0 : 1);
642   gtk_main ();
643   gtk_widget_destroy (w);
644   while (gtk_events_pending ())
645     gtk_main_iteration ();
646
647   if (confirm_value == CONFIRM_CANCEL || grab_failed)
648     pe->canceled = 1;
649
650   pinentry = NULL;
651   if (want_pass)
652     {
653       if (passphrase_ok && pe->pin)
654         return strlen (pe->pin);
655       else
656         return -1;
657     }
658   else
659     return (confirm_value == CONFIRM_OK) ? 1 : 0;
660 }
661
662
663 pinentry_cmd_handler_t pinentry_cmd_handler = gtk_cmd_handler;
664
665
666 int
667 main (int argc, char *argv[])
668 {
669   static GMemVTable secure_mem =
670     {
671       secentry_malloc,
672       secentry_realloc,
673       secentry_free,
674       NULL,
675       NULL,
676       NULL
677     };
678
679   g_mem_set_vtable (&secure_mem);
680
681   pinentry_init (PGMNAME);
682
683 #ifdef FALLBACK_CURSES
684   if (pinentry_have_display (argc, argv))
685     gtk_init (&argc, &argv);
686   else
687     pinentry_cmd_handler = curses_cmd_handler;
688 #else
689   gtk_init (&argc, &argv);
690 #endif
691
692   pinentry_parse_opts (argc, argv);
693
694   if (pinentry_loop ())
695     return 1;
696
697   return 0;
698 }