/* $Id: locker.c,v 1.56 2012/08/28 17:31:06 khorben Exp $ */ static char const _copyright[] = "Copyright (c) 2010-2012 Pierre Pronchery <khorben@defora.org>"; /* This file is part of DeforaOS Desktop Locker */ static char const _license[] = "This program is free software: you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License as published by\n" "the Free Software Foundation, version 3 of the License.\n" "\n" "This program is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License\n" "along with this program. If not, see <http://www.gnu.org/licenses/>."; #if defined(__NetBSD__) # include <sys/param.h> # include <sys/sysctl.h> #elif !defined(__FreeBSD__) # include <fcntl.h> #endif #include <sys/types.h> #include <unistd.h> #include <dirent.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #include <libintl.h> #include <gdk/gdkx.h> #include <X11/Xatom.h> #include <X11/extensions/scrnsaver.h> #include <System.h> #include <Desktop.h> #include "locker.h" #include "../config.h" #define _(string) gettext(string) #define max(a, b) ((a) > (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b)) /* Locker */ /* private */ /* types */ typedef struct _LockerPlugins { char * name; Plugin * pplugin; LockerPluginDefinition * definition; LockerPlugin * plugin; } LockerPlugins; struct _Locker { /* settings */ int suspend; Config * config; /* internal */ int enabled; int locked; GdkDisplay * display; int screen; int event; GtkWidget ** windows; size_t windows_cnt; /* authentication */ Plugin * aplugin; LockerAuthDefinition * adefinition; LockerAuth * auth; LockerAuthHelper ahelper; /* demos */ Plugin * dplugin; LockerDemoDefinition * ddefinition; LockerDemo * demo; LockerDemoHelper dhelper; /* plug-ins */ LockerPlugins * plugins; size_t plugins_cnt; LockerPluginHelper phelper; /* preferences */ GtkWidget * pr_window; GtkListStore * pr_astore; GtkWidget * pr_acombo; GtkListStore * pr_dstore; GtkWidget * pr_dcombo; GtkListStore * pr_plstore; GtkWidget * pr_plview; /* about */ GtkWidget * ab_window; }; typedef enum _LockerPluginsColumn { LPC_PLUGIN = 0, LPC_ENABLED, LPC_FILENAME, LPC_ICON, LPC_NAME } LockerPluginsColumn; #define LPC_LAST LPC_NAME #define LPC_COUNT (LPC_LAST + 1) /* constants */ static char const * _authors[] = { "Pierre Pronchery <khorben@defora.org>", NULL }; /* prototypes */ /* accessors */ static int _locker_is_locked(Locker * locker); static gboolean _locker_plugin_is_enabled(Locker * locker, char const * plugin); /* useful */ static void _locker_about(Locker * locker); /* actions */ static int _locker_action(Locker * locker, LockerAction action); static int _locker_action_activate(Locker * locker, int force); static int _locker_action_deactivate(Locker * locker, int reset); static int _locker_action_disable(Locker * locker); static int _locker_action_enable(Locker * locker); static int _locker_action_lock(Locker * locker, int force); static int _locker_action_start(Locker * locker); static int _locker_action_stop(Locker * locker); static int _locker_action_suspend(Locker * locker); static int _locker_action_unlock(Locker * locker); /* authentication */ static char const * _locker_auth_config_get(Locker * locker, char const * section, char const * variable); static int _locker_auth_config_set(Locker * locker, char const * section, char const * variable, char const * value); /* configuration */ static int _locker_config_load(Locker * locker); static int _locker_config_save(Locker * locker); /* demos */ static char const * _locker_demo_config_get(Locker * locker, char const * section, char const * variable); static int _locker_demo_config_set(Locker * locker, char const * section, char const * variable, char const * value); static int _locker_error(Locker * locker, char const * message, int ret); static int _locker_event(Locker * locker, LockerEvent event); /* plug-ins */ static char const * _locker_plugin_config_get(Locker * locker, char const * section, char const * variable); static int _locker_plugin_config_set(Locker * locker, char const * section, char const * variable, char const * value); static int _locker_plugin_load(Locker * locker, char const * plugin); static int _locker_plugin_unload(Locker * locker, char const * plugin); /* callbacks */ static gboolean _lock_on_closex(void); static GdkFilterReturn _locker_on_filter(GdkXEvent * xevent, GdkEvent * event, gpointer data); static gboolean _locker_on_map_event(gpointer data); static int _locker_on_message(void * data, uint32_t value1, uint32_t value2, uint32_t value3); static void _locker_on_realize(GtkWidget * widget, gpointer data); /* public */ /* functions */ /* locker_new */ static int _new_config(Locker * locker); static int _new_demo(Locker * locker, char const * demo); static void _new_helpers(Locker * locker); static GtkWidget * _new_auth(Locker * locker, char const * plugin); static int _new_plugins(Locker * locker); static int _new_xss(Locker * locker, size_t cnt); Locker * locker_new(int suspend, char const * demo, char const * auth) { Locker * locker; GdkScreen * screen; GtkWidget * widget = NULL; size_t cnt; size_t i; GdkColor black; GdkRectangle rect; GdkWindow * root; if((locker = object_new(sizeof(*locker))) == NULL) { _locker_error(NULL, error_get(), 1); return NULL; } _new_helpers(locker); locker->suspend = (suspend != 0) ? 1 : 0; locker->enabled = 1; locker->locked = 0; screen = gdk_screen_get_default(); locker->display = gdk_screen_get_display(screen); locker->screen = gdk_x11_get_default_screen(); if((cnt = gdk_screen_get_n_monitors(screen)) < 1) cnt = 1; locker->windows = malloc(sizeof(*locker->windows) * cnt); locker->windows_cnt = cnt; locker->aplugin = NULL; locker->adefinition = NULL; locker->auth = NULL; locker->dplugin = NULL; locker->ddefinition = NULL; locker->demo = NULL; locker->plugins = NULL; locker->plugins_cnt = 0; locker->pr_window = NULL; locker->ab_window = NULL; /* check for errors */ if(_new_config(locker) != 0 || _new_demo(locker, demo) != 0 || (widget = _new_auth(locker, auth)) == NULL || _new_xss(locker, cnt) != 0) { _locker_error(NULL, error_get(), 1); locker_delete(locker); return NULL; } _new_plugins(locker); /* create windows */ memset(&black, 0, sizeof(black)); for(i = 0; i < locker->windows_cnt; i++) { locker->windows[i] = gtk_window_new(GTK_WINDOW_TOPLEVEL); gdk_screen_get_monitor_geometry(screen, i, &rect); gtk_window_move(GTK_WINDOW(locker->windows[i]), rect.x, rect.y); gtk_window_resize(GTK_WINDOW(locker->windows[i]), rect.width, rect.height); gtk_window_set_keep_above(GTK_WINDOW(locker->windows[i]), TRUE); gtk_window_stick(GTK_WINDOW(locker->windows[i])); gtk_widget_modify_bg(locker->windows[i], GTK_STATE_NORMAL, &black); g_signal_connect_swapped(G_OBJECT(locker->windows[i]), "delete-event", G_CALLBACK(_lock_on_closex), NULL); g_signal_connect(locker->windows[i], "realize", G_CALLBACK( _locker_on_realize), locker); } /* automatically grab keyboard and mouse */ g_signal_connect_swapped(G_OBJECT(locker->windows[0]), "map-event", G_CALLBACK(_locker_on_map_event), locker); /* pack the authentication widget */ gtk_container_add(GTK_CONTAINER(locker->windows[0]), widget); root = gdk_get_default_root_window(); XScreenSaverSelectInput(GDK_DISPLAY_XDISPLAY(locker->display), GDK_WINDOW_XWINDOW(root), ScreenSaverNotifyMask); gdk_x11_register_standard_event_type(locker->display, locker->event, 1); gdk_window_add_filter(root, _locker_on_filter, locker); desktop_message_register(LOCKER_CLIENT_MESSAGE, _locker_on_message, locker); return locker; } static int _new_config(Locker * locker) { if((locker->config = config_new()) == NULL) return -1; /* ignore errors */ _locker_config_load(locker); return 0; } static GtkWidget * _new_auth(Locker * locker, char const * plugin) { if(plugin == NULL) plugin = config_get(locker->config, NULL, "auth"); if(plugin == NULL) #ifdef EMBEDDED plugin = "slider"; #else plugin = "password"; #endif if((locker->aplugin = plugin_new(LIBDIR, PACKAGE, "auth", plugin)) == NULL) return NULL; if((locker->adefinition = plugin_lookup(locker->aplugin, "plugin")) == NULL) return NULL; if(locker->adefinition->init == NULL || locker->adefinition->destroy == NULL || locker->adefinition->get_widget == NULL || locker->adefinition->action == NULL || (locker->auth = locker->adefinition->init( &locker->ahelper)) == NULL) return NULL; return locker->adefinition->get_widget(locker->auth); } static int _new_demo(Locker * locker, char const * demo) { if(demo == NULL && (demo = config_get(locker->config, NULL, "demo")) == NULL) return 0; if((locker->dplugin = plugin_new(LIBDIR, PACKAGE, "demos", demo)) == NULL) return -1; if((locker->ddefinition = plugin_lookup(locker->dplugin, "plugin")) == NULL) return -1; if(locker->ddefinition->init == NULL || locker->ddefinition->destroy == NULL || (locker->demo = locker->ddefinition->init( &locker->dhelper)) == NULL) return -1; return 0; } static void _new_helpers(Locker * locker) { /* authentication helper */ locker->ahelper.locker = locker; locker->ahelper.error = _locker_error; locker->ahelper.action = _locker_action; locker->ahelper.config_get = _locker_auth_config_get; locker->ahelper.config_set = _locker_auth_config_set; /* demos helper */ locker->dhelper.locker = locker; locker->dhelper.error = _locker_error; locker->dhelper.config_get = _locker_demo_config_get; locker->dhelper.config_set = _locker_demo_config_set; /* plug-ins helper */ locker->phelper.locker = locker; locker->phelper.error = _locker_error; locker->phelper.about_dialog = _locker_about; locker->phelper.action = _locker_action; locker->phelper.config_get = _locker_plugin_config_get; locker->phelper.config_set = _locker_plugin_config_set; } static int _new_plugins(Locker * locker) { int ret = 0; char const * p; String * plugins; size_t i; int c; if((p = config_get(locker->config, NULL, "plugins")) == NULL) p = "systray"; else if(strlen(p) == 0) return 0; if((plugins = string_new(p)) == NULL) return -1; for(i = 0;; i++) { if(plugins[i] != ',' && plugins[i] != '\0') continue; c = plugins[i]; plugins[i] = '\0'; ret |= _locker_plugin_load(locker, plugins); if(c == '\0') break; plugins += i + 1; i = 0; } string_delete(plugins); return ret; } static int _new_xss(Locker * locker, size_t cnt) { int error; /* register as screensaver */ if(XScreenSaverQueryExtension(GDK_DISPLAY_XDISPLAY(locker->display), &locker->event, &error) == 0 || XScreenSaverRegister(GDK_DISPLAY_XDISPLAY( locker->display), locker->screen, getpid(), XA_INTEGER) == 0) return -error_set_code(1, "%s", _("Could not register as screensaver")); return 0; } /* locker_delete */ void locker_delete(Locker * locker) { size_t i; LockerPlugins * p; GdkWindow * window; /* destroy the generic plug-ins */ for(i = 0; i < locker->plugins_cnt; i++) { p = &locker->plugins[i]; p->definition->destroy(p->plugin); plugin_delete(p->pplugin); free(p->name); } free(locker->plugins); /* destroy the authentication plug-in */ if(locker->adefinition != NULL) locker->adefinition->destroy(locker->auth); if(locker->aplugin != NULL) plugin_delete(locker->aplugin); /* destroy the demo plug-in */ if(locker->demo != NULL) { if(locker->ddefinition->remove != NULL) for(i = 0; i < locker->windows_cnt; i++) { #if GTK_CHECK_VERSION(2, 14, 0) window = gtk_widget_get_window( locker->windows[i]); #else window = locker->windows[i]->window; #endif locker->ddefinition->remove(locker->demo, window); } locker->ddefinition->destroy(locker->demo); } if(locker->dplugin != NULL) plugin_delete(locker->dplugin); /* destroy the windows */ for(i = 0; i < locker->windows_cnt; i++) if(locker->windows[i] != NULL) gtk_widget_destroy(locker->windows[i]); free(locker->windows); if(locker->ab_window != NULL) gtk_widget_destroy(locker->ab_window); free(locker->windows); XScreenSaverUnregister(GDK_DISPLAY_XDISPLAY(locker->display), locker->screen); if(locker->config != NULL) config_delete(locker->config); object_delete(locker); } /* useful */ /* locker_show_preferences */ static void _preferences_window(Locker * locker); static GtkWidget * _preferences_window_auth(Locker * locker); static GtkWidget * _preferences_window_demo(Locker * locker); static GtkWidget * _preferences_window_plugins(Locker * locker); /* callbacks */ static void _preferences_on_cancel(gpointer data); static void _cancel_auth(Locker * locker, GtkListStore * store); static void _cancel_demo(Locker * locker, GtkListStore * store); static void _cancel_plugins(Locker * locker, GtkListStore * store); static gboolean _preferences_on_closex(gpointer data); static void _preferences_on_ok(gpointer data); static void _preferences_on_plugins_toggled(GtkCellRendererToggle * renderer, char * path, gpointer data); static void _preferences_on_response(GtkWidget * widget, gint response, gpointer data); void locker_show_preferences(Locker * locker, gboolean show) { if(locker->pr_window == NULL) _preferences_window(locker); if(locker->pr_window != NULL) { if(show) gtk_window_present(GTK_WINDOW(locker->pr_window)); else gtk_widget_hide(locker->pr_window); return; } } static void _preferences_window(Locker * locker) { GtkWidget * vbox; GtkWidget * notebook; GtkWidget * widget; locker->pr_window = gtk_dialog_new_with_buttons( _("Screensaver preferences"), NULL, 0, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); gtk_window_set_default_size(GTK_WINDOW(locker->pr_window), 400, 300); gtk_window_set_position(GTK_WINDOW(locker->pr_window), GTK_WIN_POS_CENTER_ALWAYS); g_signal_connect_swapped(locker->pr_window, "delete-event", G_CALLBACK( _preferences_on_closex), locker); g_signal_connect(locker->pr_window, "response", G_CALLBACK( _preferences_on_response), locker); notebook = gtk_notebook_new(); gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE); /* authentication */ widget = _preferences_window_auth(locker); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), widget, gtk_label_new( _("Authentication"))); /* demos */ widget = _preferences_window_demo(locker); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), widget, gtk_label_new( _("Demos"))); /* plug-ins */ widget = _preferences_window_plugins(locker); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), widget, gtk_label_new(_("Plug-ins"))); #if GTK_CHECK_VERSION(2, 14, 0) vbox = gtk_dialog_get_content_area(GTK_DIALOG(locker->pr_window)); #else vbox = GTK_DIALOG(locker->pr_window)->vbox; #endif gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); _preferences_on_cancel(locker); gtk_widget_show_all(vbox); } static GtkWidget * _preferences_window_auth(Locker * locker) { GtkWidget * vbox; GtkWidget * hbox; GtkWidget * widget; GtkCellRenderer * renderer; vbox = gtk_vbox_new(FALSE, 4); /* selector */ hbox = gtk_hbox_new(FALSE, 4); widget = gtk_label_new(_("Plug-in: ")); gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0); locker->pr_astore = gtk_list_store_new(LPC_COUNT, G_TYPE_POINTER, G_TYPE_BOOLEAN, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING); locker->pr_acombo = gtk_combo_box_new_with_model(GTK_TREE_MODEL( locker->pr_astore)); widget = locker->pr_acombo; renderer = gtk_cell_renderer_pixbuf_new(); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), renderer, FALSE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(widget), renderer, "pixbuf", LPC_ICON, NULL); renderer = gtk_cell_renderer_text_new(); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), renderer, TRUE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(widget), renderer, "text", LPC_NAME, NULL); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); return vbox; } static GtkWidget * _preferences_window_demo(Locker * locker) { GtkWidget * vbox; GtkWidget * hbox; GtkWidget * widget; GtkCellRenderer * renderer; vbox = gtk_vbox_new(FALSE, 4); /* selector */ hbox = gtk_hbox_new(FALSE, 4); widget = gtk_label_new(_("Plug-in: ")); gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0); locker->pr_dstore = gtk_list_store_new(LPC_COUNT, G_TYPE_POINTER, G_TYPE_BOOLEAN, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING); locker->pr_dcombo = gtk_combo_box_new_with_model(GTK_TREE_MODEL( locker->pr_dstore)); widget = locker->pr_dcombo; renderer = gtk_cell_renderer_pixbuf_new(); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), renderer, FALSE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(widget), renderer, "pixbuf", LPC_ICON, NULL); renderer = gtk_cell_renderer_text_new(); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(widget), renderer, TRUE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(widget), renderer, "text", LPC_NAME, NULL); gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); return vbox; } static GtkWidget * _preferences_window_plugins(Locker * locker) { GtkWidget * vbox; GtkWidget * widget; GtkCellRenderer * renderer; GtkTreeViewColumn * column; vbox = gtk_vbox_new(FALSE, 0); widget = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(widget), GTK_SHADOW_ETCHED_IN); locker->pr_plstore = gtk_list_store_new(LPC_COUNT, G_TYPE_POINTER, G_TYPE_BOOLEAN, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING); gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE( locker->pr_plstore), LPC_NAME, GTK_SORT_ASCENDING); locker->pr_plview = gtk_tree_view_new_with_model(GTK_TREE_MODEL( locker->pr_plstore)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(locker->pr_plview), FALSE); gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(locker->pr_plview), TRUE); /* enabled */ renderer = gtk_cell_renderer_toggle_new(); g_signal_connect(renderer, "toggled", G_CALLBACK( _preferences_on_plugins_toggled), locker); column = gtk_tree_view_column_new_with_attributes(_("Enabled"), renderer, "active", LPC_ENABLED, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(locker->pr_plview), column); /* icon */ renderer = gtk_cell_renderer_pixbuf_new(); column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "pixbuf", LPC_ICON, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(locker->pr_plview), column); /* name */ renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer, "text", LPC_NAME, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(locker->pr_plview), column); gtk_container_add(GTK_CONTAINER(widget), locker->pr_plview); gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 0); return vbox; } static void _preferences_on_cancel(gpointer data) { Locker * locker = data; gtk_widget_hide(locker->pr_window); /* authentication */ _cancel_auth(locker, locker->pr_astore); /* demos */ _cancel_demo(locker, locker->pr_dstore); /* plug-ins */ _cancel_plugins(locker, locker->pr_plstore); } static void _cancel_auth(Locker * locker, GtkListStore * store) { GtkIconTheme * theme; GtkTreeIter iter; GdkPixbuf * icon; DIR * dir; struct dirent * de; size_t len; char const ext[] = ".so"; Plugin * p; LockerAuthDefinition * lad; theme = gtk_icon_theme_get_default(); gtk_list_store_clear(store); if((dir = opendir(LIBDIR "/" PACKAGE "/auth")) == NULL) return; while((de = readdir(dir)) != NULL) { if((len = strlen(de->d_name)) < sizeof(ext)) continue; if(strcmp(&de->d_name[len - sizeof(ext) + 1], ext) != 0) continue; de->d_name[len - sizeof(ext) + 1] = '\0'; #ifdef DEBUG fprintf(stderr, "DEBUG: %s() \"%s\"\n", __func__, de->d_name); #endif if((p = plugin_new(LIBDIR, PACKAGE, "auth", de->d_name)) == NULL) continue; if((lad = plugin_lookup(p, "plugin")) == NULL) { plugin_delete(p); continue; } gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, LPC_FILENAME, de->d_name, LPC_NAME, lad->name, -1); /* select if currently active */ if(locker->adefinition != NULL /* XXX check on de->d_name instead */ && strcmp(locker->adefinition->name, lad->name) == 0) gtk_combo_box_set_active_iter(GTK_COMBO_BOX( locker->pr_acombo), &iter); icon = NULL; if(lad->icon != NULL) icon = gtk_icon_theme_load_icon(theme, lad->icon, 24, 0, NULL); if(icon == NULL) icon = gtk_icon_theme_load_icon(theme, "gnome-settings", 24, 0, NULL); gtk_list_store_set(store, &iter, LPC_ICON, icon, -1); plugin_delete(p); } closedir(dir); } static void _cancel_demo(Locker * locker, GtkListStore * store) { GtkIconTheme * theme; GtkTreeIter iter; GdkPixbuf * icon; DIR * dir; struct dirent * de; size_t len; char const ext[] = ".so"; Plugin * p; LockerDemoDefinition * ldd; theme = gtk_icon_theme_get_default(); gtk_list_store_clear(store); gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, LPC_FILENAME, NULL, LPC_NAME, _("Disabled"), -1); if((dir = opendir(LIBDIR "/" PACKAGE "/demos")) == NULL) return; while((de = readdir(dir)) != NULL) { if((len = strlen(de->d_name)) < sizeof(ext)) continue; if(strcmp(&de->d_name[len - sizeof(ext) + 1], ext) != 0) continue; de->d_name[len - sizeof(ext) + 1] = '\0'; #ifdef DEBUG fprintf(stderr, "DEBUG: %s() \"%s\"\n", __func__, de->d_name); #endif if((p = plugin_new(LIBDIR, PACKAGE, "demos", de->d_name)) == NULL) continue; if((ldd = plugin_lookup(p, "plugin")) == NULL) { plugin_delete(p); continue; } gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, LPC_FILENAME, de->d_name, LPC_NAME, ldd->name, -1); /* select if currently active */ if(locker->ddefinition != NULL /* XXX check on de->d_name instead */ && strcmp(locker->ddefinition->name, ldd->name) == 0) gtk_combo_box_set_active_iter(GTK_COMBO_BOX( locker->pr_dcombo), &iter); icon = NULL; if(ldd->icon != NULL) icon = gtk_icon_theme_load_icon(theme, ldd->icon, 24, 0, NULL); if(icon == NULL) icon = gtk_icon_theme_load_icon(theme, "gnome-settings", 24, 0, NULL); gtk_list_store_set(store, &iter, LPC_ICON, icon, -1); plugin_delete(p); } closedir(dir); } static void _cancel_plugins(Locker * locker, GtkListStore * store) { GtkIconTheme * theme; GtkTreeIter iter; gboolean enabled; GdkPixbuf * icon; DIR * dir; struct dirent * de; size_t len; #ifdef __APPLE__ char const ext[] = ".dylib"; #else char const ext[] = ".so"; #endif Plugin * p; LockerPluginDefinition * lpd; gtk_list_store_clear(store); if((dir = opendir(LIBDIR "/" PACKAGE "/plugins")) == NULL) return; theme = gtk_icon_theme_get_default(); while((de = readdir(dir)) != NULL) { if((len = strlen(de->d_name)) < sizeof(ext)) continue; if(strcmp(&de->d_name[len - sizeof(ext) + 1], ext) != 0) continue; de->d_name[len - sizeof(ext) + 1] = '\0'; #ifdef DEBUG fprintf(stderr, "DEBUG: %s() \"%s\"\n", __func__, de->d_name); #endif if((p = plugin_new(LIBDIR, PACKAGE, "plugins", de->d_name)) == NULL) continue; if((lpd = plugin_lookup(p, "plugin")) == NULL) { plugin_delete(p); continue; } enabled = _locker_plugin_is_enabled(locker, de->d_name); icon = NULL; if(lpd->icon != NULL) icon = gtk_icon_theme_load_icon(theme, lpd->icon, 24, 0, NULL); if(icon == NULL) icon = gtk_icon_theme_load_icon(theme, "gnome-settings", 24, 0, NULL); gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, LPC_FILENAME, de->d_name, LPC_NAME, lpd->name, LPC_ENABLED, enabled, LPC_ICON, icon, -1); plugin_delete(p); } closedir(dir); } static gboolean _preferences_on_closex(gpointer data) { Locker * locker = data; gtk_widget_hide(locker->pr_window); return TRUE; } static void _preferences_on_ok(gpointer data) { Locker * locker = data; GtkTreeModel * model = GTK_TREE_MODEL(locker->pr_plstore); GtkTreeIter iter; gchar * p; gboolean valid; gboolean enabled; int res = 0; String * value = string_new(""); String * sep = ""; /* authentication */ p = NULL; if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(locker->pr_acombo), &iter)) gtk_tree_model_get(GTK_TREE_MODEL(locker->pr_astore), &iter, LPC_FILENAME, &p, -1); #ifdef DEBUG fprintf(stderr, "DEBUG: %s() auth=\"%s\"\n", __func__, p); #endif config_set(locker->config, NULL, "auth", p); g_free(p); /* demos */ p = NULL; if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(locker->pr_dcombo), &iter)) gtk_tree_model_get(GTK_TREE_MODEL(locker->pr_dstore), &iter, LPC_FILENAME, &p, -1); #ifdef DEBUG fprintf(stderr, "DEBUG: %s() demo=\"%s\"\n", __func__, p); #endif config_set(locker->config, NULL, "demo", p); g_free(p); /* plug-ins */ for(valid = gtk_tree_model_get_iter_first(model, &iter); valid == TRUE; valid = gtk_tree_model_iter_next(model, &iter)) { gtk_tree_model_get(model, &iter, LPC_FILENAME, &p, LPC_ENABLED, &enabled, -1); /* FIXME also save the configuration */ if(enabled) { _locker_plugin_load(locker, p); res |= string_append(&value, sep); res |= string_append(&value, p); sep = ","; } else _locker_plugin_unload(locker, p); g_free(p); } #ifdef DEBUG fprintf(stderr, "DEBUG: %s() value=\"%s\"\n", __func__, value); #endif if(res == 0 && config_set(locker->config, NULL, "plugins", value) == 0) _locker_config_save(locker); string_delete(value); _cancel_plugins(locker, locker->pr_plstore); /* XXX */ } static void _preferences_on_plugins_toggled(GtkCellRendererToggle * renderer, char * path, gpointer data) { Locker * locker = data; GtkTreeIter iter; gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(locker->pr_plstore), &iter, path); gtk_list_store_set(locker->pr_plstore, &iter, LPC_ENABLED, !gtk_cell_renderer_toggle_get_active(renderer), -1); } static void _preferences_on_response(GtkWidget * widget, gint response, gpointer data) { gtk_widget_hide(widget); if(response == GTK_RESPONSE_OK) _preferences_on_ok(data); else if(response == GTK_RESPONSE_CANCEL) _preferences_on_cancel(data); } /* private */ /* functions */ /* accessors */ /* locker_is_locked */ static int _locker_is_locked(Locker * locker) { return locker->locked; } /* locker_plugin_is_enabled */ static gboolean _locker_plugin_is_enabled(Locker * locker, char const * plugin) { size_t i; for(i = 0; i < locker->plugins_cnt; i++) if(strcmp(locker->plugins[i].name, plugin) == 0) return TRUE; return FALSE; } /* useful */ /* locker_about */ static gboolean _about_on_closex(gpointer data); static void _locker_about(Locker * locker) { GtkWidget * dialog; if(locker->ab_window != NULL) { gtk_window_present(GTK_WINDOW(locker->ab_window)); return; } dialog = desktop_about_dialog_new(); locker->ab_window = dialog; g_signal_connect_swapped(G_OBJECT(dialog), "delete-event", G_CALLBACK( _about_on_closex), locker); desktop_about_dialog_set_name(dialog, PACKAGE); desktop_about_dialog_set_version(dialog, VERSION); desktop_about_dialog_set_authors(dialog, _authors); desktop_about_dialog_set_comments(dialog, _("Screensaver for the DeforaOS desktop")); desktop_about_dialog_set_copyright(dialog, _copyright); desktop_about_dialog_set_logo_icon_name(dialog, "gnome-lockscreen"); desktop_about_dialog_set_license(dialog, _license); desktop_about_dialog_set_translator_credits(dialog, _("translator-credits")); desktop_about_dialog_set_website(dialog, "http://www.defora.org/"); gtk_widget_show(dialog); } static gboolean _about_on_closex(gpointer data) { Locker * locker = data; gtk_widget_hide(locker->ab_window); return TRUE; } /* actions */ /* locker_action */ static int _locker_action(Locker * locker, LockerAction action) { int ret = -1; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%u)\n", __func__, action); #endif switch(action) { case LOCKER_ACTION_ACTIVATE: ret = _locker_action_activate(locker, 1); break; case LOCKER_ACTION_DEACTIVATE: ret = _locker_action_deactivate(locker, 1); break; case LOCKER_ACTION_DISABLE: ret = _locker_action_disable(locker); break; case LOCKER_ACTION_ENABLE: ret = _locker_action_enable(locker); break; case LOCKER_ACTION_LOCK: ret = _locker_action_lock(locker, 1); break; case LOCKER_ACTION_SHOW_PREFERENCES: locker_show_preferences(locker, TRUE); ret = 0; break; case LOCKER_ACTION_START: ret = _locker_action_start(locker); break; case LOCKER_ACTION_STOP: ret = _locker_action_stop(locker); break; case LOCKER_ACTION_SUSPEND: ret = _locker_action_suspend(locker); break; case LOCKER_ACTION_UNLOCK: ret = _locker_action_unlock(locker); break; } return ret; } /* locker_action_activate */ static int _locker_action_activate(Locker * locker, int force) { size_t i; #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif if(_locker_event(locker, LOCKER_EVENT_ACTIVATING) != 0 && force == 0) return -1; if(locker->adefinition->action(locker->auth, LOCKER_ACTION_ACTIVATE) != 0) return -1; for(i = 0; i < locker->windows_cnt; i++) { gtk_widget_show(locker->windows[i]); gtk_window_fullscreen(GTK_WINDOW(locker->windows[i])); } _locker_action_start(locker); _locker_event(locker, LOCKER_EVENT_ACTIVATED); return 0; } /* locker_action_deactivate */ static int _locker_action_deactivate(Locker * locker, int reset) { #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif if(_locker_event(locker, LOCKER_EVENT_DEACTIVATING) != 0) return -1; if(locker->adefinition->action(locker->auth, LOCKER_ACTION_DEACTIVATE) != 0) return -1; _locker_action_stop(locker); _locker_event(locker, LOCKER_EVENT_DEACTIVATED); if(reset != 0) XResetScreenSaver(GDK_DISPLAY_XDISPLAY(locker->display)); return 0; } /* locker_action_disable */ static int _locker_action_disable(Locker * locker) { #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif if(locker->locked) return 0; locker->enabled = 0; return 0; } /* locker_action_enable */ static int _locker_action_enable(Locker * locker) { #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif locker->enabled = 1; return 0; } /* locker_action_lock */ static int _locker_action_lock(Locker * locker, int force) { int ret; #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif if(force == 0 && _locker_event(locker, LOCKER_EVENT_LOCKING) != 0) return -1; locker->locked = 1; _locker_action_activate(locker, 1); ret = locker->adefinition->action(locker->auth, LOCKER_ACTION_LOCK); _locker_event(locker, LOCKER_EVENT_LOCKED); return ret; } /* locker_action_start */ static int _locker_action_start(Locker * locker) { #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif XActivateScreenSaver(GDK_DISPLAY_XDISPLAY(locker->display)); if(locker->ddefinition != NULL && locker->ddefinition->start != NULL) locker->ddefinition->start(locker->demo); return 0; } /* locker_action_stop */ static int _locker_action_stop(Locker * locker) { GdkWindow * window; #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif if(locker->ddefinition != NULL && locker->ddefinition->stop != NULL) locker->ddefinition->stop(locker->demo); #if GTK_CHECK_VERSION(2, 14, 0) if(locker->windows[0] != NULL) { window = gtk_widget_get_window(locker->windows[0]); gdk_window_clear(window); gdk_window_invalidate_rect(window, NULL, TRUE); } #endif return 0; } /* locker_action_suspend */ static int _locker_action_suspend(Locker * locker) { #if defined(__NetBSD__) int sleep_state = 3; #elif defined(__FreeBSD__) char * suspend[] = { PREFIX "/bin/sudo", "sudo", "/usr/sbin/zzz", NULL }; GError * error = NULL; #else int fd; char * suspend[] = { "/usr/bin/sudo", "sudo", "/usr/bin/apm", "-s", NULL }; GError * error = NULL; #endif #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif if(_locker_event(locker, LOCKER_EVENT_SUSPENDING) != 0) return -1; /* automatically lock the screen when suspending */ if(_locker_action_lock(locker, 1) != 0) return -1; /* suspend immediately */ #if defined(__NetBSD__) if(sysctlbyname("machdep.sleep_state", NULL, NULL, &sleep_state, sizeof(sleep_state)) != 0) { _locker_error(locker, strerror(errno), 1); return -1; } #elif defined(__FreeBSD__) if(g_spawn_async(NULL, suspend, NULL, G_SPAWN_FILE_AND_ARGV_ZERO, NULL, NULL, NULL, &error) != TRUE) { _locker_error(locker, error->message, 1); g_error_free(error); return -1; } #else /* XXX this assumes Linux with sysfs or APM configured */ if((fd = open("/sys/power/state", O_WRONLY)) >= 0) { write(fd, "mem\n", 4); close(fd); return 0; } if(g_spawn_async(NULL, suspend, NULL, G_SPAWN_FILE_AND_ARGV_ZERO, NULL, NULL, NULL, &error) != TRUE) { _locker_error(locker, error->message, 1); g_error_free(error); return -1; } #endif return 0; } /* locker_action_unlock */ static int _locker_action_unlock(Locker * locker) { size_t i; #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif if(_locker_event(locker, LOCKER_EVENT_UNLOCKING) != 0) return -1; if(locker->adefinition->action(locker->auth, LOCKER_ACTION_UNLOCK) != 0) return -1; _locker_event(locker, LOCKER_EVENT_UNLOCKED); locker->locked = 0; _locker_action_deactivate(locker, 1); /* ungrab keyboard and mouse */ if(locker->windows == NULL) return 0; gdk_keyboard_ungrab(GDK_CURRENT_TIME); gdk_pointer_ungrab(GDK_CURRENT_TIME); for(i = 0; i < locker->windows_cnt; i++) gtk_widget_hide(locker->windows[i]); return 0; } /* locker_auth_config_get */ static char const * _locker_auth_config_get(Locker * locker, char const * section, char const * variable) { char const * ret; String * s; if((s = string_new_append("auth::", section, NULL)) == NULL) return NULL; ret = config_get(locker->config, s, variable); string_delete(s); return ret; } /* locker_auth_config_set */ static int _locker_auth_config_set(Locker * locker, char const * section, char const * variable, char const * value) { int ret; String * s; if((s = string_new_append("auth::", section, NULL)) == NULL) return -1; ret = config_set(locker->config, s, variable, value); string_delete(s); if(ret == 0) ret = _locker_config_save(locker); return ret; } /* locker_config_load */ static int _locker_config_load(Locker * locker) { int ret; char const * homedir; char * filename; if((homedir = getenv("HOME")) == NULL) homedir = g_get_home_dir(); if((filename = malloc(strlen(homedir) + sizeof(LOCKER_CONFIG_FILE) + 1)) == NULL) return -1; sprintf(filename, "%s/%s", homedir, LOCKER_CONFIG_FILE); if((ret = config_load(locker->config, filename)) != 0) _locker_error(NULL, error_get(), ret); free(filename); return ret; } /* locker_config_save */ static int _locker_config_save(Locker * locker) { int ret; char const * homedir; char * filename; if((homedir = getenv("HOME")) == NULL) homedir = g_get_home_dir(); if((filename = malloc(strlen(homedir) + sizeof(LOCKER_CONFIG_FILE) + 1)) == NULL) return -1; sprintf(filename, "%s/%s", homedir, LOCKER_CONFIG_FILE); if((ret = config_save(locker->config, filename)) != 0) _locker_error(NULL, error_get(), 1); free(filename); return ret; } /* locker_demo_config_get */ static char const * _locker_demo_config_get(Locker * locker, char const * section, char const * variable) { char const * ret; String * s; if((s = string_new_append("demo::", section, NULL)) == NULL) return NULL; ret = config_get(locker->config, s, variable); string_delete(s); return ret; } /* locker_demo_config_set */ static int _locker_demo_config_set(Locker * locker, char const * section, char const * variable, char const * value) { int ret; String * s; if((s = string_new_append("demo::", section, NULL)) == NULL) return -1; ret = config_set(locker->config, s, variable, value); string_delete(s); if(ret == 0) ret = _locker_config_save(locker); return ret; } /* locker_error */ static int _locker_error(Locker * locker, char const * message, int ret) { GtkWidget * dialog; if(locker == NULL || _locker_is_locked(locker)) { fprintf(stderr, "%s: %s\n", "locker", message); return ret; } dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, #if GTK_CHECK_VERSION(2, 6, 0) "%s", _("Error")); gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), #endif "%s", message); gtk_window_set_title(GTK_WINDOW(dialog), _("Error")); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); return ret; } /* locker_event */ static int _locker_event(Locker * locker, LockerEvent event) { int ret = 0; size_t i; LockerPluginDefinition * lpd; LockerPlugin * lp; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%u)\n", __func__, event); #endif for(i = 0; i < locker->plugins_cnt; i++) { lpd = locker->plugins[i].definition; lp = locker->plugins[i].plugin; if(lpd->event != NULL) ret |= lpd->event(lp, event); } return (ret == 0) ? 0 : -1; } /* plug-ins */ /* locker_plugin_config_get */ static char const * _locker_plugin_config_get(Locker * locker, char const * section, char const * variable) { char const * ret; String * s; if((s = string_new_append("plugin::", section, NULL)) == NULL) return NULL; ret = config_get(locker->config, s, variable); string_delete(s); return ret; } /* locker_plugin_config_set */ static int _locker_plugin_config_set(Locker * locker, char const * section, char const * variable, char const * value) { int ret; String * s; if((s = string_new_append("plugin::", section, NULL)) == NULL) return -1; ret = config_set(locker->config, s, variable, value); string_delete(s); if(ret == 0) ret = _locker_config_save(locker); return ret; } /* locker_plugin_load */ static int _locker_plugin_load(Locker * locker, char const * plugin) { LockerPlugins * p; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, plugin); #endif if(_locker_plugin_is_enabled(locker, plugin)) return 0; if((p = realloc(locker->plugins, sizeof(*p) * (locker->plugins_cnt + 1))) == NULL) return _locker_error(NULL, strerror(errno), 1); locker->plugins = p; p = &locker->plugins[locker->plugins_cnt]; if((p->pplugin = plugin_new(LIBDIR, PACKAGE, "plugins", plugin)) == NULL) return _locker_error(NULL, error_get(), 1); if((p->definition = plugin_lookup(p->pplugin, "plugin")) == NULL) { plugin_delete(p->pplugin); return _locker_error(NULL, error_get(), 1); } p->name = strdup(plugin); if(p->definition->init == NULL || p->definition->destroy == NULL || (p->plugin = p->definition->init(&locker->phelper)) == NULL) { free(p->name); plugin_delete(p->pplugin); return _locker_error(NULL, error_get(), 1); } locker->plugins_cnt++; return 0; } /* locker_plugin_unload */ static int _locker_plugin_unload(Locker * locker, char const * plugin) { size_t i; LockerPlugins * lp; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__, plugin); #endif for(i = 0; i < locker->plugins_cnt; i++) if(strcmp(locker->plugins[i].name, plugin) == 0) break; if(i == locker->plugins_cnt) /* this plug-in is not loaded */ return 0; /* unload the plug-in */ lp = &locker->plugins[i]; if(lp->definition->destroy != NULL) lp->definition->destroy(lp->plugin); plugin_delete(lp->pplugin); free(lp->name); memmove(lp, &lp[1], sizeof(*lp) * (--locker->plugins_cnt - i)); /* FIXME should call realloc() to gain some memory */ return 0; } /* callbacks */ /* locker_on_closex */ static gboolean _lock_on_closex(void) { return TRUE; } /* locker_on_filter */ static GdkFilterReturn _filter_xscreensaver_notify(Locker * locker, XScreenSaverNotifyEvent * xssne); static GdkFilterReturn _locker_on_filter(GdkXEvent * xevent, GdkEvent * event, gpointer data) { Locker * locker = data; XEvent * xev = xevent; #ifdef DEBUG fprintf(stderr, "DEBUG: %s() 0x%x 0x%x\n", __func__, xev->type, event->type); #endif if(xev->type == locker->event) return _filter_xscreensaver_notify(locker, xevent); else return GDK_FILTER_CONTINUE; } static GdkFilterReturn _filter_xscreensaver_notify(Locker * locker, XScreenSaverNotifyEvent * xssne) { #ifdef DEBUG fprintf(stderr, "DEBUG: %s() %u\n", __func__, xssne->state); #endif switch(xssne->state) { case ScreenSaverOff: _locker_action_enable(locker); _locker_action_deactivate(locker, 0); break; case ScreenSaverOn: if(locker->enabled && locker->locked == 0) _locker_action_lock(locker, 0); break; case ScreenSaverDisabled: _locker_action_disable(locker); break; } return GDK_FILTER_CONTINUE; } /* locker_on_map_event */ static gboolean _locker_on_map_event(gpointer data) { Locker * locker = data; GdkWindow * window; GdkGrabStatus status; /* FIXME detect if this is the first window */ /* FIXME the mouse may already be grabbed (Panel's lock button...) */ /* grab keyboard and mouse */ #if GTK_CHECK_VERSION(2, 14, 0) window = gtk_widget_get_window(locker->windows[0]); #else window = locker->windows[0]->window; #endif if(window == NULL) _locker_error(NULL, "Failed to grab input", 1); else { if((status = gdk_keyboard_grab(window, TRUE, GDK_CURRENT_TIME)) != GDK_GRAB_SUCCESS) _locker_error(NULL, "Failed to grab keyboard", 1); #ifdef DEBUG fprintf(stderr, "DEBUG: keyboard grab status=%u\n", status); #endif if((status = gdk_pointer_grab(window, TRUE, 0, window, NULL, GDK_CURRENT_TIME)) != GDK_GRAB_SUCCESS) _locker_error(NULL, "Failed to grab mouse", 1); #ifdef DEBUG fprintf(stderr, "DEBUG: mouse grab status=%u\n", status); #endif } return FALSE; } /* locker_on_message */ static int _locker_on_message(void * data, uint32_t value1, uint32_t value2, uint32_t value3) { Locker * locker = data; LockerAction action; gboolean show; #ifdef DEBUG fprintf(stderr, "DEBUG: %s(%u, %u, %u)\n", __func__, value1, value2, value3); #endif if(value1 != LOCKER_MESSAGE_ACTION) return 0; switch((action = value2)) { case LOCKER_ACTION_SHOW_PREFERENCES: show = value3 ? TRUE : FALSE; locker_show_preferences(locker, show); break; default: _locker_action(locker, action); break; } return 0; } /* locker_on_realize */ static void _locker_on_realize(GtkWidget * widget, gpointer data) { Locker * locker = data; GdkWindow * window; if(locker->ddefinition != NULL && locker->ddefinition->add != NULL) { #if GTK_CHECK_VERSION(2, 14, 0) window = gtk_widget_get_window(widget); #else window = widget->window; #endif locker->ddefinition->add(locker->demo, window); } }