summaryrefslogtreecommitdiffstats
path: root/driver/demo-Gtk.c
diff options
context:
space:
mode:
Diffstat (limited to 'driver/demo-Gtk.c')
-rw-r--r--driver/demo-Gtk.c6100
1 files changed, 3078 insertions, 3022 deletions
diff --git a/driver/demo-Gtk.c b/driver/demo-Gtk.c
index b5e82e2..4950369 100644
--- a/driver/demo-Gtk.c
+++ b/driver/demo-Gtk.c
@@ -1,5 +1,5 @@
/* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
- * xscreensaver, Copyright © 1993-2021 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright © 1993-2024 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
@@ -16,23 +16,6 @@
#ifdef HAVE_GTK /* whole file */
-#include "blurb.h"
-
-#include <xscreensaver-intl.h>
-
-#include <stdlib.h>
-
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-
-# ifdef __GNUC__
-# define STFU __extension__ /* ignore gcc -pendantic warnings in next sexp */
-# else
-# define STFU /* */
-# endif
-
-
#ifdef ENABLE_NLS
# include <locale.h>
#endif /* ENABLE_NLS */
@@ -42,32 +25,22 @@
#endif /* HAVE_UNAME */
#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
#include <time.h>
#include <pwd.h> /* for getpwuid() */
#include <sys/stat.h>
#include <sys/time.h>
-
#include <signal.h>
#include <errno.h>
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h> /* for waitpid() and associated macros */
#endif
-
-#include <X11/Xproto.h> /* for CARD32 */
#include <X11/Xatom.h> /* for XA_INTEGER */
-#include <X11/Intrinsic.h>
-#include <X11/StringDefs.h>
-
-/* We don't actually use any widget internals, but these are included
- so that gdb will have debug info for the widgets... */
-#include <X11/IntrinsicP.h>
-#include <X11/ShellP.h>
-
-#ifdef HAVE_XINERAMA
-# include <X11/extensions/Xinerama.h>
-#endif /* HAVE_XINERAMA */
+#include <X11/Shell.h>
#if (__GNUC__ >= 4) /* Ignore useless warnings generated by gtk.h */
# undef inline
@@ -76,130 +49,78 @@
# pragma GCC diagnostic ignored "-Wlong-long"
# pragma GCC diagnostic ignored "-Wvariadic-macros"
# pragma GCC diagnostic ignored "-Wpedantic"
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#include <gtk/gtk.h>
-
-#ifdef HAVE_CRAPPLET
-# include <gnome.h>
-# include <capplet-widget.h>
-#endif
-
-#include <gdk/gdkx.h>
-
-#ifdef HAVE_GTK2
-# include <gmodule.h>
-#else /* !HAVE_GTK2 */
-# define G_MODULE_EXPORT /**/
-#endif /* !HAVE_GTK2 */
-
-#ifndef HAVE_XML
- /* Kludge: this is defined in demo-Gtk-conf.c when HAVE_XML.
- It is unused otherwise, so in that case, stub it out. */
- static const char *hack_configuration_path = 0;
-#endif
+#include <gdk/gdkx.h> /* For gdk_x11_get_default_xdisplay(), etc. */
#if (__GNUC__ >= 4)
# pragma GCC diagnostic pop
#endif
-
+#include "blurb.h"
+#include "xscreensaver-intl.h"
#include "version.h"
#include "types.h"
#include "resources.h" /* for parse_time() */
#include "remote.h" /* for xscreensaver_command() */
+#include "screens.h"
#include "visual.h"
#include "atoms.h"
#include "usleep.h"
+#include "atoms.h"
+#include "screenshot.h"
#include "xmu.h"
-#include "logo-50.xpm"
-#include "logo-180.xpm"
-
#include "demo-Gtk-conf.h"
-#include "atoms.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-
-#ifdef HAVE_GTK2
-enum {
- COL_ENABLED,
- COL_NAME,
- COL_LAST
-};
-#endif /* HAVE_GTK2 */
-/* Deal with deprecation of direct access to struct fields on the way to GTK3
- See http://live.gnome.org/GnomeGoals/UseGseal
- */
-#if GTK_CHECK_VERSION(2,14,0)
-# define GET_PARENT(w) gtk_widget_get_parent (w)
-# define GET_WINDOW(w) gtk_widget_get_window (w)
-# define GET_ACTION_AREA(d) gtk_dialog_get_action_area (d)
-# define GET_CONTENT_AREA(d) gtk_dialog_get_content_area (d)
-# define GET_ADJ_VALUE(a) gtk_adjustment_get_value (a)
-# define SET_ADJ_VALUE(a,v) gtk_adjustment_set_value (a, v)
-# define SET_ADJ_UPPER(a,v) gtk_adjustment_set_upper (a, v)
-#else
-# define GET_PARENT(w) ((w)->parent)
-# define GET_WINDOW(w) ((w)->window)
-# define GET_ACTION_AREA(d) ((d)->action_area)
-# define GET_CONTENT_AREA(d) ((d)->vbox)
-# define GET_ADJ_VALUE(a) ((a)->value)
-# define SET_ADJ_VALUE(a,v) (a)->value = v
-# define SET_ADJ_UPPER(a,v) (a)->upper = v
-#endif
-
-#if GTK_CHECK_VERSION(2,18,0)
-# define SET_CAN_DEFAULT(w) gtk_widget_set_can_default ((w), TRUE)
-# define GET_SENSITIVE(w) gtk_widget_get_sensitive (w)
-#else
-# define SET_CAN_DEFAULT(w) GTK_WIDGET_SET_FLAGS ((w), GTK_CAN_DEFAULT)
-# define GET_SENSITIVE(w) GTK_WIDGET_IS_SENSITIVE (w)
-#endif
-
-#if GTK_CHECK_VERSION(2,20,0)
-# define GET_REALIZED(w) gtk_widget_get_realized (w)
-#else
-# define GET_REALIZED(w) GTK_WIDGET_REALIZED (w)
-#endif
/* from exec.c */
extern void exec_command (const char *shell, const char *command, int nice);
extern int on_path_p (const char *program);
-static void hack_subproc_environment (Window preview_window_id, Bool debug_p);
#undef countof
#define countof(x) (sizeof((x))/sizeof((*x)))
+const char *progclass = "XScreenSaver";
-char *progclass = "XScreenSaver";
-XrmDatabase db;
+#ifdef __GNUC__
+ __extension__ /* don't warn about "string length is greater than the
+ length ISO C89 compilers are required to support". */
+#endif
+static char *defaults[] = {
+#include "XScreenSaver_ad.h"
+ 0
+};
/* The order of the items in the mode menu. */
static int mode_menu_order[] = {
DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS, RANDOM_HACKS_SAME };
+enum { COL_ENABLED, COL_NAME, COL_LAST };
+typedef enum { D_NONE, D_LAUNCH, D_GNOME, D_KDE } dialog_button;
typedef struct {
char *short_version; /* version number of this xscreensaver build */
- GtkWidget *toplevel_widget; /* the main window */
- GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
- GtkWidget *popup_widget; /* the "Settings" dialog */
- conf_data *cdata; /* private data for per-hack configuration */
+ GtkWindow *window;
+ GtkWindow *dialog;
+
+ Display *dpy;
+ Bool wayland_p;
+ Pixmap screenshot;
+ Visual *gl_visual;
-#ifdef HAVE_GTK2
- GtkBuilder *gtk_ui; /* UI file */
-#endif /* HAVE_GTK2 */
+ conf_data *cdata; /* private data for per-hack configuration */
Bool debug_p; /* whether to print diagnostics */
Bool initializing_p; /* flag for breaking recursion loops */
+ Bool flushing_p; /* flag for breaking recursion loops */
Bool saving_p; /* flag for breaking recursion loops */
+ Bool dpms_supported_p; /* Whether XDPMS is available */
char *desired_preview_cmd; /* subprocess we intend to run */
char *running_preview_cmd; /* subprocess we are currently running */
@@ -222,16 +143,149 @@ typedef struct {
int _selected_list_element; /* don't use this: call
selected_list_element() instead */
- int nscreens; /* How many X or Xinerama screens there are */
+ Bool multi_screen_p; /* Is there more than one monitor */
saver_preferences prefs;
} state;
-/* Total fucking evilness due to the fact that it's rocket science to get
- a closure object of our own down into the various widget callbacks. */
-static state *global_state_kludge;
+/* Class definitions for the application and two windows. The classes are:
+
+ XScreenSaverApp -- The invisible GtkApplication main-loop framework.
+ XScreenSaverWindow -- The main window with the scrolling list of hacks.
+ XScreenSaverDialog -- The per-hack settings window.
+ */
+#define XSCREENSAVER_APP_TYPE (xscreensaver_app_get_type())
+G_DECLARE_FINAL_TYPE (XScreenSaverApp, xscreensaver_app, XSCREENSAVER, APP,
+ GtkApplication)
+
+struct _XScreenSaverApp {
+ GtkApplication parent;
+ Bool cmdline_debug_p;
+};
+
+
+G_DEFINE_TYPE (XScreenSaverApp, xscreensaver_app, GTK_TYPE_APPLICATION)
+
+/* The widgets we reference from the demo.ui file.
+ */
+#define ALL_WINDOW_WIDGETS \
+ W(activate_menuitem) \
+ W(lock_menuitem) \
+ W(kill_menuitem) \
+ W(restart_menuitem) \
+ W(list) \
+ W(scroller) \
+ W(preview_frame) \
+ W(short_preview_label) \
+ W(preview_author_label) \
+ W(timeout_spinbutton) \
+ W(cycle_spinbutton) \
+ W(lock_spinbutton) \
+ W(dpms_standby_spinbutton) \
+ W(dpms_suspend_spinbutton) \
+ W(dpms_off_spinbutton) \
+ W(fade_spinbutton) \
+ W(lock_button) \
+ W(dpms_button) \
+ W(dpms_quickoff_button) \
+ W(grab_desk_button) \
+ W(grab_video_button) \
+ W(grab_image_button) \
+ W(fade_button) \
+ W(unfade_button) \
+ W(preview) \
+ W(preview_notebook) \
+ W(text_radio) \
+ W(text_file_radio) \
+ W(text_file_browse) \
+ W(text_program_radio) \
+ W(text_url_radio) \
+ W(text_host_radio) \
+ W(image_text) \
+ W(image_browse_button) \
+ W(text_entry) \
+ W(text_file_entry) \
+ W(text_program_entry) \
+ W(text_url_entry) \
+ W(text_program_browse) \
+ W(theme_menu) \
+ W(mode_menu) \
+ W(next_prev_hbox) \
+ W(blanking_table) \
+ W(lock_mlabel) \
+ W(dpms_frame) \
+ W(dpms_standby_label) \
+ W(dpms_standby_mlabel) \
+ W(dpms_suspend_label) \
+ W(dpms_suspend_mlabel) \
+ W(dpms_off_label) \
+ W(dpms_off_mlabel) \
+ W(fade_label) \
+ W(demo) \
+ W(settings) \
+
+/* The widgets we reference from the prefs.ui file.
+ */
+#define ALL_DIALOG_WIDGETS \
+ W(opt_notebook) \
+ W(doc) \
+ W(settings_vbox) \
+ W(cmd_text) \
+ W(opt_frame) \
+ W(dialog_vbox) \
+ W(adv_button) \
+ W(std_button) \
+ W(cmd_label) \
+ W(manual) \
+ W(visual) \
+ W(visual_combo) \
+ W(reset_button) \
+ W(ok_button) \
+
+#define XSCREENSAVER_WINDOW_TYPE (xscreensaver_window_get_type())
+G_DECLARE_FINAL_TYPE (XScreenSaverWindow, xscreensaver_window,
+ XSCREENSAVER, WINDOW, GtkApplicationWindow)
+
+struct _XScreenSaverWindow {
+ GtkApplicationWindow parent;
+ state state;
+
+ GtkWidget
+# undef W
+# define W(NAME) * NAME,
+ ALL_WINDOW_WIDGETS
+ *_dummy;
+# undef W
+};
+
+G_DEFINE_TYPE (XScreenSaverWindow, xscreensaver_window,
+ GTK_TYPE_APPLICATION_WINDOW)
+
+
+#define XSCREENSAVER_DIALOG_TYPE (xscreensaver_dialog_get_type())
+G_DECLARE_FINAL_TYPE (XScreenSaverDialog, xscreensaver_dialog,
+ XSCREENSAVER, DIALOG, GtkDialog)
+
+struct _XScreenSaverDialog {
+ GtkApplicationWindow parent;
+ XScreenSaverWindow *main;
+ char *unedited_cmdline; /* Current hack command line before saving */
+
+ GtkWidget
+# undef W
+# define W(NAME) * NAME,
+ ALL_DIALOG_WIDGETS
+ *_dummy;
+# undef W
+};
+
+G_DEFINE_TYPE (XScreenSaverDialog, xscreensaver_dialog,
+ GTK_TYPE_DIALOG)
+
+
+static void hack_subproc_environment (Window preview_window_id, Bool debug_p);
static void populate_demo_window (state *, int list_elt);
static void populate_prefs_page (state *);
@@ -239,418 +293,379 @@ static void populate_popup_window (state *);
static Bool flush_dialog_changes_and_save (state *);
static Bool flush_popup_changes_and_save (state *);
+static Bool validate_image_directory (state *, const char *path);
static int maybe_reload_init_file (state *);
static void await_xscreensaver (state *);
static Bool xscreensaver_running_p (state *);
static void sensitize_menu_items (state *s, Bool force_p);
-static void force_dialog_repaint (state *s);
static void schedule_preview (state *, const char *cmd);
static void kill_preview_subproc (state *, Bool reset_p);
static void schedule_preview_check (state *);
+static void sensitize_demo_widgets (state *, Bool sensitive_p);
+static void kill_gnome_screensaver (state *);
+static void kill_kde_screensaver (state *);
-
-/* Prototypes of functions used by the Gtk-generated code, to avoid warnings.
- */
-void exit_menu_cb (GtkAction *, gpointer user_data);
-void about_menu_cb (GtkAction *, gpointer user_data);
-void doc_menu_cb (GtkAction *, gpointer user_data);
-void file_menu_cb (GtkAction *, gpointer user_data);
-void activate_menu_cb (GtkAction *, gpointer user_data);
-void lock_menu_cb (GtkAction *, gpointer user_data);
-void kill_menu_cb (GtkAction *, gpointer user_data);
-void restart_menu_cb (GtkWidget *, gpointer user_data);
-void run_this_cb (GtkButton *, gpointer user_data);
-void manual_cb (GtkButton *, gpointer user_data);
-void run_next_cb (GtkButton *, gpointer user_data);
-void run_prev_cb (GtkButton *, gpointer user_data);
-void pref_changed_cb (GtkWidget *, gpointer user_data);
-gboolean pref_changed_event_cb (GtkWidget *, GdkEvent *, gpointer user_data);
-void mode_menu_item_cb (GtkWidget *, gpointer user_data);
-void switch_page_cb (GtkNotebook *, GtkNotebookPage *,
- gint page_num, gpointer user_data);
-void browse_image_dir_cb (GtkButton *, gpointer user_data);
-void browse_text_file_cb (GtkButton *, gpointer user_data);
-void browse_text_program_cb (GtkButton *, gpointer user_data);
-void settings_cb (GtkButton *, gpointer user_data);
-void settings_adv_cb (GtkButton *, gpointer user_data);
-void settings_std_cb (GtkButton *, gpointer user_data);
-void settings_reset_cb (GtkButton *, gpointer user_data);
-void settings_switch_page_cb (GtkNotebook *, GtkNotebookPage *,
- gint page_num, gpointer user_data);
-void settings_cancel_cb (GtkButton *, gpointer user_data);
-void settings_ok_cb (GtkButton *, gpointer user_data);
-void preview_theme_cb (GtkWidget *, gpointer user_data);
-
-static void kill_gnome_screensaver (void);
-static void kill_kde_screensaver (void);
+/* Some pathname utilities */
-/* Some random utility functions
+/* Removed redundant . and .. components from the pathname.
+ Strip leading and trailing spaces.
+ Make it have a trailing slash if it should be a directory.
*/
-
-static GtkWidget *
-name_to_widget (state *s, const char *name)
+static char *
+normalize_pathname (const char *path, gboolean dir_p)
{
- GtkWidget *w;
- if (!s) abort();
- if (!name) abort();
- if (!*name) abort();
+ int L;
+ char *p2, *s;
+ if (!path) return 0;
+ if (!*path) return strdup ("");
-#ifdef HAVE_GTK2
- if (!s->gtk_ui)
- {
- /* First try to load the UI file from the current directory;
- if there isn't one there, check the installed directory.
- */
-# define UI_FILE "xscreensaver.ui"
- const char * const files[] = { UI_FILE,
- DEFAULT_ICONDIR "/" UI_FILE };
- int i;
+ /* Strip leading spaces */
+ while (isspace (*path)) path++;
- s->gtk_ui = gtk_builder_new ();
+ L = strlen (path);
+ p2 = (char *) malloc (L + 3);
+ strcpy (p2, path);
- for (i = 0; i < countof (files); i++)
+ /* Strip trailing spaces and slashes */
+ while (L > 0 && (isspace (p2[L-1]) || p2[L-1] == '/'))
+ p2[--L] = 0;
+
+ for (s = p2; s && *s; s++)
+ {
+ if (*s == '/' &&
+ (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
+ !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
{
- struct stat st;
- if (!stat (files[i], &st))
+ char *s0 = s;
+ while (s0 > p2 && s0[-1] != '/')
+ s0--;
+ if (s0 > p2)
{
- GError* error = NULL;
-
- if (gtk_builder_add_from_file (s->gtk_ui, files[i], &error))
- break;
- else
- {
- g_warning ("Couldn't load builder file %s: %s",
- files[i], error->message);
- g_error_free (error);
- }
+ s0--;
+ s += 3;
+ /* strcpy (s0, s); */
+ memmove(s0, s, strlen(s) + 1);
+ s = s0-1;
}
}
- if (i >= countof (files))
- {
- fprintf (stderr,
- "%s: could not load \"" UI_FILE "\"\n"
- "\tfrom " DEFAULT_ICONDIR "/ or current directory.\n",
- blurb());
- exit (-1);
- }
-# undef UI_FILE
-
- gtk_builder_connect_signals (s->gtk_ui, NULL);
+ else if (*s == '/' && !strncmp (s, "/./", 3)) { /* delete "/./" */
+ /* strcpy (s, s+2), s--; */
+ memmove(s, s+2, strlen(s+2) + 1);
+ s--;
+ }
+ else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
+ *s = 0, s--;
}
- w = GTK_WIDGET (gtk_builder_get_object (s->gtk_ui, name));
-
-#else /* !HAVE_GTK2 */
-
- w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
- name);
- if (w) return w;
- w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
- name);
-#endif /* HAVE_GTK2 */
- if (w) return w;
-
- fprintf (stderr, "%s: no widget \"%s\" (wrong UI file?)\n",
- blurb(), name);
- abort();
-}
-
-
-/* Why this behavior isn't automatic in *either* toolkit, I'll never know.
- Takes a scroller, viewport, or list as an argument.
- */
-static void
-ensure_selected_item_visible (GtkWidget *widget)
-{
-#ifdef HAVE_GTK2
- GtkTreePath *path;
- GtkTreeSelection *selection;
- GtkTreeIter iter;
- GtkTreeModel *model;
-
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
- if (!gtk_tree_selection_get_selected (selection, &model, &iter))
- path = gtk_tree_path_new_first ();
- else
- path = gtk_tree_model_get_path (model, &iter);
-
- gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
-
- gtk_tree_path_free (path);
+ /*
+ Normalize consecutive slashes.
+ Ignore doubled slashes after ":" to avoid mangling URLs.
+ */
-#else /* !HAVE_GTK2 */
+ for (s = p2; s && *s; s++){
+ if (*s == ':') continue;
+ if (!s[1] || !s[2]) continue;
+ while (s[1] == '/' && s[2] == '/')
+ /* strcpy (s+1, s+2); */
+ memmove (s+1, s+2, strlen(s+2) + 1);
+ }
- GtkScrolledWindow *scroller = 0;
- GtkViewport *vp = 0;
- GtkList *list_widget = 0;
- GList *slist;
- GList *kids;
- int nkids = 0;
- GtkWidget *selected = 0;
- int list_elt = -1;
- GtkAdjustment *adj;
- gint parent_h, child_y, child_h, children_h, ignore;
- double ratio_t, ratio_b;
+ /* and strip trailing whitespace for good measure. */
+ L = strlen(p2);
+ while (isspace(p2[L-1]))
+ p2[--L] = 0;
- if (GTK_IS_SCROLLED_WINDOW (widget))
- {
- scroller = GTK_SCROLLED_WINDOW (widget);
- vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
- list_widget = GTK_LIST (GTK_BIN(vp)->child);
- }
- else if (GTK_IS_VIEWPORT (widget))
- {
- vp = GTK_VIEWPORT (widget);
- scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
- list_widget = GTK_LIST (GTK_BIN(vp)->child);
- }
- else if (GTK_IS_LIST (widget))
+ if (dir_p)
{
- list_widget = GTK_LIST (widget);
- vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
- scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
+ p2[L++] = '/'; /* Add trailing slash */
+ p2[L] = 0;
}
- else
- abort();
-
- slist = list_widget->selection;
- selected = (slist ? GTK_WIDGET (slist->data) : 0);
- if (!selected)
- return;
- list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
+ return p2;
+}
- for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
- kids; kids = kids->next)
- nkids++;
- adj = gtk_scrolled_window_get_vadjustment (scroller);
-
- gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (vp)),
- &ignore, &ignore, &ignore, &parent_h, &ignore);
- gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (selected)),
- &ignore, &child_y, &ignore, &child_h, &ignore);
- children_h = nkids * child_h;
+/* Expand or un-expand ~/ to $HOME in a pathname, as requested.
+ Strip leading and trailing spaces.
+ Make it have a trailing slash if it should be a directory.
+ */
+static char *
+pathname_tilde (const char *p, gboolean add_p, gboolean dir_p)
+{
+ char *p2;
+ if (!p) return 0;
- ratio_t = ((double) child_y) / ((double) children_h);
- ratio_b = ((double) child_y + child_h) / ((double) children_h);
+ if (dir_p &&
+ (!strncasecmp (p, "http://", 7) ||
+ !strncasecmp (p, "https://", 8)))
+ dir_p = FALSE;
- if (adj->upper == 0.0) /* no items in list */
- return;
+ p2 = normalize_pathname (p, dir_p);
+ p = p2;
- if (ratio_t < (adj->value / adj->upper) ||
- ratio_b > ((adj->value + adj->page_size) / adj->upper))
+ if (add_p)
{
- double target;
- int slop = parent_h * 0.75; /* how much to overshoot by */
-
- if (ratio_t < (adj->value / adj->upper))
+ const char *home = getenv("HOME");
+ int L = strlen(home);
+ if (!strncmp (home, p, L) && p[L] == '/')
{
- double ratio_w = ((double) parent_h) / ((double) children_h);
- double ratio_l = (ratio_b - ratio_t);
- target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
- target += slop;
+ char *p3 = (char *) malloc (strlen (p) + 4);
+ strcpy (p3, "~");
+ strcat (p3, p + L);
+ free (p2);
+ p2 = p3;
}
- else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
- {
- target = ratio_t * adj->upper;
- target -= slop;
- }
-
- if (target > adj->upper - adj->page_size)
- target = adj->upper - adj->page_size;
- if (target < 0)
- target = 0;
-
- gtk_adjustment_set_value (adj, target);
}
-#endif /* !HAVE_GTK2 */
-}
+ else if (!strncmp (p, "~/", 2))
+ {
+ const char *home = getenv("HOME");
+ char *p3 = (char *) malloc (strlen (p) + strlen (home) + 4);
+ strcpy (p3, home);
+ strcat (p3, p + 1);
+ free (p2);
+ p2 = p3;
+ }
-static void
-warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
-{
- GtkWidget *shell = GTK_WIDGET (user_data);
- while (GET_PARENT (shell))
- shell = GET_PARENT (shell);
- gtk_widget_destroy (GTK_WIDGET (shell));
+ return p2;
}
-void restart_menu_cb (GtkWidget *widget, gpointer user_data);
-
-static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
+/* Is the path a directory that exists? */
+static gboolean
+directory_p (const char *path)
{
- restart_menu_cb (widget, user_data);
- warning_dialog_dismiss_cb (widget, user_data);
-}
+ char *p2 = pathname_tilde (path, FALSE, FALSE); /* no slash on dir */
+ struct stat st;
+ gboolean ok = FALSE;
-static void warning_dialog_killg_cb (GtkWidget *widget, gpointer user_data)
-{
- kill_gnome_screensaver ();
- warning_dialog_dismiss_cb (widget, user_data);
+ if (!p2 || !*p2)
+ ok = FALSE;
+ else if (stat (p2, &st))
+ ok = FALSE;
+ else if (!S_ISDIR (st.st_mode))
+ ok = FALSE;
+ else
+ ok = TRUE;
+ free (p2);
+ return ok;
}
-static void warning_dialog_killk_cb (GtkWidget *widget, gpointer user_data)
+
+/* Is the path a file (not directory) that exists? */
+static gboolean
+file_p (const char *path)
{
- kill_kde_screensaver ();
- warning_dialog_dismiss_cb (widget, user_data);
+ char *p2 = pathname_tilde (path, FALSE, FALSE);
+ struct stat st;
+ gboolean ok = FALSE;
+ if (!p2 || !*p2)
+ ok = FALSE;
+ else if (stat (p2, &st))
+ ok = FALSE;
+ else if (S_ISDIR (st.st_mode))
+ ok = FALSE;
+ else
+ ok = TRUE;
+ free (p2);
+ return ok;
}
-typedef enum { D_NONE, D_LAUNCH, D_GNOME, D_KDE } dialog_button;
+
+/* See if the directory has at least one image file under it.
+ Recurses to at most the given depth, chasing symlinks.
+ To do this properly would mean running "xscreensaver-getimage-file"
+ and seeing if it found anything, but that might take a long time to
+ build the cache the first time, so this is close enough.
+ */
static Bool
-warning_dialog (GtkWidget *parent, const char *message,
- dialog_button button_type, int center)
+image_files_p (const char *path, int max_depth)
{
- char *msg = strdup (message);
- char *head;
+ const char * const exts[] = {
+ "jpg", "jpeg", "pjpeg", "pjpg", "png", "gif",
+ "tif", "tiff", "xbm", "xpm", "svg",
+ };
+ struct dirent *de;
+ Bool ok = FALSE;
+ char *p2 = pathname_tilde (path, FALSE, FALSE); /* no slash on dir */
+ DIR *dir = opendir (p2);
+ if (!dir) goto DONE;
+
+ while (!ok && (de = readdir (dir)))
+ {
+ struct stat st;
+ const char *f = de->d_name;
+ char *f2;
+ if (*f == '.') continue;
- GtkWidget *dialog = gtk_dialog_new ();
- GtkWidget *label = 0;
- GtkWidget *ok = 0;
- GtkWidget *cancel = 0;
- int i = 0;
+ f2 = (char *) malloc (strlen(p2) + strlen(f) + 10);
+ strcpy (f2, p2);
+ strcat (f2, "/");
+ strcat (f2, f);
- while (parent && !GET_WINDOW (parent))
- parent = GET_PARENT (parent);
+ if (!stat (f2, &st))
+ {
+ if (S_ISDIR (st.st_mode))
+ {
+ if (max_depth > 0 && image_files_p (f2, max_depth - 1))
+ ok = TRUE;
+ }
+ else
+ {
+ int i;
+ const char *ext = strrchr (f, '.');
+ if (ext)
+ for (i = 0; i < countof(exts); i++)
+ if (!strcasecmp (ext+1, exts[i]))
+ {
+ /* fprintf (stderr, "%s: found %s\n", blurb(), f2); */
+ ok = TRUE;
+ break;
+ }
+ }
+ }
- if (!parent ||
- !GET_WINDOW (parent)) /* too early to pop up transient dialogs */
- {
- fprintf (stderr,
- "%s: too early for warning dialog?"
- "\n\n\t%s\n\n",
- progname, message);
- free(msg);
- return False;
+ free (f2);
}
- head = msg;
- while (head)
- {
- char name[20];
- char *s = strchr (head, '\n');
- if (s) *s = 0;
+ closedir (dir);
+ DONE:
+ free (p2);
+ return ok;
+}
- sprintf (name, "label%d", i++);
- {
- label = gtk_label_new (head);
-#ifdef HAVE_GTK2
- gtk_label_set_selectable (GTK_LABEL (label), TRUE);
-#endif /* HAVE_GTK2 */
+/* Some random utility functions
+ */
-#ifndef HAVE_GTK2
- if (i == 1)
- {
- GTK_WIDGET (label)->style =
- gtk_style_copy (GTK_WIDGET (label)->style);
- GTK_WIDGET (label)->style->font =
- gdk_font_load (get_string_resource("warning_dialog.headingFont",
- "Dialog.Font"));
- gtk_widget_set_style (GTK_WIDGET (label),
- GTK_WIDGET (label)->style);
- }
-#endif /* !HAVE_GTK2 */
- if (center <= 0)
- gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
- gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
- label, TRUE, TRUE, 0);
- gtk_widget_show (label);
- }
+/* Why this behavior isn't automatic, I'll never understand.
+ */
+static void
+ensure_selected_item_visible (state *s, GtkWidget *widget)
+{
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
- if (s)
- head = s+1;
- else
- head = 0;
+ /* Find the path of the selected row in the list.
+ */
+ GtkTreeView *list_widget = GTK_TREE_VIEW (win->list);
+ GtkTreeSelection *selection = gtk_tree_view_get_selection (list_widget);
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreePath *path;
- center--;
- }
+ if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+ path = gtk_tree_path_new_first ();
+ else
+ path = gtk_tree_model_get_path (model, &iter);
- label = gtk_label_new ("");
- gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
- label, TRUE, TRUE, 0);
- gtk_widget_show (label);
+ /* Make this item be visible and selected. */
+ gtk_tree_view_set_cursor (list_widget, path, NULL, FALSE);
- label = gtk_hbutton_box_new ();
- gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
- label, TRUE, TRUE, 0);
+ /* Make the scroller show that item at the center of the viewport.
+ The set_cursor() call, above, makes the item be visible, but it hugs
+ the top or bottom edge of the viewport, instead of providing more
+ surrounding context.
-#ifdef HAVE_GTK2
- if (button_type != D_NONE)
+ Doing the following in list_select_changed_cb() instead of here makes
+ the list vertically re-center when using the cursor keys instead of
+ hugging the top or bottom (good) but also makes it re-center when
+ clicking on a new item with the mouse (bad).
+ */
+ if (gtk_widget_get_realized (GTK_WIDGET (list_widget)))
{
- cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
- gtk_container_add (GTK_CONTAINER (label), cancel);
+ GdkWindow *bin = gtk_tree_view_get_bin_window (list_widget);
+ int binh = gdk_window_get_height (bin);
+ GdkRectangle r;
+ gtk_tree_view_get_cell_area (list_widget, path, NULL, &r);
+ gtk_tree_view_convert_widget_to_tree_coords (list_widget,
+ r.x, r.y, &r.x, &r.y);
+ gtk_tree_view_scroll_to_point (list_widget, r.x, r.y - binh / 2);
}
- ok = gtk_button_new_from_stock (GTK_STOCK_OK);
- gtk_container_add (GTK_CONTAINER (label), ok);
-
-#else /* !HAVE_GTK2 */
-
- ok = gtk_button_new_with_label ("OK");
- gtk_container_add (GTK_CONTAINER (label), ok);
-
- if (button_type != D_NONE)
- {
- cancel = gtk_button_new_with_label ("Cancel");
- gtk_container_add (GTK_CONTAINER (label), cancel);
- }
+ gtk_tree_path_free (path);
+}
-#endif /* !HAVE_GTK2 */
- gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
- gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
- gtk_window_set_title (GTK_WINDOW (dialog), progclass);
- SET_CAN_DEFAULT (ok);
- gtk_widget_show (ok);
- gtk_widget_grab_focus (ok);
+static void
+warning_dialog_cb (GtkDialog *dialog, gint response_id, gpointer user_data)
+{
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
+ if (s->debug_p)
+ fprintf (stderr, "%s: dialog response %d\n", blurb(), response_id);
+ switch (response_id) {
+ case D_LAUNCH: restart_menu_cb (GTK_WIDGET (dialog), user_data); break;
+ case D_GNOME: kill_gnome_screensaver (s); break;
+ case D_KDE: kill_kde_screensaver (s); break;
+ default: /* D_NONE or GTK_RESPONSE_DELETE_EVENT */
+ break;
+ }
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+}
- if (cancel)
- {
- SET_CAN_DEFAULT (cancel);
- gtk_widget_show (cancel);
- }
- gtk_widget_show (label);
- gtk_widget_show (dialog);
- if (button_type != D_NONE)
- {
- GtkSignalFunc fn;
- switch (button_type) {
- case D_LAUNCH: fn = GTK_SIGNAL_FUNC (warning_dialog_restart_cb); break;
- case D_GNOME: fn = GTK_SIGNAL_FUNC (warning_dialog_killg_cb); break;
- case D_KDE: fn = GTK_SIGNAL_FUNC (warning_dialog_killk_cb); break;
- default: abort(); break;
- }
- gtk_signal_connect_object (GTK_OBJECT (ok), "clicked", fn,
- (gpointer) dialog);
- gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
- GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
- (gpointer) dialog);
- }
- else
- {
- gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
- GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
- (gpointer) dialog);
- }
+static Bool
+warning_dialog_1 (GtkWindow *win,
+ const char *title,
+ const char *message,
+ dialog_button button_type)
+{
+ GtkWidget *dialog =
+ (button_type == D_NONE
+ ? gtk_dialog_new_with_buttons (title, win,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ _("_OK"), D_NONE,
+ NULL)
+ : gtk_dialog_new_with_buttons (title, win,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ (button_type == D_LAUNCH ? _("Launch") :
+ button_type == D_GNOME ? _("Kill") :
+ button_type == D_KDE ? _("Kill") :
+ _("_OK")),
+ button_type,
+ _("_Cancel"), D_NONE,
+ NULL));
+ GtkWidget *content_area =
+ gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+ GtkWidget *hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+ GtkWidget *vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+ GtkWidget *im = gtk_image_new_from_icon_name ("dialog-warning",
+ GTK_ICON_SIZE_DIALOG);
+ GtkWidget *label = gtk_label_new (message);
+ int margin = 32;
+
+ gtk_box_pack_start (GTK_BOX (hbox), im, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (content_area), hbox);
+
+ gtk_widget_set_margin_start (hbox, margin);
+ gtk_widget_set_margin_end (hbox, margin);
+ gtk_widget_set_margin_top (hbox, margin);
+ gtk_widget_set_margin_bottom (hbox, margin / 2);
+
+ gtk_widget_set_margin_start (label, margin / 2);
+ gtk_widget_set_valign (im, GTK_ALIGN_START);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (warning_dialog_cb),
+ win);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), button_type);
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), win);
+ gtk_widget_show_all (dialog);
- gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
- GET_WINDOW (GTK_WIDGET (parent)));
+ return TRUE;
+}
-#ifdef HAVE_GTK2
- gtk_window_present (GTK_WINDOW (dialog));
-#else /* !HAVE_GTK2 */
- gdk_window_show (GTK_WIDGET (dialog)->window);
- gdk_window_raise (GTK_WIDGET (dialog)->window);
-#endif /* !HAVE_GTK2 */
- free (msg);
- return True;
+void
+warning_dialog (GtkWindow *win, const char *title, const char *message)
+{
+ warning_dialog_1 (win, title, message, D_NONE);
}
@@ -660,8 +675,13 @@ run_cmd (state *s, Atom command, int arg)
char *err = 0;
int status;
+ if (!s->dpy) return;
flush_dialog_changes_and_save (s);
- status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
+
+ if (s->debug_p)
+ fprintf (stderr, "%s: command: %s %d\n", blurb(),
+ XGetAtomName (s->dpy, command), arg);
+ status = xscreensaver_command (s->dpy, command, arg, FALSE, &err);
/* Kludge: ignore the spurious "window unexpectedly deleted" errors... */
if (status < 0 && err && strstr (err, "unexpectedly deleted"))
@@ -670,16 +690,12 @@ run_cmd (state *s, Atom command, int arg)
if (status < 0)
{
char buf [255];
- if (err)
- sprintf (buf, "Error:\n\n%s", err);
- else
- strcpy (buf, "Unknown error!");
- warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
+ sprintf (buf, "%.100s", (err ? err : _("Unknown error!")));
+ warning_dialog (s->window, _("Error"), buf);
}
if (err) free (err);
- sensitize_menu_items (s, True);
- force_dialog_repaint (s);
+ sensitize_menu_items (s, TRUE);
}
@@ -690,14 +706,17 @@ run_hack (state *s, int list_elt, Bool report_errors_p)
char *err = 0;
int status;
+ if (!s->dpy) return;
if (list_elt < 0) return;
hack_number = s->list_elt_to_hack_number[list_elt];
flush_dialog_changes_and_save (s);
schedule_preview (s, 0);
- status = xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
- False, &err);
+ if (s->debug_p)
+ fprintf (stderr, "%s: command: DEMO %d\n", blurb(), hack_number + 1);
+ status = xscreensaver_command (s->dpy, XA_DEMO, hack_number + 1,
+ FALSE, &err);
if (status < 0 && report_errors_p)
{
@@ -711,11 +730,8 @@ run_hack (state *s, int list_elt, Bool report_errors_p)
if (status < 0)
{
char buf [255];
- if (err)
- sprintf (buf, "Error:\n\n%s", err);
- else
- strcpy (buf, "Unknown error!");
- warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
+ sprintf (buf, "%.100s", err ? err : _("Unknown error!"));
+ warning_dialog (s->window, _("Error"), buf);
}
}
else
@@ -723,283 +739,232 @@ run_hack (state *s, int list_elt, Bool report_errors_p)
/* The error is that the daemon isn't running;
offer to restart it.
*/
- const char *d = DisplayString (GDK_DISPLAY());
+ const char *d = DisplayString (s->dpy);
char msg [1024];
sprintf (msg,
- _("Warning:\n\n"
- "The XScreenSaver daemon doesn't seem to be running\n"
- "on display \"%s\". Launch it now?"),
+ _("The XScreenSaver daemon doesn't seem to be running\n"
+ "on display \"%.25s\". Launch it now?"),
d);
- warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
+ warning_dialog_1 (s->window, _("Warning"), msg, D_LAUNCH);
}
}
if (err) free (err);
- sensitize_menu_items (s, False);
+ sensitize_menu_items (s, FALSE);
}
-
-/* Button callbacks
+static pid_t
+fork_and_exec (state *s, int argc, char **argv)
+{
+ char buf [255];
+ pid_t forked = fork();
+ switch ((int) forked) {
+ case -1:
+ sprintf (buf, "%s: couldn't fork", blurb());
+ perror (buf);
+ break;
+
+ case 0:
+ if (s->dpy) close (ConnectionNumber (s->dpy));
+ execvp (argv[0], argv); /* shouldn't return. */
+
+ sprintf (buf, "%s: pid %lu: couldn't exec %s", blurb(),
+ (unsigned long) getpid(), argv[0]);
+ perror (buf);
+ exit (1); /* exits child fork */
+ break;
+
+ default: /* parent fork */
+
+ /* Put it in its own process group so that this process getting SIGTERM
+ does not propagate to the forked process. */
+ if (setpgid (forked, 0))
+ {
+ char buf [255];
+ sprintf (buf, "%s: setpgid %d", blurb(), forked);
+ perror (buf);
+ }
+
+ if (s->debug_p)
+ {
+ int i;
+ fprintf (stderr, "%s: pid %lu: forked:", blurb(),
+ (unsigned long) forked);
+ for (i = 0; i < argc; i++)
+ if (strchr (argv[i], ' '))
+ fprintf (stderr, " \"%s\"", argv[i]);
+ else
+ fprintf (stderr, " %s", argv[i]);
+ fprintf (stderr, "\n");
+ }
- According to Eric Lassauge, this G_MODULE_EXPORT crud is needed to make
- GTK work on Cygwin; apparently all GTK callbacks need this magic extra
- declaration. I do not pretend to understand.
- */
+ break;
+ }
-G_MODULE_EXPORT void
-exit_menu_cb (GtkAction *menu_action, gpointer user_data)
-{
- state *s = global_state_kludge; /* I hate C so much... */
- flush_dialog_changes_and_save (s);
- kill_preview_subproc (s, False);
- gtk_main_quit ();
+ return forked;
}
-static gboolean
-wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
+
+
+/****************************************************************************
+
+ XScreenSaverWindow callbacks, referenced by demo.ui.
+
+ ****************************************************************************/
+
+/* File menu / Quit */
+G_MODULE_EXPORT void
+quit_menu_cb (GtkAction *menu_action, gpointer user_data)
{
- state *s = (state *) data;
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
+ if (s->debug_p) fprintf (stderr, "%s: quit menu\n", blurb());
flush_dialog_changes_and_save (s);
- gtk_main_quit ();
- return TRUE;
+ kill_preview_subproc (s, FALSE);
+ g_application_quit (G_APPLICATION (
+ gtk_window_get_application (GTK_WINDOW (win))));
}
+/* Help menu / About */
G_MODULE_EXPORT void
about_menu_cb (GtkAction *menu_action, gpointer user_data)
{
-#if 1
- /* Let's just pop up the splash dialog instead. */
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
+ if (s->debug_p) fprintf (stderr, "%s: about menu\n", blurb());
preview_theme_cb (NULL, user_data);
-#else
- char msg [2048];
- char copy[1024];
- char *desc = _("For updates, check https://www.jwz.org/xscreensaver/");
-
- char *version = strdup (screensaver_id + 17);
- char *year = strchr (version, '-');
- char *s = strchr (version, ' ');
- *s = 0;
- year = strchr (year+1, '-') + 1;
- s = strchr (year, ')');
- *s = 0;
-
- /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
- non-ASCII characters aren't allowed in localizable string keys."
- (I don't want to just use (c) instead of © because that doesn't
- look as good in the plain-old default Latin1 "C" locale.)
- */
-#ifdef HAVE_GTK2
- sprintf(copy, ("Copyright \xC2\xA9 1991-%s %s"), year, s);
-#else /* !HAVE_GTK2 */
- sprintf(copy, ("Copyright \251 1991-%s %s"), year, s);
-#endif /* !HAVE_GTK2 */
-
- sprintf (msg, "%s\n\n%s", copy, desc);
-
- /* I can't make gnome_about_new() work here -- it starts dying in
- gdk_imlib_get_visual() under gnome_about_new(). If this worked,
- then this might be the thing to do:
-
- #ifdef HAVE_CRAPPLET
- {
- const gchar *auth[] = { 0 };
- GtkWidget *about = gnome_about_new (progclass, version, "", auth, desc,
- "xscreensaver.xpm");
- gtk_widget_show (about);
- }
- #else / * GTK but not GNOME * /
- ...
- */
- {
- GdkColormap *colormap;
- GdkPixmap *gdkpixmap;
- GdkBitmap *mask;
-
- GtkWidget *dialog = gtk_dialog_new ();
- GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
- GSList *proxies = gtk_action_get_proxies (menu_action);
- GtkWidget *parent = GTK_WIDGET (proxies->data);
- while (GET_PARENT (parent))
- parent = GET_PARENT (parent);
-
- hbox = gtk_hbox_new (FALSE, 20);
- gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
- hbox, TRUE, TRUE, 0);
-
- colormap = gtk_widget_get_colormap (parent);
- gdkpixmap =
- gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
- (gchar **) logo_180_xpm);
- icon = gtk_pixmap_new (gdkpixmap, mask);
- gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
-
- gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
-
- vbox = gtk_vbox_new (FALSE, 0);
- gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
-
- label1 = gtk_label_new (version);
- gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
- gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
- gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
-
-#ifndef HAVE_GTK2
- GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
- GTK_WIDGET (label1)->style->font =
- gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
- gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
-#endif /* HAVE_GTK2 */
-
- label2 = gtk_label_new (msg);
- gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
- gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
- gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
-
-#ifndef HAVE_GTK2
- GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
- GTK_WIDGET (label2)->style->font =
- gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
- gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
-#endif /* HAVE_GTK2 */
-
- hb = gtk_hbutton_box_new ();
-
- gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
- hb, TRUE, TRUE, 0);
-
-#ifdef HAVE_GTK2
- ok = gtk_button_new_from_stock (GTK_STOCK_OK);
-#else /* !HAVE_GTK2 */
- ok = gtk_button_new_with_label (_("OK"));
-#endif /* !HAVE_GTK2 */
- gtk_container_add (GTK_CONTAINER (hb), ok);
-
- gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
- gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
- gtk_window_set_title (GTK_WINDOW (dialog), progclass);
-
- gtk_widget_show (hbox);
- gtk_widget_show (icon);
- gtk_widget_show (vbox);
- gtk_widget_show (label1);
- gtk_widget_show (label2);
- gtk_widget_show (hb);
- gtk_widget_show (ok);
- gtk_widget_show (dialog);
-
- gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
- GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
- (gpointer) dialog);
- gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
- GET_WINDOW (GTK_WIDGET (parent)));
- gdk_window_show (GET_WINDOW (GTK_WIDGET (dialog)));
- gdk_window_raise (GET_WINDOW (GTK_WIDGET (dialog)));
- }
-#endif /* 0 */
}
+/* Help menu / Documentation */
G_MODULE_EXPORT void
doc_menu_cb (GtkAction *menu_action, gpointer user_data)
{
- state *s = global_state_kludge; /* I hate C so much... */
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
saver_preferences *p = &s->prefs;
char *help_command;
+ int ac = 0;
+ char *av[10];
+
+ if (s->debug_p) fprintf (stderr, "%s: doc menu\n", blurb());
if (!p->help_url || !*p->help_url)
{
- warning_dialog (s->toplevel_widget,
- _("Error:\n\n"
- "No Help URL has been specified.\n"), D_NONE, 100);
+ warning_dialog (GTK_WINDOW (win), _("Error"),
+ _("No Help URL has been specified.\n"));
return;
}
help_command = (char *) malloc (strlen (p->load_url_command) +
(strlen (p->help_url) * 5) + 20);
- strcpy (help_command, "( ");
- sprintf (help_command + strlen(help_command),
- p->load_url_command,
+ sprintf (help_command, p->load_url_command,
p->help_url, p->help_url, p->help_url, p->help_url, p->help_url);
- strcat (help_command, " ) &");
- if (system (help_command) < 0)
- fprintf (stderr, "%s: fork error\n", blurb());
+
+ av[ac++] = "/bin/sh";
+ av[ac++] = "-c";
+ av[ac++] = help_command;
+ av[ac] = 0;
+ fork_and_exec (s, ac, av);
free (help_command);
}
+/* File menu opened */
G_MODULE_EXPORT void
file_menu_cb (GtkAction *menu_action, gpointer user_data)
{
- state *s = global_state_kludge; /* I hate C so much... */
- sensitize_menu_items (s, False);
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
+ if (s->debug_p) fprintf (stderr, "%s: file menu post\n", blurb());
+ sensitize_menu_items (s, FALSE);
}
+/* File menu / Activate */
G_MODULE_EXPORT void
activate_menu_cb (GtkAction *menu_action, gpointer user_data)
{
- state *s = global_state_kludge; /* I hate C so much... */
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
+ if (s->debug_p) fprintf (stderr, "%s: activate menu\n", blurb());
run_cmd (s, XA_ACTIVATE, 0);
}
+/* File menu / Lock */
G_MODULE_EXPORT void
lock_menu_cb (GtkAction *menu_action, gpointer user_data)
{
- state *s = global_state_kludge; /* I hate C so much... */
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
+ if (s->debug_p) fprintf (stderr, "%s: lock menu\n", blurb());
run_cmd (s, XA_LOCK, 0);
}
+/* File menu / Kill daemon */
G_MODULE_EXPORT void
kill_menu_cb (GtkAction *menu_action, gpointer user_data)
{
- state *s = global_state_kludge; /* I hate C so much... */
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
+ if (s->debug_p) fprintf (stderr, "%s: kill menu\n", blurb());
run_cmd (s, XA_EXIT, 0);
}
+/* File menu / Restart */
G_MODULE_EXPORT void
restart_menu_cb (GtkWidget *widget, gpointer user_data)
{
- state *s = global_state_kludge; /* I hate C so much... */
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
+ int ac = 0;
+ char *av[10];
+ if (s->debug_p) fprintf (stderr, "%s: restart menu\n", blurb());
+ if (!s->dpy) return;
flush_dialog_changes_and_save (s);
- xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
+ xscreensaver_command (s->dpy, XA_EXIT, 0, FALSE, NULL);
sleep (1);
- if (system ("xscreensaver -splash &") < 0)
- fprintf (stderr, "%s: fork error\n", blurb());
+ av[ac++] = "xscreensaver";
+ av[ac++] = "--splash";
+ if (s->debug_p) av[ac++] = "--verbose";
+ av[ac] = 0;
+ fork_and_exec (s, ac, av);
await_xscreensaver (s);
}
+
static Bool
xscreensaver_running_p (state *s)
{
- Display *dpy = GDK_DISPLAY();
char *rversion = 0;
- server_xscreensaver_version (dpy, &rversion, 0, 0);
+ if (!s->dpy) return FALSE;
+ server_xscreensaver_version (s->dpy, &rversion, 0, 0);
if (!rversion)
- return False;
+ return FALSE;
free (rversion);
- return True;
+ return TRUE;
}
static void
await_xscreensaver (state *s)
{
int countdown = 5;
- Bool ok = False;
+ Bool ok = FALSE;
while (!ok && (--countdown > 0))
if (xscreensaver_running_p (s))
- ok = True;
+ ok = TRUE;
else
sleep (1); /* If it's not there yet, wait a second... */
- sensitize_menu_items (s, True);
+ sensitize_menu_items (s, TRUE);
if (! ok)
{
@@ -1009,37 +974,19 @@ await_xscreensaver (state *s)
Bool root_p = (geteuid () == 0);
strcpy (buf,
- _("Error:\n\n"
- "The xscreensaver daemon did not start up properly.\n"
+ _("The xscreensaver daemon did not start up properly.\n"
"\n"));
if (root_p)
- strcat (buf, STFU
- _("You are running as root. This usually means that xscreensaver\n"
- "was unable to contact your X server because access control is\n"
- "turned on."
-/*
- " Try running this command:\n"
- "\n"
- " xhost +localhost\n"
- "\n"
- "and then selecting `File / Restart Daemon'.\n"
- "\n"
- "Note that turning off access control will allow anyone logged\n"
- "on to this machine to access your screen, which might be\n"
- "considered a security problem. Please read the xscreensaver\n"
- "manual and FAQ for more information.\n"
- */
- "\n"
- "You shouldn't run X as root. Instead, you should log in as a\n"
- "normal user, and `sudo' as necessary."));
+ strcat (buf,
+ _("You are running as root. Don't do that. Instead, you should\n"
+ "log in as a normal user and use `sudo' as necessary.")
+ );
else
strcat (buf, _("Please check your $PATH and permissions."));
- warning_dialog (s->toplevel_widget, buf, D_NONE, 1);
+ warning_dialog (s->window, _("Error"), buf);
}
-
- force_dialog_repaint (s);
}
@@ -1050,12 +997,14 @@ selected_list_element (state *s)
}
+/* Write the settings to disk; call this only when changes have been made.
+ */
static int
demo_write_init_file (state *s, saver_preferences *p)
{
- Display *dpy = GDK_DISPLAY();
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
- if (!write_init_file (dpy, p, s->short_version, False))
+ if (!write_init_file (s->dpy, p, s->short_version, FALSE))
{
if (s->debug_p)
fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
@@ -1065,14 +1014,13 @@ demo_write_init_file (state *s, saver_preferences *p)
{
const char *f = init_file_name();
if (!f || !*f)
- warning_dialog (s->toplevel_widget,
- _("Error:\n\nCouldn't determine init file name!\n"),
- D_NONE, 100);
+ warning_dialog (GTK_WINDOW (win), _("Error"),
+ _("Couldn't determine init file name!\n"));
else
{
char *b = (char *) malloc (strlen(f) + 1024);
- sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
- warning_dialog (s->toplevel_widget, b, D_NONE, 100);
+ sprintf (b, _("Couldn't write %s\n"), f);
+ warning_dialog (GTK_WINDOW (win), _("Error"), b);
free (b);
}
return -1;
@@ -1080,106 +1028,57 @@ demo_write_init_file (state *s, saver_preferences *p)
}
+/* The "Preview" button on the main page. */
G_MODULE_EXPORT void
run_this_cb (GtkButton *button, gpointer user_data)
{
- state *s = global_state_kludge; /* I hate C so much... */
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
int list_elt = selected_list_element (s);
if (list_elt < 0) return;
- if (!flush_dialog_changes_and_save (s))
- run_hack (s, list_elt, True);
-}
-
-
-G_MODULE_EXPORT void
-manual_cb (GtkButton *button, gpointer user_data)
-{
- Display *dpy = GDK_DISPLAY();
- state *s = global_state_kludge; /* I hate C so much... */
- saver_preferences *p = &s->prefs;
- GtkWidget *list_widget = name_to_widget (s, "list");
- int list_elt = selected_list_element (s);
- int hack_number;
- char *name, *name2, *cmd, *str;
- char *oname = 0;
- if (list_elt < 0) return;
- hack_number = s->list_elt_to_hack_number[list_elt];
-
+ if (s->debug_p) fprintf (stderr, "%s: preview button\n", blurb());
flush_dialog_changes_and_save (s);
- ensure_selected_item_visible (list_widget);
-
- name = strdup (p->screenhacks[hack_number]->command);
- name2 = name;
- oname = name;
- while (isspace (*name2)) name2++;
- str = name2;
- while (*str && !isspace (*str)) str++;
- *str = 0;
- str = strrchr (name2, '/');
- if (str) name2 = str+1;
-
- cmd = get_string_resource (dpy, "manualCommand", "ManualCommand");
- if (cmd)
- {
- char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
- strcpy (cmd2, "( ");
- sprintf (cmd2 + strlen (cmd2),
- cmd,
- name2, name2, name2, name2);
- strcat (cmd2, " ) &");
- if (system (cmd2) < 0)
- fprintf (stderr, "%s: fork error\n", blurb());
- free (cmd2);
- }
- else
- {
- warning_dialog (GTK_WIDGET (button),
- _("Error:\n\nno `manualCommand' resource set."),
- D_NONE, 100);
- }
-
- free (oname);
+ run_hack (s, list_elt, TRUE);
}
static void
force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
{
- GtkWidget *parent = name_to_widget (s, "scroller");
- gboolean was = GET_SENSITIVE (parent);
-#ifdef HAVE_GTK2
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
+ GtkWidget *parent = win->scroller;
+ gboolean was = gtk_widget_get_sensitive (parent);
GtkTreeIter iter;
GtkTreeModel *model;
GtkTreeSelection *selection;
-#endif /* HAVE_GTK2 */
- if (!was) gtk_widget_set_sensitive (parent, True);
-#ifdef HAVE_GTK2
+ if (!was) gtk_widget_set_sensitive (parent, TRUE);
model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
if (!model) abort();
if (gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt))
{
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
gtk_tree_selection_select_iter (selection, &iter);
+ if (s->debug_p)
+ fprintf (stderr, "%s: select list elt %d\n", blurb(), list_elt);
}
-#else /* !HAVE_GTK2 */
- gtk_list_select_item (GTK_LIST (list), list_elt);
-#endif /* !HAVE_GTK2 */
- if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
- if (!was) gtk_widget_set_sensitive (parent, False);
+ if (scroll_p) ensure_selected_item_visible (s, GTK_WIDGET (list));
+ if (!was) gtk_widget_set_sensitive (parent, FALSE);
}
+/* The down arrow */
G_MODULE_EXPORT void
run_next_cb (GtkButton *button, gpointer user_data)
{
- state *s = global_state_kludge; /* I hate C so much... */
- /* saver_preferences *p = &s->prefs; */
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
Bool ops = s->preview_suppressed_p;
-
- GtkWidget *list_widget = name_to_widget (s, "list");
+ GtkWidget *list_widget = win->list;
int list_elt = selected_list_element (s);
+ if (s->debug_p) fprintf (stderr, "%s: down arrow\n", blurb());
+
if (list_elt < 0)
list_elt = 0;
else
@@ -1188,27 +1087,30 @@ run_next_cb (GtkButton *button, gpointer user_data)
if (list_elt >= s->list_count)
list_elt = 0;
- s->preview_suppressed_p = True;
+ s->preview_suppressed_p = TRUE;
flush_dialog_changes_and_save (s);
- force_list_select_item (s, list_widget, list_elt, True);
+ force_list_select_item (s, list_widget, list_elt, TRUE);
populate_demo_window (s, list_elt);
- run_hack (s, list_elt, False);
+ populate_popup_window (s);
+ run_hack (s, list_elt, FALSE);
s->preview_suppressed_p = ops;
}
+/* The up arrow */
G_MODULE_EXPORT void
run_prev_cb (GtkButton *button, gpointer user_data)
{
- state *s = global_state_kludge; /* I hate C so much... */
- /* saver_preferences *p = &s->prefs; */
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
Bool ops = s->preview_suppressed_p;
-
- GtkWidget *list_widget = name_to_widget (s, "list");
+ GtkWidget *list_widget = win->list;
int list_elt = selected_list_element (s);
+ if (s->debug_p) fprintf (stderr, "%s: up arrow\n", blurb());
+
if (list_elt < 0)
list_elt = s->list_count - 1;
else
@@ -1217,19 +1119,20 @@ run_prev_cb (GtkButton *button, gpointer user_data)
if (list_elt < 0)
list_elt = s->list_count - 1;
- s->preview_suppressed_p = True;
+ s->preview_suppressed_p = TRUE;
flush_dialog_changes_and_save (s);
- force_list_select_item (s, list_widget, list_elt, True);
+ force_list_select_item (s, list_widget, list_elt, TRUE);
populate_demo_window (s, list_elt);
- run_hack (s, list_elt, False);
+ populate_popup_window (s);
+ run_hack (s, list_elt, FALSE);
s->preview_suppressed_p = ops;
}
-/* Writes the given settings into prefs.
- Returns true if there was a change, False otherwise.
+/* Writes the settings of the given hack into prefs.
+ Returns true if there was a change, FALSE otherwise.
command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
*/
static Bool
@@ -1240,7 +1143,7 @@ flush_changes (state *s,
const char *visual)
{
saver_preferences *p = &s->prefs;
- Bool changed = False;
+ Bool changed = FALSE;
screenhack *hack;
int hack_number;
if (list_elt < 0 || list_elt >= s->list_count)
@@ -1253,7 +1156,7 @@ flush_changes (state *s,
enabled_p != hack->enabled_p)
{
hack->enabled_p = enabled_p;
- changed = True;
+ changed = TRUE;
if (s->debug_p)
fprintf (stderr, "%s: \"%s\": enabled => %d\n",
blurb(), hack->name, enabled_p);
@@ -1265,7 +1168,7 @@ flush_changes (state *s,
{
if (hack->command) free (hack->command);
hack->command = strdup (command);
- changed = True;
+ changed = TRUE;
if (s->debug_p)
fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
blurb(), hack->name, command);
@@ -1281,7 +1184,7 @@ flush_changes (state *s,
{
if (hack->visual) free (hack->visual);
hack->visual = strdup (visual);
- changed = True;
+ changed = TRUE;
if (s->debug_p)
fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
blurb(), hack->name, visual);
@@ -1302,7 +1205,7 @@ hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
{
int value;
if (!sec_p || strchr (line, ':'))
- value = parse_time ((char *) line, sec_p, True);
+ value = parse_time ((char *) line, sec_p, TRUE);
else
{
char c;
@@ -1316,11 +1219,9 @@ hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
if (value < 0)
{
char b[255];
- sprintf (b,
- _("Error:\n\n"
- "Unparsable time format: \"%s\"\n"),
+ sprintf (b, _("Unparsable time format: \"%.100s\"\n"),
line);
- warning_dialog (s->toplevel_widget, b, D_NONE, 100);
+ warning_dialog (s->window, _("Error"), b);
}
else
*store = value;
@@ -1328,97 +1229,6 @@ hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
}
-static Bool
-directory_p (const char *path)
-{
- struct stat st;
- if (!path || !*path)
- return False;
- else if (stat (path, &st))
- return False;
- else if (!S_ISDIR (st.st_mode))
- return False;
- else
- return True;
-}
-
-static Bool
-file_p (const char *path)
-{
- struct stat st;
- if (!path || !*path)
- return False;
- else if (stat (path, &st))
- return False;
- else if (S_ISDIR (st.st_mode))
- return False;
- else
- return True;
-}
-
-static char *
-normalize_directory (const char *path)
-{
- int L;
- char *p2, *s;
- if (!path || !*path) return 0;
- L = strlen (path);
- p2 = (char *) malloc (L + 2);
- strcpy (p2, path);
- if (p2[L-1] == '/') /* remove trailing slash */
- p2[--L] = 0;
-
- for (s = p2; s && *s; s++)
- {
- if (*s == '/' &&
- (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
- !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
- {
- char *s0 = s;
- while (s0 > p2 && s0[-1] != '/')
- s0--;
- if (s0 > p2)
- {
- s0--;
- s += 3;
- /* strcpy (s0, s); */
- memmove(s0, s, strlen(s) + 1);
- s = s0-1;
- }
- }
- else if (*s == '/' && !strncmp (s, "/./", 3)) { /* delete "/./" */
- /* strcpy (s, s+2), s--; */
- memmove(s, s+2, strlen(s+2) + 1);
- s--;
- }
- else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
- *s = 0, s--;
- }
-
- /*
- Normalize consecutive slashes.
- Ignore doubled slashes after ":" to avoid mangling URLs.
- */
-
- for (s = p2; s && *s; s++){
- if (*s == ':') continue;
- if (!s[1] || !s[2]) continue;
- while (s[1] == '/' && s[2] == '/')
- /* strcpy (s+1, s+2); */
- memmove (s+1, s+2, strlen(s+2) + 1);
- }
-
- /* and strip trailing whitespace for good measure. */
- L = strlen(p2);
- while (isspace(p2[L-1]))
- p2[--L] = 0;
-
- return p2;
-}
-
-
-#ifdef HAVE_GTK2
-
typedef struct {
state *s;
int i;
@@ -1426,10 +1236,10 @@ typedef struct {
} FlushForeachClosure;
static gboolean
-flush_checkbox (GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- gpointer data)
+flush_checkbox (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
{
FlushForeachClosure *closure = data;
gboolean checked;
@@ -1440,7 +1250,7 @@ flush_checkbox (GtkTreeModel *model,
if (flush_changes (closure->s, closure->i,
checked, 0, 0))
- *closure->changed = True;
+ *closure->changed = TRUE;
closure->i++;
@@ -1448,8 +1258,6 @@ flush_checkbox (GtkTreeModel *model,
return FALSE;
}
-#endif /* HAVE_GTK2 */
-
static char *
theme_name_strip (const char *s)
@@ -1476,113 +1284,80 @@ theme_name_strip (const char *s)
static Bool
flush_dialog_changes_and_save (state *s)
{
- Display *dpy = GDK_DISPLAY();
saver_preferences *p = &s->prefs;
saver_preferences P2, *p2 = &P2;
-#ifdef HAVE_GTK2
- GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
+ GtkTreeView *list_widget = GTK_TREE_VIEW (win->list);
GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
FlushForeachClosure closure;
-#else /* !HAVE_GTK2 */
- GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
- GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
- int i;
-#endif /* !HAVE_GTK2 */
- static Bool already_warned_about_missing_image_directory = False; /* very long name... */
+ Bool changed = FALSE;
- Bool changed = False;
- GtkWidget *w;
-
- if (s->saving_p) return False;
- s->saving_p = True;
+ if (s->initializing_p) return FALSE;
+ if (s->saving_p) return FALSE;
+ s->saving_p = TRUE;
*p2 = *p;
- /* Flush any checkbox changes in the list down into the prefs struct.
+ /* Flush any checkbox changes in the list down into the s2 prefs struct.
*/
-#ifdef HAVE_GTK2
closure.s = s;
closure.changed = &changed;
closure.i = 0;
gtk_tree_model_foreach (model, flush_checkbox, &closure);
-#else /* !HAVE_GTK2 */
-
- for (i = 0; kids; kids = kids->next, i++)
- {
- GtkWidget *line = GTK_WIDGET (kids->data);
- GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
- GtkWidget *line_check =
- GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
- Bool checked =
- gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
-
- if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
- changed = True;
- }
-#endif /* ~HAVE_GTK2 */
-
/* Flush the non-hack-specific settings down into the prefs struct.
*/
-# define SECONDS(FIELD,NAME) \
- w = name_to_widget (s, (NAME)); \
- hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
-
-# define MINUTES(FIELD,NAME) \
- w = name_to_widget (s, (NAME)); \
- hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
-
-# define CHECKBOX(FIELD,NAME) \
- w = name_to_widget (s, (NAME)); \
- (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
-
-# define PATHNAME(FIELD,NAME) \
- w = name_to_widget (s, (NAME)); \
- (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
-
-# define TEXT(FIELD,NAME) \
- w = name_to_widget (s, (NAME)); \
- (FIELD) = (char *) g_strdup(gtk_entry_get_text (GTK_ENTRY (w)))
-
- MINUTES (&p2->timeout, "timeout_spinbutton");
- MINUTES (&p2->cycle, "cycle_spinbutton");
- CHECKBOX (p2->lock_p, "lock_button");
- MINUTES (&p2->lock_timeout, "lock_spinbutton");
-
- CHECKBOX (p2->dpms_enabled_p, "dpms_button");
- CHECKBOX (p2->dpms_quickoff_p, "dpms_quickoff_button");
- MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
- MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
- MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
-
- CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
- CHECKBOX (p2->grab_video_p, "grab_video_button");
- CHECKBOX (p2->random_image_p, "grab_image_button");
- PATHNAME (p2->image_directory, "image_text");
-
-#if 0
- CHECKBOX (p2->verbose_p, "verbose_button");
- CHECKBOX (p2->splash_p, "splash_button");
-#endif
+# define SECONDS(PREF,WIDGET) \
+ hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (win->WIDGET)), \
+ &(PREF), TRUE)
+# define MINUTES(PREF,WIDGET) \
+ hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (win->WIDGET)), \
+ &(PREF), FALSE)
+# define CHECKBOX(PREF,WIDGET) \
+ (PREF) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (win->WIDGET))
+# define PATHNAME(PREF,WIDGET,DIRP) \
+ (PREF) = pathname_tilde ( \
+ gtk_entry_get_text (GTK_ENTRY (win->WIDGET)), TRUE, (DIRP))
+# define TEXT(PREF,WIDGET) \
+ (PREF) = (char *) g_strdup (gtk_entry_get_text (GTK_ENTRY (win->WIDGET)))
+
+ MINUTES (p2->timeout, timeout_spinbutton);
+ MINUTES (p2->cycle, cycle_spinbutton);
+ CHECKBOX (p2->lock_p, lock_button);
+ MINUTES (p2->lock_timeout, lock_spinbutton);
+
+ CHECKBOX (p2->dpms_enabled_p, dpms_button);
+ CHECKBOX (p2->dpms_quickoff_p, dpms_quickoff_button);
+ MINUTES (p2->dpms_standby, dpms_standby_spinbutton);
+ MINUTES (p2->dpms_suspend, dpms_suspend_spinbutton);
+ MINUTES (p2->dpms_off, dpms_off_spinbutton);
+
+ CHECKBOX (p2->grab_desktop_p, grab_desk_button);
+ CHECKBOX (p2->grab_video_p, grab_video_button);
+ CHECKBOX (p2->random_image_p, grab_image_button);
+ PATHNAME (p2->image_directory, image_text, TRUE);
{
- Bool v = False;
- CHECKBOX (v, "text_host_radio"); if (v) p2->tmode = TEXT_DATE;
- CHECKBOX (v, "text_radio"); if (v) p2->tmode = TEXT_LITERAL;
- CHECKBOX (v, "text_file_radio"); if (v) p2->tmode = TEXT_FILE;
- CHECKBOX (v, "text_program_radio"); if (v) p2->tmode = TEXT_PROGRAM;
- CHECKBOX (v, "text_url_radio"); if (v) p2->tmode = TEXT_URL;
- TEXT (p2->text_literal, "text_entry");
- PATHNAME (p2->text_file, "text_file_entry");
- PATHNAME (p2->text_program, "text_program_entry");
- PATHNAME (p2->text_program, "text_program_entry");
- TEXT (p2->text_url, "text_url_entry");
+ Bool v = FALSE;
+ Bool od = s->debug_p;
+ s->debug_p = FALSE;
+ CHECKBOX (v, text_host_radio); if (v) p2->tmode = TEXT_DATE;
+ CHECKBOX (v, text_radio); if (v) p2->tmode = TEXT_LITERAL;
+ CHECKBOX (v, text_file_radio); if (v) p2->tmode = TEXT_FILE;
+ CHECKBOX (v, text_program_radio); if (v) p2->tmode = TEXT_PROGRAM;
+ CHECKBOX (v, text_url_radio); if (v) p2->tmode = TEXT_URL;
+ s->debug_p = od;
}
- CHECKBOX (p2->fade_p, "fade_button");
- CHECKBOX (p2->unfade_p, "unfade_button");
- SECONDS (&p2->fade_seconds, "fade_spinbutton");
+ TEXT (p2->text_literal, text_entry);
+ PATHNAME (p2->text_file, text_file_entry, FALSE);
+ PATHNAME (p2->text_program, text_program_entry, FALSE);
+ TEXT (p2->text_url, text_url_entry);
+
+ CHECKBOX (p2->fade_p, fade_button);
+ CHECKBOX (p2->unfade_p, unfade_button);
+ SECONDS (p2->fade_seconds, fade_spinbutton);
# undef SECONDS
# undef MINUTES
@@ -1590,35 +1365,9 @@ flush_dialog_changes_and_save (state *s)
# undef PATHNAME
# undef TEXT
- /* Warn if the image directory doesn't exist, when:
- - not being warned before
- - image directory is changed and the directory doesn't exist
- - image directory is not a URL
- */
- if (p2->image_directory &&
- *p2->image_directory &&
- !directory_p (p2->image_directory) &&
- strncmp(p2->image_directory, "http://", 7) &&
- strncmp(p2->image_directory, "https://", 8) &&
- ( !already_warned_about_missing_image_directory ||
- ( p->image_directory &&
- *p->image_directory &&
- strcmp(p->image_directory, p2->image_directory)
- )
- )
- )
- {
- char b[255];
- sprintf (b, "Warning:\n\n" "Directory does not exist: \"%s\"\n",
- p2->image_directory);
- if (warning_dialog (s->toplevel_widget, b, D_NONE, 100))
- already_warned_about_missing_image_directory = True;
- }
-
-
/* Map the mode menu to `saver_mode' enum values. */
{
- GtkComboBox *opt = GTK_COMBO_BOX (name_to_widget (s, "mode_menu"));
+ GtkComboBox *opt = GTK_COMBO_BOX (win->mode_menu);
int menu_elt = gtk_combo_box_get_active (opt);
if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
p2->mode = mode_menu_order[menu_elt];
@@ -1634,10 +1383,10 @@ flush_dialog_changes_and_save (state *s)
/* Theme menu. */
{
- GtkComboBox *cbox = GTK_COMBO_BOX (name_to_widget (s, "theme_menu"));
- char *themes = get_string_resource (dpy, "themeNames", "ThemeNames");
+ GtkComboBox *cbox = GTK_COMBO_BOX (win->theme_menu);
+ char *themes = get_string_resource (s->dpy, "themeNames", "ThemeNames");
int menu_index = gtk_combo_box_get_active (cbox);
- char *token = themes;
+ char *token = themes ? themes : "default";
char *name, *last;
int i = 0;
while ((name = strtok_r (token, ",", &last)))
@@ -1646,199 +1395,238 @@ flush_dialog_changes_and_save (state *s)
if (i == menu_index)
{
char *name2 = theme_name_strip (name);
- if (p->dialog_theme) free (p->dialog_theme);
- p2->dialog_theme = name2;
+ if (p->dialog_theme && !!strcmp (p->dialog_theme, name2))
+ {
+ free (p->dialog_theme);
+ p2->dialog_theme = name2;
+ if (s->debug_p)
+ fprintf (stderr, "%s: theme => \"%s\"\n", blurb(),
+ p2->dialog_theme);
+ }
+ else
+ {
+ free (name2);
+ }
}
i++;
}
}
-# define COPY(field, name) \
- if (p->field != p2->field) { \
- changed = True; \
- if (s->debug_p) \
- fprintf (stderr, "%s: %s => %ld\n", blurb(), \
- name, (unsigned long) p2->field); \
- } \
- p->field = p2->field
-
- COPY(mode, "mode");
- COPY(selected_hack, "selected_hack");
-
- COPY(timeout, "timeout");
- COPY(cycle, "cycle");
- COPY(lock_p, "lock_p");
- COPY(lock_timeout, "lock_timeout");
-
- COPY(dpms_enabled_p, "dpms_enabled_p");
- COPY(dpms_quickoff_p, "dpms_quickoff_enabled_p");
- COPY(dpms_standby, "dpms_standby");
- COPY(dpms_suspend, "dpms_suspend");
- COPY(dpms_off, "dpms_off");
-
-#if 0
- COPY(verbose_p, "verbose_p");
- COPY(splash_p, "splash_p");
-#endif
-
- COPY(tmode, "tmode");
-
- COPY(install_cmap_p, "install_cmap_p");
- COPY(fade_p, "fade_p");
- COPY(unfade_p, "unfade_p");
- COPY(fade_seconds, "fade_seconds");
+ /* It is difficult to get "editing completed" events out of GtkEntry.
+ I want something that fires on RET or focus-out, but I can't seem
+ to find a consistent way to get that. So let's fake it here.
+ */
+ if (!s->initializing_p &&
+ !!strcmp (p->image_directory, p2->image_directory))
+ {
+ if (s->debug_p)
+ fprintf (stderr, "%s: imagedir validating \"%s\" -> \"%s\"\n",
+ blurb(), p->image_directory, p2->image_directory);
+ if (! validate_image_directory (s, p2->image_directory))
+ {
+ /* Don't save the bad new value into the preferences. */
+ if (p2->image_directory != p->image_directory)
+ free (p2->image_directory);
+ p2->image_directory = strdup (p->image_directory);
+ }
+ }
- COPY(grab_desktop_p, "grab_desktop_p");
- COPY(grab_video_p, "grab_video_p");
- COPY(random_image_p, "random_image_p");
- COPY(dialog_theme, "dialog_theme");
+ /* Copy any changes from p2 into p, and log them.
+ */
+# undef STR
+# define STR(S) #S
+# define COPY(FIELD) \
+ if (p->FIELD != p2->FIELD) { \
+ changed = TRUE; \
+ if (s->debug_p) \
+ fprintf (stderr, "%s: %s: %ld => %ld\n", blurb(),\
+ STR(FIELD), (unsigned long) p->FIELD, \
+ (unsigned long) p2->FIELD); \
+ } \
+ p->FIELD = p2->FIELD
+
+ COPY(mode);
+ COPY(selected_hack);
+
+ COPY(timeout);
+ COPY(cycle);
+ COPY(lock_p);
+ COPY(lock_timeout);
+
+ COPY(dpms_enabled_p);
+ COPY(dpms_quickoff_p);
+ COPY(dpms_standby);
+ COPY(dpms_suspend);
+ COPY(dpms_off);
+
+ COPY(tmode);
+
+ COPY(install_cmap_p);
+ COPY(fade_p);
+ COPY(unfade_p);
+ COPY(fade_seconds);
+
+ COPY(grab_desktop_p);
+ COPY(grab_video_p);
+ COPY(random_image_p);
+
+ COPY(dialog_theme);
# undef COPY
-# define COPYSTR(FIELD,NAME) \
- if (!p->FIELD || \
- !p2->FIELD || \
- strcmp(p->FIELD, p2->FIELD)) \
- { \
- changed = True; \
- if (s->debug_p) \
- fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \
- } \
- if (p->FIELD && p->FIELD != p2->FIELD) \
- free (p->FIELD); \
- p->FIELD = p2->FIELD; \
+# define COPYSTR(FIELD) \
+ if (!p->FIELD || \
+ !p2->FIELD || \
+ strcmp(p->FIELD, p2->FIELD)) \
+ { \
+ changed = TRUE; \
+ if (s->debug_p) \
+ fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), \
+ STR(FIELD), p2->FIELD); \
+ } \
+ if (p->FIELD && p->FIELD != p2->FIELD) \
+ free (p->FIELD); \
+ p->FIELD = p2->FIELD; \
p2->FIELD = 0
- COPYSTR(image_directory, "image_directory");
- COPYSTR(text_literal, "text_literal");
- COPYSTR(text_file, "text_file");
- COPYSTR(text_program, "text_program");
- COPYSTR(text_url, "text_url");
+ COPYSTR(image_directory);
+ COPYSTR(text_literal);
+ COPYSTR(text_file);
+ COPYSTR(text_program);
+ COPYSTR(text_url);
# undef COPYSTR
populate_prefs_page (s);
if (changed)
{
- sync_server_dpms_settings (GDK_DISPLAY(), p);
- changed = demo_write_init_file (s, p);
+ if (s->dpy)
+ sync_server_dpms_settings (s->dpy, p);
+ demo_write_init_file (s, p);
+
+ /* Tell the xscreensaver daemon to wake up and reload the init file,
+ in case the timeout has changed. Without this, it would wait
+ until the *old* timeout had expired before reloading. */
+ if (s->debug_p)
+ fprintf (stderr, "%s: command: DEACTIVATE\n", blurb());
+ if (s->dpy)
+ xscreensaver_command (s->dpy, XA_DEACTIVATE, 0, 0, 0);
}
- s->saving_p = False;
+ s->saving_p = FALSE;
+
return changed;
}
-/* Flush out any changes made in the popup dialog box (where changes
- take place only when the OK button is clicked.)
- */
-static Bool
-flush_popup_changes_and_save (state *s)
+/* Called when any field in the prefs dialog may have been changed.
+ Referenced by many items in demo.ui. */
+G_MODULE_EXPORT gboolean
+pref_changed_cb (GtkWidget *widget, gpointer user_data)
{
- Bool changed = False;
- saver_preferences *p = &s->prefs;
- int list_elt = selected_list_element (s);
-
- GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
- GtkComboBoxEntry *vis = GTK_COMBO_BOX_ENTRY (name_to_widget (s, "visual_combo"));
- GtkEntry *visent = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (vis)));
-
- const char *visual = gtk_entry_get_text (visent);
- const char *command = gtk_entry_get_text (cmd);
-
- char c;
- unsigned long id;
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
- if (s->saving_p) return False;
- s->saving_p = True;
-
- if (list_elt < 0)
- goto DONE;
-
- if (maybe_reload_init_file (s) != 0)
+ if (s->debug_p)
{
- changed = True;
- goto DONE;
+ if (s->flushing_p)
+ fprintf (stderr, "%s: (pref changed: %s)\n", blurb(),
+ gtk_widget_get_name (widget));
+ else
+ fprintf (stderr, "%s: pref changed: %s\n", blurb(),
+ gtk_widget_get_name (widget));
}
- /* Sanity-check and canonicalize whatever the user typed into the combo box.
- */
- if (!strcasecmp (visual, "")) visual = "";
- else if (!strcasecmp (visual, "any")) visual = "";
- else if (!strcasecmp (visual, "default")) visual = "Default";
- else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
- else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
- else if (!strcasecmp (visual, "best")) visual = "Best";
- else if (!strcasecmp (visual, "mono")) visual = "Mono";
- else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
- else if (!strcasecmp (visual, "gray")) visual = "Gray";
- else if (!strcasecmp (visual, "grey")) visual = "Gray";
- else if (!strcasecmp (visual, "color")) visual = "Color";
- else if (!strcasecmp (visual, "gl")) visual = "GL";
- else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
- else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
- else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
- else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
- else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
- else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
- else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
- else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
- else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
- else
+ if (! s->flushing_p)
{
- gdk_beep (); /* unparsable */
- visual = "";
- gtk_entry_set_text (visent, _("Any"));
+ s->flushing_p = TRUE;
+ flush_dialog_changes_and_save (s);
+ s->flushing_p = FALSE;
}
+ return GDK_EVENT_PROPAGATE;
+}
- changed = flush_changes (s, list_elt, -1, command, visual);
- if (changed)
- {
- changed = demo_write_init_file (s, p);
-
- /* Do this to re-launch the hack if (and only if) the command line
- has changed. */
- populate_demo_window (s, selected_list_element (s));
- }
- DONE:
- s->saving_p = False;
- return changed;
+/* Same as pref_changed_cb but different. */
+G_MODULE_EXPORT gboolean
+pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
+{
+ pref_changed_cb (widget, user_data);
+ return GDK_EVENT_PROPAGATE;
}
-G_MODULE_EXPORT void
-pref_changed_cb (GtkWidget *widget, gpointer user_data)
+/* Called when the timeout or DPMS spinbuttons are changed, by demo.ui.
+ */
+G_MODULE_EXPORT gboolean
+dpms_sanity_cb (GtkWidget *widget, gpointer user_data)
{
- state *s = global_state_kludge; /* I hate C so much... */
- if (! s->initializing_p)
- {
- s->initializing_p = True;
- flush_dialog_changes_and_save (s);
- s->initializing_p = False;
- }
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
+ Time timeout, standby, suspend, off;
+
+ if (s->flushing_p) return GDK_EVENT_PROPAGATE;
+ if (s->initializing_p) return GDK_EVENT_PROPAGATE;
+ if (! s->dpms_supported_p) return GDK_EVENT_PROPAGATE;
+
+ /* Read the current values from the four spinbuttons. */
+# define MINUTES(V,WIDGET) \
+ hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (win->WIDGET)), \
+ &(V), FALSE)
+ MINUTES (timeout, timeout_spinbutton);
+ MINUTES (standby, dpms_standby_spinbutton);
+ MINUTES (suspend, dpms_suspend_spinbutton);
+ MINUTES (off, dpms_off_spinbutton);
+# undef MINUTES
+
+ /* If the DPMS settings are non-zero, they must not go backwards:
+ standby >= timeout (screen saver activation)
+ suspend >= standby
+ off >= suspend
+ */
+# define MINUTES(V,LOWER,WIDGET) \
+ if ((V) != 0 && (V) < LOWER) \
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (win->WIDGET), \
+ (double) ((LOWER) + 59) / (60 * 1000))
+ MINUTES (standby, timeout, dpms_standby_spinbutton);
+ MINUTES (suspend, standby, dpms_suspend_spinbutton);
+ MINUTES (off, standby, dpms_off_spinbutton);
+ MINUTES (off, suspend, dpms_off_spinbutton);
+# undef MINUTES
+
+ return GDK_EVENT_PROPAGATE;
}
+
+/* Same as dpms_sanity_cb but different. */
G_MODULE_EXPORT gboolean
-pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
+dpms_sanity_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
- pref_changed_cb (widget, user_data);
- return FALSE;
+ dpms_sanity_cb (widget, user_data);
+ return GDK_EVENT_PROPAGATE;
}
+
+
/* Callback on menu items in the "mode" options menu.
*/
G_MODULE_EXPORT void
mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
{
- state *s = (state *) user_data;
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
saver_preferences *p = &s->prefs;
- GtkWidget *list = name_to_widget (s, "list");
+ GtkWidget *list = win->list;
int list_elt;
int menu_index = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
saver_mode new_mode = mode_menu_order[menu_index];
+ if (s->flushing_p) return; /* Called as a spurious side-effect */
+ if (s->initializing_p) return;
+
+ if (s->debug_p) fprintf (stderr, "%s: mode menu\n", blurb());
+
/* Keep the same list element displayed as before; except if we're
switching *to* "one screensaver" mode from any other mode, set
"the one" to be that which is currently selected.
@@ -1851,7 +1639,8 @@ mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
saver_mode old_mode = p->mode;
p->mode = new_mode;
populate_demo_window (s, list_elt);
- force_list_select_item (s, list, list_elt, True);
+ populate_popup_window (s);
+ force_list_select_item (s, list, list_elt, TRUE);
p->mode = old_mode; /* put it back, so the init file gets written */
}
@@ -1859,52 +1648,94 @@ mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
}
+/* Remove the "random-same" item from the screen saver mode menu
+ (we don't display that unless there are multiple screens.)
+ */
+static void
+hide_mode_menu_random_same (state *s)
+{
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
+ GtkComboBox *opt = GTK_COMBO_BOX (win->mode_menu);
+ GtkTreeModel *list = gtk_combo_box_get_model (opt);
+ unsigned int i;
+ for (i = 0; i < countof(mode_menu_order); i++)
+ {
+ if (mode_menu_order[i] == RANDOM_HACKS_SAME)
+ {
+ GtkTreeIter iter;
+ gtk_tree_model_iter_nth_child (list, &iter, NULL, i);
+ gtk_list_store_remove (GTK_LIST_STORE (list), &iter);
+ break;
+ }
+ }
+
+ /* recompute option-menu size */
+ gtk_widget_unrealize (GTK_WIDGET (opt));
+ gtk_widget_realize (GTK_WIDGET (opt));
+}
+
+
+/* Called when a new tab is selected. */
G_MODULE_EXPORT void
-switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
+switch_page_cb (GtkNotebook *notebook, GtkWidget *page,
gint page_num, gpointer user_data)
{
- state *s = global_state_kludge; /* I hate C so much... */
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
+
+ if (s->debug_p) fprintf (stderr, "%s: tab changed\n", blurb());
+ populate_prefs_page (s);
pref_changed_cb (GTK_WIDGET (notebook), user_data);
/* If we're switching to page 0, schedule the current hack to be run.
Otherwise, schedule it to stop. */
if (page_num == 0)
- populate_demo_window (s, selected_list_element (s));
+ {
+ populate_demo_window (s, selected_list_element (s));
+ populate_popup_window (s);
+ }
else
schedule_preview (s, 0);
}
-#ifdef HAVE_GTK2
+
+/* Called when a line is double-clicked in the saver list. */
static void
-list_activated_cb (GtkTreeView *list,
- GtkTreePath *path,
- GtkTreeViewColumn *column,
- gpointer data)
+list_activated_cb (GtkTreeView *list, GtkTreePath *path,
+ GtkTreeViewColumn *column, gpointer data)
{
state *s = data;
char *str;
int list_elt;
- if (gdk_pointer_is_grabbed()) return;
+ if (s->debug_p) fprintf (stderr, "%s: list activated\n", blurb());
+
+ /* I did this in Gtk 2 and I don't remember why:
+ if (gdk_pointer_is_grabbed()) return;
+ I don't understand how to use gdk_display_device_is_grabbed().
+ */
str = gtk_tree_path_to_string (path);
list_elt = strtol (str, NULL, 10);
g_free (str);
if (list_elt >= 0)
- run_hack (s, list_elt, True);
+ run_hack (s, list_elt, TRUE);
}
+/* Called when a line is selected/highlighted in the saver list. */
static void
list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
{
- state *s = (state *)data;
+ state *s = (state *) data;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreePath *path;
char *str;
int list_elt;
+ if (s->debug_p) fprintf (stderr, "%s: list selection changed\n", blurb());
+
if (!gtk_tree_selection_get_selected (selection, &model, &iter))
return;
@@ -1916,65 +1747,14 @@ list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
g_free (str);
populate_demo_window (s, list_elt);
- flush_dialog_changes_and_save (s);
/* Re-populate the Settings window any time a new item is selected
- in the list, in case both windows are currently visible.
- */
+ in the list, in case both windows are currently visible. */
populate_popup_window (s);
-}
-
-#else /* !HAVE_GTK2 */
-
-static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
- list_select_cb that comes in
- *after* we've double-clicked.
- */
-
-static gint
-list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
- gpointer data)
-{
- state *s = (state *) data;
- if (event->type == GDK_2BUTTON_PRESS)
- {
- GtkList *list = GTK_LIST (name_to_widget (s, "list"));
- int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
-
- last_doubleclick_time = time ((time_t *) 0);
-
- if (list_elt >= 0)
- run_hack (s, list_elt, True);
- }
-
- return FALSE;
-}
-
-
-static void
-list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
-{
- state *s = (state *) data;
- time_t now = time ((time_t *) 0);
- if (now >= last_doubleclick_time + 2)
- {
- int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
- populate_demo_window (s, list_elt);
- flush_dialog_changes_and_save (s);
- }
-}
-
-static void
-list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
-{
- state *s = (state *) data;
- populate_demo_window (s, -1);
flush_dialog_changes_and_save (s);
}
-#endif /* !HAVE_GTK2 */
-
/* Called when the checkboxes that are in the left column of the
scrolling list are clicked. This both populates the right pane
@@ -1982,39 +1762,25 @@ list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
also syncs this checkbox with the right pane Enabled checkbox.
*/
static void
-list_checkbox_cb (
-#ifdef HAVE_GTK2
- GtkCellRendererToggle *toggle,
- gchar *path_string,
-#else /* !HAVE_GTK2 */
- GtkWidget *cb,
-#endif /* !HAVE_GTK2 */
- gpointer data)
+list_checkbox_cb (GtkCellRendererToggle *toggle,
+ gchar *path_string, gpointer data)
{
state *s = (state *) data;
-#ifdef HAVE_GTK2
- GtkScrolledWindow *scroller =
- GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
- GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
+ GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (win->scroller);
+ GtkTreeView *list = GTK_TREE_VIEW (win->list);
GtkTreeModel *model = gtk_tree_view_get_model (list);
GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
GtkTreeIter iter;
gboolean active;
-#else /* !HAVE_GTK2 */
- GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
- GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
-
- GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
- GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
- GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
-#endif /* !HAVE_GTK2 */
GtkAdjustment *adj;
double scroll_top;
int list_elt;
-#ifdef HAVE_GTK2
+ if (s->debug_p) fprintf (stderr, "%s: list checkbox\n", blurb());
+
if (!gtk_tree_model_get_iter (model, &iter, path))
{
g_warning ("bad path: %s", path_string);
@@ -2031,17 +1797,15 @@ list_checkbox_cb (
-1);
list_elt = strtol (path_string, NULL, 10);
-#else /* !HAVE_GTK2 */
- list_elt = gtk_list_child_position (list, line);
-#endif /* !HAVE_GTK2 */
/* remember previous scroll position of the top of the list */
adj = gtk_scrolled_window_get_vadjustment (scroller);
- scroll_top = GET_ADJ_VALUE (adj);
+ scroll_top = gtk_adjustment_get_value (adj);
flush_dialog_changes_and_save (s);
- force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
+ force_list_select_item (s, GTK_WIDGET (list), list_elt, FALSE);
populate_demo_window (s, list_elt);
+ populate_popup_window (s);
/* restore the previous scroll position of the top of the list.
this is weak, but I don't really know why it's moving... */
@@ -2049,373 +1813,538 @@ list_checkbox_cb (
}
-typedef struct {
- state *state;
- GtkFileSelection *widget;
-} file_selection_data;
-
-
-
+/* If the directory or URL does not have images in it, pop up a warning
+ dialog. This happens at startup, and is fast.
+ */
static void
-store_image_directory (GtkWidget *button, gpointer user_data)
+validate_image_directory_quick (state *s)
{
- file_selection_data *fsd = (file_selection_data *) user_data;
- state *s = fsd->state;
- GtkFileSelection *selector = fsd->widget;
- GtkWidget *top = s->toplevel_widget;
saver_preferences *p = &s->prefs;
- const char *path = gtk_file_selection_get_filename (selector);
+ char *warn = 0;
+ char buf[10240];
+
+ if (!p->random_image_p) return;
+
+ if (!p->image_directory || !*p->image_directory)
+ warn = _("Image directory is unset");
+ else if (!strncmp (p->image_directory, "http://", 7) ||
+ !strncmp (p->image_directory, "https://", 8))
+ warn = 0;
+ else if (!directory_p (p->image_directory))
+ warn = _("Image directory does not exist");
+ else if (!image_files_p (p->image_directory, 10))
+ warn = _("Image directory is empty");
+
+ if (!warn) return;
+
+ sprintf (buf,
+ _("%.100s:\n\n"
+ " %.100s\n\n"
+ "Select the 'Advanced' tab and choose a directory with some\n"
+ "pictures in it, or you're going to see a lot of boring colorbars!"),
+ warn,
+ (p->image_directory ? p->image_directory : ""));
+ warning_dialog (s->window, _("Warning"), buf);
+}
- if (p->image_directory && !strcmp(p->image_directory, path))
- return; /* no change */
- /* No warning for URLs. */
- if ((!directory_p (path)) && strncmp(path, "http://", 6))
- {
- char b[255];
- sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
- warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
- return;
- }
+/* "Cancel" button on the validate image directory progress dialog. */
+static void
+validate_image_directory_cancel_cb (GtkDialog *dialog, gint response_id,
+ gpointer user_data)
+{
+ Bool *closed = (Bool *) user_data;
+ *closed = TRUE;
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+}
- if (p->image_directory) free (p->image_directory);
- p->image_directory = normalize_directory (path);
- gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
- (p->image_directory ? p->image_directory : ""));
- demo_write_init_file (s, p);
+typedef struct {
+ GtkWidget *dialog;
+ int timer_id;
+} validate_timer_closure;
+
+static int
+validate_timer_show (gpointer data)
+{
+ validate_timer_closure *vtc = (validate_timer_closure *) data;
+ gtk_widget_show_all (vtc->dialog);
+ vtc->timer_id = 0;
+ return FALSE;
}
-static void
-store_text_file (GtkWidget *button, gpointer user_data)
+/* If the directory or URL does not have images in it, pop up a warning
+ dialog and return false. This happens when the imageDirectory preference
+ is edited, and might be slow.
+
+ It does this by running "xscreensaver-getimage-file", which has the side
+ effect of populating the image cache for that directory. Since that will
+ take a while if there are a lot of files, this also pops up a progress
+ dialog with a spinner in it, and a cancel button. That progress dialog
+ only pops up if the validation has already been running for a little
+ while, so that it doesn't flicker for small or pre-cached directories.
+ */
+static Bool
+validate_image_directory (state *s, const char *path)
{
- file_selection_data *fsd = (file_selection_data *) user_data;
- state *s = fsd->state;
- GtkFileSelection *selector = fsd->widget;
- GtkWidget *top = s->toplevel_widget;
- saver_preferences *p = &s->prefs;
- const char *path = gtk_file_selection_get_filename (selector);
+ validate_timer_closure vtc;
+ char buf[1024];
+ char err[1024];
+ GtkWidget *dialog, *content_area, *label, *spinner;
+ int margin = 32;
+ Bool closed_p = FALSE;
+
+ dialog = gtk_dialog_new_with_buttons (_("XScreenSaver Image Cache"),
+ s->window,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ _("_Cancel"), GTK_RESPONSE_CLOSE,
+ NULL);
+
+ sprintf (buf, _("Populating image cache for \"%.100s\"..."), path);
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+ label = gtk_label_new (buf);
+
+ gtk_widget_set_margin_start (label, margin);
+ gtk_widget_set_margin_end (label, margin);
+ gtk_widget_set_margin_top (label, margin);
+ gtk_widget_set_margin_bottom (label, margin / 2);
+ gtk_container_add (GTK_CONTAINER (content_area), label);
+
+ spinner = gtk_spinner_new();
+ gtk_spinner_start (GTK_SPINNER (spinner));
+ gtk_container_add (GTK_CONTAINER (content_area), spinner);
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (validate_image_directory_cancel_cb),
+ &closed_p);
+ g_signal_connect (dialog, "close",
+ G_CALLBACK (validate_image_directory_cancel_cb),
+ &closed_p);
+
+ /* Only pop up the dialog box with the spinner if this has already taken
+ a little while, so that if it completes immediately, we don't flicker.
+ */
+ vtc.dialog = dialog;
+ vtc.timer_id = g_timeout_add (1000, validate_timer_show, &vtc);
- if (p->text_file && !strcmp(p->text_file, path))
- return; /* no change */
+ while (gtk_events_pending ()) /* Paint the window now. */
+ gtk_main_iteration ();
- if (!file_p (path))
- {
- char b[255];
- sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
- warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
- return;
- }
+ {
+ pid_t forked;
+ int fds [2];
+ int in, out;
- if (p->text_file) free (p->text_file);
- p->text_file = normalize_directory (path);
+ char *av[10];
+ int ac = 0;
- gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
- (p->text_file ? p->text_file : ""));
- demo_write_init_file (s, p);
-}
+ *err = 0;
+ av[ac++] = "xscreensaver-getimage-file";
+ av[ac++] = (char *) path;
+ av[ac] = 0;
+ if (pipe (fds))
+ {
+ strcpy (err, "error creating pipe");
+ goto FAIL;
+ }
-static void
-store_text_program (GtkWidget *button, gpointer user_data)
-{
- file_selection_data *fsd = (file_selection_data *) user_data;
- state *s = fsd->state;
- GtkFileSelection *selector = fsd->widget;
- /*GtkWidget *top = s->toplevel_widget;*/
- saver_preferences *p = &s->prefs;
- const char *path = gtk_file_selection_get_filename (selector);
+ in = fds [0];
+ out = fds [1];
- if (p->text_program && !strcmp(p->text_program, path))
- return; /* no change */
+ switch ((int) (forked = fork ()))
+ {
+ case -1:
+ {
+ strcpy (err, "couldn't fork");
+ goto FAIL;
+ }
+ case 0: /* Child fork */
+ {
+ int stderr_fd = 2;
-# if 0
- if (!file_p (path))
- {
- char b[255];
- sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
- warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
- return;
- }
-# endif
+ close (in); /* don't need this one */
+ if (! s->debug_p)
+ close (fileno (stdout));
+ close (ConnectionNumber (s->dpy)); /* close display fd */
- if (p->text_program) free (p->text_program);
- p->text_program = normalize_directory (path);
+ if (dup2 (out, stderr_fd) < 0) /* pipe stdout */
+ {
+ perror ("could not dup() a new stderr:");
+ exit (1);
+ }
- gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
- (p->text_program ? p->text_program : ""));
- demo_write_init_file (s, p);
-}
+ execvp (av[0], av); /* shouldn't return. */
+ sprintf (buf, "%s: running %s", blurb(), av[0]);
+ perror (buf);
+ /* Note that one must use _exit() instead of exit() in procs forked
+ off of Gtk programs -- Gtk installs an atexit handler that has a
+ copy of the X connection (which we've already closed, for safety.)
+ If one uses exit() instead of _exit(), then one sometimes gets a
+ spurious "Gdk-ERROR: Fatal IO error on X server" error message.
+ */
+ _exit (1); /* exits fork */
+ break;
+ }
+ default: /* Parent fork */
+ {
+ char *ss = err;
+ int bufsiz = sizeof(err);
-static void
-browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
-{
- file_selection_data *fsd = (file_selection_data *) user_data;
- gtk_widget_hide (GTK_WIDGET (fsd->widget));
-}
+ close (out); /* don't need this one */
-static void
-browse_image_dir_ok (GtkWidget *button, gpointer user_data)
-{
- browse_image_dir_cancel (button, user_data);
- store_image_directory (button, user_data);
-}
+ if (s->debug_p)
+ fprintf (stderr, "%s: forked %s\n", blurb(), av[0]);
-static void
-browse_text_file_ok (GtkWidget *button, gpointer user_data)
-{
- browse_image_dir_cancel (button, user_data);
- store_text_file (button, user_data);
-}
+ while (1)
+ {
+ fd_set rset;
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 1000000 / 10; /* Repaint widgets at 10 fps */
+ FD_ZERO (&rset);
+ FD_SET (in, &rset);
+ if (0 < select (in+1, &rset, 0, 0, &tv))
+ {
+ int n = read (in, (void *) ss, bufsiz);
+ if (n <= 0)
+ {
+ if (s->debug_p)
+ fprintf (stderr, "%s: %s: read EOF\n", blurb(), av[0]);
+ break;
+ }
+ else
+ {
+ ss += n;
+ bufsiz -= n;
+ *ss = 0;
+
+ if (s->debug_p)
+ fprintf (stderr, "%s: %s: read: \"%s\"\n", blurb(),
+ av[0], ss - n);
+ }
+ }
-static void
-browse_text_program_ok (GtkWidget *button, gpointer user_data)
-{
- browse_image_dir_cancel (button, user_data);
- store_text_program (button, user_data);
-}
+ /* Service Gtk events and timers */
+ while (gtk_events_pending ())
+ gtk_main_iteration ();
-static void
-browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
-{
- browse_image_dir_cancel (widget, user_data);
-}
+ if (closed_p)
+ {
+ kill (forked, SIGTERM);
+ if (s->debug_p)
+ fprintf (stderr, "%s: cancel\n", blurb());
+ break;
+ }
+ }
-G_MODULE_EXPORT void
-browse_image_dir_cb (GtkButton *button, gpointer user_data)
-{
- state *s = global_state_kludge; /* I hate C so much... */
- saver_preferences *p = &s->prefs;
- static file_selection_data *fsd = 0;
+ *ss = 0;
+ close (in);
- GtkFileSelection *selector = GTK_FILE_SELECTION(
- gtk_file_selection_new ("Please select the image directory."));
+ if (s->debug_p)
+ fprintf (stderr, "%s: %s exited\n", blurb(), av[0]);
- if (!fsd)
- fsd = (file_selection_data *) malloc (sizeof (*fsd));
+ /* Wait for the child to die. */
+ {
+ int wait_status = 0;
+ waitpid (-1, &wait_status, 0);
+ }
+ }
+ }
+ }
- fsd->widget = selector;
- fsd->state = s;
+ if (vtc.timer_id) /* Remove the popup timer if it hasn't fired. */
+ g_source_remove (vtc.timer_id);
- if (p->image_directory && *p->image_directory)
- gtk_file_selection_set_filename (selector, p->image_directory);
+ if (s->debug_p)
+ fprintf (stderr, "%s: dismiss\n", blurb());
- gtk_signal_connect (GTK_OBJECT (selector->ok_button),
- "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
- (gpointer *) fsd);
- gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
- "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
- (gpointer *) fsd);
- gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
- GTK_SIGNAL_FUNC (browse_image_dir_close),
- (gpointer *) fsd);
+ if (! closed_p)
+ gtk_widget_destroy (dialog);
- gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
+ FAIL:
+ if (*err)
+ {
+ warning_dialog (s->window, _("Warning"), err);
+ return FALSE;
+ }
- gtk_window_set_modal (GTK_WINDOW (selector), True);
- gtk_widget_show (GTK_WIDGET (selector));
+ return TRUE;
}
-G_MODULE_EXPORT void
-browse_text_file_cb (GtkButton *button, gpointer user_data)
+/* Called when the imageDirectory text field is edited directly (focus-out).
+ */
+G_MODULE_EXPORT gboolean
+image_text_pref_changed_event_cb (GtkWidget *widget, GdkEvent *event,
+ gpointer user_data)
{
- state *s = global_state_kludge; /* I hate C so much... */
+#if 0 /* This is handled in flush_dialog_changes_and_save now */
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
saver_preferences *p = &s->prefs;
- static file_selection_data *fsd = 0;
-
- GtkFileSelection *selector = GTK_FILE_SELECTION(
- gtk_file_selection_new ("Please select a text file."));
-
- if (!fsd)
- fsd = (file_selection_data *) malloc (sizeof (*fsd));
-
- fsd->widget = selector;
- fsd->state = s;
+ GtkEntry *w = GTK_ENTRY (win->image_text);
+ const char *str = gtk_entry_get_text (w);
+ char *path = pathname_tilde (str, TRUE, TRUE);
- if (p->text_file && *p->text_file)
- gtk_file_selection_set_filename (selector, p->text_file);
+ if (s->debug_p) fprintf (stderr, "%s: imagedir text edited\n", blurb());
- gtk_signal_connect (GTK_OBJECT (selector->ok_button),
- "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok),
- (gpointer *) fsd);
- gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
- "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
- (gpointer *) fsd);
- gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
- GTK_SIGNAL_FUNC (browse_image_dir_close),
- (gpointer *) fsd);
+ if (p->image_directory && strcmp(p->image_directory, path))
+ {
+ if (s->debug_p)
+ fprintf (stderr, "%s: imagedir validating \"%s\" -> \"%s\"\n", blurb(),
+ p->image_directory, path);
+ if (! validate_image_directory (s, path))
+ {
+ /* Don't save the bad new value into the preferences. */
+ free (path);
+ return GDK_EVENT_PROPAGATE;
+ }
+ }
- gtk_window_set_modal (GTK_WINDOW (selector), True);
- gtk_widget_show (GTK_WIDGET (selector));
+ free (path);
+# endif
+ pref_changed_event_cb (widget, event, user_data);
+ return GDK_EVENT_PROPAGATE;
}
-G_MODULE_EXPORT void
-browse_text_program_cb (GtkButton *button, gpointer user_data)
+/* Run a modal file selector dialog.
+ Select a file, directory, or program.
+ Normalize the resultant path and store it into the string pointer.
+ Also update the text field with the new path.
+ Returns true if any changes made.
+ */
+gboolean
+file_chooser (GtkWindow *parent, GtkEntry *entry, char **retP,
+ const char *title, gboolean verbose_p,
+ gboolean dir_p, gboolean program_p)
{
- state *s = global_state_kludge; /* I hate C so much... */
- saver_preferences *p = &s->prefs;
- static file_selection_data *fsd = 0;
-
- GtkFileSelection *selector = GTK_FILE_SELECTION(
- gtk_file_selection_new ("Please select a text-generating program."));
+ gint res;
+ gboolean changed_p = FALSE;
+ GtkWidget *dialog =
+ gtk_file_chooser_dialog_new (title, parent,
+ (dir_p
+ ? GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
+ : GTK_FILE_CHOOSER_ACTION_OPEN),
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Select"), GTK_RESPONSE_ACCEPT,
+ NULL);
+ const char *old = gtk_entry_get_text (entry); /* not *retP */
+
+ if (*old)
+ {
+ char *p2 = pathname_tilde (old, FALSE, dir_p);
+ GFile *gf;
- if (!fsd)
- fsd = (file_selection_data *) malloc (sizeof (*fsd));
+ /* If it's a command line and it begins with an absolute path,
+ default to that file and its directory. */
+ if (program_p && (*p2 == '/' || *p2 == '~'))
+ {
+ char *s = strpbrk (p2, " \t\r\n");
+ if (s) *s = 0;
+ program_p = FALSE;
+ }
- fsd->widget = selector;
- fsd->state = s;
+ gf = g_file_new_for_path (p2);
+ if (! program_p)
+ {
+ gtk_file_chooser_set_file (GTK_FILE_CHOOSER (dialog), gf, NULL);
+ if (verbose_p)
+ fprintf (stderr, "%s: chooser: default \"%s\"\n", blurb(), p2);
+ }
+ free (p2);
+ g_object_unref (gf);
+ }
- if (p->text_program && *p->text_program)
- gtk_file_selection_set_filename (selector, p->text_program);
+ res = gtk_dialog_run (GTK_DIALOG (dialog));
+ if (res == GTK_RESPONSE_ACCEPT)
+ {
+ GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog);
+ char *str = gtk_file_chooser_get_filename (chooser);
+ char *path = pathname_tilde (str, TRUE, dir_p);
+ g_free (str);
- gtk_signal_connect (GTK_OBJECT (selector->ok_button),
- "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok),
- (gpointer *) fsd);
- gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
- "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
- (gpointer *) fsd);
- gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
- GTK_SIGNAL_FUNC (browse_image_dir_close),
- (gpointer *) fsd);
+ if (*retP && !strcmp (*retP, path))
+ {
+ if (verbose_p)
+ fprintf (stderr, "%s: chooser: unchanged\n", blurb());
+ free (path); /* no change */
+ }
+ else if (dir_p && !directory_p (path))
+ {
+ char b[255];
+ sprintf (b, _("Directory does not exist: \"%.100s\"\n"), path);
+ warning_dialog (parent, _("Error"), b);
+ free (path); /* no change */
+ }
+ else if (!dir_p && !file_p (path))
+ {
+ char b[255];
+ sprintf (b, _("File does not exist: \"%.100s\"\n"), path);
+ warning_dialog (parent, _("Error"), b);
+ free (path); /* no change */
+ }
+ else
+ {
+ if (verbose_p)
+ fprintf (stderr, "%s: chooser: \"%s\" -> \"%s\n",
+ blurb(), *retP, path);
+ if (*retP) free (*retP);
+ *retP = path;
+ gtk_entry_set_text (entry, path);
+ changed_p = TRUE;
+ }
+ }
+ else if (verbose_p)
+ fprintf (stderr, "%s: chooser: cancelled\n", blurb());
- gtk_window_set_modal (GTK_WINDOW (selector), True);
- gtk_widget_show (GTK_WIDGET (selector));
+ gtk_widget_destroy (dialog);
+ return changed_p;
}
+/* The "Browse" button next to the imageDirectory text field. */
G_MODULE_EXPORT void
-preview_theme_cb (GtkWidget *w, gpointer user_data)
+browse_image_dir_cb (GtkButton *button, gpointer user_data)
{
- if (system ("xscreensaver-auth --splash &") < 0)
- fprintf (stderr, "%s: splash exec failed\n", blurb());
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
+ saver_preferences *p = &s->prefs;
+ char *old = strdup (p->image_directory);
+
+ if (s->debug_p) fprintf (stderr, "%s: imagedir browse button\n", blurb());
+ if (file_chooser (GTK_WINDOW (win),
+ GTK_ENTRY (win->image_text),
+ &p->image_directory,
+ _("Please select the image directory."),
+ s->debug_p, TRUE, FALSE))
+ {
+ if (validate_image_directory (s, p->image_directory))
+ demo_write_init_file (s, p);
+ else
+ {
+ /* Don't save the bad new value into the preferences. */
+ free (p->image_directory);
+ p->image_directory = old;
+ old = 0;
+ }
+ }
+
+ if (old) free (old);
}
+/* The "Browse" button next to the textFile text field. */
G_MODULE_EXPORT void
-settings_cb (GtkButton *button, gpointer user_data)
+browse_text_file_cb (GtkButton *button, gpointer user_data)
{
- state *s = global_state_kludge; /* I hate C so much... */
- int list_elt = selected_list_element (s);
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
+ saver_preferences *p = &s->prefs;
- populate_demo_window (s, list_elt); /* reset the widget */
- populate_popup_window (s); /* create UI on popup window */
- gtk_widget_show (s->popup_widget);
+ if (s->debug_p) fprintf (stderr, "%s: textfile browse button\n", blurb());
+ if (file_chooser (GTK_WINDOW (win),
+ GTK_ENTRY (win->text_file_entry),
+ &p->text_file,
+ _("Please select a text file."),
+ s->debug_p, FALSE, FALSE))
+ demo_write_init_file (s, p);
}
-static void
-settings_sync_cmd_text (state *s)
-{
-# ifdef HAVE_XML
- GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
- char *cmd_line = get_configurator_command_line (s->cdata, False);
- gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
- gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
- free (cmd_line);
-# endif /* HAVE_XML */
-}
+/* The "Browse" button next to the textProgram text field. */
G_MODULE_EXPORT void
-settings_adv_cb (GtkButton *button, gpointer user_data)
+browse_text_program_cb (GtkButton *button, gpointer user_data)
{
- state *s = global_state_kludge; /* I hate C so much... */
- GtkNotebook *notebook =
- GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
+ saver_preferences *p = &s->prefs;
- settings_sync_cmd_text (s);
- gtk_notebook_set_page (notebook, 1);
+ if (s->debug_p) fprintf (stderr, "%s: textprogram browse button\n", blurb());
+ if (file_chooser (GTK_WINDOW (win),
+ GTK_ENTRY (win->text_program_entry),
+ &p->text_program,
+ _("Please select a text-generating program."),
+ s->debug_p, FALSE, TRUE))
+ demo_write_init_file (s, p);
}
-G_MODULE_EXPORT void
-settings_std_cb (GtkButton *button, gpointer user_data)
-{
- state *s = global_state_kludge; /* I hate C so much... */
- GtkNotebook *notebook =
- GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
-
- /* Re-create UI to reflect the in-progress command-line settings. */
- populate_popup_window (s);
-
- gtk_notebook_set_page (notebook, 0);
-}
+/* The "Preview" button next to the Theme option menu. */
G_MODULE_EXPORT void
-settings_reset_cb (GtkButton *button, gpointer user_data)
+preview_theme_cb (GtkWidget *w, gpointer user_data)
{
-# ifdef HAVE_XML
- state *s = global_state_kludge; /* I hate C so much... */
- GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
- char *cmd_line = get_configurator_command_line (s->cdata, True);
- gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
- gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
- free (cmd_line);
- populate_popup_window (s);
-# endif /* HAVE_XML */
-}
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
+ int ac = 0;
+ char *av[10];
-G_MODULE_EXPORT void
-settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
- gint page_num, gpointer user_data)
-{
- state *s = global_state_kludge; /* I hate C so much... */
- GtkWidget *adv = name_to_widget (s, "adv_button");
- GtkWidget *std = name_to_widget (s, "std_button");
+ if (s->debug_p) fprintf (stderr, "%s: preview theme button\n", blurb());
- if (page_num == 0)
- {
- gtk_widget_show (adv);
- gtk_widget_hide (std);
- }
- else if (page_num == 1)
- {
- gtk_widget_hide (adv);
- gtk_widget_show (std);
- }
- else
- abort();
+ /* Settings button is disabled with --splash --splash so that we don't
+ end up with two copies of xscreensaver-settings running. */
+ av[ac++] = "xscreensaver-auth";
+ av[ac++] = "--splash";
+ av[ac++] = "--splash";
+ av[ac] = 0;
+ fork_and_exec (s, ac, av);
}
-
+/* The "Settings" button on the main page. */
G_MODULE_EXPORT void
-settings_cancel_cb (GtkButton *button, gpointer user_data)
+settings_cb (GtkButton *button, gpointer user_data)
{
- state *s = global_state_kludge; /* I hate C so much... */
- gtk_widget_hide (s->popup_widget);
-}
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (user_data);
+ state *s = &win->state;
+ saver_preferences *p = &s->prefs;
+ XScreenSaverDialog *dialog = XSCREENSAVER_DIALOG (s->dialog);
+ int list_elt = selected_list_element (s);
-G_MODULE_EXPORT void
-settings_ok_cb (GtkButton *button, gpointer user_data)
-{
- state *s = global_state_kludge; /* I hate C so much... */
- GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
- int page = gtk_notebook_get_current_page (notebook);
+ if (s->debug_p) fprintf (stderr, "%s: settings button\n", blurb());
- if (page == 0)
- /* Regenerate the command-line from the widget contents before saving.
- But don't do this if we're looking at the command-line page already,
- or we will blow away what they typed... */
- settings_sync_cmd_text (s);
+ populate_demo_window (s, list_elt); /* reset the widget */
+ populate_popup_window (s); /* create UI on popup window */
- flush_popup_changes_and_save (s);
- gtk_widget_hide (s->popup_widget);
-}
+ /* Pre-select the "Standard" page. */
+ settings_std_cb (NULL, s->dialog);
+ settings_switch_page_cb (GTK_NOTEBOOK (dialog->opt_notebook), NULL, 0,
+ s->dialog);
-static gboolean
-wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
-{
- state *s = (state *) data;
- settings_cancel_cb (0, (gpointer) s);
- return TRUE;
+ /* If there is no saved position for the dialog, position it to the
+ right of the main window. See also restore_window_position(),
+ which already ran at startup. */
+ {
+ int win_x, win_y, dialog_x, dialog_y;
+ char dummy;
+ char *old = p->settings_geom;
+
+ if (!old || !*old ||
+ 4 != sscanf (old, " %d , %d %d , %d %c",
+ &win_x, &win_y, &dialog_x, &dialog_y, &dummy))
+ win_x = win_y = dialog_x = dialog_y = -1;
+
+ if (dialog_x <= 0 && dialog_y <= 0)
+ {
+ int win_w, win_h;
+ gtk_window_get_position (GTK_WINDOW (s->window), &win_x, &win_y);
+ gtk_window_get_size (GTK_WINDOW (s->window), &win_w, &win_h);
+ dialog_x = win_x + win_w + 8;
+ dialog_y = win_y;
+ gtk_window_move (GTK_WINDOW (s->dialog), dialog_x, dialog_y);
+ }
+ }
+
+ gtk_widget_show (GTK_WIDGET (s->dialog));
}
-
/* Populating the various widgets
*/
@@ -2423,18 +2352,18 @@ wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
/* Returns the number of the last hack run by the server.
*/
static int
-server_current_hack (void)
+server_current_hack (state *s)
{
Atom type;
int format;
unsigned long nitems, bytesafter;
unsigned char *dataP = 0;
- Display *dpy = GDK_DISPLAY();
int hack_number = -1;
- if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
+ if (!s->dpy) return hack_number;
+ if (XGetWindowProperty (s->dpy, RootWindow(s->dpy, 0), /* always screen #0 */
XA_SCREENSAVER_STATUS,
- 0, 3, False, XA_INTEGER,
+ 0, 3, FALSE, XA_INTEGER,
&type, &format, &nitems, &bytesafter,
&dataP)
== Success
@@ -2458,13 +2387,14 @@ server_current_hack (void)
static void
scroll_to_current_hack (state *s)
{
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
saver_preferences *p = &s->prefs;
int hack_number = -1;
if (p->mode == ONE_HACK) /* in "one" mode, use the one */
hack_number = p->selected_hack;
if (hack_number < 0) /* otherwise, use the last-run */
- hack_number = server_current_hack ();
+ hack_number = server_current_hack (s);
if (hack_number < 0) /* failing that, last "one mode" */
hack_number = p->selected_hack;
if (hack_number < 0) /* failing that, newest hack. */
@@ -2489,9 +2419,10 @@ scroll_to_current_hack (state *s)
if (hack_number >= 0 && hack_number < p->screenhacks_count)
{
int list_elt = s->hack_number_to_list_elt[hack_number];
- GtkWidget *list = name_to_widget (s, "list");
- force_list_select_item (s, list, list_elt, True);
+ GtkWidget *list = win->list;
+ force_list_select_item (s, list, list_elt, TRUE);
populate_demo_window (s, list_elt);
+ populate_popup_window (s);
}
}
@@ -2499,10 +2430,8 @@ scroll_to_current_hack (state *s)
static void
populate_hack_list (state *s)
{
- Display *dpy = GDK_DISPLAY();
-#ifdef HAVE_GTK2
saver_preferences *p = &s->prefs;
- GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
+ GtkTreeView *list = GTK_TREE_VIEW (XSCREENSAVER_WINDOW (s->window)->list);
GtkListStore *model;
GtkTreeSelection *selection;
GtkCellRenderer *ren;
@@ -2560,7 +2489,7 @@ populate_hack_list (state *s)
pretty_name = (hack->name
? strdup (hack->name)
- : make_hack_name (dpy, hack->command));
+ : make_hack_name (s->dpy, hack->command));
if (!available_p)
{
@@ -2568,16 +2497,15 @@ populate_hack_list (state *s)
(but don't actually make it be insensitive, since we still
want to be able to click on it.)
*/
- GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (list));
- GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
- /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
+ GtkStyleContext *c =
+ gtk_widget_get_style_context (GTK_WIDGET (list));
+ GdkRGBA fg;
char *buf = (char *) malloc (strlen (pretty_name) + 100);
-
- sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
- /* " background=\"#%02X%02X%02X\"" */
- ">%s</span>",
- fg->red >> 8, fg->green >> 8, fg->blue >> 8,
- /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
+ gtk_style_context_get_color (c, GTK_STATE_FLAG_INSENSITIVE, &fg);
+ sprintf (buf, "<span foreground=\"#%02X%02X%02X\">%s</span>",
+ (unsigned int) (0xFF * fg.red),
+ (unsigned int) (0xFF * fg.green),
+ (unsigned int) (0xFF * fg.blue),
pretty_name);
free (pretty_name);
pretty_name = buf;
@@ -2590,107 +2518,13 @@ populate_hack_list (state *s)
-1);
free (pretty_name);
}
-
-#else /* !HAVE_GTK2 */
-
- saver_preferences *p = &s->prefs;
- GtkList *list = GTK_LIST (name_to_widget (s, "list"));
- int i;
- for (i = 0; i < s->list_count; i++)
- {
- int hack_number = s->list_elt_to_hack_number[i];
- screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
-
- /* A GtkList must contain only GtkListItems, but those can contain
- an arbitrary widget. We add an Hbox, and inside that, a Checkbox
- and a Label. We handle single and double click events on the
- line itself, for clicking on the text, but the interior checkbox
- also handles its own events.
- */
- GtkWidget *line;
- GtkWidget *line_hbox;
- GtkWidget *line_check;
- GtkWidget *line_label;
- char *pretty_name;
- Bool available_p = (hack && s->hacks_available_p [hack_number]);
-
- if (!hack) continue;
-
- /* If we're to suppress uninstalled hacks, check $PATH now. */
- if (p->ignore_uninstalled_p && !available_p)
- continue;
-
- pretty_name = (hack->name
- ? strdup (hack->name)
- : make_hack_name (hack->command));
-
- line = gtk_list_item_new ();
- line_hbox = gtk_hbox_new (FALSE, 0);
- line_check = gtk_check_button_new ();
- line_label = gtk_label_new (pretty_name);
-
- gtk_container_add (GTK_CONTAINER (line), line_hbox);
- gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
- gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
-
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
- hack->enabled_p);
- gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
-
- gtk_widget_show (line_check);
- gtk_widget_show (line_label);
- gtk_widget_show (line_hbox);
- gtk_widget_show (line);
-
- free (pretty_name);
-
- gtk_container_add (GTK_CONTAINER (list), line);
- gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
- GTK_SIGNAL_FUNC (list_doubleclick_cb),
- (gpointer) s);
-
- gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
- GTK_SIGNAL_FUNC (list_checkbox_cb),
- (gpointer) s);
-
- gtk_widget_show (line);
-
- if (!available_p)
- {
- /* Make the widget be colored like insensitive widgets
- (but don't actually make it be insensitive, since we
- still want to be able to click on it.)
- */
- GtkRcStyle *rc_style;
- GdkColor fg, bg;
-
- gtk_widget_realize (GTK_WIDGET (line_label));
-
- fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
- bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
-
- rc_style = gtk_rc_style_new ();
- rc_style->fg[GTK_STATE_NORMAL] = fg;
- rc_style->bg[GTK_STATE_NORMAL] = bg;
- rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
-
- gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
- gtk_rc_style_unref (rc_style);
- }
- }
-
- gtk_signal_connect (GTK_OBJECT (list), "select_child",
- GTK_SIGNAL_FUNC (list_select_cb),
- (gpointer) s);
- gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
- GTK_SIGNAL_FUNC (list_unselect_cb),
- (gpointer) s);
-#endif /* !HAVE_GTK2 */
}
+
static void
update_list_sensitivity (state *s)
{
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
saver_preferences *p = &s->prefs;
Bool sensitive = (p->mode == RANDOM_HACKS ||
p->mode == RANDOM_HACKS_SAME ||
@@ -2699,73 +2533,32 @@ update_list_sensitivity (state *s)
p->mode == RANDOM_HACKS_SAME);
Bool blankable = (p->mode != DONT_BLANK);
-#ifndef HAVE_GTK2
- GtkWidget *head = name_to_widget (s, "col_head_hbox");
- GtkWidget *use = name_to_widget (s, "use_col_frame");
-#endif /* HAVE_GTK2 */
- GtkWidget *scroller = name_to_widget (s, "scroller");
- GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
- GtkWidget *blanker = name_to_widget (s, "blanking_table");
-
-#ifdef HAVE_GTK2
- GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
+ GtkTreeView *list = GTK_TREE_VIEW (win->list);
GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
-#else /* !HAVE_GTK2 */
- GtkList *list = GTK_LIST (name_to_widget (s, "list"));
- GList *kids = gtk_container_children (GTK_CONTAINER (list));
-
- gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
-#endif /* !HAVE_GTK2 */
- gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
- gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
- gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
-
-#ifdef HAVE_GTK2
+ gtk_widget_set_sensitive (GTK_WIDGET (win->scroller), sensitive);
+ gtk_widget_set_sensitive (GTK_WIDGET (win->next_prev_hbox), sensitive);
+ gtk_widget_set_sensitive (GTK_WIDGET (win->blanking_table), blankable);
gtk_tree_view_column_set_visible (use, checkable);
-#else /* !HAVE_GTK2 */
- if (checkable)
- gtk_widget_show (use); /* the "Use" column header */
- else
- gtk_widget_hide (use);
-
- while (kids)
- {
- GtkBin *line = GTK_BIN (kids->data);
- GtkContainer *line_hbox = GTK_CONTAINER (line->child);
- GtkWidget *line_check =
- GTK_WIDGET (gtk_container_children (line_hbox)->data);
-
- if (checkable)
- gtk_widget_show (line_check);
- else
- gtk_widget_hide (line_check);
-
- kids = kids->next;
- }
-#endif /* !HAVE_GTK2 */
}
static void
populate_prefs_page (state *s)
{
- Display *dpy = GDK_DISPLAY();
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
saver_preferences *p = &s->prefs;
- Bool can_lock_p = True;
+ Bool can_lock_p = TRUE;
- /* Disable all the "lock" controls if locking support was not provided
- at compile-time, or if running on MacOS. */
-# if defined(NO_LOCKING) || defined(__APPLE__)
- can_lock_p = False;
+# ifdef NO_LOCKING
+ can_lock_p = FALSE;
# endif
-
/* If there is only one screen, the mode menu contains
"random" but not "random-same".
*/
- if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME)
+ if (!s->multi_screen_p && p->mode == RANDOM_HACKS_SAME)
p->mode = RANDOM_HACKS;
@@ -2780,108 +2573,100 @@ populate_prefs_page (state *s)
# undef THROTTLE
# define FMT_MINUTES(NAME,N) \
- gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
-
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (win->NAME), \
+ (double) ((N) + 59) / (60 * 1000))
# define FMT_SECONDS(NAME,N) \
- gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
-
- FMT_MINUTES ("timeout_spinbutton", p->timeout);
- FMT_MINUTES ("cycle_spinbutton", p->cycle);
- FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
- FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
- FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
- FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
- FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
-
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (win->NAME), \
+ (double) ((N) / 1000))
+
+ FMT_MINUTES (timeout_spinbutton, p->timeout);
+ FMT_MINUTES (cycle_spinbutton, p->cycle);
+ FMT_MINUTES (lock_spinbutton, p->lock_timeout);
+ FMT_MINUTES (dpms_standby_spinbutton, p->dpms_standby);
+ FMT_MINUTES (dpms_suspend_spinbutton, p->dpms_suspend);
+ FMT_MINUTES (dpms_off_spinbutton, p->dpms_off);
+ FMT_SECONDS (fade_spinbutton, p->fade_seconds);
# undef FMT_MINUTES
# undef FMT_SECONDS
# define TOGGLE_ACTIVE(NAME,ACTIVEP) \
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
- (ACTIVEP))
-
- TOGGLE_ACTIVE ("lock_button", p->lock_p);
-#if 0
- TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
- TOGGLE_ACTIVE ("splash_button", p->splash_p);
-#endif
- TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
- TOGGLE_ACTIVE ("dpms_quickoff_button", p->dpms_quickoff_p);
- TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
- TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
- TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
- TOGGLE_ACTIVE ("fade_button", p->fade_p);
- TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (win->NAME), (ACTIVEP))
+
+ TOGGLE_ACTIVE (lock_button, p->lock_p);
+ TOGGLE_ACTIVE (dpms_button, p->dpms_enabled_p && s->dpms_supported_p);
+ TOGGLE_ACTIVE (dpms_quickoff_button, (p->dpms_quickoff_p &&
+ s->dpms_supported_p));
+ TOGGLE_ACTIVE (grab_desk_button, p->grab_desktop_p);
+ TOGGLE_ACTIVE (grab_video_button, p->grab_video_p);
+ TOGGLE_ACTIVE (grab_image_button, p->random_image_p);
+ TOGGLE_ACTIVE (fade_button, p->fade_p);
+ TOGGLE_ACTIVE (unfade_button, p->unfade_p);
switch (p->tmode)
{
- case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio", True); break;
- case TEXT_FILE: TOGGLE_ACTIVE ("text_file_radio", True); break;
- case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break;
- case TEXT_URL: TOGGLE_ACTIVE ("text_url_radio", True); break;
- default: TOGGLE_ACTIVE ("text_host_radio", True); break;
+ case TEXT_LITERAL: TOGGLE_ACTIVE (text_radio, TRUE); break;
+ case TEXT_FILE: TOGGLE_ACTIVE (text_file_radio, TRUE); break;
+ case TEXT_PROGRAM: TOGGLE_ACTIVE (text_program_radio, TRUE); break;
+ case TEXT_URL: TOGGLE_ACTIVE (text_url_radio, TRUE); break;
+ default: TOGGLE_ACTIVE (text_host_radio, TRUE); break;
}
# undef TOGGLE_ACTIVE
- gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
+ gtk_entry_set_text (GTK_ENTRY (win->image_text),
(p->image_directory ? p->image_directory : ""));
- gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
- p->random_image_p);
- gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
+ gtk_widget_set_sensitive (win->image_text, p->random_image_p);
+ gtk_widget_set_sensitive (win->image_browse_button,
p->random_image_p);
- gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")),
+ gtk_entry_set_text (GTK_ENTRY (win->text_entry),
(p->text_literal ? p->text_literal : ""));
- gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
+ gtk_entry_set_text (GTK_ENTRY (win->text_file_entry),
(p->text_file ? p->text_file : ""));
- gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
+ gtk_entry_set_text (GTK_ENTRY (win->text_program_entry),
(p->text_program ? p->text_program : ""));
- gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")),
+ gtk_entry_set_text (GTK_ENTRY (win->text_url_entry),
(p->text_url ? p->text_url : ""));
- gtk_widget_set_sensitive (name_to_widget (s, "text_entry"),
+ gtk_widget_set_sensitive (win->text_entry,
p->tmode == TEXT_LITERAL);
- gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"),
+ gtk_widget_set_sensitive (win->text_file_entry,
p->tmode == TEXT_FILE);
- gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"),
+ gtk_widget_set_sensitive (win->text_file_browse,
p->tmode == TEXT_FILE);
- gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"),
+ gtk_widget_set_sensitive (win->text_program_entry,
p->tmode == TEXT_PROGRAM);
- gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"),
+ gtk_widget_set_sensitive (win->text_program_browse,
p->tmode == TEXT_PROGRAM);
- gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"),
+ gtk_widget_set_sensitive (win->text_url_entry,
p->tmode == TEXT_URL);
-
/* Theme menu */
{
- GtkComboBox *cbox = GTK_COMBO_BOX (name_to_widget (s, "theme_menu"));
-
- /* Without this, pref_changed_cb gets called an exponentially-increasing
- number of times on the themes menu, despite the call to
- gtk_list_store_clear(). */
- static Bool done_once = False;
+ GtkComboBox *cbox = GTK_COMBO_BOX (win->theme_menu);
- if (cbox && !done_once)
+ if (cbox)
{
- char *themes = get_string_resource (dpy, "themeNames", "ThemeNames");
- char *token = themes;
- char *name, *name2, *last;
+ char *themes = get_string_resource(s->dpy, "themeNames", "ThemeNames");
+ char *token = themes ? themes : strdup ("default");
+ char *name, *last = 0;
GtkListStore *model;
GtkTreeIter iter;
int i = 0;
- done_once = True;
-
- g_object_get (G_OBJECT (cbox), "model", &model, NULL);
- if (!model) abort();
- gtk_list_store_clear (model);
+ /* Bad things happen if we do these things more than once. */
+ static Bool model_built_p = FALSE;
+ static Bool signal_connected_p = FALSE;
- gtk_signal_connect (GTK_OBJECT (cbox), "changed",
- GTK_SIGNAL_FUNC (pref_changed_cb), (gpointer) s);
+ if (! model_built_p)
+ {
+ g_object_get (G_OBJECT (cbox), "model", &model, NULL);
+ if (!model) abort();
+ gtk_list_store_clear (model);
+ }
while ((name = strtok_r (token, ",", &last)))
{
+ char *name2;
int L;
token = 0;
@@ -2893,22 +2678,33 @@ populate_prefs_page (state *s)
name[L-1] == '\n'))
name[--L] = 0;
- gtk_list_store_append (model, &iter);
- gtk_list_store_set (model, &iter, 0, name, -1);
+ if (! model_built_p)
+ {
+ gtk_list_store_append (model, &iter);
+ gtk_list_store_set (model, &iter, 0, name, -1);
+ }
name2 = theme_name_strip (name);
- if (!strcmp (p->dialog_theme, name2))
+ if (p->dialog_theme && name2 && !strcmp (p->dialog_theme, name2))
gtk_combo_box_set_active (cbox, i);
free (name2);
i++;
}
+
+ model_built_p = TRUE;
+
+ if (! signal_connected_p)
+ {
+ g_signal_connect (G_OBJECT (cbox), "changed",
+ G_CALLBACK (pref_changed_cb), (gpointer) win);
+ signal_connected_p = TRUE;
+ }
}
}
-
/* Map the `saver_mode' enum to mode menu to values. */
{
- GtkComboBox *opt = GTK_COMBO_BOX (name_to_widget (s, "mode_menu"));
+ GtkComboBox *opt = GTK_COMBO_BOX (win->mode_menu);
int i;
for (i = 0; i < countof(mode_menu_order); i++)
@@ -2918,146 +2714,183 @@ populate_prefs_page (state *s)
update_list_sensitivity (s);
}
- {
- Bool dpms_supported = False;
- Display *dpy = GDK_DISPLAY();
-
-#ifdef HAVE_DPMS_EXTENSION
- {
- int op = 0, event = 0, error = 0;
- if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
- dpms_supported = True;
- }
-#endif /* HAVE_DPMS_EXTENSION */
+# define SENSITIZE(NAME,SENSITIVEP) \
+ gtk_widget_set_sensitive (win->NAME, (SENSITIVEP))
+ /* Blanking and Locking
+ */
+ SENSITIZE (lock_button, can_lock_p);
+ SENSITIZE (lock_spinbutton, can_lock_p && p->lock_p);
+ SENSITIZE (lock_mlabel, can_lock_p && p->lock_p);
-# define SENSITIZE(NAME,SENSITIVEP) \
- gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
-
- /* Blanking and Locking
- */
- SENSITIZE ("lock_button", can_lock_p);
- SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
- SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
-
- /* DPMS
- */
- SENSITIZE ("dpms_frame", dpms_supported);
- SENSITIZE ("dpms_button", dpms_supported);
-
- SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
- SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
- SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
- SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
- SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
- SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
- SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
- SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
- SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
- SENSITIZE ("dpms_quickoff_button", dpms_supported);
-
- SENSITIZE ("fade_label", (p->fade_p || p->unfade_p));
- SENSITIZE ("fade_spinbutton", (p->fade_p || p->unfade_p));
+ /* DPMS
+ */
+ SENSITIZE (dpms_button, s->dpms_supported_p);
+ SENSITIZE (dpms_standby_label, s->dpms_supported_p && p->dpms_enabled_p);
+ SENSITIZE (dpms_standby_mlabel, s->dpms_supported_p && p->dpms_enabled_p);
+ SENSITIZE (dpms_standby_spinbutton,s->dpms_supported_p && p->dpms_enabled_p);
+ SENSITIZE (dpms_suspend_label, s->dpms_supported_p && p->dpms_enabled_p);
+ SENSITIZE (dpms_suspend_mlabel, s->dpms_supported_p && p->dpms_enabled_p);
+ SENSITIZE (dpms_suspend_spinbutton,s->dpms_supported_p && p->dpms_enabled_p);
+ SENSITIZE (dpms_off_label, s->dpms_supported_p && p->dpms_enabled_p);
+ SENSITIZE (dpms_off_mlabel, s->dpms_supported_p && p->dpms_enabled_p);
+ SENSITIZE (dpms_off_spinbutton, s->dpms_supported_p && p->dpms_enabled_p);
+ SENSITIZE (dpms_quickoff_button, s->dpms_supported_p);
+
+ SENSITIZE (fade_label, (p->fade_p || p->unfade_p));
+ SENSITIZE (fade_spinbutton, (p->fade_p || p->unfade_p));
# undef SENSITIZE
- }
-}
-
-/* Allow the documentation label to re-flow when the text is changed.
- http://blog.borovsak.si/2009/05/wrapping-adn-resizing-gtklabel.html
- */
-static void
-cb_allocate (GtkWidget *label, GtkAllocation *allocation, gpointer data)
-{
- gtk_widget_set_size_request (label, allocation->width - 8, -1);
+ if (!s->dpms_supported_p)
+ gtk_frame_set_label (GTK_FRAME (win->dpms_frame),
+ _("Display Power Management (not supported by this display)"));
}
-static void
-populate_popup_window (state *s)
+/* Creates a human-readable anchor to put on a URL.
+ */
+static char *
+anchorize (const char *url)
{
- GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
- char *doc_string = 0;
-
- /* #### not in Gtk 1.2
- gtk_label_set_selectable (doc);
- */
-
- g_signal_connect (G_OBJECT (doc), "size-allocate",
- G_CALLBACK (cb_allocate), NULL);
-
-# ifdef HAVE_XML
- if (s->cdata)
- {
- free_conf_data (s->cdata);
- s->cdata = 0;
+ const char *wiki1 = "http://en.wikipedia.org/wiki/";
+ const char *wiki2 = "https://en.wikipedia.org/wiki/";
+ const char *math1 = "http://mathworld.wolfram.com/";
+ const char *math2 = "https://mathworld.wolfram.com/";
+ if (!strncmp (wiki1, url, strlen(wiki1)) ||
+ !strncmp (wiki2, url, strlen(wiki2))) {
+ char *anchor = (char *) malloc (strlen(url) * 3 + 10);
+ const char *in;
+ char *out;
+ strcpy (anchor, "Wikipedia: \"");
+ in = url + strlen(!strncmp (wiki1, url, strlen(wiki1)) ? wiki1 : wiki2);
+ out = anchor + strlen(anchor);
+ while (*in) {
+ if (*in == '_') {
+ *out++ = ' ';
+ } else if (*in == '#') {
+ *out++ = ':';
+ *out++ = ' ';
+ } else if (*in == '%') {
+ char hex[3];
+ unsigned int n = 0;
+ hex[0] = in[1];
+ hex[1] = in[2];
+ hex[2] = 0;
+ sscanf (hex, "%x", &n);
+ *out++ = (char) n;
+ in += 2;
+ } else {
+ *out++ = *in;
+ }
+ in++;
}
-
- {
- saver_preferences *p = &s->prefs;
- int list_elt = selected_list_element (s);
- int hack_number = (list_elt >= 0 && list_elt < s->list_count
- ? s->list_elt_to_hack_number[list_elt]
- : -1);
- screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
- if (hack)
- {
- GtkWidget *parent = name_to_widget (s, "settings_vbox");
- GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
- const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
- s->cdata = load_configurator (cmd_line, s->debug_p);
- if (s->cdata && s->cdata->widget)
- gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
- TRUE, TRUE, 0);
+ *out++ = '"';
+ *out = 0;
+ return anchor;
+
+ } else if (!strncmp (math1, url, strlen(math1)) ||
+ !strncmp (math2, url, strlen(math2))) {
+ char *anchor = (char *) malloc (strlen(url) * 3 + 10);
+ const char *start, *in;
+ char *out;
+ strcpy (anchor, "MathWorld: \"");
+ start = url + strlen(!strncmp (math1, url, strlen(math1)) ? math1 : math2);
+ in = start;
+ out = anchor + strlen(anchor);
+ while (*in) {
+ if (*in == '_') {
+ *out++ = ' ';
+ } else if (in != start && *in >= 'A' && *in <= 'Z') {
+ *out++ = ' ';
+ *out++ = *in;
+ } else if (!strncmp (in, ".htm", 4)) {
+ break;
+ } else {
+ *out++ = *in;
}
- }
-
- doc_string = (s->cdata
- ? s->cdata->description
- : 0);
-# else /* !HAVE_XML */
- doc_string = _("Descriptions not available: no XML support compiled in.");
-# endif /* !HAVE_XML */
-
- gtk_label_set_text (doc, (doc_string
- ? _(doc_string)
- : _("No description available.")));
+ in++;
+ }
+ *out++ = '"';
+ *out = 0;
+ return anchor;
- {
- GtkWidget *w = name_to_widget (s, "dialog_vbox");
- gtk_widget_hide (w);
- gtk_widget_unrealize (w);
- gtk_widget_realize (w);
- gtk_widget_show (w);
+ } else {
+ return strdup (url);
}
}
-static void
-sensitize_demo_widgets (state *s, Bool sensitive_p)
+/* Quote the text as HTML and make URLs be clickable links.
+ */
+static char *
+hreffify (const char *in)
{
- const char *names[] = { "demo", "settings",
- "cmd_label", "cmd_text", "manual",
- "visual", "visual_combo" };
- int i;
- for (i = 0; i < countof(names); i++)
+ char *ret, *out;
+ if (!in) return 0;
+
+ ret = out = malloc (strlen(in) * 3);
+ while (*in)
{
- GtkWidget *w = name_to_widget (s, names[i]);
- gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
+ if (!strncmp (in, "http://", 7) ||
+ !strncmp (in, "https://", 8))
+ {
+ char *url, *anchor;
+ const char *end = in;
+ while (*end &&
+ *end != ' ' && *end != '\t' && *end != '\r' && *end != '\n')
+ end++;
+
+ url = (char *) malloc (end - in + 1);
+ strncpy (url, in, end-in);
+ url [end-in] = 0;
+
+ anchor = anchorize (url);
+
+ strcpy (out, "<a href=\""); out += strlen (out);
+ strcpy (out, url); out += strlen (out);
+ strcpy (out, "\">"); out += strlen (out);
+ strcpy (out, anchor); out += strlen (out);
+ strcpy (out, "</a>"); out += strlen (out);
+ free (url);
+ free (anchor);
+ in = end;
+ }
+ else if (*in == '<')
+ {
+ strcpy (out, "&lt;");
+ out += strlen (out);
+ in++;
+ }
+ else if (*in == '>')
+ {
+ strcpy (out, "&gt;");
+ out += strlen (out);
+ in++;
+ }
+ else if (*in == '&')
+ {
+ strcpy (out, "&amp;");
+ out += strlen (out);
+ in++;
+ }
+ else
+ {
+ *out++ = *in++;
+ }
}
+ *out = 0;
+ return ret;
}
static void
sensitize_menu_items (state *s, Bool force_p)
{
- static Bool running_p = False;
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
+ static Bool running_p = FALSE;
static time_t last_checked = 0;
time_t now = time ((time_t *) 0);
- const char *names[] = { "activate_action", "lock_action", "kill_action",
- /* "demo" */ };
- int i;
if (force_p || now > last_checked + 10) /* check every 10 seconds */
{
@@ -3065,289 +2898,52 @@ sensitize_menu_items (state *s, Bool force_p)
last_checked = time ((time_t *) 0);
}
- for (i = 0; i < countof(names); i++)
- {
- GtkAction *a = GTK_ACTION (gtk_builder_get_object (s->gtk_ui, names[i]));
- gtk_action_set_sensitive (a, running_p);
- }
-}
-
-
-/* When the File menu is de-posted after a "Restart Daemon" command,
- the window underneath doesn't repaint for some reason. I guess this
- is a bug in exposure handling in GTK or GDK. This works around it.
- */
-static void
-force_dialog_repaint (state *s)
-{
-#if 1
- /* Tell GDK to invalidate and repaint the whole window.
- */
- GdkWindow *w = GET_WINDOW (s->toplevel_widget);
- GdkRegion *region = gdk_region_new ();
- GdkRectangle rect;
- rect.x = rect.y = 0;
- rect.width = rect.height = 32767;
- gdk_region_union_with_rect (region, &rect);
- gdk_window_invalidate_region (w, region, True);
- gdk_region_destroy (region);
- gdk_window_process_updates (w, True);
-#else
- /* Force the server to send an exposure event by creating and then
- destroying a window as a child of the top level shell.
- */
- Display *dpy = GDK_DISPLAY();
- Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window);
- Window w;
- XWindowAttributes xgwa;
- XGetWindowAttributes (dpy, parent, &xgwa);
- w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0);
- XMapRaised (dpy, w);
- XDestroyWindow (dpy, w);
- XSync (dpy, False);
-#endif
-}
-
-
-/* Even though we've given these text fields a maximum number of characters,
- their default size is still about 30 characters wide -- so measure out
- a string in their font, and resize them to just fit that.
- */
-static void
-fix_text_entry_sizes (state *s)
-{
- GtkWidget *w;
-
-# if 0 /* appears no longer necessary with Gtk 1.2.10 */
- const char * const spinbuttons[] = {
- "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
- "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
- "dpms_off_spinbutton",
- "-fade_spinbutton" };
- int i;
- int width = 0;
-
- for (i = 0; i < countof(spinbuttons); i++)
- {
- const char *n = spinbuttons[i];
- int cols = 4;
- while (*n == '-') n++, cols--;
- w = GTK_WIDGET (name_to_widget (s, n));
- width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
- gtk_widget_set_usize (w, width, -2);
- }
-
- /* Now fix the width of the combo box.
- */
- w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
- w = GTK_COMBO_BOX_ENTRY (w)->entry;
- width = gdk_string_width (w->style->font, "PseudoColor___");
- gtk_widget_set_usize (w, width, -2);
-
- /* Now fix the width of the file entry text.
- */
- w = GTK_WIDGET (name_to_widget (s, "image_text"));
- width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
- gtk_widget_set_usize (w, width, -2);
-
- /* Now fix the width of the command line text.
- */
- w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
- width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
- gtk_widget_set_usize (w, width, -2);
-
-# endif /* 0 */
-
- /* Now fix the height of the list widget:
- make it default to being around 10 text-lines high instead of 4.
- */
- w = GTK_WIDGET (name_to_widget (s, "list"));
- {
- int lines = 10;
- int height;
- int leading = 3; /* approximate is ok... */
- int border = 2;
-
-#ifdef HAVE_GTK2
- PangoFontMetrics *pain =
- pango_context_get_metrics (gtk_widget_get_pango_context (w),
- gtk_widget_get_style (w)->font_desc,
- gtk_get_default_language ());
- height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
- pango_font_metrics_get_descent (pain));
-#else /* !HAVE_GTK2 */
- height = w->style->font->ascent + w->style->font->descent;
-#endif /* !HAVE_GTK2 */
-
- height += leading;
- height *= lines;
- height += border * 2;
- w = GTK_WIDGET (name_to_widget (s, "scroller"));
- gtk_widget_set_usize (w, -2, height);
- }
-}
-
-
-#ifndef HAVE_GTK2
-
-/* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
- */
-
-static char *up_arrow_xpm[] = {
- "15 15 4 1",
- " c None",
- "- c #FFFFFF",
- "+ c #D6D6D6",
- "@ c #000000",
-
- " @ ",
- " @ ",
- " -+@ ",
- " -+@ ",
- " -+++@ ",
- " -+++@ ",
- " -+++++@ ",
- " -+++++@ ",
- " -+++++++@ ",
- " -+++++++@ ",
- " -+++++++++@ ",
- " -+++++++++@ ",
- " -+++++++++++@ ",
- " @@@@@@@@@@@@@ ",
- " ",
-
- /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
- the end of the array (Gtk 1.2.5.) */
- "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
- "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
-};
-
-static char *down_arrow_xpm[] = {
- "15 15 4 1",
- " c None",
- "- c #FFFFFF",
- "+ c #D6D6D6",
- "@ c #000000",
-
- " ",
- " ------------- ",
- " -+++++++++++@ ",
- " -+++++++++@ ",
- " -+++++++++@ ",
- " -+++++++@ ",
- " -+++++++@ ",
- " -+++++@ ",
- " -+++++@ ",
- " -+++@ ",
- " -+++@ ",
- " -+@ ",
- " -+@ ",
- " @ ",
- " @ ",
-
- /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
- the end of the array (Gtk 1.2.5.) */
- "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
- "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
-};
+ gtk_widget_set_sensitive (win->activate_menuitem, running_p);
+ gtk_widget_set_sensitive (win->lock_menuitem, running_p);
+ gtk_widget_set_sensitive (win->kill_menuitem, running_p);
-static void
-pixmapify_button (state *s, int down_p)
-{
- GdkPixmap *pixmap;
- GdkBitmap *mask;
- GtkWidget *pixmapwid;
- GtkStyle *style;
- GtkWidget *w;
-
- w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
- style = gtk_widget_get_style (w);
- mask = 0;
- pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
- &style->bg[GTK_STATE_NORMAL],
- (down_p
- ? (gchar **) down_arrow_xpm
- : (gchar **) up_arrow_xpm));
- pixmapwid = gtk_pixmap_new (pixmap, mask);
- gtk_widget_show (pixmapwid);
- gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
- gtk_container_add (GTK_CONTAINER (w), pixmapwid);
-}
-
-static void
-map_next_button_cb (GtkWidget *w, gpointer user_data)
-{
- state *s = (state *) user_data;
- pixmapify_button (s, 1);
-}
-
-static void
-map_prev_button_cb (GtkWidget *w, gpointer user_data)
-{
- state *s = (state *) user_data;
- pixmapify_button (s, 0);
+ gtk_menu_item_set_label (GTK_MENU_ITEM (win->restart_menuitem),
+ (running_p
+ ? _("Restart Daemon")
+ : _("Launch Daemon")));
}
-#endif /* !HAVE_GTK2 */
-
-
-#ifndef HAVE_GTK2
-/* Work around a Gtk bug that causes label widgets to wrap text too early.
- */
-
-static void
-you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
- GtkAllocation *allocation,
- void *foo)
-{
- GtkRequisition req;
- GtkWidgetAuxInfo *aux_info;
-
- aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
- aux_info->width = allocation->width;
- aux_info->height = -2;
- aux_info->x = -1;
- aux_info->y = -1;
- gtk_widget_size_request (label, &req);
-}
-
-/* Feel the love. Thanks to Nat Friedman for finding this workaround.
+/* Fill in the contents of the main window, and a few things on the
+ settings dialog.
*/
static void
-eschew_gtk_lossage (GtkLabel *label)
-{
- GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
- aux_info->width = GTK_WIDGET (label)->allocation.width;
- aux_info->height = -2;
- aux_info->x = -1;
- aux_info->y = -1;
-
- gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
-
- gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
- GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
- 0);
-
- gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
-
- gtk_widget_queue_resize (GTK_WIDGET (label));
-}
-#endif /* !HAVE_GTK2 */
-
-
-static void
populate_demo_window (state *s, int list_elt)
{
- Display *dpy = GDK_DISPLAY();
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
+ XScreenSaverDialog *dialog = XSCREENSAVER_DIALOG (s->dialog);
saver_preferences *p = &s->prefs;
screenhack *hack;
char *pretty_name;
- GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
- GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "opt_frame"));
- GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
- GtkComboBoxEntry *vis = GTK_COMBO_BOX_ENTRY (name_to_widget (s, "visual_combo"));
- GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
+ GtkFrame *frame1 = GTK_FRAME (win->preview_frame);
+ GtkFrame *frame2 = dialog ? GTK_FRAME (dialog->opt_frame) : 0;
+ GtkEntry *cmd = dialog ? GTK_ENTRY (dialog->cmd_text) : 0;
+ GtkComboBox *vis = dialog ? GTK_COMBO_BOX (dialog->visual_combo) : 0;
+
+ /* Enforce a minimum size on the preview pane. */
+ if (s->dpy)
+ {
+ int dw = DisplayWidth (s->dpy, 0);
+ int dh = DisplayHeight (s->dpy, 0);
+ int minw, minh;
+ # define TRY(W) do { \
+ minw = (W); minh = minw * 9/16 + 48; \
+ if (dw > minw * 1.5 && dh > minh * 1.5) \
+ gtk_widget_set_size_request (GTK_WIDGET (frame1), minw, minh); \
+ } while(0)
+ TRY (300);
+ TRY (400);
+ TRY (480);
+ TRY (640);
+ TRY (800);
+ /* TRY (960); */
+# undef TRY
+ }
if (p->mode == BLANK_ONLY)
{
@@ -3371,7 +2967,7 @@ populate_demo_window (state *s, int list_elt)
pretty_name = (hack
? (hack->name
? strdup (hack->name)
- : make_hack_name (dpy, hack->command))
+ : make_hack_name (s->dpy, hack->command))
: 0);
if (hack)
@@ -3383,31 +2979,40 @@ populate_demo_window (state *s, int list_elt)
if (!pretty_name)
pretty_name = strdup (_("Preview"));
- gtk_frame_set_label (frame1, _(pretty_name));
- gtk_frame_set_label (frame2, _(pretty_name));
+ if (dialog->unedited_cmdline) free (dialog->unedited_cmdline);
+ dialog->unedited_cmdline = strdup (hack ? hack->command : "");
- gtk_entry_set_text (cmd, (hack ? hack->command : ""));
- gtk_entry_set_position (cmd, 0);
+ gtk_frame_set_label (frame1, _(pretty_name));
+ if (frame2)
+ gtk_frame_set_label (frame2, _(pretty_name));
+ if (cmd)
+ gtk_entry_set_text (cmd, dialog->unedited_cmdline);
{
char title[255];
sprintf (title, _("%s: %.100s Settings"),
progclass, (pretty_name ? pretty_name : "???"));
- gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
+ gtk_window_set_title (GTK_WINDOW (s->window), title);
+ if (s->dialog)
+ gtk_window_set_title (GTK_WINDOW (s->dialog), title);
}
- gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (vis))),
- (hack
- ? (hack->visual && *hack->visual
- ? hack->visual
- : _("Any"))
- : ""));
+ /* Fill in the Visual combo-box */
+ if (vis)
+ gtk_entry_set_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (vis))),
+ (hack
+ ? (hack->visual && *hack->visual
+ ? hack->visual
+ : _("Any"))
+ : ""));
- sensitize_demo_widgets (s, (hack ? True : False));
+ sensitize_demo_widgets (s, (hack ? TRUE : FALSE));
if (pretty_name) free (pretty_name);
- ensure_selected_item_visible (list);
+ /* This causes the window to scroll out from under the mouse when
+ clicking on an item, vertically centering it. That's annoying. */
+ /* ensure_selected_item_visible (s, list); */
s->_selected_list_element = list_elt;
}
@@ -3416,19 +3021,7 @@ populate_demo_window (state *s, int list_elt)
static void
widget_deleter (GtkWidget *widget, gpointer data)
{
- /* #### Well, I want to destroy these widgets, but if I do that, they get
- referenced again, and eventually I get a SEGV. So instead of
- destroying them, I'll just hide them, and leak a bunch of memory
- every time the disk file changes. Go go go Gtk!
-
- #### Ok, that's a lie, I get a crash even if I just hide the widget
- and don't ever delete it. Fuck!
- */
-#if 0
gtk_widget_destroy (widget);
-#else
- gtk_widget_hide (widget);
-#endif
}
@@ -3452,7 +3045,6 @@ sort_hack_cmp (const void *a, const void *b)
static void
initialize_sort_map (state *s)
{
- Display *dpy = GDK_DISPLAY();
saver_preferences *p = &s->prefs;
int i, j;
@@ -3503,7 +3095,7 @@ initialize_sort_map (state *s)
screenhack *hack = p->screenhacks[i];
char *name = (hack->name && *hack->name
? strdup (hack->name)
- : make_hack_name (dpy, hack->command));
+ : make_hack_name (s->dpy, hack->command));
gchar *s2 = g_str_to_ascii (name, 0); /* Sort "Möbius" properly */
gchar *s3 = g_ascii_strdown (s2, -1);
free (name);
@@ -3538,13 +3130,13 @@ initialize_sort_map (state *s)
static int
maybe_reload_init_file (state *s)
{
- Display *dpy = GDK_DISPLAY();
saver_preferences *p = &s->prefs;
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
int status = 0;
- static Bool reentrant_lock = False;
+ static Bool reentrant_lock = FALSE;
if (reentrant_lock) return 0;
- reentrant_lock = True;
+ reentrant_lock = TRUE;
if (init_file_changed_p (p))
{
@@ -3555,120 +3147,106 @@ maybe_reload_init_file (state *s)
if (!f || !*f) return 0;
b = (char *) malloc (strlen(f) + 1024);
- sprintf (b,
- _("Warning:\n\n"
- "file \"%s\" has changed, reloading.\n"),
- f);
- warning_dialog (s->toplevel_widget, b, D_NONE, 100);
+ sprintf (b, _("file \"%s\" has changed, reloading.\n"), f);
+ warning_dialog (s->window, _("Warning"), b);
free (b);
- load_init_file (dpy, p);
+ load_init_file (s->dpy, p);
initialize_sort_map (s);
list_elt = selected_list_element (s);
- list = name_to_widget (s, "list");
+ list = win->list;
gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
populate_hack_list (s);
- force_list_select_item (s, list, list_elt, True);
+ force_list_select_item (s, list, list_elt, TRUE);
populate_prefs_page (s);
populate_demo_window (s, list_elt);
- ensure_selected_item_visible (list);
+ populate_popup_window (s);
+ ensure_selected_item_visible (s, list);
status = 1;
}
- reentrant_lock = False;
+ reentrant_lock = FALSE;
return status;
}
-
/* Making the preview window have the right X visual (so that GL works.)
*/
static Visual *get_best_gl_visual (state *);
static GdkVisual *
-x_visual_to_gdk_visual (Visual *xv)
+x_visual_to_gdk_visual (GdkWindow *win, Visual *xv)
{
- GList *gvs = gdk_list_visuals();
- if (!xv) return gdk_visual_get_system();
- for (; gvs; gvs = gvs->next)
+ if (xv)
{
- GdkVisual *gv = (GdkVisual *) gvs->data;
- if (xv == GDK_VISUAL_XVISUAL (gv))
- return gv;
+ GdkScreen *screen = gdk_window_get_screen (win);
+ GList *gvs = gdk_screen_list_visuals (screen);
+ /* This list is sometimes NULL, not even the default visual! */
+ for (; gvs; gvs = gvs->next)
+ {
+ GdkVisual *gv = (GdkVisual *) gvs->data;
+ if (xv == GDK_VISUAL_XVISUAL (gv))
+ return gv;
+ }
}
- fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
- blurb(), (unsigned long) xv->visualid);
- abort();
+ return 0;
}
+
static void
clear_preview_window (state *s)
{
- GtkWidget *p;
- GdkWindow *window;
- GtkStyle *style;
-
- if (!s->toplevel_widget) return; /* very early */
- p = name_to_widget (s, "preview");
- window = GET_WINDOW (p);
-
- if (!window) return;
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
+ int list_elt = selected_list_element (s);
+ int hack_number = (list_elt >= 0
+ ? s->list_elt_to_hack_number[list_elt]
+ : -1);
+ Bool available_p = (hack_number >= 0
+ ? s->hacks_available_p [hack_number]
+ : TRUE);
+ Bool nothing_p = (s->total_available < 5);
+
+ GtkWidget *notebook = win->preview_notebook;
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook),
+ (!s->running_preview_error_p ? 0 : /* ok */
+ nothing_p ? 3 : /* no hacks installed */
+ !available_p ? 2 : /* hack not installed */
+ s->wayland_p ? 4 : /* fucking wayland */
+ 1)); /* preview failed */
+}
- /* Flush the widget background down into the window, in case a subproc
- has changed it. */
- style = gtk_widget_get_style (p);
- gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
- gdk_window_clear (window);
- {
- int list_elt = selected_list_element (s);
- int hack_number = (list_elt >= 0
- ? s->list_elt_to_hack_number[list_elt]
- : -1);
- Bool available_p = (hack_number >= 0
- ? s->hacks_available_p [hack_number]
- : True);
- Bool nothing_p = (s->total_available < 5);
-
-#ifdef HAVE_GTK2
- GtkWidget *notebook = name_to_widget (s, "preview_notebook");
- gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
- (s->running_preview_error_p
- ? (available_p ? 1 :
- nothing_p ? 3 : 2)
- : 0));
-#else /* !HAVE_GTK2 */
- if (s->running_preview_error_p)
- {
- const char * const lines1[] = { N_("No Preview"), N_("Available") };
- const char * const lines2[] = { N_("Not"), N_("Installed") };
- int nlines = countof(lines1);
- int lh = p->style->font->ascent + p->style->font->descent;
- int y, i;
- gint w, h;
-
- const char * const *lines = (available_p ? lines1 : lines2);
-
- gdk_window_get_size (window, &w, &h);
- y = (h - (lh * nlines)) / 2;
- y += p->style->font->ascent;
- for (i = 0; i < nlines; i++)
- {
- int sw = gdk_string_width (p->style->font, _(lines[i]));
- int x = (w - sw) / 2;
- gdk_draw_string (window, p->style->font,
- p->style->fg_gc[GTK_STATE_NORMAL],
- x, y, _(lines[i]));
- y += lh;
- }
- }
-#endif /* !HAVE_GTK2 */
- }
+static gboolean
+preview_resize_cb (GtkWidget *self, GdkEvent *event, gpointer data)
+{
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (data);
+ state *s = &win->state;
- gdk_flush ();
+ /* If a subproc is running, clear the window to black when we resize.
+ Without this, sometimes turds get left behind. */
+ if (s->dpy && !s->wayland_p && s->running_preview_cmd)
+ {
+ GdkWindow *window = gtk_widget_get_window (self);
+ Window id;
+ XWindowAttributes xgwa;
+ XGCValues gcv;
+ GC gc;
+
+ if (! window) return TRUE;
+ id = gdk_x11_window_get_xid (window);
+ if (! id) return TRUE;
+
+ /* Not sure why XClearWindow is insufficient here, but it is. */
+ XGetWindowAttributes (s->dpy, id, &xgwa);
+ gcv.foreground = BlackPixelOfScreen (xgwa.screen);
+ gc = XCreateGC (s->dpy, id, GCForeground, &gcv);
+ XFillRectangle (s->dpy, id, gc, 0, 0, xgwa.width, xgwa.height);
+ XFreeGC (s->dpy, gc);
+ }
+ return FALSE;
}
@@ -3680,18 +3258,20 @@ reset_preview_window (state *s)
it's best to just always destroy and recreate the preview window
when changing hacks, instead of always trying to reuse the same one?
*/
- GtkWidget *pr = name_to_widget (s, "preview");
- if (GET_REALIZED (pr))
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
+ GtkWidget *pr = win->preview;
+ if (s->dpy && !s->wayland_p && gtk_widget_get_realized (pr))
{
- GdkWindow *window = GET_WINDOW (pr);
- Window oid = (window ? GDK_WINDOW_XWINDOW (window) : 0);
+ GdkWindow *window = gtk_widget_get_window (pr);
+ Window oid = (window ? gdk_x11_window_get_xid (window) : 0);
Window id;
gtk_widget_hide (pr);
gtk_widget_unrealize (pr);
+ gtk_widget_set_has_window (pr, TRUE);
gtk_widget_realize (pr);
gtk_widget_show (pr);
- id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
- if (s->debug_p)
+ id = (window ? gdk_x11_window_get_xid (window) : 0);
+ if (s->debug_p && oid != id)
fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
(unsigned int) oid,
(unsigned int) id);
@@ -3699,60 +3279,47 @@ reset_preview_window (state *s)
}
+/* Make the preview widget use the best GL visual.
+ We just always use that one rather than switching.
+ */
static void
fix_preview_visual (state *s)
{
- GtkWidget *widget = name_to_widget (s, "preview");
- Visual *xvisual = get_best_gl_visual (s);
- GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
- GdkVisual *dvisual = gdk_visual_get_system();
- GdkColormap *cmap = (visual == dvisual
- ? gdk_colormap_get_system ()
- : gdk_colormap_new (visual, False));
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
+ Visual *xvisual = s->gl_visual;
+ GtkWidget *widget = win->preview;
+ GdkWindow *gwindow = gtk_widget_get_window (GTK_WIDGET (win));
+ GdkScreen *gscreen = gdk_window_get_screen (gwindow);
+ GdkVisual *gvisual1 = gdk_screen_get_system_visual (gscreen);
+ GdkVisual *gvisual2 = x_visual_to_gdk_visual (gwindow, xvisual);
+
+ if (! gvisual2)
+ {
+ gvisual2 = gvisual1;
+ if (s->debug_p)
+ fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual;"
+ " winging it.\n", blurb(),
+ (xvisual ? (unsigned long) xvisual->visualid : 0L));
+ }
if (s->debug_p)
fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
- (visual == dvisual ? "default" : "non-default"),
+ (gvisual1 == gvisual2 ? "default" : "non-default"),
(xvisual ? (unsigned long) xvisual->visualid : 0L));
- if (!GET_REALIZED (widget) ||
- gtk_widget_get_visual (widget) != visual)
+ if (!gtk_widget_get_realized (widget) ||
+ gtk_widget_get_visual (widget) != gvisual2)
{
gtk_widget_unrealize (widget);
- gtk_widget_set_visual (widget, visual);
- gtk_widget_set_colormap (widget, cmap);
+ gtk_widget_set_has_window (widget, TRUE);
+ gtk_widget_set_visual (widget, gvisual2);
gtk_widget_realize (widget);
}
- /* Set the Widget colors to be white-on-black. */
- {
- GdkWindow *window = GET_WINDOW (widget);
- GtkStyle *style = gtk_style_copy (gtk_widget_get_style (widget));
- GdkColormap *cmap = gtk_widget_get_colormap (widget);
- GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
- GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
- GdkGC *fgc = gdk_gc_new(window);
- GdkGC *bgc = gdk_gc_new(window);
- if (!gdk_color_white (cmap, fg)) abort();
- if (!gdk_color_black (cmap, bg)) abort();
- gdk_gc_set_foreground (fgc, fg);
- gdk_gc_set_background (fgc, bg);
- gdk_gc_set_foreground (bgc, bg);
- gdk_gc_set_background (bgc, fg);
- style->fg_gc[GTK_STATE_NORMAL] = fgc;
- style->bg_gc[GTK_STATE_NORMAL] = fgc;
- gtk_widget_set_style (widget, style);
-
- /* For debugging purposes, put a title on the window (so that
- it can be easily found in the output of "xwininfo -tree".)
- */
- gdk_window_set_title (window, "Preview");
- }
-
gtk_widget_show (widget);
}
-
+
/* Subprocesses
*/
@@ -3803,11 +3370,12 @@ reap_zombies (state *s)
}
+#define EXEC_FAILED_EXIT_STATUS -33
+
/* Mostly lifted from driver/subprocs.c */
static Visual *
get_best_gl_visual (state *s)
{
- Display *dpy = GDK_DISPLAY();
pid_t forked;
int fds [2];
int in, out;
@@ -3838,12 +3406,10 @@ get_best_gl_visual (state *s)
}
case 0:
{
- int stdout_fd = 1;
-
close (in); /* don't need this one */
- close (ConnectionNumber (dpy)); /* close display fd */
+ close (ConnectionNumber (s->dpy)); /* close display fd */
- if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
+ if (dup2 (out, STDOUT_FILENO) < 0) /* pipe stdout */
{
perror ("could not dup() a new stdout:");
return 0;
@@ -3865,27 +3431,44 @@ get_best_gl_visual (state *s)
If one uses exit() instead of _exit(), then one sometimes gets a
spurious "Gdk-ERROR: Fatal IO error on X server" error message.
*/
- _exit (1); /* exits fork */
+ _exit (EXEC_FAILED_EXIT_STATUS); /* exits fork */
break;
}
default:
{
int result = 0;
int wait_status = 0;
+ int exit_status = EXEC_FAILED_EXIT_STATUS;
FILE *f = fdopen (in, "r");
unsigned int v = 0;
char c;
+ int i = 0;
close (out); /* don't need this one */
*buf = 0;
- if (!fgets (buf, sizeof(buf)-1, f))
- *buf = 0;
+ do {
+ errno = 0;
+ if (! fgets (buf, sizeof(buf)-1, f))
+ *buf = 0;
+ } while (errno == EINTR && /* fgets might fail due to SIGCHLD. */
+ i++ < 1000); /* And just in case. */
+
fclose (f);
/* Wait for the child to die. */
- waitpid (-1, &wait_status, 0);
+ waitpid (forked, &wait_status, 0);
+
+ exit_status = WEXITSTATUS (wait_status);
+ /* Treat exit code as a signed 8-bit quantity. */
+ if (exit_status & 0x80) exit_status |= ~0xFF;
+
+ if (exit_status == EXEC_FAILED_EXIT_STATUS)
+ {
+ fprintf (stderr, "%s: %s is not installed\n", blurb(), av[0]);
+ return 0;
+ }
if (1 == sscanf (buf, "0x%x %c", &v, &c))
result = (int) v;
@@ -3899,10 +3482,9 @@ get_best_gl_visual (state *s)
}
else
{
- Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
+ Visual *v = id_to_visual (DefaultScreenOfDisplay (s->dpy), result);
if (s->debug_p)
- fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
- blurb(), av[0], result);
+ fprintf (stderr, "%s: GL visual is 0x%X\n", blurb(), result);
if (!v) abort();
return v;
}
@@ -3916,14 +3498,14 @@ get_best_gl_visual (state *s)
static void
kill_preview_subproc (state *s, Bool reset_p)
{
- s->running_preview_error_p = False;
+ s->running_preview_error_p = FALSE;
reap_zombies (s);
clear_preview_window (s);
if (s->subproc_check_timer_id)
{
- gtk_timeout_remove (s->subproc_check_timer_id);
+ g_source_remove (s->subproc_check_timer_id);
s->subproc_check_timer_id = 0;
s->subproc_check_countdown = 0;
}
@@ -3979,30 +3561,33 @@ kill_preview_subproc (state *s, Bool reset_p)
static void
launch_preview_subproc (state *s)
{
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
saver_preferences *p = &s->prefs;
Window id;
char *new_cmd = 0;
pid_t forked;
const char *cmd = s->desired_preview_cmd;
- GtkWidget *pr = name_to_widget (s, "preview");
+ GtkWidget *pr = win->preview;
GdkWindow *window;
reset_preview_window (s);
- window = GET_WINDOW (pr);
+ window = gtk_widget_get_window (pr);
- s->running_preview_error_p = False;
+ s->running_preview_error_p = FALSE;
- if (s->preview_suppressed_p)
+ if (s->preview_suppressed_p || !s->gl_visual)
{
- kill_preview_subproc (s, False);
+ kill_preview_subproc (s, FALSE);
goto DONE;
}
new_cmd = malloc (strlen (cmd) + 40);
- id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
+ id = (window && !s->wayland_p
+ ? gdk_x11_window_get_xid (window)
+ : 0);
if (id == 0)
{
/* No window id? No command to run. */
@@ -4012,19 +3597,22 @@ launch_preview_subproc (state *s)
else
{
/* We do this instead of relying on $XSCREENSAVER_WINDOW specifically
- so that third-party savers that don't implement -window-id will fail:
+ so that third-party savers that don't implement --window-id will fail:
otherwise we might have full-screen windows popping up when we were
just trying to get a preview thumbnail.
*/
strcpy (new_cmd, cmd);
- sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
+ sprintf (new_cmd + strlen (new_cmd), " --window-id 0x%X",
(unsigned int) id);
}
- kill_preview_subproc (s, False);
+ if (id && s->screenshot)
+ screenshot_save (s->dpy, id, s->screenshot);
+
+ kill_preview_subproc (s, FALSE);
if (! new_cmd)
{
- s->running_preview_error_p = True;
+ s->running_preview_error_p = TRUE;
clear_preview_window (s);
goto DONE;
}
@@ -4036,13 +3624,13 @@ launch_preview_subproc (state *s)
char buf[255];
sprintf (buf, "%s: couldn't fork", blurb());
perror (buf);
- s->running_preview_error_p = True;
+ s->running_preview_error_p = TRUE;
goto DONE;
break;
}
case 0:
{
- close (ConnectionNumber (GDK_DISPLAY()));
+ close (ConnectionNumber (s->dpy));
hack_subproc_environment (id, s->debug_p);
@@ -4081,6 +3669,16 @@ launch_preview_subproc (state *s)
}
}
+ /* Put some props on the embedded preview window, for debugging. */
+ XStoreName (s->dpy, id, "XScreenSaver Settings Preview");
+ XChangeProperty (s->dpy, id, XA_WM_COMMAND,
+ XA_STRING, 8, PropModeReplace,
+ (unsigned char *) new_cmd,
+ strlen (new_cmd));
+ XChangeProperty (s->dpy, id, XA_NET_WM_PID,
+ XA_CARDINAL, 32, PropModeReplace,
+ (unsigned char *) &forked, 1);
+
schedule_preview_check (s);
DONE:
@@ -4101,8 +3699,7 @@ hack_environment (state *s)
"";
# endif
- Display *dpy = GDK_DISPLAY();
- const char *odpy = DisplayString (dpy);
+ const char *odpy = s->dpy ? DisplayString (s->dpy) : ":0.0";
char *ndpy = (char *) malloc(strlen(odpy) + 20);
strcpy (ndpy, "DISPLAY=");
strcat (ndpy, odpy);
@@ -4141,7 +3738,7 @@ hack_subproc_environment (Window preview_window_id, Bool debug_p)
{
/* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
necessary yet, but it will make programs work if we had invoked
- them with "-root" and not with "-window-id" -- which, of course,
+ them with "--root" and not with "--window-id" -- which, of course,
doesn't happen.
*/
char *nssw = (char *) malloc (40);
@@ -4159,6 +3756,7 @@ hack_subproc_environment (Window preview_window_id, Bool debug_p)
}
+
/* Called from a timer:
Launches the currently-chosen subprocess, if it's not already running.
If there's a different process running, kills it.
@@ -4168,7 +3766,7 @@ update_subproc_timer (gpointer data)
{
state *s = (state *) data;
if (! s->desired_preview_cmd)
- kill_preview_subproc (s, True);
+ kill_preview_subproc (s, TRUE);
else if (!s->running_preview_cmd ||
!!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
launch_preview_subproc (s);
@@ -4177,13 +3775,6 @@ update_subproc_timer (gpointer data)
return FALSE; /* do not re-execute timer */
}
-static int
-settings_timer (gpointer data)
-{
- settings_cb (0, 0);
- return FALSE;
-}
-
/* Call this when you think you might want a preview process running.
It will set a timer that will actually launch that program a second
@@ -4207,8 +3798,8 @@ schedule_preview (state *s, const char *cmd)
s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
if (s->subproc_timer_id)
- gtk_timeout_remove (s->subproc_timer_id);
- s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
+ g_source_remove (s->subproc_timer_id);
+ s->subproc_timer_id = g_timeout_add (delay, update_subproc_timer, s);
}
@@ -4219,12 +3810,12 @@ static int
check_subproc_timer (gpointer data)
{
state *s = (state *) data;
- Bool again_p = True;
+ Bool again_p = TRUE;
if (s->running_preview_error_p || /* already dead */
s->running_preview_pid <= 0)
{
- again_p = False;
+ again_p = FALSE;
}
else
{
@@ -4232,9 +3823,9 @@ check_subproc_timer (gpointer data)
reap_zombies (s);
status = kill (s->running_preview_pid, 0);
if (status < 0 && errno == ESRCH)
- s->running_preview_error_p = True;
+ s->running_preview_error_p = TRUE;
- if (s->debug_p)
+ if (s->debug_p && s->running_preview_error_p)
{
char *ss = subproc_pretty_name (s);
fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
@@ -4246,7 +3837,7 @@ check_subproc_timer (gpointer data)
if (s->running_preview_error_p)
{
clear_preview_window (s);
- again_p = False;
+ again_p = FALSE;
}
}
@@ -4254,7 +3845,7 @@ check_subproc_timer (gpointer data)
might be satisfied. */
if (--s->subproc_check_countdown <= 0)
- again_p = False;
+ again_p = FALSE;
if (again_p)
return TRUE; /* re-execute timer */
@@ -4272,7 +3863,7 @@ check_subproc_timer (gpointer data)
check whether the program is still running. The assumption here
is that if the process didn't stay up for more than a couple of
seconds, then either the program doesn't exist, or it doesn't
- take a -window-id argument.
+ take a --window-id argument.
*/
static void
schedule_preview_check (state *s)
@@ -4284,27 +3875,27 @@ schedule_preview_check (state *s)
fprintf (stderr, "%s: scheduling check\n", blurb());
if (s->subproc_check_timer_id)
- gtk_timeout_remove (s->subproc_check_timer_id);
+ g_source_remove (s->subproc_check_timer_id);
s->subproc_check_timer_id =
- gtk_timeout_add (1000 / ticks,
- check_subproc_timer, (gpointer) s);
+ g_timeout_add (1000 / ticks,
+ check_subproc_timer, (gpointer) s);
s->subproc_check_countdown = ticks * seconds;
}
static Bool
-screen_blanked_p (void)
+screen_blanked_p (state *s)
{
Atom type;
int format;
unsigned long nitems, bytesafter;
unsigned char *dataP = 0;
- Display *dpy = GDK_DISPLAY();
- Bool blanked_p = False;
+ Bool blanked_p = FALSE;
- if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
+ if (!s->dpy) return FALSE;
+ if (XGetWindowProperty (s->dpy, RootWindow (s->dpy, 0), /* always screen 0 */
XA_SCREENSAVER_STATUS,
- 0, 3, False, XA_INTEGER,
+ 0, 3, FALSE, XA_INTEGER,
&type, &format, &nitems, &bytesafter,
&dataP)
== Success
@@ -4329,63 +3920,45 @@ static int
check_blanked_timer (gpointer data)
{
state *s = (state *) data;
- Bool blanked_p = screen_blanked_p ();
+ Bool blanked_p = screen_blanked_p (s);
if (blanked_p && s->running_preview_pid)
{
if (s->debug_p)
fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
- kill_preview_subproc (s, True);
+ kill_preview_subproc (s, TRUE);
}
- return True; /* re-execute timer */
+ return TRUE; /* re-execute timer */
}
-/* How many screens are there (including Xinerama.)
- */
-static int
-screen_count (Display *dpy)
+/* Is there more than one active monitor? */
+static Bool
+multi_screen_p (Display *dpy)
{
- int nscreens = ScreenCount(dpy);
-# ifdef HAVE_XINERAMA
- if (nscreens <= 1)
+ monitor **monitors = dpy ? scan_monitors (dpy) : NULL;
+ Bool ret = FALSE;
+ if (monitors)
{
- int event_number, error_number;
- if (XineramaQueryExtension (dpy, &event_number, &error_number) &&
- XineramaIsActive (dpy))
+ int count = 0;
+ int good_count = 0;
+ while (monitors[count])
{
- XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens);
- if (xsi) XFree (xsi);
+ if (monitors[count]->sanity == S_SANE)
+ good_count++;
+ count++;
}
+ free_monitors (monitors);
+ ret = (good_count > 1);
}
-# endif /* HAVE_XINERAMA */
-
- return nscreens;
-}
-
-
-/* Setting window manager icon
- */
-
-static void
-init_icon (GdkWindow *window)
-{
- GdkBitmap *mask = 0;
- GdkPixmap *pixmap =
- gdk_pixmap_create_from_xpm_d (window, &mask, 0,
- (gchar **) logo_50_xpm);
- if (pixmap)
- gdk_window_set_icon (window, 0, pixmap, mask);
+ return ret;
}
-
-/* The main demo-mode command loop.
- */
#if 0
static Bool
-mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
- XrmRepresentation *type, XrmValue *value, XPointer closure)
+xrm_mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
+ XrmRepresentation *type, XrmValue *value, XPointer closure)
{
int i;
for (i = 0; quarks[i]; i++)
@@ -4401,98 +3974,146 @@ mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
fprintf (stderr, ": %s\n", (char *) value->addr);
- return False;
+ return FALSE;
+}
+#endif /* 0 */
+
+
+static int
+ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
+{
+ return 0;
}
-#endif
static Window
-gnome_screensaver_window (Screen *screen)
+gnome_screensaver_window (Display *dpy, char **name_ret)
{
- Display *dpy = DisplayOfScreen (screen);
- Window root = RootWindowOfScreen (screen);
- Window parent, *kids;
- unsigned int nkids;
+ int nscreens;
+ int i, screen;
Window gnome_window = 0;
- int i;
+ XErrorHandler old_handler;
- if (! XQueryTree (dpy, root, &root, &parent, &kids, &nkids))
- abort ();
- for (i = 0; i < nkids; i++)
+ if (!dpy) return 0;
+ XSync (dpy, FALSE);
+ old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+
+ nscreens = ScreenCount (dpy);
+ for (screen = 0; screen < nscreens; screen++)
{
- Atom type;
- int format;
- unsigned long nitems, bytesafter;
- unsigned char *name;
- if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128,
- False, XA_STRING, &type, &format, &nitems,
- &bytesafter, &name)
- == Success
- && type != None
- && (!strcmp ((char *) name, "gnome-screensaver") ||
- !strcmp ((char *) name, "mate-screensaver") ||
- !strcmp ((char *) name, "cinnamon-screensaver")))
- {
- gnome_window = kids[i];
- break;
- }
+ Window root = RootWindow (dpy, screen);
+ Window parent, *kids;
+ unsigned int nkids;
+
+ if (! XQueryTree (dpy, root, &root, &parent, &kids, &nkids))
+ abort ();
+ if (name_ret)
+ *name_ret = 0;
+ for (i = 0; i < nkids; i++)
+ {
+ Atom type;
+ int format;
+ unsigned long nitems, bytesafter;
+ unsigned char *name;
+ if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128,
+ FALSE, XA_STRING, &type, &format, &nitems,
+ &bytesafter, &name)
+ == Success
+ && type != None
+ && (!strcmp ((char *) name, "gnome-screensaver") ||
+ !strcmp ((char *) name, "mate-screensaver") ||
+ !strcmp ((char *) name, "cinnamon-screensaver") ||
+ !strcmp ((char *) name, "xfce4-screensaver") ||
+ !strcmp ((char *) name, "light-locker")))
+ {
+ gnome_window = kids[i];
+ if (name_ret)
+ *name_ret = strdup ((char *) name);
+ break;
+ }
+ }
+ if (kids) XFree ((char *) kids);
+ if (gnome_window)
+ break;
}
- if (kids) XFree ((char *) kids);
+ XSync (dpy, FALSE);
+ XSetErrorHandler (old_handler);
return gnome_window;
}
+
static Bool
-gnome_screensaver_active_p (void)
+gnome_screensaver_active_p (state *s, char **name_ret)
{
- Display *dpy = GDK_DISPLAY();
- Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
- return (w ? True : False);
+ Window w = gnome_screensaver_window (s->dpy, name_ret);
+ return (w ? TRUE : FALSE);
}
+
static void
-kill_gnome_screensaver (void)
+kill_gnome_screensaver (state *s)
{
- Display *dpy = GDK_DISPLAY();
- Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
- if (w) XKillClient (dpy, (XID) w);
+ Window w = gnome_screensaver_window (s->dpy, NULL);
+ if (w) XKillClient (s->dpy, (XID) w);
}
+
static Bool
kde_screensaver_active_p (void)
{
+ /* Apparently this worked in KDE 3, but not 4 or 5.
+ Maybe parsing the output of this would work in KDE 5:
+ kreadconfig5 --file kscreenlockerrc --group Daemon --key Autolock
+ but there's probably no way to kill the KDE saver.
+ Fuck it. */
FILE *p = popen ("dcop kdesktop KScreensaverIface isEnabled 2>/dev/null",
"r");
char buf[255];
- if (!p) return False;
- if (!fgets (buf, sizeof(buf)-1, p)) return False;
+ int i = 0;
+ if (!p) return FALSE;
+
+ *buf = 0;
+ do {
+ errno = 0;
+ if (! fgets (buf, sizeof(buf)-1, p))
+ *buf = 0;
+ } while (errno == EINTR && /* fgets might fail due to SIGCHLD. */
+ i++ < 1000); /* And just in case. */
+
pclose (p);
if (!strcmp (buf, "true\n"))
- return True;
+ return TRUE;
else
- return False;
+ return FALSE;
}
+
static void
-kill_kde_screensaver (void)
+kill_kde_screensaver (state *s)
{
- /* Use empty body to kill warning from gcc -Wall with
- "warning: ignoring return value of 'system',
- declared with attribute warn_unused_result"
- */
- if (system ("dcop kdesktop KScreensaverIface enable false")) {}
+ int ac = 0;
+ char *av[10];
+ av[ac++] = "dcop";
+ av[ac++] = "kdesktop";
+ av[ac++] = "KScreensaverIface";
+ av[ac++] = "enable";
+ av[ac++] = "false";
+ av[ac] = 0;
+ fork_and_exec (s, ac, av);
}
-static void
-the_network_is_not_the_computer (state *s)
+static int
+the_network_is_not_the_computer (gpointer data)
{
- Display *dpy = GDK_DISPLAY();
+ state *s = (state *) data;
char *rversion = 0, *ruser = 0, *rhost = 0;
char *luser, *lhost;
char *msg = 0;
+ char *oname = 0;
struct passwd *p = getpwuid (getuid ());
- const char *d = DisplayString (dpy);
+ const char *d = s->dpy ? DisplayString (s->dpy) : ":0.0";
# if defined(HAVE_UNAME)
struct utsname uts;
@@ -4509,7 +4130,8 @@ the_network_is_not_the_computer (state *s)
else
luser = "???";
- server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
+ if (s->dpy)
+ server_xscreensaver_version (s->dpy, &rversion, &ruser, &rhost);
/* Make a buffer that's big enough for a number of copies of all the
strings, plus some. */
@@ -4522,22 +4144,22 @@ the_network_is_not_the_computer (state *s)
1024));
*msg = 0;
- if (!rversion || !*rversion)
+ if ((!rversion || !*rversion) && !s->debug_p)
{
+# ifndef __APPLE__
sprintf (msg,
- _("Warning:\n\n"
- "The XScreenSaver daemon doesn't seem to be running\n"
- "on display \"%s\". Launch it now?"),
+ _("The XScreenSaver daemon doesn't seem to be running\n"
+ "on display \"%.25s\". Launch it now?"),
d);
+# endif
}
else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
{
/* Warn that the two processes are running as different users.
*/
sprintf(msg,
- _("Warning:\n\n"
- "%s is running as user \"%s\" on host \"%s\".\n"
- "But the xscreensaver managing display \"%s\"\n"
+ _("%s is running as user \"%s\" on host \"%s\".\n"
+ "But the xscreensaver managing display \"%.25s\"\n"
"is running as user \"%s\" on host \"%s\".\n"
"\n"
"Since they are different users, they won't be reading/writing\n"
@@ -4560,8 +4182,7 @@ the_network_is_not_the_computer (state *s)
/* Warn that the two processes are running on different hosts.
*/
sprintf (msg,
- _("Warning:\n\n"
- "%s is running as user \"%s\" on host \"%s\".\n"
+ _("%s is running as user \"%s\" on host \"%s\".\n"
"But the xscreensaver managing display \"%s\"\n"
"is running as user \"%s\" on host \"%s\".\n"
"\n"
@@ -4577,13 +4198,12 @@ the_network_is_not_the_computer (state *s)
progname,
lhost, luser);
}
- else if (!!strcmp (rversion, s->short_version))
+ else if (rversion && *rversion && !!strcmp (rversion, s->short_version))
{
/* Warn that the version numbers don't match.
*/
sprintf (msg,
- _("Warning:\n\n"
- "This is %s version %s.\n"
+ _("This is %s version %s.\n"
"But the xscreensaver managing display \"%s\"\n"
"is version %s. This could cause problems.\n"
"\n"
@@ -4593,9 +4213,10 @@ the_network_is_not_the_computer (state *s)
rversion);
}
+ validate_image_directory_quick (s);
if (*msg)
- warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
+ warning_dialog_1 (s->window, _("Warning"), msg, D_LAUNCH);
if (rversion) free (rversion);
if (ruser) free (ruser);
@@ -4608,23 +4229,42 @@ the_network_is_not_the_computer (state *s)
running" dialog so that these are on top. Good enough.
*/
- if (gnome_screensaver_active_p ())
- warning_dialog (s->toplevel_widget,
- _("Warning:\n\n"
- "The GNOME screensaver daemon appears to be running.\n"
- "It must be stopped for XScreenSaver to work properly.\n"
- "\n"
- "Stop the GNOME screen saver daemon now?\n"),
- D_GNOME, 1);
+ if (gnome_screensaver_active_p (s, &oname))
+ {
+ char msg [1024];
+ sprintf (msg,
+ _("The GNOME screen saver daemon (%s) appears to be running.\n"
+ "It must be stopped for XScreenSaver to work properly.\n"
+ "\n"
+ "Stop the \"%s\" daemon now?\n"),
+ oname, oname);
+ warning_dialog_1 (s->window, _("Warning"), msg, D_GNOME);
+ }
- if (kde_screensaver_active_p ())
- warning_dialog (s->toplevel_widget,
- _("Warning:\n\n"
- "The KDE screen saver daemon appears to be running.\n"
+ if (kde_screensaver_active_p())
+ warning_dialog_1 (s->window, _("Warning"),
+ _("The KDE screen saver daemon appears to be running.\n"
"It must be stopped for XScreenSaver to work properly.\n"
"\n"
"Stop the KDE screen saver daemon now?\n"),
- D_KDE, 1);
+ D_KDE);
+
+ if (! s->gl_visual)
+ warning_dialog (s->window, _("Error"),
+ _("No GL visuals: the xscreensaver-gl* packages are required."));
+
+ if (s->wayland_p)
+ warning_dialog (s->window, _("Warning"),
+ _("You are running Wayland rather than the X Window System.\n"
+ "\n"
+ "Under Wayland, idle-detection fails when non-X11 programs\n"
+ "are selected, meaning the screen may blank prematurely.\n"
+ "Also, locking is impossible.\n"
+ "\n"
+ "See the XScreenSaver manual for instructions on\n"
+ "configuring your system to use X11 instead of Wayland.\n"));
+
+ return FALSE; /* Only run timer once */
}
@@ -4632,739 +4272,1155 @@ the_network_is_not_the_computer (state *s)
of the program that generated them.
*/
static int
-demo_ehandler (Display *dpy, XErrorEvent *error)
+x_error (Display *dpy, XErrorEvent *error)
{
- state *s = global_state_kludge; /* I hate C so much... */
- fprintf (stderr, "\nX error in %s:\n", blurb());
+ fprintf (stderr, "\n%s: X error:\n", blurb());
XmuPrintDefaultErrorMessage (dpy, error, stderr);
- kill_preview_subproc (s, False);
- exit (-1);
+ /* No way to get 'state' in here... */
+ /* kill_preview_subproc (s, FALSE); */
+ exit (-1); /* Likewise, no way to call g_application_quit(). */
return 0;
}
-/* We use this error handler so that Gtk/Gdk errors are preceeded by the name
- of the program that generated them; and also that we can ignore one
- particular bogus error message that Gdk madly spews.
- */
static void
-g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
- const gchar *message, gpointer user_data)
-{
- /* Ignore the message "Got event for unknown window: 0x...".
- Apparently some events are coming in for the xscreensaver window
- (presumably reply events related to the ClientMessage) and Gdk
- feels the need to complain about them. So, just suppress any
- messages that look like that one.
- */
- if (strstr (message, "unknown window"))
- return;
+g_logger (const gchar *domain, GLogLevelFlags log_level,
+ const gchar *message, gpointer data)
+{
+ if (log_level & G_LOG_LEVEL_DEBUG) return;
+ if (log_level & G_LOG_LEVEL_INFO) return;
+ fprintf (stderr, "%s: %s: %s\n", blurb(), domain, message);
+ if (log_level & G_LOG_LEVEL_CRITICAL) exit (-1);
+}
- fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
- (log_domain ? log_domain : progclass),
- (log_level == G_LOG_LEVEL_ERROR ? "error" :
- log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
- log_level == G_LOG_LEVEL_WARNING ? "warning" :
- log_level == G_LOG_LEVEL_MESSAGE ? "message" :
- log_level == G_LOG_LEVEL_INFO ? "info" :
- log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
- message,
- ((!*message || message[strlen(message)-1] != '\n')
- ? "\n" : ""));
+/* Why are there two of these hooks and why does this one suck so hard?? */
+static GLogWriterOutput
+g_other_logger (GLogLevelFlags log_level, const GLogField *fields,
+ gsize n_fields, gpointer data)
+{
+ int i;
+ GLogWriterOutput ret = G_LOG_WRITER_UNHANDLED;
+ if (log_level & G_LOG_LEVEL_DEBUG) return ret;
+ if (log_level & G_LOG_LEVEL_INFO) return ret;
+ for (i = 0; i < n_fields; i++)
+ {
+ const GLogField *field = &fields[i];
+ if (strcmp (field->key, "MESSAGE")) continue;
+ fprintf (stderr, "%s: %s\n", blurb(), (char *) field->value);
+ ret = G_LOG_WRITER_HANDLED;
+ }
+ if (log_level & G_LOG_LEVEL_CRITICAL) exit (-1);
+ return ret;
}
-STFU
-static char *defaults[] = {
-#include "XScreenSaver_ad.h"
- 0
-};
+/****************************************************************************
-#if 0
-#ifdef HAVE_CRAPPLET
-static struct poptOption crapplet_options[] = {
- {NULL, '\0', 0, NULL, 0}
-};
-#endif /* HAVE_CRAPPLET */
-#endif /* 0 */
+ XScreenSaverDialog callbacks, referenced by prefs.ui.
-const char *usage = "[--display dpy] [--prefs | --settings]"
-# ifdef HAVE_CRAPPLET
- " [--crapplet]"
-# endif
- "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
+ ****************************************************************************/
-static void
-map_popup_window_cb (GtkWidget *w, gpointer user_data)
+/* The "Documentation" button on the Settings dialog */
+G_MODULE_EXPORT void
+manual_cb (GtkButton *button, gpointer user_data)
{
- state *s = (state *) user_data;
- Boolean oi = s->initializing_p;
-#ifndef HAVE_GTK2
- GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
-#endif
- s->initializing_p = True;
-#ifndef HAVE_GTK2
- eschew_gtk_lossage (label);
-#endif
- s->initializing_p = oi;
-}
+ XScreenSaverDialog *dialog = XSCREENSAVER_DIALOG (user_data);
+ XScreenSaverWindow *win = dialog->main;
+ state *s = &win->state;
+ saver_preferences *p = &s->prefs;
+ GtkWidget *list_widget = win->list;
+ int list_elt = selected_list_element (s);
+ int hack_number;
+ char *name, *name2, *cmd, *str;
+ char *oname = 0;
+ if (s->debug_p) fprintf (stderr, "%s: documentation button\n", blurb());
+ if (list_elt < 0) return;
+ hack_number = s->list_elt_to_hack_number[list_elt];
+ flush_dialog_changes_and_save (s);
+ ensure_selected_item_visible (s, list_widget);
-#if 0
-static void
-print_widget_tree (GtkWidget *w, int depth)
-{
- int i;
- for (i = 0; i < depth; i++)
- fprintf (stderr, " ");
- fprintf (stderr, "%s\n", gtk_widget_get_name (w));
+ name = strdup (p->screenhacks[hack_number]->command);
+ name2 = name;
+ oname = name;
+ while (isspace (*name2)) name2++;
+ str = name2;
+ while (*str && !isspace (*str)) str++;
+ *str = 0;
+ str = strrchr (name2, '/');
+ if (str) name2 = str+1;
- if (GTK_IS_LIST (w))
+ cmd = get_string_resource (s->dpy, "manualCommand", "ManualCommand");
+ if (cmd)
{
- for (i = 0; i < depth+1; i++)
- fprintf (stderr, " ");
- fprintf (stderr, "...list kids...\n");
+ int ac = 0;
+ char *av[10];
+ char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
+ sprintf (cmd2, cmd, name2, name2, name2, name2);
+ av[ac++] = "/bin/sh";
+ av[ac++] = "-c";
+ av[ac++] = cmd2;
+ av[ac] = 0;
+ fork_and_exec (s, ac, av);
+ free (cmd2);
}
- else if (GTK_IS_CONTAINER (w))
+ else
{
- GList *kids = gtk_container_children (GTK_CONTAINER (w));
- while (kids)
- {
- print_widget_tree (GTK_WIDGET (kids->data), depth+1);
- kids = kids->next;
- }
+ warning_dialog (s->window, _("Error"),
+ _("no `manualCommand' resource set."));
}
+
+ free (oname);
}
-#endif /* 0 */
-static int
-delayed_scroll_kludge (gpointer data)
+
+static void
+settings_sync_cmd_text (state *s)
{
- state *s = (state *) data;
- GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
- ensure_selected_item_visible (w);
+ XScreenSaverDialog *dialog = XSCREENSAVER_DIALOG (s->dialog);
+ GtkWidget *cmd = GTK_WIDGET (dialog->cmd_text);
+ char *cmd_line;
+ if (! s->cdata) return;
+ cmd_line = get_configurator_command_line (s->cdata, FALSE);
+ gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
+ free (cmd_line);
+}
- /* Oh, this is just fucking lovely, too. */
- w = GTK_WIDGET (name_to_widget (s, "preview"));
- gtk_widget_hide (w);
- gtk_widget_show (w);
- return FALSE; /* do not re-execute timer */
+/* The "Advanced" button on the settings dialog. */
+G_MODULE_EXPORT void
+settings_adv_cb (GtkButton *button, gpointer user_data)
+{
+ XScreenSaverDialog *dialog = XSCREENSAVER_DIALOG (user_data);
+ XScreenSaverWindow *win = dialog->main;
+ state *s = &win->state;
+ GtkNotebook *notebook = GTK_NOTEBOOK (dialog->opt_notebook);
+ if (s->debug_p) fprintf (stderr, "%s: settings advanced button\n", blurb());
+ settings_sync_cmd_text (s);
+ gtk_notebook_set_current_page (notebook, 1);
}
-#ifdef HAVE_GTK2
-static GtkWidget *
-create_xscreensaver_demo (void)
+/* The "Standard" button on the settings dialog. */
+G_MODULE_EXPORT void
+settings_std_cb (GtkButton *button, gpointer user_data)
{
- GtkWidget *nb;
+ XScreenSaverDialog *dialog = XSCREENSAVER_DIALOG (user_data);
+ XScreenSaverWindow *win = dialog->main;
+ state *s = &win->state;
+ GtkNotebook *notebook = GTK_NOTEBOOK (dialog->opt_notebook);
+ if (s->debug_p) fprintf (stderr, "%s: settings standard button\n", blurb());
+ settings_sync_cmd_text (s);
+ gtk_notebook_set_current_page (notebook, 0);
+}
- nb = name_to_widget (global_state_kludge, "preview_notebook");
- gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
- return name_to_widget (global_state_kludge, "xscreensaver_demo");
+/* The "Reset to Defaults" button on the settings dialog. */
+G_MODULE_EXPORT void
+settings_reset_cb (GtkButton *button, gpointer user_data)
+{
+ XScreenSaverDialog *dialog = XSCREENSAVER_DIALOG (user_data);
+ XScreenSaverWindow *win = dialog->main;
+ GtkWidget *cmd = GTK_WIDGET (dialog->cmd_text);
+ state *s = &win->state;
+ char *cmd_line;
+ if (s->debug_p) fprintf (stderr, "%s: settings reset button\n", blurb());
+ if (! s->cdata) return;
+ cmd_line = get_configurator_command_line (s->cdata, TRUE);
+ gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
+ free (cmd_line);
+ populate_popup_window (s);
}
-static GtkWidget *
-create_xscreensaver_settings_dialog (void)
+
+/* Called when "Advanced/Standard" buttons change the displayed page. */
+G_MODULE_EXPORT void
+settings_switch_page_cb (GtkNotebook *notebook, GtkWidget *page,
+ gint page_num, gpointer user_data)
{
- GtkWidget *w, *box;
+ XScreenSaverDialog *dialog = XSCREENSAVER_DIALOG (user_data);
+ XScreenSaverWindow *win = dialog->main;
+ state *s = &win->state;
+ GtkWidget *adv = dialog->adv_button;
+ GtkWidget *std = dialog->std_button;
+ if (s->debug_p) fprintf (stderr, "%s: settings page changed\n", blurb());
- box = name_to_widget (global_state_kludge, "dialog_action_area");
+ if (page_num == 0)
+ {
+ gtk_widget_show (adv);
+ gtk_widget_hide (std);
+ }
+ else if (page_num == 1)
+ {
+ gtk_widget_hide (adv);
+ gtk_widget_show (std);
+ }
+ else
+ abort();
- w = name_to_widget (global_state_kludge, "adv_button");
- gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
+ /* Nobody uses the "Advanced" tab. Let's just hide it.
+ (The tab still needs to be there, since the 'cmd_text' widget is
+ what gets stored into the .xscreensaver file.) */
+ gtk_widget_hide (adv);
+ gtk_widget_hide (std);
+}
- w = name_to_widget (global_state_kludge, "std_button");
- gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
- return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
+/* The "Cancel" button on the Settings dialog. */
+G_MODULE_EXPORT void
+settings_cancel_cb (GtkWidget *button, gpointer user_data)
+{
+ XScreenSaverDialog *dialog = XSCREENSAVER_DIALOG (user_data);
+ XScreenSaverWindow *win = dialog->main;
+ state *s = &win->state;
+ if (s->debug_p) fprintf (stderr, "%s: settings cancel button\n", blurb());
+ gtk_widget_hide (GTK_WIDGET (dialog));
+ gtk_widget_unrealize (GTK_WIDGET (dialog));
+
+ /* Restart the hack running in the Preview window with the reset options. */
+ schedule_preview (s, dialog->unedited_cmdline);
}
-#endif /* HAVE_GTK2 */
-int
-main (int argc, char **argv)
+/* The "Ok" button on the Settings dialog. */
+G_MODULE_EXPORT void
+settings_ok_cb (GtkWidget *button, gpointer user_data)
{
- XtAppContext app;
- state S, *s;
- saver_preferences *p;
- Bool prefs_p = False;
- Bool settings_p = False;
- int i;
- Display *dpy;
- Widget toplevel_shell;
- char *real_progname = argv[0];
- char *window_title;
- char *geom = 0;
- Bool crapplet_p = False;
- char *str;
+ XScreenSaverDialog *dialog = XSCREENSAVER_DIALOG (user_data);
+ XScreenSaverWindow *win = dialog->main;
+ state *s = &win->state;
+ GtkNotebook *notebook = GTK_NOTEBOOK (dialog->opt_notebook);
+ int page = gtk_notebook_get_current_page (notebook);
+ if (s->debug_p) fprintf (stderr, "%s: settings ok button\n", blurb());
-#ifdef ENABLE_NLS
- bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
- textdomain (GETTEXT_PACKAGE);
+ if (page == 0)
+ /* Regenerate the command-line from the widget contents before saving.
+ But don't do this if we're looking at the command-line page already,
+ or we will blow away what they typed... */
+ settings_sync_cmd_text (s);
-# ifdef HAVE_GTK2
- bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
-# else /* !HAVE_GTK2 */
- if (!setlocale (LC_ALL, ""))
- fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
-# endif /* !HAVE_GTK2 */
+ flush_popup_changes_and_save (s);
+ gtk_widget_hide (GTK_WIDGET (dialog));
+ gtk_widget_unrealize (GTK_WIDGET (dialog));
+}
-#endif /* ENABLE_NLS */
- str = strrchr (real_progname, '/');
- if (str) real_progname = str+1;
+/* Called when any widget value is changed in the Settings dialog. */
+static void
+dialog_change_cb (GtkWidget *widget, gpointer user_data)
+{
+ state *s = (state *) user_data;
+ XScreenSaverDialog *dialog = XSCREENSAVER_DIALOG (s->dialog);
+ char *cur_cmd, *def_cmd;
+ const char *prev_cmd;
+ if (!dialog || !s->cdata) return;
+ settings_sync_cmd_text (s);
+ cur_cmd = get_configurator_command_line (s->cdata, FALSE);
+ def_cmd = get_configurator_command_line (s->cdata, TRUE);
+ prev_cmd = dialog->unedited_cmdline;
- s = &S;
- memset (s, 0, sizeof(*s));
- s->initializing_p = True;
- p = &s->prefs;
+ /* "Reset to Defaults" button enabled only if current cmd is not default. */
+ gtk_widget_set_sensitive (dialog->reset_button,
+ !!strcmp (cur_cmd, def_cmd));
- global_state_kludge = s; /* I hate C so much... */
+ /* "Save" button enabled only if current cmd is edited. */
+ gtk_widget_set_sensitive (dialog->ok_button,
+ !!strcmp (cur_cmd, prev_cmd));
- progname = real_progname;
+ /* Restart the hack running in the Preview window with the prevailing,
+ un-saved set of options, for a realtime preview of what they do. */
+ schedule_preview (s, cur_cmd);
- s->short_version = XSCREENSAVER_VERSION;
+ free (cur_cmd);
+ free (def_cmd);
+}
+
+
+/* Fill in the contents of the "Settings" dialog for the current hack.
+ It may or may not currently be visible.
+ */
+static void
+populate_popup_window (state *s)
+{
+ saver_preferences *p = &s->prefs;
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
+ XScreenSaverDialog *dialog = XSCREENSAVER_DIALOG (s->dialog);
+
+ GtkLabel *doc = dialog ? GTK_LABEL (dialog->doc) : 0;
+ char *doc_string = 0;
+
+ if (s->cdata)
+ {
+ free_conf_data (s->cdata);
+ s->cdata = 0;
+ }
- /* Register our error message logger for every ``log domain'' known.
- There's no way to do this globally, so I grepped the Gtk/Gdk sources
- for all of the domains that seem to be in use.
- */
{
- const char * const domains[] = { 0,
- "Gtk", "Gdk", "GLib", "GModule",
- "GThread", "Gnome", "GnomeUI" };
- for (i = 0; i < countof(domains); i++)
- g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
+ int list_elt = selected_list_element (s);
+ int hack_number = (list_elt >= 0 && list_elt < s->list_count
+ ? s->list_elt_to_hack_number[list_elt]
+ : -1);
+ screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
+
+ if (p->mode == BLANK_ONLY || p->mode == DONT_BLANK)
+ hack = 0;
+
+ if (hack && dialog)
+ {
+ GtkWidget *parent = dialog->settings_vbox;
+ GtkWidget *cmd = GTK_WIDGET (dialog->cmd_text);
+ const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
+ if (!cmd_line) abort();
+ s->cdata = load_configurator (cmd_line, dialog_change_cb, s,
+ s->debug_p);
+ dialog_change_cb (NULL, s);
+ if (s->cdata && s->cdata->widget)
+ gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
+ TRUE, TRUE, 0);
+
+ /* Make the pretty name on the tab boxes include the year and be bold.
+ */
+ if (s->cdata && s->cdata->year)
+ {
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
+ GtkFrame *frame1 = GTK_FRAME (win->preview_frame);
+ GtkFrame *frame2 = GTK_FRAME (dialog->opt_frame);
+ char *pretty_name = (hack->name
+ ? strdup (hack->name)
+ : make_hack_name (s->dpy, hack->command));
+ char *s2 = (char *) malloc (strlen (pretty_name) + 20);
+ sprintf (s2, "<b>%s (%d)</b>", pretty_name, s->cdata->year);
+ free (pretty_name);
+ pretty_name = s2;
+
+ gtk_frame_set_label (frame1, _(pretty_name));
+ gtk_frame_set_label (frame2, _(pretty_name));
+ gtk_label_set_use_markup ( /* Must be after set_label */
+ GTK_LABEL (gtk_frame_get_label_widget (frame1)), TRUE);
+ gtk_label_set_use_markup (
+ GTK_LABEL (gtk_frame_get_label_widget (frame2)), TRUE);
+ free (pretty_name);
+ }
+ }
}
-#ifdef DEFAULT_ICONDIR /* from -D on compile line */
-# ifndef HAVE_GTK2
+ doc_string = (s->cdata && s->cdata->description && *s->cdata->description
+ ? _(s->cdata->description)
+ : 0);
+ doc_string = hreffify (doc_string);
+ if (doc)
+ {
+ GtkWidget *w = dialog->dialog_vbox;
+ gtk_label_set_text (doc, (doc_string
+ ? doc_string
+ : _("No description available.")));
+ gtk_label_set_use_markup (doc, TRUE);
+
+ /* Shrink the dialog to the minimum viable size. */
+ gtk_window_resize (GTK_WINDOW (dialog), 1, 1);
+
+ gtk_widget_hide (w);
+ gtk_widget_unrealize (w);
+ gtk_widget_realize (w);
+ gtk_widget_show (w);
+ }
+
+ /* Also set the documentation on the main window, below the preview. */
{
- const char *dir = DEFAULT_ICONDIR;
- if (*dir) add_pixmap_directory (dir);
+ GtkLabel *doc2 = GTK_LABEL (win->short_preview_label);
+ GtkLabel *doc3 = GTK_LABEL (win->preview_author_label);
+ char *s2 = 0;
+ char *s3 = 0;
+
+ if (doc_string)
+ {
+ /* Keep only the first paragraph, and the last line.
+ Omit everything in between. */
+ const char *second_para = strstr (doc_string, "\n\n");
+ const char *last_line = strrchr (doc_string, '\n');
+ s2 = strdup (doc_string);
+ if (second_para)
+ s2[second_para - doc_string] = 0;
+ if (last_line)
+ s3 = strdup (last_line + 1);
+ }
+
+ gtk_label_set_text (doc2,
+ (s2
+ ? _(s2)
+ : (p->mode == BLANK_ONLY || p->mode == DONT_BLANK)
+ ? ""
+ : _("No description available.")));
+ gtk_label_set_text (doc3, (s3 ? _(s3) : ""));
+ if (s2) free (s2);
+ if (s3) free (s3);
}
-# endif /* !HAVE_GTK2 */
-#endif /* DEFAULT_ICONDIR */
- /* This is gross, but Gtk understands --display and not -display...
- */
- for (i = 1; i < argc; i++)
- if (argv[i][0] && argv[i][1] &&
- !strncmp(argv[i], "-display", strlen(argv[i])))
- argv[i] = "--display";
+ if (doc_string)
+ free (doc_string);
+}
- /* We need to parse this arg really early... Sigh. */
- for (i = 1; i < argc; i++)
+static void
+sensitize_demo_widgets (state *s, Bool sensitive_p)
+{
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
+ XScreenSaverDialog *dialog = XSCREENSAVER_DIALOG (s->dialog);
+ gtk_widget_set_sensitive (win->demo, sensitive_p);
+ gtk_widget_set_sensitive (win->settings, sensitive_p);
+ if (dialog)
{
- if (argv[i] &&
- (!strcmp(argv[i], "--crapplet") ||
- !strcmp(argv[i], "--capplet")))
- {
-# if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
- int j;
- crapplet_p = True;
- for (j = i; j < argc; j++) /* remove it from the list */
- argv[j] = argv[j+1];
- argc--;
-# else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
- fprintf (stderr, "%s: not compiled with --crapplet support\n",
- real_progname);
- fprintf (stderr, "%s: %s\n", real_progname, usage);
- exit (1);
-# endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
- }
- else if (argv[i] &&
- (!strcmp(argv[i], "--debug") ||
- !strcmp(argv[i], "-debug") ||
- !strcmp(argv[i], "-d")))
- {
- int j;
- s->debug_p = True;
- for (j = i; j < argc; j++) /* remove it from the list */
- argv[j] = argv[j+1];
- argc--;
- i--;
- }
- else if (argv[i] &&
- argc > i+1 &&
- *argv[i+1] &&
- (!strcmp(argv[i], "-geometry") ||
- !strcmp(argv[i], "-geom") ||
- !strcmp(argv[i], "-geo") ||
- !strcmp(argv[i], "-g")))
- {
- int j;
- geom = argv[i+1];
- for (j = i; j < argc; j++) /* remove them from the list */
- argv[j] = argv[j+2];
- argc -= 2;
- i -= 2;
- }
- else if (argv[i] &&
- argc > i+1 &&
- *argv[i+1] &&
- (!strcmp(argv[i], "--configdir")))
- {
- int j;
- struct stat st;
- hack_configuration_path = argv[i+1];
- for (j = i; j < argc; j++) /* remove them from the list */
- argv[j] = argv[j+2];
- argc -= 2;
- i -= 2;
-
- if (0 != stat (hack_configuration_path, &st))
- {
- char buf[255];
- sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
- perror (buf);
- exit (1);
- }
- else if (!S_ISDIR (st.st_mode))
- {
- fprintf (stderr, "%s: not a directory: %s\n",
- blurb(), hack_configuration_path);
- exit (1);
- }
- }
+ gtk_widget_set_sensitive (dialog->cmd_label, sensitive_p);
+ gtk_widget_set_sensitive (dialog->cmd_text, sensitive_p);
+ gtk_widget_set_sensitive (dialog->manual, sensitive_p);
+ gtk_widget_set_sensitive (dialog->visual, sensitive_p);
+ gtk_widget_set_sensitive (dialog->visual_combo, sensitive_p);
}
+}
- if (s->debug_p)
- fprintf (stderr, "%s: using config directory \"%s\"\n",
- progname, hack_configuration_path);
+/* Flush out any changes made in the popup dialog box (where changes
+ take place only when the OK button is clicked.)
+ */
+static Bool
+flush_popup_changes_and_save (state *s)
+{
+ XScreenSaverDialog *dialog = XSCREENSAVER_DIALOG (s->dialog);
+ Bool changed = FALSE;
+ saver_preferences *p = &s->prefs;
+ int list_elt = selected_list_element (s);
+
+ GtkEntry *cmd = GTK_ENTRY (dialog->cmd_text);
+ GtkWidget *vis = GTK_WIDGET (dialog->visual_combo);
+ GtkEntry *visent = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (vis)));
+
+ const char *visual = gtk_entry_get_text (visent);
+ const char *command = gtk_entry_get_text (cmd);
+
+ char c;
+ unsigned long id;
+
+ if (s->saving_p) return FALSE;
+ s->saving_p = TRUE;
+
+ if (list_elt < 0)
+ goto DONE;
+
+ if (maybe_reload_init_file (s) != 0)
+ {
+ changed = TRUE;
+ goto DONE;
+ }
- /* Let Gtk open the X connection, then initialize Xt to use that
- same connection. Doctor Frankenstein would be proud.
+ /* Sanity-check and canonicalize whatever the user typed into the combo box.
*/
-# ifdef HAVE_CRAPPLET
- if (crapplet_p)
+ if (!strcasecmp (visual, "")) visual = "";
+ else if (!strcasecmp (visual, "any")) visual = "";
+ else if (!strcasecmp (visual, "default")) visual = "Default";
+ else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
+ else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
+ else if (!strcasecmp (visual, "best")) visual = "Best";
+ else if (!strcasecmp (visual, "mono")) visual = "Mono";
+ else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
+ else if (!strcasecmp (visual, "gray")) visual = "Gray";
+ else if (!strcasecmp (visual, "grey")) visual = "Gray";
+ else if (!strcasecmp (visual, "color")) visual = "Color";
+ else if (!strcasecmp (visual, "gl")) visual = "GL";
+ else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
+ else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
+ else if (!strcasecmp (visual, "truecolor")) visual = "TRUEColor";
+ else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
+ else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
+ else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
+ else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
+ else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
+ else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
+ else visual = "";
+
+ changed = flush_changes (s, list_elt, -1, command, visual);
+ if (changed)
{
- GnomeClient *client;
- GnomeClientFlags flags = 0;
-
- int init_results = gnome_capplet_init ("screensaver-properties",
- s->short_version,
- argc, argv, NULL, 0, NULL);
- /* init_results is:
- 0 upon successful initialization;
- 1 if --init-session-settings was passed on the cmdline;
- 2 if --ignore was passed on the cmdline;
- -1 on error.
-
- So the 1 signifies just to init the settings, and quit, basically.
- (Meaning launch the xscreensaver daemon.)
- */
+ demo_write_init_file (s, p);
- if (init_results < 0)
- {
-# if 0
- g_error ("An initialization error occurred while "
- "starting xscreensaver-capplet.\n");
-# else /* !0 */
- fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
- real_progname, init_results);
- exit (1);
-# endif /* !0 */
- }
+ /* Do this to re-launch the hack if (and only if) the command line
+ has changed. */
+ populate_demo_window (s, selected_list_element (s));
+ }
- client = gnome_master_client ();
+ DONE:
+ s->saving_p = FALSE;
+ return changed;
+}
- if (client)
- flags = gnome_client_get_flags (client);
- if (flags & GNOME_CLIENT_IS_CONNECTED)
- {
- int token =
- gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
- gnome_client_get_id (client));
- if (token)
- {
- char *session_args[20];
- int i = 0;
- session_args[i++] = real_progname;
- session_args[i++] = "--capplet";
- session_args[i++] = "--init-session-settings";
- session_args[i] = 0;
- gnome_client_set_priority (client, 20);
- gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
- gnome_client_set_restart_command (client, i, session_args);
- }
- else
- {
- gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
- }
+/****************************************************************************
- gnome_client_flush (client);
- }
+ XScreenSaverWindow
- if (init_results == 1)
- {
- system ("xscreensaver -nosplash &");
- return 0;
- }
+ ****************************************************************************/
- }
+
+static void
+xscreensaver_window_destroy (GObject *object)
+{
+ /* Called by WM close box, but not by File / Quit */
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (object);
+ quit_menu_cb (NULL, win); /* Shouldn't return? */
+ G_OBJECT_CLASS (xscreensaver_window_parent_class)->dispose (object);
+}
+
+
+/* gtk_window_move() sets the origin of the window's WM decorations, but
+ GTK's "configure-event" returns the root-relative origin of the window
+ within the decorations, so the "configure-event" numbers are too large by
+ the size of the decorations (title bar and border). Without compensating
+ for this, the window would move down and slightly to the right every time
+ we saved and restored. GDK provides no way to find those numbers, so we
+ have to hack it out X11 style...
+ */
+static void
+wm_decoration_origin (GtkWindow *gtkw, int *x, int *y)
+{
+ Display *dpy = gdk_x11_get_default_xdisplay();
+ GdkWindow *gdkw = gtk_widget_get_window (GTK_WIDGET (gtkw));
+ Window xw = gdk_x11_window_get_xid (gdkw);
+
+ Window root, parent, *kids;
+ unsigned int nkids;
+
+ Atom type = None;
+ int format;
+ unsigned long nitems, bytesafter;
+ unsigned char *data;
+
+ static Atom swm_vroot = 0;
+ XWindowAttributes xgwa;
+
+ if (!dpy || !xw) return;
+ if (! XQueryTree (dpy, xw, &root, &parent, &kids, &nkids))
+ abort ();
+ if (kids) XFree ((char *) kids);
+
+ if (parent == root) /* No window above us at all */
+ return;
+
+ if (! swm_vroot)
+ swm_vroot = XInternAtom (dpy, "__SWM_VROOT", FALSE);
+
+ /* If parent is a virtual root, there is no intervening WM decoration. */
+ if (XGetWindowProperty (dpy, parent, swm_vroot,
+ 0, 0, FALSE, AnyPropertyType,
+ &type, &format, &nitems, &bytesafter,
+ (unsigned char **) &data)
+ == Success
+ && type != None)
+ return;
+
+ /* If we have a parent, it is the WM decoration, so use its origin. */
+ if (! XGetWindowAttributes (dpy, parent, &xgwa))
+ abort();
+ *x = xgwa.x;
+ *y = xgwa.y;
+}
+
+
+static void
+save_window_position (state *s, GtkWindow *win, int x, int y, Bool dialog_p)
+{
+ saver_preferences *p = &s->prefs;
+ int win_x, win_y, dialog_x, dialog_y;
+ char dummy;
+ char *old = p->settings_geom;
+ char str[100];
+
+ if (!s->dpy || s->wayland_p) return;
+ wm_decoration_origin (win, &x, &y);
+
+ if (!old || !*old ||
+ 4 != sscanf (old, " %d , %d %d , %d %c",
+ &win_x, &win_y, &dialog_x, &dialog_y, &dummy))
+ win_x = win_y = dialog_x = dialog_y = -1;
+
+ if (dialog_p)
+ dialog_x = x, dialog_y = y;
else
-# endif /* HAVE_CRAPPLET */
+ win_x = x, win_y = y;
+
+ sprintf (str, "%d,%d %d,%d", win_x, win_y, dialog_x, dialog_y);
+
+ if (old && !strcmp (old, str)) return; /* unchanged */
+
+ p->settings_geom = strdup (str);
+
+ if (s->debug_p)
+ fprintf (stderr, "%s: geom: %s => %s\n", blurb(),
+ (old ? old : "null"), str);
+
+ /* This writes the .xscreensaver file repeatedly as the window is dragged,
+ which is too much. We could defer it with a timer. But instead let's
+ just not save it upon resize, and only save the positions once the
+ file is written due to some other change.
+ */
+ /* demo_write_init_file (s, p); */
+ if (old) free (old);
+}
+
+
+static void
+restore_window_position (state *s, GtkWindow *window, Bool dialog_p)
+{
+ saver_preferences *p = &s->prefs;
+ int win_x, win_y, dialog_x, dialog_y, x, y;
+ char dummy;
+ char *old = p->settings_geom;
+
+ if (!old || !*old ||
+ 4 != sscanf (old, " %d , %d %d , %d %c",
+ &win_x, &win_y, &dialog_x, &dialog_y, &dummy))
+ win_x = win_y = dialog_x = dialog_y = -1;
+
+ if (dialog_p)
+ x = dialog_x, y = dialog_y;
+ else
+ x = win_x, y = win_y;
+
+ if (x <= 0 || y <= 0) return;
+
+ if (s->debug_p)
+ fprintf (stderr, "%s: restore origin: %d,%d\n", blurb(), x, y);
+ gtk_window_move (window, x, y);
+}
+
+
+/* When the window is moved, save the new origin in .xscreensaver. */
+static gboolean
+xscreensaver_window_resize_cb (GtkWindow *window, GdkEvent *event,
+ gpointer data)
+{
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (window);
+ state *s = &win->state;
+ save_window_position (s, GTK_WINDOW (win),
+ event->configure.x, event->configure.y, FALSE);
+ return FALSE;
+}
+
+
+static int
+delayed_scroll_kludge (gpointer data)
+{
+ state *s = (state *) data;
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (s->window);
+ GtkWidget *list_widget = win->list;
+ ensure_selected_item_visible (s, list_widget);
+ return FALSE; /* do not re-execute timer */
+}
+
+
+static void
+quit_action_cb (GSimpleAction *action, GVariant *parameter, gpointer user_data)
+{
+ quit_menu_cb (NULL, user_data);
+}
+
+
+static GActionEntry app_entries[] = {
+ { "quit", quit_action_cb, NULL, NULL, NULL },
+/*
+ { "undo", undo_action_cb, NULL, NULL, NULL },
+ { "redo", redo_action_cb, NULL, NULL, NULL },
+ { "cut", cut_action_cb, NULL, NULL, NULL },
+ { "copy", copy_action_cb, NULL, NULL, NULL },
+ { "paste", paste_action_cb, NULL, NULL, NULL },
+ { "delete", delete_action_cb, NULL, NULL, NULL },
+*/
+};
+
+/* #### I don't know how to make the accelerators show up on the menu items,
+ and I don't understand the difference between "callbacks" and "actions".
+ I see examples in other .ui files that do things like:
+
+ <ui>
+ <menubar name="menubar">
+ <menu action="file">
+ <menuitem name="activate_menu" action="activate_action"/>
+ or
+ <menu id="menubar">
+ <submenu>
+ <attribute name="label">File</attribute>
+ <section>
+ <item>
+ <attribute name="label">Activate</attribute>
+ <attribute name="action">app.activate</attribute>
+
+ but when I put variants of that in demo.ui, nothing shows up.
+
+ It would be nice to have an "Edit" menu with working text-editing commands
+ on it, for use with our various text fields. One would think that a GUI
+ toolkit would provide boilerplate for such things, but nooooo...
+ */
+const gchar *accels[][2] = {
+ { "app.quit", "<Ctrl>Q" },
+/*
+ { "app.undo", "<Ctrl>Z" },
+ { "app.redo", "<Ctrl>Y" },
+ { "app.cut", "<Ctrl>X" },
+ { "app.copy", "<Ctrl>C" },
+ { "app.paste", "<Ctrl>V" },
+*/
+};
+
+
+static void
+xscreensaver_window_realize (GtkWidget *self, gpointer user_data)
+{
+ XScreenSaverWindow *win = XSCREENSAVER_WINDOW (self);
+ state *s = &win->state;
+ saver_preferences *p = &s->prefs;
+
+ s->initializing_p = TRUE;
+ s->short_version = XSCREENSAVER_VERSION;
+ s->window = GTK_WINDOW (win);
+
+ s->dpy = gdk_x11_get_default_xdisplay();
+
+ /* Debian 11.4, Gtk 3.24.24, 2022: under Wayland, get_default_xdisplay is
+ returning uninitialized data! However, gdk_x11_window_get_xid prints a
+ warning and returns NULL. So let's try that, and as a fallback, also try
+ and sanity check the contents of the Display structure...
+ */
+ if (! gdk_x11_window_get_xid (gtk_widget_get_window (self)))
{
- gtk_init (&argc, &argv);
+ s->dpy = NULL;
+ s->wayland_p = TRUE;
}
+ if (s->dpy)
+ {
+ if (ProtocolVersion (s->dpy) != 11 ||
+ ProtocolRevision (s->dpy) != 0)
+ {
+ fprintf (stderr, "%s: uninitialized data in Display: "
+ "protocol version %d.%d!\n", blurb(),
+ ProtocolVersion(s->dpy), ProtocolRevision(s->dpy));
+ s->dpy = NULL;
+ s->wayland_p = TRUE;
+ }
+ }
- /* We must read exactly the same resources as xscreensaver.
- That means we must have both the same progclass *and* progname,
- at least as far as the resource database is concerned. So,
- put "xscreensaver" in argv[0] while initializing Xt.
+ /* If we don't have a display connection, then we are surely under Wayland
+ even if the environment variable is not set.
*/
- argv[0] = "xscreensaver";
- progname = argv[0];
+ if (!s->dpy &&
+ !getenv ("WAYLAND_DISPLAY") &&
+ !getenv ("WAYLAND_SOCKET"))
+ putenv ("WAYLAND_DISPLAY=probably");
+ if (getenv ("WAYLAND_DISPLAY") ||
+ getenv ("WAYLAND_SOCKET"))
+ s->wayland_p = TRUE;
- /* Teach Xt to use the Display that Gtk/Gdk have already opened.
- */
- XtToolkitInitialize ();
- app = XtCreateApplicationContext ();
- dpy = GDK_DISPLAY();
- XtAppSetFallbackResources (app, defaults);
- XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
- toplevel_shell = XtAppCreateShell (progname, progclass,
- applicationShellWidgetClass,
- dpy, 0, 0);
-
- dpy = XtDisplay (toplevel_shell);
- db = XtDatabase (dpy);
- XtGetApplicationNameAndClass (dpy, (char **) &progname, &progclass);
- XSetErrorHandler (demo_ehandler);
-
- /* Let's just ignore these. They seem to confuse Irix Gtk... */
- signal (SIGPIPE, SIG_IGN);
-
- /* After doing Xt-style command-line processing, complain about any
- unrecognized command-line arguments.
+ /* If GTK is running directly under Wayland, try to open an X11 connection
+ to XWayland anyway, so that get_string_resource and load_init_file work.
*/
- for (i = 1; i < argc; i++)
+ if (! s->dpy)
{
- char *str = argv[i];
- if (str[0] == '-' && str[1] == '-')
- str++;
- if (!strcmp (str, "-prefs"))
- prefs_p = True;
- else if (!strcmp (str, "-settings"))
- settings_p = True;
- else if (crapplet_p)
- /* There are lots of random args that we don't care about when we're
- started as a crapplet, so just ignore unknown args in that case. */
- ;
- else
- {
- fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
- argv[i]);
- fprintf (stderr, "%s: %s\n", real_progname, usage);
- exit (1);
- }
+ s->dpy = XOpenDisplay (NULL);
+ if (s->debug_p)
+ {
+ if (s->dpy)
+ fprintf (stderr, "%s: opened secondary XWayland connection\n",
+ blurb());
+ else
+ fprintf (stderr, "%s: failed to open XWayland connection\n",
+ blurb());
+ }
}
- /* Load the init file, which may end up consulting the X resource database
- and the site-wide app-defaults file. Note that at this point, it's
- important that `progname' be "xscreensaver", rather than whatever
- was in argv[0].
+ /* Teach Xt to use the Display that Gtk/Gdk have already opened.
*/
- p->db = db;
- s->nscreens = screen_count (dpy);
+ if (s->dpy)
+ {
+ XtAppContext app;
+ int argc = 0;
+ char *argv[2];
+ const char *oprogname = progname;
- init_xscreensaver_atoms (dpy);
- hack_environment (s); /* must be before initialize_sort_map() */
+ progname = "xscreensaver"; /* For X resources */
+ argv[argc++] = (char *) progname;
+ argv[argc] = 0;
- load_init_file (dpy, p);
- initialize_sort_map (s);
+ XtToolkitInitialize ();
+ app = XtCreateApplicationContext ();
+ XtAppSetFallbackResources (app, defaults);
+ XtDisplayInitialize (app, s->dpy, progname, progclass, 0, 0, &argc, argv);
- /* Now that Xt has been initialized, and the resources have been read,
- we can set our `progname' variable to something more in line with
- reality.
- */
- progname = real_progname;
+ if (s->debug_p)
+ {
+ XSync (s->dpy, False);
+ XSynchronize (s->dpy, True); /* Must be after XtDisplayInitialize */
+ }
+ /* Result discarded and leaked */
+ XtAppCreateShell (progname, progclass, applicationShellWidgetClass,
+ s->dpy, 0, 0);
+ p->db = XtDatabase (s->dpy);
+ XSetErrorHandler (x_error);
-#if 0
- /* Print out all the resources we read. */
- {
- XrmName name = { 0 };
- XrmClass class = { 0 };
- int count = 0;
- XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
- (POINTER) &count);
- }
-#endif
+ signal (SIGPIPE, SIG_IGN); /* Is this necessary? */
- init_xscreensaver_atoms (dpy);
+ progname = oprogname;
- /* Create the window and all its widgets.
- */
- s->base_widget = create_xscreensaver_demo ();
- s->popup_widget = create_xscreensaver_settings_dialog ();
- s->toplevel_widget = s->base_widget;
+# if 0
+ /* Print out all the Xrm resources we read. */
+ {
+ XrmName name = { 0 };
+ XrmClass class = { 0 };
+ int count = 0;
+ XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, xrm_mapper,
+ (void *) &count);
+ }
+# endif
+ }
+ s->multi_screen_p = multi_screen_p (s->dpy);
- /* Set the main window's title. */
+ /* Let's see if the server supports DPMS.
+ */
+ s->dpms_supported_p = FALSE;
+# ifdef HAVE_DPMS_EXTENSION
{
- char *base_title = _("Screensaver Preferences");
- char *v = (char *) strdup(strchr(screensaver_id, ' '));
- char *s1, *s2, *s3, *s4;
- s1 = (char *) strchr(v, ' '); s1++;
- s2 = (char *) strchr(s1, ' ');
- s3 = (char *) strchr(v, '('); s3++;
- s4 = (char *) strchr(s3, ')');
- *s2 = 0;
- *s4 = 0;
-
- window_title = (char *) malloc (strlen (base_title) +
- strlen (progclass) +
- strlen (s1) + strlen (s3) +
- 100);
- sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
- gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
- gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
- free (v);
+ int op = 0, event = 0, error = 0;
+ if (s->dpy && XQueryExtension (s->dpy, "DPMS", &op, &event, &error))
+ /* Technically this should also check DPMSCapable(), but this is
+ almost certainly close enough. */
+ s->dpms_supported_p = TRUE;
+ else if (s->debug_p)
+ fprintf (stderr, "%s: server does not support power management\n",
+ blurb());
}
+# else /* !HAVE_DPMS_EXTENSION */
+ if (s->debug_p)
+ fprintf (stderr, "%s: DPMS not supported at compile time\n", blurb());
+# endif /* !HAVE_DPMS_EXTENSION */
- /* Adjust the (invisible) notebooks on the popup dialog... */
- {
- GtkNotebook *notebook =
- GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
- GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
- int page = 0;
-
-# ifdef HAVE_XML
- gtk_widget_hide (std);
-# else /* !HAVE_XML */
- /* Make the advanced page be the only one available. */
- gtk_widget_set_sensitive (std, False);
- std = GTK_WIDGET (name_to_widget (s, "adv_button"));
- gtk_widget_hide (std);
- std = GTK_WIDGET (name_to_widget (s, "reset_button"));
- gtk_widget_hide (std);
- page = 1;
-# endif /* !HAVE_XML */
-
- gtk_notebook_set_page (notebook, page);
- gtk_notebook_set_show_tabs (notebook, False);
- }
+# if defined(__APPLE__) && !defined(__OPTIMIZE__)
+ s->dpms_supported_p = TRUE; /* macOS X11: debugging kludge */
+# endif
- /* Various other widget initializations...
- */
- gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
- GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
- (gpointer) s);
- gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
- GTK_SIGNAL_FUNC (wm_popup_close_cb),
- (gpointer) s);
+ if (s->dpy)
+ init_xscreensaver_atoms (s->dpy);
+
+ hack_environment (s); /* must be before initialize_sort_map() */
+ load_init_file (s->dpy, p);
+ initialize_sort_map (s);
+
+ s->gl_visual = get_best_gl_visual (s);
+ s->dialog = g_object_new (XSCREENSAVER_DIALOG_TYPE, NULL);
+ XSCREENSAVER_DIALOG (s->dialog)->main = win;
+ gtk_window_set_transient_for (GTK_WINDOW (s->dialog), GTK_WINDOW (win));
+
+ sensitize_menu_items (s, TRUE);
populate_hack_list (s);
populate_prefs_page (s);
- sensitize_demo_widgets (s, False);
- fix_text_entry_sizes (s);
+ sensitize_demo_widgets (s, FALSE);
scroll_to_current_hack (s);
+ if (s->dpy && !s->wayland_p)
+ fix_preview_visual (s);
+ if (! s->multi_screen_p)
+ hide_mode_menu_random_same (s);
+
+ restore_window_position (s, GTK_WINDOW (self), FALSE);
- gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
- "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
- (gpointer) s);
-
-#ifndef HAVE_GTK2
- gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
- "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
- (gpointer) s);
- gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
- "map", GTK_SIGNAL_FUNC(map_next_button_cb),
- (gpointer) s);
-#endif /* !HAVE_GTK2 */
-
- /* Hook up callbacks to the items on the mode menu. */
- gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "mode_menu")),
- "changed", GTK_SIGNAL_FUNC (mode_menu_item_cb),
- (gpointer) s);
- if (s->nscreens <= 1)
+ g_timeout_add (60 * 1000, check_blanked_timer, s);
+
+ /* Attach the actions and their keybindings. */
+ {
+ int i;
+ GtkApplication *app = gtk_window_get_application (GTK_WINDOW (win));
+ g_action_map_add_action_entries (G_ACTION_MAP (app),
+ app_entries, countof (app_entries),
+ win);
+ for (i = 0; i < countof (accels); i++)
+ {
+ const gchar *a[2];
+ a[0] = accels[i][1];
+ a[1] = 0;
+ gtk_application_set_accels_for_action (GTK_APPLICATION (app),
+ accels[i][0], a);
+ }
+ }
+
+# if 0
+ /* Load every configurator in turn, to scan them for errors all at once. */
+ if (s->debug_p)
{
- GtkComboBox *opt = GTK_COMBO_BOX (name_to_widget (s, "mode_menu"));
- GtkTreeModel *list = gtk_combo_box_get_model (opt);
- unsigned int i;
- for (i = 0; i < countof(mode_menu_order); i++)
+ int i;
+ for (i = 0; i < p->screenhacks_count; i++)
{
- /* The "random-same" mode menu item does not appear unless
- there are multiple screens.
- */
- if (mode_menu_order[i] == RANDOM_HACKS_SAME)
- {
- GtkTreeIter iter;
- gtk_tree_model_iter_nth_child (list, &iter, NULL, i);
- gtk_list_store_remove (GTK_LIST_STORE (list), &iter);
- break;
- }
+ screenhack *hack = p->screenhacks[i];
+ conf_data *d = load_configurator (hack->command, s->debug_p);
+ if (d) free_conf_data (d);
}
-
- /* recompute option-menu size */
- gtk_widget_unrealize (GTK_WIDGET (opt));
- gtk_widget_realize (GTK_WIDGET (opt));
}
+# endif
-
- /* Handle the -prefs command-line argument. */
- if (prefs_p)
+ /* Grab the screenshot pixmap before mapping the window. */
+ if (s->dpy && !s->wayland_p)
{
- GtkNotebook *notebook =
- GTK_NOTEBOOK (name_to_widget (s, "notebook"));
- gtk_notebook_set_page (notebook, 1);
+ GdkWindow *gw = gtk_widget_get_window (win->preview);
+ Window xw = gdk_x11_window_get_xid (gw);
+ s->screenshot = screenshot_grab (s->dpy, xw, TRUE, s->debug_p);
}
-# ifdef HAVE_CRAPPLET
- if (crapplet_p)
- {
- GtkWidget *capplet;
- GtkWidget *outer_vbox;
+ /* Issue any warnings about the running xscreensaver daemon.
+ Wait a few seconds, in case things are still starting up. */
+ g_timeout_add (5 * 1000, the_network_is_not_the_computer, s);
- gtk_widget_hide (s->toplevel_widget);
+ /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
+ after we start up. Otherwise, it always appears scrolled to the top. */
+ g_timeout_add (500, delayed_scroll_kludge, s);
- capplet = capplet_widget_new ();
+ s->initializing_p = FALSE;
+}
- /* Make there be a "Close" button instead of "OK" and "Cancel" */
-# ifdef HAVE_CRAPPLET_IMMEDIATE
- capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
-# endif /* HAVE_CRAPPLET_IMMEDIATE */
- /* In crapplet-mode, take off the menubar. */
- gtk_widget_hide (name_to_widget (s, "menubar"));
- /* Reparent our top-level container to be a child of the capplet
- window.
- */
- outer_vbox = GTK_BIN (s->toplevel_widget)->child;
- gtk_widget_ref (outer_vbox);
- gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
- outer_vbox);
- STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
- gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
-
- /* Find the window above us, and set the title and close handler. */
- {
- GtkWidget *window = capplet;
- while (window && !GTK_IS_WINDOW (window))
- window = GET_PARENT (window);
- if (window)
- {
- gtk_window_set_title (GTK_WINDOW (window), window_title);
- gtk_signal_connect (GTK_OBJECT (window), "delete_event",
- GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
- (gpointer) s);
- }
- }
+static void
+xscreensaver_window_init (XScreenSaverWindow *win)
+{
+ gtk_widget_init_template (GTK_WIDGET (win));
+ g_signal_connect (win, "destroy",
+ G_CALLBACK (xscreensaver_window_destroy), win);
+ g_signal_connect (win, "realize",
+ G_CALLBACK (xscreensaver_window_realize), win);
+ g_signal_connect (win, "configure-event",
+ G_CALLBACK (xscreensaver_window_resize_cb),win);
+ g_signal_connect (win->preview, "configure-event",
+ G_CALLBACK (preview_resize_cb),win);
+}
- s->toplevel_widget = capplet;
- }
-# endif /* HAVE_CRAPPLET */
+static void
+xscreensaver_window_class_init (XScreenSaverWindowClass *class)
+{
+ gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
+ "/org/jwz/xscreensaver/demo.ui");
+
+ /* Fill in the widget fields in XScreenSaverWindow with the corresponding
+ objects created from demo.ui. */
+# undef W
+# define W(NAME) \
+ gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), \
+ XScreenSaverWindow, NAME);
+ ALL_WINDOW_WIDGETS
+# undef W
+}
- /* The Gnome folks hate the menubar. I think it's important to have access
- to the commands on the File menu (Restart Daemon, etc.) and to the
- About and Documentation commands on the Help menu.
- */
-#if 0
-#ifdef HAVE_GTK2
- gtk_widget_hide (name_to_widget (s, "menubar"));
-#endif
-#endif
- free (window_title);
- window_title = 0;
+/****************************************************************************
-#ifdef HAVE_GTK2
- /* After picking the default size, allow -geometry to override it. */
- if (geom)
- gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
-#endif
+ XScreenSaverDialog
- gtk_widget_show (s->toplevel_widget);
- init_icon (GET_WINDOW (GTK_WIDGET (s->toplevel_widget))); /* after `show' */
- fix_preview_visual (s);
+ ****************************************************************************/
- /* Realize page zero, so that we can diddle the scrollbar when the
- user tabs back to it -- otherwise, the current hack isn't scrolled
- to the first time they tab back there, when started with "-prefs".
- (Though it is if they then tab away, and back again.)
+static void
+xscreensaver_dialog_destroy (GObject *object)
+{
+ /* Called by WM close box, but not by File / Quit */
+ XScreenSaverDialog *dialog = XSCREENSAVER_DIALOG (object);
+ XScreenSaverWindow *win = dialog->main;
+ flush_dialog_changes_and_save (&win->state);
+ G_OBJECT_CLASS (xscreensaver_dialog_parent_class)->dispose (object);
+}
- #### Bah! This doesn't work. Gtk eats my ass! Someone who
- #### understands this crap, explain to me how to make this work.
- */
- gtk_widget_realize (name_to_widget (s, "demos_table"));
+static void
+xscreensaver_dialog_realize (GtkWidget *self, gpointer user_data)
+{
+ XScreenSaverDialog *dialog = XSCREENSAVER_DIALOG (self);
+ XScreenSaverWindow *win = dialog->main;
+ state *s = &win->state;
+ restore_window_position (s, GTK_WINDOW (self), TRUE);
+}
- gtk_timeout_add (60 * 1000, check_blanked_timer, s);
+/* When the window is moved, save the new origin in .xscreensaver. */
+static gboolean
+xscreensaver_dialog_resize_cb (GtkWindow *window, GdkEvent *event,
+ gpointer data)
+{
+ XScreenSaverDialog *dialog = XSCREENSAVER_DIALOG (window);
+ XScreenSaverWindow *win = dialog->main;
+ state *s = &win->state;
+ save_window_position (s, GTK_WINDOW (dialog),
+ event->configure.x, event->configure.y, TRUE);
+ return FALSE;
+}
- /* Handle the --settings command-line argument. */
- if (settings_p)
- gtk_timeout_add (500, settings_timer, 0);
+/* The WM close box. */
+static gboolean
+xscreensaver_dialog_delete_cb (GtkWidget *self, GdkEvent *event,
+ gpointer user_data)
+{
+ settings_cancel_cb (GTK_WIDGET (self), user_data);
+ return TRUE; /* Do not run other handlers */
+}
- /* Issue any warnings about the running xscreensaver daemon. */
- if (! s->debug_p)
- the_network_is_not_the_computer (s);
+static void
+xscreensaver_dialog_init (XScreenSaverDialog *win)
+{
+ gtk_widget_init_template (GTK_WIDGET (win));
+ g_signal_connect (win, "destroy",
+ G_CALLBACK (xscreensaver_dialog_destroy), win);
+ g_signal_connect (win, "realize",
+ G_CALLBACK (xscreensaver_dialog_realize), win);
+ g_signal_connect (win, "configure-event",
+ G_CALLBACK (xscreensaver_dialog_resize_cb), win);
+ g_signal_connect (win, "delete-event",
+ G_CALLBACK (xscreensaver_dialog_delete_cb), win);
+}
- if (time ((time_t *) 0) - XSCREENSAVER_RELEASED > 60*60*24*30*17)
- warning_dialog (s->toplevel_widget,
- _("Warning:\n\n"
- "This version of xscreensaver is VERY OLD!\n"
- "Please upgrade!\n"
- "\n"
- "https://www.jwz.org/xscreensaver/\n"
- "\n"
- "(If this is the latest version that your distro ships, then\n"
- "your distro is doing you a disservice. Build from source.)\n"
- ),
- D_NONE, 7);
- /* Run the Gtk event loop, and not the Xt event loop. This means that
- if there were Xt timers or fds registered, they would never get serviced,
- and if there were any Xt widgets, they would never have events delivered.
- Fortunately, we're using Gtk for all of the UI, and only initialized
- Xt so that we could process the command line and use the X resource
- manager.
- */
- s->initializing_p = False;
+static void
+xscreensaver_dialog_class_init (XScreenSaverDialogClass *class)
+{
+ gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (class),
+ "/org/jwz/xscreensaver/prefs.ui");
+
+ /* Fill in the widget fields in XScreenSaverDialog with the corresponding
+ objects created from prefs.ui. */
+# undef W
+# define W(NAME) \
+ gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (class), \
+ XScreenSaverDialog, NAME);
+ ALL_DIALOG_WIDGETS
+# undef W
+}
- /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
- after we start up. Otherwise, it always appears scrolled to the top
- when in crapplet-mode. */
- gtk_timeout_add (500, delayed_scroll_kludge, s);
+/****************************************************************************
+
+ XScreenSaverApp
+
+ ****************************************************************************/
-#if 1
- /* Load every configurator in turn, to scan them for errors all at once. */
- if (s->debug_p)
- {
- int i;
- for (i = 0; i < p->screenhacks_count; i++)
- {
- screenhack *hack = p->screenhacks[i];
- conf_data *d = load_configurator (hack->command, s->debug_p);
- if (d) free_conf_data (d);
- }
- }
-#endif
+static void
+xscreensaver_app_init (XScreenSaverApp *app)
+{
+}
-# ifdef HAVE_CRAPPLET
- if (crapplet_p)
- capplet_gtk_main ();
+
+static void
+xscreensaver_app_startup (GApplication *app)
+{
+ G_APPLICATION_CLASS (xscreensaver_app_parent_class)->startup (app);
+
+ /* Without this, the floating point numbers in the XML files are not
+ parsed properly in locales that use commas instead of periods in
+ floats: sscanf %f expects "1.0" to be "1,0" and returns 0.
+
+ This must be called later than main() because something beneath
+ g_application_run() calls setlocale(LC_ALL, "") and would override it.
+ */
+# ifdef ENABLE_NLS
+ if (!setlocale (LC_NUMERIC, "C"))
+ fprintf (stderr, "%s: warning: could not set LC_NUMERIC=C\n", blurb());
+# endif /* ENABLE_NLS */
+}
+
+
+static void
+xscreensaver_app_activate (GApplication *app)
+{
+ XScreenSaverWindow *win =
+ g_object_new (XSCREENSAVER_WINDOW_TYPE, "application", app, NULL);
+ win->state.debug_p = XSCREENSAVER_APP (app)->cmdline_debug_p;
+ gtk_widget_show_all (GTK_WIDGET (win));
+ gtk_window_present (GTK_WINDOW (win));
+}
+
+
+static void
+xscreensaver_app_open (GApplication *app,
+ GFile **files, gint n_files,
+ const gchar *hint)
+{
+ GList *windows = gtk_application_get_windows (GTK_APPLICATION (app));
+ if (windows)
+ gtk_window_present (GTK_WINDOW (windows->data));
else
-# endif /* HAVE_CRAPPLET */
- gtk_main ();
+ xscreensaver_app_activate (app);
+}
+
- kill_preview_subproc (s, False);
- exit (0);
+static int
+opts_cb (GApplication *app, GVariantDict *opts, gpointer data)
+{
+ if (g_variant_dict_contains (opts, "version")) {
+ fprintf (stderr, "%s\n", screensaver_id+4);
+ return 0;
+ } else if (g_variant_dict_contains (opts, "debug")) {
+ XSCREENSAVER_APP (app)->cmdline_debug_p = TRUE;
+ return -1;
+ } else {
+ return -1;
+ }
}
+
+static void
+xscreensaver_app_class_init (XScreenSaverAppClass *class)
+{
+ G_APPLICATION_CLASS (class)->startup = xscreensaver_app_startup;
+ G_APPLICATION_CLASS (class)->activate = xscreensaver_app_activate;
+ G_APPLICATION_CLASS (class)->open = xscreensaver_app_open;
+}
+
+static XScreenSaverApp *
+xscreensaver_app_new (void)
+{
+ XScreenSaverApp *app = g_object_new (XSCREENSAVER_APP_TYPE,
+ "application-id",
+ "org.jwz.xscreensaver.settings",
+ NULL);
+
+ g_application_add_main_option (G_APPLICATION (app), "version", 'v',
+ G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
+ "Print the version number",
+ NULL);
+ g_application_add_main_option (G_APPLICATION (app), "debug", 0,
+ G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
+ "Print diagnostics to stderr",
+ NULL);
+ g_signal_connect (app, "handle-local-options", G_CALLBACK (opts_cb), app);
+ return app;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ char *s;
+ progname = argv[0];
+ s = strrchr (progname, '/');
+ if (s) progname = s+1;
+ g_log_set_default_handler (g_logger, NULL);
+ g_log_set_writer_func (g_other_logger, NULL, NULL);
+
+# ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ textdomain (GETTEXT_PACKAGE);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif /* ENABLE_NLS */
+
+ return g_application_run (G_APPLICATION (xscreensaver_app_new()),
+ argc, argv);
+}
+
+
+
#endif /* HAVE_GTK -- whole file */