1 /* windows.c --- turning the screen black; dealing with visuals, virtual roots.
2 * xscreensaver, Copyright © 1991-2022 Jamie Zawinski <jwz@jwz.org>
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
18 # include <sys/utsname.h> /* for uname() */
19 #endif /* HAVE_UNAME */
22 #include <pwd.h> /* for getpwuid() */
24 #include <X11/Xutil.h> /* for XSetClassHint() */
25 #include <X11/Xatom.h>
26 #include <X11/Intrinsic.h>
30 # include <X11/extensions/xf86vmode.h>
31 #endif /* HAVE_XF86VMODE */
34 # include <X11/extensions/Xinerama.h>
35 #endif /* HAVE_XINERAMA */
37 #include "xscreensaver.h"
41 #include "screenshot.h"
43 #include "resources.h"
45 #include "font-retry.h"
48 extern int kill (pid_t, int); /* signal() is in sys/signal.h... */
51 ERROR! You must not include vroot.h in this file.
55 static void reset_watchdog_timer (saver_info *si);
58 store_saver_status (saver_info *si)
60 /* The contents of XA_SCREENSAVER_STATUS has LOCK/BLANK/0 in the first slot,
61 the time at which that state began in the second slot, and the ordinal of
62 the running hacks on each screen (1-based) in subsequent slots. Since
63 we don't know the blank-versus-lock status here, we leave whatever was
64 there before unchanged: it will be updated by "xscreensaver".
66 XA_SCREENSAVER_STATUS is stored on the (real) root window of screen 0.
68 XA_SCREENSAVER_VERSION and XA_SCREENSAVER_ID are stored on the unmapped
69 window created by the "xscreensaver" process. ClientMessage events are
70 sent to that window, and the responses are sent via the
71 XA_SCREENSAVER_RESPONSE property on it.
73 These properties are not used on the windows created by "xscreensaver-gfx"
74 for use by the display hacks.
76 See the different version of this function in xscreensaver.c.
78 Display *dpy = si->dpy;
79 Window w = RootWindow (dpy, 0); /* always screen 0 */
81 unsigned char *dataP = 0;
84 unsigned long nitems, bytesafter;
85 int nitems2 = si->nscreens + 2;
88 /* Read the old property, so we can change just our parts. */
89 XGetWindowProperty (dpy, w,
90 XA_SCREENSAVER_STATUS,
91 0, 999, False, XA_INTEGER,
92 &type, &format, &nitems, &bytesafter,
95 status = (PROP32 *) calloc (nitems2, sizeof(PROP32));
97 if (dataP && type == XA_INTEGER && nitems >= 3)
99 status[0] = ((PROP32 *) dataP)[0];
100 status[1] = ((PROP32 *) dataP)[1];
103 for (i = 0; i < si->nscreens; i++)
105 saver_screen_info *ssi = &si->screens[i];
106 status[2 + i] = ssi->current_hack + 1; /* 1-based */
109 XChangeProperty (si->dpy, w, XA_SCREENSAVER_STATUS, XA_INTEGER, 32,
110 PropModeReplace, (unsigned char *) status, nitems2);
113 if (si->prefs.debug_p && si->prefs.verbose_p)
116 fprintf (stderr, "%s: wrote status property: 0x%lx: %s", blurb(),
118 (status[0] == XA_LOCK ? "LOCK" :
119 status[0] == XA_BLANK ? "BLANK" :
120 status[0] == 0 ? "0" : "???"));
121 for (i = 1; i < nitems; i++)
122 fprintf (stderr, ", %lu", status[i]);
123 fprintf (stderr, "\n");
133 initialize_screensaver_window_1 (saver_screen_info *ssi)
135 saver_info *si = ssi->global;
136 saver_preferences *p = &si->prefs;
137 Bool install_cmap_p = ssi->install_cmap_p; /* not p->install_cmap_p */
139 /* This resets the screensaver window as fully as possible, since there's
140 no way of knowing what some random client may have done to us in the
141 meantime. We could just destroy and recreate the window, but that has
142 its own set of problems. (Update: that's exactly what we're doing
146 XSetWindowAttributes attrs;
147 unsigned long attrmask;
148 static Bool printed_visual_info = False; /* only print the message once. */
149 Window horked_window = 0;
151 black.red = black.green = black.blue = 0;
153 if (ssi->cmap == DefaultColormapOfScreen (ssi->screen))
156 if (ssi->current_visual != DefaultVisualOfScreen (ssi->screen))
157 /* It's not the default visual, so we have no choice but to install. */
158 install_cmap_p = True;
164 ssi->cmap = XCreateColormap (si->dpy,
165 RootWindowOfScreen (ssi->screen),
166 ssi->current_visual, AllocNone);
167 if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort ();
168 ssi->black_pixel = black.pixel;
173 Colormap def_cmap = DefaultColormapOfScreen (ssi->screen);
176 XFreeColors (si->dpy, ssi->cmap, &ssi->black_pixel, 1, 0);
177 if (ssi->cmap != def_cmap)
178 XFreeColormap (si->dpy, ssi->cmap);
180 ssi->cmap = def_cmap;
181 ssi->black_pixel = BlackPixelOfScreen (ssi->screen);
184 attrmask = (CWOverrideRedirect | CWEventMask | CWBackingStore | CWColormap |
185 CWBackPixel | CWBackingPixel | CWBorderPixel);
186 attrs.override_redirect = True;
188 attrs.event_mask = (KeyPressMask | KeyReleaseMask |
189 ButtonPressMask | ButtonReleaseMask |
192 attrs.backing_store = Always;
193 attrs.colormap = ssi->cmap;
194 attrs.background_pixel = ssi->black_pixel;
195 attrs.backing_pixel = ssi->black_pixel;
196 attrs.border_pixel = ssi->black_pixel;
198 printed_visual_info = True; /* Too noisy */
200 if (!p->verbose_p || printed_visual_info)
202 else if (ssi->current_visual == DefaultVisualOfScreen (ssi->screen))
204 fprintf (stderr, "%s: %d: visual ", blurb(), ssi->number);
205 describe_visual (stderr, ssi->screen, ssi->current_visual,
210 fprintf (stderr, "%s: using visual: ", blurb());
211 describe_visual (stderr, ssi->screen, ssi->current_visual,
213 fprintf (stderr, "%s: default visual: ", blurb());
214 describe_visual (stderr, ssi->screen,
215 DefaultVisualOfScreen (ssi->screen),
216 ssi->install_cmap_p);
218 printed_visual_info = True;
220 if (ssi->error_dialog)
222 defer_XDestroyWindow (si->app, si->dpy, ssi->error_dialog);
223 ssi->error_dialog = 0;
226 if (ssi->screensaver_window)
228 XWindowChanges changes;
229 unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
232 changes.width = ssi->width;
233 changes.height = ssi->height;
234 changes.border_width = 0;
236 /* XConfigureWindow and XChangeWindowAttributes can fail if a hack did
237 something weird to the window. In that case, we must destroy and
239 if (! (XConfigureWindow (si->dpy, ssi->screensaver_window,
240 changesmask, &changes) &&
241 XChangeWindowAttributes (si->dpy, ssi->screensaver_window,
244 horked_window = ssi->screensaver_window;
245 ssi->screensaver_window = 0;
249 if (!ssi->screensaver_window)
251 ssi->screensaver_window =
252 XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen),
253 ssi->x, ssi->y, ssi->width, ssi->height,
254 0, ssi->current_depth, InputOutput,
255 ssi->current_visual, attrmask, &attrs);
256 xscreensaver_set_wm_atoms (si->dpy, ssi->screensaver_window,
257 ssi->width, ssi->height, 0);
262 "%s: someone horked our saver window (0x%lx)! Recreating it...\n",
263 blurb(), (unsigned long) horked_window);
264 defer_XDestroyWindow (si->app, si->dpy, horked_window);
267 if (p->verbose_p > 1)
268 fprintf (stderr, "%s: %d: saver window is 0x%lx\n",
269 blurb(), ssi->number,
270 (unsigned long) ssi->screensaver_window);
276 XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window,
278 BlackPixelOfScreen (ssi->screen),
279 BlackPixelOfScreen (ssi->screen),
281 ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black,
283 XFreePixmap (si->dpy, bit);
286 XSetWindowBackground (si->dpy, ssi->screensaver_window, ssi->black_pixel);
289 XUndefineCursor (si->dpy, ssi->screensaver_window);
291 XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
296 initialize_screensaver_window (saver_info *si)
299 for (i = 0; i < si->nscreens; i++)
300 initialize_screensaver_window_1 (&si->screens[i]);
305 raise_window (saver_screen_info *ssi)
307 saver_info *si = ssi->global;
308 if (ssi->error_dialog)
310 /* Make the error be topmost, and the saver be directly below it. */
312 windows[0] = ssi->error_dialog;
313 windows[1] = ssi->screensaver_window;
314 XMapRaised (si->dpy, windows[0]);
315 XRestackWindows (si->dpy, windows, countof(windows));
318 XMapRaised (si->dpy, ssi->screensaver_window);
321 XInstallColormap (si->dpy, ssi->cmap);
325 /* Called when the RANDR (Resize and Rotate) extension tells us that
326 the size of the screen has changed while the screen was blanked.
327 Call update_screen_layout() first, then call this to synchronize
328 the size of the saver windows to the new sizes of the screens.
331 resize_screensaver_window (saver_info *si)
333 saver_preferences *p = &si->prefs;
336 for (i = 0; i < si->nscreens; i++)
338 saver_screen_info *ssi = &si->screens[i];
339 XWindowAttributes xgwa;
341 /* Make sure a window exists -- it might not if a monitor was just
342 added for the first time.
344 if (! ssi->screensaver_window)
346 initialize_screensaver_window_1 (ssi);
349 "%s: %d: newly added window 0x%lx %dx%d+%d+%d\n",
350 blurb(), i, (unsigned long) ssi->screensaver_window,
351 ssi->width, ssi->height, ssi->x, ssi->y);
354 /* Make sure the window is the right size -- it might not be if
355 the monitor changed resolution, or if a badly-behaved hack
358 XGetWindowAttributes (si->dpy, ssi->screensaver_window, &xgwa);
359 if (xgwa.x != ssi->x ||
361 xgwa.width != ssi->width ||
362 xgwa.height != ssi->height)
364 XWindowChanges changes;
365 unsigned int changesmask = CWX|CWY|CWWidth|CWHeight|CWBorderWidth;
368 changes.width = ssi->width;
369 changes.height = ssi->height;
370 changes.border_width = 0;
374 "%s: %d: resize 0x%lx from %dx%d+%d+%d to %dx%d+%d+%d\n",
375 blurb(), i, (unsigned long) ssi->screensaver_window,
376 xgwa.width, xgwa.height, xgwa.x, xgwa.y,
377 ssi->width, ssi->height, ssi->x, ssi->y);
379 if (! XConfigureWindow (si->dpy, ssi->screensaver_window,
380 changesmask, &changes))
381 fprintf (stderr, "%s: %d: someone horked our saver window"
382 " (0x%lx)! Unable to resize it!\n",
383 blurb(), i, (unsigned long) ssi->screensaver_window);
386 /* Now (if blanked) make sure that it's mapped and running a hack --
387 it might not be if we just added it. (We also might be re-using
388 an old window that existed for a previous monitor that was
389 removed and re-added.)
391 Note that spawn_screenhack() calls select_visual() which may destroy
392 and re-create the window via initialize_screensaver_window_1().
395 XSync (si->dpy, False);
397 spawn_screenhack (ssi);
400 /* Kill off any savers running on no-longer-extant monitors.
402 for (; i < si->ssi_count; i++)
404 saver_screen_info *ssi = &si->screens[i];
406 kill_screenhack (ssi);
407 if (ssi->screensaver_window)
409 XUnmapWindow (si->dpy, ssi->screensaver_window);
416 raise_windows (saver_info *si)
419 for (i = 0; i < si->nscreens; i++)
421 saver_screen_info *ssi = &si->screens[i];
427 /* Called only once, before the main loop begins.
430 blank_screen (saver_info *si)
432 saver_preferences *p = &si->prefs;
433 Bool user_active_p = False;
436 initialize_screensaver_window (si);
437 sync_server_dpms_settings (si->dpy, p);
439 /* Save a screenshot. Must be before fade-out. */
440 for (i = 0; i < si->nscreens; i++)
442 saver_screen_info *ssi = &si->screens[i];
444 XFreePixmap (si->dpy, ssi->screenshot);
446 screenshot_grab (si->dpy, ssi->screensaver_window, False, p->verbose_p);
453 Window *current_windows = (Window *)
454 malloc (si->nscreens * sizeof(*current_windows));
455 if (!current_windows) abort();
457 for (i = 0; i < si->nscreens; i++)
459 saver_screen_info *ssi = &si->screens[i];
460 current_windows[i] = ssi->screensaver_window;
461 /* Ensure that the default background of the window is really black,
462 not a pixmap or something. (This does not clear the window.) */
463 XSetWindowBackground (si->dpy, ssi->screensaver_window,
467 if (p->verbose_p) fprintf (stderr, "%s: fading...\n", blurb());
469 /* This will take several seconds to complete. */
470 user_active_p = fade_screens (si->app, si->dpy,
471 current_windows, si->nscreens,
472 p->fade_seconds / 1000.0,
474 True, /* from_desktop_p */
476 free (current_windows);
480 else if (user_active_p)
481 fprintf (stderr, "%s: fading aborted\n", blurb());
483 fprintf (stderr, "%s: fading done\n", blurb());
487 reset_watchdog_timer (si);
489 /* user_active_p means that the user aborted the fade-out -- but that does
490 not mean that we are necessarily about to exit. If we are locking, then
491 the user activity will cause the unlock dialog to appear, but
492 authentication might not succeed. */
494 for (i = 0; i < si->nscreens; i++)
495 /* This also queues each screen's cycle_timer. */
496 spawn_screenhack (&si->screens[i]);
498 /* Turn off "next" and "prev" modes after they have happened once. */
499 if (si->selection_mode < 0)
500 si->selection_mode = 0;
502 /* If we are blanking only, optionally power down monitor right now. */
503 if (p->mode == BLANK_ONLY &&
505 monitor_power_on (si, False);
509 /* Called only once, upon receipt of SIGTERM, just before exiting.
512 unblank_screen (saver_info *si)
514 saver_preferences *p = &si->prefs;
515 Bool unfade_p = p->unfade_p;
518 monitor_power_on (si, True);
525 double seconds = p->fade_seconds / 1000.0;
526 double ratio = 1/3.0;
527 Window *current_windows = (Window *)
528 calloc(sizeof(Window), si->nscreens);
529 Bool interrupted_p = False;
531 for (i = 0; i < si->nscreens; i++)
533 saver_screen_info *ssi = &si->screens[i];
534 current_windows[i] = ssi->screensaver_window;
537 if (p->verbose_p) fprintf (stderr, "%s: unfading...\n", blurb());
539 monitor_power_on (si, True);
541 /* When we fade in to the desktop, first fade out from the saver to
542 black, then fade in from black to the desktop. */
543 interrupted_p = fade_screens (si->app, si->dpy,
544 current_windows, si->nscreens,
547 False, /* from_desktop_p */
550 interrupted_p = fade_screens (si->app, si->dpy,
551 current_windows, si->nscreens,
554 False, /* from_desktop_p */
556 free (current_windows);
559 fprintf (stderr, "%s: unfading done%s\n", blurb(),
560 (interrupted_p ? " (interrupted)" : ""));
564 for (i = 0; i < si->nscreens; i++)
566 saver_screen_info *ssi = &si->screens[i];
569 Colormap c = DefaultColormapOfScreen (ssi->screen);
570 /* avoid technicolor */
571 XSetWindowBackground (si->dpy, ssi->screensaver_window,
572 BlackPixelOfScreen (ssi->screen));
573 XClearWindow (si->dpy, ssi->screensaver_window);
574 if (c) XInstallColormap (si->dpy, c);
576 XUnmapWindow (si->dpy, ssi->screensaver_window);
583 get_screen_gl_visual (saver_info *si, int real_screen_number)
585 int nscreens = ScreenCount (si->dpy);
587 if (! si->best_gl_visuals)
590 si->best_gl_visuals = (Visual **)
591 calloc (nscreens + 1, sizeof (*si->best_gl_visuals));
593 for (i = 0; i < nscreens; i++)
594 if (! si->best_gl_visuals[i])
595 si->best_gl_visuals[i] =
596 get_best_gl_visual (si, ScreenOfDisplay (si->dpy, i));
599 if (real_screen_number < 0 || real_screen_number >= nscreens) abort();
600 return si->best_gl_visuals[real_screen_number];
605 select_visual (saver_screen_info *ssi, const char *visual_name)
607 XWindowAttributes xgwa;
608 saver_info *si = ssi->global;
609 saver_preferences *p = &si->prefs;
610 Bool install_cmap_p = p->install_cmap_p;
611 Bool was_installed_p = (ssi->cmap != DefaultColormapOfScreen(ssi->screen));
615 /* On some systems (most recently, MacOS X) OpenGL programs get confused
616 when you kill one and re-start another on the same window. So maybe
617 it's best to just always destroy and recreate the xscreensaver window
618 when changing hacks, instead of trying to reuse the old one?
620 Bool always_recreate_window_p = True;
622 get_screen_gl_visual (si, 0); /* let's probe all the GL visuals early */
624 /* We make sure the existing window is actually on ssi->screen before
625 trying to use it, in case things moved around radically when monitors
626 were added or deleted. If we don't do this we could get a BadMatch
627 even though the depths match. I think.
629 memset (&xgwa, 0, sizeof(xgwa));
630 if (ssi->screensaver_window)
631 XGetWindowAttributes (si->dpy, ssi->screensaver_window, &xgwa);
633 if (visual_name && *visual_name)
635 if (!strcasecmp(visual_name, "default-i"))
637 visual_name = "default";
638 install_cmap_p = True;
640 else if (!strcasecmp(visual_name, "default-n"))
642 visual_name = "default";
643 install_cmap_p = False;
645 else if (!strcasecmp(visual_name, "GL"))
647 new_v = get_screen_gl_visual (si, ssi->real_screen_number);
648 if (!new_v && p->verbose_p)
649 fprintf (stderr, "%s: no GL visuals\n", blurb());
653 new_v = get_visual (ssi->screen, visual_name, True, False);
657 new_v = ssi->default_visual;
662 if (new_v && new_v != DefaultVisualOfScreen(ssi->screen))
663 /* It's not the default visual, so we have no choice but to install. */
664 install_cmap_p = True;
666 ssi->install_cmap_p = install_cmap_p;
668 if ((ssi->screen != xgwa.screen) ||
670 (always_recreate_window_p ||
671 (ssi->current_visual != new_v) ||
672 (install_cmap_p != was_installed_p))))
674 Colormap old_c = ssi->cmap;
675 Window old_w = ssi->screensaver_window;
677 new_v = ssi->current_visual;
682 fprintf (stderr, "%s: %d: visual ", blurb(), ssi->number);
683 describe_visual (stderr, ssi->screen, new_v, install_cmap_p);
686 fprintf (stderr, "%s: from ", blurb());
687 describe_visual (stderr, ssi->screen, ssi->current_visual,
692 ssi->current_visual = new_v;
693 ssi->current_depth = visual_depth(ssi->screen, new_v);
695 ssi->screensaver_window = 0;
697 initialize_screensaver_window_1 (ssi);
700 /* Now we can destroy the old window without horking our grabs. */
701 defer_XDestroyWindow (si->app, si->dpy, old_w);
703 if (p->verbose_p > 1)
704 fprintf (stderr, "%s: %d: destroyed old saver window 0x%lx\n",
705 blurb(), ssi->number, (unsigned long) old_w);
708 old_c != DefaultColormapOfScreen (ssi->screen))
709 XFreeColormap (si->dpy, old_c);
716 /* Synchronize the contents of si->ssi to the current state of the monitors.
717 Doesn't change anything if nothing has changed; otherwise, alters and
718 reuses existing saver_screen_info structs as much as possible.
719 Returns True if anything changed.
722 update_screen_layout (saver_info *si)
724 monitor **monitors = scan_monitors (si->dpy);
728 int seen_screens[100] = { 0, };
730 if (! monitor_layouts_differ_p (monitors, si->monitor_layout))
732 free_monitors (monitors);
736 free_monitors (si->monitor_layout);
737 si->monitor_layout = monitors;
739 while (monitors[count])
741 if (monitors[count]->sanity == S_SANE)
746 if (si->ssi_count == 0)
749 si->screens = (saver_screen_info *)
750 calloc (sizeof(*si->screens), si->ssi_count);
753 if (si->ssi_count < count)
755 si->screens = (saver_screen_info *)
756 realloc (si->screens, sizeof(*si->screens) * count);
757 memset (si->screens + si->ssi_count, 0,
758 sizeof(*si->screens) * (count - si->ssi_count));
759 si->ssi_count = count;
762 if (! si->screens) abort();
764 si->nscreens = good_count;
766 /* Regenerate the list of GL visuals as needed. */
767 if (si->best_gl_visuals)
768 free (si->best_gl_visuals);
769 si->best_gl_visuals = 0;
771 for (i = 0, j = 0; i < count; i++)
773 monitor *m = monitors[i];
774 saver_screen_info *ssi = &si->screens[j];
776 if (monitors[i]->sanity != S_SANE) continue;
781 sn = screen_number (m->screen);
782 ssi->screen = m->screen;
783 ssi->real_screen_number = sn;
784 ssi->real_screen_p = (seen_screens[sn] == 0);
787 ssi->default_visual =
788 get_visual_resource (ssi->screen, "visualID", "VisualID", False);
789 ssi->current_visual = ssi->default_visual;
790 ssi->current_depth = visual_depth (ssi->screen, ssi->current_visual);
794 ssi->width = m->width;
795 ssi->height = m->height;
797 # ifndef DEBUG_MULTISCREEN
799 saver_preferences *p = &si->prefs;
812 /* When the screensaver is active, this timer will periodically change
813 the running program. Each screen has its own timer.
816 cycle_timer (XtPointer closure, XtIntervalId *id)
818 saver_screen_info *ssi = (saver_screen_info *) closure;
819 saver_info *si = ssi->global;
821 if (ssi->error_dialog)
823 defer_XDestroyWindow (si->app, si->dpy, ssi->error_dialog);
824 ssi->error_dialog = 0;
827 maybe_reload_init_file (si);
828 kill_screenhack (ssi);
831 /* We could do a fade-out of just this screen here; but that would only work
832 if the fade method is SHM, not gamma or colormap. It would also only
833 look right if the cycle timers never fire at the same time, which is
834 currently the case. */
836 XSync (si->dpy, False);
837 spawn_screenhack (ssi); /* This also re-adds the cycle_id timer */
841 /* Called when a screenhack has exited unexpectedly.
842 We print a notification on the window, and in a little while, launch
843 a new hack (rather than waiting for the cycle timer to fire).
846 screenhack_obituary (saver_screen_info *ssi,
847 const char *name, const char *error)
849 saver_info *si = ssi->global;
850 saver_preferences *p = &si->prefs;
851 Time how_long = p->cycle;
852 Time max = 1000 * 60; /* Message stays up no longer than this */
855 XSetWindowAttributes attrs;
856 XWindowChanges changes;
857 unsigned long attrmask;
870 /* Restart the cycle timer, to take down the error dialog and launch
876 XtRemoveTimeOut (ssi->cycle_id);
878 XtAppAddTimeOut (si->app, how_long, cycle_timer, (XtPointer) ssi);
879 ssi->cycle_at = time ((time_t *) 0) + how_long / 1000;
881 fprintf (stderr, "%s: %d: cycling in %lu sec\n", blurb(), ssi->number,
884 /* Render an error message while we wait.
886 We can't just render text on top of ssi->screensaver_window because
887 if there was an OpenGL hack running on it, Xlib graphics might not
888 show up at all. Likewise, creating a sub-window doesn't work.
889 So it must be a top-level override-redirect window atop the saver.
891 cmap = ssi->cmap ? ssi->cmap : DefaultColormapOfScreen (ssi->screen);
892 window = ssi->error_dialog;
893 if (window) defer_XDestroyWindow (si->app, si->dpy, window);
894 attrs.override_redirect = True;
895 attrs.background_pixel = ssi->black_pixel;
896 attrs.border_pixel = ssi->black_pixel;
897 attrs.backing_store = Always;
898 attrs.colormap = cmap;
899 attrmask = (CWOverrideRedirect | CWBackPixel | CWBorderPixel |
900 CWBackingStore | CWColormap);
901 visual = ssi->current_visual;
902 window = ssi->error_dialog =
903 XCreateWindow (si->dpy, RootWindowOfScreen (ssi->screen),
904 0, 0, 1, 1, 0, ssi->current_depth, InputOutput, visual,
907 fn = get_string_resource (si->dpy, "errorFont", "Font");
908 cn = get_string_resource (si->dpy, "errorColor", "Color");
909 if (!fn || !*fn) fn = strdup ("monospace bold 16");
910 if (!cn || !*cn) cn = strdup ("#FF0000");
912 font = load_xft_font_retry (si->dpy, screen_number (ssi->screen), fn);
913 XftColorAllocName (si->dpy, visual, cmap, cn, &fg);
914 xftdraw = XftDrawCreate (si->dpy, window, visual, cmap);
917 get_pixel_resource (si->dpy, cmap, "errorColor", "Color");
919 gc = XCreateGC (si->dpy, window, GCForeground | GCLineWidth, &gcv);
922 sprintf (buf, "\"%.100s\" %.100s", name, error);
924 sprintf (buf, "%.100s", error);
926 XftTextExtentsUtf8 (si->dpy, font, (FcChar8 *) buf, strlen(buf), &overall);
927 x = (ssi->width - overall.width) / 2;
928 y = (ssi->height - overall.height) / 2 + font->ascent;
929 pad = bw + font->ascent * 2;
931 attrmask = CWX | CWY | CWWidth | CWHeight;
932 changes.x = ssi->x + x - pad;
933 changes.y = ssi->y + y - (font->ascent + pad);
934 changes.width = overall.width + pad * 2;
935 changes.height = font->ascent + font->descent + pad * 2;
936 XConfigureWindow (si->dpy, window, attrmask, &changes);
937 xscreensaver_set_wm_atoms (si->dpy, window, changes.width, changes.height,
938 ssi->screensaver_window);
939 XMapRaised (si->dpy, window);
940 XClearWindow (si->dpy, window);
942 XDrawRectangle (si->dpy, window, gc, gcv.line_width/2, gcv.line_width/2,
943 changes.width - gcv.line_width,
944 changes.height - gcv.line_width);
947 y = font->ascent + pad;
948 XftDrawStringUtf8 (xftdraw, &fg, font, x, y, (FcChar8 *) buf, strlen (buf));
949 XSync (si->dpy, False);
951 XFreeGC (si->dpy, gc);
952 XftDrawDestroy (xftdraw);
953 /* XftColorFree (si->dpy, visual, cmap, &fg); */
954 XftFontClose (si->dpy, font);
960 /* This timer goes off every few minutes to try and clean up anything that has
961 gone wrong. It raises the windows, in case some other window has been
962 mapped on top of them, and re-sets the server's DPMS settings.
964 Maybe we should respond to Expose events to detect when another window has
965 raised above us and re-raise ourselves sooner. But that would result in us
966 fighting against "xscreensaver-auth" which tries very hard to be on top.
969 watchdog_timer (XtPointer closure, XtIntervalId *id)
971 saver_info *si = (saver_info *) closure;
972 saver_preferences *p = &si->prefs;
973 Bool running_p, on_p, terminating_p;
975 /* If the DPMS settings on the server have changed, change them back to
976 what ~/.xscreensaver says they should be. */
977 sync_server_dpms_settings (si->dpy, p);
979 if (si->prefs.debug_p)
980 fprintf (stderr, "%s: watchdog timer raising screen\n", blurb());
984 running_p = any_screenhacks_running_p (si);
985 on_p = monitor_powered_on_p (si->dpy);
986 terminating_p = si->terminating_p;
987 if (running_p && !on_p)
990 if (si->prefs.verbose_p)
992 "%s: monitor has powered down; killing running hacks\n",
994 for (i = 0; i < si->nscreens; i++)
995 kill_screenhack (&si->screens[i]);
996 /* Do not clear current_hack here. */
998 else if (terminating_p)
1000 /* If we are in the process of shutting down and are about to exit,
1001 don't re-launch anything just because the monitor came back on. */
1003 else if (!running_p && on_p)
1005 /* If the hack number is set but no hack is running, it is because the
1006 hack was killed when the monitor powered off, above. This assumes
1007 that kill_screenhack() clears pid but not current_hack. Start the
1008 hack going again. The cycle_timer will also do this (unless "cycle"
1009 is 0) but watchdog_timer runs more frequently.
1011 if (si->nscreens > 0 && si->screens[0].current_hack >= 0)
1014 if (si->prefs.verbose_p)
1016 "%s: monitor has powered back on; re-launching hacks\n",
1018 for (i = 0; i < si->nscreens; i++)
1019 spawn_screenhack (&si->screens[i]);
1023 /* Re-schedule this timer. The watchdog timer defaults to a bit less
1024 than the hack cycle period, but is never longer than one hour.
1026 si->watchdog_id = 0;
1027 reset_watchdog_timer (si);
1032 reset_watchdog_timer (saver_info *si)
1034 saver_preferences *p = &si->prefs;
1036 if (si->watchdog_id)
1038 XtRemoveTimeOut (si->watchdog_id);
1039 si->watchdog_id = 0;
1042 if (p->watchdog_timeout <= 0) return;
1043 si->watchdog_id = XtAppAddTimeOut (si->app, p->watchdog_timeout,
1044 watchdog_timer, (XtPointer) si);
1046 fprintf (stderr, "%s: restarting watchdog_timer (%ld, %ld)\n",
1047 blurb(), p->watchdog_timeout, si->watchdog_id);