mirror of
https://github.com/Llewellynvdm/Tomb.git
synced 2024-11-22 12:35:13 +00:00
our own askpass gui
This commit is contained in:
parent
36565e2ef4
commit
650ce60587
332
src/tomb-askpass.c
Normal file
332
src/tomb-askpass.c
Normal file
@ -0,0 +1,332 @@
|
||||
/* Tomb askpass
|
||||
|
||||
Derived from gtk-led-askpass.c version 0.9
|
||||
by Dafydd Harries <daf@muse.19inch.net>, 2003 2004
|
||||
(An ssh-askpass alike software)
|
||||
|
||||
Based on ideas from ssh-askpass-gnome, by Damien Miller and Nalin Dahyabhai,
|
||||
and on Jim Knoble's x11-ssh-askpass.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
See also:
|
||||
|
||||
http://www.cgabriel.org/sw/gtk2-ssh-askpass/
|
||||
-- Jim Knoble's x11-ssh-askpass
|
||||
http://www.cgabriel.org/sw/gtk2-ssh-askpass/
|
||||
-- Christopher Gabriel's gtk2-ssh-askpass
|
||||
|
||||
Todo:
|
||||
|
||||
- Internationalise. Probably entails autotoolising.
|
||||
- Add more eye candy.
|
||||
- Implement optional mouse/server grabbing.
|
||||
- Alow overriding the title on the command line.
|
||||
- Make the LED box a proper GTK+ widget.
|
||||
|
||||
*/
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <gdk/gdkkeysyms.h>
|
||||
|
||||
/* The Tomb icon is an artwork by Jordi aka MonMort
|
||||
a nomadic graffiti artist from Barcelona */
|
||||
#include <monmort.xpm>
|
||||
|
||||
/* Title for the dialog. */
|
||||
#define TITLE "Unlocking tomb"
|
||||
|
||||
/* Width of each LED. */
|
||||
#define LED_WIDTH 8
|
||||
/* Height of each LED. */
|
||||
#define LED_HEIGHT 16
|
||||
/* Space around and between LEDs. */
|
||||
#define LED_MARGIN 5
|
||||
/* Number of LEDs to have. */
|
||||
#define LED_COUNT 12
|
||||
|
||||
/* How many times to attempt to grab the keyboard before giving up. */
|
||||
#define GRAB_TRIES_MAX 10
|
||||
/* How long to sleep, in microseconds, in between keyboard grab attempts. */
|
||||
#define GRAB_SLEEP 100000
|
||||
/* Sleep length, in milliseconds, after Control-U press. */
|
||||
#define CLEAR_SLEEP 800
|
||||
|
||||
enum {
|
||||
LED_STATE_OFF,
|
||||
LED_STATE_GREEN,
|
||||
LED_STATE_RED,
|
||||
LED_STATES
|
||||
};
|
||||
|
||||
GdkPixbuf *pb_monmort;
|
||||
|
||||
GdkColor colours[LED_STATES] = {
|
||||
/* LED_STATE_OFF */
|
||||
{ 0, 0x3333, 0x6666, 0x3333 },
|
||||
/* LED_STATE_GREEN */
|
||||
{ 0, 0x6666, 0xFFFF, 0x6666 },
|
||||
/* LED_STATE_RED */
|
||||
{ 0, 0xDDDD, 0x3333, 0x3333 }
|
||||
};
|
||||
|
||||
|
||||
void draw_led(GtkWidget *widget, gint state, guint offset)
|
||||
{
|
||||
GdkGC *gc = gdk_gc_new(widget->window);
|
||||
|
||||
/* Draw the border. */
|
||||
gdk_draw_rectangle(widget->window,
|
||||
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
|
||||
FALSE,
|
||||
LED_MARGIN + offset * (LED_WIDTH + LED_MARGIN),
|
||||
LED_MARGIN,
|
||||
LED_WIDTH,
|
||||
LED_HEIGHT);
|
||||
|
||||
gdk_gc_set_rgb_fg_color(gc, &(colours[state]));
|
||||
|
||||
/* Draw the inside rectangle. */
|
||||
gdk_draw_rectangle(widget->window,
|
||||
gc,
|
||||
TRUE,
|
||||
LED_MARGIN + offset * (LED_WIDTH + LED_MARGIN) + 1,
|
||||
LED_MARGIN + 1,
|
||||
LED_WIDTH - 1,
|
||||
LED_HEIGHT - 1);
|
||||
}
|
||||
|
||||
gboolean led_area_expose_handler(GtkWidget *led_area, GdkEventExpose *event,
|
||||
GString *passphrase)
|
||||
{
|
||||
gint i, width, height;
|
||||
gint length = passphrase->len;
|
||||
|
||||
gdk_drawable_get_size(GDK_DRAWABLE(led_area->window), &width, &height);
|
||||
|
||||
/* Draw a focus indicator if appropriate. */
|
||||
if (GTK_WIDGET_HAS_FOCUS(led_area))
|
||||
gtk_paint_focus(led_area->style, led_area->window,
|
||||
GTK_WIDGET_STATE(led_area), &(event->area), led_area,
|
||||
"", 0, 0, width, height);
|
||||
|
||||
/* Draw each LED. */
|
||||
for (i = 0; i < LED_COUNT; i++) {
|
||||
/* This is the complicated bit. */
|
||||
gboolean on = ((length / LED_COUNT) % 2 == 0) ?
|
||||
(length % LED_COUNT > i) :
|
||||
(length % LED_COUNT <= i);
|
||||
|
||||
draw_led(led_area, on ? LED_STATE_GREEN : LED_STATE_OFF, i);
|
||||
}
|
||||
|
||||
/* TRUE means not to propagate the event. */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean timeout_handler(GtkWidget *led_area)
|
||||
{
|
||||
gtk_widget_queue_draw(led_area);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void clear(GString *passphrase, GtkWidget *led_area)
|
||||
{
|
||||
gint i;
|
||||
|
||||
/*
|
||||
Delete bit by bit to ensure erasure. g_string_erase() will overwrite
|
||||
the last character with 0, so we shouldn't need to worry about leaving
|
||||
sensitive data in memory. Note that the string may be empty. This is
|
||||
so that the interface responds consistently.
|
||||
*/
|
||||
while (passphrase->len > 0)
|
||||
g_string_erase(passphrase, passphrase->len - 1, 1);
|
||||
|
||||
for (i = 0; i < LED_COUNT; i++)
|
||||
draw_led(led_area, LED_STATE_RED, i);
|
||||
|
||||
/*
|
||||
Remove the redness after a delay. If an exposure is triggered, such as
|
||||
by a key getting pressed, then the redraw will simply happen early.
|
||||
*/
|
||||
gtk_timeout_add(CLEAR_SLEEP, (GtkFunction)timeout_handler, led_area);
|
||||
}
|
||||
|
||||
gboolean led_area_key_press_handler(GtkWidget *led_area, GdkEventKey *event,
|
||||
GString *passphrase) {
|
||||
/* Obtain Unicode representation of key released. */
|
||||
gunichar c = gdk_keyval_to_unicode(event->keyval);
|
||||
/* Determine whether the key released was printable. */
|
||||
gint isprint = g_unichar_isprint(c);
|
||||
/* Obtain default modifier mask. */
|
||||
guint modifiers = gtk_accelerator_get_default_mod_mask();
|
||||
|
||||
if (event->keyval == GDK_Delete) {
|
||||
clear(passphrase, led_area);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if ((event->state & modifiers) == GDK_CONTROL_MASK) {
|
||||
|
||||
if (event->keyval == GDK_u) {
|
||||
/* C-u -- delete everything. */
|
||||
clear(passphrase, led_area);
|
||||
/* Return early in order to avoid the redraw. */
|
||||
return TRUE;
|
||||
|
||||
} else {
|
||||
|
||||
/* Unrecognised keypress. */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
} else if (event->keyval == GDK_BackSpace && passphrase->len > 0) {
|
||||
/*
|
||||
Backspace -- remove last character. See the comment above
|
||||
about g_string_erase.
|
||||
*/
|
||||
g_string_erase(passphrase, passphrase->len - 1, 1);
|
||||
} else if (isprint) {
|
||||
/* Printable character. */
|
||||
g_string_append_unichar(passphrase, c);
|
||||
} else {
|
||||
/* Unrecognized keypress, propagate. */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Trigger a redraw of the LED area. */
|
||||
gtk_widget_queue_draw(led_area);
|
||||
|
||||
/* TRUE means not to propagate the event. */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean led_area_button_press_handler(GtkWidget *led_area,
|
||||
GdkEventButton *event, gpointer data)
|
||||
{
|
||||
gtk_widget_grab_focus(led_area);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
gint response, grab_tries, i;
|
||||
char keyname[256];
|
||||
GString *passphrase = g_string_new("");
|
||||
GtkWidget *dialog, *alignment, *led_area;
|
||||
GList tmplist;
|
||||
|
||||
gtk_set_locale();
|
||||
gtk_init(&argc, &argv);
|
||||
|
||||
if (argc > 1) {
|
||||
snprintf(keyname,255,"%s",argv[1]);
|
||||
} else {
|
||||
sprintf(keyname,"unknown");
|
||||
}
|
||||
/*
|
||||
dialog
|
||||
`- vbox (implicit)
|
||||
`- aligment
|
||||
`- led_area
|
||||
*/
|
||||
|
||||
/* Question dialog with no parent; OK and Cancel buttons. */
|
||||
dialog = gtk_message_dialog_new_with_markup
|
||||
(NULL, 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK,
|
||||
"Enter the password to unlock\n"
|
||||
"<span font=\"Times 24\">%s</span>", keyname);
|
||||
gtk_window_set_title(GTK_WINDOW(dialog), TITLE);
|
||||
|
||||
// set and show the image icon
|
||||
pb_monmort = gdk_pixbuf_new_from_xpm_data(monmort);
|
||||
tmplist.data = (gpointer*)pb_monmort;
|
||||
tmplist.prev = tmplist.next = NULL;
|
||||
gtk_window_set_icon_list(GTK_WINDOW(dialog), &tmplist);
|
||||
gtk_message_dialog_set_image(GTK_MESSAGE_DIALOG(dialog),
|
||||
gtk_image_new_from_pixbuf(pb_monmort));
|
||||
|
||||
/* Place the dialog in the middle of the screen. */
|
||||
gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
|
||||
/* OK is the default action. */
|
||||
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
|
||||
|
||||
/* Add some spacing within the dialog's vbox. */
|
||||
gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), 3);
|
||||
|
||||
/* The alignment widget containing the drawing area. */
|
||||
alignment = gtk_alignment_new(0.5, 0.5, 0, 0);
|
||||
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), alignment);
|
||||
|
||||
/* The drawing area for the LEDs. */
|
||||
led_area = gtk_drawing_area_new();
|
||||
gtk_container_add(GTK_CONTAINER(alignment), led_area);
|
||||
/* Make the LED widget focusable. */
|
||||
GTK_WIDGET_SET_FLAGS(led_area, GTK_CAN_FOCUS);
|
||||
/* Make the LED widget focused. */
|
||||
gtk_widget_grab_focus(led_area);
|
||||
/* Make the LED widget receive key press and button press events. */
|
||||
gtk_widget_add_events(led_area,
|
||||
GDK_KEY_PRESS_MASK | GDK_BUTTON_PRESS_MASK);
|
||||
/* Set a size request. */
|
||||
gtk_widget_set_size_request(led_area,
|
||||
LED_MARGIN + (LED_WIDTH + LED_MARGIN) * LED_COUNT,
|
||||
LED_HEIGHT + LED_MARGIN * 2);
|
||||
/* Set up handler for key releases. */
|
||||
g_signal_connect(G_OBJECT(led_area), "key_press_event",
|
||||
G_CALLBACK(led_area_key_press_handler), passphrase);
|
||||
/* Set up handler for button releases. */
|
||||
g_signal_connect(G_OBJECT(led_area), "button_press_event",
|
||||
G_CALLBACK(led_area_button_press_handler), NULL);
|
||||
/* Set up handler for expose events. */
|
||||
g_signal_connect(G_OBJECT(led_area), "expose_event",
|
||||
G_CALLBACK(led_area_expose_handler), passphrase);
|
||||
|
||||
|
||||
/* Show all the widgets. */
|
||||
gtk_widget_show_all(dialog);
|
||||
/* Put the dialog on the screen now for the grab to work. */
|
||||
gtk_widget_show_now(dialog);
|
||||
|
||||
/* Grab the keyboard */
|
||||
gdk_keyboard_grab(GTK_WIDGET(dialog)->window, FALSE, GDK_CURRENT_TIME);
|
||||
|
||||
/* Make the dialog stay on top. */
|
||||
gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);
|
||||
|
||||
/* Run the dialog. */
|
||||
response = gtk_dialog_run(GTK_DIALOG(dialog));
|
||||
|
||||
/* Ungrab the keyboard. */
|
||||
gdk_keyboard_ungrab(GDK_CURRENT_TIME);
|
||||
|
||||
/* If the OK button was pressed, print the passphrase. */
|
||||
if (response == GTK_RESPONSE_OK)
|
||||
g_print("%s\n", passphrase->str);
|
||||
|
||||
/* Scrub the passphrase, if any. */
|
||||
for (i = 0; i < passphrase->len; i++)
|
||||
passphrase->str[i] = '\0';
|
||||
|
||||
if (response == GTK_RESPONSE_OK)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user