1 /* demo-Gtk.c --- implements the interactive demo-mode and options dialogs.
2 * xscreensaver, Copyright (c) 1993-2020 Jamie Zawinski <jwz@jwz.org>
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
10 * implied warranty.
11 */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #ifdef HAVE_GTK /* whole file */
18
19 #include <xscreensaver-intl.h>
20
21 #include <stdlib.h>
22
23 #ifdef HAVE_UNISTD_H
24 # include <unistd.h>
25 #endif
26
27 # ifdef __GNUC__
28 # define STFU __extension__ /* ignore gcc -pendantic warnings in next sexp */
29 # else
30 # define STFU /* */
31 # endif
32
33
34 #ifdef ENABLE_NLS
35 # include <locale.h>
36 #endif /* ENABLE_NLS */
37
38 #ifndef VMS
39 # include <pwd.h> /* for getpwuid() */
40 #else /* VMS */
41 # include "vms-pwd.h"
42 #endif /* VMS */
43
44 #ifdef HAVE_UNAME
45 # include <sys/utsname.h> /* for uname() */
46 #endif /* HAVE_UNAME */
47
48 #include <stdio.h>
49 #include <time.h>
50 #include <sys/stat.h>
51 #include <sys/time.h>
52
53
54 #include <signal.h>
55 #include <errno.h>
56 #ifdef HAVE_SYS_WAIT_H
57 # include <sys/wait.h> /* for waitpid() and associated macros */
58 #endif
59
60
61 #include <X11/Xproto.h> /* for CARD32 */
62 #include <X11/Xatom.h> /* for XA_INTEGER */
63 #include <X11/Intrinsic.h>
64 #include <X11/StringDefs.h>
65
66 /* We don't actually use any widget internals, but these are included
67 so that gdb will have debug info for the widgets... */
68 #include <X11/IntrinsicP.h>
69 #include <X11/ShellP.h>
70
71 #ifdef HAVE_XMU
72 # ifndef VMS
73 # include <X11/Xmu/Error.h>
74 # else /* VMS */
75 # include <Xmu/Error.h>
76 # endif
77 #else
78 # include "xmu.h"
79 #endif
80
81 #ifdef HAVE_XINERAMA
82 # include <X11/extensions/Xinerama.h>
83 #endif /* HAVE_XINERAMA */
84
85 #include <gtk/gtk.h>
86
87 #ifdef HAVE_CRAPPLET
88 # include <gnome.h>
89 # include <capplet-widget.h>
90 #endif
91
92 #include <gdk/gdkx.h>
93
94 #ifdef HAVE_GTK2
95 # include <glade/glade-xml.h>
96 # include <gmodule.h>
97 #else /* !HAVE_GTK2 */
98 # define G_MODULE_EXPORT /**/
99 #endif /* !HAVE_GTK2 */
100
101 #if defined(DEFAULT_ICONDIR) && !defined(GLADE_DIR)
102 # define GLADE_DIR DEFAULT_ICONDIR
103 #endif
104 #if !defined(DEFAULT_ICONDIR) && defined(GLADE_DIR)
105 # define DEFAULT_ICONDIR GLADE_DIR
106 #endif
107
108 #ifndef HAVE_XML
109 /* Kludge: this is defined in demo-Gtk-conf.c when HAVE_XML.
110 It is unused otherwise, so in that case, stub it out. */
111 static const char *hack_configuration_path = 0;
112 #endif
113
114
115
116 #include "version.h"
117 #include "prefs.h"
118 #include "resources.h" /* for parse_time() */
119 #include "visual.h" /* for has_writable_cells() */
120 #include "remote.h" /* for xscreensaver_command() */
121 #include "usleep.h"
122
123 #include "logo-50.xpm"
124 #include "logo-180.xpm"
125
126 #include "demo-Gtk-conf.h"
127
128 #include <stdio.h>
129 #include <string.h>
130 #include <ctype.h>
131
132 #ifdef HAVE_GTK2
133 enum {
134 COL_ENABLED,
135 COL_NAME,
136 COL_LAST
137 };
138 #endif /* HAVE_GTK2 */
139
140 /* Deal with deprecation of direct access to struct fields on the way to GTK3
141 See http://live.gnome.org/GnomeGoals/UseGseal
142 */
143 #if GTK_CHECK_VERSION(2,14,0)
144 # define GET_PARENT(w) gtk_widget_get_parent (w)
145 # define GET_WINDOW(w) gtk_widget_get_window (w)
146 # define GET_ACTION_AREA(d) gtk_dialog_get_action_area (d)
147 # define GET_CONTENT_AREA(d) gtk_dialog_get_content_area (d)
148 # define GET_ADJ_VALUE(a) gtk_adjustment_get_value (a)
149 # define SET_ADJ_VALUE(a,v) gtk_adjustment_set_value (a, v)
150 # define SET_ADJ_UPPER(a,v) gtk_adjustment_set_upper (a, v)
151 #else
152 # define GET_PARENT(w) ((w)->parent)
153 # define GET_WINDOW(w) ((w)->window)
154 # define GET_ACTION_AREA(d) ((d)->action_area)
155 # define GET_CONTENT_AREA(d) ((d)->vbox)
156 # define GET_ADJ_VALUE(a) ((a)->value)
157 # define SET_ADJ_VALUE(a,v) (a)->value = v
158 # define SET_ADJ_UPPER(a,v) (a)->upper = v
159 #endif
160
161 #if GTK_CHECK_VERSION(2,18,0)
162 # define SET_CAN_DEFAULT(w) gtk_widget_set_can_default ((w), TRUE)
163 # define GET_SENSITIVE(w) gtk_widget_get_sensitive (w)
164 #else
165 # define SET_CAN_DEFAULT(w) GTK_WIDGET_SET_FLAGS ((w), GTK_CAN_DEFAULT)
166 # define GET_SENSITIVE(w) GTK_WIDGET_IS_SENSITIVE (w)
167 #endif
168
169 #if GTK_CHECK_VERSION(2,20,0)
170 # define GET_REALIZED(w) gtk_widget_get_realized (w)
171 #else
172 # define GET_REALIZED(w) GTK_WIDGET_REALIZED (w)
173 #endif
174
175 /* from exec.c */
176 extern void exec_command (const char *shell, const char *command, int nice);
177 extern int on_path_p (const char *program);
178
179 static void hack_subproc_environment (Window preview_window_id, Bool debug_p);
180
181 #undef countof
182 #define countof(x) (sizeof((x))/sizeof((*x)))
183
184
185 /* You might think that to read an array of 32-bit quantities out of a
186 server-side property, you would pass an array of 32-bit data quantities
187 into XGetWindowProperty(). You would be wrong. You have to use an array
188 of longs, even if long is 64 bits (using 32 of each 64.)
189 */
190 typedef long PROP32;
191
192 char *progname = 0;
193 char *progclass = "XScreenSaver";
194 XrmDatabase db;
195
196 /* The order of the items in the mode menu. */
197 static int mode_menu_order[] = {
198 DONT_BLANK, BLANK_ONLY, ONE_HACK, RANDOM_HACKS, RANDOM_HACKS_SAME };
199
200
201 typedef struct {
202
203 char *short_version; /* version number of this xscreensaver build */
204
205 GtkWidget *toplevel_widget; /* the main window */
206 GtkWidget *base_widget; /* root of our hierarchy (for name lookups) */
207 GtkWidget *popup_widget; /* the "Settings" dialog */
208 conf_data *cdata; /* private data for per-hack configuration */
209
210 #ifdef HAVE_GTK2
211 GladeXML *glade_ui; /* Glade UI file */
212 #endif /* HAVE_GTK2 */
213
214 Bool debug_p; /* whether to print diagnostics */
215 Bool initializing_p; /* flag for breaking recursion loops */
216 Bool saving_p; /* flag for breaking recursion loops */
217
218 char *desired_preview_cmd; /* subprocess we intend to run */
219 char *running_preview_cmd; /* subprocess we are currently running */
220 pid_t running_preview_pid; /* pid of forked subproc (might be dead) */
221 Bool running_preview_error_p; /* whether the pid died abnormally */
222
223 Bool preview_suppressed_p; /* flag meaning "don't launch subproc" */
224 int subproc_timer_id; /* timer to delay subproc launch */
225 int subproc_check_timer_id; /* timer to check whether it started up */
226 int subproc_check_countdown; /* how many more checks left */
227
228 int *list_elt_to_hack_number; /* table for sorting the hack list */
229 int *hack_number_to_list_elt; /* the inverse table */
230 Bool *hacks_available_p; /* whether hacks are on $PATH */
231 int total_available; /* how many are on $PATH */
232 int list_count; /* how many items are in the list: this may be
233 less than p->screenhacks_count, if some are
234 suppressed. */
235
236 int _selected_list_element; /* don't use this: call
237 selected_list_element() instead */
238
239 int nscreens; /* How many X or Xinerama screens there are */
240
241 saver_preferences prefs;
242
243 } state;
244
245
246 /* Total fucking evilness due to the fact that it's rocket science to get
247 a closure object of our own down into the various widget callbacks. */
248 static state *global_state_kludge;
249
250 Atom XA_VROOT;
251 Atom XA_SCREENSAVER, XA_SCREENSAVER_RESPONSE, XA_SCREENSAVER_VERSION;
252 Atom XA_SCREENSAVER_ID, XA_SCREENSAVER_STATUS, XA_SELECT, XA_DEMO;
253 Atom XA_ACTIVATE, XA_SUSPEND, XA_BLANK, XA_LOCK, XA_RESTART, XA_EXIT;
254 Atom XA_NEXT, XA_PREV;
255
256
257 static void populate_demo_window (state *, int list_elt);
258 static void populate_prefs_page (state *);
259 static void populate_popup_window (state *);
260
261 static Bool flush_dialog_changes_and_save (state *);
262 static Bool flush_popup_changes_and_save (state *);
263
264 static int maybe_reload_init_file (state *);
265 static void await_xscreensaver (state *);
266 static Bool xscreensaver_running_p (state *);
267 static void sensitize_menu_items (state *s, Bool force_p);
268 static void force_dialog_repaint (state *s);
269
270 static void schedule_preview (state *, const char *cmd);
271 static void kill_preview_subproc (state *, Bool reset_p);
272 static void schedule_preview_check (state *);
273
274
275 /* Prototypes of functions used by the Glade-generated code,
276 to avoid warnings.
277 */
278 void exit_menu_cb (GtkMenuItem *, gpointer user_data);
279 void about_menu_cb (GtkMenuItem *, gpointer user_data);
280 void doc_menu_cb (GtkMenuItem *, gpointer user_data);
281 void file_menu_cb (GtkMenuItem *, gpointer user_data);
282 void activate_menu_cb (GtkMenuItem *, gpointer user_data);
283 void lock_menu_cb (GtkMenuItem *, gpointer user_data);
284 void kill_menu_cb (GtkMenuItem *, gpointer user_data);
285 void restart_menu_cb (GtkWidget *, gpointer user_data);
286 void run_this_cb (GtkButton *, gpointer user_data);
287 void manual_cb (GtkButton *, gpointer user_data);
288 void run_next_cb (GtkButton *, gpointer user_data);
289 void run_prev_cb (GtkButton *, gpointer user_data);
290 void pref_changed_cb (GtkWidget *, gpointer user_data);
291 gboolean pref_changed_event_cb (GtkWidget *, GdkEvent *, gpointer user_data);
292 void mode_menu_item_cb (GtkWidget *, gpointer user_data);
293 void switch_page_cb (GtkNotebook *, GtkNotebookPage *,
294 gint page_num, gpointer user_data);
295 void browse_image_dir_cb (GtkButton *, gpointer user_data);
296 void browse_text_file_cb (GtkButton *, gpointer user_data);
297 void browse_text_program_cb (GtkButton *, gpointer user_data);
298 void settings_cb (GtkButton *, gpointer user_data);
299 void settings_adv_cb (GtkButton *, gpointer user_data);
300 void settings_std_cb (GtkButton *, gpointer user_data);
301 void settings_reset_cb (GtkButton *, gpointer user_data);
302 void settings_switch_page_cb (GtkNotebook *, GtkNotebookPage *,
303 gint page_num, gpointer user_data);
304 void settings_cancel_cb (GtkButton *, gpointer user_data);
305 void settings_ok_cb (GtkButton *, gpointer user_data);
306
307 static void kill_gnome_screensaver (void);
308 static void kill_kde_screensaver (void);
309
310
311 /* Some random utility functions
312 */
313
314 const char *blurb (void);
315
316 const char *
blurb(void)317 blurb (void)
318 {
319 time_t now = time ((time_t *) 0);
320 char *ct = (char *) ctime (&now);
321 static char buf[255];
322 int n = strlen(progname);
323 if (n > 100) n = 99;
324 strncpy(buf, progname, n);
325 buf[n++] = ':';
326 buf[n++] = ' ';
327 strncpy(buf+n, ct+11, 8);
328 strcpy(buf+n+9, ": ");
329 return buf;
330 }
331
332
333 static GtkWidget *
name_to_widget(state * s,const char * name)334 name_to_widget (state *s, const char *name)
335 {
336 GtkWidget *w;
337 if (!s) abort();
338 if (!name) abort();
339 if (!*name) abort();
340
341 #ifdef HAVE_GTK2
342 if (!s->glade_ui)
343 {
344 /* First try to load the Glade file from the current directory;
345 if there isn't one there, check the installed directory.
346 */
347 # define GLADE_FILE_NAME "xscreensaver-demo.glade2"
348 const char * const files[] = { GLADE_FILE_NAME,
349 GLADE_DIR "/" GLADE_FILE_NAME };
350 int i;
351 for (i = 0; i < countof (files); i++)
352 {
353 struct stat st;
354 if (!stat (files[i], &st))
355 {
356 s->glade_ui = glade_xml_new (files[i], NULL, NULL);
357 break;
358 }
359 }
360 if (!s->glade_ui)
361 {
362 fprintf (stderr,
363 "%s: could not load \"" GLADE_FILE_NAME "\"\n"
364 "\tfrom " GLADE_DIR "/ or current directory.\n",
365 blurb());
366 exit (-1);
367 }
368 # undef GLADE_FILE_NAME
369
370 glade_xml_signal_autoconnect (s->glade_ui);
371 }
372
373 w = glade_xml_get_widget (s->glade_ui, name);
374
375 #else /* !HAVE_GTK2 */
376
377 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->base_widget),
378 name);
379 if (w) return w;
380 w = (GtkWidget *) gtk_object_get_data (GTK_OBJECT (s->popup_widget),
381 name);
382 #endif /* HAVE_GTK2 */
383 if (w) return w;
384
385 fprintf (stderr, "%s: no widget \"%s\" (wrong Glade file?)\n",
386 blurb(), name);
387 abort();
388 }
389
390
391 /* Why this behavior isn't automatic in *either* toolkit, I'll never know.
392 Takes a scroller, viewport, or list as an argument.
393 */
394 static void
ensure_selected_item_visible(GtkWidget * widget)395 ensure_selected_item_visible (GtkWidget *widget)
396 {
397 #ifdef HAVE_GTK2
398 GtkTreePath *path;
399 GtkTreeSelection *selection;
400 GtkTreeIter iter;
401 GtkTreeModel *model;
402
403 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
404 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
405 path = gtk_tree_path_new_first ();
406 else
407 path = gtk_tree_model_get_path (model, &iter);
408
409 gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
410
411 gtk_tree_path_free (path);
412
413 #else /* !HAVE_GTK2 */
414
415 GtkScrolledWindow *scroller = 0;
416 GtkViewport *vp = 0;
417 GtkList *list_widget = 0;
418 GList *slist;
419 GList *kids;
420 int nkids = 0;
421 GtkWidget *selected = 0;
422 int list_elt = -1;
423 GtkAdjustment *adj;
424 gint parent_h, child_y, child_h, children_h, ignore;
425 double ratio_t, ratio_b;
426
427 if (GTK_IS_SCROLLED_WINDOW (widget))
428 {
429 scroller = GTK_SCROLLED_WINDOW (widget);
430 vp = GTK_VIEWPORT (GTK_BIN (scroller)->child);
431 list_widget = GTK_LIST (GTK_BIN(vp)->child);
432 }
433 else if (GTK_IS_VIEWPORT (widget))
434 {
435 vp = GTK_VIEWPORT (widget);
436 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
437 list_widget = GTK_LIST (GTK_BIN(vp)->child);
438 }
439 else if (GTK_IS_LIST (widget))
440 {
441 list_widget = GTK_LIST (widget);
442 vp = GTK_VIEWPORT (GTK_WIDGET (list_widget)->parent);
443 scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
444 }
445 else
446 abort();
447
448 slist = list_widget->selection;
449 selected = (slist ? GTK_WIDGET (slist->data) : 0);
450 if (!selected)
451 return;
452
453 list_elt = gtk_list_child_position (list_widget, GTK_WIDGET (selected));
454
455 for (kids = gtk_container_children (GTK_CONTAINER (list_widget));
456 kids; kids = kids->next)
457 nkids++;
458
459 adj = gtk_scrolled_window_get_vadjustment (scroller);
460
461 gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (vp)),
462 &ignore, &ignore, &ignore, &parent_h, &ignore);
463 gdk_window_get_geometry (GET_WINDOW (GTK_WIDGET (selected)),
464 &ignore, &child_y, &ignore, &child_h, &ignore);
465 children_h = nkids * child_h;
466
467 ratio_t = ((double) child_y) / ((double) children_h);
468 ratio_b = ((double) child_y + child_h) / ((double) children_h);
469
470 if (adj->upper == 0.0) /* no items in list */
471 return;
472
473 if (ratio_t < (adj->value / adj->upper) ||
474 ratio_b > ((adj->value + adj->page_size) / adj->upper))
475 {
476 double target;
477 int slop = parent_h * 0.75; /* how much to overshoot by */
478
479 if (ratio_t < (adj->value / adj->upper))
480 {
481 double ratio_w = ((double) parent_h) / ((double) children_h);
482 double ratio_l = (ratio_b - ratio_t);
483 target = ((ratio_t - ratio_w + ratio_l) * adj->upper);
484 target += slop;
485 }
486 else /* if (ratio_b > ((adj->value + adj->page_size) / adj->upper))*/
487 {
488 target = ratio_t * adj->upper;
489 target -= slop;
490 }
491
492 if (target > adj->upper - adj->page_size)
493 target = adj->upper - adj->page_size;
494 if (target < 0)
495 target = 0;
496
497 gtk_adjustment_set_value (adj, target);
498 }
499 #endif /* !HAVE_GTK2 */
500 }
501
502 static void
warning_dialog_dismiss_cb(GtkWidget * widget,gpointer user_data)503 warning_dialog_dismiss_cb (GtkWidget *widget, gpointer user_data)
504 {
505 GtkWidget *shell = GTK_WIDGET (user_data);
506 while (GET_PARENT (shell))
507 shell = GET_PARENT (shell);
508 gtk_widget_destroy (GTK_WIDGET (shell));
509 }
510
511
512 void restart_menu_cb (GtkWidget *widget, gpointer user_data);
513
warning_dialog_restart_cb(GtkWidget * widget,gpointer user_data)514 static void warning_dialog_restart_cb (GtkWidget *widget, gpointer user_data)
515 {
516 restart_menu_cb (widget, user_data);
517 warning_dialog_dismiss_cb (widget, user_data);
518 }
519
warning_dialog_killg_cb(GtkWidget * widget,gpointer user_data)520 static void warning_dialog_killg_cb (GtkWidget *widget, gpointer user_data)
521 {
522 kill_gnome_screensaver ();
523 warning_dialog_dismiss_cb (widget, user_data);
524 }
525
warning_dialog_killk_cb(GtkWidget * widget,gpointer user_data)526 static void warning_dialog_killk_cb (GtkWidget *widget, gpointer user_data)
527 {
528 kill_kde_screensaver ();
529 warning_dialog_dismiss_cb (widget, user_data);
530 }
531
532 typedef enum { D_NONE, D_LAUNCH, D_GNOME, D_KDE } dialog_button;
533
534 static Bool
warning_dialog(GtkWidget * parent,const char * message,dialog_button button_type,int center)535 warning_dialog (GtkWidget *parent, const char *message,
536 dialog_button button_type, int center)
537 {
538 char *msg = strdup (message);
539 char *head;
540
541 GtkWidget *dialog = gtk_dialog_new ();
542 GtkWidget *label = 0;
543 GtkWidget *ok = 0;
544 GtkWidget *cancel = 0;
545 int i = 0;
546
547 while (parent && !GET_WINDOW (parent))
548 parent = GET_PARENT (parent);
549
550 if (!parent ||
551 !GET_WINDOW (parent)) /* too early to pop up transient dialogs */
552 {
553 fprintf (stderr,
554 "%s: too early for warning dialog?"
555 "\n\n\t%s\n\n",
556 progname, message);
557 free(msg);
558 return False;
559 }
560
561 head = msg;
562 while (head)
563 {
564 char name[20];
565 char *s = strchr (head, '\n');
566 if (s) *s = 0;
567
568 sprintf (name, "label%d", i++);
569
570 {
571 label = gtk_label_new (head);
572 #ifdef HAVE_GTK2
573 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
574 #endif /* HAVE_GTK2 */
575
576 #ifndef HAVE_GTK2
577 if (i == 1)
578 {
579 GTK_WIDGET (label)->style =
580 gtk_style_copy (GTK_WIDGET (label)->style);
581 GTK_WIDGET (label)->style->font =
582 gdk_font_load (get_string_resource("warning_dialog.headingFont",
583 "Dialog.Font"));
584 gtk_widget_set_style (GTK_WIDGET (label),
585 GTK_WIDGET (label)->style);
586 }
587 #endif /* !HAVE_GTK2 */
588 if (center <= 0)
589 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
590 gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
591 label, TRUE, TRUE, 0);
592 gtk_widget_show (label);
593 }
594
595 if (s)
596 head = s+1;
597 else
598 head = 0;
599
600 center--;
601 }
602
603 label = gtk_label_new ("");
604 gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
605 label, TRUE, TRUE, 0);
606 gtk_widget_show (label);
607
608 label = gtk_hbutton_box_new ();
609 gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
610 label, TRUE, TRUE, 0);
611
612 #ifdef HAVE_GTK2
613 if (button_type != D_NONE)
614 {
615 cancel = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
616 gtk_container_add (GTK_CONTAINER (label), cancel);
617 }
618
619 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
620 gtk_container_add (GTK_CONTAINER (label), ok);
621
622 #else /* !HAVE_GTK2 */
623
624 ok = gtk_button_new_with_label ("OK");
625 gtk_container_add (GTK_CONTAINER (label), ok);
626
627 if (button_type != D_NONE)
628 {
629 cancel = gtk_button_new_with_label ("Cancel");
630 gtk_container_add (GTK_CONTAINER (label), cancel);
631 }
632
633 #endif /* !HAVE_GTK2 */
634
635 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
636 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
637 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
638 SET_CAN_DEFAULT (ok);
639 gtk_widget_show (ok);
640 gtk_widget_grab_focus (ok);
641
642 if (cancel)
643 {
644 SET_CAN_DEFAULT (cancel);
645 gtk_widget_show (cancel);
646 }
647 gtk_widget_show (label);
648 gtk_widget_show (dialog);
649
650 if (button_type != D_NONE)
651 {
652 GtkSignalFunc fn;
653 switch (button_type) {
654 case D_LAUNCH: fn = GTK_SIGNAL_FUNC (warning_dialog_restart_cb); break;
655 case D_GNOME: fn = GTK_SIGNAL_FUNC (warning_dialog_killg_cb); break;
656 case D_KDE: fn = GTK_SIGNAL_FUNC (warning_dialog_killk_cb); break;
657 default: abort(); break;
658 }
659 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked", fn,
660 (gpointer) dialog);
661 gtk_signal_connect_object (GTK_OBJECT (cancel), "clicked",
662 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
663 (gpointer) dialog);
664 }
665 else
666 {
667 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
668 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
669 (gpointer) dialog);
670 }
671
672 gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
673 GET_WINDOW (GTK_WIDGET (parent)));
674
675 #ifdef HAVE_GTK2
676 gtk_window_present (GTK_WINDOW (dialog));
677 #else /* !HAVE_GTK2 */
678 gdk_window_show (GTK_WIDGET (dialog)->window);
679 gdk_window_raise (GTK_WIDGET (dialog)->window);
680 #endif /* !HAVE_GTK2 */
681
682 free (msg);
683 return True;
684 }
685
686
687 static void
run_cmd(state * s,Atom command,int arg)688 run_cmd (state *s, Atom command, int arg)
689 {
690 char *err = 0;
691 int status;
692
693 flush_dialog_changes_and_save (s);
694 status = xscreensaver_command (GDK_DISPLAY(), command, arg, False, &err);
695
696 /* Kludge: ignore the spurious "window unexpectedly deleted" errors... */
697 if (status < 0 && err && strstr (err, "unexpectedly deleted"))
698 status = 0;
699
700 if (status < 0)
701 {
702 char buf [255];
703 if (err)
704 sprintf (buf, "Error:\n\n%s", err);
705 else
706 strcpy (buf, "Unknown error!");
707 warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
708 }
709 if (err) free (err);
710
711 sensitize_menu_items (s, True);
712 force_dialog_repaint (s);
713 }
714
715
716 static void
run_hack(state * s,int list_elt,Bool report_errors_p)717 run_hack (state *s, int list_elt, Bool report_errors_p)
718 {
719 int hack_number;
720 char *err = 0;
721 int status;
722
723 if (list_elt < 0) return;
724 hack_number = s->list_elt_to_hack_number[list_elt];
725
726 flush_dialog_changes_and_save (s);
727 schedule_preview (s, 0);
728
729 status = xscreensaver_command (GDK_DISPLAY(), XA_DEMO, hack_number + 1,
730 False, &err);
731
732 if (status < 0 && report_errors_p)
733 {
734 if (xscreensaver_running_p (s))
735 {
736 /* Kludge: ignore the spurious "window unexpectedly deleted"
737 errors... */
738 if (err && strstr (err, "unexpectedly deleted"))
739 status = 0;
740
741 if (status < 0)
742 {
743 char buf [255];
744 if (err)
745 sprintf (buf, "Error:\n\n%s", err);
746 else
747 strcpy (buf, "Unknown error!");
748 warning_dialog (s->toplevel_widget, buf, D_NONE, 100);
749 }
750 }
751 else
752 {
753 /* The error is that the daemon isn't running;
754 offer to restart it.
755 */
756 const char *d = DisplayString (GDK_DISPLAY());
757 char msg [1024];
758 sprintf (msg,
759 _("Warning:\n\n"
760 "The XScreenSaver daemon doesn't seem to be running\n"
761 "on display \"%s\". Launch it now?"),
762 d);
763 warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
764 }
765 }
766
767 if (err) free (err);
768
769 sensitize_menu_items (s, False);
770 }
771
772
773
774 /* Button callbacks
775
776 According to Eric Lassauge, this G_MODULE_EXPORT crud is needed to make
777 libglade work on Cygwin; apparently all Glade callbacks need this magic
778 extra declaration. I do not pretend to understand.
779 */
780
781 G_MODULE_EXPORT void
exit_menu_cb(GtkMenuItem * menuitem,gpointer user_data)782 exit_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
783 {
784 state *s = global_state_kludge; /* I hate C so much... */
785 flush_dialog_changes_and_save (s);
786 kill_preview_subproc (s, False);
787 gtk_main_quit ();
788 }
789
790 static gboolean
wm_toplevel_close_cb(GtkWidget * widget,GdkEvent * event,gpointer data)791 wm_toplevel_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
792 {
793 state *s = (state *) data;
794 flush_dialog_changes_and_save (s);
795 gtk_main_quit ();
796 return TRUE;
797 }
798
799
800 G_MODULE_EXPORT void
about_menu_cb(GtkMenuItem * menuitem,gpointer user_data)801 about_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
802 {
803 char msg [2048];
804 char *vers = strdup (screensaver_id + 4);
805 char *s, *s2;
806 char copy[1024];
807 char year[5];
808 char *desc = _("For updates, check https://www.jwz.org/xscreensaver/");
809
810 s = strchr (vers, ',');
811 *s = 0;
812 s += 2;
813
814 s2 = vers;
815 s2 = strrchr (vers, '-');
816 s2++;
817 strncpy (year, s2, 4);
818 year[4] = 0;
819
820 /* Ole Laursen <olau@hardworking.dk> says "don't use _() here because
821 non-ASCII characters aren't allowed in localizable string keys."
822 (I don't want to just use (c) instead of � because that doesn't
823 look as good in the plain-old default Latin1 "C" locale.)
824 */
825 #ifdef HAVE_GTK2
826 sprintf(copy, ("Copyright \xC2\xA9 1991-%s %s"), year, s);
827 #else /* !HAVE_GTK2 */
828 sprintf(copy, ("Copyright \251 1991-%s %s"), year, s);
829 #endif /* !HAVE_GTK2 */
830
831 sprintf (msg, "%s\n\n%s", copy, desc);
832
833 /* I can't make gnome_about_new() work here -- it starts dying in
834 gdk_imlib_get_visual() under gnome_about_new(). If this worked,
835 then this might be the thing to do:
836
837 #ifdef HAVE_CRAPPLET
838 {
839 const gchar *auth[] = { 0 };
840 GtkWidget *about = gnome_about_new (progclass, vers, "", auth, desc,
841 "xscreensaver.xpm");
842 gtk_widget_show (about);
843 }
844 #else / * GTK but not GNOME * /
845 ...
846 */
847 {
848 GdkColormap *colormap;
849 GdkPixmap *gdkpixmap;
850 GdkBitmap *mask;
851
852 GtkWidget *dialog = gtk_dialog_new ();
853 GtkWidget *hbox, *icon, *vbox, *label1, *label2, *hb, *ok;
854 GtkWidget *parent = GTK_WIDGET (menuitem);
855 while (GET_PARENT (parent))
856 parent = GET_PARENT (parent);
857
858 hbox = gtk_hbox_new (FALSE, 20);
859 gtk_box_pack_start (GTK_BOX (GET_CONTENT_AREA (GTK_DIALOG (dialog))),
860 hbox, TRUE, TRUE, 0);
861
862 colormap = gtk_widget_get_colormap (parent);
863 gdkpixmap =
864 gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask, NULL,
865 (gchar **) logo_180_xpm);
866 icon = gtk_pixmap_new (gdkpixmap, mask);
867 gtk_misc_set_padding (GTK_MISC (icon), 10, 10);
868
869 gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
870
871 vbox = gtk_vbox_new (FALSE, 0);
872 gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
873
874 label1 = gtk_label_new (vers);
875 gtk_box_pack_start (GTK_BOX (vbox), label1, TRUE, TRUE, 0);
876 gtk_label_set_justify (GTK_LABEL (label1), GTK_JUSTIFY_LEFT);
877 gtk_misc_set_alignment (GTK_MISC (label1), 0.0, 0.75);
878
879 #ifndef HAVE_GTK2
880 GTK_WIDGET (label1)->style = gtk_style_copy (GTK_WIDGET (label1)->style);
881 GTK_WIDGET (label1)->style->font =
882 gdk_font_load (get_string_resource ("about.headingFont","Dialog.Font"));
883 gtk_widget_set_style (GTK_WIDGET (label1), GTK_WIDGET (label1)->style);
884 #endif /* HAVE_GTK2 */
885
886 label2 = gtk_label_new (msg);
887 gtk_box_pack_start (GTK_BOX (vbox), label2, TRUE, TRUE, 0);
888 gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_LEFT);
889 gtk_misc_set_alignment (GTK_MISC (label2), 0.0, 0.25);
890
891 #ifndef HAVE_GTK2
892 GTK_WIDGET (label2)->style = gtk_style_copy (GTK_WIDGET (label2)->style);
893 GTK_WIDGET (label2)->style->font =
894 gdk_font_load (get_string_resource ("about.bodyFont","Dialog.Font"));
895 gtk_widget_set_style (GTK_WIDGET (label2), GTK_WIDGET (label2)->style);
896 #endif /* HAVE_GTK2 */
897
898 hb = gtk_hbutton_box_new ();
899
900 gtk_box_pack_start (GTK_BOX (GET_ACTION_AREA (GTK_DIALOG (dialog))),
901 hb, TRUE, TRUE, 0);
902
903 #ifdef HAVE_GTK2
904 ok = gtk_button_new_from_stock (GTK_STOCK_OK);
905 #else /* !HAVE_GTK2 */
906 ok = gtk_button_new_with_label (_("OK"));
907 #endif /* !HAVE_GTK2 */
908 gtk_container_add (GTK_CONTAINER (hb), ok);
909
910 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
911 gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
912 gtk_window_set_title (GTK_WINDOW (dialog), progclass);
913
914 gtk_widget_show (hbox);
915 gtk_widget_show (icon);
916 gtk_widget_show (vbox);
917 gtk_widget_show (label1);
918 gtk_widget_show (label2);
919 gtk_widget_show (hb);
920 gtk_widget_show (ok);
921 gtk_widget_show (dialog);
922
923 gtk_signal_connect_object (GTK_OBJECT (ok), "clicked",
924 GTK_SIGNAL_FUNC (warning_dialog_dismiss_cb),
925 (gpointer) dialog);
926 gdk_window_set_transient_for (GET_WINDOW (GTK_WIDGET (dialog)),
927 GET_WINDOW (GTK_WIDGET (parent)));
928 gdk_window_show (GET_WINDOW (GTK_WIDGET (dialog)));
929 gdk_window_raise (GET_WINDOW (GTK_WIDGET (dialog)));
930 }
931 }
932
933
934 G_MODULE_EXPORT void
doc_menu_cb(GtkMenuItem * menuitem,gpointer user_data)935 doc_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
936 {
937 state *s = global_state_kludge; /* I hate C so much... */
938 saver_preferences *p = &s->prefs;
939 char *help_command;
940
941 if (!p->help_url || !*p->help_url)
942 {
943 warning_dialog (s->toplevel_widget,
944 _("Error:\n\n"
945 "No Help URL has been specified.\n"), D_NONE, 100);
946 return;
947 }
948
949 help_command = (char *) malloc (strlen (p->load_url_command) +
950 (strlen (p->help_url) * 4) + 20);
951 strcpy (help_command, "( ");
952 sprintf (help_command + strlen(help_command),
953 p->load_url_command,
954 p->help_url, p->help_url, p->help_url, p->help_url);
955 strcat (help_command, " ) &");
956 if (system (help_command) < 0)
957 fprintf (stderr, "%s: fork error\n", blurb());
958 free (help_command);
959 }
960
961
962 G_MODULE_EXPORT void
file_menu_cb(GtkMenuItem * menuitem,gpointer user_data)963 file_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
964 {
965 state *s = global_state_kludge; /* I hate C so much... */
966 sensitize_menu_items (s, False);
967 }
968
969
970 G_MODULE_EXPORT void
activate_menu_cb(GtkMenuItem * menuitem,gpointer user_data)971 activate_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
972 {
973 state *s = global_state_kludge; /* I hate C so much... */
974 run_cmd (s, XA_ACTIVATE, 0);
975 }
976
977
978 G_MODULE_EXPORT void
lock_menu_cb(GtkMenuItem * menuitem,gpointer user_data)979 lock_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
980 {
981 state *s = global_state_kludge; /* I hate C so much... */
982 run_cmd (s, XA_LOCK, 0);
983 }
984
985
986 G_MODULE_EXPORT void
kill_menu_cb(GtkMenuItem * menuitem,gpointer user_data)987 kill_menu_cb (GtkMenuItem *menuitem, gpointer user_data)
988 {
989 state *s = global_state_kludge; /* I hate C so much... */
990 run_cmd (s, XA_EXIT, 0);
991 }
992
993
994 G_MODULE_EXPORT void
restart_menu_cb(GtkWidget * widget,gpointer user_data)995 restart_menu_cb (GtkWidget *widget, gpointer user_data)
996 {
997 state *s = global_state_kludge; /* I hate C so much... */
998 flush_dialog_changes_and_save (s);
999 xscreensaver_command (GDK_DISPLAY(), XA_EXIT, 0, False, NULL);
1000 sleep (1);
1001 if (system ("xscreensaver -nosplash &") < 0)
1002 fprintf (stderr, "%s: fork error\n", blurb());
1003
1004 await_xscreensaver (s);
1005 }
1006
1007 static Bool
xscreensaver_running_p(state * s)1008 xscreensaver_running_p (state *s)
1009 {
1010 Display *dpy = GDK_DISPLAY();
1011 char *rversion = 0;
1012 server_xscreensaver_version (dpy, &rversion, 0, 0);
1013 if (!rversion)
1014 return False;
1015 free (rversion);
1016 return True;
1017 }
1018
1019 static void
await_xscreensaver(state * s)1020 await_xscreensaver (state *s)
1021 {
1022 int countdown = 5;
1023 Bool ok = False;
1024
1025 while (!ok && (--countdown > 0))
1026 if (xscreensaver_running_p (s))
1027 ok = True;
1028 else
1029 sleep (1); /* If it's not there yet, wait a second... */
1030
1031 sensitize_menu_items (s, True);
1032
1033 if (! ok)
1034 {
1035 /* Timed out, no screensaver running. */
1036
1037 char buf [1024];
1038 Bool root_p = (geteuid () == 0);
1039
1040 strcpy (buf,
1041 _("Error:\n\n"
1042 "The xscreensaver daemon did not start up properly.\n"
1043 "\n"));
1044
1045 if (root_p)
1046 strcat (buf, STFU
1047 _("You are running as root. This usually means that xscreensaver\n"
1048 "was unable to contact your X server because access control is\n"
1049 "turned on."
1050 /*
1051 " Try running this command:\n"
1052 "\n"
1053 " xhost +localhost\n"
1054 "\n"
1055 "and then selecting `File / Restart Daemon'.\n"
1056 "\n"
1057 "Note that turning off access control will allow anyone logged\n"
1058 "on to this machine to access your screen, which might be\n"
1059 "considered a security problem. Please read the xscreensaver\n"
1060 "manual and FAQ for more information.\n"
1061 */
1062 "\n"
1063 "You shouldn't run X as root. Instead, you should log in as a\n"
1064 "normal user, and `sudo' as necessary."));
1065 else
1066 strcat (buf, _("Please check your $PATH and permissions."));
1067
1068 warning_dialog (s->toplevel_widget, buf, D_NONE, 1);
1069 }
1070
1071 force_dialog_repaint (s);
1072 }
1073
1074
1075 static int
selected_list_element(state * s)1076 selected_list_element (state *s)
1077 {
1078 return s->_selected_list_element;
1079 }
1080
1081
1082 static int
demo_write_init_file(state * s,saver_preferences * p)1083 demo_write_init_file (state *s, saver_preferences *p)
1084 {
1085 Display *dpy = GDK_DISPLAY();
1086
1087 #if 0
1088 /* #### try to figure out why shit keeps getting reordered... */
1089 if (strcmp (s->prefs.screenhacks[0]->name, "DNA Lounge Slideshow"))
1090 abort();
1091 #endif
1092
1093 if (!write_init_file (dpy, p, s->short_version, False))
1094 {
1095 if (s->debug_p)
1096 fprintf (stderr, "%s: wrote %s\n", blurb(), init_file_name());
1097 return 0;
1098 }
1099 else
1100 {
1101 const char *f = init_file_name();
1102 if (!f || !*f)
1103 warning_dialog (s->toplevel_widget,
1104 _("Error:\n\nCouldn't determine init file name!\n"),
1105 D_NONE, 100);
1106 else
1107 {
1108 char *b = (char *) malloc (strlen(f) + 1024);
1109 sprintf (b, _("Error:\n\nCouldn't write %s\n"), f);
1110 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1111 free (b);
1112 }
1113 return -1;
1114 }
1115 }
1116
1117
1118 G_MODULE_EXPORT void
run_this_cb(GtkButton * button,gpointer user_data)1119 run_this_cb (GtkButton *button, gpointer user_data)
1120 {
1121 state *s = global_state_kludge; /* I hate C so much... */
1122 int list_elt = selected_list_element (s);
1123 if (list_elt < 0) return;
1124 if (!flush_dialog_changes_and_save (s))
1125 run_hack (s, list_elt, True);
1126 }
1127
1128
1129 G_MODULE_EXPORT void
manual_cb(GtkButton * button,gpointer user_data)1130 manual_cb (GtkButton *button, gpointer user_data)
1131 {
1132 Display *dpy = GDK_DISPLAY();
1133 state *s = global_state_kludge; /* I hate C so much... */
1134 saver_preferences *p = &s->prefs;
1135 GtkWidget *list_widget = name_to_widget (s, "list");
1136 int list_elt = selected_list_element (s);
1137 int hack_number;
1138 char *name, *name2, *cmd, *str;
1139 char *oname = 0;
1140 if (list_elt < 0) return;
1141 hack_number = s->list_elt_to_hack_number[list_elt];
1142
1143 flush_dialog_changes_and_save (s);
1144 ensure_selected_item_visible (list_widget);
1145
1146 name = strdup (p->screenhacks[hack_number]->command);
1147 name2 = name;
1148 oname = name;
1149 while (isspace (*name2)) name2++;
1150 str = name2;
1151 while (*str && !isspace (*str)) str++;
1152 *str = 0;
1153 str = strrchr (name2, '/');
1154 if (str) name2 = str+1;
1155
1156 cmd = get_string_resource (dpy, "manualCommand", "ManualCommand");
1157 if (cmd)
1158 {
1159 char *cmd2 = (char *) malloc (strlen (cmd) + (strlen (name2) * 4) + 100);
1160 strcpy (cmd2, "( ");
1161 sprintf (cmd2 + strlen (cmd2),
1162 cmd,
1163 name2, name2, name2, name2);
1164 strcat (cmd2, " ) &");
1165 if (system (cmd2) < 0)
1166 fprintf (stderr, "%s: fork error\n", blurb());
1167 free (cmd2);
1168 }
1169 else
1170 {
1171 warning_dialog (GTK_WIDGET (button),
1172 _("Error:\n\nno `manualCommand' resource set."),
1173 D_NONE, 100);
1174 }
1175
1176 free (oname);
1177 }
1178
1179
1180 static void
force_list_select_item(state * s,GtkWidget * list,int list_elt,Bool scroll_p)1181 force_list_select_item (state *s, GtkWidget *list, int list_elt, Bool scroll_p)
1182 {
1183 GtkWidget *parent = name_to_widget (s, "scroller");
1184 gboolean was = GET_SENSITIVE (parent);
1185 #ifdef HAVE_GTK2
1186 GtkTreeIter iter;
1187 GtkTreeModel *model;
1188 GtkTreeSelection *selection;
1189 #endif /* HAVE_GTK2 */
1190
1191 if (!was) gtk_widget_set_sensitive (parent, True);
1192 #ifdef HAVE_GTK2
1193 model = gtk_tree_view_get_model (GTK_TREE_VIEW (list));
1194 g_assert (model);
1195 if (gtk_tree_model_iter_nth_child (model, &iter, NULL, list_elt))
1196 {
1197 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
1198 gtk_tree_selection_select_iter (selection, &iter);
1199 }
1200 #else /* !HAVE_GTK2 */
1201 gtk_list_select_item (GTK_LIST (list), list_elt);
1202 #endif /* !HAVE_GTK2 */
1203 if (scroll_p) ensure_selected_item_visible (GTK_WIDGET (list));
1204 if (!was) gtk_widget_set_sensitive (parent, False);
1205 }
1206
1207
1208 G_MODULE_EXPORT void
run_next_cb(GtkButton * button,gpointer user_data)1209 run_next_cb (GtkButton *button, gpointer user_data)
1210 {
1211 state *s = global_state_kludge; /* I hate C so much... */
1212 /* saver_preferences *p = &s->prefs; */
1213 Bool ops = s->preview_suppressed_p;
1214
1215 GtkWidget *list_widget = name_to_widget (s, "list");
1216 int list_elt = selected_list_element (s);
1217
1218 if (list_elt < 0)
1219 list_elt = 0;
1220 else
1221 list_elt++;
1222
1223 if (list_elt >= s->list_count)
1224 list_elt = 0;
1225
1226 s->preview_suppressed_p = True;
1227
1228 flush_dialog_changes_and_save (s);
1229 force_list_select_item (s, list_widget, list_elt, True);
1230 populate_demo_window (s, list_elt);
1231 run_hack (s, list_elt, False);
1232
1233 s->preview_suppressed_p = ops;
1234 }
1235
1236
1237 G_MODULE_EXPORT void
run_prev_cb(GtkButton * button,gpointer user_data)1238 run_prev_cb (GtkButton *button, gpointer user_data)
1239 {
1240 state *s = global_state_kludge; /* I hate C so much... */
1241 /* saver_preferences *p = &s->prefs; */
1242 Bool ops = s->preview_suppressed_p;
1243
1244 GtkWidget *list_widget = name_to_widget (s, "list");
1245 int list_elt = selected_list_element (s);
1246
1247 if (list_elt < 0)
1248 list_elt = s->list_count - 1;
1249 else
1250 list_elt--;
1251
1252 if (list_elt < 0)
1253 list_elt = s->list_count - 1;
1254
1255 s->preview_suppressed_p = True;
1256
1257 flush_dialog_changes_and_save (s);
1258 force_list_select_item (s, list_widget, list_elt, True);
1259 populate_demo_window (s, list_elt);
1260 run_hack (s, list_elt, False);
1261
1262 s->preview_suppressed_p = ops;
1263 }
1264
1265
1266 /* Writes the given settings into prefs.
1267 Returns true if there was a change, False otherwise.
1268 command and/or visual may be 0, or enabled_p may be -1, meaning "no change".
1269 */
1270 static Bool
flush_changes(state * s,int list_elt,int enabled_p,const char * command,const char * visual)1271 flush_changes (state *s,
1272 int list_elt,
1273 int enabled_p,
1274 const char *command,
1275 const char *visual)
1276 {
1277 saver_preferences *p = &s->prefs;
1278 Bool changed = False;
1279 screenhack *hack;
1280 int hack_number;
1281 if (list_elt < 0 || list_elt >= s->list_count)
1282 abort();
1283
1284 hack_number = s->list_elt_to_hack_number[list_elt];
1285 hack = p->screenhacks[hack_number];
1286
1287 if (enabled_p != -1 &&
1288 enabled_p != hack->enabled_p)
1289 {
1290 hack->enabled_p = enabled_p;
1291 changed = True;
1292 if (s->debug_p)
1293 fprintf (stderr, "%s: \"%s\": enabled => %d\n",
1294 blurb(), hack->name, enabled_p);
1295 }
1296
1297 if (command)
1298 {
1299 if (!hack->command || !!strcmp (command, hack->command))
1300 {
1301 if (hack->command) free (hack->command);
1302 hack->command = strdup (command);
1303 changed = True;
1304 if (s->debug_p)
1305 fprintf (stderr, "%s: \"%s\": command => \"%s\"\n",
1306 blurb(), hack->name, command);
1307 }
1308 }
1309
1310 if (visual)
1311 {
1312 const char *ov = hack->visual;
1313 if (!ov || !*ov) ov = "any";
1314 if (!*visual) visual = "any";
1315 if (!!strcasecmp (visual, ov))
1316 {
1317 if (hack->visual) free (hack->visual);
1318 hack->visual = strdup (visual);
1319 changed = True;
1320 if (s->debug_p)
1321 fprintf (stderr, "%s: \"%s\": visual => \"%s\"\n",
1322 blurb(), hack->name, visual);
1323 }
1324 }
1325
1326 return changed;
1327 }
1328
1329
1330 /* Helper for the text fields that contain time specifications:
1331 this parses the text, and does error checking.
1332 */
1333 static void
hack_time_text(state * s,const char * line,Time * store,Bool sec_p)1334 hack_time_text (state *s, const char *line, Time *store, Bool sec_p)
1335 {
1336 if (*line)
1337 {
1338 int value;
1339 if (!sec_p || strchr (line, ':'))
1340 value = parse_time ((char *) line, sec_p, True);
1341 else
1342 {
1343 char c;
1344 if (sscanf (line, "%d%c", &value, &c) != 1)
1345 value = -1;
1346 if (!sec_p)
1347 value *= 60;
1348 }
1349
1350 value *= 1000; /* Time measures in microseconds */
1351 if (value < 0)
1352 {
1353 char b[255];
1354 sprintf (b,
1355 _("Error:\n\n"
1356 "Unparsable time format: \"%s\"\n"),
1357 line);
1358 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
1359 }
1360 else
1361 *store = value;
1362 }
1363 }
1364
1365
1366 static Bool
directory_p(const char * path)1367 directory_p (const char *path)
1368 {
1369 struct stat st;
1370 if (!path || !*path)
1371 return False;
1372 else if (stat (path, &st))
1373 return False;
1374 else if (!S_ISDIR (st.st_mode))
1375 return False;
1376 else
1377 return True;
1378 }
1379
1380 static Bool
file_p(const char * path)1381 file_p (const char *path)
1382 {
1383 struct stat st;
1384 if (!path || !*path)
1385 return False;
1386 else if (stat (path, &st))
1387 return False;
1388 else if (S_ISDIR (st.st_mode))
1389 return False;
1390 else
1391 return True;
1392 }
1393
1394 static char *
normalize_directory(const char * path)1395 normalize_directory (const char *path)
1396 {
1397 int L;
1398 char *p2, *s;
1399 if (!path || !*path) return 0;
1400 L = strlen (path);
1401 p2 = (char *) malloc (L + 2);
1402 strcpy (p2, path);
1403 if (p2[L-1] == '/') /* remove trailing slash */
1404 p2[--L] = 0;
1405
1406 for (s = p2; s && *s; s++)
1407 {
1408 if (*s == '/' &&
1409 (!strncmp (s, "/../", 4) || /* delete "XYZ/../" */
1410 !strncmp (s, "/..\000", 4))) /* delete "XYZ/..$" */
1411 {
1412 char *s0 = s;
1413 while (s0 > p2 && s0[-1] != '/')
1414 s0--;
1415 if (s0 > p2)
1416 {
1417 s0--;
1418 s += 3;
1419 /* strcpy (s0, s); */
1420 memmove(s0, s, strlen(s) + 1);
1421 s = s0-1;
1422 }
1423 }
1424 else if (*s == '/' && !strncmp (s, "/./", 3)) { /* delete "/./" */
1425 /* strcpy (s, s+2), s--; */
1426 memmove(s, s+2, strlen(s+2) + 1);
1427 s--;
1428 }
1429 else if (*s == '/' && !strncmp (s, "/.\000", 3)) /* delete "/.$" */
1430 *s = 0, s--;
1431 }
1432
1433 /*
1434 Normalize consecutive slashes.
1435 Ignore doubled slashes after ":" to avoid mangling URLs.
1436 */
1437
1438 for (s = p2; s && *s; s++){
1439 if (*s == ':') continue;
1440 if (!s[1] || !s[2]) continue;
1441 while (s[1] == '/' && s[2] == '/')
1442 /* strcpy (s+1, s+2); */
1443 memmove (s+1, s+2, strlen(s+2) + 1);
1444 }
1445
1446 /* and strip trailing whitespace for good measure. */
1447 L = strlen(p2);
1448 while (isspace(p2[L-1]))
1449 p2[--L] = 0;
1450
1451 return p2;
1452 }
1453
1454
1455 #ifdef HAVE_GTK2
1456
1457 typedef struct {
1458 state *s;
1459 int i;
1460 Bool *changed;
1461 } FlushForeachClosure;
1462
1463 static gboolean
flush_checkbox(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)1464 flush_checkbox (GtkTreeModel *model,
1465 GtkTreePath *path,
1466 GtkTreeIter *iter,
1467 gpointer data)
1468 {
1469 FlushForeachClosure *closure = data;
1470 gboolean checked;
1471
1472 gtk_tree_model_get (model, iter,
1473 COL_ENABLED, &checked,
1474 -1);
1475
1476 if (flush_changes (closure->s, closure->i,
1477 checked, 0, 0))
1478 *closure->changed = True;
1479
1480 closure->i++;
1481
1482 /* don't remove row */
1483 return FALSE;
1484 }
1485
1486 #endif /* HAVE_GTK2 */
1487
1488 /* Flush out any changes made in the main dialog window (where changes
1489 take place immediately: clicking on a checkbox causes the init file
1490 to be written right away.)
1491 */
1492 static Bool
flush_dialog_changes_and_save(state * s)1493 flush_dialog_changes_and_save (state *s)
1494 {
1495 saver_preferences *p = &s->prefs;
1496 saver_preferences P2, *p2 = &P2;
1497 #ifdef HAVE_GTK2
1498 GtkTreeView *list_widget = GTK_TREE_VIEW (name_to_widget (s, "list"));
1499 GtkTreeModel *model = gtk_tree_view_get_model (list_widget);
1500 FlushForeachClosure closure;
1501 #else /* !HAVE_GTK2 */
1502 GtkList *list_widget = GTK_LIST (name_to_widget (s, "list"));
1503 GList *kids = gtk_container_children (GTK_CONTAINER (list_widget));
1504 int i;
1505 #endif /* !HAVE_GTK2 */
1506 static Bool already_warned_about_missing_image_directory = False; /* very long name... */
1507
1508 Bool changed = False;
1509 GtkWidget *w;
1510
1511 if (s->saving_p) return False;
1512 s->saving_p = True;
1513
1514 *p2 = *p;
1515
1516 /* Flush any checkbox changes in the list down into the prefs struct.
1517 */
1518 #ifdef HAVE_GTK2
1519 closure.s = s;
1520 closure.changed = &changed;
1521 closure.i = 0;
1522 gtk_tree_model_foreach (model, flush_checkbox, &closure);
1523
1524 #else /* !HAVE_GTK2 */
1525
1526 for (i = 0; kids; kids = kids->next, i++)
1527 {
1528 GtkWidget *line = GTK_WIDGET (kids->data);
1529 GtkWidget *line_hbox = GTK_WIDGET (GTK_BIN (line)->child);
1530 GtkWidget *line_check =
1531 GTK_WIDGET (gtk_container_children (GTK_CONTAINER (line_hbox))->data);
1532 Bool checked =
1533 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (line_check));
1534
1535 if (flush_changes (s, i, (checked ? 1 : 0), 0, 0))
1536 changed = True;
1537 }
1538 #endif /* ~HAVE_GTK2 */
1539
1540 /* Flush the non-hack-specific settings down into the prefs struct.
1541 */
1542
1543 # define SECONDS(FIELD,NAME) \
1544 w = name_to_widget (s, (NAME)); \
1545 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), True)
1546
1547 # define MINUTES(FIELD,NAME) \
1548 w = name_to_widget (s, (NAME)); \
1549 hack_time_text (s, gtk_entry_get_text (GTK_ENTRY (w)), (FIELD), False)
1550
1551 # define CHECKBOX(FIELD,NAME) \
1552 w = name_to_widget (s, (NAME)); \
1553 (FIELD) = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))
1554
1555 # define PATHNAME(FIELD,NAME) \
1556 w = name_to_widget (s, (NAME)); \
1557 (FIELD) = normalize_directory (gtk_entry_get_text (GTK_ENTRY (w)))
1558
1559 # define TEXT(FIELD,NAME) \
1560 w = name_to_widget (s, (NAME)); \
1561 (FIELD) = (char *) g_strdup(gtk_entry_get_text (GTK_ENTRY (w)))
1562
1563 MINUTES (&p2->timeout, "timeout_spinbutton");
1564 MINUTES (&p2->cycle, "cycle_spinbutton");
1565 CHECKBOX (p2->lock_p, "lock_button");
1566 MINUTES (&p2->lock_timeout, "lock_spinbutton");
1567
1568 CHECKBOX (p2->dpms_enabled_p, "dpms_button");
1569 CHECKBOX (p2->dpms_quickoff_p, "dpms_quickoff_button");
1570 MINUTES (&p2->dpms_standby, "dpms_standby_spinbutton");
1571 MINUTES (&p2->dpms_suspend, "dpms_suspend_spinbutton");
1572 MINUTES (&p2->dpms_off, "dpms_off_spinbutton");
1573
1574 CHECKBOX (p2->grab_desktop_p, "grab_desk_button");
1575 CHECKBOX (p2->grab_video_p, "grab_video_button");
1576 CHECKBOX (p2->random_image_p, "grab_image_button");
1577 PATHNAME (p2->image_directory, "image_text");
1578
1579 #if 0
1580 CHECKBOX (p2->verbose_p, "verbose_button");
1581 CHECKBOX (p2->capture_stderr_p, "capture_button");
1582 CHECKBOX (p2->splash_p, "splash_button");
1583 #endif
1584
1585 {
1586 Bool v = False;
1587 CHECKBOX (v, "text_host_radio"); if (v) p2->tmode = TEXT_DATE;
1588 CHECKBOX (v, "text_radio"); if (v) p2->tmode = TEXT_LITERAL;
1589 CHECKBOX (v, "text_file_radio"); if (v) p2->tmode = TEXT_FILE;
1590 CHECKBOX (v, "text_program_radio"); if (v) p2->tmode = TEXT_PROGRAM;
1591 CHECKBOX (v, "text_url_radio"); if (v) p2->tmode = TEXT_URL;
1592 TEXT (p2->text_literal, "text_entry");
1593 PATHNAME (p2->text_file, "text_file_entry");
1594 PATHNAME (p2->text_program, "text_program_entry");
1595 PATHNAME (p2->text_program, "text_program_entry");
1596 TEXT (p2->text_url, "text_url_entry");
1597 }
1598
1599 CHECKBOX (p2->install_cmap_p, "install_button");
1600 CHECKBOX (p2->fade_p, "fade_button");
1601 CHECKBOX (p2->unfade_p, "unfade_button");
1602 SECONDS (&p2->fade_seconds, "fade_spinbutton");
1603
1604 # undef SECONDS
1605 # undef MINUTES
1606 # undef CHECKBOX
1607 # undef PATHNAME
1608 # undef TEXT
1609
1610 /* Warn if the image directory doesn't exist, when:
1611 - not being warned before
1612 - image directory is changed and the directory doesn't exist
1613 - image directory does not begin with http://
1614 */
1615 if (p2->image_directory &&
1616 *p2->image_directory &&
1617 !directory_p (p2->image_directory) &&
1618 strncmp(p2->image_directory, "http://", 6) &&
1619 ( !already_warned_about_missing_image_directory ||
1620 ( p->image_directory &&
1621 *p->image_directory &&
1622 strcmp(p->image_directory, p2->image_directory)
1623 )
1624 )
1625 )
1626 {
1627 char b[255];
1628 sprintf (b, "Warning:\n\n" "Directory does not exist: \"%s\"\n",
1629 p2->image_directory);
1630 if (warning_dialog (s->toplevel_widget, b, D_NONE, 100))
1631 already_warned_about_missing_image_directory = True;
1632 }
1633
1634
1635 /* Map the mode menu to `saver_mode' enum values. */
1636 {
1637 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
1638 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
1639 GtkWidget *selected = gtk_menu_get_active (menu);
1640 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
1641 int menu_elt = g_list_index (kids, (gpointer) selected);
1642 if (menu_elt < 0 || menu_elt >= countof(mode_menu_order)) abort();
1643 p2->mode = mode_menu_order[menu_elt];
1644 }
1645
1646 if (p2->mode == ONE_HACK)
1647 {
1648 int list_elt = selected_list_element (s);
1649 p2->selected_hack = (list_elt >= 0
1650 ? s->list_elt_to_hack_number[list_elt]
1651 : -1);
1652 }
1653
1654 # define COPY(field, name) \
1655 if (p->field != p2->field) { \
1656 changed = True; \
1657 if (s->debug_p) \
1658 fprintf (stderr, "%s: %s => %d\n", blurb(), name, (int) p2->field); \
1659 } \
1660 p->field = p2->field
1661
1662 COPY(mode, "mode");
1663 COPY(selected_hack, "selected_hack");
1664
1665 COPY(timeout, "timeout");
1666 COPY(cycle, "cycle");
1667 COPY(lock_p, "lock_p");
1668 COPY(lock_timeout, "lock_timeout");
1669
1670 COPY(dpms_enabled_p, "dpms_enabled_p");
1671 COPY(dpms_quickoff_p, "dpms_quickoff_enabled_p");
1672 COPY(dpms_standby, "dpms_standby");
1673 COPY(dpms_suspend, "dpms_suspend");
1674 COPY(dpms_off, "dpms_off");
1675
1676 #if 0
1677 COPY(verbose_p, "verbose_p");
1678 COPY(capture_stderr_p, "capture_stderr_p");
1679 COPY(splash_p, "splash_p");
1680 #endif
1681
1682 COPY(tmode, "tmode");
1683
1684 COPY(install_cmap_p, "install_cmap_p");
1685 COPY(fade_p, "fade_p");
1686 COPY(unfade_p, "unfade_p");
1687 COPY(fade_seconds, "fade_seconds");
1688
1689 COPY(grab_desktop_p, "grab_desktop_p");
1690 COPY(grab_video_p, "grab_video_p");
1691 COPY(random_image_p, "random_image_p");
1692
1693 # undef COPY
1694
1695 # define COPYSTR(FIELD,NAME) \
1696 if (!p->FIELD || \
1697 !p2->FIELD || \
1698 strcmp(p->FIELD, p2->FIELD)) \
1699 { \
1700 changed = True; \
1701 if (s->debug_p) \
1702 fprintf (stderr, "%s: %s => \"%s\"\n", blurb(), NAME, p2->FIELD); \
1703 } \
1704 if (p->FIELD && p->FIELD != p2->FIELD) \
1705 free (p->FIELD); \
1706 p->FIELD = p2->FIELD; \
1707 p2->FIELD = 0
1708
1709 COPYSTR(image_directory, "image_directory");
1710 COPYSTR(text_literal, "text_literal");
1711 COPYSTR(text_file, "text_file");
1712 COPYSTR(text_program, "text_program");
1713 COPYSTR(text_url, "text_url");
1714 # undef COPYSTR
1715
1716 populate_prefs_page (s);
1717
1718 if (changed)
1719 {
1720 Display *dpy = GDK_DISPLAY();
1721 Bool enabled_p = (p->dpms_enabled_p && p->mode != DONT_BLANK);
1722 sync_server_dpms_settings (dpy, enabled_p, p->dpms_quickoff_p,
1723 p->dpms_standby / 1000,
1724 p->dpms_suspend / 1000,
1725 p->dpms_off / 1000,
1726 False);
1727
1728 changed = demo_write_init_file (s, p);
1729 }
1730
1731 s->saving_p = False;
1732 return changed;
1733 }
1734
1735
1736 /* Flush out any changes made in the popup dialog box (where changes
1737 take place only when the OK button is clicked.)
1738 */
1739 static Bool
flush_popup_changes_and_save(state * s)1740 flush_popup_changes_and_save (state *s)
1741 {
1742 Bool changed = False;
1743 saver_preferences *p = &s->prefs;
1744 int list_elt = selected_list_element (s);
1745
1746 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
1747 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
1748
1749 const char *visual = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (vis)->entry));
1750 const char *command = gtk_entry_get_text (cmd);
1751
1752 char c;
1753 unsigned long id;
1754
1755 if (s->saving_p) return False;
1756 s->saving_p = True;
1757
1758 if (list_elt < 0)
1759 goto DONE;
1760
1761 if (maybe_reload_init_file (s) != 0)
1762 {
1763 changed = True;
1764 goto DONE;
1765 }
1766
1767 /* Sanity-check and canonicalize whatever the user typed into the combo box.
1768 */
1769 if (!strcasecmp (visual, "")) visual = "";
1770 else if (!strcasecmp (visual, "any")) visual = "";
1771 else if (!strcasecmp (visual, "default")) visual = "Default";
1772 else if (!strcasecmp (visual, "default-n")) visual = "Default-N";
1773 else if (!strcasecmp (visual, "default-i")) visual = "Default-I";
1774 else if (!strcasecmp (visual, "best")) visual = "Best";
1775 else if (!strcasecmp (visual, "mono")) visual = "Mono";
1776 else if (!strcasecmp (visual, "monochrome")) visual = "Mono";
1777 else if (!strcasecmp (visual, "gray")) visual = "Gray";
1778 else if (!strcasecmp (visual, "grey")) visual = "Gray";
1779 else if (!strcasecmp (visual, "color")) visual = "Color";
1780 else if (!strcasecmp (visual, "gl")) visual = "GL";
1781 else if (!strcasecmp (visual, "staticgray")) visual = "StaticGray";
1782 else if (!strcasecmp (visual, "staticcolor")) visual = "StaticColor";
1783 else if (!strcasecmp (visual, "truecolor")) visual = "TrueColor";
1784 else if (!strcasecmp (visual, "grayscale")) visual = "GrayScale";
1785 else if (!strcasecmp (visual, "greyscale")) visual = "GrayScale";
1786 else if (!strcasecmp (visual, "pseudocolor")) visual = "PseudoColor";
1787 else if (!strcasecmp (visual, "directcolor")) visual = "DirectColor";
1788 else if (1 == sscanf (visual, " %lu %c", &id, &c)) ;
1789 else if (1 == sscanf (visual, " 0x%lx %c", &id, &c)) ;
1790 else
1791 {
1792 gdk_beep (); /* unparsable */
1793 visual = "";
1794 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry), _("Any"));
1795 }
1796
1797 changed = flush_changes (s, list_elt, -1, command, visual);
1798 if (changed)
1799 {
1800 changed = demo_write_init_file (s, p);
1801
1802 /* Do this to re-launch the hack if (and only if) the command line
1803 has changed. */
1804 populate_demo_window (s, selected_list_element (s));
1805 }
1806
1807 DONE:
1808 s->saving_p = False;
1809 return changed;
1810 }
1811
1812
1813 G_MODULE_EXPORT void
pref_changed_cb(GtkWidget * widget,gpointer user_data)1814 pref_changed_cb (GtkWidget *widget, gpointer user_data)
1815 {
1816 state *s = global_state_kludge; /* I hate C so much... */
1817 if (! s->initializing_p)
1818 {
1819 s->initializing_p = True;
1820 flush_dialog_changes_and_save (s);
1821 s->initializing_p = False;
1822 }
1823 }
1824
1825 G_MODULE_EXPORT gboolean
pref_changed_event_cb(GtkWidget * widget,GdkEvent * event,gpointer user_data)1826 pref_changed_event_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
1827 {
1828 pref_changed_cb (widget, user_data);
1829 return FALSE;
1830 }
1831
1832 /* Callback on menu items in the "mode" options menu.
1833 */
1834 G_MODULE_EXPORT void
mode_menu_item_cb(GtkWidget * widget,gpointer user_data)1835 mode_menu_item_cb (GtkWidget *widget, gpointer user_data)
1836 {
1837 state *s = (state *) user_data;
1838 saver_preferences *p = &s->prefs;
1839 GtkWidget *list = name_to_widget (s, "list");
1840 int list_elt;
1841
1842 GList *menu_items =
1843 gtk_container_children (GTK_CONTAINER (GET_PARENT (widget)));
1844 int menu_index = 0;
1845 saver_mode new_mode;
1846
1847 while (menu_items)
1848 {
1849 if (menu_items->data == widget)
1850 break;
1851 menu_index++;
1852 menu_items = menu_items->next;
1853 }
1854 if (!menu_items) abort();
1855
1856 new_mode = mode_menu_order[menu_index];
1857
1858 /* Keep the same list element displayed as before; except if we're
1859 switching *to* "one screensaver" mode from any other mode, set
1860 "the one" to be that which is currently selected.
1861 */
1862 list_elt = selected_list_element (s);
1863 if (new_mode == ONE_HACK)
1864 p->selected_hack = s->list_elt_to_hack_number[list_elt];
1865
1866 {
1867 saver_mode old_mode = p->mode;
1868 p->mode = new_mode;
1869 populate_demo_window (s, list_elt);
1870 force_list_select_item (s, list, list_elt, True);
1871 p->mode = old_mode; /* put it back, so the init file gets written */
1872 }
1873
1874 pref_changed_cb (widget, user_data);
1875 }
1876
1877
1878 G_MODULE_EXPORT void
switch_page_cb(GtkNotebook * notebook,GtkNotebookPage * page,gint page_num,gpointer user_data)1879 switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
1880 gint page_num, gpointer user_data)
1881 {
1882 state *s = global_state_kludge; /* I hate C so much... */
1883 pref_changed_cb (GTK_WIDGET (notebook), user_data);
1884
1885 /* If we're switching to page 0, schedule the current hack to be run.
1886 Otherwise, schedule it to stop. */
1887 if (page_num == 0)
1888 populate_demo_window (s, selected_list_element (s));
1889 else
1890 schedule_preview (s, 0);
1891 }
1892
1893 #ifdef HAVE_GTK2
1894 static void
list_activated_cb(GtkTreeView * list,GtkTreePath * path,GtkTreeViewColumn * column,gpointer data)1895 list_activated_cb (GtkTreeView *list,
1896 GtkTreePath *path,
1897 GtkTreeViewColumn *column,
1898 gpointer data)
1899 {
1900 state *s = data;
1901 char *str;
1902 int list_elt;
1903
1904 g_return_if_fail (!gdk_pointer_is_grabbed ());
1905
1906 str = gtk_tree_path_to_string (path);
1907 list_elt = strtol (str, NULL, 10);
1908 g_free (str);
1909
1910 if (list_elt >= 0)
1911 run_hack (s, list_elt, True);
1912 }
1913
1914 static void
list_select_changed_cb(GtkTreeSelection * selection,gpointer data)1915 list_select_changed_cb (GtkTreeSelection *selection, gpointer data)
1916 {
1917 state *s = (state *)data;
1918 GtkTreeModel *model;
1919 GtkTreeIter iter;
1920 GtkTreePath *path;
1921 char *str;
1922 int list_elt;
1923
1924 if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1925 return;
1926
1927 path = gtk_tree_model_get_path (model, &iter);
1928 str = gtk_tree_path_to_string (path);
1929 list_elt = strtol (str, NULL, 10);
1930
1931 gtk_tree_path_free (path);
1932 g_free (str);
1933
1934 populate_demo_window (s, list_elt);
1935 flush_dialog_changes_and_save (s);
1936
1937 /* Re-populate the Settings window any time a new item is selected
1938 in the list, in case both windows are currently visible.
1939 */
1940 populate_popup_window (s);
1941 }
1942
1943 #else /* !HAVE_GTK2 */
1944
1945 static time_t last_doubleclick_time = 0; /* FMH! This is to suppress the
1946 list_select_cb that comes in
1947 *after* we've double-clicked.
1948 */
1949
1950 static gint
list_doubleclick_cb(GtkWidget * button,GdkEventButton * event,gpointer data)1951 list_doubleclick_cb (GtkWidget *button, GdkEventButton *event,
1952 gpointer data)
1953 {
1954 state *s = (state *) data;
1955 if (event->type == GDK_2BUTTON_PRESS)
1956 {
1957 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
1958 int list_elt = gtk_list_child_position (list, GTK_WIDGET (button));
1959
1960 last_doubleclick_time = time ((time_t *) 0);
1961
1962 if (list_elt >= 0)
1963 run_hack (s, list_elt, True);
1964 }
1965
1966 return FALSE;
1967 }
1968
1969
1970 static void
list_select_cb(GtkList * list,GtkWidget * child,gpointer data)1971 list_select_cb (GtkList *list, GtkWidget *child, gpointer data)
1972 {
1973 state *s = (state *) data;
1974 time_t now = time ((time_t *) 0);
1975
1976 if (now >= last_doubleclick_time + 2)
1977 {
1978 int list_elt = gtk_list_child_position (list, GTK_WIDGET (child));
1979 populate_demo_window (s, list_elt);
1980 flush_dialog_changes_and_save (s);
1981 }
1982 }
1983
1984 static void
list_unselect_cb(GtkList * list,GtkWidget * child,gpointer data)1985 list_unselect_cb (GtkList *list, GtkWidget *child, gpointer data)
1986 {
1987 state *s = (state *) data;
1988 populate_demo_window (s, -1);
1989 flush_dialog_changes_and_save (s);
1990 }
1991
1992 #endif /* !HAVE_GTK2 */
1993
1994
1995 /* Called when the checkboxes that are in the left column of the
1996 scrolling list are clicked. This both populates the right pane
1997 (just as clicking on the label (really, listitem) does) and
1998 also syncs this checkbox with the right pane Enabled checkbox.
1999 */
2000 static void
list_checkbox_cb(GtkCellRendererToggle * toggle,gchar * path_string,gpointer data)2001 list_checkbox_cb (
2002 #ifdef HAVE_GTK2
2003 GtkCellRendererToggle *toggle,
2004 gchar *path_string,
2005 #else /* !HAVE_GTK2 */
2006 GtkWidget *cb,
2007 #endif /* !HAVE_GTK2 */
2008 gpointer data)
2009 {
2010 state *s = (state *) data;
2011
2012 #ifdef HAVE_GTK2
2013 GtkScrolledWindow *scroller =
2014 GTK_SCROLLED_WINDOW (name_to_widget (s, "scroller"));
2015 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2016 GtkTreeModel *model = gtk_tree_view_get_model (list);
2017 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
2018 GtkTreeIter iter;
2019 gboolean active;
2020 #else /* !HAVE_GTK2 */
2021 GtkWidget *line_hbox = GTK_WIDGET (cb)->parent;
2022 GtkWidget *line = GTK_WIDGET (line_hbox)->parent;
2023
2024 GtkList *list = GTK_LIST (GTK_WIDGET (line)->parent);
2025 GtkViewport *vp = GTK_VIEWPORT (GTK_WIDGET (list)->parent);
2026 GtkScrolledWindow *scroller = GTK_SCROLLED_WINDOW (GTK_WIDGET (vp)->parent);
2027 #endif /* !HAVE_GTK2 */
2028 GtkAdjustment *adj;
2029 double scroll_top;
2030
2031 int list_elt;
2032
2033 #ifdef HAVE_GTK2
2034 if (!gtk_tree_model_get_iter (model, &iter, path))
2035 {
2036 g_warning ("bad path: %s", path_string);
2037 return;
2038 }
2039 gtk_tree_path_free (path);
2040
2041 gtk_tree_model_get (model, &iter,
2042 COL_ENABLED, &active,
2043 -1);
2044
2045 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
2046 COL_ENABLED, !active,
2047 -1);
2048
2049 list_elt = strtol (path_string, NULL, 10);
2050 #else /* !HAVE_GTK2 */
2051 list_elt = gtk_list_child_position (list, line);
2052 #endif /* !HAVE_GTK2 */
2053
2054 /* remember previous scroll position of the top of the list */
2055 adj = gtk_scrolled_window_get_vadjustment (scroller);
2056 scroll_top = GET_ADJ_VALUE (adj);
2057
2058 flush_dialog_changes_and_save (s);
2059 force_list_select_item (s, GTK_WIDGET (list), list_elt, False);
2060 populate_demo_window (s, list_elt);
2061
2062 /* restore the previous scroll position of the top of the list.
2063 this is weak, but I don't really know why it's moving... */
2064 gtk_adjustment_set_value (adj, scroll_top);
2065 }
2066
2067
2068 typedef struct {
2069 state *state;
2070 GtkFileSelection *widget;
2071 } file_selection_data;
2072
2073
2074
2075 static void
store_image_directory(GtkWidget * button,gpointer user_data)2076 store_image_directory (GtkWidget *button, gpointer user_data)
2077 {
2078 file_selection_data *fsd = (file_selection_data *) user_data;
2079 state *s = fsd->state;
2080 GtkFileSelection *selector = fsd->widget;
2081 GtkWidget *top = s->toplevel_widget;
2082 saver_preferences *p = &s->prefs;
2083 const char *path = gtk_file_selection_get_filename (selector);
2084
2085 if (p->image_directory && !strcmp(p->image_directory, path))
2086 return; /* no change */
2087
2088 /* No warning for URLs. */
2089 if ((!directory_p (path)) && strncmp(path, "http://", 6))
2090 {
2091 char b[255];
2092 sprintf (b, _("Error:\n\n" "Directory does not exist: \"%s\"\n"), path);
2093 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2094 return;
2095 }
2096
2097 if (p->image_directory) free (p->image_directory);
2098 p->image_directory = normalize_directory (path);
2099
2100 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2101 (p->image_directory ? p->image_directory : ""));
2102 demo_write_init_file (s, p);
2103 }
2104
2105
2106 static void
store_text_file(GtkWidget * button,gpointer user_data)2107 store_text_file (GtkWidget *button, gpointer user_data)
2108 {
2109 file_selection_data *fsd = (file_selection_data *) user_data;
2110 state *s = fsd->state;
2111 GtkFileSelection *selector = fsd->widget;
2112 GtkWidget *top = s->toplevel_widget;
2113 saver_preferences *p = &s->prefs;
2114 const char *path = gtk_file_selection_get_filename (selector);
2115
2116 if (p->text_file && !strcmp(p->text_file, path))
2117 return; /* no change */
2118
2119 if (!file_p (path))
2120 {
2121 char b[255];
2122 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2123 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2124 return;
2125 }
2126
2127 if (p->text_file) free (p->text_file);
2128 p->text_file = normalize_directory (path);
2129
2130 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2131 (p->text_file ? p->text_file : ""));
2132 demo_write_init_file (s, p);
2133 }
2134
2135
2136 static void
store_text_program(GtkWidget * button,gpointer user_data)2137 store_text_program (GtkWidget *button, gpointer user_data)
2138 {
2139 file_selection_data *fsd = (file_selection_data *) user_data;
2140 state *s = fsd->state;
2141 GtkFileSelection *selector = fsd->widget;
2142 /*GtkWidget *top = s->toplevel_widget;*/
2143 saver_preferences *p = &s->prefs;
2144 const char *path = gtk_file_selection_get_filename (selector);
2145
2146 if (p->text_program && !strcmp(p->text_program, path))
2147 return; /* no change */
2148
2149 # if 0
2150 if (!file_p (path))
2151 {
2152 char b[255];
2153 sprintf (b, _("Error:\n\n" "File does not exist: \"%s\"\n"), path);
2154 warning_dialog (GTK_WIDGET (top), b, D_NONE, 100);
2155 return;
2156 }
2157 # endif
2158
2159 if (p->text_program) free (p->text_program);
2160 p->text_program = normalize_directory (path);
2161
2162 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2163 (p->text_program ? p->text_program : ""));
2164 demo_write_init_file (s, p);
2165 }
2166
2167
2168
2169 static void
browse_image_dir_cancel(GtkWidget * button,gpointer user_data)2170 browse_image_dir_cancel (GtkWidget *button, gpointer user_data)
2171 {
2172 file_selection_data *fsd = (file_selection_data *) user_data;
2173 gtk_widget_hide (GTK_WIDGET (fsd->widget));
2174 }
2175
2176 static void
browse_image_dir_ok(GtkWidget * button,gpointer user_data)2177 browse_image_dir_ok (GtkWidget *button, gpointer user_data)
2178 {
2179 browse_image_dir_cancel (button, user_data);
2180 store_image_directory (button, user_data);
2181 }
2182
2183 static void
browse_text_file_ok(GtkWidget * button,gpointer user_data)2184 browse_text_file_ok (GtkWidget *button, gpointer user_data)
2185 {
2186 browse_image_dir_cancel (button, user_data);
2187 store_text_file (button, user_data);
2188 }
2189
2190 static void
browse_text_program_ok(GtkWidget * button,gpointer user_data)2191 browse_text_program_ok (GtkWidget *button, gpointer user_data)
2192 {
2193 browse_image_dir_cancel (button, user_data);
2194 store_text_program (button, user_data);
2195 }
2196
2197 static void
browse_image_dir_close(GtkWidget * widget,GdkEvent * event,gpointer user_data)2198 browse_image_dir_close (GtkWidget *widget, GdkEvent *event, gpointer user_data)
2199 {
2200 browse_image_dir_cancel (widget, user_data);
2201 }
2202
2203
2204 G_MODULE_EXPORT void
browse_image_dir_cb(GtkButton * button,gpointer user_data)2205 browse_image_dir_cb (GtkButton *button, gpointer user_data)
2206 {
2207 state *s = global_state_kludge; /* I hate C so much... */
2208 saver_preferences *p = &s->prefs;
2209 static file_selection_data *fsd = 0;
2210
2211 GtkFileSelection *selector = GTK_FILE_SELECTION(
2212 gtk_file_selection_new ("Please select the image directory."));
2213
2214 if (!fsd)
2215 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2216
2217 fsd->widget = selector;
2218 fsd->state = s;
2219
2220 if (p->image_directory && *p->image_directory)
2221 gtk_file_selection_set_filename (selector, p->image_directory);
2222
2223 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2224 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_ok),
2225 (gpointer *) fsd);
2226 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2227 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2228 (gpointer *) fsd);
2229 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2230 GTK_SIGNAL_FUNC (browse_image_dir_close),
2231 (gpointer *) fsd);
2232
2233 gtk_widget_set_sensitive (GTK_WIDGET (selector->file_list), False);
2234
2235 gtk_window_set_modal (GTK_WINDOW (selector), True);
2236 gtk_widget_show (GTK_WIDGET (selector));
2237 }
2238
2239
2240 G_MODULE_EXPORT void
browse_text_file_cb(GtkButton * button,gpointer user_data)2241 browse_text_file_cb (GtkButton *button, gpointer user_data)
2242 {
2243 state *s = global_state_kludge; /* I hate C so much... */
2244 saver_preferences *p = &s->prefs;
2245 static file_selection_data *fsd = 0;
2246
2247 GtkFileSelection *selector = GTK_FILE_SELECTION(
2248 gtk_file_selection_new ("Please select a text file."));
2249
2250 if (!fsd)
2251 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2252
2253 fsd->widget = selector;
2254 fsd->state = s;
2255
2256 if (p->text_file && *p->text_file)
2257 gtk_file_selection_set_filename (selector, p->text_file);
2258
2259 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2260 "clicked", GTK_SIGNAL_FUNC (browse_text_file_ok),
2261 (gpointer *) fsd);
2262 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2263 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2264 (gpointer *) fsd);
2265 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2266 GTK_SIGNAL_FUNC (browse_image_dir_close),
2267 (gpointer *) fsd);
2268
2269 gtk_window_set_modal (GTK_WINDOW (selector), True);
2270 gtk_widget_show (GTK_WIDGET (selector));
2271 }
2272
2273
2274 G_MODULE_EXPORT void
browse_text_program_cb(GtkButton * button,gpointer user_data)2275 browse_text_program_cb (GtkButton *button, gpointer user_data)
2276 {
2277 state *s = global_state_kludge; /* I hate C so much... */
2278 saver_preferences *p = &s->prefs;
2279 static file_selection_data *fsd = 0;
2280
2281 GtkFileSelection *selector = GTK_FILE_SELECTION(
2282 gtk_file_selection_new ("Please select a text-generating program."));
2283
2284 if (!fsd)
2285 fsd = (file_selection_data *) malloc (sizeof (*fsd));
2286
2287 fsd->widget = selector;
2288 fsd->state = s;
2289
2290 if (p->text_program && *p->text_program)
2291 gtk_file_selection_set_filename (selector, p->text_program);
2292
2293 gtk_signal_connect (GTK_OBJECT (selector->ok_button),
2294 "clicked", GTK_SIGNAL_FUNC (browse_text_program_ok),
2295 (gpointer *) fsd);
2296 gtk_signal_connect (GTK_OBJECT (selector->cancel_button),
2297 "clicked", GTK_SIGNAL_FUNC (browse_image_dir_cancel),
2298 (gpointer *) fsd);
2299 gtk_signal_connect (GTK_OBJECT (selector), "delete_event",
2300 GTK_SIGNAL_FUNC (browse_image_dir_close),
2301 (gpointer *) fsd);
2302
2303 gtk_window_set_modal (GTK_WINDOW (selector), True);
2304 gtk_widget_show (GTK_WIDGET (selector));
2305 }
2306
2307
2308
2309
2310
2311 G_MODULE_EXPORT void
settings_cb(GtkButton * button,gpointer user_data)2312 settings_cb (GtkButton *button, gpointer user_data)
2313 {
2314 state *s = global_state_kludge; /* I hate C so much... */
2315 int list_elt = selected_list_element (s);
2316
2317 populate_demo_window (s, list_elt); /* reset the widget */
2318 populate_popup_window (s); /* create UI on popup window */
2319 gtk_widget_show (s->popup_widget);
2320 }
2321
2322 static void
settings_sync_cmd_text(state * s)2323 settings_sync_cmd_text (state *s)
2324 {
2325 # ifdef HAVE_XML
2326 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2327 char *cmd_line = get_configurator_command_line (s->cdata, False);
2328 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2329 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2330 free (cmd_line);
2331 # endif /* HAVE_XML */
2332 }
2333
2334 G_MODULE_EXPORT void
settings_adv_cb(GtkButton * button,gpointer user_data)2335 settings_adv_cb (GtkButton *button, gpointer user_data)
2336 {
2337 state *s = global_state_kludge; /* I hate C so much... */
2338 GtkNotebook *notebook =
2339 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2340
2341 settings_sync_cmd_text (s);
2342 gtk_notebook_set_page (notebook, 1);
2343 }
2344
2345 G_MODULE_EXPORT void
settings_std_cb(GtkButton * button,gpointer user_data)2346 settings_std_cb (GtkButton *button, gpointer user_data)
2347 {
2348 state *s = global_state_kludge; /* I hate C so much... */
2349 GtkNotebook *notebook =
2350 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2351
2352 /* Re-create UI to reflect the in-progress command-line settings. */
2353 populate_popup_window (s);
2354
2355 gtk_notebook_set_page (notebook, 0);
2356 }
2357
2358 G_MODULE_EXPORT void
settings_reset_cb(GtkButton * button,gpointer user_data)2359 settings_reset_cb (GtkButton *button, gpointer user_data)
2360 {
2361 # ifdef HAVE_XML
2362 state *s = global_state_kludge; /* I hate C so much... */
2363 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2364 char *cmd_line = get_configurator_command_line (s->cdata, True);
2365 gtk_entry_set_text (GTK_ENTRY (cmd), cmd_line);
2366 gtk_entry_set_position (GTK_ENTRY (cmd), strlen (cmd_line));
2367 free (cmd_line);
2368 populate_popup_window (s);
2369 # endif /* HAVE_XML */
2370 }
2371
2372 G_MODULE_EXPORT void
settings_switch_page_cb(GtkNotebook * notebook,GtkNotebookPage * page,gint page_num,gpointer user_data)2373 settings_switch_page_cb (GtkNotebook *notebook, GtkNotebookPage *page,
2374 gint page_num, gpointer user_data)
2375 {
2376 state *s = global_state_kludge; /* I hate C so much... */
2377 GtkWidget *adv = name_to_widget (s, "adv_button");
2378 GtkWidget *std = name_to_widget (s, "std_button");
2379
2380 if (page_num == 0)
2381 {
2382 gtk_widget_show (adv);
2383 gtk_widget_hide (std);
2384 }
2385 else if (page_num == 1)
2386 {
2387 gtk_widget_hide (adv);
2388 gtk_widget_show (std);
2389 }
2390 else
2391 abort();
2392 }
2393
2394
2395
2396 G_MODULE_EXPORT void
settings_cancel_cb(GtkButton * button,gpointer user_data)2397 settings_cancel_cb (GtkButton *button, gpointer user_data)
2398 {
2399 state *s = global_state_kludge; /* I hate C so much... */
2400 gtk_widget_hide (s->popup_widget);
2401 }
2402
2403 G_MODULE_EXPORT void
settings_ok_cb(GtkButton * button,gpointer user_data)2404 settings_ok_cb (GtkButton *button, gpointer user_data)
2405 {
2406 state *s = global_state_kludge; /* I hate C so much... */
2407 GtkNotebook *notebook = GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
2408 int page = gtk_notebook_get_current_page (notebook);
2409
2410 if (page == 0)
2411 /* Regenerate the command-line from the widget contents before saving.
2412 But don't do this if we're looking at the command-line page already,
2413 or we will blow away what they typed... */
2414 settings_sync_cmd_text (s);
2415
2416 flush_popup_changes_and_save (s);
2417 gtk_widget_hide (s->popup_widget);
2418 }
2419
2420 static gboolean
wm_popup_close_cb(GtkWidget * widget,GdkEvent * event,gpointer data)2421 wm_popup_close_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
2422 {
2423 state *s = (state *) data;
2424 settings_cancel_cb (0, (gpointer) s);
2425 return TRUE;
2426 }
2427
2428
2429
2430 /* Populating the various widgets
2431 */
2432
2433
2434 /* Returns the number of the last hack run by the server.
2435 */
2436 static int
server_current_hack(void)2437 server_current_hack (void)
2438 {
2439 Atom type;
2440 int format;
2441 unsigned long nitems, bytesafter;
2442 unsigned char *dataP = 0;
2443 Display *dpy = GDK_DISPLAY();
2444 int hack_number = -1;
2445
2446 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
2447 XA_SCREENSAVER_STATUS,
2448 0, 3, False, XA_INTEGER,
2449 &type, &format, &nitems, &bytesafter,
2450 &dataP)
2451 == Success
2452 && type == XA_INTEGER
2453 && nitems >= 3
2454 && dataP)
2455 {
2456 PROP32 *data = (PROP32 *) dataP;
2457 hack_number = (int) data[2] - 1;
2458 }
2459
2460 if (dataP) XFree (dataP);
2461
2462 return hack_number;
2463 }
2464
2465
2466 /* Finds the number of the last hack that was run, and makes that item be
2467 selected by default.
2468 */
2469 static void
scroll_to_current_hack(state * s)2470 scroll_to_current_hack (state *s)
2471 {
2472 saver_preferences *p = &s->prefs;
2473 int hack_number = -1;
2474
2475 if (p->mode == ONE_HACK) /* in "one" mode, use the one */
2476 hack_number = p->selected_hack;
2477 if (hack_number < 0) /* otherwise, use the last-run */
2478 hack_number = server_current_hack ();
2479 if (hack_number < 0) /* failing that, last "one mode" */
2480 hack_number = p->selected_hack;
2481 if (hack_number < 0) /* failing that, newest hack. */
2482 {
2483 /* We should only get here if the user does not have a .xscreensaver
2484 file, and the screen has not been blanked with a hack since X
2485 started up: in other words, this is probably a fresh install.
2486
2487 Instead of just defaulting to hack #0 (in either "programs" or
2488 "alphabetical" order) let's try to default to the last runnable
2489 hack in the "programs" list: this is probably the hack that was
2490 most recently added to the xscreensaver distribution (and so
2491 it's probably the currently-coolest one!)
2492 */
2493 hack_number = p->screenhacks_count-1;
2494 while (hack_number > 0 &&
2495 ! (s->hacks_available_p[hack_number] &&
2496 p->screenhacks[hack_number]->enabled_p))
2497 hack_number--;
2498 }
2499
2500 if (hack_number >= 0 && hack_number < p->screenhacks_count)
2501 {
2502 int list_elt = s->hack_number_to_list_elt[hack_number];
2503 GtkWidget *list = name_to_widget (s, "list");
2504 force_list_select_item (s, list, list_elt, True);
2505 populate_demo_window (s, list_elt);
2506 }
2507 }
2508
2509
2510 static void
populate_hack_list(state * s)2511 populate_hack_list (state *s)
2512 {
2513 Display *dpy = GDK_DISPLAY();
2514 #ifdef HAVE_GTK2
2515 saver_preferences *p = &s->prefs;
2516 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2517 GtkListStore *model;
2518 GtkTreeSelection *selection;
2519 GtkCellRenderer *ren;
2520 GtkTreeIter iter;
2521 int i;
2522
2523 g_object_get (G_OBJECT (list),
2524 "model", &model,
2525 NULL);
2526 if (!model)
2527 {
2528 model = gtk_list_store_new (COL_LAST, G_TYPE_BOOLEAN, G_TYPE_STRING);
2529 g_object_set (G_OBJECT (list), "model", model, NULL);
2530 g_object_unref (model);
2531
2532 ren = gtk_cell_renderer_toggle_new ();
2533 gtk_tree_view_insert_column_with_attributes (list, COL_ENABLED,
2534 _("Use"), ren,
2535 "active", COL_ENABLED,
2536 NULL);
2537
2538 g_signal_connect (ren, "toggled",
2539 G_CALLBACK (list_checkbox_cb),
2540 s);
2541
2542 ren = gtk_cell_renderer_text_new ();
2543 gtk_tree_view_insert_column_with_attributes (list, COL_NAME,
2544 _("Screen Saver"), ren,
2545 "markup", COL_NAME,
2546 NULL);
2547
2548 g_signal_connect_after (list, "row_activated",
2549 G_CALLBACK (list_activated_cb),
2550 s);
2551
2552 selection = gtk_tree_view_get_selection (list);
2553 g_signal_connect (selection, "changed",
2554 G_CALLBACK (list_select_changed_cb),
2555 s);
2556
2557 }
2558
2559 for (i = 0; i < s->list_count; i++)
2560 {
2561 int hack_number = s->list_elt_to_hack_number[i];
2562 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2563 char *pretty_name;
2564 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2565
2566 if (!hack) continue;
2567
2568 /* If we're to suppress uninstalled hacks, check $PATH now. */
2569 if (p->ignore_uninstalled_p && !available_p)
2570 continue;
2571
2572 pretty_name = (hack->name
2573 ? strdup (hack->name)
2574 : make_hack_name (dpy, hack->command));
2575
2576 if (!available_p)
2577 {
2578 /* Make the text foreground be the color of insensitive widgets
2579 (but don't actually make it be insensitive, since we still
2580 want to be able to click on it.)
2581 */
2582 GtkStyle *style = gtk_widget_get_style (GTK_WIDGET (list));
2583 GdkColor *fg = &style->fg[GTK_STATE_INSENSITIVE];
2584 /* GdkColor *bg = &style->bg[GTK_STATE_INSENSITIVE]; */
2585 char *buf = (char *) malloc (strlen (pretty_name) + 100);
2586
2587 sprintf (buf, "<span foreground=\"#%02X%02X%02X\""
2588 /* " background=\"#%02X%02X%02X\"" */
2589 ">%s</span>",
2590 fg->red >> 8, fg->green >> 8, fg->blue >> 8,
2591 /* bg->red >> 8, bg->green >> 8, bg->blue >> 8, */
2592 pretty_name);
2593 free (pretty_name);
2594 pretty_name = buf;
2595 }
2596
2597 gtk_list_store_append (model, &iter);
2598 gtk_list_store_set (model, &iter,
2599 COL_ENABLED, hack->enabled_p,
2600 COL_NAME, pretty_name,
2601 -1);
2602 free (pretty_name);
2603 }
2604
2605 #else /* !HAVE_GTK2 */
2606
2607 saver_preferences *p = &s->prefs;
2608 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2609 int i;
2610 for (i = 0; i < s->list_count; i++)
2611 {
2612 int hack_number = s->list_elt_to_hack_number[i];
2613 screenhack *hack = (hack_number < 0 ? 0 : p->screenhacks[hack_number]);
2614
2615 /* A GtkList must contain only GtkListItems, but those can contain
2616 an arbitrary widget. We add an Hbox, and inside that, a Checkbox
2617 and a Label. We handle single and double click events on the
2618 line itself, for clicking on the text, but the interior checkbox
2619 also handles its own events.
2620 */
2621 GtkWidget *line;
2622 GtkWidget *line_hbox;
2623 GtkWidget *line_check;
2624 GtkWidget *line_label;
2625 char *pretty_name;
2626 Bool available_p = (hack && s->hacks_available_p [hack_number]);
2627
2628 if (!hack) continue;
2629
2630 /* If we're to suppress uninstalled hacks, check $PATH now. */
2631 if (p->ignore_uninstalled_p && !available_p)
2632 continue;
2633
2634 pretty_name = (hack->name
2635 ? strdup (hack->name)
2636 : make_hack_name (hack->command));
2637
2638 line = gtk_list_item_new ();
2639 line_hbox = gtk_hbox_new (FALSE, 0);
2640 line_check = gtk_check_button_new ();
2641 line_label = gtk_label_new (pretty_name);
2642
2643 gtk_container_add (GTK_CONTAINER (line), line_hbox);
2644 gtk_box_pack_start (GTK_BOX (line_hbox), line_check, FALSE, FALSE, 0);
2645 gtk_box_pack_start (GTK_BOX (line_hbox), line_label, FALSE, FALSE, 0);
2646
2647 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (line_check),
2648 hack->enabled_p);
2649 gtk_label_set_justify (GTK_LABEL (line_label), GTK_JUSTIFY_LEFT);
2650
2651 gtk_widget_show (line_check);
2652 gtk_widget_show (line_label);
2653 gtk_widget_show (line_hbox);
2654 gtk_widget_show (line);
2655
2656 free (pretty_name);
2657
2658 gtk_container_add (GTK_CONTAINER (list), line);
2659 gtk_signal_connect (GTK_OBJECT (line), "button_press_event",
2660 GTK_SIGNAL_FUNC (list_doubleclick_cb),
2661 (gpointer) s);
2662
2663 gtk_signal_connect (GTK_OBJECT (line_check), "toggled",
2664 GTK_SIGNAL_FUNC (list_checkbox_cb),
2665 (gpointer) s);
2666
2667 gtk_widget_show (line);
2668
2669 if (!available_p)
2670 {
2671 /* Make the widget be colored like insensitive widgets
2672 (but don't actually make it be insensitive, since we
2673 still want to be able to click on it.)
2674 */
2675 GtkRcStyle *rc_style;
2676 GdkColor fg, bg;
2677
2678 gtk_widget_realize (GTK_WIDGET (line_label));
2679
2680 fg = GTK_WIDGET (line_label)->style->fg[GTK_STATE_INSENSITIVE];
2681 bg = GTK_WIDGET (line_label)->style->bg[GTK_STATE_INSENSITIVE];
2682
2683 rc_style = gtk_rc_style_new ();
2684 rc_style->fg[GTK_STATE_NORMAL] = fg;
2685 rc_style->bg[GTK_STATE_NORMAL] = bg;
2686 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG|GTK_RC_BG;
2687
2688 gtk_widget_modify_style (GTK_WIDGET (line_label), rc_style);
2689 gtk_rc_style_unref (rc_style);
2690 }
2691 }
2692
2693 gtk_signal_connect (GTK_OBJECT (list), "select_child",
2694 GTK_SIGNAL_FUNC (list_select_cb),
2695 (gpointer) s);
2696 gtk_signal_connect (GTK_OBJECT (list), "unselect_child",
2697 GTK_SIGNAL_FUNC (list_unselect_cb),
2698 (gpointer) s);
2699 #endif /* !HAVE_GTK2 */
2700 }
2701
2702 static void
update_list_sensitivity(state * s)2703 update_list_sensitivity (state *s)
2704 {
2705 saver_preferences *p = &s->prefs;
2706 Bool sensitive = (p->mode == RANDOM_HACKS ||
2707 p->mode == RANDOM_HACKS_SAME ||
2708 p->mode == ONE_HACK);
2709 Bool checkable = (p->mode == RANDOM_HACKS ||
2710 p->mode == RANDOM_HACKS_SAME);
2711 Bool blankable = (p->mode != DONT_BLANK);
2712
2713 #ifndef HAVE_GTK2
2714 GtkWidget *head = name_to_widget (s, "col_head_hbox");
2715 GtkWidget *use = name_to_widget (s, "use_col_frame");
2716 #endif /* HAVE_GTK2 */
2717 GtkWidget *scroller = name_to_widget (s, "scroller");
2718 GtkWidget *buttons = name_to_widget (s, "next_prev_hbox");
2719 GtkWidget *blanker = name_to_widget (s, "blanking_table");
2720
2721 #ifdef HAVE_GTK2
2722 GtkTreeView *list = GTK_TREE_VIEW (name_to_widget (s, "list"));
2723 GtkTreeViewColumn *use = gtk_tree_view_get_column (list, COL_ENABLED);
2724 #else /* !HAVE_GTK2 */
2725 GtkList *list = GTK_LIST (name_to_widget (s, "list"));
2726 GList *kids = gtk_container_children (GTK_CONTAINER (list));
2727
2728 gtk_widget_set_sensitive (GTK_WIDGET (head), sensitive);
2729 #endif /* !HAVE_GTK2 */
2730 gtk_widget_set_sensitive (GTK_WIDGET (scroller), sensitive);
2731 gtk_widget_set_sensitive (GTK_WIDGET (buttons), sensitive);
2732
2733 gtk_widget_set_sensitive (GTK_WIDGET (blanker), blankable);
2734
2735 #ifdef HAVE_GTK2
2736 gtk_tree_view_column_set_visible (use, checkable);
2737 #else /* !HAVE_GTK2 */
2738 if (checkable)
2739 gtk_widget_show (use); /* the "Use" column header */
2740 else
2741 gtk_widget_hide (use);
2742
2743 while (kids)
2744 {
2745 GtkBin *line = GTK_BIN (kids->data);
2746 GtkContainer *line_hbox = GTK_CONTAINER (line->child);
2747 GtkWidget *line_check =
2748 GTK_WIDGET (gtk_container_children (line_hbox)->data);
2749
2750 if (checkable)
2751 gtk_widget_show (line_check);
2752 else
2753 gtk_widget_hide (line_check);
2754
2755 kids = kids->next;
2756 }
2757 #endif /* !HAVE_GTK2 */
2758 }
2759
2760
2761 static void
populate_prefs_page(state * s)2762 populate_prefs_page (state *s)
2763 {
2764 saver_preferences *p = &s->prefs;
2765
2766 Bool can_lock_p = True;
2767
2768 /* Disable all the "lock" controls if locking support was not provided
2769 at compile-time, or if running on MacOS. */
2770 # if defined(NO_LOCKING) || defined(__APPLE__)
2771 can_lock_p = False;
2772 # endif
2773
2774
2775 /* If there is only one screen, the mode menu contains
2776 "random" but not "random-same".
2777 */
2778 if (s->nscreens <= 1 && p->mode == RANDOM_HACKS_SAME)
2779 p->mode = RANDOM_HACKS;
2780
2781
2782 /* The file supports timeouts of less than a minute, but the GUI does
2783 not, so throttle the values to be at least one minute (since "0" is
2784 a bad rounding choice...)
2785 */
2786 # define THROTTLE(NAME) if (p->NAME != 0 && p->NAME < 60000) p->NAME = 60000
2787 THROTTLE (timeout);
2788 THROTTLE (cycle);
2789 /* THROTTLE (passwd_timeout); */ /* GUI doesn't set this; leave it alone */
2790 # undef THROTTLE
2791
2792 # define FMT_MINUTES(NAME,N) \
2793 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) + 59) / (60 * 1000))
2794
2795 # define FMT_SECONDS(NAME,N) \
2796 gtk_spin_button_set_value (GTK_SPIN_BUTTON (name_to_widget (s, (NAME))), (double)((N) / 1000))
2797
2798 FMT_MINUTES ("timeout_spinbutton", p->timeout);
2799 FMT_MINUTES ("cycle_spinbutton", p->cycle);
2800 FMT_MINUTES ("lock_spinbutton", p->lock_timeout);
2801 FMT_MINUTES ("dpms_standby_spinbutton", p->dpms_standby);
2802 FMT_MINUTES ("dpms_suspend_spinbutton", p->dpms_suspend);
2803 FMT_MINUTES ("dpms_off_spinbutton", p->dpms_off);
2804 FMT_SECONDS ("fade_spinbutton", p->fade_seconds);
2805
2806 # undef FMT_MINUTES
2807 # undef FMT_SECONDS
2808
2809 # define TOGGLE_ACTIVE(NAME,ACTIVEP) \
2810 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (name_to_widget (s,(NAME))),\
2811 (ACTIVEP))
2812
2813 TOGGLE_ACTIVE ("lock_button", p->lock_p);
2814 #if 0
2815 TOGGLE_ACTIVE ("verbose_button", p->verbose_p);
2816 TOGGLE_ACTIVE ("capture_button", p->capture_stderr_p);
2817 TOGGLE_ACTIVE ("splash_button", p->splash_p);
2818 #endif
2819 TOGGLE_ACTIVE ("dpms_button", p->dpms_enabled_p);
2820 TOGGLE_ACTIVE ("dpms_quickoff_button", p->dpms_quickoff_p);
2821 TOGGLE_ACTIVE ("grab_desk_button", p->grab_desktop_p);
2822 TOGGLE_ACTIVE ("grab_video_button", p->grab_video_p);
2823 TOGGLE_ACTIVE ("grab_image_button", p->random_image_p);
2824 TOGGLE_ACTIVE ("install_button", p->install_cmap_p);
2825 TOGGLE_ACTIVE ("fade_button", p->fade_p);
2826 TOGGLE_ACTIVE ("unfade_button", p->unfade_p);
2827
2828 switch (p->tmode)
2829 {
2830 case TEXT_LITERAL: TOGGLE_ACTIVE ("text_radio", True); break;
2831 case TEXT_FILE: TOGGLE_ACTIVE ("text_file_radio", True); break;
2832 case TEXT_PROGRAM: TOGGLE_ACTIVE ("text_program_radio", True); break;
2833 case TEXT_URL: TOGGLE_ACTIVE ("text_url_radio", True); break;
2834 default: TOGGLE_ACTIVE ("text_host_radio", True); break;
2835 }
2836
2837 # undef TOGGLE_ACTIVE
2838
2839 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "image_text")),
2840 (p->image_directory ? p->image_directory : ""));
2841 gtk_widget_set_sensitive (name_to_widget (s, "image_text"),
2842 p->random_image_p);
2843 gtk_widget_set_sensitive (name_to_widget (s, "image_browse_button"),
2844 p->random_image_p);
2845
2846 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_entry")),
2847 (p->text_literal ? p->text_literal : ""));
2848 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_file_entry")),
2849 (p->text_file ? p->text_file : ""));
2850 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_program_entry")),
2851 (p->text_program ? p->text_program : ""));
2852 gtk_entry_set_text (GTK_ENTRY (name_to_widget (s, "text_url_entry")),
2853 (p->text_url ? p->text_url : ""));
2854
2855 gtk_widget_set_sensitive (name_to_widget (s, "text_entry"),
2856 p->tmode == TEXT_LITERAL);
2857 gtk_widget_set_sensitive (name_to_widget (s, "text_file_entry"),
2858 p->tmode == TEXT_FILE);
2859 gtk_widget_set_sensitive (name_to_widget (s, "text_file_browse"),
2860 p->tmode == TEXT_FILE);
2861 gtk_widget_set_sensitive (name_to_widget (s, "text_program_entry"),
2862 p->tmode == TEXT_PROGRAM);
2863 gtk_widget_set_sensitive (name_to_widget (s, "text_program_browse"),
2864 p->tmode == TEXT_PROGRAM);
2865 gtk_widget_set_sensitive (name_to_widget (s, "text_url_entry"),
2866 p->tmode == TEXT_URL);
2867
2868
2869 /* Map the `saver_mode' enum to mode menu to values. */
2870 {
2871 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
2872
2873 int i;
2874 for (i = 0; i < countof(mode_menu_order); i++)
2875 if (mode_menu_order[i] == p->mode)
2876 break;
2877 gtk_option_menu_set_history (opt, i);
2878 update_list_sensitivity (s);
2879 }
2880
2881 {
2882 Bool found_any_writable_cells = False;
2883 Bool fading_possible = False;
2884 Bool dpms_supported = False;
2885
2886 Display *dpy = GDK_DISPLAY();
2887 int nscreens = ScreenCount(dpy); /* real screens, not Xinerama */
2888 int i;
2889 for (i = 0; i < nscreens; i++)
2890 {
2891 Screen *s = ScreenOfDisplay (dpy, i);
2892 if (has_writable_cells (s, DefaultVisualOfScreen (s)))
2893 {
2894 found_any_writable_cells = True;
2895 break;
2896 }
2897 }
2898
2899 fading_possible = found_any_writable_cells;
2900 #ifdef HAVE_XF86VMODE_GAMMA
2901 fading_possible = True;
2902 #endif
2903
2904 #ifdef HAVE_DPMS_EXTENSION
2905 {
2906 int op = 0, event = 0, error = 0;
2907 if (XQueryExtension (dpy, "DPMS", &op, &event, &error))
2908 dpms_supported = True;
2909 }
2910 #endif /* HAVE_DPMS_EXTENSION */
2911
2912
2913 # define SENSITIZE(NAME,SENSITIVEP) \
2914 gtk_widget_set_sensitive (name_to_widget (s, (NAME)), (SENSITIVEP))
2915
2916 /* Blanking and Locking
2917 */
2918 SENSITIZE ("lock_button", can_lock_p);
2919 SENSITIZE ("lock_spinbutton", can_lock_p && p->lock_p);
2920 SENSITIZE ("lock_mlabel", can_lock_p && p->lock_p);
2921
2922 /* DPMS
2923 */
2924 SENSITIZE ("dpms_frame", dpms_supported);
2925 SENSITIZE ("dpms_button", dpms_supported);
2926 SENSITIZE ("dpms_quickoff_button", dpms_supported);
2927
2928 SENSITIZE ("dpms_standby_label", dpms_supported && p->dpms_enabled_p);
2929 SENSITIZE ("dpms_standby_mlabel", dpms_supported && p->dpms_enabled_p);
2930 SENSITIZE ("dpms_standby_spinbutton", dpms_supported && p->dpms_enabled_p);
2931 SENSITIZE ("dpms_suspend_label", dpms_supported && p->dpms_enabled_p);
2932 SENSITIZE ("dpms_suspend_mlabel", dpms_supported && p->dpms_enabled_p);
2933 SENSITIZE ("dpms_suspend_spinbutton", dpms_supported && p->dpms_enabled_p);
2934 SENSITIZE ("dpms_off_label", dpms_supported && p->dpms_enabled_p);
2935 SENSITIZE ("dpms_off_mlabel", dpms_supported && p->dpms_enabled_p);
2936 SENSITIZE ("dpms_off_spinbutton", dpms_supported && p->dpms_enabled_p);
2937
2938 /* Colormaps
2939 */
2940 SENSITIZE ("cmap_frame", found_any_writable_cells || fading_possible);
2941 SENSITIZE ("install_button", found_any_writable_cells);
2942 SENSITIZE ("fade_button", fading_possible);
2943 SENSITIZE ("unfade_button", fading_possible);
2944
2945 SENSITIZE ("fade_label", (fading_possible &&
2946 (p->fade_p || p->unfade_p)));
2947 SENSITIZE ("fade_spinbutton", (fading_possible &&
2948 (p->fade_p || p->unfade_p)));
2949
2950 # undef SENSITIZE
2951 }
2952 }
2953
2954
2955 static void
populate_popup_window(state * s)2956 populate_popup_window (state *s)
2957 {
2958 GtkLabel *doc = GTK_LABEL (name_to_widget (s, "doc"));
2959 char *doc_string = 0;
2960
2961 /* #### not in Gtk 1.2
2962 gtk_label_set_selectable (doc);
2963 */
2964
2965 # ifdef HAVE_XML
2966 if (s->cdata)
2967 {
2968 free_conf_data (s->cdata);
2969 s->cdata = 0;
2970 }
2971
2972 {
2973 saver_preferences *p = &s->prefs;
2974 int list_elt = selected_list_element (s);
2975 int hack_number = (list_elt >= 0 && list_elt < s->list_count
2976 ? s->list_elt_to_hack_number[list_elt]
2977 : -1);
2978 screenhack *hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
2979 if (hack)
2980 {
2981 GtkWidget *parent = name_to_widget (s, "settings_vbox");
2982 GtkWidget *cmd = GTK_WIDGET (name_to_widget (s, "cmd_text"));
2983 const char *cmd_line = gtk_entry_get_text (GTK_ENTRY (cmd));
2984 s->cdata = load_configurator (cmd_line, s->debug_p);
2985 if (s->cdata && s->cdata->widget)
2986 gtk_box_pack_start (GTK_BOX (parent), s->cdata->widget,
2987 TRUE, TRUE, 0);
2988 }
2989 }
2990
2991 doc_string = (s->cdata
2992 ? s->cdata->description
2993 : 0);
2994 # else /* !HAVE_XML */
2995 doc_string = _("Descriptions not available: no XML support compiled in.");
2996 # endif /* !HAVE_XML */
2997
2998 gtk_label_set_text (doc, (doc_string
2999 ? _(doc_string)
3000 : _("No description available.")));
3001 }
3002
3003
3004 static void
sensitize_demo_widgets(state * s,Bool sensitive_p)3005 sensitize_demo_widgets (state *s, Bool sensitive_p)
3006 {
3007 const char *names[] = { "demo", "settings",
3008 "cmd_label", "cmd_text", "manual",
3009 "visual", "visual_combo" };
3010 int i;
3011 for (i = 0; i < countof(names); i++)
3012 {
3013 GtkWidget *w = name_to_widget (s, names[i]);
3014 gtk_widget_set_sensitive (GTK_WIDGET(w), sensitive_p);
3015 }
3016 }
3017
3018
3019 static void
sensitize_menu_items(state * s,Bool force_p)3020 sensitize_menu_items (state *s, Bool force_p)
3021 {
3022 static Bool running_p = False;
3023 static time_t last_checked = 0;
3024 time_t now = time ((time_t *) 0);
3025 const char *names[] = { "activate_menu", "lock_menu", "kill_menu",
3026 /* "demo" */ };
3027 int i;
3028
3029 if (force_p || now > last_checked + 10) /* check every 10 seconds */
3030 {
3031 running_p = xscreensaver_running_p (s);
3032 last_checked = time ((time_t *) 0);
3033 }
3034
3035 for (i = 0; i < countof(names); i++)
3036 {
3037 GtkWidget *w = name_to_widget (s, names[i]);
3038 gtk_widget_set_sensitive (GTK_WIDGET(w), running_p);
3039 }
3040 }
3041
3042
3043 /* When the File menu is de-posted after a "Restart Daemon" command,
3044 the window underneath doesn't repaint for some reason. I guess this
3045 is a bug in exposure handling in GTK or GDK. This works around it.
3046 */
3047 static void
force_dialog_repaint(state * s)3048 force_dialog_repaint (state *s)
3049 {
3050 #if 1
3051 /* Tell GDK to invalidate and repaint the whole window.
3052 */
3053 GdkWindow *w = GET_WINDOW (s->toplevel_widget);
3054 GdkRegion *region = gdk_region_new ();
3055 GdkRectangle rect;
3056 rect.x = rect.y = 0;
3057 rect.width = rect.height = 32767;
3058 gdk_region_union_with_rect (region, &rect);
3059 gdk_window_invalidate_region (w, region, True);
3060 gdk_region_destroy (region);
3061 gdk_window_process_updates (w, True);
3062 #else
3063 /* Force the server to send an exposure event by creating and then
3064 destroying a window as a child of the top level shell.
3065 */
3066 Display *dpy = GDK_DISPLAY();
3067 Window parent = GDK_WINDOW_XWINDOW (s->toplevel_widget->window);
3068 Window w;
3069 XWindowAttributes xgwa;
3070 XGetWindowAttributes (dpy, parent, &xgwa);
3071 w = XCreateSimpleWindow (dpy, parent, 0, 0, xgwa.width, xgwa.height, 0,0,0);
3072 XMapRaised (dpy, w);
3073 XDestroyWindow (dpy, w);
3074 XSync (dpy, False);
3075 #endif
3076 }
3077
3078
3079 /* Even though we've given these text fields a maximum number of characters,
3080 their default size is still about 30 characters wide -- so measure out
3081 a string in their font, and resize them to just fit that.
3082 */
3083 static void
fix_text_entry_sizes(state * s)3084 fix_text_entry_sizes (state *s)
3085 {
3086 GtkWidget *w;
3087
3088 # if 0 /* appears no longer necessary with Gtk 1.2.10 */
3089 const char * const spinbuttons[] = {
3090 "timeout_spinbutton", "cycle_spinbutton", "lock_spinbutton",
3091 "dpms_standby_spinbutton", "dpms_suspend_spinbutton",
3092 "dpms_off_spinbutton",
3093 "-fade_spinbutton" };
3094 int i;
3095 int width = 0;
3096
3097 for (i = 0; i < countof(spinbuttons); i++)
3098 {
3099 const char *n = spinbuttons[i];
3100 int cols = 4;
3101 while (*n == '-') n++, cols--;
3102 w = GTK_WIDGET (name_to_widget (s, n));
3103 width = gdk_text_width (w->style->font, "MMMMMMMM", cols);
3104 gtk_widget_set_usize (w, width, -2);
3105 }
3106
3107 /* Now fix the width of the combo box.
3108 */
3109 w = GTK_WIDGET (name_to_widget (s, "visual_combo"));
3110 w = GTK_COMBO (w)->entry;
3111 width = gdk_string_width (w->style->font, "PseudoColor___");
3112 gtk_widget_set_usize (w, width, -2);
3113
3114 /* Now fix the width of the file entry text.
3115 */
3116 w = GTK_WIDGET (name_to_widget (s, "image_text"));
3117 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmm");
3118 gtk_widget_set_usize (w, width, -2);
3119
3120 /* Now fix the width of the command line text.
3121 */
3122 w = GTK_WIDGET (name_to_widget (s, "cmd_text"));
3123 width = gdk_string_width (w->style->font, "mmmmmmmmmmmmmmmmmmmm");
3124 gtk_widget_set_usize (w, width, -2);
3125
3126 # endif /* 0 */
3127
3128 /* Now fix the height of the list widget:
3129 make it default to being around 10 text-lines high instead of 4.
3130 */
3131 w = GTK_WIDGET (name_to_widget (s, "list"));
3132 {
3133 int lines = 10;
3134 int height;
3135 int leading = 3; /* approximate is ok... */
3136 int border = 2;
3137
3138 #ifdef HAVE_GTK2
3139 PangoFontMetrics *pain =
3140 pango_context_get_metrics (gtk_widget_get_pango_context (w),
3141 gtk_widget_get_style (w)->font_desc,
3142 gtk_get_default_language ());
3143 height = PANGO_PIXELS (pango_font_metrics_get_ascent (pain) +
3144 pango_font_metrics_get_descent (pain));
3145 #else /* !HAVE_GTK2 */
3146 height = w->style->font->ascent + w->style->font->descent;
3147 #endif /* !HAVE_GTK2 */
3148
3149 height += leading;
3150 height *= lines;
3151 height += border * 2;
3152 w = GTK_WIDGET (name_to_widget (s, "scroller"));
3153 gtk_widget_set_usize (w, -2, height);
3154 }
3155 }
3156
3157
3158 #ifndef HAVE_GTK2
3159
3160 /* Pixmaps for the up and down arrow buttons (yeah, this is sleazy...)
3161 */
3162
3163 static char *up_arrow_xpm[] = {
3164 "15 15 4 1",
3165 " c None",
3166 "- c #FFFFFF",
3167 "+ c #D6D6D6",
3168 "@ c #000000",
3169
3170 " @ ",
3171 " @ ",
3172 " -+@ ",
3173 " -+@ ",
3174 " -+++@ ",
3175 " -+++@ ",
3176 " -+++++@ ",
3177 " -+++++@ ",
3178 " -+++++++@ ",
3179 " -+++++++@ ",
3180 " -+++++++++@ ",
3181 " -+++++++++@ ",
3182 " -+++++++++++@ ",
3183 " @@@@@@@@@@@@@ ",
3184 " ",
3185
3186 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3187 the end of the array (Gtk 1.2.5.) */
3188 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3189 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3190 };
3191
3192 static char *down_arrow_xpm[] = {
3193 "15 15 4 1",
3194 " c None",
3195 "- c #FFFFFF",
3196 "+ c #D6D6D6",
3197 "@ c #000000",
3198
3199 " ",
3200 " ------------- ",
3201 " -+++++++++++@ ",
3202 " -+++++++++@ ",
3203 " -+++++++++@ ",
3204 " -+++++++@ ",
3205 " -+++++++@ ",
3206 " -+++++@ ",
3207 " -+++++@ ",
3208 " -+++@ ",
3209 " -+++@ ",
3210 " -+@ ",
3211 " -+@ ",
3212 " @ ",
3213 " @ ",
3214
3215 /* Need these here because gdk_pixmap_create_from_xpm_d() walks off
3216 the end of the array (Gtk 1.2.5.) */
3217 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",
3218 "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
3219 };
3220
3221 static void
pixmapify_button(state * s,int down_p)3222 pixmapify_button (state *s, int down_p)
3223 {
3224 GdkPixmap *pixmap;
3225 GdkBitmap *mask;
3226 GtkWidget *pixmapwid;
3227 GtkStyle *style;
3228 GtkWidget *w;
3229
3230 w = GTK_WIDGET (name_to_widget (s, (down_p ? "next" : "prev")));
3231 style = gtk_widget_get_style (w);
3232 mask = 0;
3233 pixmap = gdk_pixmap_create_from_xpm_d (w->window, &mask,
3234 &style->bg[GTK_STATE_NORMAL],
3235 (down_p
3236 ? (gchar **) down_arrow_xpm
3237 : (gchar **) up_arrow_xpm));
3238 pixmapwid = gtk_pixmap_new (pixmap, mask);
3239 gtk_widget_show (pixmapwid);
3240 gtk_container_remove (GTK_CONTAINER (w), GTK_BIN (w)->child);
3241 gtk_container_add (GTK_CONTAINER (w), pixmapwid);
3242 }
3243
3244 static void
map_next_button_cb(GtkWidget * w,gpointer user_data)3245 map_next_button_cb (GtkWidget *w, gpointer user_data)
3246 {
3247 state *s = (state *) user_data;
3248 pixmapify_button (s, 1);
3249 }
3250
3251 static void
map_prev_button_cb(GtkWidget * w,gpointer user_data)3252 map_prev_button_cb (GtkWidget *w, gpointer user_data)
3253 {
3254 state *s = (state *) user_data;
3255 pixmapify_button (s, 0);
3256 }
3257 #endif /* !HAVE_GTK2 */
3258
3259
3260 #ifndef HAVE_GTK2
3261 /* Work around a Gtk bug that causes label widgets to wrap text too early.
3262 */
3263
3264 static void
you_are_not_a_unique_or_beautiful_snowflake(GtkWidget * label,GtkAllocation * allocation,void * foo)3265 you_are_not_a_unique_or_beautiful_snowflake (GtkWidget *label,
3266 GtkAllocation *allocation,
3267 void *foo)
3268 {
3269 GtkRequisition req;
3270 GtkWidgetAuxInfo *aux_info;
3271
3272 aux_info = gtk_object_get_data (GTK_OBJECT (label), "gtk-aux-info");
3273
3274 aux_info->width = allocation->width;
3275 aux_info->height = -2;
3276 aux_info->x = -1;
3277 aux_info->y = -1;
3278
3279 gtk_widget_size_request (label, &req);
3280 }
3281
3282 /* Feel the love. Thanks to Nat Friedman for finding this workaround.
3283 */
3284 static void
eschew_gtk_lossage(GtkLabel * label)3285 eschew_gtk_lossage (GtkLabel *label)
3286 {
3287 GtkWidgetAuxInfo *aux_info = g_new0 (GtkWidgetAuxInfo, 1);
3288 aux_info->width = GTK_WIDGET (label)->allocation.width;
3289 aux_info->height = -2;
3290 aux_info->x = -1;
3291 aux_info->y = -1;
3292
3293 gtk_object_set_data (GTK_OBJECT (label), "gtk-aux-info", aux_info);
3294
3295 gtk_signal_connect (GTK_OBJECT (label), "size_allocate",
3296 GTK_SIGNAL_FUNC (you_are_not_a_unique_or_beautiful_snowflake),
3297 0);
3298
3299 gtk_widget_set_usize (GTK_WIDGET (label), -2, -2);
3300
3301 gtk_widget_queue_resize (GTK_WIDGET (label));
3302 }
3303 #endif /* !HAVE_GTK2 */
3304
3305
3306 static void
populate_demo_window(state * s,int list_elt)3307 populate_demo_window (state *s, int list_elt)
3308 {
3309 Display *dpy = GDK_DISPLAY();
3310 saver_preferences *p = &s->prefs;
3311 screenhack *hack;
3312 char *pretty_name;
3313 GtkFrame *frame1 = GTK_FRAME (name_to_widget (s, "preview_frame"));
3314 GtkFrame *frame2 = GTK_FRAME (name_to_widget (s, "opt_frame"));
3315 GtkEntry *cmd = GTK_ENTRY (name_to_widget (s, "cmd_text"));
3316 GtkCombo *vis = GTK_COMBO (name_to_widget (s, "visual_combo"));
3317 GtkWidget *list = GTK_WIDGET (name_to_widget (s, "list"));
3318
3319 if (p->mode == BLANK_ONLY)
3320 {
3321 hack = 0;
3322 pretty_name = strdup (_("Blank Screen"));
3323 schedule_preview (s, 0);
3324 }
3325 else if (p->mode == DONT_BLANK)
3326 {
3327 hack = 0;
3328 pretty_name = strdup (_("Screen Saver Disabled"));
3329 schedule_preview (s, 0);
3330 }
3331 else
3332 {
3333 int hack_number = (list_elt >= 0 && list_elt < s->list_count
3334 ? s->list_elt_to_hack_number[list_elt]
3335 : -1);
3336 hack = (hack_number >= 0 ? p->screenhacks[hack_number] : 0);
3337
3338 pretty_name = (hack
3339 ? (hack->name
3340 ? strdup (hack->name)
3341 : make_hack_name (dpy, hack->command))
3342 : 0);
3343
3344 if (hack)
3345 schedule_preview (s, hack->command);
3346 else
3347 schedule_preview (s, 0);
3348 }
3349
3350 if (!pretty_name)
3351 pretty_name = strdup (_("Preview"));
3352
3353 gtk_frame_set_label (frame1, _(pretty_name));
3354 gtk_frame_set_label (frame2, _(pretty_name));
3355
3356 gtk_entry_set_text (cmd, (hack ? hack->command : ""));
3357 gtk_entry_set_position (cmd, 0);
3358
3359 {
3360 char title[255];
3361 sprintf (title, _("%s: %.100s Settings"),
3362 progclass, (pretty_name ? pretty_name : "???"));
3363 gtk_window_set_title (GTK_WINDOW (s->popup_widget), title);
3364 }
3365
3366 gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (vis)->entry),
3367 (hack
3368 ? (hack->visual && *hack->visual
3369 ? hack->visual
3370 : _("Any"))
3371 : ""));
3372
3373 sensitize_demo_widgets (s, (hack ? True : False));
3374
3375 if (pretty_name) free (pretty_name);
3376
3377 ensure_selected_item_visible (list);
3378
3379 s->_selected_list_element = list_elt;
3380 }
3381
3382
3383 static void
widget_deleter(GtkWidget * widget,gpointer data)3384 widget_deleter (GtkWidget *widget, gpointer data)
3385 {
3386 /* #### Well, I want to destroy these widgets, but if I do that, they get
3387 referenced again, and eventually I get a SEGV. So instead of
3388 destroying them, I'll just hide them, and leak a bunch of memory
3389 every time the disk file changes. Go go go Gtk!
3390
3391 #### Ok, that's a lie, I get a crash even if I just hide the widget
3392 and don't ever delete it. Fuck!
3393 */
3394 #if 0
3395 gtk_widget_destroy (widget);
3396 #else
3397 gtk_widget_hide (widget);
3398 #endif
3399 }
3400
3401
3402 static char **sort_hack_cmp_names_kludge;
3403 static int
sort_hack_cmp(const void * a,const void * b)3404 sort_hack_cmp (const void *a, const void *b)
3405 {
3406 if (a == b)
3407 return 0;
3408 else
3409 {
3410 int aa = *(int *) a;
3411 int bb = *(int *) b;
3412 const char last[] = "\377\377\377\377\377\377\377\377\377\377\377";
3413 return strcmp ((aa < 0 ? last : sort_hack_cmp_names_kludge[aa]),
3414 (bb < 0 ? last : sort_hack_cmp_names_kludge[bb]));
3415 }
3416 }
3417
3418
3419 static void
initialize_sort_map(state * s)3420 initialize_sort_map (state *s)
3421 {
3422 Display *dpy = GDK_DISPLAY();
3423 saver_preferences *p = &s->prefs;
3424 int i, j;
3425
3426 if (s->list_elt_to_hack_number) free (s->list_elt_to_hack_number);
3427 if (s->hack_number_to_list_elt) free (s->hack_number_to_list_elt);
3428 if (s->hacks_available_p) free (s->hacks_available_p);
3429
3430 s->list_elt_to_hack_number = (int *)
3431 calloc (sizeof(int), p->screenhacks_count + 1);
3432 s->hack_number_to_list_elt = (int *)
3433 calloc (sizeof(int), p->screenhacks_count + 1);
3434 s->hacks_available_p = (Bool *)
3435 calloc (sizeof(Bool), p->screenhacks_count + 1);
3436 s->total_available = 0;
3437
3438 /* Check which hacks actually exist on $PATH
3439 */
3440 for (i = 0; i < p->screenhacks_count; i++)
3441 {
3442 screenhack *hack = p->screenhacks[i];
3443 int on = on_path_p (hack->command) ? 1 : 0;
3444 s->hacks_available_p[i] = on;
3445 s->total_available += on;
3446 }
3447
3448 /* Initialize list->hack table to unsorted mapping, omitting nonexistent
3449 hacks, if desired.
3450 */
3451 j = 0;
3452 for (i = 0; i < p->screenhacks_count; i++)
3453 {
3454 if (!p->ignore_uninstalled_p ||
3455 s->hacks_available_p[i])
3456 s->list_elt_to_hack_number[j++] = i;
3457 }
3458 s->list_count = j;
3459
3460 for (; j < p->screenhacks_count; j++)
3461 s->list_elt_to_hack_number[j] = -1;
3462
3463
3464 /* Generate list of sortable names (once)
3465 */
3466 sort_hack_cmp_names_kludge = (char **)
3467 calloc (sizeof(char *), p->screenhacks_count);
3468 for (i = 0; i < p->screenhacks_count; i++)
3469 {
3470 screenhack *hack = p->screenhacks[i];
3471 char *name = (hack->name && *hack->name
3472 ? strdup (hack->name)
3473 : make_hack_name (dpy, hack->command));
3474 char *str;
3475 for (str = name; *str; str++)
3476 *str = tolower(*str);
3477 sort_hack_cmp_names_kludge[i] = name;
3478 }
3479
3480 /* Sort list->hack map alphabetically
3481 */
3482 qsort (s->list_elt_to_hack_number,
3483 p->screenhacks_count,
3484 sizeof(*s->list_elt_to_hack_number),
3485 sort_hack_cmp);
3486
3487 /* Free names
3488 */
3489 for (i = 0; i < p->screenhacks_count; i++)
3490 free (sort_hack_cmp_names_kludge[i]);
3491 free (sort_hack_cmp_names_kludge);
3492 sort_hack_cmp_names_kludge = 0;
3493
3494 /* Build inverse table */
3495 for (i = 0; i < p->screenhacks_count; i++)
3496 {
3497 int n = s->list_elt_to_hack_number[i];
3498 if (n != -1)
3499 s->hack_number_to_list_elt[n] = i;
3500 }
3501 }
3502
3503
3504 static int
maybe_reload_init_file(state * s)3505 maybe_reload_init_file (state *s)
3506 {
3507 Display *dpy = GDK_DISPLAY();
3508 saver_preferences *p = &s->prefs;
3509 int status = 0;
3510
3511 static Bool reentrant_lock = False;
3512 if (reentrant_lock) return 0;
3513 reentrant_lock = True;
3514
3515 if (init_file_changed_p (p))
3516 {
3517 const char *f = init_file_name();
3518 char *b;
3519 int list_elt;
3520 GtkWidget *list;
3521
3522 if (!f || !*f) return 0;
3523 b = (char *) malloc (strlen(f) + 1024);
3524 sprintf (b,
3525 _("Warning:\n\n"
3526 "file \"%s\" has changed, reloading.\n"),
3527 f);
3528 warning_dialog (s->toplevel_widget, b, D_NONE, 100);
3529 free (b);
3530
3531 load_init_file (dpy, p);
3532 initialize_sort_map (s);
3533
3534 list_elt = selected_list_element (s);
3535 list = name_to_widget (s, "list");
3536 gtk_container_foreach (GTK_CONTAINER (list), widget_deleter, NULL);
3537 populate_hack_list (s);
3538 force_list_select_item (s, list, list_elt, True);
3539 populate_prefs_page (s);
3540 populate_demo_window (s, list_elt);
3541 ensure_selected_item_visible (list);
3542
3543 status = 1;
3544 }
3545
3546 reentrant_lock = False;
3547 return status;
3548 }
3549
3550
3551
3552 /* Making the preview window have the right X visual (so that GL works.)
3553 */
3554
3555 static Visual *get_best_gl_visual (state *);
3556
3557 static GdkVisual *
x_visual_to_gdk_visual(Visual * xv)3558 x_visual_to_gdk_visual (Visual *xv)
3559 {
3560 GList *gvs = gdk_list_visuals();
3561 if (!xv) return gdk_visual_get_system();
3562 for (; gvs; gvs = gvs->next)
3563 {
3564 GdkVisual *gv = (GdkVisual *) gvs->data;
3565 if (xv == GDK_VISUAL_XVISUAL (gv))
3566 return gv;
3567 }
3568 fprintf (stderr, "%s: couldn't convert X Visual 0x%lx to a GdkVisual\n",
3569 blurb(), (unsigned long) xv->visualid);
3570 abort();
3571 }
3572
3573 static void
clear_preview_window(state * s)3574 clear_preview_window (state *s)
3575 {
3576 GtkWidget *p;
3577 GdkWindow *window;
3578 GtkStyle *style;
3579
3580 if (!s->toplevel_widget) return; /* very early */
3581 p = name_to_widget (s, "preview");
3582 window = GET_WINDOW (p);
3583
3584 if (!window) return;
3585
3586 /* Flush the widget background down into the window, in case a subproc
3587 has changed it. */
3588 style = gtk_widget_get_style (p);
3589 gdk_window_set_background (window, &style->bg[GTK_STATE_NORMAL]);
3590 gdk_window_clear (window);
3591
3592 {
3593 int list_elt = selected_list_element (s);
3594 int hack_number = (list_elt >= 0
3595 ? s->list_elt_to_hack_number[list_elt]
3596 : -1);
3597 Bool available_p = (hack_number >= 0
3598 ? s->hacks_available_p [hack_number]
3599 : True);
3600 Bool nothing_p = (s->total_available < 5);
3601
3602 #ifdef HAVE_GTK2
3603 GtkWidget *notebook = name_to_widget (s, "preview_notebook");
3604 gtk_notebook_set_page (GTK_NOTEBOOK (notebook),
3605 (s->running_preview_error_p
3606 ? (available_p ? 1 :
3607 nothing_p ? 3 : 2)
3608 : 0));
3609 #else /* !HAVE_GTK2 */
3610 if (s->running_preview_error_p)
3611 {
3612 const char * const lines1[] = { N_("No Preview"), N_("Available") };
3613 const char * const lines2[] = { N_("Not"), N_("Installed") };
3614 int nlines = countof(lines1);
3615 int lh = p->style->font->ascent + p->style->font->descent;
3616 int y, i;
3617 gint w, h;
3618
3619 const char * const *lines = (available_p ? lines1 : lines2);
3620
3621 gdk_window_get_size (window, &w, &h);
3622 y = (h - (lh * nlines)) / 2;
3623 y += p->style->font->ascent;
3624 for (i = 0; i < nlines; i++)
3625 {
3626 int sw = gdk_string_width (p->style->font, _(lines[i]));
3627 int x = (w - sw) / 2;
3628 gdk_draw_string (window, p->style->font,
3629 p->style->fg_gc[GTK_STATE_NORMAL],
3630 x, y, _(lines[i]));
3631 y += lh;
3632 }
3633 }
3634 #endif /* !HAVE_GTK2 */
3635 }
3636
3637 gdk_flush ();
3638 }
3639
3640
3641 static void
reset_preview_window(state * s)3642 reset_preview_window (state *s)
3643 {
3644 /* On some systems (most recently, MacOS X) OpenGL programs get confused
3645 when you kill one and re-start another on the same window. So maybe
3646 it's best to just always destroy and recreate the preview window
3647 when changing hacks, instead of always trying to reuse the same one?
3648 */
3649 GtkWidget *pr = name_to_widget (s, "preview");
3650 if (GET_REALIZED (pr))
3651 {
3652 GdkWindow *window = GET_WINDOW (pr);
3653 Window oid = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3654 Window id;
3655 gtk_widget_hide (pr);
3656 gtk_widget_unrealize (pr);
3657 gtk_widget_realize (pr);
3658 gtk_widget_show (pr);
3659 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3660 if (s->debug_p)
3661 fprintf (stderr, "%s: window id 0x%X -> 0x%X\n", blurb(),
3662 (unsigned int) oid,
3663 (unsigned int) id);
3664 }
3665 }
3666
3667
3668 static void
fix_preview_visual(state * s)3669 fix_preview_visual (state *s)
3670 {
3671 GtkWidget *widget = name_to_widget (s, "preview");
3672 Visual *xvisual = get_best_gl_visual (s);
3673 GdkVisual *visual = x_visual_to_gdk_visual (xvisual);
3674 GdkVisual *dvisual = gdk_visual_get_system();
3675 GdkColormap *cmap = (visual == dvisual
3676 ? gdk_colormap_get_system ()
3677 : gdk_colormap_new (visual, False));
3678
3679 if (s->debug_p)
3680 fprintf (stderr, "%s: using %s visual 0x%lx\n", blurb(),
3681 (visual == dvisual ? "default" : "non-default"),
3682 (xvisual ? (unsigned long) xvisual->visualid : 0L));
3683
3684 if (!GET_REALIZED (widget) ||
3685 gtk_widget_get_visual (widget) != visual)
3686 {
3687 gtk_widget_unrealize (widget);
3688 gtk_widget_set_visual (widget, visual);
3689 gtk_widget_set_colormap (widget, cmap);
3690 gtk_widget_realize (widget);
3691 }
3692
3693 /* Set the Widget colors to be white-on-black. */
3694 {
3695 GdkWindow *window = GET_WINDOW (widget);
3696 GtkStyle *style = gtk_style_copy (gtk_widget_get_style (widget));
3697 GdkColormap *cmap = gtk_widget_get_colormap (widget);
3698 GdkColor *fg = &style->fg[GTK_STATE_NORMAL];
3699 GdkColor *bg = &style->bg[GTK_STATE_NORMAL];
3700 GdkGC *fgc = gdk_gc_new(window);
3701 GdkGC *bgc = gdk_gc_new(window);
3702 if (!gdk_color_white (cmap, fg)) abort();
3703 if (!gdk_color_black (cmap, bg)) abort();
3704 gdk_gc_set_foreground (fgc, fg);
3705 gdk_gc_set_background (fgc, bg);
3706 gdk_gc_set_foreground (bgc, bg);
3707 gdk_gc_set_background (bgc, fg);
3708 style->fg_gc[GTK_STATE_NORMAL] = fgc;
3709 style->bg_gc[GTK_STATE_NORMAL] = fgc;
3710 gtk_widget_set_style (widget, style);
3711
3712 /* For debugging purposes, put a title on the window (so that
3713 it can be easily found in the output of "xwininfo -tree".)
3714 */
3715 gdk_window_set_title (window, "Preview");
3716 }
3717
3718 gtk_widget_show (widget);
3719 }
3720
3721
3722 /* Subprocesses
3723 */
3724
3725 static char *
subproc_pretty_name(state * s)3726 subproc_pretty_name (state *s)
3727 {
3728 if (s->running_preview_cmd)
3729 {
3730 char *ps = strdup (s->running_preview_cmd);
3731 char *ss = strchr (ps, ' ');
3732 if (ss) *ss = 0;
3733 ss = strrchr (ps, '/');
3734 if (!ss)
3735 ss = ps;
3736 else
3737 {
3738 ss = strdup (ss+1);
3739 free (ps);
3740 }
3741 return ss;
3742 }
3743 else
3744 return strdup ("???");
3745 }
3746
3747
3748 static void
reap_zombies(state * s)3749 reap_zombies (state *s)
3750 {
3751 int wait_status = 0;
3752 pid_t pid;
3753 while ((pid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED)) > 0)
3754 {
3755 if (s->debug_p)
3756 {
3757 if (pid == s->running_preview_pid)
3758 {
3759 char *ss = subproc_pretty_name (s);
3760 fprintf (stderr, "%s: pid %lu (%s) died\n", blurb(),
3761 (unsigned long) pid, ss);
3762 free (ss);
3763 }
3764 else
3765 fprintf (stderr, "%s: pid %lu died\n", blurb(),
3766 (unsigned long) pid);
3767 }
3768 }
3769 }
3770
3771
3772 /* Mostly lifted from driver/subprocs.c */
3773 static Visual *
get_best_gl_visual(state * s)3774 get_best_gl_visual (state *s)
3775 {
3776 Display *dpy = GDK_DISPLAY();
3777 pid_t forked;
3778 int fds [2];
3779 int in, out;
3780 char buf[1024];
3781
3782 char *av[10];
3783 int ac = 0;
3784
3785 av[ac++] = "xscreensaver-gl-helper";
3786 av[ac] = 0;
3787
3788 if (pipe (fds))
3789 {
3790 perror ("error creating pipe:");
3791 return 0;
3792 }
3793
3794 in = fds [0];
3795 out = fds [1];
3796
3797 switch ((int) (forked = fork ()))
3798 {
3799 case -1:
3800 {
3801 sprintf (buf, "%s: couldn't fork", blurb());
3802 perror (buf);
3803 exit (1);
3804 }
3805 case 0:
3806 {
3807 int stdout_fd = 1;
3808
3809 close (in); /* don't need this one */
3810 close (ConnectionNumber (dpy)); /* close display fd */
3811
3812 if (dup2 (out, stdout_fd) < 0) /* pipe stdout */
3813 {
3814 perror ("could not dup() a new stdout:");
3815 return 0;
3816 }
3817
3818 execvp (av[0], av); /* shouldn't return. */
3819
3820 if (errno != ENOENT)
3821 {
3822 /* Ignore "no such file or directory" errors, unless verbose.
3823 Issue all other exec errors, though. */
3824 sprintf (buf, "%s: running %s", blurb(), av[0]);
3825 perror (buf);
3826 }
3827
3828 /* Note that one must use _exit() instead of exit() in procs forked
3829 off of Gtk programs -- Gtk installs an atexit handler that has a
3830 copy of the X connection (which we've already closed, for safety.)
3831 If one uses exit() instead of _exit(), then one sometimes gets a
3832 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
3833 */
3834 _exit (1); /* exits fork */
3835 break;
3836 }
3837 default:
3838 {
3839 int result = 0;
3840 int wait_status = 0;
3841
3842 FILE *f = fdopen (in, "r");
3843 unsigned int v = 0;
3844 char c;
3845
3846 close (out); /* don't need this one */
3847
3848 *buf = 0;
3849 if (!fgets (buf, sizeof(buf)-1, f))
3850 *buf = 0;
3851 fclose (f);
3852
3853 /* Wait for the child to die. */
3854 waitpid (-1, &wait_status, 0);
3855
3856 if (1 == sscanf (buf, "0x%x %c", &v, &c))
3857 result = (int) v;
3858
3859 if (result == 0)
3860 {
3861 if (s->debug_p)
3862 fprintf (stderr, "%s: %s did not report a GL visual!\n",
3863 blurb(), av[0]);
3864 return 0;
3865 }
3866 else
3867 {
3868 Visual *v = id_to_visual (DefaultScreenOfDisplay (dpy), result);
3869 if (s->debug_p)
3870 fprintf (stderr, "%s: %s says the GL visual is 0x%X.\n",
3871 blurb(), av[0], result);
3872 if (!v) abort();
3873 return v;
3874 }
3875 }
3876 }
3877
3878 abort();
3879 }
3880
3881
3882 static void
kill_preview_subproc(state * s,Bool reset_p)3883 kill_preview_subproc (state *s, Bool reset_p)
3884 {
3885 s->running_preview_error_p = False;
3886
3887 reap_zombies (s);
3888 clear_preview_window (s);
3889
3890 if (s->subproc_check_timer_id)
3891 {
3892 gtk_timeout_remove (s->subproc_check_timer_id);
3893 s->subproc_check_timer_id = 0;
3894 s->subproc_check_countdown = 0;
3895 }
3896
3897 if (s->running_preview_pid)
3898 {
3899 int status = kill (s->running_preview_pid, SIGTERM);
3900 char *ss = subproc_pretty_name (s);
3901
3902 if (status < 0)
3903 {
3904 if (errno == ESRCH)
3905 {
3906 if (s->debug_p)
3907 fprintf (stderr, "%s: pid %lu (%s) was already dead.\n",
3908 blurb(), (unsigned long) s->running_preview_pid, ss);
3909 }
3910 else
3911 {
3912 char buf [1024];
3913 sprintf (buf, "%s: couldn't kill pid %lu (%s)",
3914 blurb(), (unsigned long) s->running_preview_pid, ss);
3915 perror (buf);
3916 }
3917 }
3918 else {
3919 int endstatus;
3920 waitpid(s->running_preview_pid, &endstatus, 0);
3921 if (s->debug_p)
3922 fprintf (stderr, "%s: killed pid %lu (%s)\n", blurb(),
3923 (unsigned long) s->running_preview_pid, ss);
3924 }
3925
3926 free (ss);
3927 s->running_preview_pid = 0;
3928 if (s->running_preview_cmd) free (s->running_preview_cmd);
3929 s->running_preview_cmd = 0;
3930 }
3931
3932 reap_zombies (s);
3933
3934 if (reset_p)
3935 {
3936 reset_preview_window (s);
3937 clear_preview_window (s);
3938 }
3939 }
3940
3941
3942 /* Immediately and unconditionally launches the given process,
3943 after appending the -window-id option; sets running_preview_pid.
3944 */
3945 static void
launch_preview_subproc(state * s)3946 launch_preview_subproc (state *s)
3947 {
3948 saver_preferences *p = &s->prefs;
3949 Window id;
3950 char *new_cmd = 0;
3951 pid_t forked;
3952 const char *cmd = s->desired_preview_cmd;
3953
3954 GtkWidget *pr = name_to_widget (s, "preview");
3955 GdkWindow *window;
3956
3957 reset_preview_window (s);
3958
3959 window = GET_WINDOW (pr);
3960
3961 s->running_preview_error_p = False;
3962
3963 if (s->preview_suppressed_p)
3964 {
3965 kill_preview_subproc (s, False);
3966 goto DONE;
3967 }
3968
3969 new_cmd = malloc (strlen (cmd) + 40);
3970
3971 id = (window ? GDK_WINDOW_XWINDOW (window) : 0);
3972 if (id == 0)
3973 {
3974 /* No window id? No command to run. */
3975 free (new_cmd);
3976 new_cmd = 0;
3977 }
3978 else
3979 {
3980 /* We do this instead of relying on $XSCREENSAVER_WINDOW specifically
3981 so that third-party savers that don't implement -window-id will fail:
3982 otherwise we might have full-screen windows popping up when we were
3983 just trying to get a preview thumbnail.
3984 */
3985 strcpy (new_cmd, cmd);
3986 sprintf (new_cmd + strlen (new_cmd), " -window-id 0x%X",
3987 (unsigned int) id);
3988 }
3989
3990 kill_preview_subproc (s, False);
3991 if (! new_cmd)
3992 {
3993 s->running_preview_error_p = True;
3994 clear_preview_window (s);
3995 goto DONE;
3996 }
3997
3998 switch ((int) (forked = fork ()))
3999 {
4000 case -1:
4001 {
4002 char buf[255];
4003 sprintf (buf, "%s: couldn't fork", blurb());
4004 perror (buf);
4005 s->running_preview_error_p = True;
4006 goto DONE;
4007 break;
4008 }
4009 case 0:
4010 {
4011 close (ConnectionNumber (GDK_DISPLAY()));
4012
4013 hack_subproc_environment (id, s->debug_p);
4014
4015 usleep (250000); /* pause for 1/4th second before launching, to give
4016 the previous program time to die and flush its X
4017 buffer, so we don't get leftover turds on the
4018 window. */
4019
4020 exec_command (p->shell, new_cmd, p->nice_inferior);
4021 /* Don't bother printing an error message when we are unable to
4022 exec subprocesses; we handle that by polling the pid later.
4023
4024 Note that one must use _exit() instead of exit() in procs forked
4025 off of Gtk programs -- Gtk installs an atexit handler that has a
4026 copy of the X connection (which we've already closed, for safety.)
4027 If one uses exit() instead of _exit(), then one sometimes gets a
4028 spurious "Gdk-ERROR: Fatal IO error on X server" error message.
4029 */
4030 _exit (1); /* exits child fork */
4031 break;
4032
4033 default:
4034
4035 if (s->running_preview_cmd) free (s->running_preview_cmd);
4036 s->running_preview_cmd = strdup (s->desired_preview_cmd);
4037 s->running_preview_pid = forked;
4038
4039 if (s->debug_p)
4040 {
4041 char *ss = subproc_pretty_name (s);
4042 fprintf (stderr, "%s: forked %lu (%s)\n", blurb(),
4043 (unsigned long) forked, ss);
4044 free (ss);
4045 }
4046 break;
4047 }
4048 }
4049
4050 schedule_preview_check (s);
4051
4052 DONE:
4053 if (new_cmd) free (new_cmd);
4054 new_cmd = 0;
4055 }
4056
4057
4058 /* Modify $DISPLAY and $PATH for the benefit of subprocesses.
4059 */
4060 static void
hack_environment(state * s)4061 hack_environment (state *s)
4062 {
4063 static const char *def_path =
4064 # ifdef DEFAULT_PATH_PREFIX
4065 DEFAULT_PATH_PREFIX;
4066 # else
4067 "";
4068 # endif
4069
4070 Display *dpy = GDK_DISPLAY();
4071 const char *odpy = DisplayString (dpy);
4072 char *ndpy = (char *) malloc(strlen(odpy) + 20);
4073 strcpy (ndpy, "DISPLAY=");
4074 strcat (ndpy, odpy);
4075 if (putenv (ndpy))
4076 abort ();
4077
4078 if (s->debug_p)
4079 fprintf (stderr, "%s: %s\n", blurb(), ndpy);
4080
4081 /* don't free(ndpy) -- some implementations of putenv (BSD 4.4, glibc
4082 2.0) copy the argument, but some (libc4,5, glibc 2.1.2) do not.
4083 So we must leak it (and/or the previous setting). Yay.
4084 */
4085
4086 if (def_path && *def_path)
4087 {
4088 const char *opath = getenv("PATH");
4089 char *npath = (char *) malloc(strlen(def_path) + strlen(opath) + 20);
4090 strcpy (npath, "PATH=");
4091 strcat (npath, def_path);
4092 strcat (npath, ":");
4093 strcat (npath, opath);
4094
4095 if (putenv (npath))
4096 abort ();
4097 /* do not free(npath) -- see above */
4098
4099 if (s->debug_p)
4100 fprintf (stderr, "%s: added \"%s\" to $PATH\n", blurb(), def_path);
4101 }
4102 }
4103
4104
4105 static void
hack_subproc_environment(Window preview_window_id,Bool debug_p)4106 hack_subproc_environment (Window preview_window_id, Bool debug_p)
4107 {
4108 /* Store a window ID in $XSCREENSAVER_WINDOW -- this isn't strictly
4109 necessary yet, but it will make programs work if we had invoked
4110 them with "-root" and not with "-window-id" -- which, of course,
4111 doesn't happen.
4112 */
4113 char *nssw = (char *) malloc (40);
4114 sprintf (nssw, "XSCREENSAVER_WINDOW=0x%X", (unsigned int) preview_window_id);
4115
4116 /* Allegedly, BSD 4.3 didn't have putenv(), but nobody runs such systems
4117 any more, right? It's not Posix, but everyone seems to have it. */
4118 if (putenv (nssw))
4119 abort ();
4120
4121 if (debug_p)
4122 fprintf (stderr, "%s: %s\n", blurb(), nssw);
4123
4124 /* do not free(nssw) -- see above */
4125 }
4126
4127
4128 /* Called from a timer:
4129 Launches the currently-chosen subprocess, if it's not already running.
4130 If there's a different process running, kills it.
4131 */
4132 static int
update_subproc_timer(gpointer data)4133 update_subproc_timer (gpointer data)
4134 {
4135 state *s = (state *) data;
4136 if (! s->desired_preview_cmd)
4137 kill_preview_subproc (s, True);
4138 else if (!s->running_preview_cmd ||
4139 !!strcmp (s->desired_preview_cmd, s->running_preview_cmd))
4140 launch_preview_subproc (s);
4141
4142 s->subproc_timer_id = 0;
4143 return FALSE; /* do not re-execute timer */
4144 }
4145
4146 static int
settings_timer(gpointer data)4147 settings_timer (gpointer data)
4148 {
4149 settings_cb (0, 0);
4150 return FALSE;
4151 }
4152
4153
4154 /* Call this when you think you might want a preview process running.
4155 It will set a timer that will actually launch that program a second
4156 from now, if you haven't changed your mind (to avoid double-click
4157 spazzing, etc.) `cmd' may be null meaning "no process".
4158 */
4159 static void
schedule_preview(state * s,const char * cmd)4160 schedule_preview (state *s, const char *cmd)
4161 {
4162 int delay = 1000 * 0.5; /* 1/2 second hysteresis */
4163
4164 if (s->debug_p)
4165 {
4166 if (cmd)
4167 fprintf (stderr, "%s: scheduling preview \"%s\"\n", blurb(), cmd);
4168 else
4169 fprintf (stderr, "%s: scheduling preview death\n", blurb());
4170 }
4171
4172 if (s->desired_preview_cmd) free (s->desired_preview_cmd);
4173 s->desired_preview_cmd = (cmd ? strdup (cmd) : 0);
4174
4175 if (s->subproc_timer_id)
4176 gtk_timeout_remove (s->subproc_timer_id);
4177 s->subproc_timer_id = gtk_timeout_add (delay, update_subproc_timer, s);
4178 }
4179
4180
4181 /* Called from a timer:
4182 Checks to see if the subproc that should be running, actually is.
4183 */
4184 static int
check_subproc_timer(gpointer data)4185 check_subproc_timer (gpointer data)
4186 {
4187 state *s = (state *) data;
4188 Bool again_p = True;
4189
4190 if (s->running_preview_error_p || /* already dead */
4191 s->running_preview_pid <= 0)
4192 {
4193 again_p = False;
4194 }
4195 else
4196 {
4197 int status;
4198 reap_zombies (s);
4199 status = kill (s->running_preview_pid, 0);
4200 if (status < 0 && errno == ESRCH)
4201 s->running_preview_error_p = True;
4202
4203 if (s->debug_p)
4204 {
4205 char *ss = subproc_pretty_name (s);
4206 fprintf (stderr, "%s: timer: pid %lu (%s) is %s\n", blurb(),
4207 (unsigned long) s->running_preview_pid, ss,
4208 (s->running_preview_error_p ? "dead" : "alive"));
4209 free (ss);
4210 }
4211
4212 if (s->running_preview_error_p)
4213 {
4214 clear_preview_window (s);
4215 again_p = False;
4216 }
4217 }
4218
4219 /* Otherwise, it's currently alive. We might be checking again, or we
4220 might be satisfied. */
4221
4222 if (--s->subproc_check_countdown <= 0)
4223 again_p = False;
4224
4225 if (again_p)
4226 return TRUE; /* re-execute timer */
4227 else
4228 {
4229 s->subproc_check_timer_id = 0;
4230 s->subproc_check_countdown = 0;
4231 return FALSE; /* do not re-execute timer */
4232 }
4233 }
4234
4235
4236 /* Call this just after launching a subprocess.
4237 This sets a timer that will, five times a second for two seconds,
4238 check whether the program is still running. The assumption here
4239 is that if the process didn't stay up for more than a couple of
4240 seconds, then either the program doesn't exist, or it doesn't
4241 take a -window-id argument.
4242 */
4243 static void
schedule_preview_check(state * s)4244 schedule_preview_check (state *s)
4245 {
4246 int seconds = 2;
4247 int ticks = 5;
4248
4249 if (s->debug_p)
4250 fprintf (stderr, "%s: scheduling check\n", blurb());
4251
4252 if (s->subproc_check_timer_id)
4253 gtk_timeout_remove (s->subproc_check_timer_id);
4254 s->subproc_check_timer_id =
4255 gtk_timeout_add (1000 / ticks,
4256 check_subproc_timer, (gpointer) s);
4257 s->subproc_check_countdown = ticks * seconds;
4258 }
4259
4260
4261 static Bool
screen_blanked_p(void)4262 screen_blanked_p (void)
4263 {
4264 Atom type;
4265 int format;
4266 unsigned long nitems, bytesafter;
4267 unsigned char *dataP = 0;
4268 Display *dpy = GDK_DISPLAY();
4269 Bool blanked_p = False;
4270
4271 if (XGetWindowProperty (dpy, RootWindow (dpy, 0), /* always screen #0 */
4272 XA_SCREENSAVER_STATUS,
4273 0, 3, False, XA_INTEGER,
4274 &type, &format, &nitems, &bytesafter,
4275 &dataP)
4276 == Success
4277 && type == XA_INTEGER
4278 && nitems >= 3
4279 && dataP)
4280 {
4281 Atom *data = (Atom *) dataP;
4282 blanked_p = (data[0] == XA_BLANK || data[0] == XA_LOCK);
4283 }
4284
4285 if (dataP) XFree (dataP);
4286
4287 return blanked_p;
4288 }
4289
4290 /* Wake up every now and then and see if the screen is blanked.
4291 If it is, kill off the small-window demo -- no point in wasting
4292 cycles by running two screensavers at once...
4293 */
4294 static int
check_blanked_timer(gpointer data)4295 check_blanked_timer (gpointer data)
4296 {
4297 state *s = (state *) data;
4298 Bool blanked_p = screen_blanked_p ();
4299 if (blanked_p && s->running_preview_pid)
4300 {
4301 if (s->debug_p)
4302 fprintf (stderr, "%s: screen is blanked: killing preview\n", blurb());
4303 kill_preview_subproc (s, True);
4304 }
4305
4306 return True; /* re-execute timer */
4307 }
4308
4309
4310 /* How many screens are there (including Xinerama.)
4311 */
4312 static int
screen_count(Display * dpy)4313 screen_count (Display *dpy)
4314 {
4315 int nscreens = ScreenCount(dpy);
4316 # ifdef HAVE_XINERAMA
4317 if (nscreens <= 1)
4318 {
4319 int event_number, error_number;
4320 if (XineramaQueryExtension (dpy, &event_number, &error_number) &&
4321 XineramaIsActive (dpy))
4322 {
4323 XineramaScreenInfo *xsi = XineramaQueryScreens (dpy, &nscreens);
4324 if (xsi) XFree (xsi);
4325 }
4326 }
4327 # endif /* HAVE_XINERAMA */
4328
4329 return nscreens;
4330 }
4331
4332
4333 /* Setting window manager icon
4334 */
4335
4336 static void
init_icon(GdkWindow * window)4337 init_icon (GdkWindow *window)
4338 {
4339 GdkBitmap *mask = 0;
4340 GdkPixmap *pixmap =
4341 gdk_pixmap_create_from_xpm_d (window, &mask, 0,
4342 (gchar **) logo_50_xpm);
4343 if (pixmap)
4344 gdk_window_set_icon (window, 0, pixmap, mask);
4345 }
4346
4347
4348 /* The main demo-mode command loop.
4349 */
4350
4351 #if 0
4352 static Bool
4353 mapper (XrmDatabase *db, XrmBindingList bindings, XrmQuarkList quarks,
4354 XrmRepresentation *type, XrmValue *value, XPointer closure)
4355 {
4356 int i;
4357 for (i = 0; quarks[i]; i++)
4358 {
4359 if (bindings[i] == XrmBindTightly)
4360 fprintf (stderr, (i == 0 ? "" : "."));
4361 else if (bindings[i] == XrmBindLoosely)
4362 fprintf (stderr, "*");
4363 else
4364 fprintf (stderr, " ??? ");
4365 fprintf(stderr, "%s", XrmQuarkToString (quarks[i]));
4366 }
4367
4368 fprintf (stderr, ": %s\n", (char *) value->addr);
4369
4370 return False;
4371 }
4372 #endif
4373
4374
4375 static Window
gnome_screensaver_window(Screen * screen)4376 gnome_screensaver_window (Screen *screen)
4377 {
4378 Display *dpy = DisplayOfScreen (screen);
4379 Window root = RootWindowOfScreen (screen);
4380 Window parent, *kids;
4381 unsigned int nkids;
4382 Window gnome_window = 0;
4383 int i;
4384
4385 if (! XQueryTree (dpy, root, &root, &parent, &kids, &nkids))
4386 abort ();
4387 for (i = 0; i < nkids; i++)
4388 {
4389 Atom type;
4390 int format;
4391 unsigned long nitems, bytesafter;
4392 unsigned char *name;
4393 if (XGetWindowProperty (dpy, kids[i], XA_WM_COMMAND, 0, 128,
4394 False, XA_STRING, &type, &format, &nitems,
4395 &bytesafter, &name)
4396 == Success
4397 && type != None
4398 && !strcmp ((char *) name, "gnome-screensaver"))
4399 {
4400 gnome_window = kids[i];
4401 break;
4402 }
4403 }
4404
4405 if (kids) XFree ((char *) kids);
4406 return gnome_window;
4407 }
4408
4409 static Bool
gnome_screensaver_active_p(void)4410 gnome_screensaver_active_p (void)
4411 {
4412 Display *dpy = GDK_DISPLAY();
4413 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4414 return (w ? True : False);
4415 }
4416
4417 static void
kill_gnome_screensaver(void)4418 kill_gnome_screensaver (void)
4419 {
4420 Display *dpy = GDK_DISPLAY();
4421 Window w = gnome_screensaver_window (DefaultScreenOfDisplay (dpy));
4422 if (w) XKillClient (dpy, (XID) w);
4423 }
4424
4425 static Bool
kde_screensaver_active_p(void)4426 kde_screensaver_active_p (void)
4427 {
4428 FILE *p = popen ("dcop kdesktop KScreensaverIface isEnabled 2>/dev/null",
4429 "r");
4430 char buf[255];
4431 if (!p) return False;
4432 if (!fgets (buf, sizeof(buf)-1, p)) return False;
4433 pclose (p);
4434 if (!strcmp (buf, "true\n"))
4435 return True;
4436 else
4437 return False;
4438 }
4439
4440 static void
kill_kde_screensaver(void)4441 kill_kde_screensaver (void)
4442 {
4443 /* Use empty body to kill warning from gcc -Wall with
4444 "warning: ignoring return value of 'system',
4445 declared with attribute warn_unused_result"
4446 */
4447 if (system ("dcop kdesktop KScreensaverIface enable false")) {}
4448 }
4449
4450
4451 static void
the_network_is_not_the_computer(state * s)4452 the_network_is_not_the_computer (state *s)
4453 {
4454 Display *dpy = GDK_DISPLAY();
4455 char *rversion = 0, *ruser = 0, *rhost = 0;
4456 char *luser, *lhost;
4457 char *msg = 0;
4458 struct passwd *p = getpwuid (getuid ());
4459 const char *d = DisplayString (dpy);
4460
4461 # if defined(HAVE_UNAME)
4462 struct utsname uts;
4463 if (uname (&uts) < 0)
4464 lhost = "<UNKNOWN>";
4465 else
4466 lhost = uts.nodename;
4467 # elif defined(VMS)
4468 strcpy (lhost, getenv("SYS$NODE"));
4469 # else /* !HAVE_UNAME && !VMS */
4470 strcat (lhost, "<UNKNOWN>");
4471 # endif /* !HAVE_UNAME && !VMS */
4472
4473 if (p && p->pw_name)
4474 luser = p->pw_name;
4475 else
4476 luser = "???";
4477
4478 server_xscreensaver_version (dpy, &rversion, &ruser, &rhost);
4479
4480 /* Make a buffer that's big enough for a number of copies of all the
4481 strings, plus some. */
4482 msg = (char *) malloc (10 * ((rversion ? strlen(rversion) : 0) +
4483 (ruser ? strlen(ruser) : 0) +
4484 (rhost ? strlen(rhost) : 0) +
4485 strlen(lhost) +
4486 strlen(luser) +
4487 strlen(d) +
4488 1024));
4489 *msg = 0;
4490
4491 if (!rversion || !*rversion)
4492 {
4493 sprintf (msg,
4494 _("Warning:\n\n"
4495 "The XScreenSaver daemon doesn't seem to be running\n"
4496 "on display \"%s\". Launch it now?"),
4497 d);
4498 }
4499 else if (p && ruser && *ruser && !!strcmp (ruser, p->pw_name))
4500 {
4501 /* Warn that the two processes are running as different users.
4502 */
4503 sprintf(msg,
4504 _("Warning:\n\n"
4505 "%s is running as user \"%s\" on host \"%s\".\n"
4506 "But the xscreensaver managing display \"%s\"\n"
4507 "is running as user \"%s\" on host \"%s\".\n"
4508 "\n"
4509 "Since they are different users, they won't be reading/writing\n"
4510 "the same ~/.xscreensaver file, so %s isn't\n"
4511 "going to work right.\n"
4512 "\n"
4513 "You should either re-run %s as \"%s\", or re-run\n"
4514 "xscreensaver as \"%s\".\n"
4515 "\n"
4516 "Restart the xscreensaver daemon now?\n"),
4517 progname, luser, lhost,
4518 d,
4519 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4520 progname,
4521 progname, (ruser ? ruser : "???"),
4522 luser);
4523 }
4524 else if (rhost && *rhost && !!strcmp (rhost, lhost))
4525 {
4526 /* Warn that the two processes are running on different hosts.
4527 */
4528 sprintf (msg,
4529 _("Warning:\n\n"
4530 "%s is running as user \"%s\" on host \"%s\".\n"
4531 "But the xscreensaver managing display \"%s\"\n"
4532 "is running as user \"%s\" on host \"%s\".\n"
4533 "\n"
4534 "If those two machines don't share a file system (that is,\n"
4535 "if they don't see the same ~%s/.xscreensaver file) then\n"
4536 "%s won't work right.\n"
4537 "\n"
4538 "Restart the daemon on \"%s\" as \"%s\" now?\n"),
4539 progname, luser, lhost,
4540 d,
4541 (ruser ? ruser : "???"), (rhost ? rhost : "???"),
4542 luser,
4543 progname,
4544 lhost, luser);
4545 }
4546 else if (!!strcmp (rversion, s->short_version))
4547 {
4548 /* Warn that the version numbers don't match.
4549 */
4550 sprintf (msg,
4551 _("Warning:\n\n"
4552 "This is %s version %s.\n"
4553 "But the xscreensaver managing display \"%s\"\n"
4554 "is version %s. This could cause problems.\n"
4555 "\n"
4556 "Restart the xscreensaver daemon now?\n"),
4557 progname, s->short_version,
4558 d,
4559 rversion);
4560 }
4561
4562
4563 if (*msg)
4564 warning_dialog (s->toplevel_widget, msg, D_LAUNCH, 1);
4565
4566 if (rversion) free (rversion);
4567 if (ruser) free (ruser);
4568 if (rhost) free (rhost);
4569 free (msg);
4570 msg = 0;
4571
4572 /* Note: since these dialogs are not modal, they will stack up.
4573 So we do this check *after* popping up the "xscreensaver is not
4574 running" dialog so that these are on top. Good enough.
4575 */
4576
4577 if (gnome_screensaver_active_p ())
4578 warning_dialog (s->toplevel_widget,
4579 _("Warning:\n\n"
4580 "The GNOME screensaver daemon appears to be running.\n"
4581 "It must be stopped for XScreenSaver to work properly.\n"
4582 "\n"
4583 "Stop the GNOME screen saver daemon now?\n"),
4584 D_GNOME, 1);
4585
4586 if (kde_screensaver_active_p ())
4587 warning_dialog (s->toplevel_widget,
4588 _("Warning:\n\n"
4589 "The KDE screen saver daemon appears to be running.\n"
4590 "It must be stopped for XScreenSaver to work properly.\n"
4591 "\n"
4592 "Stop the KDE screen saver daemon now?\n"),
4593 D_KDE, 1);
4594 }
4595
4596
4597 /* We use this error handler so that X errors are preceeded by the name
4598 of the program that generated them.
4599 */
4600 static int
demo_ehandler(Display * dpy,XErrorEvent * error)4601 demo_ehandler (Display *dpy, XErrorEvent *error)
4602 {
4603 state *s = global_state_kludge; /* I hate C so much... */
4604 fprintf (stderr, "\nX error in %s:\n", blurb());
4605 XmuPrintDefaultErrorMessage (dpy, error, stderr);
4606 kill_preview_subproc (s, False);
4607 exit (-1);
4608 return 0;
4609 }
4610
4611
4612 /* We use this error handler so that Gtk/Gdk errors are preceeded by the name
4613 of the program that generated them; and also that we can ignore one
4614 particular bogus error message that Gdk madly spews.
4615 */
4616 static void
g_log_handler(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)4617 g_log_handler (const gchar *log_domain, GLogLevelFlags log_level,
4618 const gchar *message, gpointer user_data)
4619 {
4620 /* Ignore the message "Got event for unknown window: 0x...".
4621 Apparently some events are coming in for the xscreensaver window
4622 (presumably reply events related to the ClientMessage) and Gdk
4623 feels the need to complain about them. So, just suppress any
4624 messages that look like that one.
4625 */
4626 if (strstr (message, "unknown window"))
4627 return;
4628
4629 fprintf (stderr, "%s: %s-%s: %s%s", blurb(),
4630 (log_domain ? log_domain : progclass),
4631 (log_level == G_LOG_LEVEL_ERROR ? "error" :
4632 log_level == G_LOG_LEVEL_CRITICAL ? "critical" :
4633 log_level == G_LOG_LEVEL_WARNING ? "warning" :
4634 log_level == G_LOG_LEVEL_MESSAGE ? "message" :
4635 log_level == G_LOG_LEVEL_INFO ? "info" :
4636 log_level == G_LOG_LEVEL_DEBUG ? "debug" : "???"),
4637 message,
4638 ((!*message || message[strlen(message)-1] != '\n')
4639 ? "\n" : ""));
4640 }
4641
4642
4643 STFU
4644 static char *defaults[] = {
4645 #include "XScreenSaver_ad.h"
4646 0
4647 };
4648
4649 #if 0
4650 #ifdef HAVE_CRAPPLET
4651 static struct poptOption crapplet_options[] = {
4652 {NULL, '\0', 0, NULL, 0}
4653 };
4654 #endif /* HAVE_CRAPPLET */
4655 #endif /* 0 */
4656
4657 const char *usage = "[--display dpy] [--prefs | --settings]"
4658 # ifdef HAVE_CRAPPLET
4659 " [--crapplet]"
4660 # endif
4661 "\n\t\t [--debug] [--sync] [--no-xshm] [--configdir dir]";
4662
4663 static void
map_popup_window_cb(GtkWidget * w,gpointer user_data)4664 map_popup_window_cb (GtkWidget *w, gpointer user_data)
4665 {
4666 state *s = (state *) user_data;
4667 Boolean oi = s->initializing_p;
4668 #ifndef HAVE_GTK2
4669 GtkLabel *label = GTK_LABEL (name_to_widget (s, "doc"));
4670 #endif
4671 s->initializing_p = True;
4672 #ifndef HAVE_GTK2
4673 eschew_gtk_lossage (label);
4674 #endif
4675 s->initializing_p = oi;
4676 }
4677
4678
4679 #if 0
4680 static void
4681 print_widget_tree (GtkWidget *w, int depth)
4682 {
4683 int i;
4684 for (i = 0; i < depth; i++)
4685 fprintf (stderr, " ");
4686 fprintf (stderr, "%s\n", gtk_widget_get_name (w));
4687
4688 if (GTK_IS_LIST (w))
4689 {
4690 for (i = 0; i < depth+1; i++)
4691 fprintf (stderr, " ");
4692 fprintf (stderr, "...list kids...\n");
4693 }
4694 else if (GTK_IS_CONTAINER (w))
4695 {
4696 GList *kids = gtk_container_children (GTK_CONTAINER (w));
4697 while (kids)
4698 {
4699 print_widget_tree (GTK_WIDGET (kids->data), depth+1);
4700 kids = kids->next;
4701 }
4702 }
4703 }
4704 #endif /* 0 */
4705
4706 static int
delayed_scroll_kludge(gpointer data)4707 delayed_scroll_kludge (gpointer data)
4708 {
4709 state *s = (state *) data;
4710 GtkWidget *w = GTK_WIDGET (name_to_widget (s, "list"));
4711 ensure_selected_item_visible (w);
4712
4713 /* Oh, this is just fucking lovely, too. */
4714 w = GTK_WIDGET (name_to_widget (s, "preview"));
4715 gtk_widget_hide (w);
4716 gtk_widget_show (w);
4717
4718 return FALSE; /* do not re-execute timer */
4719 }
4720
4721 #ifdef HAVE_GTK2
4722
4723 static GtkWidget *
create_xscreensaver_demo(void)4724 create_xscreensaver_demo (void)
4725 {
4726 GtkWidget *nb;
4727
4728 nb = name_to_widget (global_state_kludge, "preview_notebook");
4729 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
4730
4731 return name_to_widget (global_state_kludge, "xscreensaver_demo");
4732 }
4733
4734 static GtkWidget *
create_xscreensaver_settings_dialog(void)4735 create_xscreensaver_settings_dialog (void)
4736 {
4737 GtkWidget *w, *box;
4738
4739 box = name_to_widget (global_state_kludge, "dialog_action_area");
4740
4741 w = name_to_widget (global_state_kludge, "adv_button");
4742 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4743
4744 w = name_to_widget (global_state_kludge, "std_button");
4745 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (box), w, TRUE);
4746
4747 return name_to_widget (global_state_kludge, "xscreensaver_settings_dialog");
4748 }
4749
4750 #endif /* HAVE_GTK2 */
4751
4752 int
main(int argc,char ** argv)4753 main (int argc, char **argv)
4754 {
4755 XtAppContext app;
4756 state S, *s;
4757 saver_preferences *p;
4758 Bool prefs_p = False;
4759 Bool settings_p = False;
4760 int i;
4761 Display *dpy;
4762 Widget toplevel_shell;
4763 char *real_progname = argv[0];
4764 char *window_title;
4765 char *geom = 0;
4766 Bool crapplet_p = False;
4767 char *str;
4768
4769 #ifdef ENABLE_NLS
4770 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
4771 textdomain (GETTEXT_PACKAGE);
4772
4773 # ifdef HAVE_GTK2
4774 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
4775 # else /* !HAVE_GTK2 */
4776 if (!setlocale (LC_ALL, ""))
4777 fprintf (stderr, "%s: locale not supported by C library\n", real_progname);
4778 # endif /* !HAVE_GTK2 */
4779
4780 #endif /* ENABLE_NLS */
4781
4782 str = strrchr (real_progname, '/');
4783 if (str) real_progname = str+1;
4784
4785 s = &S;
4786 memset (s, 0, sizeof(*s));
4787 s->initializing_p = True;
4788 p = &s->prefs;
4789
4790 global_state_kludge = s; /* I hate C so much... */
4791
4792 progname = real_progname;
4793
4794 s->short_version = (char *) malloc (5);
4795 memcpy (s->short_version, screensaver_id + 17, 4);
4796 s->short_version [4] = 0;
4797
4798
4799 /* Register our error message logger for every ``log domain'' known.
4800 There's no way to do this globally, so I grepped the Gtk/Gdk sources
4801 for all of the domains that seem to be in use.
4802 */
4803 {
4804 const char * const domains[] = { 0,
4805 "Gtk", "Gdk", "GLib", "GModule",
4806 "GThread", "Gnome", "GnomeUI" };
4807 for (i = 0; i < countof(domains); i++)
4808 g_log_set_handler (domains[i], G_LOG_LEVEL_MASK, g_log_handler, 0);
4809 }
4810
4811 #ifdef DEFAULT_ICONDIR /* from -D on compile line */
4812 # ifndef HAVE_GTK2
4813 {
4814 const char *dir = DEFAULT_ICONDIR;
4815 if (*dir) add_pixmap_directory (dir);
4816 }
4817 # endif /* !HAVE_GTK2 */
4818 #endif /* DEFAULT_ICONDIR */
4819
4820 /* This is gross, but Gtk understands --display and not -display...
4821 */
4822 for (i = 1; i < argc; i++)
4823 if (argv[i][0] && argv[i][1] &&
4824 !strncmp(argv[i], "-display", strlen(argv[i])))
4825 argv[i] = "--display";
4826
4827
4828 /* We need to parse this arg really early... Sigh. */
4829 for (i = 1; i < argc; i++)
4830 {
4831 if (argv[i] &&
4832 (!strcmp(argv[i], "--crapplet") ||
4833 !strcmp(argv[i], "--capplet")))
4834 {
4835 # if defined(HAVE_CRAPPLET) || defined(HAVE_GTK2)
4836 int j;
4837 crapplet_p = True;
4838 for (j = i; j < argc; j++) /* remove it from the list */
4839 argv[j] = argv[j+1];
4840 argc--;
4841 # else /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4842 fprintf (stderr, "%s: not compiled with --crapplet support\n",
4843 real_progname);
4844 fprintf (stderr, "%s: %s\n", real_progname, usage);
4845 exit (1);
4846 # endif /* !HAVE_CRAPPLET && !HAVE_GTK2 */
4847 }
4848 else if (argv[i] &&
4849 (!strcmp(argv[i], "--debug") ||
4850 !strcmp(argv[i], "-debug") ||
4851 !strcmp(argv[i], "-d")))
4852 {
4853 int j;
4854 s->debug_p = True;
4855 for (j = i; j < argc; j++) /* remove it from the list */
4856 argv[j] = argv[j+1];
4857 argc--;
4858 i--;
4859 }
4860 else if (argv[i] &&
4861 argc > i+1 &&
4862 *argv[i+1] &&
4863 (!strcmp(argv[i], "-geometry") ||
4864 !strcmp(argv[i], "-geom") ||
4865 !strcmp(argv[i], "-geo") ||
4866 !strcmp(argv[i], "-g")))
4867 {
4868 int j;
4869 geom = argv[i+1];
4870 for (j = i; j < argc; j++) /* remove them from the list */
4871 argv[j] = argv[j+2];
4872 argc -= 2;
4873 i -= 2;
4874 }
4875 else if (argv[i] &&
4876 argc > i+1 &&
4877 *argv[i+1] &&
4878 (!strcmp(argv[i], "--configdir")))
4879 {
4880 int j;
4881 struct stat st;
4882 hack_configuration_path = argv[i+1];
4883 for (j = i; j < argc; j++) /* remove them from the list */
4884 argv[j] = argv[j+2];
4885 argc -= 2;
4886 i -= 2;
4887
4888 if (0 != stat (hack_configuration_path, &st))
4889 {
4890 char buf[255];
4891 sprintf (buf, "%s: %.200s", blurb(), hack_configuration_path);
4892 perror (buf);
4893 exit (1);
4894 }
4895 else if (!S_ISDIR (st.st_mode))
4896 {
4897 fprintf (stderr, "%s: not a directory: %s\n",
4898 blurb(), hack_configuration_path);
4899 exit (1);
4900 }
4901 }
4902 }
4903
4904
4905 if (s->debug_p)
4906 fprintf (stderr, "%s: using config directory \"%s\"\n",
4907 progname, hack_configuration_path);
4908
4909
4910 /* Let Gtk open the X connection, then initialize Xt to use that
4911 same connection. Doctor Frankenstein would be proud.
4912 */
4913 # ifdef HAVE_CRAPPLET
4914 if (crapplet_p)
4915 {
4916 GnomeClient *client;
4917 GnomeClientFlags flags = 0;
4918
4919 int init_results = gnome_capplet_init ("screensaver-properties",
4920 s->short_version,
4921 argc, argv, NULL, 0, NULL);
4922 /* init_results is:
4923 0 upon successful initialization;
4924 1 if --init-session-settings was passed on the cmdline;
4925 2 if --ignore was passed on the cmdline;
4926 -1 on error.
4927
4928 So the 1 signifies just to init the settings, and quit, basically.
4929 (Meaning launch the xscreensaver daemon.)
4930 */
4931
4932 if (init_results < 0)
4933 {
4934 # if 0
4935 g_error ("An initialization error occurred while "
4936 "starting xscreensaver-capplet.\n");
4937 # else /* !0 */
4938 fprintf (stderr, "%s: gnome_capplet_init failed: %d\n",
4939 real_progname, init_results);
4940 exit (1);
4941 # endif /* !0 */
4942 }
4943
4944 client = gnome_master_client ();
4945
4946 if (client)
4947 flags = gnome_client_get_flags (client);
4948
4949 if (flags & GNOME_CLIENT_IS_CONNECTED)
4950 {
4951 int token =
4952 gnome_startup_acquire_token ("GNOME_SCREENSAVER_PROPERTIES",
4953 gnome_client_get_id (client));
4954 if (token)
4955 {
4956 char *session_args[20];
4957 int i = 0;
4958 session_args[i++] = real_progname;
4959 session_args[i++] = "--capplet";
4960 session_args[i++] = "--init-session-settings";
4961 session_args[i] = 0;
4962 gnome_client_set_priority (client, 20);
4963 gnome_client_set_restart_style (client, GNOME_RESTART_ANYWAY);
4964 gnome_client_set_restart_command (client, i, session_args);
4965 }
4966 else
4967 {
4968 gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
4969 }
4970
4971 gnome_client_flush (client);
4972 }
4973
4974 if (init_results == 1)
4975 {
4976 system ("xscreensaver -nosplash &");
4977 return 0;
4978 }
4979
4980 }
4981 else
4982 # endif /* HAVE_CRAPPLET */
4983 {
4984 gtk_init (&argc, &argv);
4985 }
4986
4987
4988 /* We must read exactly the same resources as xscreensaver.
4989 That means we must have both the same progclass *and* progname,
4990 at least as far as the resource database is concerned. So,
4991 put "xscreensaver" in argv[0] while initializing Xt.
4992 */
4993 argv[0] = "xscreensaver";
4994 progname = argv[0];
4995
4996
4997 /* Teach Xt to use the Display that Gtk/Gdk have already opened.
4998 */
4999 XtToolkitInitialize ();
5000 app = XtCreateApplicationContext ();
5001 dpy = GDK_DISPLAY();
5002 XtAppSetFallbackResources (app, defaults);
5003 XtDisplayInitialize (app, dpy, progname, progclass, 0, 0, &argc, argv);
5004 toplevel_shell = XtAppCreateShell (progname, progclass,
5005 applicationShellWidgetClass,
5006 dpy, 0, 0);
5007
5008 dpy = XtDisplay (toplevel_shell);
5009 db = XtDatabase (dpy);
5010 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
5011 XSetErrorHandler (demo_ehandler);
5012
5013 /* Let's just ignore these. They seem to confuse Irix Gtk... */
5014 signal (SIGPIPE, SIG_IGN);
5015
5016 /* After doing Xt-style command-line processing, complain about any
5017 unrecognized command-line arguments.
5018 */
5019 for (i = 1; i < argc; i++)
5020 {
5021 char *str = argv[i];
5022 if (str[0] == '-' && str[1] == '-')
5023 str++;
5024 if (!strcmp (str, "-prefs"))
5025 prefs_p = True;
5026 else if (!strcmp (str, "-settings"))
5027 settings_p = True;
5028 else if (crapplet_p)
5029 /* There are lots of random args that we don't care about when we're
5030 started as a crapplet, so just ignore unknown args in that case. */
5031 ;
5032 else
5033 {
5034 fprintf (stderr, _("%s: unknown option: %s\n"), real_progname,
5035 argv[i]);
5036 fprintf (stderr, "%s: %s\n", real_progname, usage);
5037 exit (1);
5038 }
5039 }
5040
5041 /* Load the init file, which may end up consulting the X resource database
5042 and the site-wide app-defaults file. Note that at this point, it's
5043 important that `progname' be "xscreensaver", rather than whatever
5044 was in argv[0].
5045 */
5046 p->db = db;
5047 s->nscreens = screen_count (dpy);
5048
5049 hack_environment (s); /* must be before initialize_sort_map() */
5050
5051 load_init_file (dpy, p);
5052 initialize_sort_map (s);
5053
5054 /* Now that Xt has been initialized, and the resources have been read,
5055 we can set our `progname' variable to something more in line with
5056 reality.
5057 */
5058 progname = real_progname;
5059
5060
5061 #if 0
5062 /* Print out all the resources we read. */
5063 {
5064 XrmName name = { 0 };
5065 XrmClass class = { 0 };
5066 int count = 0;
5067 XrmEnumerateDatabase (db, &name, &class, XrmEnumAllLevels, mapper,
5068 (POINTER) &count);
5069 }
5070 #endif
5071
5072
5073 /* Intern the atoms that xscreensaver_command() needs.
5074 */
5075 XA_VROOT = XInternAtom (dpy, "__SWM_VROOT", False);
5076 XA_SCREENSAVER = XInternAtom (dpy, "SCREENSAVER", False);
5077 XA_SCREENSAVER_VERSION = XInternAtom (dpy, "_SCREENSAVER_VERSION",False);
5078 XA_SCREENSAVER_STATUS = XInternAtom (dpy, "_SCREENSAVER_STATUS", False);
5079 XA_SCREENSAVER_ID = XInternAtom (dpy, "_SCREENSAVER_ID", False);
5080 XA_SCREENSAVER_RESPONSE = XInternAtom (dpy, "_SCREENSAVER_RESPONSE", False);
5081 XA_SELECT = XInternAtom (dpy, "SELECT", False);
5082 XA_DEMO = XInternAtom (dpy, "DEMO", False);
5083 XA_ACTIVATE = XInternAtom (dpy, "ACTIVATE", False);
5084 XA_SUSPEND = XInternAtom (dpy, "SUSPEND", False);
5085 XA_BLANK = XInternAtom (dpy, "BLANK", False);
5086 XA_LOCK = XInternAtom (dpy, "LOCK", False);
5087 XA_NEXT = XInternAtom (dpy, "NEXT", False);
5088 XA_PREV = XInternAtom (dpy, "PREV", False);
5089 XA_EXIT = XInternAtom (dpy, "EXIT", False);
5090 XA_RESTART = XInternAtom (dpy, "RESTART", False);
5091
5092
5093 /* Create the window and all its widgets.
5094 */
5095 s->base_widget = create_xscreensaver_demo ();
5096 s->popup_widget = create_xscreensaver_settings_dialog ();
5097 s->toplevel_widget = s->base_widget;
5098
5099
5100 /* Set the main window's title. */
5101 {
5102 char *base_title = _("Screensaver Preferences");
5103 char *v = (char *) strdup(strchr(screensaver_id, ' '));
5104 char *s1, *s2, *s3, *s4;
5105 s1 = (char *) strchr(v, ' '); s1++;
5106 s2 = (char *) strchr(s1, ' ');
5107 s3 = (char *) strchr(v, '('); s3++;
5108 s4 = (char *) strchr(s3, ')');
5109 *s2 = 0;
5110 *s4 = 0;
5111
5112 window_title = (char *) malloc (strlen (base_title) +
5113 strlen (progclass) +
5114 strlen (s1) + strlen (s3) +
5115 100);
5116 sprintf (window_title, "%s (%s %s, %s)", base_title, progclass, s1, s3);
5117 gtk_window_set_title (GTK_WINDOW (s->toplevel_widget), window_title);
5118 gtk_window_set_title (GTK_WINDOW (s->popup_widget), window_title);
5119 free (v);
5120 }
5121
5122 /* Adjust the (invisible) notebooks on the popup dialog... */
5123 {
5124 GtkNotebook *notebook =
5125 GTK_NOTEBOOK (name_to_widget (s, "opt_notebook"));
5126 GtkWidget *std = GTK_WIDGET (name_to_widget (s, "std_button"));
5127 int page = 0;
5128
5129 # ifdef HAVE_XML
5130 gtk_widget_hide (std);
5131 # else /* !HAVE_XML */
5132 /* Make the advanced page be the only one available. */
5133 gtk_widget_set_sensitive (std, False);
5134 std = GTK_WIDGET (name_to_widget (s, "adv_button"));
5135 gtk_widget_hide (std);
5136 std = GTK_WIDGET (name_to_widget (s, "reset_button"));
5137 gtk_widget_hide (std);
5138 page = 1;
5139 # endif /* !HAVE_XML */
5140
5141 gtk_notebook_set_page (notebook, page);
5142 gtk_notebook_set_show_tabs (notebook, False);
5143 }
5144
5145 /* Various other widget initializations...
5146 */
5147 gtk_signal_connect (GTK_OBJECT (s->toplevel_widget), "delete_event",
5148 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5149 (gpointer) s);
5150 gtk_signal_connect (GTK_OBJECT (s->popup_widget), "delete_event",
5151 GTK_SIGNAL_FUNC (wm_popup_close_cb),
5152 (gpointer) s);
5153
5154 populate_hack_list (s);
5155 populate_prefs_page (s);
5156 sensitize_demo_widgets (s, False);
5157 fix_text_entry_sizes (s);
5158 scroll_to_current_hack (s);
5159
5160 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "cancel_button")),
5161 "map", GTK_SIGNAL_FUNC(map_popup_window_cb),
5162 (gpointer) s);
5163
5164 #ifndef HAVE_GTK2
5165 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "prev")),
5166 "map", GTK_SIGNAL_FUNC(map_prev_button_cb),
5167 (gpointer) s);
5168 gtk_signal_connect (GTK_OBJECT (name_to_widget (s, "next")),
5169 "map", GTK_SIGNAL_FUNC(map_next_button_cb),
5170 (gpointer) s);
5171 #endif /* !HAVE_GTK2 */
5172
5173 /* Hook up callbacks to the items on the mode menu. */
5174 {
5175 GtkOptionMenu *opt = GTK_OPTION_MENU (name_to_widget (s, "mode_menu"));
5176 GtkMenu *menu = GTK_MENU (gtk_option_menu_get_menu (opt));
5177 GList *kids = gtk_container_children (GTK_CONTAINER (menu));
5178 int i;
5179 for (i = 0; kids; kids = kids->next, i++)
5180 {
5181 gtk_signal_connect (GTK_OBJECT (kids->data), "activate",
5182 GTK_SIGNAL_FUNC (mode_menu_item_cb),
5183 (gpointer) s);
5184
5185 /* The "random-same" mode menu item does not appear unless
5186 there are multple screens.
5187 */
5188 if (s->nscreens <= 1 &&
5189 mode_menu_order[i] == RANDOM_HACKS_SAME)
5190 gtk_widget_hide (GTK_WIDGET (kids->data));
5191 }
5192
5193 if (s->nscreens <= 1) /* recompute option-menu size */
5194 {
5195 gtk_widget_unrealize (GTK_WIDGET (menu));
5196 gtk_widget_realize (GTK_WIDGET (menu));
5197 }
5198 }
5199
5200
5201 /* Handle the -prefs command-line argument. */
5202 if (prefs_p)
5203 {
5204 GtkNotebook *notebook =
5205 GTK_NOTEBOOK (name_to_widget (s, "notebook"));
5206 gtk_notebook_set_page (notebook, 1);
5207 }
5208
5209 # ifdef HAVE_CRAPPLET
5210 if (crapplet_p)
5211 {
5212 GtkWidget *capplet;
5213 GtkWidget *outer_vbox;
5214
5215 gtk_widget_hide (s->toplevel_widget);
5216
5217 capplet = capplet_widget_new ();
5218
5219 /* Make there be a "Close" button instead of "OK" and "Cancel" */
5220 # ifdef HAVE_CRAPPLET_IMMEDIATE
5221 capplet_widget_changes_are_immediate (CAPPLET_WIDGET (capplet));
5222 # endif /* HAVE_CRAPPLET_IMMEDIATE */
5223 /* In crapplet-mode, take off the menubar. */
5224 gtk_widget_hide (name_to_widget (s, "menubar"));
5225
5226 /* Reparent our top-level container to be a child of the capplet
5227 window.
5228 */
5229 outer_vbox = GTK_BIN (s->toplevel_widget)->child;
5230 gtk_widget_ref (outer_vbox);
5231 gtk_container_remove (GTK_CONTAINER (s->toplevel_widget),
5232 outer_vbox);
5233 STFU GTK_OBJECT_SET_FLAGS (outer_vbox, GTK_FLOATING);
5234 gtk_container_add (GTK_CONTAINER (capplet), outer_vbox);
5235
5236 /* Find the window above us, and set the title and close handler. */
5237 {
5238 GtkWidget *window = capplet;
5239 while (window && !GTK_IS_WINDOW (window))
5240 window = GET_PARENT (window);
5241 if (window)
5242 {
5243 gtk_window_set_title (GTK_WINDOW (window), window_title);
5244 gtk_signal_connect (GTK_OBJECT (window), "delete_event",
5245 GTK_SIGNAL_FUNC (wm_toplevel_close_cb),
5246 (gpointer) s);
5247 }
5248 }
5249
5250 s->toplevel_widget = capplet;
5251 }
5252 # endif /* HAVE_CRAPPLET */
5253
5254
5255 /* The Gnome folks hate the menubar. I think it's important to have access
5256 to the commands on the File menu (Restart Daemon, etc.) and to the
5257 About and Documentation commands on the Help menu.
5258 */
5259 #if 0
5260 #ifdef HAVE_GTK2
5261 gtk_widget_hide (name_to_widget (s, "menubar"));
5262 #endif
5263 #endif
5264
5265 free (window_title);
5266 window_title = 0;
5267
5268 #ifdef HAVE_GTK2
5269 /* After picking the default size, allow -geometry to override it. */
5270 if (geom)
5271 gtk_window_parse_geometry (GTK_WINDOW (s->toplevel_widget), geom);
5272 #endif
5273
5274 gtk_widget_show (s->toplevel_widget);
5275 init_icon (GET_WINDOW (GTK_WIDGET (s->toplevel_widget))); /* after `show' */
5276 fix_preview_visual (s);
5277
5278 /* Realize page zero, so that we can diddle the scrollbar when the
5279 user tabs back to it -- otherwise, the current hack isn't scrolled
5280 to the first time they tab back there, when started with "-prefs".
5281 (Though it is if they then tab away, and back again.)
5282
5283 #### Bah! This doesn't work. Gtk eats my ass! Someone who
5284 #### understands this crap, explain to me how to make this work.
5285 */
5286 gtk_widget_realize (name_to_widget (s, "demos_table"));
5287
5288
5289 gtk_timeout_add (60 * 1000, check_blanked_timer, s);
5290
5291
5292 /* Handle the --settings command-line argument. */
5293 if (settings_p)
5294 gtk_timeout_add (500, settings_timer, 0);
5295
5296
5297 /* Issue any warnings about the running xscreensaver daemon. */
5298 if (! s->debug_p)
5299 the_network_is_not_the_computer (s);
5300
5301
5302 if (senesculent_p())
5303 warning_dialog (s->toplevel_widget,
5304 _("Warning:\n\n"
5305 "This version of xscreensaver is VERY OLD!\n"
5306 "Please upgrade!\n"
5307 "\n"
5308 "https://www.jwz.org/xscreensaver/\n"
5309 "\n"
5310 "(If this is the latest version that your distro ships, then\n"
5311 "your distro is doing you a disservice. Build from source.)\n"
5312 ),
5313 D_NONE, 7);
5314
5315
5316 /* Run the Gtk event loop, and not the Xt event loop. This means that
5317 if there were Xt timers or fds registered, they would never get serviced,
5318 and if there were any Xt widgets, they would never have events delivered.
5319 Fortunately, we're using Gtk for all of the UI, and only initialized
5320 Xt so that we could process the command line and use the X resource
5321 manager.
5322 */
5323 s->initializing_p = False;
5324
5325 /* This totally sucks -- set a timer that whacks the scrollbar 0.5 seconds
5326 after we start up. Otherwise, it always appears scrolled to the top
5327 when in crapplet-mode. */
5328 gtk_timeout_add (500, delayed_scroll_kludge, s);
5329
5330
5331 #if 1
5332 /* Load every configurator in turn, to scan them for errors all at once. */
5333 if (s->debug_p)
5334 {
5335 int i;
5336 for (i = 0; i < p->screenhacks_count; i++)
5337 {
5338 screenhack *hack = p->screenhacks[i];
5339 conf_data *d = load_configurator (hack->command, s->debug_p);
5340 if (d) free_conf_data (d);
5341 }
5342 }
5343 #endif
5344
5345
5346 # ifdef HAVE_CRAPPLET
5347 if (crapplet_p)
5348 capplet_gtk_main ();
5349 else
5350 # endif /* HAVE_CRAPPLET */
5351 gtk_main ();
5352
5353 kill_preview_subproc (s, False);
5354 exit (0);
5355 }
5356
5357 #endif /* HAVE_GTK -- whole file */
5358