/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2004-2006 William Jon McCann <mccann@jhu.edu> * * 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 2 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * * Authors: William Jon McCann <mccann@jhu.edu> * */ #include "config.h" #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <time.h> #include <unistd.h> #include <sys/utsname.h> #include <glib/gprintf.h> #include <glib/gstdio.h> #include <glib/gi18n.h> #include <gdk/gdkkeysyms.h> #include <gdk/gdkx.h> #include <gtk/gtk.h> #include <gio/gio.h> #if GTK_CHECK_VERSION (3, 0, 0) #include <gdk/gdkkeysyms-compat.h> #include <gtk/gtkx.h> #define MATE_DESKTOP_USE_UNSTABLE_API #include <libmate-desktop/mate-desktop-utils.h> #define gdk_spawn_command_line_on_screen mate_gdk_spawn_command_line_on_screen #define GTK_WIDGET_VISIBLE gtk_widget_get_visible #define GTK_WIDGET_IS_SENSITIVE gtk_widget_is_sensitive #define GTK_WIDGET_HAS_FOCUS gtk_widget_has_focus #endif #ifdef WITH_KBD_LAYOUT_INDICATOR #include <libmatekbd/matekbd-indicator.h> #endif #ifdef WITH_LIBNOTIFY #include <libnotify/notify.h> #endif #include "gs-lock-plug.h" #include "gs-debug.h" #define GSETTINGS_SCHEMA "org.mate.screensaver" #define KEY_LOCK_DIALOG_THEME "lock-dialog-theme" #define MDM_FLEXISERVER_COMMAND "mdmflexiserver" #define MDM_FLEXISERVER_ARGS "--startnew Standard" #define GDM_FLEXISERVER_COMMAND "gdmflexiserver" #define GDM_FLEXISERVER_ARGS "--startnew Standard" /* same as SMS ;) */ #define NOTE_BUFFER_MAX_CHARS 160 enum { AUTH_PAGE = 0, }; #define FACE_ICON_SIZE 48 #define DIALOG_TIMEOUT_MSEC 60000 static void gs_lock_plug_finalize (GObject *object); #define GS_LOCK_PLUG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_LOCK_PLUG, GSLockPlugPrivate)) struct GSLockPlugPrivate { GtkWidget *vbox; GtkWidget *auth_action_area; GtkWidget *notebook; GtkWidget *auth_face_image; GtkWidget *auth_time_label; GtkWidget *auth_date_label; GtkWidget *auth_realname_label; GtkWidget *auth_username_label; GtkWidget *auth_prompt_label; GtkWidget *auth_prompt_entry; GtkWidget *auth_prompt_box; GtkWidget *auth_capslock_label; GtkWidget *auth_message_label; GtkWidget *status_message_label; GtkWidget *auth_unlock_button; GtkWidget *auth_switch_button; GtkWidget *auth_cancel_button; GtkWidget *auth_logout_button; GtkWidget *auth_note_button; GtkWidget *note_tab; GtkWidget *note_tab_label; GtkWidget *note_text_view; GtkWidget *note_ok_button; GtkWidget *note_cancel_button; GtkWidget *auth_prompt_kbd_layout_indicator; gboolean caps_lock_on; gboolean switch_enabled; gboolean leave_note_enabled; gboolean logout_enabled; char *logout_command; char *status_message; guint timeout; guint datetime_timeout_id; guint cancel_timeout_id; guint auth_check_idle_id; guint response_idle_id; GList *key_events; }; typedef struct _ResponseData ResponseData; struct _ResponseData { gint response_id; }; enum { RESPONSE, CLOSE, LAST_SIGNAL }; enum { PROP_0, PROP_LOGOUT_ENABLED, PROP_LOGOUT_COMMAND, PROP_SWITCH_ENABLED, PROP_STATUS_MESSAGE }; static guint lock_plug_signals [LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE (GSLockPlug, gs_lock_plug, GTK_TYPE_PLUG) static void gs_lock_plug_style_set (GtkWidget *widget, GtkStyle *previous_style) { GSLockPlug *plug; if (GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->style_set) { GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->style_set (widget, previous_style); } plug = GS_LOCK_PLUG (widget); if (! plug->priv->vbox) { return; } gtk_container_set_border_width (GTK_CONTAINER (plug->priv->vbox), 12); gtk_box_set_spacing (GTK_BOX (plug->priv->vbox), 12); gtk_container_set_border_width (GTK_CONTAINER (plug->priv->auth_action_area), 0); gtk_box_set_spacing (GTK_BOX (plug->priv->auth_action_area), 5); } static gboolean is_program_in_path (const char *program) { char *tmp = g_find_program_in_path (program); if (tmp != NULL) { g_free (tmp); return TRUE; } else { return FALSE; } } static void do_user_switch (GSLockPlug *plug) { GError *error; gboolean res; char *command; if (is_program_in_path (MDM_FLEXISERVER_COMMAND)) { /* MDM */ command = g_strdup_printf ("%s %s", MDM_FLEXISERVER_COMMAND, MDM_FLEXISERVER_ARGS); error = NULL; res = gdk_spawn_command_line_on_screen (gdk_screen_get_default (), command, &error); g_free (command); if (! res) { gs_debug ("Unable to start MDM greeter: %s", error->message); g_error_free (error); } } else if (is_program_in_path (GDM_FLEXISERVER_COMMAND)) { /* GDM */ command = g_strdup_printf ("%s %s", GDM_FLEXISERVER_COMMAND, GDM_FLEXISERVER_ARGS); error = NULL; res = gdk_spawn_command_line_on_screen (gdk_screen_get_default (), command, &error); g_free (command); if (! res) { gs_debug ("Unable to start GDM greeter: %s", error->message); g_error_free (error); } } else if (g_getenv ("XDG_SEAT_PATH") != NULL) { /* LightDM */ GDBusProxyFlags flags = G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START; GDBusProxy *proxy = NULL; error = NULL; proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, flags, NULL, "org.freedesktop.DisplayManager", g_getenv ("XDG_SEAT_PATH"), "org.freedesktop.DisplayManager.Seat", NULL, &error); if (proxy != NULL) { g_dbus_proxy_call_sync (proxy, "SwitchToGreeter", g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL); g_object_unref (proxy); } else { gs_debug ("Unable to start LightDM greeter: %s", error->message); g_error_free (error); } } } static void set_status_text (GSLockPlug *plug, const char *text) { if (plug->priv->auth_message_label != NULL) { gtk_label_set_text (GTK_LABEL (plug->priv->auth_message_label), text); } } static void date_time_update (GSLockPlug *plug) { GDateTime *datetime; gchar *time; gchar *date; gchar *str; datetime = g_date_time_new_now_local (); time = g_date_time_format (datetime, "%X"); /* Translators: Date format, see https://developer.gnome.org/glib/stable/glib-GDateTime.html#g-date-time-format */ date = g_date_time_format (datetime, _("%A, %B %e")); str = g_strdup_printf ("<span size=\"xx-large\" weight=\"ultrabold\">%s</span>", time); gtk_label_set_text (GTK_LABEL (plug->priv->auth_time_label), str); gtk_label_set_use_markup (GTK_LABEL (plug->priv->auth_time_label), TRUE); g_free (str); str = g_strdup_printf ("<span size=\"large\">%s</span>", date); gtk_label_set_markup (GTK_LABEL (plug->priv->auth_date_label), str); gtk_label_set_use_markup (GTK_LABEL (plug->priv->auth_date_label), TRUE); g_free (str); g_free (time); g_free (date); g_date_time_unref (datetime); } void gs_lock_plug_set_sensitive (GSLockPlug *plug, gboolean sensitive) { g_return_if_fail (GS_IS_LOCK_PLUG (plug)); gtk_widget_set_sensitive (plug->priv->auth_prompt_entry, sensitive); gtk_widget_set_sensitive (plug->priv->auth_action_area, sensitive); } static void remove_datetime_timeout (GSLockPlug *plug) { if (plug->priv->datetime_timeout_id > 0) { g_source_remove (plug->priv->datetime_timeout_id); plug->priv->datetime_timeout_id = 0; } } static void remove_cancel_timeout (GSLockPlug *plug) { if (plug->priv->cancel_timeout_id > 0) { g_source_remove (plug->priv->cancel_timeout_id); plug->priv->cancel_timeout_id = 0; } } static void remove_response_idle (GSLockPlug *plug) { if (plug->priv->response_idle_id > 0) { g_source_remove (plug->priv->response_idle_id); plug->priv->response_idle_id = 0; } } static void gs_lock_plug_response (GSLockPlug *plug, gint response_id) { int new_response; new_response = response_id; g_return_if_fail (GS_IS_LOCK_PLUG (plug)); /* Act only on response IDs we recognize */ if (!(response_id == GS_LOCK_PLUG_RESPONSE_OK || response_id == GS_LOCK_PLUG_RESPONSE_CANCEL)) { return; } remove_cancel_timeout (plug); remove_response_idle (plug); if (response_id == GS_LOCK_PLUG_RESPONSE_CANCEL) { gtk_entry_set_text (GTK_ENTRY (plug->priv->auth_prompt_entry), ""); } g_signal_emit (plug, lock_plug_signals [RESPONSE], 0, new_response); } static gboolean response_cancel_idle_cb (GSLockPlug *plug) { plug->priv->response_idle_id = 0; gs_lock_plug_response (plug, GS_LOCK_PLUG_RESPONSE_CANCEL); return FALSE; } static gboolean dialog_timed_out (GSLockPlug *plug) { gs_lock_plug_set_sensitive (plug, FALSE); set_status_text (plug, _("Time has expired.")); if (plug->priv->response_idle_id != 0) { g_warning ("Response idle ID already set but shouldn't be"); } remove_response_idle (plug); plug->priv->response_idle_id = g_timeout_add (2000, (GSourceFunc)response_cancel_idle_cb, plug); return FALSE; } static void capslock_update (GSLockPlug *plug, gboolean is_on) { plug->priv->caps_lock_on = is_on; if (plug->priv->auth_capslock_label == NULL) { return; } if (is_on) { gtk_label_set_text (GTK_LABEL (plug->priv->auth_capslock_label), _("You have the Caps Lock key on.")); } else { gtk_label_set_text (GTK_LABEL (plug->priv->auth_capslock_label), ""); } } static gboolean is_capslock_on (void) { GdkKeymap *keymap; gboolean res; res = FALSE; keymap = gdk_keymap_get_default (); if (keymap != NULL) { res = gdk_keymap_get_caps_lock_state (keymap); } return res; } static void restart_cancel_timeout (GSLockPlug *plug) { remove_cancel_timeout (plug); plug->priv->cancel_timeout_id = g_timeout_add (plug->priv->timeout, (GSourceFunc)dialog_timed_out, plug); } void gs_lock_plug_get_text (GSLockPlug *plug, char **text) { const char *typed_text; char *null_text; char *local_text; typed_text = gtk_entry_get_text (GTK_ENTRY (plug->priv->auth_prompt_entry)); local_text = g_locale_from_utf8 (typed_text, strlen (typed_text), NULL, NULL, NULL); null_text = g_strnfill (strlen (typed_text) + 1, '\b'); gtk_entry_set_text (GTK_ENTRY (plug->priv->auth_prompt_entry), null_text); gtk_entry_set_text (GTK_ENTRY (plug->priv->auth_prompt_entry), ""); g_free (null_text); if (text != NULL) { *text = local_text; } else { g_free (local_text); } } typedef struct { GSLockPlug *plug; gint response_id; GMainLoop *loop; gboolean destroyed; } RunInfo; static void shutdown_loop (RunInfo *ri) { if (g_main_loop_is_running (ri->loop)) g_main_loop_quit (ri->loop); } static void run_unmap_handler (GSLockPlug *plug, gpointer data) { RunInfo *ri = data; shutdown_loop (ri); } static void run_response_handler (GSLockPlug *plug, gint response_id, gpointer data) { RunInfo *ri; ri = data; ri->response_id = response_id; shutdown_loop (ri); } static gint run_delete_handler (GSLockPlug *plug, GdkEventAny *event, gpointer data) { RunInfo *ri = data; shutdown_loop (ri); return TRUE; /* Do not destroy */ } static void run_destroy_handler (GSLockPlug *plug, gpointer data) { RunInfo *ri = data; /* shutdown_loop will be called by run_unmap_handler */ ri->destroyed = TRUE; } static void run_keymap_handler (GdkKeymap *keymap, GSLockPlug *plug) { capslock_update (plug, is_capslock_on ()); } /* adapted from GTK+ gtkdialog.c */ int gs_lock_plug_run (GSLockPlug *plug) { RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL, FALSE }; gboolean was_modal; gulong response_handler; gulong unmap_handler; gulong destroy_handler; gulong delete_handler; gulong keymap_handler; GdkKeymap *keymap; g_return_val_if_fail (GS_IS_LOCK_PLUG (plug), -1); g_object_ref (plug); #if GTK_CHECK_VERSION (3, 0, 0) was_modal = gtk_window_get_modal (GTK_WINDOW (plug)); #else was_modal = GTK_WINDOW (plug)->modal; #endif if (!was_modal) { gtk_window_set_modal (GTK_WINDOW (plug), TRUE); } if (!GTK_WIDGET_VISIBLE (plug)) { gtk_widget_show (GTK_WIDGET (plug)); } keymap = gdk_keymap_get_for_display (gtk_widget_get_display (GTK_WIDGET (plug))); keymap_handler = g_signal_connect (keymap, "state-changed", G_CALLBACK (run_keymap_handler), plug); response_handler = g_signal_connect (plug, "response", G_CALLBACK (run_response_handler), &ri); unmap_handler = g_signal_connect (plug, "unmap", G_CALLBACK (run_unmap_handler), &ri); delete_handler = g_signal_connect (plug, "delete_event", G_CALLBACK (run_delete_handler), &ri); destroy_handler = g_signal_connect (plug, "destroy", G_CALLBACK (run_destroy_handler), &ri); ri.loop = g_main_loop_new (NULL, FALSE); GDK_THREADS_LEAVE (); g_main_loop_run (ri.loop); GDK_THREADS_ENTER (); g_main_loop_unref (ri.loop); ri.loop = NULL; if (!ri.destroyed) { if (! was_modal) { gtk_window_set_modal (GTK_WINDOW (plug), FALSE); } g_signal_handler_disconnect (plug, response_handler); g_signal_handler_disconnect (plug, unmap_handler); g_signal_handler_disconnect (plug, delete_handler); g_signal_handler_disconnect (plug, destroy_handler); g_signal_handler_disconnect (plug, keymap_handler); } g_object_unref (plug); return ri.response_id; } static cairo_surface_t * surface_from_pixbuf (GdkPixbuf *pixbuf) { cairo_surface_t *surface; cairo_t *cr; surface = cairo_image_surface_create (gdk_pixbuf_get_has_alpha (pixbuf) ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24, gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf)); cr = cairo_create (surface); gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0); cairo_paint (cr); cairo_destroy (cr); return surface; } static void rounded_rectangle (cairo_t *cr, gdouble aspect, gdouble x, gdouble y, gdouble corner_radius, gdouble width, gdouble height) { gdouble radius; gdouble degrees; radius = corner_radius / aspect; degrees = G_PI / 180.0; cairo_new_sub_path (cr); cairo_arc (cr, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees); cairo_arc (cr, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees); cairo_arc (cr, x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees); cairo_arc (cr, x + radius, y + radius, radius, 180 * degrees, 270 * degrees); cairo_close_path (cr); } #if GTK_CHECK_VERSION (3, 0, 0) /* copied from gnome-screensaver 3.x */ /** * go_cairo_convert_data_to_pixbuf: * @src: a pointer to pixel data in cairo format * @dst: a pointer to pixel data in pixbuf format * @width: image width * @height: image height * @rowstride: data rowstride * * Converts the pixel data stored in @src in CAIRO_FORMAT_ARGB32 cairo format * to GDK_COLORSPACE_RGB pixbuf format and move them * to @dst. If @src == @dst, pixel are converted in place. **/ static void go_cairo_convert_data_to_pixbuf (unsigned char *dst, unsigned char const *src, int width, int height, int rowstride) { int i,j; unsigned int t; unsigned char a, b, c; g_return_if_fail (dst != NULL); #define MULT(d,c,a,t) G_STMT_START { t = (a)? c * 255 / a: 0; d = t;} G_STMT_END if (src == dst || src == NULL) { for (i = 0; i < height; i++) { for (j = 0; j < width; j++) { #if G_BYTE_ORDER == G_LITTLE_ENDIAN MULT(a, dst[2], dst[3], t); MULT(b, dst[1], dst[3], t); MULT(c, dst[0], dst[3], t); dst[0] = a; dst[1] = b; dst[2] = c; #else MULT(a, dst[1], dst[0], t); MULT(b, dst[2], dst[0], t); MULT(c, dst[3], dst[0], t); dst[3] = dst[0]; dst[0] = a; dst[1] = b; dst[2] = c; #endif dst += 4; } dst += rowstride - width * 4; } } else { for (i = 0; i < height; i++) { for (j = 0; j < width; j++) { #if G_BYTE_ORDER == G_LITTLE_ENDIAN MULT(dst[0], src[2], src[3], t); MULT(dst[1], src[1], src[3], t); MULT(dst[2], src[0], src[3], t); dst[3] = src[3]; #else MULT(dst[0], src[1], src[0], t); MULT(dst[1], src[2], src[0], t); MULT(dst[2], src[3], src[0], t); dst[3] = src[0]; #endif src += 4; dst += 4; } src += rowstride - width * 4; dst += rowstride - width * 4; } } #undef MULT } static void cairo_to_pixbuf (guint8 *src_data, GdkPixbuf *dst_pixbuf) { unsigned char *src; unsigned char *dst; guint w; guint h; guint rowstride; w = gdk_pixbuf_get_width (dst_pixbuf); h = gdk_pixbuf_get_height (dst_pixbuf); rowstride = gdk_pixbuf_get_rowstride (dst_pixbuf); dst = gdk_pixbuf_get_pixels (dst_pixbuf); src = src_data; go_cairo_convert_data_to_pixbuf (dst, src, w, h, rowstride); } static GdkPixbuf * frame_pixbuf (GdkPixbuf *source) { GdkPixbuf *dest; cairo_t *cr; cairo_surface_t *surface; guint w; guint h; guint rowstride; int frame_width; double radius; guint8 *data; frame_width = 5; w = gdk_pixbuf_get_width (source) + frame_width * 2; h = gdk_pixbuf_get_height (source) + frame_width * 2; radius = w / 10; dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, w, h); rowstride = gdk_pixbuf_get_rowstride (dest); data = g_new0 (guint8, h * rowstride); surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, w, h, rowstride); cr = cairo_create (surface); cairo_surface_destroy (surface); /* set up image */ cairo_rectangle (cr, 0, 0, w, h); cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0); cairo_fill (cr); rounded_rectangle (cr, 1.0, frame_width + 0.5, frame_width + 0.5, radius, w - frame_width * 2 - 1, h - frame_width * 2 - 1); cairo_set_source_rgba (cr, 0.5, 0.5, 0.5, 0.3); cairo_fill_preserve (cr); surface = surface_from_pixbuf (source); cairo_set_source_surface (cr, surface, frame_width, frame_width); cairo_fill (cr); cairo_surface_destroy (surface); cairo_to_pixbuf (data, dest); cairo_destroy (cr); g_free (data); return dest; } /* end copied from gdm-user.c */ static void image_set_from_pixbuf (GtkImage *image, GdkPixbuf *source) { GdkPixbuf *pixbuf; pixbuf = frame_pixbuf (source); gtk_image_set_from_pixbuf (image, pixbuf); g_object_unref (pixbuf); } #else static void image_set_from_pixbuf (GtkImage *image, GdkPixbuf *source) { cairo_t *cr; cairo_t *cr_mask; cairo_surface_t *surface; GdkPixmap *pixmap; GdkPixmap *bitmask; int w; int h; int frame_width; double radius; GdkColor color; double r; double g; double b; frame_width = 5; w = gdk_pixbuf_get_width (source) + frame_width * 2; h = gdk_pixbuf_get_height (source) + frame_width * 2; radius = w / 10; pixmap = gdk_pixmap_new (GTK_WIDGET (image)->window, w, h, -1); bitmask = gdk_pixmap_new (GTK_WIDGET (image)->window, w, h, 1); cr = gdk_cairo_create (pixmap); cr_mask = gdk_cairo_create (bitmask); /* setup mask */ cairo_rectangle (cr_mask, 0, 0, w, h); cairo_set_operator (cr_mask, CAIRO_OPERATOR_CLEAR); cairo_fill (cr_mask); rounded_rectangle (cr_mask, 1.0, 0.5, 0.5, radius, w - 1, h - 1); cairo_set_operator (cr_mask, CAIRO_OPERATOR_OVER); cairo_set_source_rgb (cr_mask, 1, 1, 1); cairo_fill (cr_mask); color = GTK_WIDGET (image)->style->bg [GTK_STATE_NORMAL]; r = (float)color.red / 65535.0; g = (float)color.green / 65535.0; b = (float)color.blue / 65535.0; /* set up image */ cairo_rectangle (cr, 0, 0, w, h); cairo_set_source_rgb (cr, r, g, b); cairo_fill (cr); rounded_rectangle (cr, 1.0, frame_width + 0.5, frame_width + 0.5, radius, w - frame_width * 2 - 1, h - frame_width * 2 - 1); cairo_set_source_rgba (cr, 0.5, 0.5, 0.5, 0.3); cairo_fill_preserve (cr); surface = surface_from_pixbuf (source); cairo_set_source_surface (cr, surface, frame_width, frame_width); cairo_fill (cr); gtk_image_set_from_pixmap (image, pixmap, bitmask); cairo_surface_destroy (surface); g_object_unref (bitmask); g_object_unref (pixmap); cairo_destroy (cr_mask); cairo_destroy (cr); } #endif static gboolean check_user_file (const gchar *filename, uid_t user, gssize max_file_size, gboolean relax_group, gboolean relax_other) { struct stat fileinfo; if (max_file_size < 0) { max_file_size = G_MAXSIZE; } /* Exists/Readable? */ if (g_stat (filename, &fileinfo) < 0) { return FALSE; } /* Is a regular file */ if (G_UNLIKELY (!S_ISREG (fileinfo.st_mode))) { return FALSE; } /* Owned by user? */ if (G_UNLIKELY (fileinfo.st_uid != user)) { return FALSE; } /* Group not writable or relax_group? */ if (G_UNLIKELY ((fileinfo.st_mode & S_IWGRP) == S_IWGRP && !relax_group)) { return FALSE; } /* Other not writable or relax_other? */ if (G_UNLIKELY ((fileinfo.st_mode & S_IWOTH) == S_IWOTH && !relax_other)) { return FALSE; } /* Size is kosher? */ if (G_UNLIKELY (fileinfo.st_size > max_file_size)) { return FALSE; } return TRUE; } static gboolean set_face_image (GSLockPlug *plug) { GdkPixbuf *pixbuf; const char *homedir; char *path; int icon_size = 96; gsize user_max_file = 65536; uid_t uid; homedir = g_get_home_dir (); uid = getuid (); path = g_build_filename (homedir, ".face", NULL); pixbuf = NULL; if (check_user_file (path, uid, user_max_file, 0, 0)) { pixbuf = gdk_pixbuf_new_from_file_at_size (path, icon_size, icon_size, NULL); } g_free (path); if (pixbuf == NULL) { return FALSE; } image_set_from_pixbuf (GTK_IMAGE (plug->priv->auth_face_image), pixbuf); g_object_unref (pixbuf); return TRUE; } static void gs_lock_plug_show (GtkWidget *widget) { GSLockPlug *plug = GS_LOCK_PLUG (widget); gs_profile_start (NULL); gs_profile_start ("parent"); if (GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->show) { GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->show (widget); } gs_profile_end ("parent"); if (plug->priv->auth_face_image) { set_face_image (plug); } capslock_update (plug, is_capslock_on ()); restart_cancel_timeout (plug); gs_profile_end (NULL); } static void gs_lock_plug_hide (GtkWidget *widget) { if (GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->hide) { GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->hide (widget); } } static void queue_key_event (GSLockPlug *plug, GdkEventKey *event) { GdkEvent *saved_event; saved_event = gdk_event_copy ((GdkEvent *)event); plug->priv->key_events = g_list_prepend (plug->priv->key_events, saved_event); } static void forward_key_events (GSLockPlug *plug) { plug->priv->key_events = g_list_reverse (plug->priv->key_events); while (plug->priv->key_events != NULL) { GdkEventKey *event = plug->priv->key_events->data; gtk_window_propagate_key_event (GTK_WINDOW (plug), event); gdk_event_free ((GdkEvent *)event); plug->priv->key_events = g_list_delete_link (plug->priv->key_events, plug->priv->key_events); } } #if !GTK_CHECK_VERSION (3, 0, 0) static void gs_lock_plug_size_request (GtkWidget *widget, GtkRequisition *requisition) { GSLockPlug *plug = GS_LOCK_PLUG (widget); int mod_width; int mod_height; if (GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->size_request) { GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->size_request (widget, requisition); } /* don't constrain size when themed */ if (plug->priv->vbox == NULL) { return; } mod_width = requisition->height * 1.3; mod_height = requisition->width / 1.6; if (requisition->width < mod_width) { /* if the dialog is tall fill out the width */ requisition->width = mod_width; } else if (requisition->height < mod_height) { /* if the dialog is wide fill out the height */ requisition->height = mod_height; } } #endif static void gs_lock_plug_set_logout_enabled (GSLockPlug *plug, gboolean logout_enabled) { g_return_if_fail (GS_LOCK_PLUG (plug)); if (plug->priv->logout_enabled == logout_enabled) { return; } plug->priv->logout_enabled = logout_enabled; g_object_notify (G_OBJECT (plug), "logout-enabled"); if (plug->priv->auth_logout_button == NULL) { return; } if (logout_enabled) { gtk_widget_show (plug->priv->auth_logout_button); } else { gtk_widget_hide (plug->priv->auth_logout_button); } } static void gs_lock_plug_set_logout_command (GSLockPlug *plug, const char *command) { g_return_if_fail (GS_LOCK_PLUG (plug)); g_free (plug->priv->logout_command); if (command) { plug->priv->logout_command = g_strdup (command); } else { plug->priv->logout_command = NULL; } } static void gs_lock_plug_set_status_message (GSLockPlug *plug, const char *status_message) { g_return_if_fail (GS_LOCK_PLUG (plug)); g_free (plug->priv->status_message); plug->priv->status_message = g_strdup (status_message); if (plug->priv->status_message_label) { if (plug->priv->status_message) { gtk_label_set_text (GTK_LABEL (plug->priv->status_message_label), plug->priv->status_message); gtk_widget_show (plug->priv->status_message_label); } else { gtk_widget_hide (plug->priv->status_message_label); } } } static void gs_lock_plug_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GSLockPlug *self; self = GS_LOCK_PLUG (object); switch (prop_id) { case PROP_LOGOUT_ENABLED: g_value_set_boolean (value, self->priv->logout_enabled); break; case PROP_LOGOUT_COMMAND: g_value_set_string (value, self->priv->logout_command); break; case PROP_SWITCH_ENABLED: g_value_set_boolean (value, self->priv->switch_enabled); break; case PROP_STATUS_MESSAGE: g_value_set_string (value, self->priv->status_message); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gs_lock_plug_set_switch_enabled (GSLockPlug *plug, gboolean switch_enabled) { g_return_if_fail (GS_LOCK_PLUG (plug)); if (plug->priv->switch_enabled == switch_enabled) { return; } plug->priv->switch_enabled = switch_enabled; g_object_notify (G_OBJECT (plug), "switch-enabled"); if (plug->priv->auth_switch_button == NULL) { return; } if (switch_enabled) { if (is_program_in_path (MDM_FLEXISERVER_COMMAND)) { /* MDM */ gtk_widget_show (plug->priv->auth_switch_button); } else if (is_program_in_path (GDM_FLEXISERVER_COMMAND)) { /* GDM */ gtk_widget_show (plug->priv->auth_switch_button); } else if (g_getenv ("XDG_SEAT_PATH") != NULL) { /* LightDM */ gtk_widget_show (plug->priv->auth_switch_button); } else { gs_debug ("Warning: Unknown DM for switch button"); gtk_widget_hide (plug->priv->auth_switch_button); } } else { gtk_widget_hide (plug->priv->auth_switch_button); } } static void gs_lock_plug_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GSLockPlug *self; self = GS_LOCK_PLUG (object); switch (prop_id) { case PROP_LOGOUT_ENABLED: gs_lock_plug_set_logout_enabled (self, g_value_get_boolean (value)); break; case PROP_LOGOUT_COMMAND: gs_lock_plug_set_logout_command (self, g_value_get_string (value)); break; case PROP_STATUS_MESSAGE: gs_lock_plug_set_status_message (self, g_value_get_string (value)); break; case PROP_SWITCH_ENABLED: gs_lock_plug_set_switch_enabled (self, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gs_lock_plug_close (GSLockPlug *plug) { /* Synthesize delete_event to close dialog. */ GtkWidget *widget = GTK_WIDGET (plug); GdkEvent *event; event = gdk_event_new (GDK_DELETE); #if GTK_CHECK_VERSION (3, 0, 0) event->any.window = g_object_ref (gtk_widget_get_window(widget)); #else event->any.window = g_object_ref (widget->window); #endif event->any.send_event = TRUE; gtk_main_do_event (event); gdk_event_free (event); } static void gs_lock_plug_class_init (GSLockPlugClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GtkBindingSet *binding_set; object_class->finalize = gs_lock_plug_finalize; object_class->get_property = gs_lock_plug_get_property; object_class->set_property = gs_lock_plug_set_property; widget_class->style_set = gs_lock_plug_style_set; widget_class->show = gs_lock_plug_show; widget_class->hide = gs_lock_plug_hide; #if !GTK_CHECK_VERSION (3, 0, 0) widget_class->size_request = gs_lock_plug_size_request; #endif klass->close = gs_lock_plug_close; g_type_class_add_private (klass, sizeof (GSLockPlugPrivate)); lock_plug_signals [RESPONSE] = g_signal_new ("response", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GSLockPlugClass, response), NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); lock_plug_signals [CLOSE] = g_signal_new ("close", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GSLockPlugClass, close), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); g_object_class_install_property (object_class, PROP_LOGOUT_ENABLED, g_param_spec_boolean ("logout-enabled", NULL, NULL, FALSE, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_LOGOUT_COMMAND, g_param_spec_string ("logout-command", NULL, NULL, NULL, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_STATUS_MESSAGE, g_param_spec_string ("status-message", NULL, NULL, NULL, G_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_SWITCH_ENABLED, g_param_spec_boolean ("switch-enabled", NULL, NULL, FALSE, G_PARAM_READWRITE)); binding_set = gtk_binding_set_by_class (klass); gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0, "close", 0); } static void clear_clipboards (GSLockPlug *plug) { GtkClipboard *clipboard; clipboard = gtk_widget_get_clipboard (GTK_WIDGET (plug), GDK_SELECTION_PRIMARY); gtk_clipboard_clear (clipboard); gtk_clipboard_set_text (clipboard, "", -1); clipboard = gtk_widget_get_clipboard (GTK_WIDGET (plug), GDK_SELECTION_CLIPBOARD); gtk_clipboard_clear (clipboard); gtk_clipboard_set_text (clipboard, "", -1); } static void take_note (GtkButton *button, GSLockPlug *plug) { int page; page = gtk_notebook_page_num (GTK_NOTEBOOK (plug->priv->notebook), plug->priv->note_tab); gtk_notebook_set_current_page (GTK_NOTEBOOK (plug->priv->notebook), page); /* this counts as activity so restart the timer */ restart_cancel_timeout (plug); } static void submit_note (GtkButton *button, GSLockPlug *plug) { #ifdef WITH_LIBNOTIFY char *text; char summary[128]; char *escaped_text; GtkTextBuffer *buffer; GtkTextIter start, end; time_t t; struct tm *tmp; NotifyNotification *note; gtk_notebook_set_current_page (GTK_NOTEBOOK (plug->priv->notebook), AUTH_PAGE); buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (plug->priv->note_text_view)); gtk_text_buffer_get_bounds (buffer, &start, &end); text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); gtk_text_buffer_set_text (buffer, "", 0); escaped_text = g_markup_escape_text (text, -1); t = time (NULL); tmp = localtime (&t); strftime (summary, 128, "%X", tmp); note = notify_notification_new (summary, escaped_text, NULL); notify_notification_set_timeout (note, NOTIFY_EXPIRES_NEVER); notify_notification_show (note, NULL); g_object_unref (note); g_free (text); g_free (escaped_text); gs_lock_plug_response (plug, GS_LOCK_PLUG_RESPONSE_CANCEL); #endif /* WITH_LIBNOTIFY */ } static void cancel_note (GtkButton *button, GSLockPlug *plug) { GtkTextBuffer *buffer; gtk_notebook_set_current_page (GTK_NOTEBOOK (plug->priv->notebook), AUTH_PAGE); buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (plug->priv->note_text_view)); gtk_text_buffer_set_text (buffer, "", 0); /* this counts as activity so restart the timer */ restart_cancel_timeout (plug); gtk_window_set_default (GTK_WINDOW (plug), plug->priv->auth_unlock_button); clear_clipboards (plug); } static void logout_button_clicked (GtkButton *button, GSLockPlug *plug) { char **argv = NULL; GError *error = NULL; gboolean res; if (! plug->priv->logout_command) { return; } res = g_shell_parse_argv (plug->priv->logout_command, NULL, &argv, &error); if (! res) { g_warning ("Could not parse logout command: %s", error->message); g_error_free (error); return; } g_spawn_async (g_get_home_dir (), argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error); g_strfreev (argv); if (error) { g_warning ("Could not run logout command: %s", error->message); g_error_free (error); } } void gs_lock_plug_set_busy (GSLockPlug *plug) { GdkCursor *cursor; GtkWidget *top_level; top_level = gtk_widget_get_toplevel (GTK_WIDGET (plug)); cursor = gdk_cursor_new (GDK_WATCH); #if GTK_CHECK_VERSION (3, 0, 0) gdk_window_set_cursor (gtk_widget_get_window (top_level), cursor); #else gdk_window_set_cursor (top_level->window, cursor); #endif gdk_cursor_unref (cursor); } void gs_lock_plug_set_ready (GSLockPlug *plug) { GdkCursor *cursor; GtkWidget *top_level; top_level = gtk_widget_get_toplevel (GTK_WIDGET (plug)); cursor = gdk_cursor_new (GDK_LEFT_PTR); #if GTK_CHECK_VERSION (3, 0, 0) gdk_window_set_cursor (gtk_widget_get_window (top_level), cursor); #else gdk_window_set_cursor (top_level->window, cursor); #endif gdk_cursor_unref (cursor); } void gs_lock_plug_enable_prompt (GSLockPlug *plug, const char *message, gboolean visible) { g_return_if_fail (GS_IS_LOCK_PLUG (plug)); gs_debug ("Setting prompt to: %s", message); gtk_widget_set_sensitive (plug->priv->auth_unlock_button, TRUE); gtk_widget_show (plug->priv->auth_unlock_button); gtk_widget_grab_default (plug->priv->auth_unlock_button); gtk_label_set_text (GTK_LABEL (plug->priv->auth_prompt_label), message); gtk_widget_show (plug->priv->auth_prompt_label); gtk_entry_set_visibility (GTK_ENTRY (plug->priv->auth_prompt_entry), visible); gtk_widget_set_sensitive (plug->priv->auth_prompt_entry, TRUE); gtk_widget_show (plug->priv->auth_prompt_entry); if (! GTK_WIDGET_HAS_FOCUS (plug->priv->auth_prompt_entry)) { gtk_widget_grab_focus (plug->priv->auth_prompt_entry); } /* were there any key events sent to the plug while the * entry wasnt ready? If so, forward them along */ forward_key_events (plug); restart_cancel_timeout (plug); } void gs_lock_plug_disable_prompt (GSLockPlug *plug) { g_return_if_fail (GS_IS_LOCK_PLUG (plug)); /* gtk_widget_hide (plug->priv->auth_prompt_entry); */ /* gtk_widget_hide (plug->priv->auth_prompt_label); */ gtk_widget_set_sensitive (plug->priv->auth_unlock_button, FALSE); gtk_widget_set_sensitive (plug->priv->auth_prompt_entry, FALSE); /* gtk_widget_hide (plug->priv->auth_unlock_button); */ gtk_widget_grab_default (plug->priv->auth_cancel_button); } void gs_lock_plug_show_message (GSLockPlug *plug, const char *message) { g_return_if_fail (GS_IS_LOCK_PLUG (plug)); set_status_text (plug, message ? message : ""); } /* button press handler used to inhibit popup menu */ static gint entry_button_press (GtkWidget *widget, GdkEventButton *event) { if (event->button == 3 && event->type == GDK_BUTTON_PRESS) { return TRUE; } return FALSE; } static gint entry_key_press (GtkWidget *widget, GdkEventKey *event, GSLockPlug *plug) { restart_cancel_timeout (plug); /* if the input widget is visible and ready for input * then just carry on as usual */ if (GTK_WIDGET_VISIBLE (plug->priv->auth_prompt_entry) && GTK_WIDGET_IS_SENSITIVE (plug->priv->auth_prompt_entry)) { return FALSE; } if (strcmp (event->string, "") == 0) { return FALSE; } queue_key_event (plug, event); return TRUE; } /* adapted from gtk_dialog_add_button */ static GtkWidget * gs_lock_plug_add_button (GSLockPlug *plug, GtkWidget *action_area, const gchar *button_text) { GtkWidget *button; g_return_val_if_fail (GS_IS_LOCK_PLUG (plug), NULL); g_return_val_if_fail (button_text != NULL, NULL); button = gtk_button_new_from_stock (button_text); #if GTK_CHECK_VERSION (3, 0, 0) gtk_widget_set_can_default (button, TRUE); #else GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); #endif gtk_widget_show (button); gtk_box_pack_end (GTK_BOX (action_area), button, FALSE, TRUE, 0); return button; } static char * get_user_display_name (void) { const char *name; char *utf8_name; name = g_get_real_name (); if (name == NULL || strcmp (name, "Unknown") == 0) { name = g_get_user_name (); } utf8_name = NULL; if (name != NULL) { utf8_name = g_locale_to_utf8 (name, -1, NULL, NULL, NULL); } return utf8_name; } static char * get_user_name (void) { const char *name; char *utf8_name; name = g_get_user_name (); utf8_name = NULL; if (name != NULL) { utf8_name = g_locale_to_utf8 (name, -1, NULL, NULL, NULL); } return utf8_name; } static void create_page_one_buttons (GSLockPlug *plug) { gs_profile_start ("page one buttons"); plug->priv->auth_switch_button = gs_lock_plug_add_button (GS_LOCK_PLUG (plug), plug->priv->auth_action_area, _("S_witch User...")); gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (plug->priv->auth_action_area), plug->priv->auth_switch_button, TRUE); gtk_button_set_focus_on_click (GTK_BUTTON (plug->priv->auth_switch_button), FALSE); gtk_widget_set_no_show_all (plug->priv->auth_switch_button, TRUE); plug->priv->auth_logout_button = gs_lock_plug_add_button (GS_LOCK_PLUG (plug), plug->priv->auth_action_area, _("Log _Out")); gtk_button_set_focus_on_click (GTK_BUTTON (plug->priv->auth_logout_button), FALSE); gtk_widget_set_no_show_all (plug->priv->auth_logout_button, TRUE); plug->priv->auth_cancel_button = gs_lock_plug_add_button (GS_LOCK_PLUG (plug), plug->priv->auth_action_area, GTK_STOCK_CANCEL); gtk_button_set_focus_on_click (GTK_BUTTON (plug->priv->auth_cancel_button), FALSE); plug->priv->auth_unlock_button = gs_lock_plug_add_button (GS_LOCK_PLUG (plug), plug->priv->auth_action_area, _("_Unlock")); gtk_button_set_focus_on_click (GTK_BUTTON (plug->priv->auth_unlock_button), FALSE); gtk_window_set_default (GTK_WINDOW (plug), plug->priv->auth_unlock_button); gs_profile_end ("page one buttons"); } /* adapted from MDM */ static char * expand_string (const char *text) { GString *str; const char *p; char *username; int i; int n_chars; struct utsname name; str = g_string_sized_new (strlen (text)); p = text; n_chars = g_utf8_strlen (text, -1); i = 0; while (i < n_chars) { gunichar ch; ch = g_utf8_get_char (p); /* Backslash commands */ if (ch == '\\') { p = g_utf8_next_char (p); i++; ch = g_utf8_get_char (p); if (i >= n_chars || ch == '\0') { g_warning ("Unescaped \\ at end of text\n"); goto bail; } else if (ch == 'n') { g_string_append_unichar (str, '\n'); } else { g_string_append_unichar (str, ch); } } else if (ch == '%') { p = g_utf8_next_char (p); i++; ch = g_utf8_get_char (p); if (i >= n_chars || ch == '\0') { g_warning ("Unescaped %% at end of text\n"); goto bail; } switch (ch) { case '%': g_string_append (str, "%"); break; case 'c': /* clock */ break; case 'd': /* display */ g_string_append (str, g_getenv ("DISPLAY")); break; case 'h': /* hostname */ g_string_append (str, g_get_host_name ()); break; case 'm': /* machine name */ uname (&name); g_string_append (str, name.machine); break; case 'n': /* nodename */ uname (&name); g_string_append (str, name.nodename); break; case 'r': /* release */ uname (&name); g_string_append (str, name.release); break; case 'R': /* Real name */ username = get_user_display_name (); g_string_append (str, username); g_free (username); break; case 's': /* system name */ uname (&name); g_string_append (str, name.sysname); break; case 'U': /* Username */ username = get_user_name (); g_string_append (str, username); g_free (username); break; default: if (ch < 127) { g_warning ("unknown escape code %%%c in text\n", (char)ch); } else { g_warning ("unknown escape code %%(U%x) in text\n", (int)ch); } } } else { g_string_append_unichar (str, ch); } p = g_utf8_next_char (p); i++; } bail: return g_string_free (str, FALSE); } static void expand_string_for_label (GtkWidget *label) { const char *template; char *str; template = gtk_label_get_label (GTK_LABEL (label)); str = expand_string (template); gtk_label_set_label (GTK_LABEL (label), str); g_free (str); } static void create_page_one (GSLockPlug *plug) { GtkWidget *align; GtkWidget *vbox; GtkWidget *vbox2; GtkWidget *hbox; char *str; gs_profile_start ("page one"); align = gtk_alignment_new (0.5, 0.5, 1, 1); gtk_notebook_append_page (GTK_NOTEBOOK (plug->priv->notebook), align, NULL); vbox = gtk_vbox_new (FALSE, 12); gtk_container_add (GTK_CONTAINER (align), vbox); vbox2 = gtk_vbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0); str = g_strdup ("<span size=\"xx-large\" weight=\"ultrabold\">%s</span>"); plug->priv->auth_time_label = gtk_label_new (str); g_free (str); gtk_misc_set_alignment (GTK_MISC (plug->priv->auth_time_label), 0.5, 0.5); gtk_label_set_use_markup (GTK_LABEL (plug->priv->auth_time_label), TRUE); gtk_box_pack_start (GTK_BOX (vbox2), plug->priv->auth_time_label, FALSE, FALSE, 0); str = g_strdup ("<span size=\"large\">%s</span>"); plug->priv->auth_date_label = gtk_label_new (str); g_free (str); gtk_misc_set_alignment (GTK_MISC (plug->priv->auth_date_label), 0.5, 0.5); gtk_label_set_use_markup (GTK_LABEL (plug->priv->auth_date_label), TRUE); gtk_box_pack_start (GTK_BOX (vbox2), plug->priv->auth_date_label, FALSE, FALSE, 0); plug->priv->auth_face_image = gtk_image_new (); gtk_box_pack_start (GTK_BOX (vbox), plug->priv->auth_face_image, TRUE, TRUE, 0); gtk_misc_set_alignment (GTK_MISC (plug->priv->auth_face_image), 0.5, 1.0); vbox2 = gtk_vbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0); str = g_strdup ("<span size=\"x-large\">%R</span>"); plug->priv->auth_realname_label = gtk_label_new (str); g_free (str); expand_string_for_label (plug->priv->auth_realname_label); gtk_misc_set_alignment (GTK_MISC (plug->priv->auth_realname_label), 0.5, 0.5); gtk_label_set_use_markup (GTK_LABEL (plug->priv->auth_realname_label), TRUE); gtk_box_pack_start (GTK_BOX (vbox2), plug->priv->auth_realname_label, FALSE, FALSE, 0); /* To translators: This expands to USERNAME on HOSTNAME */ str = g_strdup_printf ("<span size=\"small\">%s</span>", _("%U on %h")); plug->priv->auth_username_label = gtk_label_new (str); g_free (str); expand_string_for_label (plug->priv->auth_username_label); gtk_misc_set_alignment (GTK_MISC (plug->priv->auth_username_label), 0.5, 0.5); gtk_label_set_use_markup (GTK_LABEL (plug->priv->auth_username_label), TRUE); gtk_box_pack_start (GTK_BOX (vbox2), plug->priv->auth_username_label, FALSE, FALSE, 0); vbox2 = gtk_vbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), vbox2, TRUE, TRUE, 0); hbox = gtk_hbox_new (FALSE, 6); gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0); plug->priv->auth_prompt_label = gtk_label_new_with_mnemonic (_("_Password:")); gtk_misc_set_alignment (GTK_MISC (plug->priv->auth_prompt_label), 0, 0.5); gtk_box_pack_start (GTK_BOX (hbox), plug->priv->auth_prompt_label, FALSE, FALSE, 0); plug->priv->auth_prompt_entry = gtk_entry_new (); gtk_box_pack_start (GTK_BOX (hbox), plug->priv->auth_prompt_entry, TRUE, TRUE, 0); gtk_label_set_mnemonic_widget (GTK_LABEL (plug->priv->auth_prompt_label), plug->priv->auth_prompt_entry); plug->priv->auth_capslock_label = gtk_label_new (""); gtk_misc_set_alignment (GTK_MISC (plug->priv->auth_capslock_label), 0.5, 0.5); gtk_box_pack_start (GTK_BOX (vbox2), plug->priv->auth_capslock_label, FALSE, FALSE, 0); /* Status text */ plug->priv->auth_message_label = gtk_label_new (NULL); gtk_box_pack_start (GTK_BOX (vbox), plug->priv->auth_message_label, FALSE, FALSE, 0); /* Buttons */ plug->priv->auth_action_area = gtk_hbutton_box_new (); gtk_button_box_set_layout (GTK_BUTTON_BOX (plug->priv->auth_action_area), GTK_BUTTONBOX_END); gtk_box_pack_end (GTK_BOX (vbox), plug->priv->auth_action_area, FALSE, TRUE, 0); gtk_widget_show (plug->priv->auth_action_area); create_page_one_buttons (plug); gs_profile_end ("page one"); } static void unlock_button_clicked (GtkButton *button, GSLockPlug *plug) { gs_lock_plug_response (plug, GS_LOCK_PLUG_RESPONSE_OK); } static void cancel_button_clicked (GtkButton *button, GSLockPlug *plug) { gs_lock_plug_response (plug, GS_LOCK_PLUG_RESPONSE_CANCEL); } static void switch_user_button_clicked (GtkButton *button, GSLockPlug *plug) { remove_response_idle (plug); gs_lock_plug_set_sensitive (plug, FALSE); plug->priv->response_idle_id = g_timeout_add (2000, (GSourceFunc)response_cancel_idle_cb, plug); gs_lock_plug_set_busy (plug); do_user_switch (plug); } static char * get_dialog_theme_name (GSLockPlug *plug) { char *name; GSettings *settings; settings = g_settings_new (GSETTINGS_SCHEMA); name = g_settings_get_string (settings, KEY_LOCK_DIALOG_THEME); g_object_unref (settings); return name; } static gboolean load_theme (GSLockPlug *plug) { char *theme; char *filename; char *gtkbuilder; char *rc; GtkBuilder *builder; GtkWidget *lock_dialog; GError *error=NULL; theme = get_dialog_theme_name (plug); if (theme == NULL) { return FALSE; } filename = g_strdup_printf ("lock-dialog-%s.ui", theme); gtkbuilder = g_build_filename (GTKBUILDERDIR, filename, NULL); g_free (filename); if (! g_file_test (gtkbuilder, G_FILE_TEST_IS_REGULAR)) { g_free (gtkbuilder); g_free (theme); return FALSE; } filename = g_strdup_printf ("lock-dialog-%s.gtkrc", theme); g_free (theme); rc = g_build_filename (GTKBUILDERDIR, filename, NULL); g_free (filename); if (g_file_test (rc, G_FILE_TEST_IS_REGULAR)) { gtk_rc_parse (rc); } g_free (rc); builder = gtk_builder_new(); if (!gtk_builder_add_from_file (builder,gtkbuilder,&error)) { g_warning ("Couldn't load builder file '%s': %s", gtkbuilder, error->message); g_error_free(error); g_free (gtkbuilder); return FALSE; } g_free (gtkbuilder); lock_dialog = GTK_WIDGET (gtk_builder_get_object(builder, "lock-dialog")); gtk_container_add (GTK_CONTAINER (plug), lock_dialog); plug->priv->vbox = NULL; plug->priv->notebook = GTK_WIDGET (gtk_builder_get_object(builder, "notebook")); plug->priv->auth_face_image = GTK_WIDGET (gtk_builder_get_object(builder, "auth-face-image")); plug->priv->auth_action_area = GTK_WIDGET (gtk_builder_get_object(builder, "auth-action-area")); plug->priv->auth_time_label = GTK_WIDGET (gtk_builder_get_object(builder, "auth-time-label")); plug->priv->auth_date_label = GTK_WIDGET (gtk_builder_get_object(builder, "auth-date-label")); plug->priv->auth_realname_label = GTK_WIDGET (gtk_builder_get_object(builder, "auth-realname-label")); plug->priv->auth_username_label = GTK_WIDGET (gtk_builder_get_object(builder, "auth-username-label")); plug->priv->auth_prompt_label = GTK_WIDGET (gtk_builder_get_object(builder, "auth-prompt-label")); plug->priv->auth_prompt_entry = GTK_WIDGET (gtk_builder_get_object(builder, "auth-prompt-entry")); plug->priv->auth_prompt_box = GTK_WIDGET (gtk_builder_get_object(builder, "auth-prompt-box")); plug->priv->auth_capslock_label = GTK_WIDGET (gtk_builder_get_object(builder, "auth-capslock-label")); plug->priv->auth_message_label = GTK_WIDGET (gtk_builder_get_object(builder, "auth-status-label")); plug->priv->auth_unlock_button = GTK_WIDGET (gtk_builder_get_object(builder, "auth-unlock-button")); plug->priv->auth_cancel_button = GTK_WIDGET (gtk_builder_get_object(builder, "auth-cancel-button")); plug->priv->auth_logout_button = GTK_WIDGET (gtk_builder_get_object(builder, "auth-logout-button")); plug->priv->auth_switch_button = GTK_WIDGET (gtk_builder_get_object(builder, "auth-switch-button")); plug->priv->auth_note_button = GTK_WIDGET (gtk_builder_get_object(builder, "auth-note-button")); plug->priv->note_tab = GTK_WIDGET (gtk_builder_get_object(builder, "note-tab")); plug->priv->note_tab_label = GTK_WIDGET (gtk_builder_get_object(builder, "note-tab-label")); plug->priv->note_ok_button = GTK_WIDGET (gtk_builder_get_object(builder, "note-ok-button")); plug->priv->note_text_view = GTK_WIDGET (gtk_builder_get_object(builder, "note-text-view")); plug->priv->note_cancel_button = GTK_WIDGET (gtk_builder_get_object(builder, "note-cancel-button")); /* Placeholder for the keyboard indicator */ plug->priv->auth_prompt_kbd_layout_indicator = GTK_WIDGET (gtk_builder_get_object(builder, "auth-prompt-kbd-layout-indicator")); if (plug->priv->auth_logout_button != NULL) { gtk_widget_set_no_show_all (plug->priv->auth_logout_button, TRUE); } if (plug->priv->auth_switch_button != NULL) { gtk_widget_set_no_show_all (plug->priv->auth_switch_button, TRUE); } if (plug->priv->auth_note_button != NULL) { gtk_widget_set_no_show_all (plug->priv->auth_note_button, TRUE); } date_time_update (plug); gtk_widget_show_all (lock_dialog); plug->priv->status_message_label = GTK_WIDGET (gtk_builder_get_object(builder, "status-message-label")); return TRUE; } static int delete_handler (GSLockPlug *plug, GdkEventAny *event, gpointer data) { gs_lock_plug_response (plug, GS_LOCK_PLUG_RESPONSE_CANCEL); return TRUE; /* Do not destroy */ } static void on_note_text_buffer_changed (GtkTextBuffer *buffer, GSLockPlug *plug) { int len; len = gtk_text_buffer_get_char_count (buffer); gtk_widget_set_sensitive (plug->priv->note_ok_button, len <= NOTE_BUFFER_MAX_CHARS); } static void gs_lock_plug_init (GSLockPlug *plug) { gs_profile_start (NULL); plug->priv = GS_LOCK_PLUG_GET_PRIVATE (plug); clear_clipboards (plug); #ifdef WITH_LIBNOTIFY notify_init ("mate-screensaver-dialog"); plug->priv->leave_note_enabled = TRUE; #else plug->priv->leave_note_enabled = FALSE; #endif if (! load_theme (plug)) { gs_debug ("Unable to load theme!"); plug->priv->vbox = gtk_vbox_new (FALSE, 0); gtk_container_add (GTK_CONTAINER (plug), plug->priv->vbox); /* Notebook */ plug->priv->notebook = gtk_notebook_new (); gtk_notebook_set_show_tabs (GTK_NOTEBOOK (plug->priv->notebook), FALSE); gtk_notebook_set_show_border (GTK_NOTEBOOK (plug->priv->notebook), FALSE); gtk_box_pack_start (GTK_BOX (plug->priv->vbox), plug->priv->notebook, TRUE, TRUE, 0); /* Page 1 */ create_page_one (plug); date_time_update (plug); gtk_widget_show_all (plug->priv->vbox); } plug->priv->datetime_timeout_id = g_timeout_add_seconds (1, (GSourceFunc) date_time_update, plug); if (plug->priv->note_text_view != NULL) { GtkTextBuffer *buffer; buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (plug->priv->note_text_view)); g_signal_connect (buffer, "changed", G_CALLBACK (on_note_text_buffer_changed), plug); } /* Layout indicator */ #ifdef WITH_KBD_LAYOUT_INDICATOR if (plug->priv->auth_prompt_kbd_layout_indicator != NULL) { XklEngine *engine; #if GTK_CHECK_VERSION (3, 0, 0) engine = xkl_engine_get_instance (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ())); #else engine = xkl_engine_get_instance (GDK_DISPLAY ()); #endif if (xkl_engine_get_num_groups (engine) > 1) { GtkWidget *layout_indicator; layout_indicator = matekbd_indicator_new (); matekbd_indicator_set_parent_tooltips (MATEKBD_INDICATOR (layout_indicator), TRUE); gtk_box_pack_start (GTK_BOX (plug->priv->auth_prompt_kbd_layout_indicator), layout_indicator, FALSE, FALSE, 6); gtk_widget_show_all (layout_indicator); gtk_widget_show (plug->priv->auth_prompt_kbd_layout_indicator); } else { gtk_widget_hide (plug->priv->auth_prompt_kbd_layout_indicator); } g_object_unref (engine); } #endif if (plug->priv->auth_note_button != NULL) { if (plug->priv->leave_note_enabled) { gtk_widget_show_all (plug->priv->auth_note_button); } else { gtk_widget_hide (plug->priv->auth_note_button); } } if (plug->priv->auth_switch_button != NULL) { if (plug->priv->switch_enabled) { gtk_widget_show_all (plug->priv->auth_switch_button); } else { gtk_widget_hide (plug->priv->auth_switch_button); } } gtk_widget_grab_default (plug->priv->auth_unlock_button); if (plug->priv->auth_username_label != NULL) { expand_string_for_label (plug->priv->auth_username_label); } if (plug->priv->auth_realname_label != NULL) { expand_string_for_label (plug->priv->auth_realname_label); } if (! plug->priv->logout_enabled || ! plug->priv->logout_command) { if (plug->priv->auth_logout_button != NULL) { gtk_widget_hide (plug->priv->auth_logout_button); } } plug->priv->timeout = DIALOG_TIMEOUT_MSEC; g_signal_connect (plug, "key_press_event", G_CALLBACK (entry_key_press), plug); /* button press handler used to inhibit popup menu */ g_signal_connect (plug->priv->auth_prompt_entry, "button_press_event", G_CALLBACK (entry_button_press), NULL); gtk_entry_set_activates_default (GTK_ENTRY (plug->priv->auth_prompt_entry), TRUE); gtk_entry_set_visibility (GTK_ENTRY (plug->priv->auth_prompt_entry), FALSE); g_signal_connect (plug->priv->auth_unlock_button, "clicked", G_CALLBACK (unlock_button_clicked), plug); g_signal_connect (plug->priv->auth_cancel_button, "clicked", G_CALLBACK (cancel_button_clicked), plug); if (plug->priv->status_message_label) { if (plug->priv->status_message) { gtk_label_set_text (GTK_LABEL (plug->priv->status_message_label), plug->priv->status_message); } else { gtk_widget_hide (plug->priv->status_message_label); } } if (plug->priv->auth_switch_button != NULL) { g_signal_connect (plug->priv->auth_switch_button, "clicked", G_CALLBACK (switch_user_button_clicked), plug); } if (plug->priv->auth_note_button != NULL) { g_signal_connect (plug->priv->auth_note_button, "clicked", G_CALLBACK (take_note), plug); g_signal_connect (plug->priv->note_ok_button, "clicked", G_CALLBACK (submit_note), plug); g_signal_connect (plug->priv->note_cancel_button, "clicked", G_CALLBACK (cancel_note), plug); } if (plug->priv->note_tab_label != NULL) { expand_string_for_label (plug->priv->note_tab_label); } if (plug->priv->auth_logout_button != NULL) { g_signal_connect (plug->priv->auth_logout_button, "clicked", G_CALLBACK (logout_button_clicked), plug); } g_signal_connect (plug, "delete_event", G_CALLBACK (delete_handler), NULL); gs_profile_end (NULL); } static void gs_lock_plug_finalize (GObject *object) { GSLockPlug *plug; g_return_if_fail (object != NULL); g_return_if_fail (GS_IS_LOCK_PLUG (object)); plug = GS_LOCK_PLUG (object); g_return_if_fail (plug->priv != NULL); g_free (plug->priv->logout_command); remove_response_idle (plug); remove_cancel_timeout (plug); remove_datetime_timeout (plug); #ifdef WITH_LIBNOTIFY notify_uninit (); #endif G_OBJECT_CLASS (gs_lock_plug_parent_class)->finalize (object); } GtkWidget * gs_lock_plug_new (void) { GtkWidget *result; result = g_object_new (GS_TYPE_LOCK_PLUG, NULL); gtk_window_set_focus_on_map (GTK_WINDOW (result), TRUE); return result; }