summaryrefslogtreecommitdiff
path: root/src/gs-window-x11.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gs-window-x11.c')
-rw-r--r--src/gs-window-x11.c2647
1 files changed, 2647 insertions, 0 deletions
diff --git a/src/gs-window-x11.c b/src/gs-window-x11.c
new file mode 100644
index 0000000..2e6f0f7
--- /dev/null
+++ b/src/gs-window-x11.c
@@ -0,0 +1,2647 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2008 William Jon McCann <[email protected]>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <string.h>
+
+#include <gdk/gdkx.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+#include "gs-window.h"
+#include "gs-marshal.h"
+#include "subprocs.h"
+#include "gs-debug.h"
+
+#ifdef HAVE_SHAPE_EXT
+#include <X11/extensions/shape.h>
+#endif
+
+static void gs_window_class_init (GSWindowClass *klass);
+static void gs_window_init (GSWindow *window);
+static void gs_window_finalize (GObject *object);
+
+static gboolean popup_dialog_idle (GSWindow *window);
+static void gs_window_dialog_finish (GSWindow *window);
+static void remove_command_watches (GSWindow *window);
+
+enum
+{
+ DIALOG_RESPONSE_CANCEL,
+ DIALOG_RESPONSE_OK
+};
+
+#define MAX_QUEUED_EVENTS 16
+#define INFO_BAR_SECONDS 30
+
+#define GS_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_WINDOW, GSWindowPrivate))
+
+struct GSWindowPrivate
+{
+ int monitor;
+
+ GdkRectangle geometry;
+ guint obscured : 1;
+ guint dialog_up : 1;
+
+ guint lock_enabled : 1;
+ guint user_switch_enabled : 1;
+ guint logout_enabled : 1;
+ guint keyboard_enabled : 1;
+
+ guint64 logout_timeout;
+ char *logout_command;
+ char *keyboard_command;
+ char *status_message;
+
+ GtkWidget *vbox;
+ GtkWidget *drawing_area;
+ GtkWidget *lock_box;
+ GtkWidget *lock_socket;
+ GtkWidget *keyboard_socket;
+ GtkWidget *info_bar;
+ GtkWidget *info_content;
+
+ GdkPixmap *background_pixmap;
+
+ guint popup_dialog_idle_id;
+
+ guint dialog_map_signal_id;
+ guint dialog_unmap_signal_id;
+ guint dialog_response_signal_id;
+
+ guint watchdog_timer_id;
+ guint info_bar_timer_id;
+
+ gint lock_pid;
+ gint lock_watch_id;
+ gint dialog_response;
+ gboolean dialog_quit_requested;
+ gboolean dialog_shake_in_progress;
+
+ gint keyboard_pid;
+ gint keyboard_watch_id;
+
+ GList *key_events;
+
+ gdouble last_x;
+ gdouble last_y;
+
+ GTimer *timer;
+
+#ifdef HAVE_SHAPE_EXT
+ int shape_event_base;
+#endif
+};
+
+enum
+{
+ ACTIVITY,
+ DEACTIVATED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_OBSCURED,
+ PROP_DIALOG_UP,
+ PROP_LOCK_ENABLED,
+ PROP_LOGOUT_ENABLED,
+ PROP_KEYBOARD_ENABLED,
+ PROP_KEYBOARD_COMMAND,
+ PROP_LOGOUT_COMMAND,
+ PROP_LOGOUT_TIMEOUT,
+ PROP_MONITOR,
+ PROP_STATUS_MESSAGE
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GSWindow, gs_window, GTK_TYPE_WINDOW)
+
+static void
+set_invisible_cursor (GdkWindow *window,
+ gboolean invisible)
+{
+ GdkBitmap *empty_bitmap;
+ GdkCursor *cursor = NULL;
+ GdkColor useless;
+ char invisible_cursor_bits [] = { 0x0 };
+
+ if (invisible)
+ {
+ useless.red = useless.green = useless.blue = 0;
+ useless.pixel = 0;
+
+ empty_bitmap = gdk_bitmap_create_from_data (window,
+ invisible_cursor_bits,
+ 1, 1);
+
+ cursor = gdk_cursor_new_from_pixmap (empty_bitmap,
+ empty_bitmap,
+ &useless,
+ &useless, 0, 0);
+
+ g_object_unref (empty_bitmap);
+ }
+
+ gdk_window_set_cursor (window, cursor);
+
+ if (cursor)
+ {
+ gdk_cursor_unref (cursor);
+ }
+}
+
+/* derived from tomboy */
+static void
+gs_window_override_user_time (GSWindow *window)
+{
+ guint32 ev_time = gtk_get_current_event_time ();
+
+ if (ev_time == 0)
+ {
+ gint ev_mask = gtk_widget_get_events (GTK_WIDGET (window));
+ if (!(ev_mask & GDK_PROPERTY_CHANGE_MASK))
+ {
+ gtk_widget_add_events (GTK_WIDGET (window),
+ GDK_PROPERTY_CHANGE_MASK);
+ }
+
+ /*
+ * NOTE: Last resort for D-BUS or other non-interactive
+ * openings. Causes roundtrip to server. Lame.
+ */
+ ev_time = gdk_x11_get_server_time (GTK_WIDGET (window)->window);
+ }
+
+ gdk_x11_window_set_user_time (GTK_WIDGET (window)->window, ev_time);
+}
+
+static void
+force_no_pixmap_background (GtkWidget *widget)
+{
+ static gboolean first_time = TRUE;
+
+ if (first_time)
+ {
+ gtk_rc_parse_string ("\n"
+ " style \"gs-theme-engine-style\"\n"
+ " {\n"
+ " bg_pixmap[NORMAL] = \"<none>\"\n"
+ " bg_pixmap[INSENSITIVE] = \"<none>\"\n"
+ " bg_pixmap[ACTIVE] = \"<none>\"\n"
+ " bg_pixmap[PRELIGHT] = \"<none>\"\n"
+ " bg[NORMAL] = { 0.0, 0.0, 0.0 }\n"
+ " bg[INSENSITIVE] = { 0.0, 0.0, 0.0 }\n"
+ " bg[ACTIVE] = { 0.0, 0.0, 0.0 }\n"
+ " bg[PRELIGHT] = { 0.0, 0.0, 0.0 }\n"
+ " }\n"
+ " widget \"gs-window-drawing-area*\" style : highest \"gs-theme-engine-style\"\n"
+ "\n");
+ first_time = FALSE;
+ }
+
+ gtk_widget_set_name (widget, "gs-window-drawing-area");
+}
+
+static void
+clear_children (Window window)
+{
+ Window root;
+ Window parent;
+ Window *children;
+ unsigned int n_children;
+ int status;
+
+ children = NULL;
+ status = XQueryTree (GDK_DISPLAY (), window, &root, &parent, &children, &n_children);
+
+ if (status == 0)
+ {
+ if (children)
+ {
+ XFree (children);
+ }
+ return;
+ }
+
+ if (children)
+ {
+ while (n_children)
+ {
+ Window child;
+
+ child = children [--n_children];
+
+ XClearWindow (GDK_DISPLAY (), child);
+ clear_children (child);
+ }
+
+ XFree (children);
+ }
+}
+
+static void
+widget_clear_all_children (GtkWidget *widget)
+{
+ GdkWindow *w;
+
+ gs_debug ("Clearing all child windows");
+
+ gdk_error_trap_push ();
+
+ w = widget->window;
+
+ clear_children (GDK_WINDOW_XID (w));
+
+ gdk_display_sync (gtk_widget_get_display (widget));
+ gdk_error_trap_pop ();
+}
+
+void
+gs_window_set_background_pixmap (GSWindow *window,
+ GdkPixmap *pixmap)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ if (window->priv->background_pixmap != NULL)
+ {
+ g_object_unref (window->priv->background_pixmap);
+ }
+
+ if (pixmap != NULL)
+ {
+ window->priv->background_pixmap = g_object_ref (pixmap);
+ gdk_window_set_back_pixmap (GTK_WIDGET (window)->window,
+ pixmap,
+ FALSE);
+ }
+}
+
+static void
+gs_window_clear_to_background_pixmap (GSWindow *window)
+{
+ GtkStateType state;
+ GtkStyle *style;
+
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ if (! GTK_WIDGET_VISIBLE (GTK_WIDGET (window)))
+ {
+ return;
+ }
+
+ if (window->priv->background_pixmap == NULL)
+ {
+ /* don't allow null pixmaps */
+ return;
+ }
+
+ gs_debug ("Clearing window to background pixmap");
+
+ style = gtk_style_copy (GTK_WIDGET (window)->style);
+
+ state = (GtkStateType) 0;
+ while (state < (GtkStateType) G_N_ELEMENTS (GTK_WIDGET (window)->style->bg_pixmap))
+ {
+
+ if (style->bg_pixmap[state] != NULL)
+ {
+ g_object_unref (style->bg_pixmap[state]);
+ }
+
+ style->bg_pixmap[state] = g_object_ref (window->priv->background_pixmap);
+ state++;
+ }
+
+ gtk_widget_set_style (GTK_WIDGET (window), style);
+ g_object_unref (style);
+
+ if (window->priv->background_pixmap != NULL)
+ {
+ gdk_window_set_back_pixmap (GTK_WIDGET (window)->window,
+ window->priv->background_pixmap,
+ FALSE);
+ }
+
+ gdk_window_clear (GTK_WIDGET (window)->window);
+
+ gdk_flush ();
+}
+
+static void
+clear_widget (GtkWidget *widget)
+{
+ GdkColor color = { 0, 0x0000, 0x0000, 0x0000 };
+ GdkColormap *colormap;
+ GtkStateType state;
+ GtkStyle *style;
+
+ if (! GTK_WIDGET_VISIBLE (widget))
+ {
+ return;
+ }
+
+ gs_debug ("Clearing widget");
+
+ state = (GtkStateType) 0;
+ while (state < (GtkStateType) G_N_ELEMENTS (widget->style->bg))
+ {
+ gtk_widget_modify_bg (widget, state, &color);
+ state++;
+ }
+
+ style = gtk_style_copy (widget->style);
+
+ state = (GtkStateType) 0;
+ while (state < (GtkStateType) G_N_ELEMENTS (widget->style->bg_pixmap))
+ {
+
+ if (style->bg_pixmap[state] != NULL)
+ {
+ g_object_unref (style->bg_pixmap[state]);
+ }
+
+ style->bg_pixmap[state] = NULL;
+ state++;
+ }
+
+ colormap = gdk_drawable_get_colormap (widget->window);
+ gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
+ gdk_window_set_background (widget->window, &color);
+
+ gtk_widget_set_style (widget, style);
+ g_object_unref (style);
+
+ gdk_window_clear (widget->window);
+
+ /* If a screensaver theme adds child windows we need to clear them too */
+ widget_clear_all_children (widget);
+
+ gdk_flush ();
+}
+
+void
+gs_window_clear (GSWindow *window)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ clear_widget (GTK_WIDGET (window));
+ clear_widget (window->priv->drawing_area);
+}
+
+static GdkRegion *
+get_outside_region (GSWindow *window)
+{
+ int i;
+ GdkRegion *region;
+
+ region = gdk_region_new ();
+ for (i = 0; i < window->priv->monitor; i++)
+ {
+ GdkRectangle geometry;
+
+ gdk_screen_get_monitor_geometry (GTK_WINDOW (window)->screen,
+ i, &geometry);
+ gdk_region_union_with_rect (region, &geometry);
+ }
+
+ return region;
+}
+
+static void
+update_geometry (GSWindow *window)
+{
+ GdkRectangle geometry;
+ GdkRegion *outside_region;
+ GdkRegion *monitor_region;
+
+ outside_region = get_outside_region (window);
+
+ gdk_screen_get_monitor_geometry (GTK_WINDOW (window)->screen,
+ window->priv->monitor,
+ &geometry);
+ gs_debug ("got geometry for monitor %d: x=%d y=%d w=%d h=%d",
+ window->priv->monitor,
+ geometry.x,
+ geometry.y,
+ geometry.width,
+ geometry.height);
+ monitor_region = gdk_region_rectangle (&geometry);
+ gdk_region_subtract (monitor_region, outside_region);
+ gdk_region_destroy (outside_region);
+
+ gdk_region_get_clipbox (monitor_region, &geometry);
+ gdk_region_destroy (monitor_region);
+
+ gs_debug ("using geometry for monitor %d: x=%d y=%d w=%d h=%d",
+ window->priv->monitor,
+ geometry.x,
+ geometry.y,
+ geometry.width,
+ geometry.height);
+
+ window->priv->geometry.x = geometry.x;
+ window->priv->geometry.y = geometry.y;
+ window->priv->geometry.width = geometry.width;
+ window->priv->geometry.height = geometry.height;
+}
+
+static void
+screen_size_changed (GdkScreen *screen,
+ GSWindow *window)
+{
+ gs_debug ("Got screen size changed signal");
+ gtk_widget_queue_resize (GTK_WIDGET (window));
+}
+
+/* copied from panel-toplevel.c */
+static void
+gs_window_move_resize_window (GSWindow *window,
+ gboolean move,
+ gboolean resize)
+{
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (window);
+
+ g_assert (GTK_WIDGET_REALIZED (widget));
+
+ gs_debug ("Move and/or resize window on monitor %d: x=%d y=%d w=%d h=%d",
+ window->priv->monitor,
+ window->priv->geometry.x,
+ window->priv->geometry.y,
+ window->priv->geometry.width,
+ window->priv->geometry.height);
+
+ if (move && resize)
+ {
+ gdk_window_move_resize (widget->window,
+ window->priv->geometry.x,
+ window->priv->geometry.y,
+ window->priv->geometry.width,
+ window->priv->geometry.height);
+ }
+ else if (move)
+ {
+ gdk_window_move (widget->window,
+ window->priv->geometry.x,
+ window->priv->geometry.y);
+ }
+ else if (resize)
+ {
+ gdk_window_resize (widget->window,
+ window->priv->geometry.width,
+ window->priv->geometry.height);
+ }
+}
+
+static void
+gs_window_real_unrealize (GtkWidget *widget)
+{
+ g_signal_handlers_disconnect_by_func (gtk_window_get_screen (GTK_WINDOW (widget)),
+ screen_size_changed,
+ widget);
+
+ if (GTK_WIDGET_CLASS (gs_window_parent_class)->unrealize)
+ {
+ GTK_WIDGET_CLASS (gs_window_parent_class)->unrealize (widget);
+ }
+}
+
+/* copied from gdk */
+extern char **environ;
+
+static gchar **
+spawn_make_environment_for_screen (GdkScreen *screen,
+ gchar **envp)
+{
+ gchar **retval = NULL;
+ gchar *display_name;
+ gint display_index = -1;
+ gint i, env_len;
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+
+ if (envp == NULL)
+ envp = environ;
+
+ for (env_len = 0; envp[env_len]; env_len++)
+ if (strncmp (envp[env_len], "DISPLAY", strlen ("DISPLAY")) == 0)
+ display_index = env_len;
+
+ retval = g_new (char *, env_len + 1);
+ retval[env_len] = NULL;
+
+ display_name = gdk_screen_make_display_name (screen);
+
+ for (i = 0; i < env_len; i++)
+ if (i == display_index)
+ retval[i] = g_strconcat ("DISPLAY=", display_name, NULL);
+ else
+ retval[i] = g_strdup (envp[i]);
+
+ g_assert (i == env_len);
+
+ g_free (display_name);
+
+ return retval;
+}
+
+static gboolean
+spawn_command_line_on_screen_sync (GdkScreen *screen,
+ const gchar *command_line,
+ char **standard_output,
+ char **standard_error,
+ int *exit_status,
+ GError **error)
+{
+ char **argv = NULL;
+ char **envp = NULL;
+ gboolean retval;
+
+ g_return_val_if_fail (command_line != NULL, FALSE);
+
+ if (! g_shell_parse_argv (command_line, NULL, &argv, error))
+ {
+ return FALSE;
+ }
+
+ envp = spawn_make_environment_for_screen (screen, NULL);
+
+ retval = g_spawn_sync (NULL,
+ argv,
+ envp,
+ G_SPAWN_SEARCH_PATH,
+ NULL,
+ NULL,
+ standard_output,
+ standard_error,
+ exit_status,
+ error);
+
+ g_strfreev (argv);
+ g_strfreev (envp);
+
+ return retval;
+}
+
+static GdkVisual *
+get_best_visual_for_screen (GdkScreen *screen)
+{
+ char *command;
+ char *std_output;
+ int exit_status;
+ GError *error;
+ unsigned long v;
+ char c;
+ GdkVisual *visual;
+ gboolean res;
+
+ visual = NULL;
+
+ command = g_build_filename (LIBEXECDIR, "mate-screensaver-gl-helper", NULL);
+
+ error = NULL;
+ std_output = NULL;
+ res = spawn_command_line_on_screen_sync (screen,
+ command,
+ &std_output,
+ NULL,
+ &exit_status,
+ &error);
+ if (! res)
+ {
+ gs_debug ("Could not run command '%s': %s", command, error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (1 == sscanf (std_output, "0x%lx %c", &v, &c))
+ {
+ if (v != 0)
+ {
+ VisualID visual_id;
+
+ visual_id = (VisualID) v;
+ visual = gdkx_visual_get (visual_id);
+
+ gs_debug ("Found best GL visual for screen %d: 0x%x",
+ gdk_screen_get_number (screen),
+ (unsigned int) visual_id);
+ }
+ }
+out:
+ g_free (std_output);
+ g_free (command);
+
+ return visual;
+}
+
+static GdkColormap *
+get_best_colormap_for_screen (GdkScreen *screen)
+{
+ GdkColormap *colormap;
+ GdkVisual *visual;
+
+ g_return_val_if_fail (screen != NULL, NULL);
+
+ visual = get_best_visual_for_screen (screen);
+
+ colormap = NULL;
+ if (visual != NULL)
+ {
+ colormap = gdk_colormap_new (visual, FALSE);
+ }
+
+ return colormap;
+}
+
+static void
+widget_set_best_colormap (GtkWidget *widget)
+{
+ GdkColormap *colormap;
+
+ g_return_if_fail (widget != NULL);
+
+ colormap = get_best_colormap_for_screen (gtk_widget_get_screen (widget));
+ if (colormap != NULL)
+ {
+ gtk_widget_set_colormap (widget, colormap);
+ g_object_unref (colormap);
+ }
+}
+
+static void
+gs_window_real_realize (GtkWidget *widget)
+{
+ widget_set_best_colormap (widget);
+
+ if (GTK_WIDGET_CLASS (gs_window_parent_class)->realize)
+ {
+ GTK_WIDGET_CLASS (gs_window_parent_class)->realize (widget);
+ }
+
+ gs_window_override_user_time (GS_WINDOW (widget));
+
+ gs_window_move_resize_window (GS_WINDOW (widget), TRUE, TRUE);
+
+ g_signal_connect (gtk_window_get_screen (GTK_WINDOW (widget)),
+ "size_changed",
+ G_CALLBACK (screen_size_changed),
+ widget);
+}
+
+/* every so often we should raise the window in case
+ another window has somehow gotten on top */
+static gboolean
+watchdog_timer (GSWindow *window)
+{
+ GtkWidget *widget = GTK_WIDGET (window);
+
+ gdk_window_focus (widget->window, GDK_CURRENT_TIME);
+
+ return TRUE;
+}
+
+static void
+remove_watchdog_timer (GSWindow *window)
+{
+ if (window->priv->watchdog_timer_id != 0)
+ {
+ g_source_remove (window->priv->watchdog_timer_id);
+ window->priv->watchdog_timer_id = 0;
+ }
+}
+
+static void
+add_watchdog_timer (GSWindow *window,
+ glong timeout)
+{
+ window->priv->watchdog_timer_id = g_timeout_add (timeout,
+ (GSourceFunc)watchdog_timer,
+ window);
+}
+
+static void
+remove_popup_dialog_idle (GSWindow *window)
+{
+ if (window->priv->popup_dialog_idle_id != 0)
+ {
+ g_source_remove (window->priv->popup_dialog_idle_id);
+ window->priv->popup_dialog_idle_id = 0;
+ }
+}
+
+static void
+add_popup_dialog_idle (GSWindow *window)
+{
+ window->priv->popup_dialog_idle_id = g_idle_add ((GSourceFunc)popup_dialog_idle, window);
+}
+
+static gboolean
+emit_deactivated_idle (GSWindow *window)
+{
+ g_signal_emit (window, signals [DEACTIVATED], 0);
+
+ return FALSE;
+}
+
+static void
+add_emit_deactivated_idle (GSWindow *window)
+{
+ g_idle_add ((GSourceFunc)emit_deactivated_idle, window);
+}
+
+static void
+gs_window_raise (GSWindow *window)
+{
+ GdkWindow *win;
+
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ gs_debug ("Raising screensaver window");
+
+ win = GTK_WIDGET (window)->window;
+
+ gdk_window_raise (win);
+}
+
+static gboolean
+x11_window_is_ours (Window window)
+{
+ GdkWindow *gwindow;
+ gboolean ret;
+
+ ret = FALSE;
+
+ gwindow = gdk_window_lookup (window);
+ if (gwindow && (window != GDK_ROOT_WINDOW ()))
+ {
+ ret = TRUE;
+ }
+
+ return ret;
+}
+
+#ifdef HAVE_SHAPE_EXT
+static void
+unshape_window (GSWindow *window)
+{
+ gdk_window_shape_combine_region (GTK_WIDGET (window)->window,
+ NULL,
+ 0,
+ 0);
+}
+#endif
+
+static void
+gs_window_xevent (GSWindow *window,
+ GdkXEvent *xevent)
+{
+ XEvent *ev;
+
+ ev = xevent;
+
+ /* MapNotify is used to tell us when new windows are mapped.
+ ConfigureNofify is used to tell us when windows are raised. */
+ switch (ev->xany.type)
+ {
+ case MapNotify:
+ {
+ XMapEvent *xme = &ev->xmap;
+
+ if (! x11_window_is_ours (xme->window))
+ {
+ gs_window_raise (window);
+ }
+ else
+ {
+ gs_debug ("not raising our windows");
+ }
+
+ break;
+ }
+ case ConfigureNotify:
+ {
+ XConfigureEvent *xce = &ev->xconfigure;
+
+ if (! x11_window_is_ours (xce->window))
+ {
+ gs_window_raise (window);
+ }
+ else
+ {
+ gs_debug ("not raising our windows");
+ }
+
+ break;
+ }
+ default:
+ /* extension events */
+#ifdef HAVE_SHAPE_EXT
+ if (ev->xany.type == (window->priv->shape_event_base + ShapeNotify))
+ {
+ /*XShapeEvent *xse = (XShapeEvent *) ev;*/
+ unshape_window (window);
+ gs_debug ("Window was reshaped!");
+ }
+#endif
+
+ break;
+ }
+
+}
+
+static GdkFilterReturn
+xevent_filter (GdkXEvent *xevent,
+ GdkEvent *event,
+ GSWindow *window)
+{
+ gs_window_xevent (window, xevent);
+
+ return GDK_FILTER_CONTINUE;
+}
+
+static void
+select_popup_events (void)
+{
+ XWindowAttributes attr;
+ unsigned long events;
+
+ gdk_error_trap_push ();
+
+ memset (&attr, 0, sizeof (attr));
+ XGetWindowAttributes (GDK_DISPLAY (), GDK_ROOT_WINDOW (), &attr);
+
+ events = SubstructureNotifyMask | attr.your_event_mask;
+ XSelectInput (GDK_DISPLAY (), GDK_ROOT_WINDOW (), events);
+
+ gdk_display_sync (gdk_display_get_default ());
+ gdk_error_trap_pop ();
+}
+
+static void
+window_select_shape_events (GSWindow *window)
+{
+#ifdef HAVE_SHAPE_EXT
+ unsigned long events;
+ int shape_error_base;
+
+ gdk_error_trap_push ();
+
+ if (XShapeQueryExtension (GDK_DISPLAY (), &window->priv->shape_event_base, &shape_error_base))
+ {
+ events = ShapeNotifyMask;
+ XShapeSelectInput (GDK_DISPLAY (), GDK_WINDOW_XID (GTK_WIDGET (window)->window), events);
+ }
+
+ gdk_display_sync (gdk_display_get_default ());
+ gdk_error_trap_pop ();
+#endif
+}
+
+static void
+gs_window_real_show (GtkWidget *widget)
+{
+ GSWindow *window;
+
+ if (GTK_WIDGET_CLASS (gs_window_parent_class)->show)
+ {
+ GTK_WIDGET_CLASS (gs_window_parent_class)->show (widget);
+ }
+
+ gs_window_clear (GS_WINDOW (widget));
+
+ set_invisible_cursor (widget->window, TRUE);
+
+ window = GS_WINDOW (widget);
+ if (window->priv->timer)
+ {
+ g_timer_destroy (window->priv->timer);
+ }
+ window->priv->timer = g_timer_new ();
+
+ remove_watchdog_timer (window);
+ add_watchdog_timer (window, 30000);
+
+ select_popup_events ();
+ window_select_shape_events (window);
+ gdk_window_add_filter (NULL, (GdkFilterFunc)xevent_filter, window);
+}
+
+static void
+set_info_text_and_icon (GSWindow *window,
+ const char *icon_stock_id,
+ const char *primary_text,
+ const char *secondary_text)
+{
+ GtkWidget *content_area;
+ GtkWidget *hbox_content;
+ GtkWidget *image;
+ GtkWidget *vbox;
+ gchar *primary_markup;
+ gchar *secondary_markup;
+ GtkWidget *primary_label;
+ GtkWidget *secondary_label;
+
+ hbox_content = gtk_hbox_new (FALSE, 8);
+ gtk_widget_show (hbox_content);
+
+ image = gtk_image_new_from_stock (icon_stock_id, GTK_ICON_SIZE_DIALOG);
+ gtk_widget_show (image);
+ gtk_box_pack_start (GTK_BOX (hbox_content), image, FALSE, FALSE, 0);
+ gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_widget_show (vbox);
+ gtk_box_pack_start (GTK_BOX (hbox_content), vbox, FALSE, FALSE, 0);
+
+ primary_markup = g_strdup_printf ("<b>%s</b>", primary_text);
+ primary_label = gtk_label_new (primary_markup);
+ g_free (primary_markup);
+ gtk_widget_show (primary_label);
+ gtk_box_pack_start (GTK_BOX (vbox), primary_label, TRUE, TRUE, 0);
+ gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE);
+ gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (primary_label), 0, 0.5);
+
+ if (secondary_text != NULL)
+ {
+ secondary_markup = g_strdup_printf ("<small>%s</small>",
+ secondary_text);
+ secondary_label = gtk_label_new (secondary_markup);
+ g_free (secondary_markup);
+ gtk_widget_show (secondary_label);
+ gtk_box_pack_start (GTK_BOX (vbox), secondary_label, TRUE, TRUE, 0);
+ gtk_label_set_use_markup (GTK_LABEL (secondary_label), TRUE);
+ gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (secondary_label), 0, 0.5);
+ }
+
+ /* remove old content */
+ content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (window->priv->info_bar));
+ if (window->priv->info_content != NULL)
+ {
+ gtk_container_remove (GTK_CONTAINER (content_area), window->priv->info_content);
+ }
+ gtk_box_pack_start (GTK_BOX (content_area),
+ hbox_content,
+ TRUE, FALSE, 0);
+ window->priv->info_content = hbox_content;
+}
+
+static gboolean
+info_bar_timeout (GSWindow *window)
+{
+ window->priv->info_bar_timer_id = 0;
+ gtk_widget_hide (window->priv->info_bar);
+ return FALSE;
+}
+
+void
+gs_window_show_message (GSWindow *window,
+ const char *summary,
+ const char *body,
+ const char *icon)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ set_info_text_and_icon (window,
+ icon,
+ summary,
+ body);
+ gtk_widget_show (window->priv->info_bar);
+
+ if (window->priv->info_bar_timer_id > 0)
+ {
+ g_source_remove (window->priv->info_bar_timer_id);
+ }
+
+ window->priv->info_bar_timer_id = g_timeout_add_seconds (INFO_BAR_SECONDS,
+ (GSourceFunc)info_bar_timeout,
+ window);
+}
+
+void
+gs_window_show (GSWindow *window)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ gtk_widget_show (GTK_WIDGET (window));
+}
+
+static void
+gs_window_real_hide (GtkWidget *widget)
+{
+ GSWindow *window;
+
+ window = GS_WINDOW (widget);
+
+ gdk_window_remove_filter (NULL, (GdkFilterFunc)xevent_filter, window);
+
+ remove_watchdog_timer (window);
+
+ if (GTK_WIDGET_CLASS (gs_window_parent_class)->hide)
+ {
+ GTK_WIDGET_CLASS (gs_window_parent_class)->hide (widget);
+ }
+}
+
+void
+gs_window_destroy (GSWindow *window)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ gs_window_cancel_unlock_request (window);
+
+ gtk_widget_destroy (GTK_WIDGET (window));
+}
+
+GdkWindow *
+gs_window_get_gdk_window (GSWindow *window)
+{
+ g_return_val_if_fail (GS_IS_WINDOW (window), NULL);
+
+ return GTK_WIDGET (window)->window;
+}
+
+GtkWidget *
+gs_window_get_drawing_area (GSWindow *window)
+{
+ g_return_val_if_fail (GS_IS_WINDOW (window), NULL);
+
+ return window->priv->drawing_area;
+}
+
+/* just for debugging */
+static gboolean
+error_watch (GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ gboolean finished = FALSE;
+
+ if (condition & G_IO_IN)
+ {
+ GIOStatus status;
+ GError *error = NULL;
+ char *line;
+
+ line = NULL;
+ status = g_io_channel_read_line (source, &line, NULL, NULL, &error);
+
+ switch (status)
+ {
+ case G_IO_STATUS_NORMAL:
+ gs_debug ("command error output: %s", line);
+ break;
+ case G_IO_STATUS_EOF:
+ finished = TRUE;
+ break;
+ case G_IO_STATUS_ERROR:
+ finished = TRUE;
+ gs_debug ("Error reading from child: %s\n", error->message);
+ g_error_free (error);
+ return FALSE;
+ case G_IO_STATUS_AGAIN:
+ default:
+ break;
+ }
+ g_free (line);
+ }
+ else if (condition & G_IO_HUP)
+ {
+ finished = TRUE;
+ }
+
+ if (finished)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+spawn_on_window (GSWindow *window,
+ char *command,
+ int *pid,
+ GIOFunc watch_func,
+ gpointer user_data,
+ gint *watch_id)
+{
+ int argc;
+ char **argv;
+ GError *error;
+ gboolean result;
+ GIOChannel *channel;
+ int standard_output;
+ int standard_error;
+ int child_pid;
+ int id;
+
+ error = NULL;
+ if (! g_shell_parse_argv (command, &argc, &argv, &error))
+ {
+ gs_debug ("Could not parse command: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ error = NULL;
+ result = gdk_spawn_on_screen_with_pipes (GTK_WINDOW (window)->screen,
+ NULL,
+ argv,
+ NULL,
+ G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
+ NULL,
+ NULL,
+ &child_pid,
+ NULL,
+ &standard_output,
+ &standard_error,
+ &error);
+
+ if (! result)
+ {
+ gs_debug ("Could not start command '%s': %s", command, error->message);
+ g_error_free (error);
+ g_strfreev (argv);
+ return FALSE;
+ }
+
+ if (pid != NULL)
+ {
+ *pid = child_pid;
+ }
+ else
+ {
+ g_spawn_close_pid (child_pid);
+ }
+
+ /* output channel */
+ channel = g_io_channel_unix_new (standard_output);
+ g_io_channel_set_close_on_unref (channel, TRUE);
+ g_io_channel_set_flags (channel,
+ g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK,
+ NULL);
+ id = g_io_add_watch (channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ watch_func,
+ user_data);
+ if (watch_id != NULL)
+ {
+ *watch_id = id;
+ }
+ g_io_channel_unref (channel);
+
+ /* error channel */
+ channel = g_io_channel_unix_new (standard_error);
+ g_io_channel_set_close_on_unref (channel, TRUE);
+ g_io_channel_set_flags (channel,
+ g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK,
+ NULL);
+ id = g_io_add_watch (channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ error_watch,
+ NULL);
+ g_io_channel_unref (channel);
+
+ g_strfreev (argv);
+
+ return result;
+}
+
+static void
+lock_plug_added (GtkWidget *widget,
+ GSWindow *window)
+{
+ gtk_widget_show (widget);
+}
+
+static gboolean
+lock_plug_removed (GtkWidget *widget,
+ GSWindow *window)
+{
+ gtk_widget_hide (widget);
+ gtk_container_remove (GTK_CONTAINER (window->priv->vbox), GTK_WIDGET (window->priv->lock_box));
+ window->priv->lock_box = NULL;
+
+ return TRUE;
+}
+
+static void
+keyboard_plug_added (GtkWidget *widget,
+ GSWindow *window)
+{
+ gtk_widget_show (widget);
+}
+
+static gboolean
+keyboard_plug_removed (GtkWidget *widget,
+ GSWindow *window)
+{
+ gtk_widget_hide (widget);
+ gtk_container_remove (GTK_CONTAINER (window->priv->vbox), GTK_WIDGET (window->priv->keyboard_socket));
+
+ return TRUE;
+}
+
+static void
+keyboard_socket_destroyed (GtkWidget *widget,
+ GSWindow *window)
+{
+ g_signal_handlers_disconnect_by_func (widget, keyboard_socket_destroyed, window);
+ g_signal_handlers_disconnect_by_func (widget, keyboard_plug_added, window);
+ g_signal_handlers_disconnect_by_func (widget, keyboard_plug_removed, window);
+
+ window->priv->keyboard_socket = NULL;
+}
+
+static void
+forward_key_events (GSWindow *window)
+{
+ window->priv->key_events = g_list_reverse (window->priv->key_events);
+
+ while (window->priv->key_events != NULL)
+ {
+ GdkEventKey *event = window->priv->key_events->data;
+
+ gtk_window_propagate_key_event (GTK_WINDOW (window), event);
+
+ gdk_event_free ((GdkEvent *)event);
+ window->priv->key_events = g_list_delete_link (window->priv->key_events,
+ window->priv->key_events);
+ }
+}
+
+static void
+remove_key_events (GSWindow *window)
+{
+ window->priv->key_events = g_list_reverse (window->priv->key_events);
+
+ while (window->priv->key_events)
+ {
+ GdkEventKey *event = window->priv->key_events->data;
+
+ gdk_event_free ((GdkEvent *)event);
+ window->priv->key_events = g_list_delete_link (window->priv->key_events,
+ window->priv->key_events);
+ }
+}
+
+static void
+lock_socket_show (GtkWidget *widget,
+ GSWindow *window)
+{
+ gtk_widget_child_focus (window->priv->lock_socket, GTK_DIR_TAB_FORWARD);
+
+ /* send queued events to the dialog */
+ forward_key_events (window);
+}
+
+static void
+lock_socket_destroyed (GtkWidget *widget,
+ GSWindow *window)
+{
+ g_signal_handlers_disconnect_by_func (widget, lock_socket_show, window);
+ g_signal_handlers_disconnect_by_func (widget, lock_socket_destroyed, window);
+ g_signal_handlers_disconnect_by_func (widget, lock_plug_added, window);
+ g_signal_handlers_disconnect_by_func (widget, lock_plug_removed, window);
+
+ window->priv->lock_socket = NULL;
+}
+
+static void
+create_keyboard_socket (GSWindow *window,
+ guint32 id)
+{
+ int height;
+
+ height = (gdk_screen_get_height (gtk_widget_get_screen (GTK_WIDGET (window)))) / 4;
+
+ window->priv->keyboard_socket = gtk_socket_new ();
+ gtk_widget_set_size_request (window->priv->keyboard_socket, -1, height);
+
+ g_signal_connect (window->priv->keyboard_socket, "destroy",
+ G_CALLBACK (keyboard_socket_destroyed), window);
+ g_signal_connect (window->priv->keyboard_socket, "plug_added",
+ G_CALLBACK (keyboard_plug_added), window);
+ g_signal_connect (window->priv->keyboard_socket, "plug_removed",
+ G_CALLBACK (keyboard_plug_removed), window);
+ gtk_box_pack_start (GTK_BOX (window->priv->vbox), window->priv->keyboard_socket, FALSE, FALSE, 0);
+ gtk_socket_add_id (GTK_SOCKET (window->priv->keyboard_socket), id);
+}
+
+/* adapted from gspawn.c */
+static int
+wait_on_child (int pid)
+{
+ int status;
+
+wait_again:
+ if (waitpid (pid, &status, 0) < 0)
+ {
+ if (errno == EINTR)
+ {
+ goto wait_again;
+ }
+ else if (errno == ECHILD)
+ {
+ ; /* do nothing, child already reaped */
+ }
+ else
+ {
+ gs_debug ("waitpid () should not fail in 'GSWindow'");
+ }
+ }
+
+ return status;
+}
+
+static void
+kill_keyboard_command (GSWindow *window)
+{
+ if (window->priv->keyboard_pid > 0)
+ {
+ signal_pid (window->priv->keyboard_pid, SIGTERM);
+ }
+}
+
+static void
+kill_dialog_command (GSWindow *window)
+{
+ /* If a dialog is up we need to signal it
+ and wait on it */
+ if (window->priv->lock_pid > 0)
+ {
+ signal_pid (window->priv->lock_pid, SIGTERM);
+ }
+}
+
+static void
+keyboard_command_finish (GSWindow *window)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ /* send a signal just in case */
+ kill_keyboard_command (window);
+
+ gs_debug ("Keyboard finished");
+
+ if (window->priv->keyboard_pid > 0)
+ {
+ int exit_status;
+
+ exit_status = wait_on_child (window->priv->keyboard_pid);
+
+ g_spawn_close_pid (window->priv->keyboard_pid);
+ window->priv->keyboard_pid = 0;
+ }
+}
+
+static gboolean
+keyboard_command_watch (GIOChannel *source,
+ GIOCondition condition,
+ GSWindow *window)
+{
+ gboolean finished = FALSE;
+
+ g_return_val_if_fail (GS_IS_WINDOW (window), FALSE);
+
+ if (condition & G_IO_IN)
+ {
+ GIOStatus status;
+ GError *error = NULL;
+ char *line;
+
+ line = NULL;
+ status = g_io_channel_read_line (source, &line, NULL, NULL, &error);
+
+ switch (status)
+ {
+ case G_IO_STATUS_NORMAL:
+ {
+ guint32 id;
+ char c;
+ gs_debug ("keyboard command output: %s", line);
+ if (1 == sscanf (line, " %" G_GUINT32_FORMAT " %c", &id, &c))
+ {
+ create_keyboard_socket (window, id);
+ }
+ }
+ break;
+ case G_IO_STATUS_EOF:
+ finished = TRUE;
+ break;
+ case G_IO_STATUS_ERROR:
+ finished = TRUE;
+ gs_debug ("Error reading from child: %s\n", error->message);
+ g_error_free (error);
+ return FALSE;
+ case G_IO_STATUS_AGAIN:
+ default:
+ break;
+ }
+
+ g_free (line);
+ }
+ else if (condition & G_IO_HUP)
+ {
+ finished = TRUE;
+ }
+
+ if (finished)
+ {
+ window->priv->keyboard_watch_id = 0;
+ keyboard_command_finish (window);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+embed_keyboard (GSWindow *window)
+{
+ gboolean res;
+
+ if (! window->priv->keyboard_enabled
+ || window->priv->keyboard_command == NULL)
+ return;
+
+ gs_debug ("Adding embedded keyboard widget");
+
+ /* FIXME: verify command is safe */
+
+ gs_debug ("Running command: %s", window->priv->keyboard_command);
+
+ res = spawn_on_window (window,
+ window->priv->keyboard_command,
+ &window->priv->keyboard_pid,
+ (GIOFunc)keyboard_command_watch,
+ window,
+ &window->priv->keyboard_watch_id);
+ if (! res)
+ {
+ gs_debug ("Could not start command: %s", window->priv->keyboard_command);
+ }
+}
+
+static void
+create_lock_socket (GSWindow *window,
+ guint32 id)
+{
+ window->priv->lock_socket = gtk_socket_new ();
+ window->priv->lock_box = gtk_alignment_new (0.5, 0.5, 0, 0);
+ gtk_widget_show (window->priv->lock_box);
+ gtk_box_pack_start (GTK_BOX (window->priv->vbox), window->priv->lock_box, TRUE, TRUE, 0);
+
+ gtk_container_add (GTK_CONTAINER (window->priv->lock_box), window->priv->lock_socket);
+
+ g_signal_connect (window->priv->lock_socket, "show",
+ G_CALLBACK (lock_socket_show), window);
+ g_signal_connect (window->priv->lock_socket, "destroy",
+ G_CALLBACK (lock_socket_destroyed), window);
+ g_signal_connect (window->priv->lock_socket, "plug_added",
+ G_CALLBACK (lock_plug_added), window);
+ g_signal_connect (window->priv->lock_socket, "plug_removed",
+ G_CALLBACK (lock_plug_removed), window);
+
+ gtk_socket_add_id (GTK_SOCKET (window->priv->lock_socket), id);
+
+ if (window->priv->keyboard_enabled)
+ {
+ embed_keyboard (window);
+ }
+}
+
+static void
+gs_window_dialog_finish (GSWindow *window)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ gs_debug ("Dialog finished");
+
+ /* make sure we finish the keyboard thing too */
+ keyboard_command_finish (window);
+
+ /* send a signal just in case */
+ kill_dialog_command (window);
+
+ if (window->priv->lock_pid > 0)
+ {
+ int exit_status;
+
+ exit_status = wait_on_child (window->priv->lock_pid);
+
+ g_spawn_close_pid (window->priv->lock_pid);
+ window->priv->lock_pid = 0;
+ }
+
+ /* remove events for the case were we failed to show socket */
+ remove_key_events (window);
+}
+
+static void
+maybe_kill_dialog (GSWindow *window)
+{
+ if (!window->priv->dialog_shake_in_progress
+ && window->priv->dialog_quit_requested
+ && window->priv->lock_pid > 0)
+ {
+ kill (window->priv->lock_pid, SIGTERM);
+ }
+}
+
+/* very rudimentary animation for indicating an auth failure */
+static void
+shake_dialog (GSWindow *window)
+{
+ int i;
+ guint left;
+ guint right;
+
+ window->priv->dialog_shake_in_progress = TRUE;
+
+ for (i = 0; i < 9; i++)
+ {
+ if (i % 2 == 0)
+ {
+ left = 30;
+ right = 0;
+ }
+ else
+ {
+ left = 0;
+ right = 30;
+ }
+
+ if (! window->priv->lock_box)
+ {
+ break;
+ }
+
+ gtk_alignment_set_padding (GTK_ALIGNMENT (window->priv->lock_box),
+ 0, 0,
+ left,
+ right);
+
+ while (gtk_events_pending ())
+ {
+ gtk_main_iteration ();
+ }
+
+ g_usleep (10000);
+ }
+
+ window->priv->dialog_shake_in_progress = FALSE;
+ maybe_kill_dialog (window);
+}
+
+static void
+window_set_dialog_up (GSWindow *window,
+ gboolean dialog_up)
+{
+ if (window->priv->dialog_up == dialog_up)
+ {
+ return;
+ }
+
+ window->priv->dialog_up = dialog_up;
+ g_object_notify (G_OBJECT (window), "dialog-up");
+}
+
+static void
+popdown_dialog (GSWindow *window)
+{
+ gs_window_dialog_finish (window);
+
+ gtk_widget_show (window->priv->drawing_area);
+
+ gs_window_clear (window);
+ set_invisible_cursor (GTK_WIDGET (window)->window, TRUE);
+
+ window_set_dialog_up (window, FALSE);
+
+ /* reset the pointer positions */
+ window->priv->last_x = -1;
+ window->priv->last_y = -1;
+
+ if (window->priv->lock_box != NULL)
+ {
+ gtk_container_remove (GTK_CONTAINER (window->priv->vbox), GTK_WIDGET (window->priv->lock_box));
+ window->priv->lock_box = NULL;
+ }
+
+ remove_popup_dialog_idle (window);
+ remove_command_watches (window);
+}
+
+static gboolean
+lock_command_watch (GIOChannel *source,
+ GIOCondition condition,
+ GSWindow *window)
+{
+ gboolean finished = FALSE;
+
+ g_return_val_if_fail (GS_IS_WINDOW (window), FALSE);
+
+ if (condition & G_IO_IN)
+ {
+ GIOStatus status;
+ GError *error = NULL;
+ char *line;
+
+ line = NULL;
+ status = g_io_channel_read_line (source, &line, NULL, NULL, &error);
+
+ switch (status)
+ {
+ case G_IO_STATUS_NORMAL:
+ gs_debug ("command output: %s", line);
+
+ if (strstr (line, "WINDOW ID=") != NULL)
+ {
+ guint32 id;
+ char c;
+ if (1 == sscanf (line, " WINDOW ID= %" G_GUINT32_FORMAT " %c", &id, &c))
+ {
+ create_lock_socket (window, id);
+ }
+ }
+ else if (strstr (line, "NOTICE=") != NULL)
+ {
+ if (strstr (line, "NOTICE=AUTH FAILED") != NULL)
+ {
+ shake_dialog (window);
+ }
+ }
+ else if (strstr (line, "RESPONSE=") != NULL)
+ {
+ if (strstr (line, "RESPONSE=OK") != NULL)
+ {
+ gs_debug ("Got OK response");
+ window->priv->dialog_response = DIALOG_RESPONSE_OK;
+ }
+ else
+ {
+ gs_debug ("Got CANCEL response");
+ window->priv->dialog_response = DIALOG_RESPONSE_CANCEL;
+ }
+ finished = TRUE;
+ }
+ else if (strstr (line, "REQUEST QUIT") != NULL)
+ {
+ gs_debug ("Got request for quit");
+ window->priv->dialog_quit_requested = TRUE;
+ maybe_kill_dialog (window);
+ }
+ break;
+ case G_IO_STATUS_EOF:
+ finished = TRUE;
+ break;
+ case G_IO_STATUS_ERROR:
+ finished = TRUE;
+ gs_debug ("Error reading from child: %s\n", error->message);
+ g_error_free (error);
+ return FALSE;
+ case G_IO_STATUS_AGAIN:
+ default:
+ break;
+ }
+
+ g_free (line);
+ }
+ else if (condition & G_IO_HUP)
+ {
+ finished = TRUE;
+ }
+
+ if (finished)
+ {
+ popdown_dialog (window);
+
+ if (window->priv->dialog_response == DIALOG_RESPONSE_OK)
+ {
+ add_emit_deactivated_idle (window);
+ }
+
+ window->priv->lock_watch_id = 0;
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+is_logout_enabled (GSWindow *window)
+{
+ double elapsed;
+
+ if (! window->priv->logout_enabled)
+ {
+ return FALSE;
+ }
+
+ if (! window->priv->logout_command)
+ {
+ return FALSE;
+ }
+
+ elapsed = g_timer_elapsed (window->priv->timer, NULL);
+
+ if (window->priv->logout_timeout < (elapsed * 1000))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+is_user_switch_enabled (GSWindow *window)
+{
+ return window->priv->user_switch_enabled;
+}
+
+static void
+popup_dialog (GSWindow *window)
+{
+ gboolean result;
+ char *tmp;
+ GString *command;
+
+ gs_debug ("Popping up dialog");
+
+ tmp = g_build_filename (LIBEXECDIR, "mate-screensaver-dialog", NULL);
+ command = g_string_new (tmp);
+ g_free (tmp);
+
+ if (is_logout_enabled (window))
+ {
+ command = g_string_append (command, " --enable-logout");
+ g_string_append_printf (command, " --logout-command='%s'", window->priv->logout_command);
+ }
+
+ if (window->priv->status_message)
+ {
+ char *quoted;
+
+ quoted = g_shell_quote (window->priv->status_message);
+ g_string_append_printf (command, " --status-message=%s", quoted);
+ g_free (quoted);
+ }
+
+ if (is_user_switch_enabled (window))
+ {
+ command = g_string_append (command, " --enable-switch");
+ }
+
+ if (gs_debug_enabled ())
+ {
+ command = g_string_append (command, " --verbose");
+ }
+
+ gtk_widget_hide (window->priv->drawing_area);
+
+ gs_window_clear_to_background_pixmap (window);
+
+ set_invisible_cursor (GTK_WIDGET (window)->window, FALSE);
+
+ window->priv->dialog_quit_requested = FALSE;
+ window->priv->dialog_shake_in_progress = FALSE;
+
+ result = spawn_on_window (window,
+ command->str,
+ &window->priv->lock_pid,
+ (GIOFunc)lock_command_watch,
+ window,
+ &window->priv->lock_watch_id);
+ if (! result)
+ {
+ gs_debug ("Could not start command: %s", command->str);
+ }
+
+ g_string_free (command, TRUE);
+}
+
+static gboolean
+popup_dialog_idle (GSWindow *window)
+{
+ popup_dialog (window);
+
+ window->priv->popup_dialog_idle_id = 0;
+
+ return FALSE;
+}
+
+void
+gs_window_request_unlock (GSWindow *window)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ gs_debug ("Requesting unlock");
+
+ if (! GTK_WIDGET_VISIBLE (GTK_WIDGET (window)))
+ {
+ gs_debug ("Request unlock but window is not visible!");
+ return;
+ }
+
+ if (window->priv->lock_watch_id > 0)
+ {
+ return;
+ }
+
+ if (! window->priv->lock_enabled)
+ {
+ add_emit_deactivated_idle (window);
+
+ return;
+ }
+
+ if (window->priv->popup_dialog_idle_id == 0)
+ {
+ add_popup_dialog_idle (window);
+ }
+
+ window_set_dialog_up (window, TRUE);
+}
+
+void
+gs_window_cancel_unlock_request (GSWindow *window)
+{
+ /* FIXME: This is a bit of a hammer approach...
+ * Maybe we should send a delete-event to
+ * the plug?
+ */
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ popdown_dialog (window);
+}
+
+void
+gs_window_set_lock_enabled (GSWindow *window,
+ gboolean lock_enabled)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ if (window->priv->lock_enabled == lock_enabled)
+ {
+ return;
+ }
+
+ window->priv->lock_enabled = lock_enabled;
+ g_object_notify (G_OBJECT (window), "lock-enabled");
+}
+
+void
+gs_window_set_screen (GSWindow *window,
+ GdkScreen *screen)
+{
+
+ g_return_if_fail (GS_IS_WINDOW (window));
+ g_return_if_fail (GDK_IS_SCREEN (screen));
+
+ gtk_window_set_screen (GTK_WINDOW (window), screen);
+}
+
+GdkScreen *
+gs_window_get_screen (GSWindow *window)
+{
+ g_return_val_if_fail (GS_IS_WINDOW (window), NULL);
+
+ return GTK_WINDOW (window)->screen;
+}
+
+void
+gs_window_set_keyboard_enabled (GSWindow *window,
+ gboolean enabled)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ window->priv->keyboard_enabled = enabled;
+}
+
+void
+gs_window_set_keyboard_command (GSWindow *window,
+ const char *command)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ g_free (window->priv->keyboard_command);
+
+ if (command != NULL)
+ {
+ window->priv->keyboard_command = g_strdup (command);
+ }
+ else
+ {
+ window->priv->keyboard_command = NULL;
+ }
+}
+
+void
+gs_window_set_logout_enabled (GSWindow *window,
+ gboolean logout_enabled)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ window->priv->logout_enabled = logout_enabled;
+}
+
+void
+gs_window_set_user_switch_enabled (GSWindow *window,
+ gboolean user_switch_enabled)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ window->priv->user_switch_enabled = user_switch_enabled;
+}
+
+void
+gs_window_set_logout_timeout (GSWindow *window,
+ glong logout_timeout)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ if (logout_timeout < 0)
+ {
+ window->priv->logout_timeout = 0;
+ }
+ else
+ {
+ window->priv->logout_timeout = logout_timeout;
+ }
+}
+
+void
+gs_window_set_logout_command (GSWindow *window,
+ const char *command)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ g_free (window->priv->logout_command);
+
+ if (command)
+ {
+ window->priv->logout_command = g_strdup (command);
+ }
+ else
+ {
+ window->priv->logout_command = NULL;
+ }
+}
+
+void
+gs_window_set_status_message (GSWindow *window,
+ const char *status_message)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ g_free (window->priv->status_message);
+ window->priv->status_message = g_strdup (status_message);
+}
+
+void
+gs_window_set_monitor (GSWindow *window,
+ int monitor)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ if (window->priv->monitor == monitor)
+ {
+ return;
+ }
+
+ window->priv->monitor = monitor;
+
+ gtk_widget_queue_resize (GTK_WIDGET (window));
+
+ g_object_notify (G_OBJECT (window), "monitor");
+}
+
+int
+gs_window_get_monitor (GSWindow *window)
+{
+ g_return_val_if_fail (GS_IS_WINDOW (window), -1);
+
+ return window->priv->monitor;
+}
+
+static void
+gs_window_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GSWindow *self;
+
+ self = GS_WINDOW (object);
+
+ switch (prop_id)
+ {
+ case PROP_LOCK_ENABLED:
+ gs_window_set_lock_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_KEYBOARD_ENABLED:
+ gs_window_set_keyboard_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_KEYBOARD_COMMAND:
+ gs_window_set_keyboard_command (self, g_value_get_string (value));
+ break;
+ case PROP_LOGOUT_ENABLED:
+ gs_window_set_logout_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_LOGOUT_COMMAND:
+ gs_window_set_logout_command (self, g_value_get_string (value));
+ break;
+ case PROP_STATUS_MESSAGE:
+ gs_window_set_status_message (self, g_value_get_string (value));
+ break;
+ case PROP_LOGOUT_TIMEOUT:
+ gs_window_set_logout_timeout (self, g_value_get_long (value));
+ break;
+ case PROP_MONITOR:
+ gs_window_set_monitor (self, g_value_get_int (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_window_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GSWindow *self;
+
+ self = GS_WINDOW (object);
+
+ switch (prop_id)
+ {
+ case PROP_LOCK_ENABLED:
+ g_value_set_boolean (value, self->priv->lock_enabled);
+ break;
+ case PROP_KEYBOARD_ENABLED:
+ g_value_set_boolean (value, self->priv->keyboard_enabled);
+ break;
+ case PROP_KEYBOARD_COMMAND:
+ g_value_set_string (value, self->priv->keyboard_command);
+ break;
+ 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_STATUS_MESSAGE:
+ g_value_set_string (value, self->priv->status_message);
+ break;
+ case PROP_LOGOUT_TIMEOUT:
+ g_value_set_long (value, self->priv->logout_timeout);
+ break;
+ case PROP_MONITOR:
+ g_value_set_int (value, self->priv->monitor);
+ break;
+ case PROP_OBSCURED:
+ g_value_set_boolean (value, self->priv->obscured);
+ break;
+ case PROP_DIALOG_UP:
+ g_value_set_boolean (value, self->priv->dialog_up);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+queue_key_event (GSWindow *window,
+ GdkEventKey *event)
+{
+ /* Eat the first return, enter, escape, or space */
+ if (window->priv->key_events == NULL
+ && (event->keyval == GDK_Return
+ || event->keyval == GDK_KP_Enter
+ || event->keyval == GDK_Escape
+ || event->keyval == GDK_space))
+ {
+ return;
+ }
+
+ /* Only cache MAX_QUEUED_EVENTS key events. If there are any more than this then
+ something is wrong */
+ /* Don't queue keys that may cause focus navigation in the dialog */
+ if (g_list_length (window->priv->key_events) < MAX_QUEUED_EVENTS
+ && event->keyval != GDK_Tab
+ && event->keyval != GDK_Up
+ && event->keyval != GDK_Down)
+ {
+ window->priv->key_events = g_list_prepend (window->priv->key_events,
+ gdk_event_copy ((GdkEvent *)event));
+ }
+}
+
+static gboolean
+maybe_handle_activity (GSWindow *window)
+{
+ gboolean handled;
+
+ handled = FALSE;
+
+ /* if we already have a socket then don't bother */
+ if (! window->priv->lock_socket
+ && GTK_WIDGET_IS_SENSITIVE (GTK_WIDGET (window)))
+ {
+ g_signal_emit (window, signals [ACTIVITY], 0, &handled);
+ }
+
+ return handled;
+}
+
+static gboolean
+gs_window_real_key_press_event (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ /*g_message ("KEY PRESS state: %u keyval %u", event->state, event->keyval);*/
+
+ /* Ignore brightness keys */
+ if (event->hardware_keycode == 101 || event->hardware_keycode == 212)
+ {
+ gs_debug ("Ignoring brightness keys");
+ return TRUE;
+ }
+
+ maybe_handle_activity (GS_WINDOW (widget));
+
+ queue_key_event (GS_WINDOW (widget), event);
+
+ if (GTK_WIDGET_CLASS (gs_window_parent_class)->key_press_event)
+ {
+ GTK_WIDGET_CLASS (gs_window_parent_class)->key_press_event (widget, event);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gs_window_real_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GSWindow *window;
+ gdouble distance;
+ gdouble min_distance;
+ gdouble min_percentage = 0.1;
+ GdkScreen *screen;
+
+ window = GS_WINDOW (widget);
+
+ screen = gs_window_get_screen (window);
+ min_distance = gdk_screen_get_width (screen) * min_percentage;
+
+ /* if the last position was not set then don't detect motion */
+ if (window->priv->last_x < 0 || window->priv->last_y < 0)
+ {
+ window->priv->last_x = event->x;
+ window->priv->last_y = event->y;
+
+ return FALSE;
+ }
+
+ /* just an approximate distance */
+ distance = MAX (ABS (window->priv->last_x - event->x),
+ ABS (window->priv->last_y - event->y));
+
+ if (distance > min_distance)
+ {
+ maybe_handle_activity (window);
+
+ window->priv->last_x = -1;
+ window->priv->last_y = -1;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gs_window_real_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GSWindow *window;
+
+ window = GS_WINDOW (widget);
+ maybe_handle_activity (window);
+
+ return FALSE;
+}
+
+static gboolean
+gs_window_real_scroll_event (GtkWidget *widget,
+ GdkEventScroll *event)
+{
+ GSWindow *window;
+
+ window = GS_WINDOW (widget);
+ maybe_handle_activity (window);
+
+ return FALSE;
+}
+
+static void
+gs_window_real_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ GSWindow *window;
+ GtkBin *bin;
+ GdkRectangle old_geometry;
+ int position_changed = FALSE;
+ int size_changed = FALSE;
+
+ window = GS_WINDOW (widget);
+ bin = GTK_BIN (widget);
+
+ if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
+ {
+ gtk_widget_size_request (bin->child, requisition);
+ }
+
+ old_geometry = window->priv->geometry;
+
+ update_geometry (window);
+
+ requisition->width = window->priv->geometry.width;
+ requisition->height = window->priv->geometry.height;
+
+ if (! GTK_WIDGET_REALIZED (widget))
+ {
+ return;
+ }
+
+ if (old_geometry.width != window->priv->geometry.width ||
+ old_geometry.height != window->priv->geometry.height)
+ {
+ size_changed = TRUE;
+ }
+
+ if (old_geometry.x != window->priv->geometry.x ||
+ old_geometry.y != window->priv->geometry.y)
+ {
+ position_changed = TRUE;
+ }
+
+ gs_window_move_resize_window (window, position_changed, size_changed);
+}
+
+static gboolean
+gs_window_real_grab_broken (GtkWidget *widget,
+ GdkEventGrabBroken *event)
+{
+ if (event->grab_window != NULL)
+ {
+ gs_debug ("Grab broken on window %X %s, new grab on window %X",
+ (guint32) GDK_WINDOW_XID (event->window),
+ event->keyboard ? "keyboard" : "pointer",
+ (guint32) GDK_WINDOW_XID (event->grab_window));
+ }
+ else
+ {
+ gs_debug ("Grab broken on window %X %s, new grab is outside application",
+ (guint32) GDK_WINDOW_XID (event->window),
+ event->keyboard ? "keyboard" : "pointer");
+ }
+
+ return FALSE;
+}
+
+gboolean
+gs_window_is_obscured (GSWindow *window)
+{
+ g_return_val_if_fail (GS_IS_WINDOW (window), FALSE);
+
+ return window->priv->obscured;
+}
+
+gboolean
+gs_window_is_dialog_up (GSWindow *window)
+{
+ g_return_val_if_fail (GS_IS_WINDOW (window), FALSE);
+
+ return window->priv->dialog_up;
+}
+
+static void
+window_set_obscured (GSWindow *window,
+ gboolean obscured)
+{
+ if (window->priv->obscured == obscured)
+ {
+ return;
+ }
+
+ window->priv->obscured = obscured;
+ g_object_notify (G_OBJECT (window), "obscured");
+}
+
+static gboolean
+gs_window_real_visibility_notify_event (GtkWidget *widget,
+ GdkEventVisibility *event)
+{
+ switch (event->state)
+ {
+ case GDK_VISIBILITY_FULLY_OBSCURED:
+ window_set_obscured (GS_WINDOW (widget), TRUE);
+ break;
+ case GDK_VISIBILITY_PARTIAL:
+ break;
+ case GDK_VISIBILITY_UNOBSCURED:
+ window_set_obscured (GS_WINDOW (widget), FALSE);
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+gs_window_class_init (GSWindowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = gs_window_finalize;
+ object_class->get_property = gs_window_get_property;
+ object_class->set_property = gs_window_set_property;
+
+ widget_class->show = gs_window_real_show;
+ widget_class->hide = gs_window_real_hide;
+ widget_class->realize = gs_window_real_realize;
+ widget_class->unrealize = gs_window_real_unrealize;
+ widget_class->key_press_event = gs_window_real_key_press_event;
+ widget_class->motion_notify_event = gs_window_real_motion_notify_event;
+ widget_class->button_press_event = gs_window_real_button_press_event;
+ widget_class->scroll_event = gs_window_real_scroll_event;
+ widget_class->size_request = gs_window_real_size_request;
+ widget_class->grab_broken_event = gs_window_real_grab_broken;
+ widget_class->visibility_notify_event = gs_window_real_visibility_notify_event;
+
+ g_type_class_add_private (klass, sizeof (GSWindowPrivate));
+
+ signals [ACTIVITY] =
+ g_signal_new ("activity",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSWindowClass, activity),
+ NULL,
+ NULL,
+ gs_marshal_BOOLEAN__VOID,
+ G_TYPE_BOOLEAN,
+ 0);
+ signals [DEACTIVATED] =
+ g_signal_new ("deactivated",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSWindowClass, deactivated),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ g_object_class_install_property (object_class,
+ PROP_OBSCURED,
+ g_param_spec_boolean ("obscured",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ PROP_DIALOG_UP,
+ g_param_spec_boolean ("dialog-up",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ PROP_LOCK_ENABLED,
+ g_param_spec_boolean ("lock-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ 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_TIMEOUT,
+ g_param_spec_long ("logout-timeout",
+ NULL,
+ NULL,
+ -1,
+ G_MAXLONG,
+ 0,
+ 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_KEYBOARD_ENABLED,
+ g_param_spec_boolean ("keyboard-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_KEYBOARD_COMMAND,
+ g_param_spec_string ("keyboard-command",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_MONITOR,
+ g_param_spec_int ("monitor",
+ "Xinerama monitor",
+ "The monitor (in terms of Xinerama) which the window is on",
+ 0,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+}
+
+static void
+create_info_bar (GSWindow *window)
+{
+ window->priv->info_bar = gtk_info_bar_new ();
+ gtk_widget_set_no_show_all (window->priv->info_bar, TRUE);
+ gtk_box_pack_end (GTK_BOX (window->priv->vbox), window->priv->info_bar, FALSE, FALSE, 0);
+}
+
+static void
+gs_window_init (GSWindow *window)
+{
+ window->priv = GS_WINDOW_GET_PRIVATE (window);
+
+ window->priv->geometry.x = -1;
+ window->priv->geometry.y = -1;
+ window->priv->geometry.width = -1;
+ window->priv->geometry.height = -1;
+
+ window->priv->last_x = -1;
+ window->priv->last_y = -1;
+
+ gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
+
+ gtk_window_set_skip_taskbar_hint (GTK_WINDOW (window), TRUE);
+ gtk_window_set_skip_pager_hint (GTK_WINDOW (window), TRUE);
+
+ gtk_window_set_keep_above (GTK_WINDOW (window), TRUE);
+
+ gtk_window_fullscreen (GTK_WINDOW (window));
+
+ gtk_widget_set_events (GTK_WIDGET (window),
+ gtk_widget_get_events (GTK_WIDGET (window))
+ | GDK_POINTER_MOTION_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK
+ | GDK_KEY_PRESS_MASK
+ | GDK_KEY_RELEASE_MASK
+ | GDK_EXPOSURE_MASK
+ | GDK_VISIBILITY_NOTIFY_MASK
+ | GDK_ENTER_NOTIFY_MASK
+ | GDK_LEAVE_NOTIFY_MASK);
+
+ window->priv->vbox = gtk_vbox_new (FALSE, 12);
+ gtk_widget_show (window->priv->vbox);
+ gtk_container_add (GTK_CONTAINER (window), window->priv->vbox);
+
+ window->priv->drawing_area = gtk_drawing_area_new ();
+ gtk_widget_show (window->priv->drawing_area);
+ gtk_box_pack_start (GTK_BOX (window->priv->vbox), window->priv->drawing_area, TRUE, TRUE, 0);
+ create_info_bar (window);
+
+ force_no_pixmap_background (window->priv->drawing_area);
+}
+
+static void
+remove_command_watches (GSWindow *window)
+{
+ if (window->priv->lock_watch_id != 0)
+ {
+ g_source_remove (window->priv->lock_watch_id);
+ window->priv->lock_watch_id = 0;
+ }
+ if (window->priv->keyboard_watch_id != 0)
+ {
+ g_source_remove (window->priv->keyboard_watch_id);
+ window->priv->keyboard_watch_id = 0;
+ }
+}
+
+static void
+gs_window_finalize (GObject *object)
+{
+ GSWindow *window;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_WINDOW (object));
+
+ window = GS_WINDOW (object);
+
+ g_return_if_fail (window->priv != NULL);
+
+ g_free (window->priv->logout_command);
+ g_free (window->priv->keyboard_command);
+
+ if (window->priv->info_bar_timer_id > 0)
+ {
+ g_source_remove (window->priv->info_bar_timer_id);
+ }
+
+ remove_watchdog_timer (window);
+ remove_popup_dialog_idle (window);
+
+ if (window->priv->timer)
+ {
+ g_timer_destroy (window->priv->timer);
+ }
+
+ remove_key_events (window);
+
+ remove_command_watches (window);
+
+ gs_window_dialog_finish (window);
+
+ if (window->priv->background_pixmap)
+ {
+ g_object_unref (window->priv->background_pixmap);
+ }
+
+ G_OBJECT_CLASS (gs_window_parent_class)->finalize (object);
+}
+
+GSWindow *
+gs_window_new (GdkScreen *screen,
+ int monitor,
+ gboolean lock_enabled)
+{
+ GObject *result;
+
+ result = g_object_new (GS_TYPE_WINDOW,
+ "type", GTK_WINDOW_POPUP,
+ "screen", screen,
+ "monitor", monitor,
+ "lock-enabled", lock_enabled,
+ NULL);
+
+ return GS_WINDOW (result);
+}