From http://www.jwz.org/xscreensaver/xscreensaver-5.38.tar.gz
[xscreensaver] / hacks / xlockmore.c
1 /* xlockmore.c --- xscreensaver compatibility layer for xlockmore modules.
2  * xscreensaver, Copyright (c) 1997-2017 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  * This file, along with xlockmore.h, make it possible to compile an xlockmore
13  * module into a standalone program, and thus use it with xscreensaver.
14  * By Jamie Zawinski <jwz@jwz.org> on 10-May-97; based on the ideas
15  * in the older xlock.h by Charles Hannum <mycroft@ai.mit.edu>.  (I had
16  * to redo it, since xlockmore has diverged so far from xlock...)
17  */
18
19 #include "xlockmoreI.h"
20 #include "screenhack.h"
21
22 #ifndef HAVE_JWXYZ
23 # include <X11/Intrinsic.h>
24 #endif /* !HAVE_JWXYZ */
25
26 #include <assert.h>
27 #include <float.h>
28
29 #define countof(x) (sizeof((x))/sizeof(*(x)))
30
31 #define MAX_COLORS (1L<<13)
32
33 extern struct xscreensaver_function_table *xscreensaver_function_table;
34
35 extern const char *progclass;
36
37 extern struct xlockmore_function_table xlockmore_function_table;
38
39 static void *xlockmore_init (Display *, Window, 
40                              struct xlockmore_function_table *);
41 static unsigned long xlockmore_draw (Display *, Window, void *);
42 static void xlockmore_reshape (Display *, Window, void *, 
43                                unsigned int w, unsigned int h);
44 static Bool xlockmore_event (Display *, Window, void *, XEvent *);
45 static void xlockmore_free (Display *, Window, void *);
46
47
48 void
49 xlockmore_setup (struct xscreensaver_function_table *xsft, void *arg)
50 {
51   struct xlockmore_function_table *xlmft = 
52     (struct xlockmore_function_table *) arg;
53   int i, j;
54   char *s;
55   XrmOptionDescRec *new_options;
56   char **new_defaults;
57   const char *xlockmore_defaults;
58   ModeSpecOpt *xlockmore_opts = xlmft->opts;
59
60 # undef ya_rand_init
61   ya_rand_init (0);
62
63   xsft->init_cb    = (void *(*) (Display *, Window)) xlockmore_init;
64   xsft->draw_cb    = xlockmore_draw;
65   xsft->reshape_cb = xlockmore_reshape;
66   xsft->event_cb   = xlockmore_event;
67   xsft->free_cb    = xlockmore_free;
68
69   progclass = xlmft->progclass;
70   xlockmore_defaults = xlmft->defaults;
71
72   /* Translate the xlockmore `opts[]' argument to a form that
73      screenhack.c expects.
74    */
75   new_options = (XrmOptionDescRec *) 
76     calloc (xlockmore_opts->numopts*3 + 100, sizeof (*new_options));
77
78   for (i = 0; i < xlockmore_opts->numopts; i++)
79     {
80       XrmOptionDescRec *old = &xlockmore_opts->opts[i];
81       XrmOptionDescRec *new = &new_options[i];
82
83       if (old->option[0] == '-')
84         new->option = old->option;
85       else
86         {
87           /* Convert "+foo" to "-no-foo". */
88           new->option = (char *) malloc (strlen(old->option) + 5);
89           strcpy (new->option, "-no-");
90           strcat (new->option, old->option + 1);
91         }
92
93       new->specifier = strrchr (old->specifier, '.');
94       if (!new->specifier) abort();
95
96       new->argKind = old->argKind;
97       new->value = old->value;
98     }
99
100   /* Add extra args, if they're mentioned in the defaults... */
101   {
102     char *args[] = { "-count", "-cycles", "-delay", "-ncolors",
103                      "-size", "-font", "-wireframe", "-use3d", "-useSHM" };
104     for (j = 0; j < countof(args); j++)
105       if (strstr(xlockmore_defaults, args[j]+1))
106         {
107           XrmOptionDescRec *new = &new_options[i++];
108           new->option = args[j];
109           new->specifier = strdup(args[j]);
110           new->specifier[0] = '.';
111           if (!strcmp(new->option, "-wireframe"))
112             {
113               new->argKind = XrmoptionNoArg;
114               new->value = "True";
115               new = &new_options[i++];
116               new->option = "-no-wireframe";
117               new->specifier = new_options[i-2].specifier;
118               new->argKind = XrmoptionNoArg;
119               new->value = "False";
120             }
121           else if (!strcmp(new->option, "-use3d"))
122             {
123               new->option = "-3d";
124               new->argKind = XrmoptionNoArg;
125               new->value = "True";
126               new = &new_options[i++];
127               new->option = "-no-3d";
128               new->specifier = new_options[i-2].specifier;
129               new->argKind = XrmoptionNoArg;
130               new->value = "False";
131             }
132           else if (!strcmp(new->option, "-useSHM"))
133             {
134               new->option = "-shm";
135               new->argKind = XrmoptionNoArg;
136               new->value = "True";
137               new = &new_options[i++];
138               new->option = "-no-shm";
139               new->specifier = new_options[i-2].specifier;
140               new->argKind = XrmoptionNoArg;
141               new->value = "False";
142             }
143           else
144             {
145               new->argKind = XrmoptionSepArg;
146               new->value = 0;
147             }
148         }
149   }
150
151
152
153   /* Construct the kind of `defaults' that screenhack.c expects from
154      the xlockmore `vars[]' argument.
155    */
156   i = 0;
157
158   new_defaults = (char **) calloc (1, xlockmore_opts->numvarsdesc * 10 + 1000);
159
160   /* Put on the PROGCLASS.background/foreground resources. */
161   s = (char *) malloc(50);
162   *s = 0;
163 # ifndef HAVE_JWXYZ
164   strcpy (s, progclass);
165 # endif
166   strcat (s, ".background: black");
167   new_defaults [i++] = s;
168
169   s = (char *) malloc(50);
170   *s = 0;
171 # ifndef HAVE_JWXYZ
172   strcpy (s, progclass);
173 # endif
174   strcat (s, ".foreground: white");
175   new_defaults [i++] = s;
176
177   /* Copy the lines out of the `xlockmore_defaults' var and into this array. */
178   s = strdup (xlockmore_defaults);
179   while (s && *s)
180     {
181       new_defaults [i++] = s;
182       s = strchr(s, '\n');
183       if (s)
184         *s++ = 0;
185     }
186
187   /* Copy the defaults out of the `xlockmore_opts->' variable. */
188   for (j = 0; j < xlockmore_opts->numvarsdesc; j++)
189     {
190       const char *def = xlockmore_opts->vars[j].def;
191
192       if (!def) abort();
193       if (!*def) abort();
194       if (strlen(def) > 1000) abort();
195
196       s = (char *) malloc (strlen (xlockmore_opts->vars[j].name) +
197                            strlen (def) + 10);
198       strcpy (s, "*");
199       strcat (s, xlockmore_opts->vars[j].name);
200       strcat (s, ": ");
201       strcat (s, def);
202       new_defaults [i++] = s;
203
204       /* Go through the list of resources and print a warning if there
205          are any duplicates.
206        */
207       {
208         char *onew = strdup (xlockmore_opts->vars[j].name);
209         const char *new = onew;
210         int k;
211         if ((s = strrchr (new, '.'))) new = s+1;
212         if ((s = strrchr (new, '*'))) new = s+1;
213         for (k = 0; k < i-1; k++)
214           {
215             char *oold = strdup (new_defaults[k]);
216             const char *old = oold;
217             if ((s = strchr (oold, ':'))) *s = 0;
218             if ((s = strrchr (old, '.'))) old = s+1;
219             if ((s = strrchr (old, '*'))) old = s+1;
220             if (!strcasecmp (old, new))
221               {
222                 fprintf (stderr,
223                          "%s: duplicate resource \"%s\": "
224                          "set in both DEFAULTS and vars[]\n",
225                          progname, old);
226               }
227             free (oold);
228           }
229         free (onew);
230       }
231     }
232
233   new_defaults [i] = 0;
234
235   xsft->progclass = progclass;
236   xsft->options   = new_options;
237   xsft->defaults  = (const char * const *) new_defaults;
238 }
239
240
241 static void
242 xlockmore_release_screens (ModeInfo *mi)
243 {
244   struct xlockmore_function_table *xlmft = mi->xlmft;
245
246   /* 2. Call release_##, if it exists. */
247   if (xlmft->hack_release)
248     xlmft->hack_release (mi);
249
250   /* 3. Free the state array. */
251   if (xlmft->state_array) {
252     free(*xlmft->state_array);
253     *xlmft->state_array = NULL;
254     xlmft->state_array = NULL;
255   }
256
257   /* 4. Pretend FreeAllGL(mi) gets called here. */
258
259   mi->xlmft->got_init = 0;
260 }
261
262
263 static void
264 xlockmore_free_screens (ModeInfo *mi)
265 {
266   struct xlockmore_function_table *xlmft = mi->xlmft;
267
268   /* Optimization: xlockmore_read_resources calls this lots on first start. */
269   if (!xlmft->got_init)
270     return;
271
272   /* Order is important here: */
273
274   /* 1. Call free_## for all screens. */
275   if (xlmft->hack_free) {
276     int old_screen = mi->screen_number;
277     for (mi->screen_number = 0; mi->screen_number < XLOCKMORE_NUM_SCREENS;
278          ++mi->screen_number) {
279       xlmft->hack_free (mi);
280     }
281     mi->screen_number = old_screen;
282   }
283
284   xlockmore_release_screens (mi);
285 }
286
287
288 static void
289 xlockmore_read_resources (ModeInfo *mi)
290 {
291   Display *dpy = mi->dpy;
292   ModeSpecOpt *xlockmore_opts = mi->xlmft->opts;
293   int i;
294   for (i = 0; i < xlockmore_opts->numvarsdesc; i++)
295     {
296       void  *var   = xlockmore_opts->vars[i].var;
297       Bool  *var_b = (Bool *)  var;
298       char **var_c = (char **) var;
299       int   *var_i = (int *) var;
300       float *var_f = (float *) var;
301
302       /* If any of the options changed, stop this hack's other instances. */
303       switch (xlockmore_opts->vars[i].type)
304         {
305         case t_String:
306           {
307             char *c = get_string_resource (dpy, xlockmore_opts->vars[i].name,
308                                            xlockmore_opts->vars[i].classname);
309             if ((!c && !*var_c) || (c && *var_c && !strcmp(c, *var_c))) {
310               free (c);
311             } else {
312               xlockmore_free_screens (mi);
313               free (*var_c);
314               *var_c = c;
315             }
316           }
317           break;
318         case t_Float:
319           {
320             float f = get_float_resource (dpy, xlockmore_opts->vars[i].name,
321                                           xlockmore_opts->vars[i].classname);
322             float frac = fabsf(*var_f) * (1.0f / (1l << (FLT_MANT_DIG - 4)));
323             if (f < *var_f - frac || f > *var_f + frac) {
324               xlockmore_free_screens (mi);
325               *var_f = f;
326             }
327           }
328           break;
329         case t_Int:
330           {
331             int ii = get_integer_resource (dpy, xlockmore_opts->vars[i].name,
332                                           xlockmore_opts->vars[i].classname);
333             if (ii != *var_i) {
334               xlockmore_free_screens (mi);
335               *var_i = ii;
336             }
337           }
338           break;
339         case t_Bool:
340           {
341             Bool b = get_boolean_resource (dpy, xlockmore_opts->vars[i].name,
342                                            xlockmore_opts->vars[i].classname);
343             if (b != *var_b) {
344               xlockmore_free_screens (mi);
345               *var_b = b;
346             }
347           }
348           break;
349         default:
350           abort ();
351         }
352     }
353 }
354
355
356 /* We keep a list of all of the screen numbers that are in use and not
357    yet freed so that they can have sensible screen numbers.  If three
358    displays are created (0, 1, 2) and then #1 is closed, then the fourth
359    display will be given the now-unused display number 1. (Everything in
360    here assumes a 1:1 Display/Screen mapping.)
361
362    XLOCKMORE_NUM_SCREENS is the most number of live displays at one time. So
363    if it's 64, then we'll blow up if the system has 64 monitors and also has
364    System Preferences open (the small preview window).
365
366    Note that xlockmore-style savers tend to allocate big structures, so
367    setting XLOCKMORE_NUM_SCREENS to 1000 will waste a few megabytes.  Also
368    most (all?) of them assume that the number of screens never changes, so
369    dynamically expanding this array won't work.
370  */
371
372
373 static void *
374 xlockmore_init (Display *dpy, Window window, 
375                 struct xlockmore_function_table *xlmft)
376 {
377   ModeInfo *mi = (ModeInfo *) calloc (1, sizeof(*mi));
378   XGCValues gcv;
379   XColor color;
380   int i;
381   Bool root_p;
382
383   if (! xlmft)
384     abort();
385   mi->xlmft = xlmft;
386
387   mi->dpy = dpy;
388   mi->window = window;
389   XGetWindowAttributes (dpy, window, &mi->xgwa);
390   
391   /* In Cocoa and Android-based xscreensaver, as well as with DEBUG_PAIR,
392      hacks run in the same address space, so each one needs to get its own
393      screen number.
394
395      Find the first empty slot in live_displays and plug us in.
396    */
397   {
398     const int size = XLOCKMORE_NUM_SCREENS;
399     int i;
400     for (i = 0; i < size; i++) {
401       if (! (xlmft->live_displays & (1 << i)))
402         break;
403     }
404     if (i >= size) abort();
405     xlmft->live_displays |= 1ul << i;
406     xlmft->got_init &= ~(1ul << i);
407     mi->screen_number = i;
408   }
409
410   root_p = (window == RootWindowOfScreen (mi->xgwa.screen));
411
412 #ifndef HAVE_JWXYZ
413
414   /* Everybody gets motion events, just in case. */
415   XSelectInput (dpy, window, (mi->xgwa.your_event_mask | PointerMotionMask));
416
417 #endif /* !HAVE_JWXYZ */
418
419   if (mi->xlmft->hack_release) {
420 /*
421     fprintf (
422       stderr,
423       "%s: WARNING: hack_release is not usually recommended; see MI_INIT.\n",
424       progname);
425 */
426   }
427
428   color.flags = DoRed|DoGreen|DoBlue;
429   color.red = color.green = color.blue = 0;
430   if (!XAllocColor(dpy, mi->xgwa.colormap, &color))
431     abort();
432   mi->black = color.pixel;
433   color.red = color.green = color.blue = 0xFFFF;
434   if (!XAllocColor(dpy, mi->xgwa.colormap, &color))
435     abort();
436   mi->white = color.pixel;
437
438   if (mono_p)
439     {
440       static XColor colors[2];
441     MONO:
442       mi->npixels = 2;
443       if (! mi->pixels)
444         mi->pixels = (unsigned long *) 
445           calloc (mi->npixels, sizeof (*mi->pixels));
446       if (!mi->colors)
447         mi->colors = (XColor *) 
448           calloc (mi->npixels, sizeof (*mi->colors));
449       colors[0].flags = DoRed|DoGreen|DoBlue;
450       colors[1].flags = DoRed|DoGreen|DoBlue;
451       colors[0].red = colors[0].green = colors[0].blue = 0;
452       colors[1].red = colors[1].green = colors[1].blue = 0xFFFF;
453       mi->writable_p = False;
454     }
455   else
456     {
457       mi->npixels = get_integer_resource (dpy, "ncolors", "Integer");
458       if (mi->npixels <= 0)
459         mi->npixels = 64;
460       else if (mi->npixels > MAX_COLORS)
461         mi->npixels = MAX_COLORS;
462
463       mi->colors = (XColor *) calloc (mi->npixels, sizeof (*mi->colors));
464
465       mi->writable_p = mi->xlmft->want_writable_colors;
466
467       switch (mi->xlmft->desired_color_scheme)
468         {
469         case color_scheme_uniform:
470           make_uniform_colormap (mi->xgwa.screen, mi->xgwa.visual,
471                                  mi->xgwa.colormap,
472                                  mi->colors, &mi->npixels,
473                                  True, &mi->writable_p, True);
474           break;
475         case color_scheme_smooth:
476           make_smooth_colormap (mi->xgwa.screen, mi->xgwa.visual,
477                                 mi->xgwa.colormap,
478                                 mi->colors, &mi->npixels,
479                                 True, &mi->writable_p, True);
480           break;
481         case color_scheme_bright:
482         case color_scheme_default:
483           make_random_colormap (mi->xgwa.screen, mi->xgwa.visual,
484                                 mi->xgwa.colormap,
485                                 mi->colors, &mi->npixels,
486                                 (mi->xlmft->desired_color_scheme ==
487                                  color_scheme_bright),
488                                 True, &mi->writable_p, True);
489           break;
490         default:
491           abort();
492         }
493
494       if (mi->npixels <= 2)
495         goto MONO;
496       else
497         {
498           mi->pixels = (unsigned long *)
499             calloc (mi->npixels, sizeof (*mi->pixels));
500           for (i = 0; i < mi->npixels; i++)
501             mi->pixels[i] = mi->colors[i].pixel;
502         }
503     }
504
505   gcv.foreground = mi->white;
506   gcv.background = mi->black;
507   mi->gc = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
508
509   mi->fullrandom = True;
510
511   mi->pause      = get_integer_resource (dpy, "delay", "Usecs");
512
513   mi->cycles     = get_integer_resource (dpy, "cycles", "Int");
514   mi->batchcount = get_integer_resource (dpy, "count", "Int");
515   mi->size       = get_integer_resource (dpy, "size", "Int");
516
517   mi->threed = get_boolean_resource (dpy, "use3d", "Boolean");
518   mi->threed_delta = get_float_resource (dpy, "delta3d", "Float");
519   mi->threed_right_color = get_pixel_resource (dpy,
520                                                mi->xgwa.colormap, "right3d", "Color");
521   mi->threed_left_color = get_pixel_resource (dpy,
522                                               mi->xgwa.colormap, "left3d", "Color");
523   mi->threed_both_color = get_pixel_resource (dpy,
524                                               mi->xgwa.colormap, "both3d", "Color");
525   mi->threed_none_color = get_pixel_resource (dpy,
526                                               mi->xgwa.colormap, "none3d", "Color");
527
528   mi->wireframe_p = get_boolean_resource (dpy, "wireframe", "Boolean");
529   mi->root_p = root_p;
530   mi->fps_p = get_boolean_resource (dpy, "doFPS", "DoFPS");
531   mi->recursion_depth = -1;  /* see fps.c */
532
533   if (mi->pause < 0)
534     mi->pause = 0;
535   else if (mi->pause > 100000000)
536     mi->pause = 100000000;
537
538   /* If this hack uses fonts (meaning, mentioned "font" in DEFAULTS)
539      then load it. */
540   {
541     char *name = get_string_resource (dpy, "font", "Font");
542     if (name)
543       {
544         XFontStruct *f = XLoadQueryFont (dpy, name);
545         const char *def1 = "-*-helvetica-bold-r-normal-*-180-*";
546         const char *def2 = "fixed";
547         if (!f)
548           {
549             fprintf (stderr, "%s: font %s does not exist, using %s\n",
550                      progname, name, def1);
551             f = XLoadQueryFont (dpy, def1);
552           }
553         if (!f)
554           {
555             fprintf (stderr, "%s: font %s does not exist, using %s\n",
556                      progname, def1, def2);
557             f = XLoadQueryFont (dpy, def2);
558           }
559         if (f) XSetFont (dpy, mi->gc, f->fid);
560         if (f) XFreeFont (dpy, f);
561         free (name);
562       }
563   }
564   
565   xlockmore_read_resources (mi);
566
567   return mi;
568 }
569
570
571 static void
572 xlockmore_do_init (ModeInfo *mi)
573 {
574   mi->xlmft->got_init |= 1 << mi->screen_number;
575   XClearWindow (mi->dpy, mi->window);
576   mi->xlmft->hack_init (mi);
577 }
578
579
580 static Bool
581 xlockmore_got_init (ModeInfo *mi)
582 {
583   return mi->xlmft->got_init & (1 << mi->screen_number);
584 }
585
586
587 static void
588 xlockmore_abort_erase (ModeInfo *mi)
589 {
590   if (mi->eraser) {
591     eraser_free (mi->eraser);
592     mi->eraser = NULL;
593   }
594   mi->needs_clear = False;
595 }
596
597
598 static void
599 xlockmore_check_init (ModeInfo *mi)
600 {
601   if (! xlockmore_got_init (mi)) {
602     xlockmore_abort_erase (mi);
603     xlockmore_do_init (mi);
604   }
605 }
606
607
608 static unsigned long
609 xlockmore_draw (Display *dpy, Window window, void *closure)
610 {
611   ModeInfo *mi = (ModeInfo *) closure;
612   unsigned long orig_pause = mi->pause;
613   unsigned long this_pause;
614
615   if (mi->needs_clear) {
616     /* OpenGL hacks never get here. */
617     if (!mi->is_drawn) {
618       XClearWindow (dpy, window);
619     } else {
620       mi->eraser = erase_window (dpy, window, mi->eraser);
621       /* Delay calls to xlockmore hooks while the erase animation is running. */
622       if (mi->eraser)
623         return 33333;
624     }
625     mi->needs_clear = False;
626   }
627
628   xlockmore_check_init (mi);
629   if (mi->needs_clear)
630     return 0;
631   mi->xlmft->hack_draw (mi);
632
633   this_pause = mi->pause;
634   mi->pause  = orig_pause;
635   return mi->needs_clear ? 0 : this_pause;
636 }
637
638
639 static void
640 xlockmore_reshape (Display *dpy, Window window, void *closure, 
641                  unsigned int w, unsigned int h)
642 {
643   ModeInfo *mi = (ModeInfo *) closure;
644   if (mi) {
645     /* Ignore spurious resize events, because xlockmore_do_init usually clears
646        the screen, and there's no reason to do that if we don't have to.
647      */
648 # ifndef HAVE_MOBILE
649     /* These are not spurious on mobile: they are rotations. */
650     if (mi->xgwa.width == w && mi->xgwa.height == h)
651       return;
652 # endif
653     mi->xgwa.width = w;
654     mi->xgwa.height = h;
655
656     /* Finish any erase operations. */
657     if (mi->needs_clear) {
658       xlockmore_abort_erase (mi);
659       XClearWindow (dpy, window);
660     }
661
662     /* If there hasn't been an init yet, init now, but don't call reshape_##.
663      */
664     if (xlockmore_got_init (mi) && mi->xlmft->hack_reshape) {
665       mi->xlmft->hack_reshape (mi, mi->xgwa.width, mi->xgwa.height);
666     } else {
667       mi->is_drawn = False;
668       xlockmore_do_init (mi);
669     }
670   }
671 }
672
673 static Bool
674 xlockmore_event (Display *dpy, Window window, void *closure, XEvent *event)
675 {
676   ModeInfo *mi = (ModeInfo *) closure;
677   if (mi) {
678     if (mi->xlmft->hack_handle_events) {
679       xlockmore_check_init (mi);
680       return mi->xlmft->hack_handle_events (mi, event);
681     }
682
683     if (screenhack_event_helper (mi->dpy, mi->window, event)) {
684       /* If a clear is in progress, don't interrupt or restart it. */
685       if (mi->needs_clear)
686         mi->xlmft->got_init &= ~(1ul << mi->screen_number);
687       else
688         mi->xlmft->hack_init (mi);
689       return True;
690     }
691   }
692   return False;
693 }
694
695 void
696 xlockmore_do_fps (Display *dpy, Window w, fps_state *fpst, void *closure)
697 {
698   ModeInfo *mi = (ModeInfo *) closure;
699   fps_compute (fpst, 0, mi ? mi->recursion_depth : -1);
700   fps_draw (fpst);
701 }
702
703
704 static void
705 xlockmore_free (Display *dpy, Window window, void *closure)
706 {
707   ModeInfo *mi = (ModeInfo *) closure;
708
709   if (mi->eraser)
710     eraser_free (mi->eraser);
711
712   /* Some hacks may need to do things with their Display * on cleanup. And
713      under JWXYZ, the Display * for this hack gets cleaned up right after
714      xlockmore_free returns. Thus, hack_free has to happen now, rather than
715      after the final screen has been released.
716    */
717   if (mi->xlmft->hack_free)
718     mi->xlmft->hack_free (mi);
719
720   /* Find us in live_displays and clear that slot. */
721   assert (mi->xlmft->live_displays & (1ul << mi->screen_number));
722   mi->xlmft->live_displays &= ~(1ul << mi->screen_number);
723   if (!mi->xlmft->live_displays)
724     xlockmore_release_screens (mi);
725
726   XFreeGC (dpy, mi->gc);
727   free_colors (mi->xgwa.screen, mi->xgwa.colormap, mi->colors, mi->npixels);
728   free (mi->colors);
729   free (mi->pixels);
730
731   free (mi);
732 }
733
734
735 void
736 xlockmore_mi_init (ModeInfo *mi, size_t state_size, void **state_array)
737 {
738   struct xlockmore_function_table *xlmft = mi->xlmft;
739
740   /* Steal the state_array for safe keeping.
741      Only necessary when the screenhack isn't a once per process deal.
742      (i.e. macOS, iOS, Android)
743    */
744   assert ((!xlmft->state_array && !*state_array) ||
745           xlmft->state_array == state_array);
746   xlmft->state_array = state_array;
747
748   if (!*xlmft->state_array) {
749     *xlmft->state_array = calloc (XLOCKMORE_NUM_SCREENS, state_size);
750
751     if (!*xlmft->state_array) {
752 #ifdef HAVE_JWXYZ
753       /* Throws an exception instead of exiting the process. */
754       jwxyz_abort ("%s: out of memory", progname);
755 #else
756       fprintf (stderr, "%s: out of memory\n", progname);
757       exit (1);
758 #endif
759     }
760   }
761
762   /* Find the appropriate state object, clear it, and we're done. */
763   {
764     if (xlmft->hack_free)
765       xlmft->hack_free (mi);
766     memset ((char *)(*xlmft->state_array) + mi->screen_number * state_size, 0,
767             state_size);
768   }
769 }
770
771
772 Bool
773 xlockmore_no_events (ModeInfo *mi, XEvent *event)
774 {
775   return False;
776 }