#include <assert.h>
#include <time.h>
#include <stdlib.h>
+
+#include <unistd.h>
+
#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#if !GTK_CHECK_VERSION(3,0,0)
#include <gdk/gdkkeysyms.h>
+#endif
+
+#include "gtkfont.h"
+#include "gtkcompat.h"
+#include "gtkmisc.h"
#include "misc.h"
struct drawing_area_ctx {
GtkWidget *area;
+#ifndef DRAW_DEFAULT_CAIRO
GdkColor *cols;
+#endif
int width, height, current;
};
GtkWidget *dialog, *promptlabel;
struct drawing_area_ctx drawingareas[N_DRAWING_AREAS];
int active_area;
+#if GTK_CHECK_VERSION(2,0,0)
GtkIMContext *imc;
+#endif
+#ifndef DRAW_DEFAULT_CAIRO
GdkColormap *colmap;
GdkColor cols[2];
+#endif
char *passphrase;
int passlen, passsize;
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkDevice *keyboard; /* for gdk_device_grab */
+#endif
};
static void visually_acknowledge_keypress(struct askpass_ctx *ctx)
return ctx->passlen - i;
}
+static void add_text_to_passphrase(struct askpass_ctx *ctx, gchar *str)
+{
+ int len = strlen(str);
+ if (ctx->passlen + len >= ctx->passsize) {
+ /* Take some care with buffer expansion, because there are
+ * pieces of passphrase in the old buffer so we should ensure
+ * realloc doesn't leave a copy lying around in the address
+ * space. */
+ int oldsize = ctx->passsize;
+ char *newbuf;
+
+ ctx->passsize = (ctx->passlen + len) * 5 / 4 + 1024;
+ newbuf = snewn(ctx->passsize, char);
+ memcpy(newbuf, ctx->passphrase, oldsize);
+ smemclr(ctx->passphrase, oldsize);
+ sfree(ctx->passphrase);
+ ctx->passphrase = newbuf;
+ }
+ strcpy(ctx->passphrase + ctx->passlen, str);
+ ctx->passlen += len;
+ visually_acknowledge_keypress(ctx);
+}
+
static gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
{
struct askpass_ctx *ctx = (struct askpass_ctx *)data;
- if (event->keyval == GDK_Return && event->type == GDK_KEY_PRESS) {
+
+ if (event->keyval == GDK_KEY_Return &&
+ event->type == GDK_KEY_PRESS) {
gtk_main_quit();
- } else if (event->keyval == GDK_Escape && event->type == GDK_KEY_PRESS) {
+ } else if (event->keyval == GDK_KEY_Escape &&
+ event->type == GDK_KEY_PRESS) {
smemclr(ctx->passphrase, ctx->passsize);
ctx->passphrase = NULL;
gtk_main_quit();
} else {
+#if GTK_CHECK_VERSION(2,0,0)
if (gtk_im_context_filter_keypress(ctx->imc, event))
return TRUE;
+#endif
if (event->type == GDK_KEY_PRESS) {
if (!strcmp(event->string, "\x15")) {
break;
}
visually_acknowledge_keypress(ctx);
- } else if (event->keyval == GDK_BackSpace) {
+ } else if (event->keyval == GDK_KEY_BackSpace) {
/* Backspace. Delete one character. */
if (ctx->passlen > 0)
ctx->passlen -= last_char_len(ctx);
visually_acknowledge_keypress(ctx);
+#if !GTK_CHECK_VERSION(2,0,0)
+ } else if (event->string[0]) {
+ add_text_to_passphrase(ctx, event->string);
+#endif
}
}
}
return TRUE;
}
+#if GTK_CHECK_VERSION(2,0,0)
static void input_method_commit_event(GtkIMContext *imc, gchar *str,
gpointer data)
{
struct askpass_ctx *ctx = (struct askpass_ctx *)data;
- int len = strlen(str);
- if (ctx->passlen + len >= ctx->passsize) {
- /* Take some care with buffer expansion, because there are
- * pieces of passphrase in the old buffer so we should ensure
- * realloc doesn't leave a copy lying around in the address
- * space. */
- int oldsize = ctx->passsize;
- char *newbuf;
-
- ctx->passsize = (ctx->passlen + len) * 5 / 4 + 1024;
- newbuf = snewn(ctx->passsize, char);
- memcpy(newbuf, ctx->passphrase, oldsize);
- smemclr(ctx->passphrase, oldsize);
- sfree(ctx->passphrase);
- ctx->passphrase = newbuf;
- }
- strcpy(ctx->passphrase + ctx->passlen, str);
- ctx->passlen += len;
- visually_acknowledge_keypress(ctx);
+ add_text_to_passphrase(ctx, str);
}
+#endif
static gint configure_area(GtkWidget *widget, GdkEventConfigure *event,
gpointer data)
return TRUE;
}
+#ifdef DRAW_DEFAULT_CAIRO
+static void askpass_redraw_cairo(cairo_t *cr, struct drawing_area_ctx *ctx)
+{
+ cairo_set_source_rgb(cr, 1-ctx->current, 1-ctx->current, 1-ctx->current);
+ cairo_paint(cr);
+}
+#else
+static void askpass_redraw_gdk(GdkWindow *win, struct drawing_area_ctx *ctx)
+{
+ GdkGC *gc = gdk_gc_new(win);
+ gdk_gc_set_foreground(gc, &ctx->cols[ctx->current]);
+ gdk_draw_rectangle(win, gc, TRUE, 0, 0, ctx->width, ctx->height);
+ gdk_gc_unref(gc);
+}
+#endif
+
+#if GTK_CHECK_VERSION(3,0,0)
+static gint draw_area(GtkWidget *widget, cairo_t *cr, gpointer data)
+{
+ struct drawing_area_ctx *ctx = (struct drawing_area_ctx *)data;
+ askpass_redraw_cairo(cr, ctx);
+ return TRUE;
+}
+#else
static gint expose_area(GtkWidget *widget, GdkEventExpose *event,
gpointer data)
{
struct drawing_area_ctx *ctx = (struct drawing_area_ctx *)data;
- GdkGC *gc = gdk_gc_new(ctx->area->window);
- gdk_gc_set_foreground(gc, &ctx->cols[ctx->current]);
- gdk_draw_rectangle(widget->window, gc, TRUE,
- 0, 0, ctx->width, ctx->height);
- gdk_gc_unref(gc);
+#ifdef DRAW_DEFAULT_CAIRO
+ cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(ctx->area));
+ askpass_redraw_cairo(cr, ctx);
+ cairo_destroy(cr);
+#else
+ askpass_redraw_gdk(gtk_widget_get_window(ctx->area), ctx);
+#endif
+
return TRUE;
}
+#endif
static int try_grab_keyboard(struct askpass_ctx *ctx)
{
- int ret = gdk_keyboard_grab(ctx->dialog->window, FALSE, GDK_CURRENT_TIME);
+ int ret;
+
+#if GTK_CHECK_VERSION(3,0,0)
+ /*
+ * Grabbing the keyboard is quite complicated in GTK 3.
+ */
+ GdkDeviceManager *dm;
+ GdkDevice *pointer, *keyboard;
+
+ dm = gdk_display_get_device_manager
+ (gtk_widget_get_display(ctx->dialog));
+ if (!dm)
+ return FALSE;
+
+ pointer = gdk_device_manager_get_client_pointer(dm);
+ if (!pointer)
+ return FALSE;
+ keyboard = gdk_device_get_associated_device(pointer);
+ if (!keyboard)
+ return FALSE;
+ if (gdk_device_get_source(keyboard) != GDK_SOURCE_KEYBOARD)
+ return FALSE;
+
+ ctx->keyboard = keyboard;
+ ret = gdk_device_grab(ctx->keyboard,
+ gtk_widget_get_window(ctx->dialog),
+ GDK_OWNERSHIP_NONE,
+ TRUE,
+ GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
+ NULL,
+ GDK_CURRENT_TIME);
+#else
+ /*
+ * It's much simpler in GTK 1 and 2!
+ */
+ ret = gdk_keyboard_grab(gtk_widget_get_window(ctx->dialog),
+ FALSE, GDK_CURRENT_TIME);
+#endif
+
return ret == GDK_GRAB_SUCCESS;
}
const char *prompt_text)
{
int i;
- gboolean success[2];
+ GtkBox *action_area;
ctx->passlen = 0;
ctx->passsize = 2048;
/*
* Create widgets.
*/
- ctx->dialog = gtk_dialog_new();
+ ctx->dialog = our_dialog_new();
gtk_window_set_title(GTK_WINDOW(ctx->dialog), window_title);
+ gtk_window_set_position(GTK_WINDOW(ctx->dialog), GTK_WIN_POS_CENTER);
ctx->promptlabel = gtk_label_new(prompt_text);
+ align_label_left(GTK_LABEL(ctx->promptlabel));
+ gtk_widget_show(ctx->promptlabel);
gtk_label_set_line_wrap(GTK_LABEL(ctx->promptlabel), TRUE);
- gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area
- (GTK_DIALOG(ctx->dialog))),
- ctx->promptlabel);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_label_set_width_chars(GTK_LABEL(ctx->promptlabel), 48);
+#endif
+ our_dialog_add_to_content_area(GTK_WINDOW(ctx->dialog),
+ ctx->promptlabel, TRUE, TRUE, 0);
+#if GTK_CHECK_VERSION(2,0,0)
ctx->imc = gtk_im_multicontext_new();
- ctx->colmap = gdk_colormap_get_system();
- ctx->cols[0].red = ctx->cols[0].green = ctx->cols[0].blue = 0xFFFF;
- ctx->cols[1].red = ctx->cols[1].green = ctx->cols[1].blue = 0;
- gdk_colormap_alloc_colors(ctx->colmap, ctx->cols, 2,
- FALSE, TRUE, success);
- if (!success[0] | !success[1])
- return "unable to allocate colours";
+#endif
+#ifndef DRAW_DEFAULT_CAIRO
+ {
+ gboolean success[2];
+ ctx->colmap = gdk_colormap_get_system();
+ ctx->cols[0].red = ctx->cols[0].green = ctx->cols[0].blue = 0xFFFF;
+ ctx->cols[1].red = ctx->cols[1].green = ctx->cols[1].blue = 0;
+ gdk_colormap_alloc_colors(ctx->colmap, ctx->cols, 2,
+ FALSE, TRUE, success);
+ if (!success[0] | !success[1])
+ return "unable to allocate colours";
+ }
+#endif
+
+ action_area = our_dialog_make_action_hbox(GTK_WINDOW(ctx->dialog));
+
for (i = 0; i < N_DRAWING_AREAS; i++) {
ctx->drawingareas[i].area = gtk_drawing_area_new();
+#ifndef DRAW_DEFAULT_CAIRO
ctx->drawingareas[i].cols = ctx->cols;
+#endif
ctx->drawingareas[i].current = 0;
ctx->drawingareas[i].width = ctx->drawingareas[i].height = 0;
/* It would be nice to choose this size in some more
* context-sensitive way, like measuring the size of some
* piece of template text. */
gtk_widget_set_size_request(ctx->drawingareas[i].area, 32, 32);
- gtk_container_add(GTK_CONTAINER(gtk_dialog_get_action_area
- (GTK_DIALOG(ctx->dialog))),
- ctx->drawingareas[i].area);
- gtk_signal_connect(GTK_OBJECT(ctx->drawingareas[i].area),
- "configure_event",
- GTK_SIGNAL_FUNC(configure_area),
- &ctx->drawingareas[i]);
- gtk_signal_connect(GTK_OBJECT(ctx->drawingareas[i].area),
- "expose_event",
- GTK_SIGNAL_FUNC(expose_area),
- &ctx->drawingareas[i]);
+ gtk_box_pack_end(action_area, ctx->drawingareas[i].area,
+ TRUE, TRUE, 5);
+ g_signal_connect(G_OBJECT(ctx->drawingareas[i].area),
+ "configure_event",
+ G_CALLBACK(configure_area),
+ &ctx->drawingareas[i]);
+#if GTK_CHECK_VERSION(3,0,0)
+ g_signal_connect(G_OBJECT(ctx->drawingareas[i].area),
+ "draw",
+ G_CALLBACK(draw_area),
+ &ctx->drawingareas[i]);
+#else
+ g_signal_connect(G_OBJECT(ctx->drawingareas[i].area),
+ "expose_event",
+ G_CALLBACK(expose_area),
+ &ctx->drawingareas[i]);
+#endif
gtk_widget_show(ctx->drawingareas[i].area);
}
ctx->active_area = rand() % N_DRAWING_AREAS;
* ensure key events go to it.
*/
gtk_widget_set_sensitive(ctx->promptlabel, TRUE);
+
+#if GTK_CHECK_VERSION(2,0,0)
gtk_window_set_keep_above(GTK_WINDOW(ctx->dialog), TRUE);
+#endif
/*
* Actually show the window, and wait for it to be shown.
/*
* And now that we've got the keyboard grab, connect up our
- * keyboard handlers, and display the prompt.
+ * keyboard handlers.
*/
+#if GTK_CHECK_VERSION(2,0,0)
g_signal_connect(G_OBJECT(ctx->imc), "commit",
G_CALLBACK(input_method_commit_event), ctx);
- gtk_signal_connect(GTK_OBJECT(ctx->promptlabel), "key_press_event",
- GTK_SIGNAL_FUNC(key_event), ctx);
- gtk_signal_connect(GTK_OBJECT(ctx->promptlabel), "key_release_event",
- GTK_SIGNAL_FUNC(key_event), ctx);
- gtk_im_context_set_client_window(ctx->imc, ctx->dialog->window);
- gtk_widget_show(ctx->promptlabel);
+#endif
+ g_signal_connect(G_OBJECT(ctx->promptlabel), "key_press_event",
+ G_CALLBACK(key_event), ctx);
+ g_signal_connect(G_OBJECT(ctx->promptlabel), "key_release_event",
+ G_CALLBACK(key_event), ctx);
+#if GTK_CHECK_VERSION(2,0,0)
+ gtk_im_context_set_client_window(ctx->imc,
+ gtk_widget_get_window(ctx->dialog));
+#endif
return NULL;
}
static void gtk_askpass_cleanup(struct askpass_ctx *ctx)
{
+#if GTK_CHECK_VERSION(3,0,0)
+ gdk_device_ungrab(ctx->keyboard, GDK_CURRENT_TIME);
+#else
gdk_keyboard_ungrab(GDK_CURRENT_TIME);
+#endif
gtk_grab_remove(ctx->promptlabel);
if (ctx->passphrase) {
ret = dupprintf("usage: %s <prompt text>", argv[0]);
} else {
srand(time(NULL));
- ret = gtk_askpass_main(argv[1], &success);
+ ret = gtk_askpass_main(NULL, "Enter passphrase", argv[1], &success);
}
if (!success) {