X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fxlockmore.c;h=72caa104e7c09dfff8d66183400fd65c3c6d43ea;hb=39809ded547bdbb08207d3e514950425215b4410;hp=6018469239a478a7c0cac736843603b1b00abb56;hpb=07faf451b99879183ed7e909e43a0e065be1ee7f;p=xscreensaver diff --git a/hacks/xlockmore.c b/hacks/xlockmore.c index 60184692..72caa104 100644 --- a/hacks/xlockmore.c +++ b/hacks/xlockmore.c @@ -1,6 +1,5 @@ /* xlockmore.c --- xscreensaver compatibility layer for xlockmore modules. - * xscreensaver, Copyright (c) 1997, 1998, 2001, 2002, 2004, 2006 - * Jamie Zawinski + * xscreensaver, Copyright (c) 1997-2017 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -20,9 +19,12 @@ #include "xlockmoreI.h" #include "screenhack.h" -#ifndef HAVE_COCOA +#ifndef HAVE_JWXYZ # include -#endif /* !HAVE_COCOA */ +#endif /* !HAVE_JWXYZ */ + +#include +#include #define countof(x) (sizeof((x))/sizeof(*(x))) @@ -98,8 +100,7 @@ xlockmore_setup (struct xscreensaver_function_table *xsft, void *arg) /* Add extra args, if they're mentioned in the defaults... */ { char *args[] = { "-count", "-cycles", "-delay", "-ncolors", - "-size", "-font", "-wireframe", "-use3d", "-useSHM", - "-showFPS" }; + "-size", "-font", "-wireframe", "-use3d", "-useSHM" }; for (j = 0; j < countof(args); j++) if (strstr(xlockmore_defaults, args[j]+1)) { @@ -139,17 +140,6 @@ xlockmore_setup (struct xscreensaver_function_table *xsft, void *arg) new->argKind = XrmoptionNoArg; new->value = "False"; } - else if (!strcmp(new->option, "-showFPS")) - { - new->option = "-fps"; - new->argKind = XrmoptionNoArg; - new->value = "True"; - new = &new_options[i++]; - new->option = "-no-fps"; - new->specifier = new_options[i-2].specifier; - new->argKind = XrmoptionNoArg; - new->value = "False"; - } else { new->argKind = XrmoptionSepArg; @@ -165,12 +155,12 @@ xlockmore_setup (struct xscreensaver_function_table *xsft, void *arg) */ i = 0; - new_defaults = (char **) calloc (1, xlockmore_opts->numvarsdesc * 10 + 100); + new_defaults = (char **) calloc (1, xlockmore_opts->numvarsdesc * 10 + 1000); /* Put on the PROGCLASS.background/foreground resources. */ s = (char *) malloc(50); *s = 0; -# ifndef HAVE_COCOA +# ifndef HAVE_JWXYZ strcpy (s, progclass); # endif strcat (s, ".background: black"); @@ -178,7 +168,7 @@ xlockmore_setup (struct xscreensaver_function_table *xsft, void *arg) s = (char *) malloc(50); *s = 0; -# ifndef HAVE_COCOA +# ifndef HAVE_JWXYZ strcpy (s, progclass); # endif strcat (s, ".foreground: white"); @@ -248,6 +238,53 @@ xlockmore_setup (struct xscreensaver_function_table *xsft, void *arg) } +static void +xlockmore_release_screens (ModeInfo *mi) +{ + struct xlockmore_function_table *xlmft = mi->xlmft; + + /* 2. Call release_##, if it exists. */ + if (xlmft->hack_release) + xlmft->hack_release (mi); + + /* 3. Free the state array. */ + if (xlmft->state_array) { + free(*xlmft->state_array); + *xlmft->state_array = NULL; + xlmft->state_array = NULL; + } + + /* 4. Pretend FreeAllGL(mi) gets called here. */ + + mi->xlmft->got_init = 0; +} + + +static void +xlockmore_free_screens (ModeInfo *mi) +{ + struct xlockmore_function_table *xlmft = mi->xlmft; + + /* Optimization: xlockmore_read_resources calls this lots on first start. */ + if (!xlmft->got_init) + return; + + /* Order is important here: */ + + /* 1. Call free_## for all screens. */ + if (xlmft->hack_free) { + int old_screen = mi->screen_number; + for (mi->screen_number = 0; mi->screen_number < XLOCKMORE_NUM_SCREENS; + ++mi->screen_number) { + xlmft->hack_free (mi); + } + mi->screen_number = old_screen; + } + + xlockmore_release_screens (mi); +} + + static void xlockmore_read_resources (ModeInfo *mi) { @@ -262,31 +299,77 @@ xlockmore_read_resources (ModeInfo *mi) int *var_i = (int *) var; float *var_f = (float *) var; + /* If any of the options changed, stop this hack's other instances. */ switch (xlockmore_opts->vars[i].type) - { - case t_String: - *var_c = get_string_resource (dpy, xlockmore_opts->vars[i].name, - xlockmore_opts->vars[i].classname); - break; - case t_Float: - *var_f = get_float_resource (dpy, xlockmore_opts->vars[i].name, - xlockmore_opts->vars[i].classname); - break; - case t_Int: - *var_i = get_integer_resource (dpy, xlockmore_opts->vars[i].name, - xlockmore_opts->vars[i].classname); - break; - case t_Bool: - *var_b = get_boolean_resource (dpy, xlockmore_opts->vars[i].name, - xlockmore_opts->vars[i].classname); - break; - default: - abort (); - } + { + case t_String: + { + char *c = get_string_resource (dpy, xlockmore_opts->vars[i].name, + xlockmore_opts->vars[i].classname); + if ((!c && !*var_c) || (c && *var_c && !strcmp(c, *var_c))) { + free (c); + } else { + xlockmore_free_screens (mi); + free (*var_c); + *var_c = c; + } + } + break; + case t_Float: + { + float f = get_float_resource (dpy, xlockmore_opts->vars[i].name, + xlockmore_opts->vars[i].classname); + float frac = fabsf(*var_f) * (1.0f / (1l << (FLT_MANT_DIG - 4))); + if (f < *var_f - frac || f > *var_f + frac) { + xlockmore_free_screens (mi); + *var_f = f; + } + } + break; + case t_Int: + { + int ii = get_integer_resource (dpy, xlockmore_opts->vars[i].name, + xlockmore_opts->vars[i].classname); + if (ii != *var_i) { + xlockmore_free_screens (mi); + *var_i = ii; + } + } + break; + case t_Bool: + { + Bool b = get_boolean_resource (dpy, xlockmore_opts->vars[i].name, + xlockmore_opts->vars[i].classname); + if (b != *var_b) { + xlockmore_free_screens (mi); + *var_b = b; + } + } + break; + default: + abort (); + } } } +/* We keep a list of all of the screen numbers that are in use and not + yet freed so that they can have sensible screen numbers. If three + displays are created (0, 1, 2) and then #1 is closed, then the fourth + display will be given the now-unused display number 1. (Everything in + here assumes a 1:1 Display/Screen mapping.) + + XLOCKMORE_NUM_SCREENS is the most number of live displays at one time. So + if it's 64, then we'll blow up if the system has 64 monitors and also has + System Preferences open (the small preview window). + + Note that xlockmore-style savers tend to allocate big structures, so + setting XLOCKMORE_NUM_SCREENS to 1000 will waste a few megabytes. Also + most (all?) of them assume that the number of screens never changes, so + dynamically expanding this array won't work. + */ + + static void * xlockmore_init (Display *dpy, Window window, struct xlockmore_function_table *xlmft) @@ -295,7 +378,6 @@ xlockmore_init (Display *dpy, Window window, XGCValues gcv; XColor color; int i; - int orig_pause; Bool root_p; if (! xlmft) @@ -306,51 +388,43 @@ xlockmore_init (Display *dpy, Window window, mi->window = window; XGetWindowAttributes (dpy, window, &mi->xgwa); -#ifdef HAVE_COCOA - - /* In Cocoa-based xscreensaver, all hacks run in the same address space, - so each one needs to get its own screen number. We just use a global - counter for that, instead of actually trying to figure out which - monitor each window is on. Also, the embedded "preview" view counts - as a screen as well. - - Note that the screen number will increase each time the saver is - restarted (e.g., each time preferences are changed!) So we just - keep pushing the num_screens number up as needed, and assume that - no more than 10 simultanious copies will be running at once... + /* In Cocoa and Android-based xscreensaver, as well as with DEBUG_PAIR, + hacks run in the same address space, so each one needs to get its own + screen number. + + Find the first empty slot in live_displays and plug us in. */ { - static int screen_tick = 0; - mi->num_screens = 10; - mi->screen_number = screen_tick++; - if (screen_tick >= mi->num_screens) - mi->num_screens += 10; - } - - root_p = True; -#else /* !HAVE_COCOA -- real Xlib */ - - /* In Xlib-based xscreensaver, each hack runs in its own address space, - so each one only needs to be aware of one screen. - */ - mi->num_screens = 1; - mi->screen_number = 0; - - { /* kludge for DEBUG_PAIR */ - static int screen_tick = 0; - mi->num_screens++; - if (screen_tick) - mi->screen_number++; - screen_tick++; + const int size = XLOCKMORE_NUM_SCREENS; + int i; + for (i = 0; i < size; i++) { + if (! (xlmft->live_displays & (1 << i))) + break; + } + if (i >= size) abort(); + xlmft->live_displays |= 1ul << i; + xlmft->got_init &= ~(1ul << i); + mi->screen_number = i; } root_p = (window == RootWindowOfScreen (mi->xgwa.screen)); +#ifndef HAVE_JWXYZ + /* Everybody gets motion events, just in case. */ XSelectInput (dpy, window, (mi->xgwa.your_event_mask | PointerMotionMask)); -#endif /* !HAVE_COCOA */ - +#endif /* !HAVE_JWXYZ */ + + if (mi->xlmft->hack_release) { +/* + fprintf ( + stderr, + "%s: WARNING: hack_release is not usually recommended; see MI_INIT.\n", + progname); +*/ + } + color.flags = DoRed|DoGreen|DoBlue; color.red = color.green = color.blue = 0; if (!XAllocColor(dpy, mi->xgwa.colormap, &color)) @@ -363,7 +437,6 @@ xlockmore_init (Display *dpy, Window window, if (mono_p) { - static unsigned long pixels[2]; static XColor colors[2]; MONO: mi->npixels = 2; @@ -373,8 +446,6 @@ xlockmore_init (Display *dpy, Window window, if (!mi->colors) mi->colors = (XColor *) calloc (mi->npixels, sizeof (*mi->colors)); - pixels[0] = mi->black; - pixels[1] = mi->white; colors[0].flags = DoRed|DoGreen|DoBlue; colors[1].flags = DoRed|DoGreen|DoBlue; colors[0].red = colors[0].green = colors[0].blue = 0; @@ -396,18 +467,21 @@ xlockmore_init (Display *dpy, Window window, switch (mi->xlmft->desired_color_scheme) { case color_scheme_uniform: - make_uniform_colormap (dpy, mi->xgwa.visual, mi->xgwa.colormap, + make_uniform_colormap (mi->xgwa.screen, mi->xgwa.visual, + mi->xgwa.colormap, mi->colors, &mi->npixels, True, &mi->writable_p, True); break; case color_scheme_smooth: - make_smooth_colormap (dpy, mi->xgwa.visual, mi->xgwa.colormap, + make_smooth_colormap (mi->xgwa.screen, mi->xgwa.visual, + mi->xgwa.colormap, mi->colors, &mi->npixels, True, &mi->writable_p, True); break; case color_scheme_bright: case color_scheme_default: - make_random_colormap (dpy, mi->xgwa.visual, mi->xgwa.colormap, + make_random_colormap (mi->xgwa.screen, mi->xgwa.visual, + mi->xgwa.colormap, mi->colors, &mi->npixels, (mi->xlmft->desired_color_scheme == color_scheme_bright), @@ -453,16 +527,13 @@ xlockmore_init (Display *dpy, Window window, mi->wireframe_p = get_boolean_resource (dpy, "wireframe", "Boolean"); mi->root_p = root_p; - mi->fps_p = get_boolean_resource (dpy, "showFPS", "Boolean"); -#ifdef HAVE_XSHM_EXTENSION - mi->use_shm = get_boolean_resource (dpy, "useSHM", "Boolean"); -#endif /* !HAVE_XSHM_EXTENSION */ + mi->fps_p = get_boolean_resource (dpy, "doFPS", "DoFPS"); + mi->recursion_depth = -1; /* see fps.c */ if (mi->pause < 0) mi->pause = 0; else if (mi->pause > 100000000) mi->pause = 100000000; - orig_pause = mi->pause; /* If this hack uses fonts (meaning, mentioned "font" in DEFAULTS) then load it. */ @@ -471,7 +542,7 @@ xlockmore_init (Display *dpy, Window window, if (name) { XFontStruct *f = XLoadQueryFont (dpy, name); - const char *def1 = "-*-times-bold-r-normal-*-180-*"; + const char *def1 = "-*-helvetica-bold-r-normal-*-180-*"; const char *def2 = "fixed"; if (!f) { @@ -490,29 +561,78 @@ xlockmore_init (Display *dpy, Window window, free (name); } } - + xlockmore_read_resources (mi); - XClearWindow (dpy, window); + return mi; +} + +static void +xlockmore_do_init (ModeInfo *mi) +{ + mi->xlmft->got_init |= 1 << mi->screen_number; + XClearWindow (mi->dpy, mi->window); mi->xlmft->hack_init (mi); +} - return mi; + +static Bool +xlockmore_got_init (ModeInfo *mi) +{ + return mi->xlmft->got_init & (1 << mi->screen_number); +} + + +static void +xlockmore_abort_erase (ModeInfo *mi) +{ + if (mi->eraser) { + eraser_free (mi->eraser); + mi->eraser = NULL; + } + mi->needs_clear = False; } + +static void +xlockmore_check_init (ModeInfo *mi) +{ + if (! xlockmore_got_init (mi)) { + xlockmore_abort_erase (mi); + xlockmore_do_init (mi); + } +} + + static unsigned long xlockmore_draw (Display *dpy, Window window, void *closure) { ModeInfo *mi = (ModeInfo *) closure; - unsigned long orig_pause = mi->pause; unsigned long this_pause; + if (mi->needs_clear) { + /* OpenGL hacks never get here. */ + if (!mi->is_drawn) { + XClearWindow (dpy, window); + } else { + mi->eraser = erase_window (dpy, window, mi->eraser); + /* Delay calls to xlockmore hooks while the erase animation is running. */ + if (mi->eraser) + return 33333; + } + mi->needs_clear = False; + } + + xlockmore_check_init (mi); + if (mi->needs_clear) + return 0; mi->xlmft->hack_draw (mi); this_pause = mi->pause; mi->pause = orig_pause; - return this_pause; + return mi->needs_clear ? 0 : this_pause; } @@ -521,48 +641,136 @@ xlockmore_reshape (Display *dpy, Window window, void *closure, unsigned int w, unsigned int h) { ModeInfo *mi = (ModeInfo *) closure; - if (mi->xlmft->hack_reshape) - { - XGetWindowAttributes (dpy, window, &mi->xgwa); + if (mi) { + /* Ignore spurious resize events, because xlockmore_do_init usually clears + the screen, and there's no reason to do that if we don't have to. + */ +# ifndef HAVE_MOBILE + /* These are not spurious on mobile: they are rotations. */ + if (mi->xgwa.width == w && mi->xgwa.height == h) + return; +# endif + mi->xgwa.width = w; + mi->xgwa.height = h; + + /* Finish any erase operations. */ + if (mi->needs_clear) { + xlockmore_abort_erase (mi); + XClearWindow (dpy, window); + } + + /* If there hasn't been an init yet, init now, but don't call reshape_##. + */ + if (xlockmore_got_init (mi) && mi->xlmft->hack_reshape) { mi->xlmft->hack_reshape (mi, mi->xgwa.width, mi->xgwa.height); + } else { + mi->is_drawn = False; + xlockmore_do_init (mi); } + } } static Bool xlockmore_event (Display *dpy, Window window, void *closure, XEvent *event) { ModeInfo *mi = (ModeInfo *) closure; - if (mi->xlmft->hack_handle_events) - { - mi->xlmft->hack_handle_events (mi, event); - /* Since xlockmore hacks don't tell us whether they handled the - event, assume they handled buttons (so we don't beep) but that - they didn't handle anything else (so that "q" still quits). - */ - return (event->xany.type == ButtonPress); + if (mi) { + if (mi->xlmft->hack_handle_events) { + xlockmore_check_init (mi); + return mi->xlmft->hack_handle_events (mi, event); } - else - return False; + + if (screenhack_event_helper (mi->dpy, mi->window, event)) { + /* If a clear is in progress, don't interrupt or restart it. */ + if (mi->needs_clear) + mi->xlmft->got_init &= ~(1ul << mi->screen_number); + else + mi->xlmft->hack_init (mi); + return True; + } + } + return False; } +void +xlockmore_do_fps (Display *dpy, Window w, fps_state *fpst, void *closure) +{ + ModeInfo *mi = (ModeInfo *) closure; + fps_compute (fpst, 0, mi ? mi->recursion_depth : -1); + fps_draw (fpst); +} + + static void xlockmore_free (Display *dpy, Window window, void *closure) { ModeInfo *mi = (ModeInfo *) closure; + + if (mi->eraser) + eraser_free (mi->eraser); + + /* Some hacks may need to do things with their Display * on cleanup. And + under JWXYZ, the Display * for this hack gets cleaned up right after + xlockmore_free returns. Thus, hack_free has to happen now, rather than + after the final screen has been released. + */ if (mi->xlmft->hack_free) mi->xlmft->hack_free (mi); -#ifdef USE_GL -/* #### - if (mi->fps_state) - fps_free (mi); - */ -#endif /* USE_GL */ + + /* Find us in live_displays and clear that slot. */ + assert (mi->xlmft->live_displays & (1ul << mi->screen_number)); + mi->xlmft->live_displays &= ~(1ul << mi->screen_number); + if (!mi->xlmft->live_displays) + xlockmore_release_screens (mi); XFreeGC (dpy, mi->gc); - free_colors (dpy, mi->xgwa.colormap, mi->colors, mi->npixels); + free_colors (mi->xgwa.screen, mi->xgwa.colormap, mi->colors, mi->npixels); free (mi->colors); free (mi->pixels); free (mi); } + +void +xlockmore_mi_init (ModeInfo *mi, size_t state_size, void **state_array) +{ + struct xlockmore_function_table *xlmft = mi->xlmft; + + /* Steal the state_array for safe keeping. + Only necessary when the screenhack isn't a once per process deal. + (i.e. macOS, iOS, Android) + */ + assert ((!xlmft->state_array && !*state_array) || + xlmft->state_array == state_array); + xlmft->state_array = state_array; + + if (!*xlmft->state_array) { + *xlmft->state_array = calloc (XLOCKMORE_NUM_SCREENS, state_size); + + if (!*xlmft->state_array) { +#ifdef HAVE_JWXYZ + /* Throws an exception instead of exiting the process. */ + jwxyz_abort ("%s: out of memory", progname); +#else + fprintf (stderr, "%s: out of memory\n", progname); + exit (1); +#endif + } + } + + /* Find the appropriate state object, clear it, and we're done. */ + { + if (xlmft->hack_free) + xlmft->hack_free (mi); + memset ((char *)(*xlmft->state_array) + mi->screen_number * state_size, 0, + state_size); + } +} + + +Bool +xlockmore_no_events (ModeInfo *mi, XEvent *event) +{ + return False; +}