From http://www.jwz.org/xscreensaver/xscreensaver-5.39.tar.gz
[xscreensaver] / hacks / xlockmore.c
1 /* xlockmore.c --- xscreensaver compatibility layer for xlockmore modules.
2  * xscreensaver, Copyright (c) 1997-2018 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               if (*var_c) 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 = load_font_retry (dpy, name);
545         if (!f) abort();
546         XSetFont (dpy, mi->gc, f->fid);
547         XFreeFont (dpy, f);
548         free (name);
549       }
550   }
551   
552   xlockmore_read_resources (mi);
553
554   return mi;
555 }
556
557
558 static void
559 xlockmore_clear (ModeInfo *mi)
560 {
561 # ifndef HAVE_ANDROID
562   /* TODO: Clear the window for Xlib hacks on Android. */
563   XClearWindow (mi->dpy, mi->window);
564 # endif
565 }
566
567
568 static void
569 xlockmore_do_init (ModeInfo *mi)
570 {
571   mi->xlmft->got_init |= 1 << mi->screen_number;
572   xlockmore_clear (mi);
573   mi->xlmft->hack_init (mi);
574 }
575
576
577 static Bool
578 xlockmore_got_init (ModeInfo *mi)
579 {
580   return mi->xlmft->got_init & (1 << mi->screen_number);
581 }
582
583
584 static void
585 xlockmore_abort_erase (ModeInfo *mi)
586 {
587   if (mi->eraser) {
588     eraser_free (mi->eraser);
589     mi->eraser = NULL;
590   }
591   mi->needs_clear = False;
592 }
593
594
595 static void
596 xlockmore_check_init (ModeInfo *mi)
597 {
598   if (! xlockmore_got_init (mi)) {
599     xlockmore_abort_erase (mi);
600     xlockmore_do_init (mi);
601   }
602 }
603
604
605 static unsigned long
606 xlockmore_draw (Display *dpy, Window window, void *closure)
607 {
608   ModeInfo *mi = (ModeInfo *) closure;
609   unsigned long orig_pause = mi->pause;
610   unsigned long this_pause;
611
612   if (mi->needs_clear) {
613     /* OpenGL hacks never get here. */
614     if (!mi->is_drawn) {
615       xlockmore_clear (mi);
616     } else {
617       mi->eraser = erase_window (dpy, window, mi->eraser);
618       /* Delay calls to xlockmore hooks while the erase animation is running. */
619       if (mi->eraser)
620         return 33333;
621     }
622     mi->needs_clear = False;
623   }
624
625   xlockmore_check_init (mi);
626   if (mi->needs_clear)
627     return 0;
628   mi->xlmft->hack_draw (mi);
629
630   this_pause = mi->pause;
631   mi->pause  = orig_pause;
632   return mi->needs_clear ? 0 : this_pause;
633 }
634
635
636 static void
637 xlockmore_reshape (Display *dpy, Window window, void *closure, 
638                  unsigned int w, unsigned int h)
639 {
640   ModeInfo *mi = (ModeInfo *) closure;
641   if (mi) {
642     /* Ignore spurious resize events, because xlockmore_do_init usually clears
643        the screen, and there's no reason to do that if we don't have to.
644      */
645 # ifndef HAVE_MOBILE
646     /* These are not spurious on mobile: they are rotations. */
647     if (mi->xgwa.width == w && mi->xgwa.height == h)
648       return;
649 # endif
650     mi->xgwa.width = w;
651     mi->xgwa.height = h;
652
653     /* Finish any erase operations. */
654     if (mi->needs_clear) {
655       xlockmore_abort_erase (mi);
656       xlockmore_clear (mi);
657     }
658
659     /* If there hasn't been an init yet, init now, but don't call reshape_##.
660      */
661     if (xlockmore_got_init (mi) && mi->xlmft->hack_reshape) {
662       mi->xlmft->hack_reshape (mi, mi->xgwa.width, mi->xgwa.height);
663     } else {
664       mi->is_drawn = False;
665       xlockmore_do_init (mi);
666     }
667   }
668 }
669
670 static Bool
671 xlockmore_event (Display *dpy, Window window, void *closure, XEvent *event)
672 {
673   ModeInfo *mi = (ModeInfo *) closure;
674   if (mi) {
675     if (mi->xlmft->hack_handle_events) {
676       xlockmore_check_init (mi);
677       return mi->xlmft->hack_handle_events (mi, event);
678     }
679
680     if (screenhack_event_helper (mi->dpy, mi->window, event)) {
681       /* If a clear is in progress, don't interrupt or restart it. */
682       if (mi->needs_clear)
683         mi->xlmft->got_init &= ~(1ul << mi->screen_number);
684       else
685         mi->xlmft->hack_init (mi);
686       return True;
687     }
688   }
689   return False;
690 }
691
692 void
693 xlockmore_do_fps (Display *dpy, Window w, fps_state *fpst, void *closure)
694 {
695   ModeInfo *mi = (ModeInfo *) closure;
696   fps_compute (fpst, 0, mi ? mi->recursion_depth : -1);
697   fps_draw (fpst);
698 }
699
700
701 static void
702 xlockmore_free (Display *dpy, Window window, void *closure)
703 {
704   ModeInfo *mi = (ModeInfo *) closure;
705
706   if (mi->eraser)
707     eraser_free (mi->eraser);
708
709   /* Some hacks may need to do things with their Display * on cleanup. And
710      under JWXYZ, the Display * for this hack gets cleaned up right after
711      xlockmore_free returns. Thus, hack_free has to happen now, rather than
712      after the final screen has been released.
713    */
714   if (mi->xlmft->hack_free)
715     mi->xlmft->hack_free (mi);
716
717   /* Find us in live_displays and clear that slot. */
718   assert (mi->xlmft->live_displays & (1ul << mi->screen_number));
719   mi->xlmft->live_displays &= ~(1ul << mi->screen_number);
720   if (!mi->xlmft->live_displays)
721     xlockmore_release_screens (mi);
722
723   XFreeGC (dpy, mi->gc);
724   free_colors (mi->xgwa.screen, mi->xgwa.colormap, mi->colors, mi->npixels);
725   free (mi->colors);
726   free (mi->pixels);
727
728   free (mi);
729 }
730
731
732 void
733 xlockmore_mi_init (ModeInfo *mi, size_t state_size, void **state_array)
734 {
735   struct xlockmore_function_table *xlmft = mi->xlmft;
736
737   /* Steal the state_array for safe keeping.
738      Only necessary when the screenhack isn't a once per process deal.
739      (i.e. macOS, iOS, Android)
740    */
741   assert ((!xlmft->state_array && !*state_array) ||
742           xlmft->state_array == state_array);
743   xlmft->state_array = state_array;
744
745   if (!*xlmft->state_array) {
746     *xlmft->state_array = calloc (XLOCKMORE_NUM_SCREENS, state_size);
747
748     if (!*xlmft->state_array) {
749 #ifdef HAVE_JWXYZ
750       /* Throws an exception instead of exiting the process. */
751       jwxyz_abort ("%s: out of memory", progname);
752 #else
753       fprintf (stderr, "%s: out of memory\n", progname);
754       exit (1);
755 #endif
756     }
757   }
758
759   /* Find the appropriate state object, clear it, and we're done. */
760   {
761     if (xlmft->hack_free)
762       xlmft->hack_free (mi);
763     memset ((char *)(*xlmft->state_array) + mi->screen_number * state_size, 0,
764             state_size);
765   }
766 }
767
768
769 Bool
770 xlockmore_no_events (ModeInfo *mi, XEvent *event)
771 {
772   return False;
773 }