From 38886de0c3e9ea5729ef23e4c653fa2822f52e8f Mon Sep 17 00:00:00 2001 From: Simon Rettberg Date: Tue, 6 Apr 2021 14:43:39 +0200 Subject: xscreensaver 6.00 --- utils/Makefile.in | 73 +-- utils/aligned_malloc.c | 259 +++++++++- utils/aligned_malloc.h | 8 +- utils/async_netdb.c | 4 +- utils/fade.c | 962 ------------------------------------- utils/font-retry.c | 630 ++++++++++++++++++------ utils/font-retry.h | 14 +- utils/grabclient.c | 21 +- utils/grabscreen.c | 19 +- utils/grabscreen.h | 4 +- utils/images/logo-180.xpm | 4 +- utils/images/logo-360.gif | Bin 0 -> 7868 bytes utils/images/logo-360.xpm | 390 +++++++++++++++ utils/images/logo-50.xpm | 2 +- utils/images/logo.pdf | 1156 +++++++++++++++++++++++++++++++++++++++++++++ utils/logo.c | 52 +- utils/minixpm.c | 2 +- utils/minixpm.h | 3 +- utils/queue.h | 638 +++++++++++++++++++++++++ utils/textclient-mobile.c | 2 +- utils/textclient.c | 129 ++++- utils/textclient.h | 4 +- utils/thread_util.c | 239 +--------- utils/thread_util.h | 2 +- utils/usleep.h | 4 - utils/utf8wc.c | 6 +- utils/version.h | 4 +- utils/visual-gl.c | 350 ++++++++++++-- utils/visual.h | 6 +- utils/vms-gtod.c | 31 -- utils/vms-gtod.h | 85 ---- utils/vms-strdup.c | 25 - utils/xdbe.c | 5 +- utils/xft.c | 110 ++++- utils/xft.h | 7 +- utils/xftwrap.c | 206 ++++++++ utils/xftwrap.h | 35 ++ utils/xmu.c | 197 ++------ utils/xmu.h | 8 +- utils/xshm.c | 29 +- utils/yarandom.c | 4 +- 41 files changed, 3859 insertions(+), 1870 deletions(-) delete mode 100644 utils/fade.c create mode 100644 utils/images/logo-360.gif create mode 100644 utils/images/logo-360.xpm create mode 100644 utils/images/logo.pdf create mode 100644 utils/queue.h delete mode 100644 utils/vms-gtod.c delete mode 100644 utils/vms-gtod.h delete mode 100644 utils/vms-strdup.c create mode 100644 utils/xftwrap.c create mode 100644 utils/xftwrap.h (limited to 'utils') diff --git a/utils/Makefile.in b/utils/Makefile.in index dbb593b..d624e2f 100644 --- a/utils/Makefile.in +++ b/utils/Makefile.in @@ -1,4 +1,4 @@ -# utils/Makefile.in --- xscreensaver, Copyright (c) 1997-2010 Jamie Zawinski. +# utils/Makefile.in --- xscreensaver, Copyright © 1997-2021 Jamie Zawinski. # the `../configure' script generates `utils/Makefile' from this file. @@ -102,35 +102,33 @@ X_CFLAGS = @X_CFLAGS@ INCLUDES_1 = -I$(srcdir) -I.. INCLUDES = $(INCLUDES_1) @INCLUDES@ -SRCS = alpha.c colors.c fade.c grabscreen.c grabclient.c hsv.c \ +SRCS = alpha.c colors.c grabscreen.c grabclient.c hsv.c \ overlay.c resources.c spline.c usleep.c visual.c \ visual-gl.c xmu.c logo.c yarandom.c erase.c \ xshm.c xdbe.c colorbars.c minixpm.c textclient.c \ textclient-mobile.c aligned_malloc.c thread_util.c \ - async_netdb.c xft.c utf8wc.c pow2.c font-retry.c -OBJS = alpha.o colors.o fade.o grabscreen.o grabclient.o hsv.o \ + async_netdb.c xft.c xftwrap.c utf8wc.c pow2.c font-retry.c +OBJS = alpha.o colors.o grabscreen.o grabclient.o hsv.o \ overlay.o resources.o spline.o usleep.o visual.o \ visual-gl.o xmu.o logo.o yarandom.o erase.o \ xshm.o xdbe.o colorbars.o minixpm.o textclient.o \ - textclient-mobile.o aligned_malloc.o thread_util.o \ - async_netdb.o xft.o utf8wc.o pow2.o \ - font-retry.o font-retry-xft.o -HDRS = alpha.h colors.h fade.h grabscreen.h hsv.h resources.h \ + aligned_malloc.o thread_util.o \ + async_netdb.o xft.o xftwrap.o utf8wc.o pow2.o \ + font-retry.o +HDRS = alpha.h colors.h grabscreen.h hsv.h resources.h \ spline.h usleep.h utils.h version.h visual.h vroot.h xmu.h \ yarandom.h erase.h xshm.h xdbe.h colorbars.h minixpm.h \ xscreensaver-intl.h textclient.h aligned_malloc.h \ - thread_util.h async_netdb.h xft.h utf8wc.h pow2.h \ - font-retry.h + thread_util.h async_netdb.h xft.h xftwrap.h utf8wc.h pow2.h \ + font-retry.h queue.h STAR = * LOGOS = images/$(STAR).xpm \ images/$(STAR).png \ images/$(STAR).gif \ - images/$(STAR).eps + images/$(STAR).pdf EXTRAS = README Makefile.in ad2c bin2c -VMSFILES = compile_axp.com compile_decc.com vms-gtod.c vms-gtod.h \ - vms-strdup.c -TARFILES = $(EXTRAS) $(VMSFILES) $(SRCS) $(HDRS) $(LOGOS) +TARFILES = $(EXTRAS) $(SRCS) $(HDRS) $(LOGOS) default: all @@ -185,49 +183,24 @@ TAGS: tags tags: find $(srcdir) -name '*.[chly]' -print | xargs etags -a -echo_tarfiles: - @echo $(TARFILES) - +list_tarfiles: + @find $(TARFILES) -type f -print | sort # How we build object files in this directory. CCUTILS = $(INCLUDES) $(DEFS) $(CPPFLAGS) $(CFLAGS) $(X_CFLAGS) .c.o: $(CC) -c $(CCUTILS) $< -# Two versions of this: driver/ does not link with Xft, but hacks/ does. -font-retry-xft.o: $(srcdir)/font-retry.c +font-retry.o: $(srcdir)/font-retry.c $(CC) -c $(CCUTILS) -DUSE_XFT $< -o $@ -# Rules for generating the VMS makefiles on Unix, so that it doesn't have to -# be done by hand... -# -VMS_AXP_COMPILE=$$ CC/DECC/PREFIX=ALL/DEFINE=(VMS,HAVE_CONFIG_H)/INCL=([],[-]) - -compile_axp.com: Makefile.in - @echo generating $@ from $<... ; \ - ( ( for c in $(SRCS) vms-*.c ; do \ - c=`echo $$c | tr a-z A-Z` ; \ - echo "$(VMS_AXP_COMPILE) $$c" ; \ - done ; \ - ) | sort -d ; \ - echo '$$ lib/cre utils.olb_axp' ; \ - echo '$$ lib utils.olb_axp *.obj' ; \ - echo '$$! delete/noconf *.obj;' ; \ - ) > $@ - -compile_decc.com: compile_axp.com - @echo generating $@ from $<... ; \ - sed 's/axp/decc/g' < $< > $@ - -distdepend:: compile_axp.com compile_decc.com - - ############################################################################## # # DO NOT DELETE: updated by make distdepend aligned_malloc.o: $(srcdir)/aligned_malloc.h +aligned_malloc.o: ../config.h alpha.o: $(srcdir)/alpha.h alpha.o: ../config.h alpha.o: $(srcdir)/hsv.h @@ -256,11 +229,6 @@ erase.o: $(srcdir)/resources.h erase.o: $(srcdir)/usleep.h erase.o: $(srcdir)/utils.h erase.o: $(srcdir)/yarandom.h -fade.o: ../config.h -fade.o: $(srcdir)/fade.h -fade.o: $(srcdir)/usleep.h -fade.o: $(srcdir)/utils.h -fade.o: $(srcdir)/visual.h font-retry.o: ../config.h font-retry.o: $(srcdir)/font-retry.h font-retry.o: $(srcdir)/utils.h @@ -286,11 +254,11 @@ hsv.o: $(srcdir)/hsv.h hsv.o: $(srcdir)/utils.h logo.o: ../config.h logo.o: $(srcdir)/images/logo-180.xpm +logo.o: $(srcdir)/images/logo-360.xpm logo.o: $(srcdir)/images/logo-50.xpm logo.o: $(srcdir)/minixpm.h logo.o: $(srcdir)/resources.h logo.o: $(srcdir)/utils.h -logo.o: $(srcdir)/visual.h minixpm.o: ../config.h minixpm.o: $(srcdir)/minixpm.h overlay.o: ../config.h @@ -328,12 +296,17 @@ xdbe.o: ../config.h xdbe.o: $(srcdir)/resources.h xdbe.o: $(srcdir)/utils.h xdbe.o: $(srcdir)/xdbe.h +xdbe.o: $(srcdir)/xmu.h xft.o: ../config.h +xftwrap.o: ../config.h +xftwrap.o: $(srcdir)/utils.h +xftwrap.o: $(srcdir)/xft.h +xftwrap.o: $(srcdir)/xftwrap.h xmu.o: ../config.h +xmu.o: $(srcdir)/xmu.h xshm.o: $(srcdir)/aligned_malloc.h xshm.o: ../config.h xshm.o: $(srcdir)/resources.h -xshm.o: $(srcdir)/thread_util.h xshm.o: $(srcdir)/utils.h xshm.o: $(srcdir)/xshm.h yarandom.o: ../config.h diff --git a/utils/aligned_malloc.c b/utils/aligned_malloc.c index b345ad1..8c45fc8 100644 --- a/utils/aligned_malloc.c +++ b/utils/aligned_malloc.c @@ -13,6 +13,10 @@ software for any purpose. It is provided "as is" without express or implied warranty. */ +#if HAVE_CONFIG_H +# include "config.h" +#endif + #include "aligned_malloc.h" #include @@ -21,6 +25,254 @@ implied warranty. #include #include +/* Might be changed by thread_util.c:threads_available() + */ +unsigned int aligned_malloc_default_alignment = sizeof(void *); + + +#if HAVE_UNISTD_H +# include +#endif + +#if defined __MACH__ && defined __APPLE__ /* OS X, iOS */ +# include +# include +#endif + +#define IS_POWER_OF_2(x) ((x) > 0 && !((x) & ((x) - 1))) + +/* + arraysize(a). Also known as countof(x), XtNumber(x), NELEMS(x), LEN(x), + NUMOF(x), ARRAY_SIZE(x), etc., since the fine folks behind C never got + around to including this incredibly useful macro in the standard library, + which is where it belongs. + + Much of the code here assumes that multiple processors in a system all use + the same cache line size...which might be wrong on occasion. +*/ + +#define arraysize(a) (sizeof(a) / sizeof(*(a))) +#define arrayend(a) ((a) + arraysize(a)) + +/* +These numbers are from: +- Linux: arch/(arch name)/include/asm/cache.h, note + L1_CACHE_BYTES/L1_CACHE_SHIFT/SMP_CACHE_BYTES. +- FreeBSD: sys/(sys name)/include/param.h, note + CACHE_LINE_SHIFT/CACHE_LINE_SIZE. + +Preprocessor symbols come from: +- TARGET_CPU_CPP_BUILTINS() in the GNU C preprocessor + +- http://predef.sourceforge.net/ +*/ + +/* +Several architectures need preprocessor symbols. + +Qualcomm Hexagon: 1 << 5 +Imagination Technologies META: 1 << 6 +OpenRISC: 16 (Linux has the cache line size as a todo.) +Unicore: 1 << 5 +*/ + +#if defined __MACH__ && defined __APPLE__ /* OS X, iOS */ +# include /* For TARGET_OS_IPHONE. */ +# ifdef TARGET_OS_IPHONE +# define _CACHE_LINE_SIZE 64 +# endif +#endif + +#if defined __FreeBSD__ && !defined _CACHE_LINE_SIZE +# include +# ifdef CACHE_LINE_SIZE +# define _CACHE_LINE_SIZE CACHE_LINE_SIZE +# endif +#endif + +#if !defined _CACHE_LINE_SIZE +# if defined __alpha || defined __alpha__ +/* DEC Alpha */ +# define _CACHE_LINE_SIZE 64 /* EV6 and above. EV4 and EV5 use 32 bytes. */ +# elif defined __arm__ +/* ARM architecture */ +# define _CACHE_LINE_SIZE (1 << 6) +# elif defined __AVR || defined __AVR__ +/* Atmel AVR32 */ +# define _CACHE_LINE_SIZE (1 << 5) +# elif defined __bfin || defined __BFIN__ +/* Analog Devices Blackfin */ +# define _CACHE_LINE_SIZE (1 << 5) +# elif defined _TMS320C6X || defined __TMS320C6X__ +/* Texas Instruments TMS320C6x */ +# define _CACHE_LINE_SIZE (1 << 7) /* From L2. L1 data cache line is 1 << 6. */ +# elif defined __cris +/* Axis Communications ETRAX CRIS */ +# define _CACHE_LINE_SIZE 32 +# elif defined __ia64__ || defined _IA64 +/* Intel Itanium */ +# define _CACHE_LINE_SIZE (1 << 7) +# elif defined __M32R__ || defined __m32r__ +/* Mitsubishi/Renesas M32R */ +# define _CACHE_LINE_SIZE (1 << 4) +# elif defined __m68k__ || defined M68000 || defined __MC68K__ +/* Motorola 68000 */ +# define _CACHE_LINE_SIZE (1 << 4) +# elif defined __MICROBLAZE__ || defined __microblaze__ +/* Xilinx MicroBlaze */ +# define _CACHE_LINE_SIZE (1 << 5) +# elif defined __mips__ || defined __mips || defined __MIPS__ +/* MIPS */ +# define _CACHE_LINE_SIZE (1 << 6) +# elif defined __mn10300__ || defined __MN10300__ +/* Matsushita/Panasonic MN103 */ +# define _CACHE_LINE_SIZE 32 /* MN103E010 has 16 bytes. */ +# elif defined __hppa || defined __hppa__ +/* Hewlett-Packard PA-RISC */ +# define _CACHE_LINE_SIZE 64 /* PA-RISC 2.0 uses 64 bytes, PA-RISC 1.1 uses 32. */ +# elif defined __powerpc || defined _ARCH_PPC +/* Power Architecture (a.k.a. PowerPC) */ +# define _CACHE_LINE_SIZE (1 << 7) /* Linux has a list of PPC models with associated L1_CACHE_SHIFT values. */ +# elif defined __s390__ || defined __370__ || defined __zarch__ || defined __SYSC_ZARCH__ +/* IBM System/390 */ +# define _CACHE_LINE_SIZE 256 +# elif defined SUNPLUS || defined __SCORE__ || defined __score__ +/* Sunplus S+core */ +# define _CACHE_LINE_SIZE (1 << 4) +# elif defined __sh__ +/* Hitachi SuperH */ +# define _CACHE_LINE_SIZE (1 << 5) /* SH3 and earlier used 1 << 4. */ +# elif defined __sparc__ || defined __sparc +/* SPARC */ +# define _CACHE_LINE_SIZE (1 << 7) /* Linux and FreeBSD disagree as to what this should be. */ +# elif defined __tile__ +/* Tilera TILE series */ +# define _CACHE_LINE_SIZE (1 << 6) /* TILEPro uses different sizes for L1 and L2. */ +# elif defined __i386 || defined __x86_64 +/* x86(-64) */ +# define _CACHE_LINE_SIZE (1 << 7) +# elif defined __xtensa__ || defined __XTENSA__ +/* Cadence Design Systems/Tensilica Xtensa */ +# define _CACHE_LINE_SIZE (1 << 5) /* 1 << 4 on some models. */ +# endif +#endif /* !defined _CACHE_LINE_SIZE */ + +#if defined __NetBSD__ && !defined _CACHE_LINE_SIZE +/* +NetBSD defines COHERENCY_UNIT to be 32 on MIPS, and 64 for all other platforms -- which is wrong. Still, this is what the kernel +uses; if this value didn't work, the system wouldn't run. +*/ +# include +# ifdef COHERENCY_UNIT +# define _CACHE_LINE_SIZE COHERENCY_UNIT +# endif +#endif + +#ifndef _CACHE_LINE_SIZE +# define _CACHE_LINE_SIZE 256 /* Fallback cache line size. */ +#endif + +static unsigned _get_cache_line_size(void) +{ + /* + The general idea: + - Try to get the actual cache line size from the operating system. + - In the interest of keeping things simple, this only checks with + glibc and OS X. + - A few other methods that could be added: + - Query x86 CPUs directly with the CPUID instruction. + - Query various ELF systems through the auxillary vector. + (Power, Alpha, SuperH) + - Query Linux through + /sys/devices/system/cpu/cpu?/cache/index?/coherency_line_size + (x86 only, AFAIK) + - Query Linux through cache_alignment in /proc/cpuinfo + - Query Solaris through PICL. + - If that fails, return a value appropriate for the current CPU + architecture. + - Otherwise, return a sufficiently large number. + */ + + /* + sysconf(3) is not a syscall, it's a glibc call that, for cache line sizes, + uses CPUID on x86 and returns 0 on other platforms. If it were to work on + most other platforms, it would have to get cache information from the + kernel, since that information is usually made available by the processor + only in privileged mode. + https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/i386/sysconf.c;hb=HEAD + */ + + /* uClibc, newlib, dietlibc, musl, Bionic do not have this. */ + +# if HAVE_UNISTD_H && ( \ + defined _SC_LEVEL1_DCACHE_LINESIZE || \ + defined _SC_LEVEL2_CACHE_LINESIZE || \ + defined _SC_LEVEL3_CACHE_LINESIZE || \ + defined _SC_LEVEL4_CACHE_LINESIZE) + { + static const int names[] = + { +# ifdef _SC_LEVEL1_DCACHE_LINESIZE + _SC_LEVEL1_DCACHE_LINESIZE, +# endif +# ifdef _SC_LEVEL2_CACHE_LINESIZE + _SC_LEVEL2_CACHE_LINESIZE, +# endif +# ifdef _SC_LEVEL3_CACHE_LINESIZE + _SC_LEVEL3_CACHE_LINESIZE, +# endif +# ifdef _SC_LEVEL4_CACHE_LINESIZE + _SC_LEVEL4_CACHE_LINESIZE +# endif + }; + + const int *name; + long result = 0; + + for(name = names; name != arrayend(names); ++name) + { + long sysconf_result = sysconf(*name); /* Can return -1 or 0 on + failure. */ + + if(sysconf_result > result) + result = sysconf_result; + } + + if(result) + return result; + + /* Currently, this fails for every platform that isn't x86. Perhaps + future versions will support other processors? */ + } +# endif + +# if defined __MACH__ && defined __APPLE__ + { + uint32_t result; /* sysctl.h says that hw.cachelinesize is a + CTLTYPE_INT. */ + size_t size = sizeof(result); + static const int name[] = {CTL_HW, HW_CACHELINE}; + + if(!sysctl((int *)name, 2, &result, &size, NULL, 0)) /* (int *) is for OS X. */ + { + assert(size == sizeof(result)); + return result; + }; + } +# endif + + /* Guess based on the CPU type. */ + return _CACHE_LINE_SIZE; +} + +unsigned get_cache_line_size(void) +{ + unsigned result = _get_cache_line_size(); + assert(result >= sizeof(void *)); + assert(IS_POWER_OF_2(result)); + return result; +} + /* aligned_alloc() (C11) or posix_memalign() (POSIX) are other possibilities for aligned_malloc(). */ @@ -28,10 +280,15 @@ implied warranty. int aligned_malloc(void **ptr, unsigned alignment, size_t size) { void *block_start; - ptrdiff_t align1 = alignment - 1; + ptrdiff_t align1; + + if (alignment == 0) + alignment = aligned_malloc_default_alignment; assert(alignment && !(alignment & (alignment - 1))); /* alignment must be a power of two. */ + align1 = alignment - 1; + size += sizeof(void *) + align1; block_start = malloc(size); if(!block_start) diff --git a/utils/aligned_malloc.h b/utils/aligned_malloc.h index b3f43c9..0ec675f 100644 --- a/utils/aligned_malloc.h +++ b/utils/aligned_malloc.h @@ -18,9 +18,13 @@ implied warranty. #include +extern unsigned int aligned_malloc_default_alignment; + +unsigned get_cache_line_size(void); + /* This can't simply be named posix_memalign, since the real thing uses free(), but this one can't. */ - int aligned_malloc(void **ptr, unsigned alignment, size_t size); - void aligned_free(void *); +int aligned_malloc(void **ptr, unsigned alignment, size_t size); +void aligned_free(void *); #endif /* __ALIGNED_MALLOC_H__ */ diff --git a/utils/async_netdb.c b/utils/async_netdb.c index 930da40..d9e2102 100644 --- a/utils/async_netdb.c +++ b/utils/async_netdb.c @@ -48,10 +48,10 @@ int _async_netdb_is_done (struct io_thread *io) # define _get_addr_family(addr) ((addr)->x_sockaddr_in.sin_family) # define _get_addr_len(addr) ((addr)->x_sockaddr_in.sin_len) -static const int _has_threads = -1; - # if ASYNC_NETDB_FAKE_EAI +static const int _has_threads = -1; + const char *_async_netdb_strerror (int errcode) { /* (h)strerror should return messages in the user's preferred language. */ diff --git a/utils/fade.c b/utils/fade.c deleted file mode 100644 index 7a2ce2b..0000000 --- a/utils/fade.c +++ /dev/null @@ -1,962 +0,0 @@ -/* xscreensaver, Copyright (c) 1992-2011 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 - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation. No representations are made about the suitability of this - * software for any purpose. It is provided "as is" without express or - * implied warranty. - */ - -#include "utils.h" - -#include /* for gettimeofday() */ - -#ifdef VMS -# include "vms-gtod.h" -#endif /* VMS */ - -#include "visual.h" -#include "usleep.h" -#include "fade.h" - -Colormap -copy_colormap (Screen *screen, Visual *visual, - Colormap cmap, Colormap into_cmap) -{ - int i; - Display *dpy = DisplayOfScreen (screen); - Window window = RootWindowOfScreen (screen); - int ncolors = CellsOfScreen (screen); - XColor *colors = 0; - - /* If this is a colormap on a mono visual, or one with insanely many - color cells, bug out. */ - if (ncolors <= 2 || ncolors > 4096) - return 0; - /* If this is a non-writable visual, bug out. */ - if (!has_writable_cells (screen, visual)) - return 0; - - if (! into_cmap) - into_cmap = XCreateColormap (dpy, window, visual, AllocAll); - if (! cmap) - cmap = DefaultColormapOfScreen (screen); - - colors = (XColor *) calloc(sizeof(XColor), ncolors); - for (i = 0; i < ncolors; i++) - colors [i].pixel = i; - XQueryColors (dpy, cmap, colors, ncolors); - XStoreColors (dpy, into_cmap, colors, ncolors); - free (colors); - return into_cmap; -} - - -void -blacken_colormap (Screen *screen, Colormap cmap) -{ - Display *dpy = DisplayOfScreen (screen); - int ncolors = CellsOfScreen (screen); - XColor *colors; - int i; - if (ncolors > 4096) - return; - colors = (XColor *) calloc(sizeof(XColor), ncolors); - for (i = 0; i < ncolors; i++) - colors[i].pixel = i; - XStoreColors (dpy, cmap, colors, ncolors); - free (colors); -} - - - -static void fade_screens_1 (Display *dpy, Colormap *cmaps, - Window *black_windows, int nwindows, - int seconds, int ticks, - Bool out_p, Bool clear_windows); - -#ifdef HAVE_SGI_VC_EXTENSION -static int sgi_gamma_fade (Display *dpy, - Window *black_windows, int nwindows, - int seconds, int ticks, - Bool out_p, Bool clear_windows); -#endif /* HAVE_SGI_VC_EXTENSION */ - -#ifdef HAVE_XF86VMODE_GAMMA -static int xf86_gamma_fade (Display *dpy, - Window *black_windows, int nwindows, - int seconds, int ticks, - Bool out_p, Bool clear_windows); -#endif /* HAVE_XF86VMODE_GAMMA */ - - -void -fade_screens (Display *dpy, Colormap *cmaps, - Window *black_windows, int nwindows, - int seconds, int ticks, - Bool out_p, Bool clear_windows) -{ - int oseconds = seconds; - Bool was_in_p = !out_p; - - /* When we're asked to fade in, first fade out, then fade in. - That way all the transitions are smooth -- from what's on the - screen, to black, to the desktop. - */ - if (was_in_p) - { - clear_windows = True; - out_p = True; - seconds /= 3; - if (seconds == 0) - seconds = 1; - } - - AGAIN: - -/* #### printf("\n\nfade_screens %d %d %d\n", seconds, ticks, out_p); */ - -#ifdef HAVE_SGI_VC_EXTENSION - /* First try to do it by fading the gamma in an SGI-specific way... */ - if (0 == sgi_gamma_fade(dpy, black_windows, nwindows, - seconds, ticks, out_p, - clear_windows)) - ; - else -#endif /* HAVE_SGI_VC_EXTENSION */ - -#ifdef HAVE_XF86VMODE_GAMMA - /* Then try to do it by fading the gamma in an XFree86-specific way... */ - if (0 == xf86_gamma_fade(dpy, black_windows, nwindows, - seconds, ticks, out_p, - clear_windows)) - ; - else -#endif /* HAVE_XF86VMODE_GAMMA */ - - /* Else, do it the old-fashioned way, which (somewhat) loses if - there are TrueColor windows visible. */ - fade_screens_1 (dpy, cmaps, black_windows, nwindows, - seconds, ticks, - out_p, clear_windows); - - /* If we were supposed to be fading in, do so now (we just faded out, - so now fade back in.) - */ - if (was_in_p) - { - was_in_p = False; - out_p = False; - seconds = oseconds * 2 / 3; - if (seconds == 0) - seconds = 1; - goto AGAIN; - } -} - - -static void -sleep_from (struct timeval *now, struct timeval *then, long usecs_per_step) -{ - /* If several seconds have passed, the machine must have been asleep - or thrashing or something. Don't sleep in that case, to avoid - overflowing and sleeping for an unconscionably long time. This - function should only be sleeping for very short periods. - */ - if (now->tv_sec - then->tv_sec < 5) - { - long diff = (((now->tv_sec - then->tv_sec) * 1000000) + - now->tv_usec - then->tv_usec); - if (usecs_per_step > diff) - usleep (usecs_per_step - diff); - } - - then->tv_sec = now->tv_sec; - then->tv_usec = now->tv_usec; -} - - - -/* The business with `cmaps_per_screen' is to fake out the SGI 8-bit video - hardware, which is capable of installing multiple (4) colormaps - simultaneously. We have to install multiple copies of the same set of - colors in order to fill up all the available slots in the hardware color - lookup table, so we install an extra N colormaps per screen to make sure - that all screens really go black. - - I'm told that this trick also works with XInside's AcceleratedX when using - the Matrox Millennium card (which also allows multiple PseudoColor and - TrueColor visuals to co-exist and display properly at the same time.) - - This trick works ok on the 24-bit Indy video hardware, but doesn't work at - all on the O2 24-bit hardware. I guess the higher-end hardware is too - "good" for this to work (dammit.) So... I figured out the "right" way to - do this on SGIs, which is to ramp the monitor's gamma down to 0. That's - what is implemented in sgi_gamma_fade(), so we use that if we can. - */ -static void -fade_screens_1 (Display *dpy, Colormap *cmaps, - Window *black_windows, int nwindows, - int seconds, int ticks, - Bool out_p, Bool clear_windows) -{ - int i, j, k; - int steps = seconds * ticks; - long usecs_per_step = (long)(seconds * 1000000) / (long)steps; - XEvent dummy_event; - int cmaps_per_screen = 5; - int nscreens = ScreenCount(dpy); - int ncmaps = nscreens * cmaps_per_screen; - Colormap *fade_cmaps = 0; - Bool installed = False; - int total_ncolors; - XColor *orig_colors, *current_colors, *screen_colors, *orig_screen_colors; - struct timeval then, now; -#ifdef GETTIMEOFDAY_TWO_ARGS - struct timezone tzp; -#endif - - total_ncolors = 0; - for (i = 0; i < nscreens; i++) - total_ncolors += CellsOfScreen (ScreenOfDisplay(dpy, i)); - - orig_colors = (XColor *) calloc(sizeof(XColor), total_ncolors); - current_colors = (XColor *) calloc(sizeof(XColor), total_ncolors); - - /* Get the contents of the colormap we are fading from or to. */ - screen_colors = orig_colors; - for (i = 0; i < nscreens; i++) - { - int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, i)); - Colormap cmap = (cmaps ? cmaps[i] : 0); - if (!cmap) cmap = DefaultColormap(dpy, i); - - for (j = 0; j < ncolors; j++) - screen_colors[j].pixel = j; - XQueryColors (dpy, cmap, screen_colors, ncolors); - - screen_colors += ncolors; - } - - memcpy (current_colors, orig_colors, total_ncolors * sizeof (XColor)); - - - /* Make the writable colormaps (we keep these around and reuse them.) */ - if (!fade_cmaps) - { - fade_cmaps = (Colormap *) calloc(sizeof(Colormap), ncmaps); - for (i = 0; i < nscreens; i++) - { - Visual *v = DefaultVisual(dpy, i); - Screen *s = ScreenOfDisplay(dpy, i); - if (has_writable_cells (s, v)) - for (j = 0; j < cmaps_per_screen; j++) - fade_cmaps[(i * cmaps_per_screen) + j] = - XCreateColormap (dpy, RootWindowOfScreen (s), v, AllocAll); - } - } - -#ifdef GETTIMEOFDAY_TWO_ARGS - gettimeofday(&then, &tzp); -#else - gettimeofday(&then); -#endif - - /* Iterate by steps of the animation... */ - for (i = (out_p ? steps : 0); - (out_p ? i > 0 : i < steps); - (out_p ? i-- : i++)) - { - - /* For each screen, compute the current value of each color... - */ - orig_screen_colors = orig_colors; - screen_colors = current_colors; - for (j = 0; j < nscreens; j++) - { - int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, j)); - for (k = 0; k < ncolors; k++) - { - /* This doesn't take into account the relative luminance of the - RGB components (0.299, 0.587, and 0.114 at gamma 2.2) but - the difference is imperceptible for this application... */ - screen_colors[k].red = orig_screen_colors[k].red * i / steps; - screen_colors[k].green = orig_screen_colors[k].green * i / steps; - screen_colors[k].blue = orig_screen_colors[k].blue * i / steps; - } - screen_colors += ncolors; - orig_screen_colors += ncolors; - } - - /* Put the colors into the maps... - */ - screen_colors = current_colors; - for (j = 0; j < nscreens; j++) - { - int ncolors = CellsOfScreen (ScreenOfDisplay (dpy, j)); - for (k = 0; k < cmaps_per_screen; k++) - { - Colormap c = fade_cmaps[j * cmaps_per_screen + k]; - if (c) - XStoreColors (dpy, c, screen_colors, ncolors); - } - screen_colors += ncolors; - } - - /* Put the maps on the screens, and then take the windows off the screen. - (only need to do this the first time through the loop.) - */ - if (!installed) - { - for (j = 0; j < ncmaps; j++) - if (fade_cmaps[j]) - XInstallColormap (dpy, fade_cmaps[j]); - installed = True; - - if (black_windows && !out_p) - for (j = 0; j < nwindows; j++) - if (black_windows[j]) - { - XUnmapWindow (dpy, black_windows[j]); - XClearWindow (dpy, black_windows[j]); - } - } - - XSync (dpy, False); - - /* If there is user activity, bug out. (Bug out on keypresses or - mouse presses, but not motion, and not release events. Bugging - out on motion made the unfade hack be totally useless, I think.) - - We put the event back so that the calling code can notice it too. - It would be better to not remove it at all, but that's harder - because Xlib has such a non-design for this kind of crap, and - in this application it doesn't matter if the events end up out - of order, so in the grand unix tradition we say "fuck it" and - do something that mostly works for the time being. - */ - if (XCheckMaskEvent (dpy, (KeyPressMask|ButtonPressMask), &dummy_event)) - { - XPutBackEvent (dpy, &dummy_event); - goto DONE; - } - -#ifdef GETTIMEOFDAY_TWO_ARGS - gettimeofday(&now, &tzp); -#else - gettimeofday(&now); -#endif - - /* If we haven't already used up our alotted time, sleep to avoid - changing the colormap too fast. */ - sleep_from (&now, &then, usecs_per_step); - } - - DONE: - - if (orig_colors) free (orig_colors); - if (current_colors) free (current_colors); - - /* If we've been given windows to raise after blackout, raise them before - releasing the colormaps. - */ - if (out_p && black_windows) - { - for (i = 0; i < nwindows; i++) - { - if (clear_windows) - XClearWindow (dpy, black_windows[i]); - XMapRaised (dpy, black_windows[i]); - } - XSync(dpy, False); - } - - /* Now put the target maps back. - If we're fading out, use the given cmap (or the default cmap, if none.) - If we're fading in, always use the default cmap. - */ - for (i = 0; i < nscreens; i++) - { - Colormap cmap = (cmaps ? cmaps[i] : 0); - if (!cmap || !out_p) - cmap = DefaultColormap(dpy, i); - XInstallColormap (dpy, cmap); - } - - /* The fade (in or out) is complete, so we don't need the black maps on - stage any more. - */ - for (i = 0; i < ncmaps; i++) - if (fade_cmaps[i]) - { - XUninstallColormap(dpy, fade_cmaps[i]); - XFreeColormap(dpy, fade_cmaps[i]); - fade_cmaps[i] = 0; - } - free(fade_cmaps); - fade_cmaps = 0; -} - - - -/* SGI Gamma fading */ - -#ifdef HAVE_SGI_VC_EXTENSION - -# include - -struct screen_sgi_gamma_info { - int gamma_map; /* ??? always using 0 */ - int nred, ngreen, nblue; - unsigned short *red1, *green1, *blue1; - unsigned short *red2, *green2, *blue2; - int gamma_size; - int gamma_precision; - Bool alpha_p; -}; - - -static void sgi_whack_gamma(Display *dpy, int screen, - struct screen_sgi_gamma_info *info, float ratio); - -static int -sgi_gamma_fade (Display *dpy, - Window *black_windows, int nwindows, - int seconds, int ticks, - Bool out_p, Bool clear_windows) -{ - int steps = seconds * ticks; - long usecs_per_step = (long)(seconds * 1000000) / (long)steps; - XEvent dummy_event; - int nscreens = ScreenCount(dpy); - struct timeval then, now; -#ifdef GETTIMEOFDAY_TWO_ARGS - struct timezone tzp; -#endif - int i, screen; - int status = -1; - struct screen_sgi_gamma_info *info = (struct screen_sgi_gamma_info *) - calloc(nscreens, sizeof(*info)); - - /* Get the current gamma maps for all screens. - Bug out and return -1 if we can't get them for some screen. - */ - for (screen = 0; screen < nscreens; screen++) - { - if (!XSGIvcQueryGammaMap(dpy, screen, info[screen].gamma_map, - &info[screen].gamma_size, - &info[screen].gamma_precision, - &info[screen].alpha_p)) - goto FAIL; - - if (!XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map, - XSGIVC_COMPONENT_RED, - &info[screen].nred, &info[screen].red1)) - goto FAIL; - if (! XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map, - XSGIVC_COMPONENT_GREEN, - &info[screen].ngreen, &info[screen].green1)) - goto FAIL; - if (!XSGIvcQueryGammaColors(dpy, screen, info[screen].gamma_map, - XSGIVC_COMPONENT_BLUE, - &info[screen].nblue, &info[screen].blue1)) - goto FAIL; - - if (info[screen].gamma_precision == 8) /* Scale it up to 16 bits. */ - { - int j; - for(j = 0; j < info[screen].nred; j++) - info[screen].red1[j] = - ((info[screen].red1[j] << 8) | info[screen].red1[j]); - for(j = 0; j < info[screen].ngreen; j++) - info[screen].green1[j] = - ((info[screen].green1[j] << 8) | info[screen].green1[j]); - for(j = 0; j < info[screen].nblue; j++) - info[screen].blue1[j] = - ((info[screen].blue1[j] << 8) | info[screen].blue1[j]); - } - - info[screen].red2 = (unsigned short *) - malloc(sizeof(*info[screen].red2) * (info[screen].nred+1)); - info[screen].green2 = (unsigned short *) - malloc(sizeof(*info[screen].green2) * (info[screen].ngreen+1)); - info[screen].blue2 = (unsigned short *) - malloc(sizeof(*info[screen].blue2) * (info[screen].nblue+1)); - } - -#ifdef GETTIMEOFDAY_TWO_ARGS - gettimeofday(&then, &tzp); -#else - gettimeofday(&then); -#endif - - /* If we're fading in (from black), then first crank the gamma all the - way down to 0, then take the windows off the screen. - */ - if (!out_p) - { - for (screen = 0; screen < nscreens; screen++) - sgi_whack_gamma(dpy, screen, &info[screen], 0.0); - - for (screen = 0; screen < nwindows; screen++) - if (black_windows && black_windows[screen]) - { - XUnmapWindow (dpy, black_windows[screen]); - XClearWindow (dpy, black_windows[screen]); - XSync(dpy, False); - } - } - - /* Iterate by steps of the animation... */ - for (i = (out_p ? steps : 0); - (out_p ? i > 0 : i < steps); - (out_p ? i-- : i++)) - { - for (screen = 0; screen < nscreens; screen++) - { - sgi_whack_gamma(dpy, screen, &info[screen], - (((float)i) / ((float)steps))); - - /* If there is user activity, bug out. (Bug out on keypresses or - mouse presses, but not motion, and not release events. Bugging - out on motion made the unfade hack be totally useless, I think.) - - We put the event back so that the calling code can notice it too. - It would be better to not remove it at all, but that's harder - because Xlib has such a non-design for this kind of crap, and - in this application it doesn't matter if the events end up out - of order, so in the grand unix tradition we say "fuck it" and - do something that mostly works for the time being. - */ - if (XCheckMaskEvent (dpy, (KeyPressMask|ButtonPressMask), - &dummy_event)) - { - XPutBackEvent (dpy, &dummy_event); - goto DONE; - } - -#ifdef GETTIMEOFDAY_TWO_ARGS - gettimeofday(&now, &tzp); -#else - gettimeofday(&now); -#endif - - /* If we haven't already used up our alotted time, sleep to avoid - changing the colormap too fast. */ - sleep_from (&now, &then, usecs_per_step); - } - } - - - DONE: - - if (out_p && black_windows) - { - for (screen = 0; screen < nwindows; screen++) - { - if (clear_windows) - XClearWindow (dpy, black_windows[screen]); - XMapRaised (dpy, black_windows[screen]); - } - XSync(dpy, False); - } - - /* I can't explain this; without this delay, we get a flicker. - I suppose there's some lossage with stale bits being in the - hardware frame buffer or something, and this delay gives it - time to flush out. This sucks! */ - usleep(100000); /* 1/10th second */ - - for (screen = 0; screen < nscreens; screen++) - sgi_whack_gamma(dpy, screen, &info[screen], 1.0); - XSync(dpy, False); - - status = 0; - - FAIL: - for (screen = 0; screen < nscreens; screen++) - { - if (info[screen].red1) free (info[screen].red1); - if (info[screen].green1) free (info[screen].green1); - if (info[screen].blue1) free (info[screen].blue1); - if (info[screen].red2) free (info[screen].red2); - if (info[screen].green2) free (info[screen].green2); - if (info[screen].blue2) free (info[screen].blue2); - } - free(info); - - return status; -} - -static void -sgi_whack_gamma(Display *dpy, int screen, struct screen_sgi_gamma_info *info, - float ratio) -{ - int k; - - if (ratio < 0) ratio = 0; - if (ratio > 1) ratio = 1; - for (k = 0; k < info->gamma_size; k++) - { - info->red2[k] = info->red1[k] * ratio; - info->green2[k] = info->green1[k] * ratio; - info->blue2[k] = info->blue1[k] * ratio; - } - - XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->nred, - XSGIVC_MComponentRed, info->red2); - XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->ngreen, - XSGIVC_MComponentGreen, info->green2); - XSGIvcStoreGammaColors16(dpy, screen, info->gamma_map, info->nblue, - XSGIVC_MComponentBlue, info->blue2); - XSync(dpy, False); -} - -#endif /* HAVE_SGI_VC_EXTENSION */ - - - -/* XFree86 4.x+ Gamma fading */ - -#ifdef HAVE_XF86VMODE_GAMMA - -#include - -typedef struct { - XF86VidModeGamma vmg; - int size; - unsigned short *r, *g, *b; -} xf86_gamma_info; - -static int xf86_check_gamma_extension (Display *dpy); -static Bool xf86_whack_gamma (Display *dpy, int screen, - xf86_gamma_info *ginfo, float ratio); - -static int -xf86_gamma_fade (Display *dpy, - Window *black_windows, int nwindows, - int seconds, int ticks, - Bool out_p, Bool clear_windows) -{ - int steps = seconds * ticks; - long usecs_per_step = (long)(seconds * 1000000) / (long)steps; - XEvent dummy_event; - int nscreens = ScreenCount(dpy); - struct timeval then, now; -#ifdef GETTIMEOFDAY_TWO_ARGS - struct timezone tzp; -#endif - int i, screen; - int status = -1; - xf86_gamma_info *info = 0; - - static int ext_ok = -1; - - /* Only probe the extension once: the answer isn't going to change. */ - if (ext_ok == -1) - ext_ok = xf86_check_gamma_extension (dpy); - - /* If this server doesn't have the gamma extension, bug out. */ - if (ext_ok == 0) - goto FAIL; - -# ifndef HAVE_XF86VMODE_GAMMA_RAMP - if (ext_ok == 2) ext_ok = 1; /* server is newer than client! */ -# endif - - info = (xf86_gamma_info *) calloc(nscreens, sizeof(*info)); - - /* Get the current gamma maps for all screens. - Bug out and return -1 if we can't get them for some screen. - */ - for (screen = 0; screen < nscreens; screen++) - { - if (ext_ok == 1) /* only have gamma parameter, not ramps. */ - { - if (!XF86VidModeGetGamma(dpy, screen, &info[screen].vmg)) - goto FAIL; - } -# ifdef HAVE_XF86VMODE_GAMMA_RAMP - else if (ext_ok == 2) /* have ramps */ - { - if (!XF86VidModeGetGammaRampSize(dpy, screen, &info[screen].size)) - goto FAIL; - if (info[screen].size <= 0) - goto FAIL; - - info[screen].r = (unsigned short *) - calloc(info[screen].size, sizeof(unsigned short)); - info[screen].g = (unsigned short *) - calloc(info[screen].size, sizeof(unsigned short)); - info[screen].b = (unsigned short *) - calloc(info[screen].size, sizeof(unsigned short)); - - if (!(info[screen].r && info[screen].g && info[screen].b)) - goto FAIL; - - if (!XF86VidModeGetGammaRamp(dpy, screen, info[screen].size, - info[screen].r, - info[screen].g, - info[screen].b)) - goto FAIL; - } -# endif /* HAVE_XF86VMODE_GAMMA_RAMP */ - else - abort(); - } - -#ifdef GETTIMEOFDAY_TWO_ARGS - gettimeofday(&then, &tzp); -#else - gettimeofday(&then); -#endif - - /* If we're fading in (from black), then first crank the gamma all the - way down to 0, then take the windows off the screen. - */ - if (!out_p) - { - for (screen = 0; screen < nscreens; screen++) - xf86_whack_gamma(dpy, screen, &info[screen], 0.0); - for (screen = 0; screen < nwindows; screen++) - if (black_windows && black_windows[screen]) - { - XUnmapWindow (dpy, black_windows[screen]); - XClearWindow (dpy, black_windows[screen]); - XSync(dpy, False); - } - } - - /* Iterate by steps of the animation... */ - for (i = (out_p ? steps : 0); - (out_p ? i > 0 : i < steps); - (out_p ? i-- : i++)) - { - for (screen = 0; screen < nscreens; screen++) - { - xf86_whack_gamma(dpy, screen, &info[screen], - (((float)i) / ((float)steps))); - - /* If there is user activity, bug out. (Bug out on keypresses or - mouse presses, but not motion, and not release events. Bugging - out on motion made the unfade hack be totally useless, I think.) - - We put the event back so that the calling code can notice it too. - It would be better to not remove it at all, but that's harder - because Xlib has such a non-design for this kind of crap, and - in this application it doesn't matter if the events end up out - of order, so in the grand unix tradition we say "fuck it" and - do something that mostly works for the time being. - */ - if (XCheckMaskEvent (dpy, (KeyPressMask|ButtonPressMask), - &dummy_event)) - { - XPutBackEvent (dpy, &dummy_event); - goto DONE; - } - -#ifdef GETTIMEOFDAY_TWO_ARGS - gettimeofday(&now, &tzp); -#else - gettimeofday(&now); -#endif - - /* If we haven't already used up our alotted time, sleep to avoid - changing the colormap too fast. */ - sleep_from (&now, &then, usecs_per_step); - } - } - - - DONE: - - if (out_p && black_windows) - { - for (screen = 0; screen < nwindows; screen++) - { - if (clear_windows) - XClearWindow (dpy, black_windows[screen]); - XMapRaised (dpy, black_windows[screen]); - } - XSync(dpy, False); - } - - /* I can't explain this; without this delay, we get a flicker. - I suppose there's some lossage with stale bits being in the - hardware frame buffer or something, and this delay gives it - time to flush out. This sucks! */ - usleep(100000); /* 1/10th second */ - - for (screen = 0; screen < nscreens; screen++) - xf86_whack_gamma(dpy, screen, &info[screen], 1.0); - XSync(dpy, False); - - status = 0; - - FAIL: - if (info) - { - for (screen = 0; screen < nscreens; screen++) - { - if (info[screen].r) free(info[screen].r); - if (info[screen].g) free(info[screen].g); - if (info[screen].b) free(info[screen].b); - } - free(info); - } - - return status; -} - - -/* This bullshit is needed because the VidMode extension doesn't work - on remote displays -- but if the remote display has the extension - at all, XF86VidModeQueryExtension returns true, and then - XF86VidModeQueryVersion dies with an X error. Thank you XFree, - may I have another. - */ - -static Bool error_handler_hit_p = False; - -static int -ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error) -{ - error_handler_hit_p = True; - return 0; -} - -static Bool -safe_XF86VidModeQueryVersion (Display *dpy, int *majP, int *minP) -{ - Bool result; - XErrorHandler old_handler; - XSync (dpy, False); - error_handler_hit_p = False; - old_handler = XSetErrorHandler (ignore_all_errors_ehandler); - - result = XF86VidModeQueryVersion (dpy, majP, minP); - - XSync (dpy, False); - XSetErrorHandler (old_handler); - XSync (dpy, False); - - return (error_handler_hit_p - ? False - : result); -} - - - -/* VidModeExtension version 2.0 or better is needed to do gamma. - 2.0 added gamma values; 2.1 added gamma ramps. - */ -# define XF86_VIDMODE_GAMMA_MIN_MAJOR 2 -# define XF86_VIDMODE_GAMMA_MIN_MINOR 0 -# define XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR 2 -# define XF86_VIDMODE_GAMMA_RAMP_MIN_MINOR 1 - - - -/* Returns 0 if gamma fading not available; 1 if only gamma value setting - is available; 2 if gamma ramps are available. - */ -static int -xf86_check_gamma_extension (Display *dpy) -{ - int event, error, major, minor; - - if (!XF86VidModeQueryExtension (dpy, &event, &error)) - return 0; /* display doesn't have the extension. */ - - if (!safe_XF86VidModeQueryVersion (dpy, &major, &minor)) - return 0; /* unable to get version number? */ - - if (major < XF86_VIDMODE_GAMMA_MIN_MAJOR || - (major == XF86_VIDMODE_GAMMA_MIN_MAJOR && - minor < XF86_VIDMODE_GAMMA_MIN_MINOR)) - return 0; /* extension is too old for gamma. */ - - if (major < XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR || - (major == XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR && - minor < XF86_VIDMODE_GAMMA_RAMP_MIN_MINOR)) - return 1; /* extension is too old for gamma ramps. */ - - /* Copacetic */ - return 2; -} - - -/* XFree doesn't let you set gamma to a value smaller than this. - Apparently they didn't anticipate the trick I'm doing here... - */ -#define XF86_MIN_GAMMA 0.1 - - -static Bool -xf86_whack_gamma(Display *dpy, int screen, xf86_gamma_info *info, - float ratio) -{ - Bool status; - - XErrorHandler old_handler; - XSync (dpy, False); - error_handler_hit_p = False; - old_handler = XSetErrorHandler (ignore_all_errors_ehandler); - - if (ratio < 0) ratio = 0; - if (ratio > 1) ratio = 1; - - if (info->size == 0) /* we only have a gamma number, not a ramp. */ - { - XF86VidModeGamma g2; - - g2.red = info->vmg.red * ratio; - g2.green = info->vmg.green * ratio; - g2.blue = info->vmg.blue * ratio; - -# ifdef XF86_MIN_GAMMA - if (g2.red < XF86_MIN_GAMMA) g2.red = XF86_MIN_GAMMA; - if (g2.green < XF86_MIN_GAMMA) g2.green = XF86_MIN_GAMMA; - if (g2.blue < XF86_MIN_GAMMA) g2.blue = XF86_MIN_GAMMA; -# endif - - status = XF86VidModeSetGamma (dpy, screen, &g2); - } - else - { -# ifdef HAVE_XF86VMODE_GAMMA_RAMP - - unsigned short *r, *g, *b; - int i; - r = (unsigned short *) malloc(info->size * sizeof(unsigned short)); - g = (unsigned short *) malloc(info->size * sizeof(unsigned short)); - b = (unsigned short *) malloc(info->size * sizeof(unsigned short)); - - for (i = 0; i < info->size; i++) - { - r[i] = info->r[i] * ratio; - g[i] = info->g[i] * ratio; - b[i] = info->b[i] * ratio; - } - - status = XF86VidModeSetGammaRamp(dpy, screen, info->size, r, g, b); - - free (r); - free (g); - free (b); - -# else /* !HAVE_XF86VMODE_GAMMA_RAMP */ - abort(); -# endif /* !HAVE_XF86VMODE_GAMMA_RAMP */ - } - - XSync (dpy, False); - XSetErrorHandler (old_handler); - XSync (dpy, False); - - return status; -} - -#endif /* HAVE_XF86VMODE_GAMMA */ diff --git a/utils/font-retry.c b/utils/font-retry.c index f8e6507..3122969 100644 --- a/utils/font-retry.c +++ b/utils/font-retry.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 2018 by Jamie Zawinski +/* xscreensaver, Copyright (c) 2018-2021 by 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 @@ -9,11 +9,56 @@ * implied warranty. */ -/* Like XLoadQueryFont, but if it fails, it tries some heuristics to - load something close. +/* The value of XScreenSaver's font resource strings is a comma-separated + list of font names that look like this: + + Helvetica Bold Italic 12 + + We parse that into an XLFD and pass that along to the underlying systems: + XftFontOpenXlfd or jwxyz. + + Xft does aggressive substitution of unknown fonts, so if we don't get an + exact match on the font name, we move on to the next element in the list. + For the last element, we take whatever substitution we got. + + XftNameParse uses this syntax: + + "Helvetica Neue-12:style=Bold" + "Helvetica Neue-12:style=Bold Italic" + "Helvetica Neue-12:slant=110:weight=200" + + Cocoa uses PostScript names, with hyphens: + + [NSFont fontWithName:@"Helvetica-BoldOblique" size:12]; + [NSFont fontWithName:@"Times-Roman" size:12]; + + Alternately, with separated styles: + + [[NSFontManager sharedFontManager] + fontWithFamily:@"Helvetica" traits:NSBoldFontMask weight:5 size:12]; + + Android separates names from styles: + + Paint.setTypeface (Typeface.create ("Helvetica", Typeface.BOLD)); + Paint.setTextSize (12); + + See android/xscreensaver/src/org/jwz/xscreensaver/jwxyz.java + + + In XScreenSaver 6, USE_XFT is always true, as all programs now use Xft. + + In XScreenSaver 5, this file was compiled in two different ways: + - As font-retry.o for programs that did not link with libXft; + - As font-retry-xft.o for programs that did. + + #ifdef USE_XFT is whether Xft code should be called. + #ifdef HAVE_XFT is whether libXft is natively available. + + The reason there are two defines is that if HAVE_XFT is not defined, + the Xft API is still available through emulation provided by "xft.h". */ -#define _GNU_SOURCE +#define _GNU_SOURCE /* Why is this here? */ #include "utils.h" #include "visual.h" @@ -26,194 +71,471 @@ extern const char *progname; #define countof(x) (sizeof((x))/sizeof((*x))) #undef DEBUG +#undef SELFTEST + +#ifdef HAVE_JWXYZ +# define USE_XFT 1 +#endif + +#ifdef SELFTEST +static void xft_selftest (Display *dpy, int screen); +#endif + + +/* Parse font names of the form "Helvetica Neue Bold Italic 12". + */ +static char * +font_name_to_xlfd (const char *font_name) +{ + char *name = strdup (font_name); + char *xlfd = 0; + char *s, *s2, *b, *i, *o, c; + float size; + + if (name[0] == '-' || name[0] == '*') + return name; /* Already an XLFD */ + + /* Downcase ASCII; dash to space */ + for (s = name; *s; s++) + if (*s >= 'A' && *s <= 'Z') + *s += 'a'-'A'; + else if (*s == '-') + *s = ' '; + + /* "Family NN" or "Family-NN" */ + s = strrchr (name, ' '); + s2 = strrchr (name, '-'); + if (s && s2 && s2 > s) s = s2; + if (!s) goto FAIL; + if (1 != sscanf (s+1, " %f %c", &size, &c)) goto FAIL; + if (size <= 0) goto FAIL; + *s = 0; + + /* "Family Bold", etc. */ + b = strstr (name, " bold"); + i = strstr (name, " italic"); + o = strstr (name, " oblique"); + if (b) *b = 0; + if (i) *i = 0; + if (o) *o = 0; + + xlfd = (char *) malloc (strlen(name) + 80); + sprintf (xlfd, "-*-%s-%s-%s-*-*-*-%d-*-*-*-*-*-*", + name, + (b ? "bold" : "medium"), + (i ? "i" : o ? "o" : "r"), + (int) (size * 10)); + free (name); + return xlfd; + + FAIL: + fprintf (stderr, "%s: XFT: unparsable: \"%s\"\n", progname, font_name); + if (name) free (name); + if (xlfd) free (xlfd); + return 0; +} + + +#ifdef USE_XFT + +/* Xft silently substitutes fonts if the one you requested wasn't available. + This leads to the deplorable situation where we ask for a fixed width font + and are given a variable width font instead. This doesn't happen with + Courier, since Xft special-cases that one, but it happens with any other + fixed width font that is not similarly privileged. + + Even worse: when Xft substitutes fonts, it makes no attempt to return fonts + that are capable of displaying the language of the current locale. For + example: if your locale is set to Japanese and you request "Helvetica", Xft + silently substitutes "Nimbus Sans" -- a font which is not capable of + displaying Japanese characters. If instead you requested "asdfasdfghjkl", + you get "Noto Sans CJK JP", which does work. So that's just spectacular. + + Since there does not appear to be a way to ask Xft whether a particular + font exists, we load the font and then check to see if the name we got + is the name we requested. + + In more detail: + + - XftFontOpenXlfd is defined as: + XftFontOpenPattern (XftFontMatch (XftXlfdParse (xlfd))) + - XftFontOpenName is defined as: + XftFontOpenPattern (XftFontMatch (XftNameParse (name))) + - Calling XftFontOpenPattern with a pattern that has not been filtered + through XftFontMatch does not work. + - XftFontMatch substitutes another font if the pattern doesn't match. + - If the pattern has family "Courier", it substitutes a fixed width + font, e.g. "Liberation Mono". + - Otherwise it substitutes a variable width font, e.g. "DejaVu Sans". + It does this even if the pattern contained "spacing=100" or "110", + indicating a monospace or charcell font. + - XftXlfdParse does not translate "spacing" from XLFD to XftPattern, + but that doesn't matter since XftFontMatch ignores it anyway. + + Xft source is here: + https://opensource.apple.com/source/X11ForMacOSXSource/X11ForMacOSXSource-1.0/xc/lib/Xft1/ + + Courier, Helvetica and the other historical PostScript font names seem to + be special-cased in /etc/fonts/conf.d/ in the files 30-metric-aliases.conf, + 45-latin.conf and/or 60-latin.conf, which uses an idiosyncratic scripting + language implemented as XML! Some incomplete documentation on that baroque + mess is here: + https://www.freedesktop.org/software/fontconfig/fontconfig-user.html + + The Xft configuration files seem to special-case the names "monospace", + "serif" and "sans-serif" as generic fallback fonts. + + However, "sans-serif" is problematic because it does not survive the trip + through XftXlfdParse and XftFontOpenPattern -- XLFD font families cannot + include hyphens. So we have no choice but to turn it into "sans serif", + which is *not* special-cased by the Xft config files. + + In summary, everything is terrible, and it's a wonder anything works at all. + */ +static Bool +xlfd_substituted_p (XftFont *f, const char *xlfd) +{ +# ifndef HAVE_XFT + return False; /* No substitutions in the Xft emulator. */ +# else /* HAVE_XFT */ + + Bool ret = True; + const char *oxlfd = xlfd; + char *s = 0; + char *xname = 0; + char fname[1024]; + + if (*xlfd == '-' || strchr (xlfd, '*')) /* it's an xlfd */ + { + if (*xlfd != '-') goto FAIL; + xlfd++; + s = strchr (xlfd, '-'); if (!s) goto FAIL; /* skip foundry */ + xlfd = s+1; + s = strchr (xlfd, '-'); if (!s) goto FAIL; /* skip family */ + + { + char buf[1024]; + XftPattern *pat = XftXlfdParse (oxlfd, True, True); + XftNameUnparse (pat, buf, sizeof(buf)-1); + } + } + else /* It's an Xft name */ + { + s = strchr (xlfd, '-'); if (!s) goto FAIL; /* skip family */ + } + + xname = strdup (xlfd); + xname[s - xlfd] = 0; + + *fname = 0; + XftNameUnparse (f->pattern, fname, sizeof(fname)-1); + s = strchr (fname, ':'); /* Strip to "Family-NN" */ + if (s) *s = 0; + s = strrchr (fname, '-'); /* Strip to family */ + if (s) *s = 0; + + ret = !strcasestr (fname, xname); + +# ifdef DEBUG + if (ret) + fprintf (stderr, "%s: XFT: requested \"%s\" but got \"%s\"\n", + progname, xname, fname); +# endif + + FAIL: + + if (!s) + fprintf (stderr, "%s: unparsable XLFD: %s\n", progname, oxlfd); + if (xname) free (xname); + return ret; +# endif /* HAVE_XFT */ +} +#endif /* USE_XFT */ + static void * -load_font_retry_1 (Display *dpy, int screen, const char *xlfd, Bool xft_p) +load_font_retry_1 (Display *dpy, int screen, const char *font_list, Bool xft_p) { # ifdef USE_XFT # define LOADFONT(F) (xft_p \ - ? (void *) XftFontOpenXlfd (dpy, screen, (F)) \ + ? (void *) XftFontOpenXlfd (dpy, screen, (F)) \ : (void *) XLoadQueryFont (dpy, (F))) +# define UNLOADFONT(F) (xft_p \ + ? (void) XftFontClose (dpy, (F)) \ + : (void) XFreeFont (dpy, (F))) # else # define LOADFONT(F) ((void *) XLoadQueryFont (dpy, (F))) +# define UNLOADFONT(F) XFreeFont (dpy, (F)) # endif - void *f = xlfd ? LOADFONT(xlfd) : 0; + char *font_name = 0; + void *f = 0; + void *fallback = 0; # ifndef USE_XFT if (xft_p) abort(); # endif -# ifdef HAVE_JWXYZ - return f; -# else /* !HAVE_JWXYZ */ - if (! xlfd) xlfd = ""; - if (f) - { -# ifdef DEBUG - fprintf (stderr, "%s: loaded %s\n", progname, xlfd); +# ifdef SELFTEST + xft_selftest (dpy, screen); # endif - return f; - } - else + + if (! font_list) font_list = ""; + + /* Treat the string as a comma-separated list of font names. + Names are XLFDs or the XScreenSaver syntax described above. + Try to load each of them in order. + If a substitution was made, keep going, unless this is the last. + */ + if (font_list) { - Bool bold_p = (!!strcasestr (xlfd, "-bold-") || - !!strcasestr (xlfd, "-ocr")); - Bool italic_p = (!!strcasestr (xlfd, "-i-") || - !!strcasestr (xlfd, "-o-")); - Bool fixed_p = (!!strcasestr (xlfd, "courier") || - !!strcasestr (xlfd, "-ocr") || - !!strcasestr (xlfd, "-m-") || - !!strcasestr (xlfd, "-c-")); - int size = 0; + char *token = strdup (font_list); + char *otoken = token; + char *name2, *lasts; + if (!token) abort(); + while ((name2 = strtok_r (token, ",", &lasts))) + { + int L; + token = 0; + + /* Strip leading and trailing whitespace */ + while (*name2 == ' ' || *name2 == '\t' || *name2 == '\n') + name2++; + L = strlen(name2); + while (L && (name2[L-1] == ' ' || name2[L-1] == '\t' || + name2[L-1] == '\n')) + name2[--L] = 0; + + if (font_name) free (font_name); + font_name = font_name_to_xlfd (name2); # ifdef DEBUG - fprintf (stderr, "%s: failed %s\n", progname, xlfd); + fprintf (stderr, "%s: trying \"%s\" as \"%s\"\n", progname, + name2, font_name); # endif - if (!strcmp (xlfd, "vga")) /* BSOD uses this: it has no XLFD name. */ - fixed_p = True, size = 120; + f = LOADFONT (font_name); - /* Look for the first number in the string like "-180-" */ - if (! size) - { - const char *s; - for (s = xlfd; *s; s++) - if (s[0] == '-' && s[1] >= '0' && s[1] <= '9') - { - int i = s[1] - '0'; - const char *s2 = s+2; - while (*s2 >= '0' && *s2 <= '9') - { - i = i * 10 + *s2 - '0'; - s2++; - } - if (*s2 != '-') continue; /* Number ends with dash */ - if (i < 60 || i >= 2000) continue; /* In range 6pt - 200pt */ - if (i % 10) continue; /* Multiple of 10 */ - - size = i; - break; - } - } +# ifdef USE_XFT + /* If we did not get an exact match for the font family we requested, + reject this font and try the next one in the list. */ + if (f && xft_p && xlfd_substituted_p (f, font_name)) + { + if (fallback) + UNLOADFONT (fallback); + fallback = f; + f = 0; + } +# ifdef DEBUG + else if (!f) + fprintf (stderr, "%s: no match for \"%s\"\n", progname, font_name); +# endif +# endif /* USE_XFT */ - if (! size) - { - fprintf (stderr, "%s: unloadable, unparsable font: \"%s\"\n", - progname, xlfd); - xlfd = "fixed"; - return LOADFONT(xlfd); + if (f) break; } - else + free (otoken); + if (!font_name) abort(); + + /* If the last font in the list was an Xft pattern that matched but + was inexact, use it. */ + if (!f) { - const char *variable[] = { - "helvetica", - "arial", - "bitstream vera sans", - "gill sans", - "times", - "times new roman", - "new century schoolbook", - "utopia", - "palatino", - "lucida", - "bitstream charter", - - /* Don't use a wildcard family. If none of the above worked, then - then almost none of the X11 fonts are installed, and it's not - unlikely that "-*-*-medium-r-*-*-*-140-*-*-*-10646-1" will - match an Arabic or or Japanese font that contains no Latin - glyphs at all, even in a Latin locale. So in that case, just - let "helvetica" fall back to "fixed". - */ - /* "*" */ - }; - const char *fixed[] = { - "courier", - "courier new", - "courier 10 pitch", - "lucidatypewriter", - "american typewriter", - "fixed", - "ocr a std", - /* As above, but "can't happen" because we already tried fixed? */ - /* "*" */ - }; - const char *charsets[] = { "iso10646-1", "iso8859-1", "*-*" }; - const char *weights[] = { "bold", "medium" }; - const char *slants[] = { "o", "i", "r" }; - const char *spacings[] = { "m", "c", "p" }; - int a, b, c, d, e, g; - char buf[1024]; - - for (a = 0; a < countof(charsets); a++) - for (b = (bold_p ? 0 : 1); b < countof(weights); b++) - for (c = (italic_p ? 0 : 2); c < countof(slants); c++) - for (d = 0; - d < (fixed_p ? countof(fixed) : countof(variable)); - d++) - for (g = size; g >= 60; g -= 10) - for (e = (fixed_p ? 0 : 2); e < countof(spacings); e++) - { - sprintf (buf, - "-%s-%s-%s-%s-%s-%s-%s-%d-%s-%s-%s-%s-%s", - "*", /* foundry */ - (fixed_p ? fixed[d] : variable[d]), - weights[b], - slants[c], - "*", /* set width */ - "*", /* add style */ - "*", /* pixel size */ - g, /* point size */ - "*", /* x resolution */ - "*", /* y resolution */ - spacings[e], - "*", /* average width */ - charsets[a]); -# ifdef DEBUG - fprintf(stderr, "%s: trying %s\n", progname, buf); -# endif - f = LOADFONT(buf); - if (f) - { -# ifdef DEBUG - fprintf (stderr, - "%s: substituted \"%s\" for \"%s\"\n", - progname, buf, xlfd); -# endif - return f; - } - } - - fprintf (stderr, "%s: unable to find any alternatives to \"%s\"\n", - progname, xlfd); - xlfd = "fixed"; - return LOADFONT(xlfd); + f = fallback; + fallback = 0; } } -# endif /* !HAVE_JWXYZ */ + + if (!font_name) abort(); + if (!f) abort(); + +# ifdef DEBUG + if (f && font_name) + fprintf (stderr, "%s:%s loaded %s\n", progname, + (xft_p ? " XFT:" : ""), font_name); +# if defined(USE_XFT) && defined(HAVE_XFT) + if (xft_p && f) + { + XftPattern *p = ((XftFont *) f)->pattern; + char name[1024]; + char *s, *s1, *s2, *s3; + XftNameUnparse (p, name, sizeof(name)-1); + s = strstr (name, ":style="); + s1 = (s ? strstr (s+1, ",") : 0); + s2 = (s ? strstr (s+1, ":") : 0); + s3 = (s1 && s1 < s2 ? s1 : s2); + if (s3) strcpy (s3+1, " [...]"); + fprintf (stderr, "%s: XFT name: %s\n", progname, name); + } +# endif /* USE_XFT && HAVE_XFT */ +# endif /* DEBUG */ + + if (fallback) UNLOADFONT (fallback); + if (font_name) free (font_name); + return f; } + +#if 1 /* No longer used in XScreenSaver 6. + (Used by retired flag, juggle, xsublim) */ XFontStruct * -load_font_retry (Display *dpy, const char *xlfd) +load_font_retry (Display *dpy, const char *font_list) { - return (XFontStruct *) load_font_retry_1 (dpy, 0, xlfd, 0); + return (XFontStruct *) load_font_retry_1 (dpy, 0, font_list, 0); } +#endif -#ifdef USE_XFT +#if defined(USE_XFT) || defined(HAVE_JWXYZ) XftFont * -load_xft_font_retry (Display *dpy, int screen, const char *xlfd) +load_xft_font_retry (Display *dpy, int screen, const char *font_list) { - return (XftFont *) load_font_retry_1 (dpy, screen, xlfd, 1); + return (XftFont *) load_font_retry_1 (dpy, screen, font_list, 1); } +#endif -#elif defined(HAVE_JWXYZ) -XftFont * -load_xft_font_retry (Display *dpy, int screen, const char *xlfd) + +#ifdef SELFTEST +static void +xft_selftest (Display *dpy, int screen) { - return XftFontOpenXlfd (dpy, screen, xlfd); -} + int i; + const char *tests[] = { + "-*-ocr a std-medium-r-*-*-*-480-*-*-m-*-*-*", + "OCR A Std-48", + "OCR A Std-48:style=Bold Italic", + "OCR A Std-48:spacing=100", + "OCR A Std-48:spacing=110", + "-*-courier-medium-r-*-*-*-480-*-*-m-*-*-*", + "-*-courier-bold-o-*-*-*-480-*-*-m-*-*-*", + "Courier-48:style=Bold Italic", + "Courier-48:style=Italic Bold", /* seems to be illegal */ + "Courier-48:spacing=100", + "Courier-48:spacing=110", + "-*-helvetica-bold-o-*-*-*-480-*-*-m-*-*-*", + "Helvetica-48:style=Bold Italic", + "Liberation Mono-48:style=Bold Italic", + "Liberation Sans-48:style=Bold Italic", + "-*-sans serif-bold-o-*-*-*-480-*-*-m-*-*-*", + "-*-sans-serif-bold-o-*-*-*-480-*-*-m-*-*-*", + "-*-sans\\-serif-bold-o-*-*-*-480-*-*-m-*-*-*", + }; + + const char *tests2[] = { "Helvetica 10", + "Helvetica Bold 10", + "Helvetica Bold Italic 10", + "Helvetica Oblique Bold-10.5", + "Times New Roman-10", + "Times New Roman Bold-10", + "Times New Roman-Bold Oblique Italic 10", + "Times New Roman-Oblique Italic Bold 10", + "Times-20:style=Bold", + "Times-Oblique-20:style=Bold", + "sans serif-20:style=Bold", + "sans-serif-20:style=Bold", + "sans\\-serif-20:style=Bold", + }; + + fprintf (stderr, "\n"); + for (i = 0; i < countof(tests2); i++) + fprintf (stderr, "%s\n%s\n\n", tests2[i], font_name_to_xlfd (tests2[i])); + + fprintf (stderr, "\n"); + for (i = 0; i < countof(tests); i++) { + const char *name1 = tests[i]; + XftResult ret; + XftPattern *pat1 = 0, *pat2 = 0, *pat3 = 0; + char name2[1024], name3[1024]; + XftFont *ff; + Bool xlfd_p = (*name1 == '-'); -#endif /* !HAVE_JWXYZ */ +# define TRUNC(V) do { \ + char *s = strstr (V, ":style="); \ + char *s1 = (s ? strstr (s+1, ",") : 0); \ + char *s2 = (s ? strstr (s+1, ":") : 0); \ + char *s3 = (s1 && s1 < s2 ? s1 : s2); \ + if (s3) strcpy (s3+1, " [...]"); \ + } while(0) + + *name2 = 0; + + /* Name to Parse */ + + if (xlfd_p) + pat1 = XftXlfdParse (name1, True, True); + else + pat1 = XftNameParse (name1); + XftNameUnparse (pat1, name2, sizeof(name2)-1); + TRUNC(name2); + fprintf (stderr, "%s (\"%s\")\n\t-> \"%s\"\n", + (xlfd_p ? "XftXlfdParse" : "XftNameParse"), + name1, name2); + + + /* Name to pattern to Open */ + + ff = XftFontOpenPattern (dpy, pat1); + if (ff) { + pat2 = ff->pattern; + XftNameUnparse (pat2, name3, sizeof(name3)-1); + } else { + pat2 = 0; + strcpy (name3, "NULL"); + } + TRUNC(name3); + fprintf (stderr, "XftFontOpenPattern (\"%s\")\n\t-> \"%s\"\n", + name2, name3); + + + /* Name to pattern to Match */ + + pat2 = XftFontMatch (dpy, screen, pat1, &ret); + XftNameUnparse (pat2, name3, sizeof(name3)-1); + TRUNC(name3); + fprintf (stderr, "XftFontMatch (\"%s\")\n\t-> \"%s\", %s\n", + name2, name3, + (ret == XftResultMatch ? "Match" : + ret == XftResultNoMatch ? "NoMatch" : + ret == XftResultTypeMismatch ? "TypeMismatch" : + ret == XftResultNoId ? "NoId" : "???")); + + + /* Name to pattern to Match to Open */ + + strcpy (name2, name3); + ff = XftFontOpenPattern (dpy, pat2); + if (ff) { + pat3 = ff->pattern; + XftNameUnparse (pat3, name3, sizeof(name3)-1); + } else { + pat3 = 0; + strcpy (name3, "NULL"); + } + TRUNC(name3); + fprintf (stderr, "XftFontOpenPattern (\"%s\")\n\t-> \"%s\"\n", + name2, name3); + + + /* Name to Open */ + + ff = (xlfd_p + ? XftFontOpenXlfd (dpy, screen, name1) + : XftFontOpenName (dpy, screen, name1)); + *name2 = 0; + if (ff) { + pat1 = ff->pattern; + XftNameUnparse (pat1, name2, sizeof(name2)-1); + } else { + strcpy (name2, "NULL"); + } + TRUNC(name2); + fprintf (stderr, "%s (\"%s\")\n\t-> \"%s\"\n", + (xlfd_p ? "XftFontOpenXlfd" : "XftFontOpenName"), + name1, name2); + fprintf (stderr, "\n"); + } + + exit (0); +} +#endif /* SELFTEST */ diff --git a/utils/font-retry.h b/utils/font-retry.h index 83ba0e0..2611a18 100644 --- a/utils/font-retry.h +++ b/utils/font-retry.h @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 2018 by Jamie Zawinski +/* xscreensaver, Copyright © 2018-2021 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 @@ -12,13 +12,17 @@ #ifndef __FONT_RETRY_H__ #define __FONT_RETRY_H__ -/* Like XLoadQueryFont, but if it fails, it tries some heuristics to - load something close. +/* Like XLoadQueryFont, XftFontOpenXlfd or XftFontOpenName, except that the + argument can be a comma-separated list of font names. Each name can be + either an XLFD or Xft pattern. Returns the first exact match it finds, or + if there is no exact match, applies heuristics to the last font in the list + until it finds a substitution. */ -extern XFontStruct *load_font_retry (Display *, const char *xlfd); +extern XFontStruct *load_font_retry (Display *, const char *font_list); # ifdef __XSCREENSAVER_XFT_H__ /* if xft.h has been included */ -extern XftFont *load_xft_font_retry (Display *, int screen, const char *xlfd); +extern XftFont *load_xft_font_retry (Display *, int screen, + const char *font_list); # endif #endif /* __FONT_RETRY_H__ */ diff --git a/utils/grabclient.c b/utils/grabclient.c index abe069b..58db732 100644 --- a/utils/grabclient.c +++ b/utils/grabclient.c @@ -127,11 +127,11 @@ typedef struct { XtInputId pipe_id; FILE *pipe; -# if !defined(USE_IPHONE) && !defined(HAVE_COCOA) /* Real X11 */ +# if !defined(HAVE_IPHONE) && !defined(HAVE_COCOA) /* Real X11 */ pid_t pid; # endif -# if !defined(USE_IPHONE) && defined(HAVE_COCOA) /* Desktop OSX */ +# if !defined(HAVE_IPHONE) && defined(HAVE_COCOA) /* Desktop OSX */ char *directory; # endif @@ -236,7 +236,6 @@ checkerboard (Screen *screen, Drawable drawable) win_width = xgwa.width; win_height = xgwa.height; cmap = xgwa.colormap; - screen = xgwa.screen; } else /* it's a pixmap */ { @@ -564,7 +563,7 @@ load_random_image_x11 (Screen *screen, Window window, Drawable drawable, #elif defined (HAVE_COCOA) /* OSX or iOS */ -# ifndef USE_IPHONE /* HAVE_COCOA && !USE_IPHONE -- desktop OSX */ +# ifndef HAVE_IPHONE /* HAVE_COCOA && !HAVE_IPHONE -- desktop OSX */ # define BACKSLASH(c) \ (! ((c >= 'a' && c <= 'z') || \ @@ -699,7 +698,7 @@ xscreensaver_getimage_file_cb (XtPointer closure, int *source, XtInputId *id) } -# else /* HAVE_COCOA && USE_IPHONE -- iOS */ +# else /* HAVE_COCOA && HAVE_IPHONE -- iOS */ /* Callback for ios_load_random_image(), called after we have loaded an image from the iOS device's Photo Library. See grabclient-ios.m. @@ -753,7 +752,7 @@ ios_load_random_image_cb (void *uiimage, const char *filename, free (clo2); } -# endif /* HAVE_COCOA && USE_IPHONE */ +# endif /* HAVE_COCOA && HAVE_IPHONE */ static void @@ -774,7 +773,7 @@ osx_load_image_file_async (Screen *screen, Window xwindow, Drawable drawable, clo2->callback = callback; clo2->closure = closure; -# ifndef USE_IPHONE /* Desktop OSX */ +# ifndef HAVE_IPHONE /* Desktop OSX */ clo2->directory = strdup (dir); clo2->pipe = open_image_name_pipe (dir); clo2->pipe_id = XtAppAddInput (XtDisplayToApplicationContext ( @@ -782,14 +781,14 @@ osx_load_image_file_async (Screen *screen, Window xwindow, Drawable drawable, fileno (clo2->pipe), (XtPointer) (XtInputReadMask | XtInputExceptMask), xscreensaver_getimage_file_cb, (XtPointer) clo2); -# else /* USE_IPHONE */ +# else /* HAVE_IPHONE */ { XWindowAttributes xgwa; XGetWindowAttributes (DisplayOfScreen (screen), xwindow, &xgwa); ios_load_random_image (ios_load_random_image_cb, clo2, xgwa.width, xgwa.height); } -# endif /* USE_IPHONE */ +# endif /* HAVE_IPHONE */ } @@ -828,13 +827,13 @@ load_random_image_cocoa (Screen *screen, Window window, Drawable drawable, geom.width = xgwa.width; geom.height = xgwa.height; -# ifndef USE_IPHONE +# ifndef HAVE_IPHONE if (filep) dir = get_string_resource (dpy, "imageDirectory", "ImageDirectory"); if (!dir || !*dir) filep = False; -# endif /* ! USE_IPHONE */ +# endif /* ! HAVE_IPHONE */ if (deskp && filep) { deskp = !(random() & 5); /* if both, desktop 1/5th of the time */ diff --git a/utils/grabscreen.c b/utils/grabscreen.c index 77fe3c9..e2742bf 100644 --- a/utils/grabscreen.c +++ b/utils/grabscreen.c @@ -25,10 +25,8 @@ On MacOS systems running X11, which nobody does any more: - "driver/xscreensaver-getimage" runs the Perl script - "driver/xscreensaver-getimage-desktop", which in turn runs the MacOS - program "/usr/sbin/screencapture" to get the Mac desktop image as a - PNG file. + "driver/xscreensaver-getimage" runs "/usr/sbin/screencapture" to + get the Mac desktop image as a PNG. On MacOS systems running the native Cocoa build, or on iOS or Android systems: @@ -48,14 +46,6 @@ #include #include -#ifdef HAVE_XMU -# ifndef VMS -# include -# else /* VMS */ -# include -# endif /* VMS */ -#endif - #include "usleep.h" #include "colors.h" #include "grabscreen.h" @@ -264,10 +254,7 @@ install_screen_colormaps (Screen *screen) { XWindowAttributes xgwa; Window client; -#ifdef HAVE_XMU - /* #### need to put XmuClientWindow() in xmu.c, sigh... */ - if (! (client = XmuClientWindow (dpy, kids[i]))) -#endif + /* if (! (client = XmuClientWindow (dpy, kids[i]))) */ client = kids[i]; xgwa.colormap = 0; XGetWindowAttributes (dpy, client, &xgwa); diff --git a/utils/grabscreen.h b/utils/grabscreen.h index 40054c8..a17e671 100644 --- a/utils/grabscreen.h +++ b/utils/grabscreen.h @@ -92,14 +92,14 @@ extern Bool osx_load_image_file (Screen *, Window, Drawable, const char *filename, XRectangle *geom_ret); #endif /* HAVE_JWXYZ */ -#ifdef USE_IPHONE +#ifdef HAVE_IPHONE extern void ios_load_random_image (void (*callback) (void *uiimage, const char *filename, int w, int h, void *closure), void *closure, int width, int height); -#endif /* USE_IPHONE */ +#endif /* HAVE_IPHONE */ #ifdef HAVE_ANDROID char *jwxyz_draw_random_image (Display *dpy, /* utils/grabclient.c */ diff --git a/utils/images/logo-180.xpm b/utils/images/logo-180.xpm index b093c8f..c61837c 100644 --- a/utils/images/logo-180.xpm +++ b/utils/images/logo-180.xpm @@ -1,13 +1,14 @@ /* XPM */ /* XScreenSaver Logo, designed by Angela Goodman - Copyright (c) 2001, 2002 by Jamie Zawinski + Copyright © 2001-2021 Jamie Zawinski Unauthorized use or reproduction prohibited. https://www.jwz.org/xscreensaver/ */ static const char * const logo_180_xpm[] = { +/* columns rows colors chars-per-pixel */ "180 180 16 1", " c None m None", ") c #FF0000 m #FFFFFF", @@ -25,6 +26,7 @@ static const char * const logo_180_xpm[] = { "% c #37332D m #000000", "& c #2D0C05 m #000000", "* c #A46E00 m #000000", +/* pixels */ " ", " . ", " .. ", diff --git a/utils/images/logo-360.gif b/utils/images/logo-360.gif new file mode 100644 index 0000000..cb53a2c Binary files /dev/null and b/utils/images/logo-360.gif differ diff --git a/utils/images/logo-360.xpm b/utils/images/logo-360.xpm new file mode 100644 index 0000000..b80da2a --- /dev/null +++ b/utils/images/logo-360.xpm @@ -0,0 +1,390 @@ +/* XPM */ + +/* XScreenSaver Logo, designed by Angela Goodman + Copyright © 2001-2021 Jamie Zawinski + Unauthorized use or reproduction prohibited. + + https://www.jwz.org/xscreensaver/ + */ + +static const char * const logo_360_xpm[] = { +/* columns rows colors chars-per-pixel */ +"360 360 16 1", +" c None m None", +"# c #000000 m #000000", +". c #929291 m #000000", +"X c #A30204 m #000000", +"o c #604D28 m #000000", +"O c #600204 m #000000", +"+ c #37332D m #000000", +"@ c #2D0C05 m #000000", +"_ c #A46E04 m #000000", +"$ c #DBDBDA m #000000", +"% c #ED9E04 m #FFFFFF", +"& c #797977 m #000000", +"* c #B0B0AE m #000000", +"= c #FEFEFC m #FFFFFF", +"- c #4F3504 m #000000", +"; c #FD0204 m #FFFFFF", +/* pixels */ +" ", +" ## ", +" #### ", +" ###### ", +" ###### ", +" ###### ", +" ###### ", +" ####### ", +" ####### ", +" ## ####### ", +" ### ######## ", +" #### ####### ", +" ############ ", +" ###+######### ", +" +O++######## ", +" ++X#+######## ", +" @XX@######### ", +" @+;O@######## ", +" ##;;O@######## ", +" @X;;+@######## ", +" @+;;;+@####### ", +" #@X;;X#+#++#### ", +" @O;;;X@#o###### ", +" ++;;;;O#oo@#### ", +" @X;;;;+@_+@#### ", +" @O;;;;;#+o+##### ", +" ++;;;;;X#+o@##### ", +" @X;;;;;O#o+@#### ", +" @O;;;;;;+@o++#### ", +" ++;;;;;;;++o@##### ", +" #+X;;;;;;X@++@#### ", +" @X;;;;;;;X#++@#### ", +" @+;;;;;;;;++o#+#### ", +" ++;;;;;;;;;+++@##### ", +" @X;;;;;;;;X@++@#### ", +" @O;;;;;;;;;O@o++#### ", +" ++;;;;;;;;;;++o@##### ", +" ##X;;;;;;;;;X@++@#### ", +" @X;;;;;;;;;;O@o+@#### ", +" @O;;;;;;;;;;;++o@##### ", +" @+;;;;;;;;;;;X@oo@#### ", +" ++;;;;;;;;;;;;O@_+@#### ", +" +X;;;;;;;;;;;;++_#+#### ", +" @X;;;;;;;;;;;;X#oo@##### ### ", +" @O;;;;;;;;;;;;;+@_+@#### ##### ", +" @+;;;;;;;;;;;;;X@o%++#### ## ##### ", +" ++;;;;;;;;;;;;;;O#__@##### ### ###### ", +" +X;;;;;;;;;;;;;;++%+@#### ### ####### ", +" @X;;;;;;;;;;;;;;O#_%@+#### #### ###### ", +" @X;;;;;;;;;;;;;;;++%o@#### ### ####### ", +" @O;;;;;;;;;;;;;;;X#_%+@#### +++# ####### ", +" @+;;;;;;;;;;;;;;;;++%_@##### #+O# ####### ", +" @+;;;;;;;;;;;;;;;;O#_%o@#### @O++ ####### ", +" ++;;;;;;;;;;;;;;;;;@o%%+@#### @+X@ ######## ", +" ++;;;;;;;;;;;;;;;;;O@%%_@#### #+X+@ ####### ", +" #X;;;;;;;;;;;;;;;;X@o%%+@#### @OX@# ######## ", +" @X;;;;;;;;;;;;;;;;;++%%_@#### ++;O@ ######## ", +" @X;;;;;;;;;;;;;;;;;O#_%%+@#### #@X;++ ######## ", +" @X;;;;;;;;;;;;;;;;;X#_%%_@#### @+;X+ ######## ", +" @X;;;;;;;;;;;;;;;;;;+o%%%+@### ##X;O@ ######### ", +" @X;;;;;;;;;;;;;;;;;;++%%%o@#### @O;;+@######### ", +" @X;;;;;;;;;;;;;;;;;;O+%%%_@#### ++;;;++######### ", +" @X;;;;;;;;;;;;;;;;;;X#_%%%++### @O;;X+########## ", +" @X;;;;;;;;;;;;;;;;;;X#_%%%+@### ++;;;X@########## ", +" @X;;;;;;;;;;;;;;;;;;X#_%%_@+### #@X;;;O@######### ", +" @X;;;;;;;;;;;;;;;;;;O@%%%+@#### @+;;;;+@###+##### ", +" @X;;;;;;;;;;;;;;;;;;++%%_@#### #@X;;;;++##@+++### ", +" @X;;;;;;;;;;;;;;;;;X@o%%+@#### @+;;;;;++#++_+#### ", +" #;;;;;;;;;;;;;;;;;;+@_%_@#### #@X;;;;;###@o_@#### ", +" ++;;;;;;;;;;;;;;;;;X@o%%o@#### @+;;;;;X@#++%o@### ", +" ++;;;;;;;;;;;;;;;;;+@%%_@+### #@X;;;;;X@#@o%o@### ", +" @+;;;;;;;;;;;;;;;;X#o%%o@#### @+;;;;;;X@+#%%o@### ", +" @O;;;;;;;;;;;;;;;;++%%%#+### #@X;;;;;;X@@o%%+@### ", +" @X;;;;;;;;;;;;;;;X#o%%o@#### @+;;;;;;;X@+_%%+@### ", +" +X;;;;;;;;;;;;;;;+#%%%+@### @X;;;;;;;X#o%%%++### ", +" ++;;;;;;;;;;;;;;;X#_%%_@#### ++;;;;;;;;X@_%%%++### ", +" @+;;;;;;;;;;;;;;;++%%%+@### @X;;;;;;;;X@%%%%#+### ", +" @O;;;;;;;;;;;;;;X#_%%_@#### ++;;;;;;;;;X@%%%%++### ", +" #####################################################################################################+X;;;;;;;;;;;;;;++%%%+@#################@O;;;;;;;;;X@%%%_@+########################################################################################################################## ", +" #######################################################################################################++;;;;;;;;;;;;;;X#_%%_@#################++;;;;;;;;;;X@_%%_@############################################################################################################################## ", +" ########################################################################################################@O;;;;;;;;;;;;;;++%%%o@#################@O;;;;;;;;;;X#_%%_@############################################################################################################################### ", +" #########################################################################################################@X;;;;;;;;;;;;;X#_%%_@###################X;;;;;;;;;;X#_%%_@################################################################################################################################ ", +" #########################################################################################################@+;;;;;;;;;;;;;;++%%%o@#################@+;;;;;;;;;;;X#_%%_@+################################################################################################################################ ", +" ##########################################################################################################@X;;;;;;;;;;;;;X#_%%%+@#################@X;;;;;;;;;;;X#_%%_++################################################################################################################################# ", +" #########################################################################################################++;;;;;;;;;;;;;;++%%%_@#################++;;;;;;;;;;;;X@_%%%++################################################################################################################################## ", +" ##########+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#@X;;;;;;;;;;;;;X#o%%%+@##+@@@@@@@@@@@@@+@O;;;;;;;;;;;;;+o%%%#+##+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@########## ", +" ########+@@++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++@+;;;;;;;;;;;;;;+#%%%_@###++++++++++++++++X;;;;;;;;;;;;;+o%%%++##+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#@@######## ", +" ########@@&$=============================================================================================&#X;;;;;;;;;;;;;X#o%%%o@##@&============$++;;;;;;;;;;;;;;++%%%++#@+$===================================================================================================================$&#@######## ", +" #######+@*==============================================================================================*#O;;;;;;;;;;;;;;++%%%%#+##@*============*#X;;;;;;;;;;;;;;++%%%+@#@+======================================================================================================================*#@####### ", +" #######@+==============================================================================================$++;;;;;;;;;;;;;;X#o%%%o@##@+=============&@;;;;;;;;;;;;;;;O+%%%+@#+@$======================================================================================================================&@####### ", +" #######@o==============================================================================================o@X;;;;;;;;;;;;;;O@%%%%+@##@*============$+O;;;;;;;;;;;;;;;X#_%%o@##@*======================================================================================================================&@####### ", +" #######@o=============================================================================================&#X;;;;;;;;;;;;;;;@o%%%_@##@+=============*#X;;;;;;;;;;;;;;;X#_%%o@##@*======================================================================================================================&@####### ", +" #######@o============================================================================================&#O;;;;;;;;;;;;;;;O@_%%%+@##@&=============o+;;;;;;;;;;;;;;;;X@_%%o@##@*======================================================================================================================&@####### ", +" #######@o===========================================================================================&#O;;;;;;;;;;;;;;;;++%%%%++#+@$============$@O;;;;;;;;;;;;;;;;;@o%%_@##@&======================================================================================================================&@####### ", +" #######@o==========================================================================================*#+;;;;;;;;;;;;;;;;X#_%%%_@##@+=============*#X;;;;;;;;;;;;;;;;;++%%_@##@o======================================================================================================================&@####### ", +" #######@o=========================================================================================$@+;;;;;;;;;;;;;;;;;++%%%%+@##@*=============&@;;;;;;;;;;;;;;;;;;O+%%%#+#@+======================================================================================================================&@####### ", +" #######@o========================================================================================$+@X;;;;;;;;;;;;;;;;X#o%%%_@##@#$============$+O;;;;;;;;;;;;;;;;;;O@%%%++#@+======================================================================================================================&@####### ", +" #######@o========================================================================================o#X;;;;;;;;;;;;;;;;;O@%%%%o@##@o=============*#X;;;;;;;;;;;;;;;;;;X#_%%+@#@#$=====================================================================================================================&@####### ", +" #######@o=======================================================================================&#O;;;;;;;;;;;;;;;;;;@o%%%%+@##@*=============&@;;;;;;;;;;;;;;;;;;;;@o%%o@##@*=====================================================================================================================&@####### ", +" #######@o======================================================================================*#+;;;;;;;;;;;;;;;;;;X#_%%%_@##@+==============+O;;;;;;;;;;;;;;;;;;;;++%%o@##@&=====================================================================================================================&@####### ", +" #######@o=====================================================================================$++;;;;;;;;;;;;;;;;;;;++%%%%o@##@&=============*#X;;;;;;;;;;;;;;;;;;;;O+%%_@##@&=====================================================================================================================&@####### ", +" #######@o=====================================================================================+@X;;;;;;;;;;;;;;;;;;;@o%%%%+@#+@*=============*@X;;;;;;;;;;;;;;;;;;;;X#_%_@##@+=====================================================================================================================&@####### ", +" #######@o====================================================================================&#X;;;;;;;;;;;;;;;;;;;O#_%%%_@##@+==============++;;;;;;;;;;;;;;;;;;;;;X#o%%+@#@#$====================================================================================================================&@####### ", +" #######@o===================================================================================*#+;;;;;;;;;;;;;;;;;;;;++%%%%o@##@&=============$+O;;;;;;;;;;;;;;;;;;;;;;+o%%+@##@*====================================================================================================================&@####### ", +" #######@o==================================================================================$++;;;;;;;;;;;;;;;;;;;;X@o%%%%+@##@*=============*#X;;;;;;;;;;;;;;;;;;;;;;++%%o@##@&====================================================================================================================&@####### ", +" #######@o==================================================================================&#X;;;;;;;;;;;;;;;;;;;;X#_%%%_@##@+==============&@;;;;;;;;;;;;;;;;;;;;;;;X#_%_@##@o====================================================================================================================&@####### ", +" #######@o=================================================================================*#O;;;;;;;;;;;;;;;;;;;;;++%%%%_@##@&==============++;;;;;;;;;;;;;;;;;;;;;;;X@_%%++#@+====================================================================================================================&@####### ", +" #######@o=================================================================================+@;;;;;;;;;;;;;;;;;;;;;;+o%%%%o@##@*=============$+O;;;;;;;;;;;;;;;;;;;;;;;;++%%+@#+@*===================================================================================================================&@####### ", +" #######@o==============================================================*+&=====$=========*#X;;;;;;;;;;;;;;;;;;;;;X#_%%%%+@#@#$=============*#X;;;;;;;;;;;;;;;;;;;;;;;;O+%%o@##@*===================================================================================================================&@####### ", +" #######@o=============================================================*@@@$==$+@+*======$++;;;;;;;;;;;;;;;;;;;;;;O@%%%%_@##@+==============*@X;;;;;;;;;;;;;;;;;;;;;;;;X#_%_@##@o===================================================================================================================&@####### ", +" #######@o============================================================$++O@$==+@+@+======&#X;;;;;;;;;;;;;;;;;;;;;;++%%%%_@##@&==============&@;;;;;;;;;;;;;;;;;;;;;;;;;;@o%%#+#@+$==================================================================================================================&@####### ", +" #######@o============================================================&#XX@$=*@##+@$====$#O;;;;;;;;;;;;;;;;;;;;;;;@o%%%%o@##@*==============++;;;;;;;;;;;;;;;;;;;;;;;;;;++%%+@##@*==================================================================================================================&@####### ", +" #######@o=======================$*&&ooooooooooooooooooooooooooooooooo@O;O@oo+@###+ooooo+@X;;;;;;;;;;;;;;;;;;;;;;X@_%%%%+@##@+oooooooooooooo@O;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%o@##@+ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo&&*$=======================&@####### ", +" #######@o=====================$&+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#;;O@@@@#####@@@@@#O;;;;;;;;;;;;;;;;;;;;;;;X#_%%%%++###@@@@@@@@@@@@@@@@X;;;;;;;;;;;;;;;;;;;;;;;;;;X@o%_@###@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+&$=====================&@####### ", +" #######@o====================*+@@###################################@O;;++############+#;;;;;;;;;;;;;;;;;;;;;;;;O@_%%%_@###################+X;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%+@#############################################################################################@@+&====================&@####### ", +" #######@o===================&@@####################################++;;;##############@O;;;;;;;;;;;;;;;;;;;;;;;;O+%%%%_@####################;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%o@###############################################################################################@@&===================&@####### ", +" #######@o==================&@@#####################################@X;;X@##############;;;;;;;;;;;;;;;;;;;;;;;;;++%%%%_@##################++;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%_@#################################################################################################@&==================&@####### ", +" #######@o=================*@@#####################################++;;;O@############@O;;;;;;;;;;;;;;;;;;;;;;;;;++%%%%o@##################@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%+@################################################################################################+@*=================&@####### ", +" #######@o=================+@######################################@O;;;+@############@X;;;;;;;;;;;;;;;;;;;;;;;;;++%%%%o@##################@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@_%o@#################################################################################################@#$================&@####### ", +" #######@o================*@######################################++;;;;++###########@+;;;;;;;;;;;;;;;;;;;;;;;;;;++%%%%o@##################@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@o%_@##################################################################################################@&================&@####### ", +" #######@o================+@######################################@O;;;X+############@X;;;;;;;;;;;;;;;;;;;;;;;;;;++%%%%+@##################@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%+@#################################################################################################@+================&@####### ", +" #######@o===============$@+######################################+X;;;X@#############;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%%%+@##################@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%o@##################################################################################################@*===============&@####### ", +" #######@o===============*@######################################@+;;;;+@###########@+;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%%%+@##################@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@o%_@##################################################################################################@&===============&@####### ", +" #######@o===============&@######################################@X;;;;++###########@X;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%%%+@##################@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%+@#################################################################################################@o===============&@####### ", +" #######@o===============&@#####################################@+;;;;X@###########++;;;;;;;;;;;;;;;;;;;;;;;;;;;;O+%%%%o@##################+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%_@#################################################################################################@+===============&@####### ", +" #######@o===============&@#####################################@X;;;;O@###########@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@_%%%o@###################X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%++################################################################################################@+===============&@####### ", +" #######@o===============&@####################################++;;;;;++###########@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%%%o@#################++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%o@################################################################################################@+===============&@####### ", +" #######@o===============&@####################################@O;;;;X#############+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@o%%%o@#################++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@o%%++###############################################################################################@+===============&@####### ", +" #######@o===============&@####################################+X;;;;X@###########++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%%_@#################++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@%%+@###############################################################################################@+===============&@####### ", +" #######@o===============&@###################################@+;;;;;O@###########@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@%%%_@#################++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%_@###############################################################################################@+===============&@####### ", +" #######@o===============&@###################################@X;;;;;+@###########@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%%%+@################++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+#%%+@##############################################################################################@+===============&@####### ", +" #######@o===============&@####################################;;;;;;+############@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%%+@#################+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%_@##############################################################################################@+===============&@####### ", +" #######@o===============&@##################################@O;;;;;X@#############;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%%_@##################X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%+@#############################################################################################@+===============&@####### ", +" #######@o===============&@##################################@X;;;;;O@###########++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@o%%%#+################@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%_@#############################################################################################@+===============&@####### ", +" #######@o===============&@#################################++;;;;;;O@###########++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@%%%+@################@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%+@############################################################################################@+===============&@####### ", +" #######@o===============&@#################################@O;;;;;;++###########@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@o%%_@################@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%_@############################################################################################@+===============&@####### ", +" #######@o===============&@#################################@X;;;;;;++###########@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%%++###############@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%+@###########################################################################################@+===============&@####### ", +" #######@o===============&@################################++;;;;;;X###+#########@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@+%%o@###############@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%_@###########################################################################################@+===============&@####### ", +" #######@o===============&@################################@O;;;;;;X@#+++########@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%%#+##############@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%+@##########################################################################################@+===============&@####### ", +" #######@o===============&@#################################X;;;;;;X@##o+########@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%o@##############@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%_@+#########################################################################################@+===============&@####### ", +" #######@o===============&@###############################@+;;;;;;;X@@+_@########@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%%#+#############++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+#%%o@#########################################################################################@+===============&@####### ", +" #######@o===============&@###############################@X;;;;;;;O@@__@########@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%o@##############+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%%++########################################################################################@+===============&@####### ", +" #######@o===============&@###############################+X;;;;;;;O@@%_@########@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%+@#############@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@_%_@########################################################################################@+===============&@####### ", +" #######@o===============&@##############################@+;;;;;;;;+@+%_@########@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#+%_@#############@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@+%%+@#######################################################################################@+===============&@####### ", +" #######@o===============&@##############################@O;;;;;;;;+#o%_@########@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%o@############++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%_@+######################################################################################@+===============&@####### ", +" #######@o===============&@##############################+X;;;;;;;;+@_%_@###+@@@@@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+#_%+@#####@@@@@@@+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%o@#####+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@######################################@+===============&@####### ", +" #######@o===============&@#############################@+;;;;;;;;;++%%%++#++++++@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%_@####++++++++@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%%+@###++++++++++++++++++++++++++++++++++++++++++++@@####################################@+===============&@####### ", +" #######@o===============&@#############################@O;;;;;;;;;++%%%#+#@+====++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@+%o@###@&=====$++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%_@+##@+$========================================$*o@@##################################@+===============&@####### ", +" #######@o===============&@#############################+X;;;;;;;;;++%%%++#@+====&+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%+@##+@*=====&@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;#+%%_@###@+===========================================*@@#################################@+===============&@####### ", +" #######@o===============&@############################++;;;;;;;;;;++%%%++#@+====&@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%+@##@@*====*#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%%o@###@o===========================================*@+################################@+===============&@####### ", +" #######@o===============&@############################@O;;;;;;;;;;++%%%+@#@#$===*@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+#__@+##@+$====++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%%+@###@&===========================================&@################################@+===============&@####### ", +" #######@o===============&@############################@X;;;;;;;;;;++%%%o@##@*===$@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@__@###@+$===*#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@_%%+@##+@&==========================================$+@###############################@+===============&@####### ", +" #######@o===============&@###########################++;;;;;;;;;;;++%%%o@##@*====+O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X++%o@###@o===$++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@+%%_@+##+@&==========================================&@###############################@+===============&@####### ", +" #######@o===============&@###########################@+;;;;;;;;;;;++%%%o@##@*====o+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@+%+@###@&===&@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#+%%_@+##+@&=========================================*@###############################@+===============&@####### ", +" #######@o===============&@###########################@X;;;;;;;;;;;O+%%%_@##@&====&@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%+@###@*==$@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#o%%o@###+@&========================================*@###############################@+===============&@####### ", +" #######@o===============&@###########################@X;;;;;;;;;;;X@%%%_@##@+====*#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#o_@+##@@*==&@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#o%%o@###+@*=======================================*@###############################@+===============&@####### ", +" #######@o===============&@##########################++;;;;;;;;;;;;X#_%%%++#@+=====++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+#__@+##@@*=$@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+#_%%o@###@+$======================================*@###############################@+===============&@####### ", +" #######@o===============&@##########################@O;;;;;;;;;;;;X#_%%%+@#@+$====&@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+#_o@###@+$=&#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%%o@###@&======================================*@###############################@+===============&@####### ", +" #######@o===============&@##########################@X;;;;;;;;;;;;X@_%%%o@##@*====*#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@_o@###@+$=++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@+%%%o@##+@*=====================================*@###############################@+===============&@####### ", +" #######@o===============&@###########################X;;;;;;;;;;;;;@o%%%o@##@&=====++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;#+_+@###@o=*#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%%%+@##@o=====================================*@###############################@+===============&@####### ", +" #######@o===============&@#########################++;;;;;;;;;;;;;;+o%%%_@##@&=====&@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@+_+@###@&=+@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@_%%_@###@*====================================*@###############################@+===============&@####### ", +" #######@o===============&@#########################@O;;;;;;;;;;;;;;++%%%%++#@+=====$#O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%+@###@**#O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@+%%%+@##@o====================================*@###############################@+===============&@####### ", +" #######@o===============&@#########################@X;;;;;;;;;;;;;;O@%%%%+@#+@$=====++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#o_+@##@@*&@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%%_@##+@$===================================*@###############################@+===============&@####### ", +" #######@o===============&@##########################;;;;;;;;;;;;;;;X#_%%%o@##@&=====*#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#__@+##@+&+O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%%+@##@&===================================*@###############################@+===============&@####### ", +" #######@o===============&@########################++;;;;;;;;;;;;;;;;@o%%%_@##@o======++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@%_@###@++@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%%_@##@+$==================================*@###############################@+===============&@####### ", +" #######@o===============&@########################@+;;;;;;;;;;;;;;;;++%%%%#+#@+$=====&@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%o@###@+@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@_%%+@##@*==================================*@###############################@+===============&@####### ", +" #######@o===============&@########################@O;;;;;;;;;;;;;;;;O@%%%%+@##@*=====*#O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%+@###+#+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@o%%_@##@+==================================*@###############################@+===============&@####### ", +" #######@o===============&@########################@X;;;;;;;;;;;;;;;;X#_%%%o@##@&======++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%#@####@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%%++##@*=================================*@###############################@+===============&@####### ", +" #######@o===============&@########################+;;;;;;;;;;;;;;;;;X@o%%%_@##@+======*#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%_@+####+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@+%%o@##@&=================================*@###############################@+===============&@####### ", +" #######@o===============&@#######################++;;;;;;;;;;;;;;;;;;++%%%%+@##@*======++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%o@####@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%_@##@+$================================*@###############################@+===============&@####### ", +" #######@o===============&@#######################@O;;;;;;;;;;;;;;;;;;O@%%%%o@##@&======&#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%+@####@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@o%%+@##@*================================*@###############################@+===============&@####### ", +" #######@o===============&@#######################@X;;;;;;;;;;;;;;;;;;X#_%%%_@##@+======$++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%#+###@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%o@##@&================================*@###############################@+===============&@####### ", +" #######@o===============&@#######################@X;;;;;;;;;;;;;;;;;;;++%%%%+@##@*======&@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%_@####@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@o%_@##@+================================*@###############################@+===============&@####### ", +" #######@o===============&@######################++;;;;;;;;;;;;;;;;;;;;O@_%%%o@##@&======$@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%+@###@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@%%+@##@*===============================*@###############################@+===============&@####### ", +" #######@o===============&@######################++;;;;;;;;;;;;;;;;;;;;X#o%%%_@##@+$======&@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%#+###@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%o@##@&===============================*@###############################@+===============&@####### ", +" #######@o===============&@######################@+;;;;;;;;;;;;;;;;;;;;;++%%%%+@##@&======*#O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%o@###@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%_@##@+===============================*@###############################@+===============&@####### ", +" #######@o===============&@######################@O;;;;;;;;;;;;;;;;;;;;;X#_%%%_@##@+=======+@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%+@###@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%+@##@*==============================*@###############################@+===============&@####### ", +" #######@o===============&@######################@O;;;;;;;;;;;;;;;;;;;;;;+o%%%%++##@*======*#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%_@###@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%o@##@&==============================*@###############################@+===============&@####### ", +" #######@o===============&@######################@X;;;;;;;;;;;;;;;;;;;;;;O@%%%%o@##@&=======++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%+@###@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#%_@##@o==============================*@###############################@+===============&@####### ", +" #######@o===============&@#######################X;;;;;;;;;;;;;;;;;;;;;;X@o%%%_@##+@$======*#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%_@###@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@o%++#@#$=============================*@###############################@+===============&@####### ", +" #######@o===============&@#####################++;;;;;;;;;;;;;;;;;;;;;;;;++%%%%+@##@&======$++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%+@###+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%+@##@*=============================*@###############################@+===============&@####### ", +" #######@o===============&@#####################++;;;;;;;;;;;;;;;;;;;;;;;;X#_%%%_@##@@$======&#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%_@###@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#__@##@&=============================*@###############################@+===============&@####### ", +" #######@o===============&@#####################@+;;;;;;;;;;;;;;;;;;;;;;;;;++%%%%+@##@&======$++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@o%%+@##++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+o_@##@+=============================*@###############################@+===============&@####### ", +" #######@o===============&@#####################@O;;;;;;;;;;;;;;;;;;;;;;;;;X#_%%%_@##@+$======&#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@%%_@###@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%+@#+@$============================*@###############################@+===============&@####### ", +" #######@o===============&@#####################@O;;;;;;;;;;;;;;;;;;;;;;;;;;++%%%%+@##@&======$@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%%+@##++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_o@##@*============================*@###############################@+===============&@####### ", +" #######@o===============&@#####################@O;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%%%_@##@+$======&@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%o@###@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@o_@##@&============================*@###############################@+===============&@####### ", +" #######@o===============&@#####################@X;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%%%+@##@&======$@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@_%_@###@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O+_++#@+============================*@###############################@+===============&@####### ", +" #######@o===============&@#####################@X;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%%%_@##@@$======&@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@o%%+@####;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_+@#+@$===========================*@###############################@+===============&@####### ", +" #######@o===============&@#####################+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@%%%%+@##@&======$#O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%_@###@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+oo@##@*===========================*@###############################@+===============&@####### ", +" #######@o===============&@######################X;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@o%%%_@##+@*======o@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%%+@##++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++o@##@&===========================*@###############################@+===============&@####### ", +" #######@o===============&@######################;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%%%+@##@o======*#O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%o@###+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@o+##@+===========================*@###############################@+===============&@####### ", +" #######@o===============&@#####################+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%%_@###@*======o@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%_@###@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@o+@#++$==========================*@###############################@+===============&@####### ", +" #######@o===============&@####################++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%%%+@##@+======*#O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@+%%+@##@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++o@##@*==========================*@###############################@+===============&@####### ", +" #######@o===============&@####################++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%%_@###@&======++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%_@##++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O+o@##@&==========================*@###############################@+===============&@####### ", +" #######@o===============&@####################++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@o%%%o@##@+$=====*#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+#_%+@##+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@o+##@o==========================*@###############################@+===============&@####### ", +" #######@o===============&@#####################+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%%%++##@&======++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+#__+##@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++++#@+==========================*@###############################@+===============&@####### ", +" #######@o===============&@######################;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%%o@##+@*=====*#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+#o+@#@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++++#+@$=========================*@###############################@+===============&@####### ", +" #######@o===============&@######################X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@o%%%+@##@+=====$++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#+#+#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@++##@*=========================*@###############################@+===============&@####### ", +" #######@o===============&@#####################+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%%_@###@&=====&#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X+#+O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X++###@&=========================*@###############################@+===============&@####### ", +" #######@o===============&@#####################@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%%o@##@#$====$++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+####@+=========================*@###############################@+===============&@####### ", +" #######@o===============&@#####################@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@+%%%#+##@o=====&@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@###@+$========================*@###############################@+===============&@####### ", +" #######@o===============&@#####################@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%%o@###@*====$@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@###+@*========================*@###############################@+===============&@####### ", +" #######@o===============&@#####################@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@_%%+@##@+=====o@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X+####@*========================*@###############################@+===============&@####### ", +" #######@o===============&@#####################@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%_@###@&====*#O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++###@&========================*@###############################@+===============&@####### ", +" #######@o===============&@#####################++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%%o@##@@$====++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@###@o========================*@###############################@+===============&@####### ", +" #######@o===============&@######################+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%%++##@o====*#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@###@+========================*@###############################@+===============&@####### ", +" #######@o===============&@######################@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@_%_@###@*====++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@###+@$=======================*@###############################@+===============&@####### ", +" #######@o===============&@######################@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@+%%+@##@+====&@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#####@*=======================*@###############################@+===============&@####### ", +" #######@o===============&@######################++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%_@+##@&===*#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++###@&=======================*@###############################@+===============&@####### ", +" #######@o===============&@#######################@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_%o@##@@*===++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@###@o=======================*@###############################@+===============&@####### ", +" #######@o===============&@#######################@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@%%+@##@+===&@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@###@+=======================*@###############################@+===============&@####### ", +" #######@o===============&@########################+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@+%_@###@*==*#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@###@+$======================*@###############################@+===============&@####### ", +" #######@o===============&@########################@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%+@##@+===++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#####@*======================*@###############################@+===============&@####### ", +" #######@o===============&@########################++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+#_%@+##@*==o@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++###@*======================*@###############################@+===============&@####### ", +" #######@o===============&@#########################@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%o@##@+==*#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@###@&======================*@###############################@+===============&@####### ", +" #######@o===============&@#########################@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%#+##@*=*#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@###@&======================*@###############################@+===============&@####### ", +" #######@o===============&@###########################;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O#_o@##@+==++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@###@+======================*@###############################@+===============&@####### ", +" #######@o===============&@##########################@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++_++##@*=o+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@###@+$=====================*@###############################@+===============&@####### ", +" #######@o===============&@##########################++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#oo@##@+=*@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X+###+@$=====================*@###############################@+===============&@####### ", +" #######@o===============&@###########################@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++_#+#+@$*#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;#####@*=====================*@###############################@+===============&@####### ", +" #######@o===============&@###########################@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_o@##@&$@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++###@&=====================*@###############################@+===============&@####### ", +" #######@o===============&@############################+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++_@##@+=+O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@###@&=====================*@###############################@+===============&@####### ", +" #######@o===============&@############################@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o+@#+#$o+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@###@+=====================*@###############################@+===============&@####### ", +" #######@o===============&@##############################;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+++@##@&o+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@###@+$====================*@###############################@+===============&@####### ", +" #######@o===============&@#############################@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@+###+#++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@###+@$====================*@###############################@+===============&@####### ", +" #######@o===============&@#############################++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@####+@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@++##@*====================*@###############################@+===============&@####### ", +" #######@o===============&@##############################@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@#######;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X+++##@&====================*@###############################@+===============&@####### ", +" #######@o===============&@##############################++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@+#####;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X++###@&====================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X++++##+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+++##@o====================*@###############################@+===============&@####### ", +" #######@o===============&@###############################++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@+++++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+o++#@+====================*@###############################@+===============&@####### ", +" #######@o===============&@################################@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@+#@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+o+@#@+$===================*@###############################@+===============&@####### ", +" #######@o===============&@################################@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@@@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+oo@##@*===================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@+@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O+O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+oo@##@*===================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*o@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+o_@##@*===================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@**#O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+o_@##@&===================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*=o@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+o%@##@+===================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*=*#O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+o%++#@+===================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*==o@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+o%+@#@+$==================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*==$@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+o%+@##@*==================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*===&#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+o%o@##@*==================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*===$++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@o%o@##@&==================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*====*#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@o%_@##@&==================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*=====+@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@_%_@+#@+==================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*=====*#O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@_%%++#@+==================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*======&@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@%%%+@#@+$=================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*======$++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%%o@#@+$=================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*=======*#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@o%%%o@##@*=================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*========+@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%%%_@##@&=================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*========$@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@%%%%_@##@&=================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*=========&#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%%%+@##@o=================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*=========$+@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%%%+@###@&=================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*==========*#+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@%%%o@###@+==================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*===========&#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;#+%%o@###@+$==================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*============+@;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#_%_@+##@+$===================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*============*@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%%+@##@@*====================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*=============&#O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o%+@###@*=====================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*==============+@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++%o@###@&======================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*==============$++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#o_@###@+=======================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*===============*#+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+#_#+##@+$=======================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*================&#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@++@##+@*========================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*=================+@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@+@###@&=========================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*=================$++;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@#+###@+==========================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@*==================*#+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@####@+$==========================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@&===================&#O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@####@@*===========================*@###############################@+===============&@####### ", +" #######@o===============&@###############################@o====================o#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++####@*============================&@###############################@+===============&@####### ", +" #######@o===============&@###############################@+$===================$+@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@####@&=============================+@###############################@+===============&@####### ", +" #######@o===============&@################################@&====================$+@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@####@+=============================*@################################@+===============&@####### ", +" #######@o===============&@################################+@*====================*@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;++###@#$============================$#@################################@+===============&@####### ", +" #######@o===============&@#################################@+*====================*@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@###@@*============================*+@#################################@+===============&@####### ", +" #######@o===============&@##################################@@&$===================*@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@###+@*===========================$&+@##################################@+===============&@####### ", +" #######@o===============&@###################################+@@+oooooooooooooooooo&+@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#+###+#ooooooooooooooooooooooooooo++@@###################################@+===============&@####### ", +" #######@o===============&@#####################################@@@@@@@@@@@@@@@@@@@@@+#@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@######@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#####################################@+===============&@####### ", +" #######@o===============&@#############################################################@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@#########################################################################@+===============&@####### ", +" #######@o===============&@##############################################################@X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@##########################################################################@+===============&@####### ", +" #######@o===============&@##############################################################+#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X@###########################################################################@+===============&@####### ", +" #######@o===============&@###############################################################++X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X#+###########################################################################@+===============&@####### ", +" #######@o===============&@################################################################@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@############################################################################@+===============&@####### ", +" #######@o===============&@#################################################################@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@#############################################################################@+===============&@####### ", +" #######@o===============&@##################################################################@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@##############################################################################@+===============&@####### ", +" #######@o===============&@###################################################################@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@###############################################################################@+===============&@####### ", +" #######@o===============&@####################################################################@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@################################################################################@+===============&@####### ", +" #######@o===============&@#####################################################################@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+@#################################################################################@+===============&@####### ", +" #######@o===============&@######################################################################@+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X+@##################################################################################@+===============&@####### ", +" #######@o===============&@#######################################################################@+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X+@###################################################################################@+===============&@####### ", +" #######@o===============&@########################################################################@#X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X++####################################################################################@+===============&@####### ", +" #######@o===============&@#########################################################################++X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@+#####################################################################################@+===============&@####### ", +" #######@o===============&@##########################################################################+@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O@#######################################################################################@+===============&@####### ", +" #######@o===============&@############################################################################@+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X+@########################################################################################@+===============&@####### ", +" #######@o===============&@#############################################################################@+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O+@#########################################################################################@+===============&@####### ", +" #######@o===============&@##############################################################################+@O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X+@###########################################################################################@+===============&@####### ", +" #######@o===============&@################################################################################@+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;O+@############################################################################################@+===============&@####### ", +" #######@o===============&@#################################################################################+@+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X+@##############################################################################################@o===============&@####### ", +" #######@o===============*@###################################################################################@#O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X+@+###############################################################################################@&===============&@####### ", +" #######@o===============$@+####################################################################################@+O;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X+@@#################################################################################################@*===============&@####### ", +" #######@o================+@#####################################################################################+@+OX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X+@@##################################################################################################@+================&@####### ", +" #######@o================*@#######################################################################################+@#+X;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;X+@+####################################################################################################@&================&@####### ", +" #######@o=================+@#########################################################################################@@+OX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;XO+#+++###################################################################################################@+$================&@####### ", +" #######@o=================*@@##########################################################################################+@#+OX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;XO+#@+_o++##################################################################################################+@*=================&@####### ", +" #######@o==================*@@############################################################################################@@+++OX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;XX++#@+o__+@@##################################################################################################+@&==================&@####### ", +" #######@o===================*@@##############################################################################################+@@@#+OOXXX;;;;;;;;;;;;;;;;;XXOO+@###+_%%%o@@###################################################################################################@@&===================&@####### ", +" #######@o====================*+@@#################################################################################################+@@@@#+++++++OO+++++++@@##++o__%%%%o#@+##################################################################################################@@+*====================&@####### ", +" #######@o=====================$&+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#####################+++@@@@@@@@@++oo___%%%%%%%o#@+#################+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+&$=====================&@####### ", +" #######@o========================**&ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo+@#################################+o_%%%%%%%_o#@+##################++ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo&&*$=======================&@####### ", +" #######@o=========================================================================================================$+@#################################+@@#+o_%_o@@+####################+@$=========================================================================================================&@####### ", +" #######@o=========================================================================================================*@#####################################+@@@+@@+#######################@&=========================================================================================================&@####### ", +" #######@o=========================================================================================================+@#########################################+##########################@+$========================================================================================================&@####### ", +" #######@o========================================================================================================*@######################################################################@&========================================================================================================&@####### ", +" #######@o========================================================================================================+@######################################################################@+$=======================================================================================================&@####### ", +" #######@o=======================================================================================================*@########################################################################@&=======================================================================================================&@####### ", +" #######@o=======================================================================================================+@########################################################################@#$======================================================================================================&@####### ", +" #######@o======================================================================================================&@##########################################################################@+======================================================================================================&@####### ", +" #######@o=====================================================================================================*@+###########################################################################@&=====================================================================================================&@####### ", +" #######@o====================================================================================================$+@############################################################################@@*====================================================================================================&@####### ", +" #######@o===================================================================================================$+@##############################################################################@+$===================================================================================================&@####### ", +" #######@o===================================================================================================o@################################################################################@+$==================================================================================================&@####### ", +" #######@o==================================================================================================&@##################################################################################@+$=================================================================================================&@####### ", +" #######@o=================================================================================================o@####################################################################################@+$================================================================================================&@####### ", +" #######@o===============================================================================================$o@######################################################################################@+$===============================================================================================&@####### ", +" #######@o==============================================================================================$+@########################################################################################@+*==============================================================================================&@####### ", +" #######@o=============================================================================================*+@##########################################################################################@@*=============================================================================================&@####### ", +" #######@o============================================================================================&@@############################################################################################@@&$===========================================================================================&@####### ", +" #######@o==========================================================================================*+@@###############################################################################################@+*==========================================================================================&@####### ", +" #######@o========================================================================================$&@@##################################################################################################@@o*========================================================================================&@####### ", +" #######@o======================================================================================$&+@+#####################################################################################################@@&$======================================================================================&@####### ", +" #######@o====================================================================================$&+@@########################################################################################################+@@&*====================================================================================&@####### ", +" #######@o=================================================================================$*o@@@############################################################################################################@@@+*$=================================================================================&@####### ", +" #######@o==============================================================================$*&+@@+################################################################################################################+@@+&*$==============================================================================&@####### ", +" #######@o============================================================================*++@@@######################################################################################################################@@@++*============================================================================&@####### ", +" #######@o===========================================================================$#@@+###########################################################################################################################@@@*===========================================================================&@####### ", +" #######@o===========================================================================&@################################################################################################################################@+===========================================================================&@####### ", +" #######@o===========================================================================o@################################################################################################################################@+===========================================================================&@####### ", +" #######@o===========================================================================&@################################################################################################################################@+===========================================================================&@####### ", +" #######@o===========================================================================$#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*===========================================================================&@####### ", +" #######@o============================================================================*o+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*============================================================================&@####### ", +" #######@o===============================================================================$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$==============================================================================&@####### ", +" #######@o==========================================================================================================================================================================================================================================================================================&@####### ", +" #######@o==========================================================================================================================================================================================================================================================================================&@####### ", +" #######@o==========================================================================================================================================================================================================================================================================================&@####### ", +" #######@o==========================================================================================================================================================================================================================================================================================&@####### ", +" #######@o==========================================================================================================================================================================================================================================================================================&@####### ", +" #######@o==========================================================================================================================================================================================================================================================================================&@####### ", +" #######@o==========================================================================================================================================================================================================================================================================================&@####### ", +" #######@o==========================================================================================================================================================================================================================================================================================&@####### ", +" #######@+==========================================================================================================================================================================================================================================================================================o@####### ", +" ########@*========================================================================================================================================================================================================================================================================================*+@####### ", +" ########@@&$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$&#@######## ", +" ########+@@++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++#@@######## ", +" ##########+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@########## ", +" ######################################################################################################################################################################################################################################################################################################### ", +" ######################################################################################################################################################################################################################################################################################################## ", +" ###################################################################################################################################################################################################################################################################################################### ", +" #################################################################################################################################################################################################################################################################################################### ", +" ################################################################################################################################################################################################################################################################################################## ", +" ############################################################################################################################################################################################################################################################################################## ", +" ########################################################################################################################################################################################################################################################################################## ", +" ", +" ", +" ", +" ", +" " +}; diff --git a/utils/images/logo-50.xpm b/utils/images/logo-50.xpm index 2661a96..b96acc7 100644 --- a/utils/images/logo-50.xpm +++ b/utils/images/logo-50.xpm @@ -1,7 +1,7 @@ /* XPM */ /* XScreenSaver Logo, designed by Angela Goodman - Copyright (c) 2001, 2002 by Jamie Zawinski + Copyright © 2001-2021 Jamie Zawinski Unauthorized use or reproduction prohibited. https://www.jwz.org/xscreensaver/ diff --git a/utils/images/logo.pdf b/utils/images/logo.pdf new file mode 100644 index 0000000..fcb4e5d --- /dev/null +++ b/utils/images/logo.pdf @@ -0,0 +1,1156 @@ +%PDF-1.5 % +1 0 obj <>/OCGs[5 0 R 28 0 R 50 0 R 72 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream + + + + + application/pdf + + + jwz 8.5x11 CMYK + + + + + Adobe Illustrator CS6 (Macintosh) + 2021-02-04T18:07:40-08:00 + 2021-02-04T19:52:55-08:00 + 2021-02-04T19:52:55-08:00 + + + + 228 + 256 + JPEG + /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAADkAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7 FXYq8K/5yh1eK78tS6DaXU1tqmnG31aa23RLqzLPA3pkEeoYZCrsOw37VCrIv+cevzK/xj5NS0vp ueu6MFt73kfilipSGf35KOLH+YE9xir1PFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY q7FXYq7FXYq7FXYq7FXYq7FXkf8Azkd5MtdY8rW+tlP9I0aQrNInwuLS6pFLv4IxRzXbiG8TkZ3W 3NtwiJmBL6T+L+D5m8n+ZNd/Lbzrb6tbAyRxfDdRDZLi0dgHU9aVpt4MBgx5BIWG3V6WeDIYT5j8 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 /wCkO3/5oxV3+BPI/wD1L2mf9Idv/wA0Yq7/AAJ5H/6l7TP+kO3/AOaMVd/gTyP/ANS9pn/SHb/8 0Yq7/Ankf/qXtM/6Q7f/AJoxVNLDTdO062Frp9rDZ2yklYLeNYkBO5IVABviqIxV2KuxV2KuxV2K vBv+cvyP8E6KK7nUqgfKCTFIeD/+UR/8GX/sQxVkP5vfk35r8neZ7nV9GtZ59CluDdWF9aBme2Yt zCSen8UZjb7L9DtvXYKseH53fmyo4/4mvfh23ZSdvGq1xWm/+V4fmz/1M1596f8ANOK07/leH5s/ 9TNefen/ADTitO/5Xh+bP/UzXn3p/wA04rTv+V4fmz/1M1596f8ANOK07/leH5s/9TNefen/ADTi tO/5Xh+bP/UzXn3p/wA04rTv+V4fmz/1M1596f8ANOK07/leH5s/9TNefen/ADTitO/5Xh+bP/Uz Xn3p/wA04rTv+V4fmz/1M1596f8ANOK07/leH5s/9TNefen/ADTitO/5Xh+bP/UzXn3p/wA04rTv +V4fmz/1M1596f8ANOK07/leH5s/9TNefen/ADTitO/5Xh+bP/UzXn3p/wA04rTv+V4fmz/1M159 6f8ANOK07/leH5s/9TNefen/ADTitO/5Xh+bP/UzXn3p/wA04rTv+V4fmz/1M1596f8ANOK07/le H5s/9TNefen/ADTitO/5Xh+bP/UzXn3p/wA04rTv+V4fmz/1M1596f8ANOK07/leH5s/9TNefen/ ADTitO/5Xh+bP/UzXn3p/wA04rTv+V4fmz/1M1596f8ANOK0hHm/M38ydSt4na/8w3UdY4AeTxxV oW32ijrtyY098VfRn/Qvkn/Kkf8AB/rR/wCIfX/S3r1Po/XuPD0+VK8PS/dcv9l7Yof/2Q== + + + + + + uuid:9E3E5C9A8C81DB118734DB58FDDE4BA7 + xmp.did:F77F11740720681183D1DC35A180BA8D + uuid:32b9882d-d735-f340-9045-3cc9a9725e9f + proof:pdf + + uuid:5043498c-ce3d-7641-8f33-296bf1a24f51 + xmp.did:0380117407206811822ACFB88DCA4B7E + uuid:9E3E5C9A8C81DB118734DB58FDDE4BA7 + default + + + + + saved + xmp.iid:0280117407206811822ADEA6FFA93920 + 2014-06-05T18:45:01-07:00 + Adobe Illustrator CS6 (Macintosh) + / + + + saved + xmp.iid:F77F11740720681183D1DC35A180BA8D + 2021-02-04T18:07:40-08:00 + Adobe Illustrator CS6 (Macintosh) + / + + + + + + Basic RGB + + + 1 + False + False + + 8.000000 + 8.000000 + Inches + + + + Black + PANTONE 485 CVC + PANTONE 152 CVC + + + + + + Default Swatch Group + 0 + + + + White + CMYK + PROCESS + 0.000000 + 0.000000 + 0.000000 + 0.000000 + + + Black + CMYK + PROCESS + 75.020996 + 67.968300 + 67.022202 + 90.164001 + + + RGB Red + CMYK + PROCESS + 0.000000 + 99.398796 + 100.000000 + 0.000000 + + + RGB Yellow + CMYK + PROCESS + 6.198197 + 0.000000 + 96.514801 + 0.000000 + + + RGB Green + CMYK + PROCESS + 62.764900 + 0.000000 + 100.000000 + 0.000000 + + + RGB Cyan + CMYK + PROCESS + 51.793701 + 0.000000 + 12.539900 + 0.000000 + + + RGB Blue + CMYK + PROCESS + 88.366501 + 76.916100 + 0.000000 + 0.000000 + + + RGB Magenta + CMYK + PROCESS + 26.990200 + 81.640297 + 0.000000 + 0.000000 + + + R=193 G=39 B=45 + CMYK + PROCESS + 16.961897 + 97.897301 + 92.724503 + 6.744498 + + + R=237 G=28 B=36 + CMYK + PROCESS + 0.445598 + 99.142403 + 97.323601 + 0.094599 + + + R=241 G=90 B=36 + CMYK + PROCESS + 0.000000 + 79.803200 + 96.874901 + 0.012201 + + + R=247 G=147 B=30 + CMYK + PROCESS + 0.036597 + 50.066399 + 98.565605 + 0.000000 + + + R=251 G=176 B=59 + CMYK + PROCESS + 0.021398 + 34.848602 + 87.130501 + 0.000000 + + + R=252 G=238 B=33 + CMYK + PROCESS + 4.513597 + 0.000000 + 93.188400 + 0.000000 + + + R=217 G=224 B=33 + CMYK + PROCESS + 18.896801 + 0.039703 + 98.232994 + 0.000000 + + + R=140 G=198 B=63 + CMYK + PROCESS + 50.109100 + 0.137299 + 99.038704 + 0.000000 + + + R=57 G=181 B=74 + CMYK + PROCESS + 74.615097 + 0.000000 + 99.703995 + 0.009203 + + + R=0 G=146 B=69 + CMYK + PROCESS + 86.385902 + 17.578400 + 100.000000 + 3.823900 + + + R=0 G=104 B=55 + CMYK + PROCESS + 90.218994 + 33.475197 + 98.764000 + 26.065498 + + + R=34 G=181 B=115 + CMYK + PROCESS + 76.195900 + 0.000000 + 74.978294 + 0.000000 + + + R=0 G=169 B=157 + CMYK + PROCESS + 78.441994 + 9.085202 + 45.755695 + 0.027502 + + + R=41 G=171 B=226 + CMYK + PROCESS + 69.720001 + 14.846998 + 0.000000 + 0.000000 + + + R=0 G=113 B=188 + CMYK + PROCESS + 87.536400 + 52.645100 + 0.000000 + 0.000000 + + + R=46 G=49 B=146 + CMYK + PROCESS + 99.130203 + 96.548401 + 3.494298 + 0.500500 + + + R=27 G=20 B=100 + CMYK + PROCESS + 100.000000 + 100.000000 + 26.251602 + 24.524302 + + + R=102 G=45 B=145 + CMYK + PROCESS + 74.520500 + 98.516800 + 0.940001 + 0.228900 + + + R=147 G=39 B=143 + CMYK + PROCESS + 49.558197 + 99.191299 + 1.370299 + 0.109899 + + + R=158 G=0 B=93 + CMYK + PROCESS + 34.891300 + 100.000000 + 37.204506 + 10.815602 + + + R=212 G=20 B=90 + CMYK + PROCESS + 11.844099 + 100.000000 + 49.249996 + 0.872803 + + + R=237 G=30 B=121 + CMYK + PROCESS + 0.000000 + 97.000099 + 20.495903 + 0.000000 + + + R=199 G=178 B=153 + CMYK + PROCESS + 22.854996 + 27.426601 + 39.942001 + 0.000000 + + + R=153 G=134 B=117 + CMYK + PROCESS + 39.844398 + 42.853401 + 52.529198 + 6.808603 + + + R=115 G=99 B=87 + CMYK + PROCESS + 50.398998 + 53.136497 + 60.897202 + 23.593498 + + + R=83 G=71 B=65 + CMYK + PROCESS + 57.598198 + 60.415001 + 63.738503 + 41.934799 + + + R=198 G=156 B=109 + CMYK + PROCESS + 22.671902 + 38.394703 + 63.668297 + 1.181000 + + + R=166 G=124 B=82 + CMYK + PROCESS + 31.595303 + 49.182903 + 73.931496 + 9.890902 + + + R=140 G=98 B=57 + CMYK + PROCESS + 36.234100 + 57.402897 + 84.057404 + 22.613901 + + + R=117 G=76 B=36 + CMYK + PROCESS + 39.169895 + 64.104698 + 93.441704 + 35.580997 + + + R=96 G=56 B=19 + CMYK + PROCESS + 41.538094 + 69.994698 + 97.024498 + 49.155403 + + + R=66 G=33 B=11 + CMYK + PROCESS + 47.794300 + 73.476799 + 83.712502 + 68.157501 + + + PANTONE 485 CVC + SPOT + 100.000000 + CMYK + 0.000000 + 100.000000 + 91.000000 + 0.000000 + + + PANTONE 152 CVC + SPOT + 100.000000 + CMYK + 0.000000 + 51.000000 + 100.000000 + 0.000000 + + + + + + Cold + 1 + + + + C=56 M=0 Y=20 K=0 + CMYK + PROCESS + 55.742699 + 0.000000 + 19.989300 + 0.000000 + + + C=51 M=43 Y=0 K=0 + CMYK + PROCESS + 51.079601 + 43.097603 + 0.000000 + 0.000000 + + + C=26 M=41 Y=0 K=0 + CMYK + PROCESS + 26.440800 + 41.147499 + 0.000000 + 0.000000 + + + + + + Grays + 1 + + + + R=0 G=0 B=0 + CMYK + PROCESS + 75.020996 + 67.968300 + 67.022202 + 90.164001 + + + R=26 G=26 B=26 + CMYK + PROCESS + 72.576500 + 66.600998 + 65.191101 + 78.161301 + + + R=51 G=51 B=51 + CMYK + PROCESS + 69.482002 + 63.180000 + 62.151497 + 58.361202 + + + R=77 G=77 B=77 + CMYK + PROCESS + 65.377304 + 57.988899 + 56.878002 + 36.661304 + + + R=102 G=102 B=102 + CMYK + PROCESS + 59.728401 + 51.235199 + 50.771301 + 20.132797 + + + R=128 G=128 B=128 + CMYK + PROCESS + 51.647198 + 43.149502 + 43.149502 + 7.547098 + + + R=153 G=153 B=153 + CMYK + PROCESS + 42.636799 + 34.787498 + 35.315502 + 0.985700 + + + R=179 G=179 B=179 + CMYK + PROCESS + 30.679798 + 24.246597 + 24.765402 + 0.000000 + + + R=204 G=204 B=204 + CMYK + PROCESS + 19.366800 + 15.005702 + 15.533697 + 0.000000 + + + R=230 G=230 B=230 + CMYK + PROCESS + 8.459598 + 6.131101 + 6.576598 + 0.000000 + + + R=242 G=242 B=242 + CMYK + PROCESS + 3.704900 + 2.508599 + 2.508599 + 0.000000 + + + + + + + + + Adobe PDF library 10.01 + + + + + + + + + + + + + + + + + + + + + + + + + endstream endobj 3 0 obj <> endobj 7 0 obj <>/Resources<>/ExtGState<>/Properties<>>>/Thumb 79 0 R/TrimBox[0.0 0.0 576.0 576.0]/Type/Page>> endobj 74 0 obj <>stream +HIn\7} +^)ik%"0@X1H+A l↠~Mc 57_=KI~><|󮤟?]>&Y;jik_y'ʕR$fr.WW˽ع̖\mrCڰS.K&U7V^%өt_#e7l@f.ryro/ G61:1 +_A RoA`Y/\KJ.`\HbM򱞀ڵyf)cʎ9v 6qGvSg?4ѳe>g֡,2|܆ެAh;5G<מ,EK E/b_nl6l#5px?y# N,ǔWy>l=,%y4u +j$c u-]89< =3NO:h:Qpc҈9`[ >Tbcw z6$((lAr`66T8G'=Y">,mUAkKf%nGHuHҽ% tKj`"4b8a=ǀ VomH6_ uJUQ=f7fWDaI ZI,À[-R̖EhԎ}D3@0Dd{Zs^_8I6E# 1Qޒ |<Ɗ5kh[&8 xe":8$'gkl A^-=zʑ82:JLPQZ"5cKC sc ='8fZRK XiЍG KG#lt9wHk&',GP\p d22:)̘}.'J$C]f7J_Iሉ +yu%nύ.q0 5A&0r%Va*<+e T_5!" +UtK/18q?& J5b~и( } QԺDnd$}S|QhUشR#!Sb^+E}!WAy?P.{qJ@JU#/AI uRyc#I~'[Ec !x endstream endobj 79 0 obj <>stream +8;Z]"4-mc,$q40+7-7`dl_t +&"$(CG"m(k@EV&h\B,oL,KM!FM&G4jiCMdPrDjH+Qp"pccm$+FW-O%]e8q'DdeR"k +#pF0sghHa)eA%;n!(]HbVn[OXoNlFrfg%%lIc?X)RjufQ*,+bBO_[]&RARQJU-FSX +167F_,[FH<'=l8,q,eLnes;IQ(QHhYf^^q +r(P"(G/@Xb@RX)OV`<'D_WFmJ^4r7P0BZ/\^4m`H:%(o-0b$@4Q-LOh%Oq(HI>9d] +gAu$X4[1MVSO43F[/;1&^`ob>Nt%!FLKRb1HFj'1Y,mY[0NbmcO=9dae&?*ALoWP> +./"`@:*&(]INXcsYS6j.FEqcn\Zk\b,8_R(S-ucj2_n3eOf\#17i]W!K-KKUM"=Z"IY\WK4%K.lZtNd +'RJ^6)42)re]"$Lo.4p?H?;U6B=W!ZaT&MWNCmGbR<@gsjRPM8Khc$4~> endstream endobj 80 0 obj [/Indexed/DeviceRGB 255 81 0 R] endobj 81 0 obj <>stream +8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 +b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` +E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn +6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( +l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> endstream endobj 72 0 obj <> endobj 82 0 obj [/View/Design] endobj 83 0 obj <>>> endobj 78 0 obj <> endobj 76 0 obj [/Separation/PANTONE#20152#20CVC/DeviceCMYK<>] endobj 77 0 obj [/Separation/PANTONE#20485#20CVC/DeviceCMYK<>] endobj 75 0 obj <> endobj 84 0 obj <> endobj 85 0 obj <>stream +%!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 16.0 %%AI8_CreatorVersion: 16.0.0 %%For: (Jamie Zawinski) () %%Title: (logo.pdf) %%CreationDate: 2/4/21 7:52 PM %%Canvassize: 16383 %%BoundingBox: 81 -649 531 -142 %%HiResBoundingBox: 81.4517 -648.5947 530.5342 -142.624 %%DocumentProcessColors: Black %AI5_FileFormat 12.0 %AI12_BuildNumber: 682 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%DocumentCustomColors: (PANTONE 152 CVC) %%+ (PANTONE 485 CVC) %%CMYKCustomColor: 0 0.51 1 0 (PANTONE 152 CVC) %%+ 0 1 0.91 0 (PANTONE 485 CVC) %%CMYKProcessColor: 1 1 1 1 ([Registration]) %AI3_Cropmarks: 18 -684 594 -108 %AI3_TemplateBox: 306.5 -396.5 306.5 -396.5 %AI3_TileBox: 34.3765 -881.1035 613.1768 -126.3032 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 0 %AI9_ColorModel: 2 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI9_OpenToView: 15.1631 -118.1631 1.96 1225 1158 18 1 1 310 161 1 0 0 1 1 0 1 1 0 1 %AI5_OpenViewLayers: 7 %%PageOrigin:-0.223633 -791.7031 %AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 86 0 obj <>stream +%%BoundingBox: 81 -649 531 -142 %%HiResBoundingBox: 81.4517 -648.5947 530.5342 -142.624 %AI7_Thumbnail: 116 128 8 %%BeginData: 19220 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FD27FF5252FD72FF27F87DFD6FFFA8FF7D2720FD70FF277D202752 %FD6FFFA8272720277DFD6EFFA8522727F827A8FD6EFF7D764B27F852FD6E %FFA84B932727F87DFD6EFF7D939A272727A8FD6DFF7D759376272721A8FD %6CFFA84BB59376272752FD6DFF526F939352F82752FD6CFF7D9A93B59352 %27277DFD6BFFA84B9393936F51F827A8FD6AFFA87693B593B551522752FD %6BFF52FD05932751F87DFD0DFFA8FFA827A8FD58FF7D6FBB93B5939A5227 %20A8FD0BFF7D52FF27277DFD58FF7D75FD059376512727FD0AFFA8F8A852 %2752FD59FFA875B593B593B56F7C522752FD09FF277D7D2727A8FD59FFA8 %76FD06934BA0F827A8FD07FF52517D27F852FD5BFF76B593BC93B593767C %7C207DFD06FF7D76765220277DFD5AFFA876FD06936F7C9F2727FD05FFA8 %526F52F827F8FD5BFFA951B593B593B5939951C22727A8FD04FF7D6F76F8 %272752FD5BFFA876FD06936F76752727FD04FFA84B934B27F82752FD5BFF %A851BB93B593BC93767B5227A8FD04FF529A93522776277DFD5BFF7D75FD %05939A51A0F852FD04FF7D519393277651277DFD5BFF7D6FB593B593B54B %A05127A8FD04FF7D6FB593529F76F8FD36FF7EA87DA87DA87DA87DA87DA8 %7DA87DA87DA87DA87DA87DA87DA87DA87DA87DA87DA87DA87DA851FD0593 %6F76752727A87DA8A87D4B9393934BC127277DA87DA87DA87DA87DA87DA8 %7DA87DA87DA87DA87DA87DA87DA87DA87DA87DA87DA87DA87DA87DA87DA8 %7DA87DA8FD05FFA87D272720272727202727272027272720272727202727 %2720272727202727272027272720272727207693B593BC93529F52272720 %272727209A93B593769F5220272727202727272027272720272727202727 %27202727272027272720272727202727272027272720272727202727527D %FFFFA827F827F827F827F827F827F827F827F827F827F827F827F827F827 %F827F827F827F827F827F827F8276FFD049376759FF827F827F827F852FD %04934BA04B27F827F827F827F827F827F827F827F827F827F827F827F827 %F827F827F827F827F827F827F827F827F827F827F827F82752FF7D2727F8 %FD25274BB593B593934BC252FD072775B593B593769F52FD2E27F8272727 %A8F827F852A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8 %FFA8FFA8FFA8FFA8FFFFFF5299FD04936F76992727A8A8FFFFA84BFD0593 %4BA051277DFFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FF %A8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFA8FFFFA82727F87D272727A8FD23 %FF527693B593BC93529F7C277DFD04FF7D7693B593BC93767B7C27A8FD2D %FF7D272752F827F8A8FD22FF7D75FD05937675C12027A8FD04FF766FFD05 %936F7C75277DFD2DFF5227F87D27F827A8FD21FF7D7693B593B593B54BC2 %762752FD05FF52B593B593B5939A76A0F87DFD2DFF7DF82753F827F8A8FD %20FF7D51FD06936F529952F8A8FD04FF7D75FD079376752727FD2DFF5227 %F87D272027A8FD1FFFA85293BB93B593BC93767BC22752FD05FF7D6FB593 %BC93B593BC51A02727AFFD2CFF7D202753F827F8A8FD1FFF526FFD079351 %C1752752FD05FF51FD0893767552F8A8FD2CFF5227F87D2727277EFD17FF %A8FD06FF7D4BB593B593B593B56F7C9F52F8A8FD04FFA87693B593B593B5 %93B56F7C76277DFD2CFF7D272752F827F8A8FD0CFFA8FFFFFFA8FFFFFFA8 %FFA8277DA8F87DFFA84BFD08937575C12627A8FD04FF7D6FFD09934B7CF8 %52FD04FFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FFFFFFA8FF %FFFFA8FD0BFF5227F87D272727A8FD07FFA87D5252275252522752285227 %5227526F5227272752279A93BC93B593BC93B551C29F2727522852275227 %9A93BC93B593BC93B593767C522052525227525252275252522752525227 %522852275228522752285227522852275252527DFD08FF7D272752F827F8 %A8FD06FF7D27F827F827F827F827F827F827F8276F75F827F827F852FD09 %93529FA0F827F827F827F827FD0A936F765127F827F827F827F827F827F8 %27F827F827F827F827F827F827F827F827F827F827F827F82752FD07FF52 %27F87D27F827A8FD05FFA8272727F8272727F8272727F8FD0427B55127F8 %2727276FB593B593B593B5939A51C175272727F82727274BB593B593B593 %B593B593B551A02727F8272727F8272727F8272727F8272727F8272727F8 %272727F8272727F8272727F827272752FD06FF7DF82753F827F8A8FD05FF %7DF827F827F827F827F827F827F827F876934BF827F8274BFD09936F769F %76F827F827F827F852FD0B93755152F827F827F827F827F827F827F827F8 %27F827F827F827F827F827F827F827F827F827F827F827A8FD05FF5227F8 %7D272027A8FD05FF52272727202727272027272720272727939A27272027 %277693BC93B593BC93B593BC52C2752727272027272751BB93B593BC93B5 %93BC93B59376762720272727202727272027272720272727202727272027 %272720272727202727272027272720A8FD05FF7D202753F827F8A8FD05FF %52F827F827F827F827F827F827F8274B934B27F827F827FD0B93769F7CF8 %27F827F827F876FD0C934B7C2727F827F827F827F827F827F827F827F827 %F827F827F827F827F827F827F827F827F827F8277DFD05FF5227F87D2727 %277EFD05FF5227F8272727F8272727F8272727F89A9352F8FD0427B593B5 %93B593B593B593B56FA09F27F8272727F82751B593B593B593B593B593B5 %939A517C2727F8272727F8272727F8272727F8272727F8272727F8272727 %F8272727F8272727F82727A8FD05FF7D272752F827F8A8FD05FF52F827F8 %27F827F827F827F827F85193932727F827F852FD0C934B9F2727F827F827 %F876FD0D93767527F827F827F827F827F827F827F827F827F827F827F827 %F827F827F827F827F827F827F8277DFD05FF5227F87D272727A8FD05FF52 %2720272727202727272027272775BB99272027272775BB93B593BC93B593 %BC93B593767C7C20272727202751B593BC93B593BC93B593BC93B56F7C76 %272027272720272727202727272027272720272727202727272027272720 %272727202727A8FD05FF7D272752F827F8A8FD05FF52F827F827F827F827 %F827F82727939376F827F827F876FD0C936F527527F827F827F852FD0E93 %4B7C2727F827F827F827F827F827F827F827F827F827F827F827F827F827 %F827F827F827F8277DFD05FF5227F87D27F827A8FD05FF52272727F82727 %27F8272727F87693B54B515127F82775B593B593B593B593B593B593B56F %7C5227F8FD04279A93B593B593B593B593B593B59399517C2727F8272727 %F8272727F8272727F8272727F8272727F8272727F8272727F8272727F8A8 %FD05FF7DF82753F827F8A8FD05FF52F827F827F827F827F827F8276F9393 %515152F827F876FD0E934B762727F827F8276FFD0E93757576F827F827F8 %27F827F827F827F827F827F827F827F827F827F827F827F827F827F8277D %FD05FF5227F87D272027A8FD05FF522727272027272720FD0427BC93B551 %A05227525275B593BC93B593BC93B593BC93B593BC51762727527D277693 %B593BC93B593BC93B593BC93B593767C5220FD04527D5252527D5252527D %FD0452272720272727202727272027272720A8FD05FF7D202753F827F8A8 %FD05FF52F827F827F827F827F827F876939393769F76F8A8A876FD109351 %52F852FFFF51FD0F936F527527F8A8FD10FF2727F827F827F827F827F827 %F8277DFD05FF5227F87D2727277EFD05FF5227F8272727F8272727F82775 %B593B575A05127A8FF51B593B593B593B593B593B593B59393939A5152F8 %52FF7D6FB593B593B593B593B593B593B593B56F7C7627F8A8FD10FF2727 %F8272727F8272727F82727A8FD05FF7D272752F827F8A8FD05FF52F827F8 %27F827F827F82727FD0493769F76F87DFF766FFD1093994B27F852A852FD %10934B7C7527F8A8FD0FFF27F827F827F827F827F827F8277DFD05FF5227 %F87D272727A8FD05FF52272027272720272727207693B593BB75A07B2752 %FF7D7693B593BC93B593BC93B593BC93B593BC939A5152277D7D7693B593 %BC93B593BC93B593BC93B593BB517C752727FD0FFF272720272727202727 %27202727A8FD05FF7D272752F827F8A8FD05FF52F827F827F827F827F827 %6FFD049376759FF852FFFF4BFD1293752727F85252FD11934B9F512752FD %0EFF52F827F827F827F827F827F8277DFD05FF5227F87D27F827A8FD05FF %52272727F8272727F827279A93B593B56F7C9F51F8FFFF7D6FB593B593B5 %93B593B593B593B593B593B593765151275275B593B593B593B593B593B5 %93B59393939A51A02027A8FD0DFFFD0427F8272727F8272727F8A8FD05FF %7DF82753F827F8A8FD05FF52F827F827F827F827F84BFD06934BC151277D %FF7D51FD1393764B27F8276FFD1093767576F87DFD0DFF27F827F827F827 %F827F827F8277DFD05FF5227F87D272027A8FD05FF522727272027272720 %2751B593BC93B593769FA02059FFFF529A93B593BC93B593BC93B593BC93 %B593BC93B593765227207693B593BC93B593BC93B593BC93B593BC93767B %2727FD0DFFFD0427202727272027272720A8FD05FF7D202753F827F8A8FD %05FF52F827F827F827F827F876FD06936F7C9F27F8FFFFA84BFD13936F76 %5127F89AFD10936F7C51277DFD0CFF27F827F827F827F827F827F8277DFD %05FF5227F87D2727277EFD05FF5227F8272727F82727276FB593B593B593 %9351C276277DFFFF5293B593B593B593B593B593B593B593B593B593B56F %7C51274BB593B593B593B593B593B593B593B593B5517CF87DFD0CFF2727 %F8272727F8272727F82727A8FD05FF7D272752F827F8A8FD05FF52F827F8 %27F827F82727FD08937675A0F827A8FF7D75FD14934B7CF8276FFD109376 %5127F8FD0CFF27F827F827F827F827F827F8277DFD05FF5227F87D272727 %A8FD05FF5227202727272027275293BC93B593BC93B56F769F5220A8FFFF %529A93B593BC93B593BC93B593BC93B593BC93B5939A767C205293B593BC %93B593BC93B593BC93B593BC9376512784FD0BFF27272027272720272727 %202727A8FD05FF7D272752F827F8A8FD05FF52F827F827F827F82727FD09 %934BA0752727FFFF7D4BFD1493767527F89AFD10937552F87DFD0BFF52F8 %27F827F827F827F827F8277DFD05FF5227F87D27F827A8FD05FF52272727 %F8272727F87693B593B593B593B5939A51C251277DFFFF5293B593B593B5 %93B593B593B593B593B593B593B56F7C52274BB593B593B593B593B593B5 %93B593B5939A512727FD0BFFFD0427F8272727F8272727F8A8FD05FF7DF8 %2753F827F8A8FD05FF52F827F827F827F8274BFD0A937675A0F827A8FF7D %75FD14934BA0F8276FFD1093522727A8FD0AFF27F827F827F827F827F827 %F8277DFD05FF5227F87D272027A8FD05FF5227272720272727207693B593 %BC93B593BC93B593529F76207DFFFF529A93B593BC93B593BC93B593BC93 %B593BC93B593767C52207693B593BC93B593BC93B593BC93B593BB752720 %A8FD0AFFFD0427202727272027272720A8FD05FF7D202753F827F8A8FD05 %FF52F827F827F827F8274BFD0B936F769F27F8A8FF7D4BFD13936F767527 %27FD10939A272752FD0AFF27F827F827F827F827F827F8277DFD05FF5227 %F87D2727277EFD05FF5227F8272727F827277693B593B593B593B593B593 %B54BA0762752FFFF5293B593B593B593B593B593B593B593B593B593B56F %52272793B593B593B593B593B593B593B593B593522752FD0AFF2727F827 %2727F8272727F82727A8FD05FF7D272752F827F8A8FD05FF52F827F827F8 %27F8274BFD0D934BA04B277DFF7D75FD14936F766FFD11934B27F8A8FD09 %FF27F827F827F827F827F827F8277DFD05FF5227F87D272727A8FD05FF52 %27202727272027277693BC93B593BC93B593BC93B5939A52A02027A8FF52 %9A93B593BC93B593BC93B593BC93B593BC93B593BC93B593BC93B593BC93 %B593BC93B593BC93B5939A27277DFD09FF27272027272720272727202727 %A8FD05FF7D272752F827F8A8FD05FF52F827F827F827F82727FD0E937675 %76F852FF7D4BFD27936F27F87DFD09FF52F827F827F827F827F827F8277D %FD05FF5227F87D27F827A8FD05FF52272727F8272727F82793B593B593B5 %93B593B593B593B593767652F8A8FF5293B593B593B593B593B593B593B5 %93B593B593B593B593B593B593B593B593B593B593B593B593B54B2752FD %09FFFD0427F8272727F8272727F8A8FD05FF7DF82753F827F8A8FD05FF52 %F827F827F827F827F876FD0F93525127F8FF7D6FFD279352F852FD09FF27 %F827F827F827F827F827F8277DFD05FF5227F87D272027A8FD05FF522727 %272027272720274BB593BC93B593BC93B593BC93B593BB937651277DA84B %BB93B593BC93B593BC93B593BC93B593BC93B593BC93B593BC93B593BC93 %B593BC93B593BC93B5752727FD09FFFD0427202727272027272720A8FD05 %FF7D202753F827F8A8FD05FF52F827F827F827F827F8276FFD0F936F5227 %277D76FD279375F8277DFD08FF27F827F827F827F827F827F8277DFD05FF %5227F87D2727277EFD05FF5227F8272727F8272727F85293B593B593B593 %B593B593B593B593B57552F87D769993B593B593B593B593B593B593B593 %B593B593B593B593B593B593B593B593B593B593B593B59327F884FD08FF %2727F8272727F8272727F82727A8FD05FF7D272752F827F8A8FD05FF52F8 %27F827F827F827F827F899FD10934B27F87D6FFD2793272752FD08FF27F8 %27F827F827F827F827F8277DFD05FF5227F87D272727A8FD05FF52272027 %272720272727202751B593BC93B593BC93B593BC93B593BC93762727279A %93BC93B593BC93B593BC93B593BC93B593BC93B593BC93B593BC93B593BC %93B593BC93B593BC93522052FD08FF27272027272720272727202727A8FD %05FF7D272752F827F8A8FD05FF52F827F827F827F827F827F8276FFD1093 %4BF8276FFD27934B2727FD08FF52F827F827F827F827F827F8277DFD05FF %5227F87D27F827A8FD05FF52272727F8272727F8272727F87693B593B593 %B593B593B593B593B593B57527F89A93B593B593B593B593B593B593B593 %B593B593B593B593B593B593B593B593B593B593B593B593762727A8FD07 %FFFD0427F8272727F8272727F8A8FD05FF7DF82753F827F8A8FD05FF52F8 %27F827F827F827F827F827F899FD10936F516FFD27934B4BF8A8FD07FF27 %F827F827F827F827F827F8277DFD05FF5227F87D272027A8FD05FF522727 %272027272720272727207D52B593BC93B593BC93B593BC93B593BC93B593 %BC93B593BC93B593BC93B593BC93B593BC93B593BC93B593BC93B593BC93 %B593BC93B593BC93B593764B277DFD07FFFD0427202727272027272720A8 %FD05FF7D202753F827F8A8FD05FF52F827F827F827F827F827F8277D7D6F %FD39934B76F87DFD07FF27F827F827F827F827F827F8277DFD05FF5227F8 %7D2727277EFD05FF5227F8272727F8272727F827277DFF5293B593B593B5 %93B593B593B593B593B593B593B593B593B593B593B593B593B593B593B5 %93B593B593B593B593B593B593B593B593B593B59376752752FD07FF2727 %F8272727F8272727F82727A8FD05FF7D272752F827F8A8FD05FF52F827F8 %27F827F827F827F8277DFF7D52FD389351A0F852FD07FF27F827F827F827 %F827F827F8277DFD05FF5227F87D272727A8FD05FF522720272727202727 %272027277DFFFF529A93BC93B593BC93B593BC93B593BC93B593BC93B593 %BC93B593BC93B593BC93B593BC93B593BC93B593BC93B593BC93B593BC93 %B593BC93769F5227FD07FF27272027272720272727202727A8FD05FF7D27 %2752F827F8A8FD05FF52F827F827F827F827F827F8277DFFFFFF4BFD3693 %7675C12727A8FD06FF52F827F827F827F827F827F8277DFD05FF5227F87D %27F827A8FD05FF52272727F8272727F8272727F87DFFFFFFA84BB593B593 %B593B593B593B593B593B593B593B593B593B593B593B593B593B593B593 %B593B593B593B593B593B593B593B593B593B551A0522727FD07FFFD0427 %F8272727F8272727F8A8FD05FF7DF82753F827F8A8FD05FF52F827F827F8 %27F827F827F82753FD04FF7D4BFD33936F76512727FD08FF27F827F827F8 %27F827F827F8277DFD05FF5227F87D272027A8FD05FF5227272720272727 %20272727207DFD05FF526FBB93B593BC93B593BC93B593BC93B593BC93B5 %93BC93B593BC93B593BC93B593BC93B593BC93B593BC93B593BC93B593BC %9376512727FD09FFFD0427202727272027272720A8FD05FF7D202753F827 %F8A8FD05FF52F827F827F827F827F827F8277DFD05FFA852FD3193992727 %F8A8FD09FF27F827F827F827F827F827F8277DFD05FF5227F87D2727277E %FD05FF5227F8272727F8272727F827277DFD06FFA85293B593B593B593B5 %93B593B593B593B593B593B593B593B593B593B593B593B593B593B593B5 %93B593B593B593B593B54B27277DFD0AFF2727F8272727F8272727F82727 %A8FD05FF7D272752F827F8A8FD05FF52F827F827F827F827F827F82752FD %07FF7D51FD2E936F27F853FD0AFFA827F827F827F827F827F827F8277DFD %05FF5227F87D272727A8FD05FF52272027272720272727202727277DFD07 %FF7D7693B593BC93B593BC93B593BC93B593BC93B593BC93B593BC93B593 %BC93B593BC93B593BC93B593BC93B593BB934B207DFD0AFFAF5227272027 %272720272727202727A8FD05FF7D272752F827F8A8FD05FF52F827F827F8 %27F827F827F827F827277D527D527D527DF876FD2B9352F827527D527D52 %7D527D527D5227F827F827F827F827F827F827F8277DFD05FF5227F87D27 %F827A8FD05FF52272727F8272727F8272727F8272727F8272727F8272727 %F87693B593B593B593B593B593B593B593B593B593B593B593B593B593B5 %93B593B593B593B593B593B59376F8272727F8272727F8272727F8272727 %F8272727F8272727F8272727F8A8FD05FF7DF82753F827F8A8FD05FF52F8 %27F827F827F827F827F827F827F827F827F827F827F827F876FD279376F8 %27F827F827F827F827F827F827F827F827F827F827F827F827F827F8277D %FD05FF5227F87D272027A8FD05FF52272727202727272027272720272727 %20272727202727272027277693BC93B593BC93B593BC93B593BC93B593BC %93B593BC93B593BC93B593BC93B593BC93B5937627272027272720272727 %202727272027272720272727202727272027272720A8FD05FF7D202753F8 %27F8A8FD05FF52F827F827F827F827F827F827F827F827F827F827F827F8 %27F827F876FD239376F827F827F827F827F827F827F827F827F827F827F8 %27F827F827F827F827F8277DFD05FF5227F87D2727277EFD05FF5227F827 %2727F8272727F8272727F8272727F8272727F8272727F827277693B593B5 %93B593B593B593B593B593B593B593B593B593B593B593B593B593B59352 %2727F8272727F8272727F8272727F8272727F8272727F8272727F8272727 %F82727A8FD05FF7D272752F827F8A8FD05FF52F827F827F827F827F827F8 %27F827F827F827F827F827F827F827F827F84B6FFD1D936F27F827F827F8 %27F827F827F827F827F827F827F827F827F827F827F827F827F827F8277D %FD05FF5227F87D272727A8FD05FF52272027272720272727202727272027 %2727202727272027272720272727202775B593BC93B593BC93B593BC93B5 %93BC93B593BC93B593BC93B5939A51272027272720272727202727272027 %272720272727202727272027272720272727202727A8FD05FF7D272752F8 %27F8A8FD05FF7DF827F827F827F827F827F827F827F827F827F827F827F8 %27F827F827F827F8272776FD1793762727F827F827F827F827F827F827F8 %27F827F827F827F827F827F827F827F827F827F827F827A8FD05FF5227F8 %7D27F827A8FD05FFA8272727F8272727F8272727F8272727F8272727F827 %2727F8272727F8272727F827274B4B9A93B593B593B593B593B593B593B5 %93B5939A51522727F8272727F8272727F8272727F8272727F8272727F827 %2727F8272727F8272727F827272752FD06FF7DF82753F827F8A8FD06FF52 %27F827F827F827F827F827F827F827F827F827F827F827F827F827F827F8 %27F827F827F84B4B756F99FD0793996F754B764B27F827F827F827F827F8 %27F827F827F827F827F827F827F827F827F827F827F827F827F827F82727 %A8FD06FF5227F87D272027A8FD07FF7D52FD2027202727272027274B2752 %27524B7676A0757C51272027272720FD212752FD08FF7D202753F827F8A8 %FD0AFFA8AFA8A8A8AFA8A8A8AFA8A8A8AFA8A8A8AFA8A8A8AFA8A8A8AFA8 %A8A8FF2727F827F827F827F827F827F82720527552F827F827F827F827F8 %7DA8A8A8AFA8A8A8AFA8A8A8AFA8A8A8AFA8A8A8AFA8A8A8AFA8A8A8AFA8 %A8A8FD09FF5227F87D2727277EFD27FFA8272727F8272727F8272727F827 %2727F8272727F8272727F8FD0427FD28FF7D272752F827F8A8FD27FF27F8 %27F827F827F827F827F827F827F827F827F827F827F827F827F82752FD27 %FF5227F87D272727A8FD26FF7D2027272720272727202727272027272720 %27272720272727202727272027A8FD26FF7D272752F827F8A8FD25FF7DF8 %27F827F827F827F827F827F827F827F827F827F827F827F827F827F827F8 %27A8FD25FF5227F87D27F827A8FD24FF7DF8272727F8272727F8272727F8 %272727F8272727F8272727F8272727F8272727F827A8FD24FF7DF82753F8 %27F8A8FD22FF7E52F827F827F827F827F827F827F827F827F827F827F827 %F827F827F827F827F827F827F82752FD23FF5227F87D272027A8FD20FFA8 %522727272027272720272727202727272027272720272727202727272027 %27272027272720FD0427A8FD21FF7D202753F827F8A8FD1DFFA87D52F827 %F827F827F827F827F827F827F827F827F827F827F827F827F827F827F827 %F827F827F827F827F827F82752A8A8FD1DFF5227F87D2727277EFD1CFFA8 %2727F8272727F8272727F8272727F8272727F8272727F8272727F8272727 %F8272727F8272727F8272727F8272727F8272752A8FD1CFF7D272752F827 %F8A8FD1CFF5227F827F827F827F827F827F827F827F827F827F827F827F8 %27F827F827F827F827F827F827F827F827F827F827F827F827F8A8FD1CFF %5227F87D272727A8FD1DFF7DA87DA87DA87DA87DA87DA87DA87DA87DA87D %A87DA87DA87DA87DA87DA87DA87DA87DA87DA87DA87DA87DA87DA87DA87D %A8FD1DFF7D272752F827F8A8FD6CFF5227F87D27F827A8FD6CFF7DF82753 %F827F8A8FD6CFF5227F87D52202752FD6BA87D2720277D7D27F827F827F8 %27F827F827F827F827F827F827F827F827F827F827F827F827F827F827F8 %27F827F827F827F827F827F827F827F827F827F827F827F827F827F827F8 %27F827F827F827F827F827F827F827F827F827F827F827F827F827F827F8 %27F827F827F827F827F827F827F827F827F8FFFF5227F8272727F8272727 %F8272727F8272727F8272727F8272727F8272727F8272727F8272727F827 %2727F8272727F8272727F8272727F8272727F8272727F8272727F8272727 %F8272727F8272727F8272727F8272727F8272727F8272727F8272727F827 %2727F8272727F8272727F82727A8FFFFFF7D52F827F827F827F827F827F8 %27F827F827F827F827F827F827F827F827F827F827F827F827F827F827F8 %27F827F827F827F827F827F827F827F827F827F827F827F827F827F827F8 %27F827F827F827F827F827F827F827F827F827F827F827F827F827F827F8 %27F827F827F82752A8FFFF %%EndData endstream endobj 87 0 obj <>stream +HWko1ZrܗqQ1u;h/ + E +4=ɥDqW,w< tBm0W1߽xk^TREZmT̕=r |xf#BkS7c4͚K26M}q]̍拃C(som\ӆĿEx]~qr.}=Lon>_b2ln0Ypu}yzv"kgq $F7F]V<=7S#=}~[D f<Wˬ&N 'õlDM0g=JtV/Q1)>QrBgDplA|>ݹz\ȿo*o#6=#C{ za^D/җ2z {z{IzvB=Dj')w9זBj8,(҈4s^A@*($)pjw`),asrQa4+0!AG>Ai 4ch?mj F i&;DA&ȶ)a T! -F +)(d"GFL$ʓ)dڪB. 4Cn#r"=9R?F Q + +@a`*E )*G +(. A,59N0ګwJxlOXc.A +FP|cUO!}(`h_4e` k;E ST3dHã>TUv715:0*@sn L +SCS]Z `=HC@ Ly7fqJp@@Ӫ#fy +4};,SƾLrӬO3pg3( $q(A!ToS$iS0vxӎ}?W 3 &6@8&ӱ0w=o݃ݝ>{Hw dx@3 ë;?|lsݶ{|;ova>..#;5ƹxwv-;!3!kM?&)'E-8ִ~pbS݊oVr^uxػqAqjbB<`{VLTL(@SD2AD#D"D#kk{NStKE`vBOW{gv$6ҺQy{VT&K:rCx4iI=hcȏ?TsE 5Mp([@YPSPC}HK;l׾є1n{i;##ڞ>M\C]n -}:^ _AQ؞8õ9}e.#8D$#XRQ!}ϭ;]ߎt/#QE/G@8 ,-s3D4x3MOǠr;aeE_y|f"췓"[l?]x$kYG;v\{;,ڋ=] roggUudgC=(WoG*5M)Y14 CWЋ螬Lt[k*Lo>XBLgvax2TOĻ#'_߽A$g3QaNas |2?^fڋT_KwhSOӗG2/q7>};t}l !K^63| _;=~׿A?mߎ-_^ܾ|KgGj|?mӱǯ߿nի?LG}o_#p?=~W o{ t:e|p}ļVS +gI |f!nږJ7o{`}ke;*~'Fɍ F劉)nY? 0p_q9&[)u}O/U]@1QpdPė ss_jw]>L[ RdK&VPSx[#+iINBM[%YqR6i9.v(gq&j@Sش QLEQpJi,uIŒYHx&QzAJ ,PC*{'L[9kֳ[&xPt&h`~0?.*@!jMOT)gc3CaA. {(4[{ҤXTs(*6qC#Pkᅔ4%&2qxhq4"'*BpjZ!ZB b/ttvm^,a\s'h7&$}u]?`CaёO6!IBwEma;PUY^HBIY| ?A + iQhgR@h`!XAcjE=DhSJaR*|jn y0H>1φe%^MM&y&K5~:rpI'x&ËmJ */mhbu`u<"C+6")c@ޒDeWbE,PMJdaq,tYhh5Iq:ܩmޱ;D`c%FMi4aw;"#Ci] .VR?pL`ZWYf5R)iJN@61z+ܡH,g &EҚ.+fI<.]!wSKZâqY‘ yM46yՆ8$3:bPU6I@ΰšUY31e>Բv9cfr4T"f|T*ZTcrY兲'U-J;֩fut8*&^wv3¢: gڛ_d9 Ԇ/;Q1Z+Jmn&R~VCH@;e3u +$-OوsVS\(e8p],vP}eaT+cE@FxJ,TEe8Q v8.G2Z{UmG%$9W+TrKg 6ҭ $+#J +vTY )ʖЁck%Ԕ"$DX*J`ъ-HE21iqUv(>J.e嬯VM[+Lt1ȷ/^+,H++M+8:rQ(^ROLa6M"R(jY g ޼FRkɹtn IIWFg_E6YJ.7= +6z t}X)UTO K?VGLRiRs|:sC|Ö1чd)y&ϽsxJUrGzXA<>9PsxD[(ǗȨM9-Ŝ'@ps۬ m-2JD-Tr9K 0HqN^,v`Ҳnhf5u6yIœiv/8G,),{x +8lbTcwfw!P^9Aaw ]+kpP 2W+>FZA#KGJX1BD=Nmycꄫ*?߆iR9Ubr*;GQs*C*ڙP=Q*f9&LIGVR8-fb<(Z]F/ +ѱAJ""6tޫ2[KG@;>{Jm?"}F)9~ %Dd5>(R<ϊ_s?:E#>uL6`ԢlN:+ =@K\SwBU[i%W~P8WC dbu*0I>bhcÌ3UoUXz=oznCVe܌v̉Dq9?;;QvQj _Yk5A` &- A+@'2=uacBGJx^Ub{\gg~7wRi^"_"h|?nNN >s/p Ǽ +P/ș5ۗo/eqÒw3{X gO-nP-%,+}ȯ) FlݺgC~MB7pz`dsfgeډvymNYbXܹ 08wasm~;p;o3]6+e=4& Ny/֨:zݏ:̟PMXFi[K?o3<3׊tʖ&S:)>{l u.*u" iU"2iE|J@J7\>ym4#PS T%m*`7ju"*ٳE8f}>s&3X`j6JsUkHԠO Z|13H_* +@T] ==g7cmߣ;(ы\ռ~ڊY@Ρz @%77m}%~Ypw`ך~Ze:}1)ce\r, Ԙ|?`4+@w<K;8ULXv|ĀU2?@&j +iǭi/fL'I#-z](щ*T&ߞle0U:IȨ^d0|jt#ذXi`X xٴe,po9ROᜰуJv#ČjG[P$BBUiШ;u#PN.wj<u'"a-p }n1Mm1oygRΕtYz9!ڡ}. ­p(5ϸ>_gǶ=yt5|%_>>o[{/?㛟?o|?Gz+gR,uWA'RrAa#Щ#Qay:9X$>ls ϶N0D 2k|[Udj ],Ehr#>#l<[WDу2DGqD Ghͥ\BJJt>Ü4aéVWXF +<"uYϑ?|'`ǗߜeFӨr[ 48ҹ~G9,\CsAsfR :YAn="eGtA4Z Ȱ+Cl1˾|*" =e5Hb v^Ġf;JPl<@l`0VY# ;vnГ:i͔&ytRܡoQnct.=pz_twzRi,[rVARx~ &tZ'6({R;,n[;68x-'hkc+q VMni9 {~UI?:aT MT=&KK1Z8xZ1E""]ѢƱ!ԢYΨoAbQ#90g('Dx͍G̵QY#1n7!?-mkakQ(=姅o+M gfA-B=44L[wwҫ_W6sm+Ui.ڮF!J"F?ky^UvsHPU!jk}JQ+5Fp f4ϧ6 sʪ+JĈH:ѬYJ:ŇVž |C""Ď"7d Ȼ`2D +@BPj=tgKF9KOx<݅r\1$䐑597pmhJlT/,cU'ѝ˩Sy}@Yµ +\)$LMRha#T?H> +p >UTR[`Xz@t' w}z@/ /4Y56 (JdT-|\lsԇ:fQԂ<ςBtWR7ϹsO2  #աP`'*yS/VGDߺZ5/V5['ȿ:ӣ0^ׁ~%T,>.]Q ͦ1v-\cIsQ"R08 +|UIhѬjf$q׺)Wc3K@hˀk0?ZW_F͡B/WwZ!y>(LyJfĭ b+[j=L` z Esװ>_/,9OW##[m!Qb:bԺ!kHd2-cKG _JQH|;ew_uW @\f'%< j 2nȅ[.2TGcΣD\:" +V@Z,<<]n0}7 YH]fSG2yZg{E}'kǙ$cg3Y4uR'9Zwldb;vT +DO `T9[׻g+8}ŅS8+Y-MANpS=Go?cf5-M`Fd0 hlvƻ"F +7]OԐxa}nXuS7(60nndtwWMՋLŽLi:0 ^}4w1Ӗ~?"(Th"l< + "P₾H@ħ{2@T`_+_WK Λ]ݚ37*U ěBWnUS y֡s/h\9&At>c.!!VIu}Dw2lB Be˩~e  o/gE$c8҆$<0Ɉ"ŏFļDp({Bhƈ0)3*iٕMNeDOUZ#HО`]O$YF2 })+0pX9 +9KZcvE0[4@;!!ï#1Ox+ՕywBpમ +9!e'R?8w9zAu:'ӤcP0[Ur t;3VhX!ɯ"JS+Dn}@[qy-Т2+d;ү *^T7LᔮVMq 6<+.[@R0^JDV OU)bdF;7DC D.48@tpxT /3mɏ~__>~o_ v_޽~/˧~|7ӿp/?bp7k;ס&S%&o~̤.zN`7M*3(":{ƻE<>*Qd0oPľ+P̢YS % mpVpÓ@wKu +eS8* +RcgmMy}Pcma lOTLTJm5| ij؞6MBYZhG<ūZS@#ikPM  3 V3ZBM$q| ԯgu:4:<*L;P|ձ B笴V@]x~KV+ o缎Hpإ,A[#2ao1z_WJ8Wg4Wod%L:P@<Ei E DMҊK)u!d=7iƤ1 +%@&\uA`ɀcuhDϋ ֯B׏N5[ţR_3[Tm${ +6v#NT[CI1vvw?'8` +"If4]8\%y"rQj (D"N=Z{pJ!\s/ >B @A?"}EK2N~P܇kY;5FƘM-B$ǔt9ĸjk]àr5oo]-h&d]z) 6^2j[Phf85}iEE"cE ]k&}tYD>M/Q%Uݢe!%2 K&Dl˯(vjEy:p4pփ5Q=bB601ʊvb[%VH:kJ5FdUw08UnN猝PU}).RpSj|K*hTK,-T_E4mGIX`ˇ0pg C=A*( *85T/8@4FU$qmg=Kr,^ 4CĸNQ׀Ng#1,52V:BmAY^QIgh-XxP&Dx GwǸ,8a?qxnr?U9d_v-nN@1yw9Ls,L&FJju tM뛍j:}6ڌ\ >*\74g[pkl hx1T]嫚@U*0W&:TD!Pԃ k%ɐǮyHQjn6K~jU| 3ЀQ\o{ϥL+%)fSϙj%d4BpLgwP] +IFDz=+RǓC l-U}xdzc@}53Sܣ;xS:i +CMjGdK6!!VKiy t +)Jq7#@7e,hr\&tHNi+]D&C{Dm$=(0Z*[3&oM е|_[kI[k-XެjVꢵd+Yr@Kg8'YD&}- +`eO G\pVڥ@-'8SϙzˆBKŃpT⣮ۇ<`r8\c"kugA.6.ru +~7*ѮBhN{RL9kIpْ1b'xr@"32 )'tÀ-Ae,d+r ،m$(NVlq DYb?f̸/(cAsIUKU-ki~Q1;5ԏJE1?W{ L: ynhUdzhVJX#"?i2ŦU le_PڊE]RWU3sb?~?(cklnKGXo3)6# H!˗v* VRt'ސ tA*'I0V#5 $?ldDτe9=jawN! VD)i o +)!4V 7ᑱ`'JA {W1utq4#ȅ6l#67.? ܷ+Y"iuo@UWpC1$׈ }/乞 Tv"2JV0%MXܫ?fs:3]2sE> "۰c._zh>xѦN0a]a~ZѺU>JR Ll4i_Ǡ +Cu +cɚSL]Gc2Ɨ2)‚TiDU*0e0Z+_&+t7V hKRCZhٯS)\h͚ȕ{2 U=LjqhOr!i:8!jO{ SרYt?Nd~a$V[nDbIrWo켟9'=FwQ֯]TL Q deO쾸[b(#`Y1DT*L,׳"OBdQxN5Ejych[Εr%pj4ԞFsjRbb$K,;'Q [JUs' V2gPkcPzV\Ub^Z:Ԗ*9ɯdŀ&!64 V88fSO2D5DcA'd_Y-$ǢL@m;j?aY/*9FTm'HlD7 +]*`hyl7RS ʴ~>~jfIeӫg컮oDZ*µm)CT+'gF <` %,:R1G3wNʱu9t]$v wiTJ%\/ +3oQCJTd;/܊UztlHo- r~_sNE[;3E!iPۋ$ңT+`Jm\-mvw'c:ݯn%+L$|Dt&@ |ҕzVQx+$70bfǀsl3ov\ړ +v:%:8&1X0_AЏ T>)4b5ev-TDEЃNsI.||1"5؉=mR<";u\B=%Pb!n1hVT^~ c'r0gQUC XK8BZ6=QtwJM1NJR*/$u8Qz $WOUP(9T!->(یޒ1*xqEz/^} Hd0Zqk<״= eal66Ti#zME۫+5C_h~ѪGNl +PtƲVpvV]0^fSz`9;zŒ\!NsXlfA=9-cQ"m8?6+0kMt`8݆g^jUc+yKC5KmxK\{($PnҲIm4Qw:K2mt%Xo!lʙeu+x'탒"nӃ~zBCZ0ۀ-H j*zYKxC#C蠍L{F- UO5hhC`-lCW +< K`ҍBv%-~VmC R,\},5B0lqZo5OC]B,K./p$6oJ~O_>ۯ|}W(-b}/?W?_~ۯ_З?ϺC*#_L[VA;Ÿ29t/v5!) ܏WJn0򑉎D"H +m1:F;¸`+C^F4FC /I68Zq>gޠ+>B 4j92}qH̜J“⋞1x;Vj]XEOfT%f`w%qDcw?0a(,̃'fV5$+zytEӐ{2:   +TGKZ{@q6'#]H4TMyFdV(gZG_\>mj{HHa?D=P KW~B&8N[zQc:԰ndw)tA/&$^7wjR[W䞪̞#T7TTʫ-ceBy,e"3 +Aei$SFsNĨf5 Ћέ ρ yOm=b +*e-K#"Y;ZGWŤ:bZ5m1|rR`!zg>6Rx3@`j=iN 0Jٯ30MPTetXDc+M,.P(,)@bKwz*ǻ"F`\w`ZbqcQr_%ȍ!n`=TjՐذ"#៉~!ẺqR19"tH`GbRT9VC>JC'pӘ#Z mX:T┮W&!VT2]RIPvWX8}cgMރ6抳H%Ҳ#ԡbS{WN`"is092t0O˖IE;"QArҺ 6SJZ(U}Rldb+u2#j<).!'Slen,!a$Te,m*$_M:5G$-[0 ̩4NUѥZ-mϒ }h Y1T6_ԛo!Pp +4׺(6RO"lKy$T뫈vKu(}K3Mi:}e5fa +S橻[Hh_iQָ@8ԑf0J 85ZgJk ʜ Xܰ=<6A9Z!C3`vQ*MTf@r$0b֯ȊpB6 ¯r)S #B}}s$jʶ4^CS/*PF +7p{wHf*fFDoDH'hR3˿Ɯ)1Ī.I7L5 x߿T^ M2>@A}Ze:~hK^öjFݎ!/5[=Ɖq_n[ΩTc:x Jk@sn +U-*w6·A"t++Vkp)'V~U+X΍H,ϸUV 0֪qRws,Z>qRw3aR"HBTJJo_ MK/^}mQL"$`ƚocOyBk*Xf~<|{q3HNՈ98+@N +UۇP&6F1;875` Y1+G nX&;c[թEzX>nQQI)Phi*}o=rb\z UβB] !$DwGQ +D%Y3y vQx^ef\!k15M[Z R}d@zf(FK+>2(`BJB.dW"F1ȴ' h8l"8KqbܟV%T+BmzzEgrXrX,VX=h[yj)^,cz{nJ a'sx&e?5hYn5a@hIR,=O <ъB~MLפtB Y[xd{2 m7Ow.!j<Ф$%ȆwB)Sp36t&A&]}-װy"w2=ar7MOs,1v)oڡ&+SjŢ0AYq#ð57F8Ӭ2-j4[twe;4%6z-kR ZsU-Mzǫ=\l,F묵dU gS .rMW{EAsfνP:JF}Ե}a 5иMc:R J9-kP$w5{oKZ Ȭ-$B kdrx,^9` \)s +j<`s no PA B-Z [ؐJta%IsCUz 6ͲǒTV6J1ޘQB PtfyN皁/0g5 +;=iF!z J9I;OZU_bV|lv 7Y*< u;8_ +6>`I92Iń!s)9|AYI%Wɼ|2N:}ǾQ CQf~,Qbe1[tp +|LLB + tSZH@րѱ:Z4f,:VP%n7WDRJX b&i Lt\$=pC2ک\ LYlYz_e\ 1*2ḅ@ + NVP[yo5NUT_s&Rz=KxSfL!(ss٤ޞhƤ}؉>=KF'䬀~5a:pu峬\O PK+?n_ZQmщRT[J}o~?_~~|_yOՂ~|}ϯǟ>|/廿\(WI]T(Mli-$'TC40_a&: FSE@e~`c +qɝbBGm5[CY$,^Djck9t 24Q_эNk{H +[Hvem#'weGKerՎ< +f(-ed;~ wѾ%H"?bf'(4]cam; 1 o<g,J=VSD&nZ==%DmzEg4[Rc4+T* jfHɒ"ߝı,7 A<7.z,Jo4$@lcA+QY<5>8c\SYq.y}RzRx4:@Ϯ7gB9D8,i_Kew&>zA]-:\j+#acF ZE}>_%gN +,i]wr.ɑ#J&Q9@_9;| CDM1Y@9ȼG(:2v ̒i4GQt(_h +wbP(%gTyCAo*Ok4 lq %!3e2sHXI$꬞7SP_ i-vtˎhKVEitM8F15Y^.UkeEEhC K3=u" ),ww'ʟ"D|s䡭kNos|>]4q,(Ę5J~E)K ;!RZפݴX)OZ-q1(3,z{k4ݐ\|,d G[54؞Hh)Չ+1Ol3ZKuQTv%oW/4'YTWR72PȰ5 *HJ{jTO,<.:3 6fF9/UA\1m{$[8(( E0.eȞ-?~ڋlKg;峑=.DdU}WP^%m:t}E֟s3@~H_ @ +4ޯb ;=S5J2 v[ 9ʂU&UAB(2_ /ku*B&?rOwM:1fОI*]UyϲSg0iRJnk 辤wf$d@iB5 Sԋܝ<1TWb*ԘvVTT{~oq׶llYynҚks4ˊ  .U c ào RiYXlsA`kn1}yꓺc;HD# -Y$Z5㱒&@(3I_wZ\Um8SUy36|`m/#8iw^S|qw [|TuA%HݳZd;DwcMKFƇ*St/{Qbt O]CXjcDOR"=Srނ :wr ^:v_ CekJ6%Ck7iUyrs`="֫$pӳFָEFpԳ8w,_AlP7TW#şvܘh1%L^~%oȍ=nlE*;h- yt$uȠ+OrPĤpPf݃T}le һW~WҽT\"@DA L(!o-FO +g8ֲ"k=&fW6i.i9r!3ឮn YN|Tu4(I΁B#n/8D4kѷNB1Hh-Im[xS&+fa^T0E\v4vsg`NSRF'#2'.p!k@og:Гv wީbΧ";OSP,ȩAMxyn>@geY~R+:۷JT檹F"X:8"Kumz24IJVB}JRՓi4ASx,TIԂǤ -vj +.sXf8ac@,9w28 erj\`)! 6":}T;Tv"c A㾺"v ~|3cD3ӱœ~X1 CFIxMy7Q^TgfM@KKmrdQȚTdMĝ!^v5Nb~;͠2ݵߠ׻ePGf2)̞5ˉ#458Ԛ%8η``+C>l<7sLFN\IAz(H_Pj@-'ת?b+? v֭b=R/\rwP H5RQQ5#)dS:w]$3j]rvҷ:ȰIx#tJ +D{ m0^mLx~2-?EIL9?DeJeLNfkHܷM8S%T${mș:QW. $H +Rݻ:]{YW$:z~W.?{ٶx4̋+:`j@ Qsz .p2Y$[rSMnĭ`hDjin6pRm1y$[Fy-:hy:#ڣXrBeQxH xB' endstream endobj 88 0 obj <>stream +HdWۮ]Ga %Vfg)PrTUZY=UWد5|W_v 컐 q{_|i'z>浬aeIjB:GuΝڣ} +t-Ӻ6 M+N+{-m2`=Jvԕ/rxJF츠'ihʍzxc_=yeźkQis\6:" mDh\tKHo}Y5`EIۀ5|m6@@Z/м(Ѝ/O& $ t[ͳl\+^O İ'SȦdcq̳o<5 1[_o\cNb~[i5 'Nd46'kzl 'gpo2t +Pt=QdDlriiR-(X_-P%!QMFs`dN2__I~@"^©c0` /~j*![uc0-zG!JEV k(ZN7Vۆ;xȉo_Ђ'bGtJs7lg2iNQ+'2N7L2XQH}g++ySL[*> hH&׌OXCRZ%PeH9r]($HUD\FUvܞ.kڌYd!>ijQځ%*+|FjyV!̃|+)}h0S8=e01UgLH˘Z!4ƭylJ9o&#b؈uFwjDq ʠfsq@V=ȍՕ7-lQaPN(>ѕDWY@R?@$[<GE]){jɘ[;Ib "lJ]v"2L]G:P7'> ;{;v69Xwd4Rˏ DΕ@Y5D: 9, :WK("?BN@)5ծp.5@ K%%oV{:\kN{G5vIsj7ҟt%Qo;h8Q'o^R'ҵ؉6ê{ {+Ծ^.]e+g_@AИ!݃{w}!:k[RUs5o1R'+yB[]U-*=3z&l-+̜cl@f둯2ryyHxuTNs*פ$b A9L抇,Q WQJ~Fml&OVfڽ4{ЇKi2bWyʹ= NBiStI#* AwaAD<1QY{<Ir&Ta(R/rޔ0B!wKQmG2('%¦B>yy*anJ*Кqv??EaL>Z`iͪB۬ȇp$%)2+z / + 5 j<ٕ%$Ao>Lt4)*ً8 ҏiDhHĒJ\ħҔic%A0#s Ӆ3yD?r4 9V%h'::K60zu#sS1hT0ha kQ6g)>1,@:,fKC-PϞ7T!(q3b>`_ Jڼ5OiϸJ)Rg\$•IB1_hIW|Tu3h .vED"9O +QcBN=|]/)tfab02j$[9IҼ%hZ G[MOIJVk#ްrvOq«92U[d잘)$[(JT!tK=e: -rDfd$YMwǝAI(V*IL\M)& qY,N1|֤B: +[ƬCB,VxV5Y?gi @|QA;$5gszz dw!w FHG& +r-څ'`bU$kmw3!-|Dbd!((k!V##:L/B +8|u!!nV{ؤ͑ +IyAKRiY/^C$] :=:оF N_,dETGKHܒQ:js*>Zуy(s^YPU*VF[ҋoȅK߻a}U5 ږ(ڪyBN !kh-[:m v!29B&MzBGhnS[߼6MdI$)C~1(o[_{de($Zv-޾#e˴jV5dBԠhp!DD]wB +#P1:lklt><z }? +8Œ }ɝ5-ՊvUFxw=Ό}1Z=ܞHt\E +^mPsDzt.:.H~;Cfr)A^!͗ H$ `w{[m1*HdS^lD2:$[!l \Ĵ7F~KFMy2^*uGd꽢`6$w1~GljVZCgŐ#!h4iB_4+f+}Gw'X%rzn:==q @=0*/ZgYVÆGWl2</iesÊL˜[tS#|&@cqlЃL\Syw +!V&x((3mKUw|i]%av#{o}G E-.%<9+и|u4ߌT~76Xj˰?v)5{Jjt)i=xrs/Ӻ)KESUKcT{gtYz(1Y6@#׽`VͲz:ci#,1w|E Jέ_#Aң^>E^gHiUzK^k+l.qeT`1T)mV~ulg*Y+eC(5\꽬ǩB5c_Ɛgl$_)dQi R|OGH4QQ9$Wϙyť}.Pб)3s& +{lɖ|&`9\h`UX2϶50-dҝJ3}CsFi\Ϙ_c7VLҒm.ǫ,fb ["["RC!%,t!d`P@͔yP0P9$Q2l匒^Y}M)G9'|6EfTT;!_>UиDmx= P cFjDw,LW‡-:t:g{t>/e7߹hʗ%2OW\_@bѹ&fU`^'kTy$mOEno`&%lY4-PoQަ+ %^FqL%Ũ5 +_|.G;. CB 8z |o g>GAusjK1F܍J)$LpFJEV(Zm%0 -.sg3aFÎ&:3Ej>W^ JN yknGk`wtٞҧ]k\3P̎lߤ3eBL R Eb ];}+1[J +pq}X=]'%cDB N2z6N:UcC%Kj ,,rm( l9+=xXfl~:}[sIrً R_W)"UB.cƪ9f?(߁Yb`I)39WiE{Q:v]uUVCb@(]=QZ3KVoNw9 |W]gL-JS@ؤt( KTЫkf`=0T[л]}ECiDF#>3Q@!%%=PIiOnAPuj#ե7DU9M6}(ՠ،+IgdС{T:||վ)u_~'MG)0O(sbBl"pak1Z k @[PHB}%m+ձԵQ #]J*d\S)M\0U65)Vm i#5>#Vk 2ʙY_Pf7r٪ƥ~,7ԙjt1* nDF2Ktbm׭t^c=4{9Ѻ~2v`[$Cɕ,oӚ}\WHǏ}ډ@RA~=8̤ؔh꣍{|5Nj΀ +(ꨆQi + Ě,d=M0> dJv xڕڱi(VYQDžsGWeO",Mih3) +L5K:]+5+D/RE =\C@]}{7Bh;~֐H$nAA]A,"BuGaFz;MtAAֺ +At-3A"im-uphVծx*u죲ǔu:%Ri@vbl;HjK,}0㲫Ӎty+ХâzAZ$f'dFT׌-d;'O(郄.^h\ICo6ps3l5@KXp!x+h?h鼁J 9[hi¨ѳ +s4)۴+(hzV\N~bu#{lFx ѿz"[mķv@w o1 s/XqEg<Et&2$E9NS e6iH-pq 6@y|3J%B3Hй88Ew?/ED 04d<~^ 2W+(K^dEf9M'fytH5v.n;wyaӡTڼaBJxfT!Te=yۛ05 NݗADLcErJu_\ -Z\y$2X3[Pa4J@nɺu` Pj6=mt2uZ^yF` ݋^/h>LL8)Luȭ%1&8Jhه՟}rK Q?in)KQqS63 K:k&Sgguyj *FbR;PpsD܏DPU3+Z +H-hB@݉\r["x\#!,VyH:T/w+X=W!2vwGXv_)TR?`+ɍ 7>^[KX{ܲ[%HZ>jIaP聑|{LliBLΎiyӛRD(3sFm[gQ1 l񬎶܃Ԏ/ߊzBXhxAzT,MU{K7?m8ed;O@0nNG&T7`5$i\қTH؈F. "O#sYB{vPEbyA4 +xL-dQQw;1CqQ` +胱OkРP|lDP(T\e.IgQqñU\ҩc]m,&P8 *bLF-pԞd@|n[ v`XvD- ZKjRp.L+ݦRّd" AȘQMH> :}@הޡ 4N6MǴ8KXgݺ[ve8J`X&s)Ħ}8، c u6-iYvzUY;0jdڽRu|O"aiQy+Lp8) _7;dMA\ +}o~aGk*EՃpTgmcvb/HSVT֤(hatޛ1 +vZӔغw kkhQKGm}x n<Đ-Ӣi]EeDbrkJ%A鑄z^QGPBf.CL![LD;]&|yGDt' ԉf!8x3Ph%jQױ`< #|6vcmgk?O~Vj}_?OO?oןo쥞ݥid70k~UQ8qlȳI/E(- tP =ԟ6Hy!y+G$Dm(fLEhh-s!^G0ueLߧ͗W\0ɢR ⸵-W$PHqzm̸ADؙ>oM\,h'\R64Rh+#ۛ teG1ˌbGu2LqONuNISGmT:W"d&[PF=[NDȌʶ2^@9`٢4l$}DL!S6ۈI([/۝,9̤#dK1B:9K N9BuY;> -Z%5è<[ JJ$uP"{ g ɴ+W*Ff lf7m&llw:'jlAaɗ}73NC.bb|hEEYeMG!UjL' _1+T(ᭊVYaP%eF"aH%Sܱvu>ůa/ͫ-ba~'jshHH!H9} (?idkdݖrł!bBZVUpY3H튜)oVv7V/p e&Cv}#X]okvq;Pm<]:\bX\H̷$5@oގb x/wJS.dLx®sq*UK4/eKzy0ږ8P=,Yz\0J\"~u2W |%Š}*+.y~≯53jDh)PgB)+J8V"KIk lW+?4Wsnޜ?f 8@guiapNL ŤfJSZ+5X +Ԛo[O%zf^K <*=N~@9m31 ~/-ˈaoZcCN T"(&C3?e..HEܥw<$,TD<02Jt6>JD<8U^tEUL `|L~Ԏc0 +Zzr~:0g ȥ~jܨ#m+𪪚{4ph2b6b), >EҀͲw2Zw@=>}1m;Gl,E(Mn ;ʧK^f)TfsfF|bꈅs4mhoV4 =)!v{l/C%vXT¨MGaRO +eNHMj2;x`~k@_~~bxm >+p<=$.71{yh:@:ʃP;҈1DpPQ0Qt 92LWwf.5) ^f0akEQ>w|D}ܡm`2TNPd>?5i44[A($0Fy( TOlAvb|I'VI*u-yLspNSΝbimUJ` ~^@9J"1|3aŪ؏eT)#>$yt˳X$k:-!fcYO\fAlDI77 +&p9 +lݣ04BV; B8Ki\O5,N''bB6*ȃȚp&8NP "Yu> lJ@װI9mTA@Nf8NO[|:fĚv%`ު +>ωI9j\ڟ ['-ENIQTE AKY +,! %H30i0&{S^څF]'==Nr墦+Kz=lqUBEy+v!?>{6mqЮb m_<Ƌ"7nbAlyZCz+b2aѦn-Xpp=3ʥE#%[Z56ԳXRf_p-j|*%&zJxu"0]KƒҢ,<)nqDɤ+f6PTs9?tnE N;K0f3@k +@1H#flYr(p3|X{ԓ xxNwzÎz UIKia]uqTM{`+*t|5 `쑛x^i_?Denߠ-ANGn1mQ>0 *$-LZY&wOivxVe{v̞ xJ¹P¨T<ޣ'ȘwCG&u珁{\ĪiC\s)NU::p~/ _s +sê< p-2 q5b1h+b;f}% IѬi;@fjbE$30yfёi'F)q[%;k @SUqC@pp +n7a$X:Qi Y j 0HGs9$<<,2OP|V<4Rlu2o"HPhnF^L: O&l,V)q;LS})#P⑴8$ +@hDr,1,rc;ӘF<`s %A0R#0IH"7B;J]V'm%D 2Ōt +oX4~Xwٞ#a PQ#b\D3aеae٤ wVPQ̰E=2qxӘ:# c-ֈv|z M0)룮;2KZe_5-YD p7fF/Hpk@h}!VLswmNCg`P-/R蓱I^ +;y4/4Q6GD:s xK',uwKRøto_APߣAde +XZ?N'_GO_|ssھ|v~_>ܲחG,MzK1~˴6ɫݛS}nO?~a rҳyyї׻_/v?~f=Bp?^Ym{J_n |߮?<}ϯީ =W/Qd7Ǐ'lq>_˳OG?\]ܽ[7gny^>n^/yI˷˿wriDHk1 dmncDS~{5fPc?)ZWDWO4a`rOe0[׳ d:F˝ޏEC^dK˜xoಙ*CnN٨kpI[Ig 6Q*tPc˼Ɍ`RԠ(%d#cס{L1ox] E-G +'epuM~qinMh^+]T; +!J<0P GABwQkh _5QzFueA4VRy5,ipOXB&b-P qѳ1eY~XnvtX9+.İN0A}]fxZi_pt6zmZ9%FhlRW'KL5atqiq)\eBnu2dkY X 5^$ $ze9lg R9|020/,u5_#Ih ,N٥1%3T +pu^skujփ `! `j-(s5PuǮ^*>;zh%zP}B(VA<èK -Ջ)Avkreke-EYNzpn|cyusvXjgM ;5cd{o,['rұcXh`i-{.ٗvFwL g{ɾb5T-c+ ŵ@na;=9IQ)j::tߞ)kP/Nug\Z1>TW5NmIZΚwp1gkUޘ~ +S#r[Gn^wՏ"9md% +fVuCY+6&J7Rɑ|VJ$XY2 žg(7#5ha9{F\02a-Ka<[qmwgBJs9o'ܦxx` Ø{g!t1fo9G$yLS;5> +Z\1$Kc +:{ƞ%R]l5J֑M1 ő]9Jy("I}tńVQ[(并bJs2ѧgIğE:+}m}K ¡@uZT.̀Iݪ5PX<KSL*1X yuG~uCԽSd +w>V7E3kM%c vgq=-)3G1u;\4\<_Kb!P&Hp95V Qs#$ju?:$j[2N"ZW +T-J*ٍ-W3uS@st;77]h6w`jVOFK0g,YW>)F`[k JYBG̰@KiRJc;|~Œ9>ӷudͭG92G0N„">ٲTU4Hm H+$bEgwG֬FfNMWǢf08u+ӡ*JLڴ `<Y`iԵB( 531(u ˶<խ^ؑ8l+O>>ScE@דTE@yͶ.;MnA%::\η¾j0#0!3Zu, 3+5 9 1?3dJ&h?BhŨQs3US2jp xϟ0C0o%CF0sN|K)TcGk/`ZۙJ2-Ti+={\~ \M a<ɞ%/d*V}0Z8qF,[q}\/ĘC$gL,Q} W[C,Gf+3lx45ʶ#]abNQ}IZUoWx0%# ļe1' $ x *nY,:Xg>pIQY?;ޣB1^L3 (S ,F\񱹾 >°Vaj+Tv|Pܭ".]GiY(R qeS}Z]^ԼɣdFsk7%ƽ*!npf@)dB ۺTK1YzTbsDj 'WG~?ejc21mV+zWu,}So嚊$ Ӊ6a؁oZ+W2?~ +2|_ Zkf -)dK?\] rYgu1gYO9K(z[r^GC @v3{_cQ^ !@pc,Z2fhVh8;Zuv +2WX~PS-a ӷb0nE+?\]СI(/47IwNDrA).qoqrXejq,Ix6BxU +C~6܊]TYVk CDV,woXe^c4+>LuqW?4&;ahBYVں>qa'6(CIF-KH!94,z`Q + R(^ok9 YsƝ{]k[>Y3c~+lfRTY=G2- |Ѡ|f05׀9J;ߓF :ߠU/yڪ! &4RF]X#VܖRVRnޟyS_w_Y\jkə +I,~T2AYŔV}|0k="*Ôb!ޤeeQ9[g񤙍)"$^/fN`YTuצIYr䞍oӍ[㠂$7ݥlU"!!juEn~\8hmjo8 JbNuVm>XWn΍9:\[tpa l/ZK*LkշX }Vgw23wx5P4B}ܸts:[Uڼ/y_ʣrgL-W@\h}p ejٞ'n}*4c R +K' N¨-oKO *S¡,Qewe5ֳo xJMekʹSYo/C[cKVvhhb=Wf4\+:M2`bTsUO\6)Ýwmbz-$2 = 6TU{{0m>"n3%Q(׽daQRRӄMgښ(>YB޿;;=cn]-/ԷK\^t.U-l[Aׄc?4@vJ5Q(p}# 2  -ppAQ3)*Ges$_Ԙzo.VM檵%L=}[OږmPݦ\Sp: *PB^Oԡ͒"*+d* `Oi`t Gv7xRYv`tf[nCM+]*N-՚bQ{oV[kacE>*ڼ4rt/U0֐>Z&Ӛ\`ދM%ewL 0Ԓv`o^*UM}.=%6o"ðQ=u#~}@;)\WK9 +Q{=ۺE#DZR8A3UU4cR.I\RfjQ64^hNX +0?v) l62X3$F-WO0}gG3a'9,,^%V>zB$>OV߷tA\j68 45tX^kF0)ļUvLi3`K5Ozkfh~MBa3;<9*#nwO*2kMdDӌ(S^g(8e]hyq=^WN=fPH Fq֓x]=89e%5[B V8CwIM^lL?KDYs`梎ݸ~<@WTۛڱ-WK@"jxeT`tB3YـcՍ.>\Юo)+ ڦ*ƛOV'| -*(I !O@ԟk +}'8'>|~w #} NC 豣$cjCBz4UMY=D.Ji_ćkK5M{KmF^uTUl]Q1xuXK7e}Ǔ\Wz'F?#|z6A_%Yi훉 xu k)g3UP\!{f*%ƻ2 l|'^BꇫJw+ [g8ip࢜rmLfSkrwGke[&TR/QϟГ~KUtYٟW'xv1qh P {\ #kg'MۤUZo:s:3n?zW +-IG}&Vm&mk~YLpOqG0Q4zIY(ioƅ*?uט▲` )%`s_U~ߟڝNp$[ 3D)!. JZKx=Hs k' w%N{`P O,C,,]`+c% LFMvV9e…3|c e!takn "#`rhQء.yoS_U-R/qj\tC̡e%b0Vŵ+|WCeXZ, J)}:R]jֳ8\YdS/N 1E_f@^xvV!*[yE?KSkgSv+g 8LsҶEVS{-3O/S2DӋ92]47y^Ϯd]/) m {+>kL}$ܓ̬"E'ZQcm%ٶ̏!h\h*3T/|M 3aA Nr'O85&P|"XF}{ec]Hv%9sʅ9AG[1BϗyQӎ~}L5 zU'53ͧ SWm</gFOU!r +Mr%,:ثOrz^7C4󶠴@gXU=MFx~<98׽^$/tmoO{[= ,3d|-7`ѕyw{Z ނ`l4_ZW6SW}}D.LQ[5"[Mqe6IpZg^럸a2N(pL>a/6-n0 +R-Fwcv6-DŽ70?3tm|?M<]cNk6KB29H]B':3f]P|v;m]>Do~bU,X#@ "Sj=\h}bK>*5n_V[kI,.u+Y$xA3rk$nN(.A_?]zͽ[ ֒^Dpeê&ظgI?{"pZq%hPྡྷt?&얪^mDS'RuAʫlwR Vc?4Nn:{˒v6re)BC[17gxz{$z{(2n6d!^z)>M6ݙzj=AU` ॸocmw=4=wh՞|LY¢.p]]iUwca g۾o\|(Ikv[l%LClK5uOfGI/|9h8}Ϫx9/c,??{W D3O9O9ˊb.M XVy1*v2Z]L[fX9tW:iZ1hՆK3$%(.AEEk'pZ,_l$}OD~^IR^4<}kAuV Q4e zBa4i? nK{2@ +*a7H!򠻬> f3Ta" 2[ۦO +t^021V-9k~ +.oq\, wffHrKyLǢ~o#|IY|M_<=Lr?*褋E6e\ac'9T;M}ZI4ξ&BU_oVtzVvuU]Ls]f-v?r88jG؟q钽4i7-ˇV.4QU݀c}.?WRw endstream endobj 89 0 obj <>stream +HWkoI%#qOv2eEOv#VVh$_?nu7/$-d‡[ܺ-MeI[[X0!&Ai@KGP_ gs4Fh"0MtO8Uֺ=oA}aZ"Ly3)rE(ڰAj21`nGŢ% b4J琵"cbz)f ,@51m +@IUK++RqxD%恮jYoRJe}h(K9 @RȨ@PV")+ nQр*E2h;!%R`CDbZٖJkqҒQί.Bc숁@4 + Vv)tyJ*.(d\;rB3n%-Mh1_X~ u Bu!'kYEg,d52cf3yN帛:}BIJcXQvw}W5@E'FcKF8Bld ]AjG(Ep@Z)4@E^W2pi\9ǥ\B +]\ 5p05[+V"9 SZxGX\=rm+SzI ۔ZT\pWٖ g+LʹL2.E7BؘxƄvXFP#m3rFr+!hj$j@Ȥ/cxLEDPҰb4`C}^d8* +A()hA+QW 6.~m4I; /Ij ̃D%d!q:Q!F}fX m@`%&X(b) 5yװםnW@<PKj\oy@DNwwlk7ŠlZ_`5/8 {56v[T =7Z~oXN*`*]ܭ{i'{9M'AT@6~|M"6Sm8X[wxhBxgA:ުam^织(ͅ4ݻ#ϕc/jy7ˊpWfut˷I>fu']E>dz""{9txɇ0q7lP63NvIwQT o[g/X$qm>bb‚ZlChhi>1pyE5!r=MG7|ɮ~ڼ4j3;|fJI>ɦio5=Նc\z&lz5[sBu\?u!BWax9M'Att:(h!ux$|j[v }^oYx|M?eΓ_mH~.{XrVdYcUSV*W6%d@@C4O%Ť=@PalnDv!99=`?Qܻzw6:-E_h.HxK88ӓF[{'z au ןoÔϚ.c_dYhq3VEن_Gnj28\iuPĒ9e,ћH5fi`xZIrG)43\Fm tRa gmuuiǠ NE_$>G %t-Óm% qcv`doi@9rǧ6?,) H/P"$+B;%lrKʁXgbәyENwFY0HNIwY^;zrV׎éc$Ls:%Tie8F%/ҶEi$dRf–I vS~{8<>ƶIߍ:E)tpKw2h%@DZq&8ᎨT_Y3HFH _{xV0&V2Rep2Nu0R1ñ"a!R53Ռ`oG +E,E8|c=;x'cmh +oXv޹g4E@ $G?dpFKTmC S"q>b3eį-=?ib8=O#BG)8>o8&x6|gȘ ^_8cݦ%qu~pֽY<)=5ʕ_WN8l&X./6YdJFY0HNI7_X^;zOrV׎étdx8i UcZ0λaKmUl)ezR>mT;| #J\<<`^llh !<pKw2h%@DZq&8ڦcfL05PC\+PN('.u89=`?Qܻzw6:-O#!G|΂؞.ç,')qʖ Oe6LEiRf4\%$VER7Y`N:}U[Ѝo.oMN:usWnxԭSNNMf`j(mx3 hD+˧X Q\D0|WD]UX,qWg=F UQ~^D+>Q/˾=L)+x!ˍ,TDȓ JiǏp,{xSZdsXVvΖ߫dNj"aD];xGH3Ռ5yx"r&$zvNJ)3,5ұRy}Ww^iG7:~/ʟWz(fO2Dа~ϱnY/,SmSNr`0޳iXQD#Z =Tu{yҩa ALcfL05e Qv4Nr!8^Ktx… u{&KJd3a{{>]1ߛP> R\ r\e + sEC,f'vRX~VZJ~ShN(2͞AHC֜(];~%bMdǚVW藿rF H}#nڪ(?PQ]DsSXz]“ +;h{8Djġr-Fdq*XNZP; +&cJOlJq(FԥoFJf4wpyPj*晉3'P2%V8:-;{\`O+wzy9H~ԈCyw޿r`'|z97+1%&0xL֥Ћ%\[ڦr`$O5Ҥ> Fl{ %cS*f`jX*D"m ;G;↤O(k[4PަqbUV[6q-RJ +|u쌽XbDD_9c%|J+qn+w^e.u0^ #ҷ ;Z?Cf4wѽw)T7Zc4 endstream endobj 90 0 obj <>stream +HWn:_p +4K([@ņ"u(҉Cq,Ei) +vqi.B@7Gbɛ^߂aiޟOq@Yu|ԫ=~}<L +hS{3iQU\(^)DQQ,2_G,~} 2E {9gT۫cɥ{mUUpspqlp0Kʹ|b41A\LrGTH`>9P w`EBOx;`=Ky0ZEC1-uɖU[Y)r Gꯧ\ +*zcaxoWRTCrSU(LQnQ tjm*mGg18g3>muý|zuCЭ\M`8}F&L09H=L}"8>a5 l16z*i@0rn k|^ +C=tp< ` ؟am +#C-ݲ:X[_]lăI#oh(%\>3ꑵ#xC5cT<$,,[m?;R?&V8,3:|pP(_Q*ZU݃Xߟ\ee}O}NI|*g(֗bv)VGuĭJ4 + PlÖm邡B`HE͸q0M{ `H\:1y4af(֗bv)o5}أ7k&%Lbʕr +dL C.:ҵ-L) +ĥc6-f 9"YF+Lr3%^t6="{sǶV|C( w}=CEWi_'ˆؤSva0H[ + +m^ЪfO + x 0mc<<:xw2C? +  +"}u`DBN58*_pܪ p8b&\V/i*(Xq BfJќ"=6`[Wh`Qd#׻AĬ|1ûE̵^ƙȱ0dq|#Vh -yHy|/p/k۵~mu{mWzD_d\;_y+>y}!_n"x4 + e+1DD ҈"9UHKn +Zބ^,]{cFGɧ06 KJ<{!=oxXq~|`%o~u% oKiw@Q0XYxh XRM[-T,lp|j^UGX Ppƭ$T3ҵUT0NᄳU(4a;Y1+e2Ot)K] +R`M)l>إ2QZG~+Yq`9𬋁] -] b`{d:?聩1xx@A?RBpR`)g6θqڄ&fw& +)Ç}ɡdL HE|o0ې S K'3L:v{Dn5H^XdNѫ])\Qr*X!F6K5Xr>8G2G;]r!3Ya=ӶTbVHW4f8'u +.mȜ*]kx6-QޕrY0 xh$1!8Op_IQh$C]ps6095jyxحܨam(8eCQeOjܣh"-8a;ˌNx^~DR^VјJwI=]glg)6r)]0^+'3sz!c~?ԙ OO\oEƮj[Z`eA. `c$Qgp&c` +׋F)Jֶ S k6<v #AHcw1bcNb_'6HdN,MਮdXƊC,GxbľHw`F^oۻRcvFb @̓%'Fvg ,-)ft1&%eY ,vWW9uӞD|?^,R༺tjhWRWzW3V[^͇gO|wd+}.n'&۟m|<_[^?blxz?_-cq&/1N=9Ϧ_ovZ-c HHfoW)>NV{7S!ތ7@XB8偸ʀS4kdڿ8^K1x9zȒdv1*VRIL[aҧϳ4{ֻ[LGgv=wx1xCg.E&E>SgsP +!l^HƋӬج(B!~F UCR1Ɯx?KB[EZԳ~.9͚7Ȥ=\:δ=BlPx:Mo9rZNOVn;+%NY^'e*ДW]QꍡVYDo#'N5@}v1'^h3n uwn9.`9_Z7Sco>H@z,b \Y3|բعjƾJТh -Bl?f ZmVH&M7,@csh+vֻ7k@_d4U lUQ/hk u +(JNܰ/W7W(uWj"߯VΉz2׼t[PJf ^"dUiI\'5[3SU|#kcUݛ>vaܥ콚۴muF@64܎U3HWO@8T5o`-zAZ"[lݖ_ȄV Z/? *aC޺GݫuKS|SJh5DO - 72L0j`iqJkN-e||~hEr]YŪJORHt#Khg^ Z8Rg;sVߡ4ީiڐ>Ros')2%I;w`#62JqQ+B݋{bu6.#LϜ. GO*޽_,gS0j^ :_}3̢\:T_`(`[k YEJ@u<@2:]WxD5T IdOF2JH[h!R +HHA{ǭ,D 7WepRW Dy :ЌN;lpT0jhY Y&6hxI7pG .6QJY)VyCq'3 *o( +&9"5'%x'!0[@ +}NjAod v9J9l6T.X/w: ߂܄2g7܈%x#r.E Dg$ +FEuHA%8.bhL8ᇐ1` 84 $A4/^!nDܠ5O>'111rX@ q"2 ‰I5ƴtȸ$߄*)mFvG4#0HXN(~c1@Y 2R?S)4n4`iC^_LSoZ)%eL^R>stream +HWkOKJ +6$DFZ{زM=U=~lV+:u:ϫŠׅWE(Ɗv3҈":*6"JaV)eJ$ l2кT*hTiB2) y6eVN[HIF(lsJ(lCT<_]n5?,p#6Szet&/T54W; ^( YJ邧2b[FelX}2et-_d`{8#$-B[> .PvQ% T+[NtLJ7&CWŔpB_Qy҂Nq{GK8 |f갰eP*7 U֔))j~X.ƴ LY>y +NU2JIe6j \9Jb7Jyˁp +`C3q&aݤwDN1E 0`B3F7p\WR} Β1" HZ)abUA.'do5$ii :1). +-[ Br2G.880^ y$e1<G +Ad<-Fǀ"LQ,+r#>Pz4jA蹇}‰LTt%‰/r?/_ԣe=m;7}{Ghz36c*q3W;~Cøbo_|> + ,P`Hց̎n󁺃f&KqY +CQΑ nY=$^*ϵ~`Tz%CצL1zP]4<= 4ה?fWaT:X)Pv $nD+HI=ЬQ$Hp'{>pQ)L%Սރh2:JHJN*" a @O+7=)H3C**-У](8 gL_Zu|~o:۱e֝|#bpiuU/!-{w7k1\ +t҂g\Z 'ykm&: 'usۼPL{_6ËIm=yt܍[Ŵr2/ts{>QM+{W}^̫>tZJ0>:̑1wIxſ#Řv<-kH丝YwҎoDD7iŧ"U,h丝YwҎߍORfٕPQĪ}P"?|VUFG,sU?0#h<%p:Cvkt?a~.bdV 㔿 g˻I(駆'(tXz?@qX736znD^K~AcgQ +igoa0\ԣzr>' +yo&[n +r8C]gBj߻8#KxKN9yHMQ@*OG=~_;mdIx_fe`2?␵2³EjX1nN`~i[g!k!}N] +T!0`eɍng/L:bojH7Y̧e$/2)y'EyM^rR-RБߑ|f1Xyf7B]#'X3~7B$NC%ÔXJ{y :> oV4{;$l姖>ZٱѐΛlD^F "gyXܹl9nqH)LbzLjDFln{gyfЈ7F<0H3|<ۓB['D( {dh"k|[A@K'7jfbK67%o.)E?Y{V,{e)Lt`׬(XpW#EFbDYc3g<ڪ/=emzD + +Taj=Qw#pِNz #1mٽQΒIJ%>gL{ƴ'8OdS& 7̊ ~y$X~t9-?5`r89OkTꡧZDUDyˆ?zƯ4YƓ|vXdva:2#mOH2u?EYO?vG 39Y8l(ΑѠeJ3;\чl5茰F*5*AeKKr4R9'@cG +2( q`Mi6+ӊy +i +^m{ vOJG|6`CRH04CwLÍ8x`9jƌ6T Ll$8JCngdfxLJs49h$[ds`C0@zKz )kNH9K| M1,^UL⼦vJp晣[Z`k &6VHeFkGu +aK㬖FU\\ݰZ9rT^IR RR 5U +ZށTbO,QWyC,pU\8DkϔډPo,@!S:AŘ;ȂJ+7gHv:9( @`BWi-m`b +ЀIwX pDγn _FHqV1u +S*caM! XCn2 +*LB*l XHJU`o5 ft&r@F8 K%mH HBî=B~u╌A>'{Eyo2ry'sM_`uZcܟyEj #`M _]t;ԽO7rxE#CVc4g0\a%DrMr@hU^ $Ezuk1GXhp-< SI'0]b +hЖQьn I#xFKK^`A } + [u$@R"$OQ(9%9`rYl,hADc;]'Fh|q,=*MkwN&1[88}rjRج|vMK6nc[|z~:IOYĿ3OںԑtY`7d?.^i=m:]**]|`1:s#a9͆eӳ6USnB q]&Yft-蜿>V6]٬]MӔ6\>O7̗(AIoe욯Miq0 ۽6ʳyp Կ<4Mu)6mO¿jn䓠0i7'|МѭÅ xSL.AeL!u9,&bDO-!+,NO v 9ԧGۇ'~$Gd627ɳk.7:H5+?,T(Rfߒ=p G.ˊx4цH"qm0UւHqrVDKi$~IcHdI3Z0 +Jp=bϫ,S%2䪂Œ{a-+/ҿK].ؙ5)C$gn4@)+v$;OR:.fюpyRɐWron:uZz@Uv둋vT +]`X=Ӈ1'svc%La+lC!scqi+g(rhaڎb{xO=`8lX>:p O&v"]dUySy05*Ozwi"ʍr()a(;$y3.vXHK `kX.a=S4$ftftfu>P=ه_ Şɧa2eXB dK-^HO ;YTs Uq8.hm8MͰ~4N1bwVFWz +{;ZMCj=8䡦rp>WYYWNW $Ōbin: ]TҒE"|r50)'w\GkuHQ)--AӼ׍(ȩ%A>= C_?΃3^j׺}(D?,Ħru̮7JSEx(.P!**qF!:w4bRU&+=5`VGh^Ͷf|%v_k=;Zt57nAY?k.O<~&Ss=a쁺f=qn5؝ +s?J\ 4Wyta7jZ=5Yoj֠מda6^<*`q(*v(#Y^k Y((MN <[sh#͔=(LyV .Ɖ!/=|^~۳H[6]fOodJG. SaLzdz +D؁0kJҲ +yGT@%HHjg􋋶v*\m?FӤ QwνM"X~pQT㓭Zq/I_LbZB9HPu ,-IqsfJxϏ6nda茷&ΨoւnC?<8ԇʢ{h7IHăr/7WF5NrMPQ-u8SۉtCnd-y`5F$!/]*%35N[9nHh +Ռ`ĄA|R (ܜ,tދhWS c;JSҍGGI.5O1U}#=:P +8l+hZotS,p> ta;tZ Bf:ipҁ4 \aiKU5bЅT .%Tfpa#^7G^Ɩy"Ɇ|%F׻:mr8-, fLpR] #Wq@`GQT, +lXss"Z}:fP|3[];c)IT 2w b{0铉{$x *9Yk`O3'3 K+Obq14N$Z]o~w AqEy~~LVg"Z If*LMjʀC\~~4wTR~ThǠ|yc#4g"|Oa >>cK;ON+<?ZxVS^X:(QP%Ah>biSEgƧIѬ@ wYhnkFQR]L-P9E'ϕOC^ $uFƤ¡y:Qՠ4gѷBhA pԢ2eC7 Ov:R+ +34g/\%}=$͐+i*iWf\$ .LNVJ)g \DI5L錝 9IEdf=ϔBy@&ѲE5mf-kLJҰb=ICSs@!P1.@q4KC=$ҫ%J>2|܀k{dRbJt*a0ܕ+'4N} :a4]o#~2:ߙ<}> Vmɓ)5YP߬W疢}~L@VɬڢmY|ճ8SkO2Gz!55Ő:UP/d"d9,l,ʏvUdTo0&8T̒$n @PoCNt^ljS+xdk+0)dSP9B .sl8-tƊғKcVG(\ 3#ک8R$pGNYkiXtcP 8W`o߷}]YRCyw')&.E>6脣iB L-p wLՀKv>è,PSc!m] ="\M3z|J[ *ԋRP}fP lZ%<TMa)l}X+⅃T& 9)CO83+U56 HzӞ%oXf|PjLǑ\oR{r @6dhS0{xҋa2IK#hCߚӝ`'h-_aH~zj$if]y2c|)D_uNf5GQOz}ʰ/8T,\2Hm(ɾW7HE7 86Il .GÅijf-":A |v[6.'lp(; +:c/5e-L^nY*_rgzV@yX{؎*_mwgU7g!6][olIQ9tma=߫Ese|/˟PK,q!wjL))~8i)c5z7ux/Krź +uj[z_uhC*s^ +2)ϙZƺ۾˜mmO x +v#i~@5ե ܢ@}"])mVbDS'9'8IbY)6fםQ{ɇzbh6Dt@*Va n,_E~ì]$]GDzl,@ܧl?tR\i&h!#I>"J.Z$_)93O{9{*Rt̖W`n]'2u~I{\~aJ;h0[[.䭫'{:r)wqWj2HS{$., ^VdH-~I7;LŇ'RNLZ y53n%d[LfS9?8-YU)4^BqV( fox 5B>,=ݨĶLҌӰW +a@k㫉.۔axkf]XQvx8/ӵu._PT3 +"r@C P)Mw[oEl`w+:prb@B>Ry"bDžJp*f`.O*N`84xJ/.$bio9|zu:?{4) 1Oxĸ\mˢİ~t>Ģ{ǾEi_5tr L3CeeZe?K9Gޅn73?WφZS]+$ .ٳ-DR쌟zq̠ ;=FM=w0;l_|yy?G!Z C9gMeOjҎy-]!v]@пgk,u:G7 m0+mΫB5]`49$T5JBΕC,hV;wNu_ᚤw 1d4]$pHGL1s̽5_)n0BZ㭄' +Rߘ#z DTH'*9HD?Ȣ@f@:nt"IjbX&P@3 Y$H16Fҗ3AvҜZ^%뼪&ꪎ\0ZL2 -6t!!6! +-E"bUD] x&n{~һ%Hkƹ G~Z+koAF ۜyZej<۹G{ '673z:ս*z3>ݟeL[\,w) +E2]m\;?y⼠) /bͽJ梄cl".Q~ɝzUY6uPG(c:Y<dsXh@R!m,kL'3$A[ܱ癇+Z,Q⺁UUw @Zx$(㮞ED}SvBt#=t *ʹ'rk̟fLiYoSߟ]ɚT,wC6"piC}t]nSsw˕]VXqѳrpIH`]KE)Ȉf 5 +Ƴ(~?/-.R)\Nm}0$>ڷ/C#ᘎRg?cU0oe>ԉ#F* ho*\ZN]B1Pl` DҘK,3z- #& +4|͈YC"]1PT$,lRjH ctBEu15wAnGT -.dS̖&C6xQc6Jj:zs?n[6Jh =<}c]m14&}^q *~<'?B$W܀AM TA+ >Fa$cśv̤$ޏqHGc08)0_wj"a'*՛>ǽ׏6BL+Xh}2^r"ns!o[:=J\.78=wӷay~nR>,zZ-5@9nn>…D8փ'^leӼO%\]dy, ~B2wY[_Wٙ5*\OǾlkֺU@5LKchLVmd{5ȕ&hd 5WUJW[_o&ؑ&w/ʺL@1WzJ}k͢uQhbԉF%.˗NS c~7U\4 @:`s_Φٽt4QT‘BfzFs4n" >;ڋ~~UWBcLٗsD-:)-&ݪTOh"% +wf!uV4p@1lF%M9@7j_ rƤ)Ij2|` 0A)]aʁxN)eS#Xylr8==Su~uQls/§@4s_o:,u`4t+犠\tUC:>Cr.ƺFgI;Uo_O_*1ٶV\j[.zq6L3^;+_if<Or`.Y9'Jspj8-'/]< kKwxj)%.>>Q]k +8{ J̑}ٲq."A Zz6n60"zki-ISzcq:}L +1ڧ4ޱO6҈nt[yeHw8~<ӻЯP)QINĘB](3U]wm3$[\Z.5BmBILMc s_W^saAg~2<8xF*('>mo 3Ā('6ܗ-5|Cȯ85㚯YQCN0Tm;BR-q˽nyT>stream +HWz8}?bVC;Hٳd#02$$HBe?8w?]\%L)njn^υ ?uMoUf>G2C-_z)gړ^~3}s +,'_izmNZYjXBk-y,U!U~*Q)Fka|6L9JF95FBb.cWaLlD̥Y6 +Z)v;H)(j`G_q@zG,+ɱP;Z[㏡sT.m>MXN=5)dˣYPݷ'}څe kzbj^lA?Cv[ 0= ź^98y"yކ.o]OnC#b4ÚhcWj 4M %Sv2?>Cvmtc~1f۞}W%3&$wJB+2%4k@>;`y#G*#`,E/Z*yj])p㩧N*&cGomd*ZJKpMulqKe߹`Ӯz,1\\ť89foclq*9N9t2 :q;BrJ#ѵY[yY5Է[MHwǁH=Fl 5 +$[ODW]*wY[U( Z}_lŷX(?;K|P+`,;?#tinO +zb llw/$/QDӥ"NFfBkT6YL yb~)<3h&z)JξiV-LQ .ooV؈kc-d~s~{o32*DpΓ|'0${w8/IO{wi+51c1@<7Hafi@-MP} SPw(,I)j WAL=ۇ5mB sxz -rͼ)w,ܣRQ1u295|GjMԺP0I~P:>VZ0oNS3n6jcx/ٵ-"M >UN 5)%좲ȫXqINH4JJ^ S %g)MbSϩStBޚʃ*ȭ_28O+̀dm6^]6~;^_PU 9 n {ri {<Iv%7|2;lT-+aܼ8_LIϲ:IvI;h{~2[ILrʅH:8tdQud%ZH$[@17^)A?W9R{?=v&:sLxNJRہEڹ$}zq _S(DsS^ԥT-pd ֱX + ҄x;'#zj2zcVDL +J8|"q.gC.\L)L[3dNl==J gitEYO\k7{[,ܣRQY-Td`QupWy.\kNcK21{bB pX˹gX6y6 &shIځx}A :4`]݈H-̠ bNF8ԡ5|Pa #P5z\JG pbȴ|B "16Xll +x+pȥw7{3wKNs1>q>r\:26-6o"mQrA(9oi%ΔڧG@i)Df@ .n*s*]D@Pí+y-ѷX7}$ޏˢ(=Ӯz-?`ϋKr~e@ tϸ$ N­8I?I}ITi+)PKf3r 5jخNڞ}1+N!̥b 5?ch6hL?Ch-:j۽lc&OŠoü^@Im1W7G`T_y̌y%ЗʅvxiMa+pq2F(Ed^A&yK$8ܹ\|s6z9@[.w1L";?X  @n1wbUjU?lyl2yWldgpDٟ0!0=AIC/aXMj OJ;t(Y(܍ @$X5q"T^2֑ewiLexoM;<\ZwQc~N׬v{u#*.Ni˟UKu*qMmU,tK:ej#ɯ'ڃ6zרVJ: +!_JRV5ޓi]/Q `Z]<&?5tTP]:x{WA0:Gb7?ic;=//[in8; jiZUn)F] y)|s:qqaNgօb7$ s۩;FqrN$6]Ӽ$}#!. 2״N#rjNo|΅7=SU@X+kYAP_F-!XZ~=ݲč5-E V>9,U\(_) H{~Լ')DLXtO%Ȯ3- +q-Ͱ^`u<1σE<.߅0D̔yve*lk*u0ŝŬQɬ`<0+`^I+rTmcJX\!n_=:oD={<@^&/{:ɾfسB~`ړl0pQCp`@c,8r ; +:bU #J@;y%!ciAv܂.%'x60_]&DԅN 's%86B +ɜcAQ!K5"N)D E^[{D^Ӷ52#/XSQ6t$ɒsWv+_:o&7phJ2$oews 4S+n߂$1H %)JoFa5|Cᒜ;c ʈ9-ye90$g9q;(l,  Cs'%r Cp{I(ׇMrxf /d;W܋()#%MhҢlM$`Z1&ڟC *Vi]9 S+@exS>xeW%rdU} :Vz;Jړߨh#) [2e)%DބX՘}{%Y3\~1t%vfagזN "oF" ]\({,(uI;4>)C/}8T*`cX&.Q$ +1ƃ;Hso 5ڊhK@u~)x2_NV֯?]/h9 +YvWb:| =9ƥ:V=hh7piPJ_KC׫a7 Y[ ٟ + KڛƪG1)󮴌n_<=8s.N8ĮW~B~{8D2wBMc2' "t cy#v>ʜveY}N[Jbv"+/E [){d*g lbOTM'd4l  QBhHИ0 q1(AVKbdx,D3^IڦH2Ցe+ QV-zWJw3+MO]9Et0L2% a5p lӵ Jb5I4嚂^jsl_h,Ə4[,S5t-^[U Jکe g9d*d=ABr@X`=ֹD.Dϖ~^^j%Y^wpx;\#mKxI³I1p|u; Qt?` l7APR8Dj~Tq]rA2srN + +9 i-}8Knt8:ekZ\4TÝ^ĐS hp89E"rO"+ipzl5{rX!`1^XlT&A-:_ jw ً{>'#85ajnR l3Ld<]SIW1ku v2? + I"Fɒ:^{rQ xEMKC3KӬLӮ_V*ffuڑ-AbwuWkA:)v5d>SjQw_ZCc]MRD':?n~t+GxDnFMS-Q=4OWS!JA+&{bT\X"y7v6}2X߶Nɟ +?{S c޷v6UNػREh'F~€tmLՈ%Kaª^JqEpK5"˫k[WņK`/_5>o48 j aі.  &-βa*:;chq0bJ;o|(&\\JI$0umsU=vTwY#l 5ߩCƮɗUą]1(f-NX'$c^gA]&A_J#T]L@+Ls(/a1ը,(5Dmr-\칸- -4 c(4'xKlL zj@ꗮk֡]Mk5h552@5f'ȃ(K/o"WU rn^1ly:T[a堋Ԉ{a¹MEa\{GֳJ tɎ*x`o45b X 놉p ƕ\[ +Iuw]5fq*=*c)ؼ +9﯎rH}{wj3p/NI"d}4.-Fg{E[B_ S]M1\frp6 +Ppui.v r+B!3 9oAm4'>^z|(y ^1{gpoC U6}y׆65i`!}[/qwA63/@a ; 7+ qp{M21 alXi8tT` ,  Px̦2~ĔaTE>|idixO͐7LJ/ ӟӭs&u`WExesMP*Lǻ&*(H=4k錚vV`i{<Vi٪I1j4 hӏ-]ṛyVg۲6)"J=a,ϔ.E *Xpou:%/s_Q)f_&zIfW#M7YGyaoZ`@)_Ke8kevfӧᇫh#xm,yތ6|mE*,>բEh^=j6S-y $muWJ_yR6ҕ-o<Զt: !!>B6?6ǹ58sV?U cWͺSP0K* +OYj  oD,0+ˣ:A+[- +\UI>bY,x/켒 -;e }OWl` +p DjF`gWW_+XW1#U 85ƲԈ/2ڜxnGvS˰KA_iop{ ^Ä(0F2xPf?VlU +]Մ8ߖ΢Phܳ+ &^\0p W*Yz5DӎX-cM0H<q>ʴm'%T$M*RǾeOk**.V8I*|*e*Zު7V}{ jk-S}MFv=G)sQP|? +Q+ Z CLdS* +J&X m2s9[Wg3_JeYO;A$@0d$(A$D`>а7g?:MuuW_Uk2&l}͆(G!ׯha :ӌbA"2z%tf\A8̵7."z:v$$0"W/aCh/_U@aHy![5sM[o.=2 T_:FΑ2S + +É P) ! d'] ALP&`2AP6kK[c͛]6£St Cfsl1;48U;2Btg] -'FH坉~F(${8@E1A- p/vPi# ;*y9AA%ǔzTtD;a`SFsb讏Or"-v|wpY\ARA0Kі8c%AGAlI TcJeDp?@BęK r78_F/ko @g8g`.N~Ï225LXf|Rwp%_ZK '5[ylNڟZrj + +N.5$Bs6EԳ:"ao^h  +)vX 5ƹ%_$]~&[z$͞UveAR3T%ZK~pSnC/'8bډh6Fp#ʬ^kz Ѹkr"Ј*ڮCH+XkHX"u#0~'#(&?|S`~mptdB?§-!Q\=AvGFcw6ajk66 ֊^~$j狰6ߐqOt\ݩp.tRc}<,c CqHG d{, dD齟;檷*.vJS`Q.qTBBpG#yc=8*1-3w$%9wfH ]e!:Jqeb0 C|T '0ɕ 4)ZĔo '$OY=r%l౾!OG U"p4;]oVz* D&F +KAűԗu^Ko9bB%i/`FC!u7(Wa7BJ +Qū#uqH$+6[/brtZa5[EəfTf:գ cI&;{7btҍ¢xH--#!B.eq83o2NDSxwzG唅tҘ}ꇜpS}0*%i IޅףV!qY#-qU^ʆ@84@L,,De}%#${.~d\Kj\}h#3)#t 8o#&˟ /͏shD? ]%ÜA  Y:whMd:bQSlo|!Sus!K ܂ % qTG`~ LiGn`-9%' X \[ɤ`;[7LcJRWA $ 2* aက׽}3=\t7ջw"BS&n&Ga;Utz/`r54WӖF"'qdz[8dh7ުa!"Ǫ3}ѕ6jGmg<F=5JG%}L`+)޵=K)j,>wlP\ &1/!uPF,ѷ\pVV RpŲ-;kQ#j8إ׎lbLzT-JayK (OoNJo=k]^}h:rɥVǺxëgJ#94f,N&oJL bDZ0EͮPnlQKQ$cWAew%@X9Un|7]@Gn)^}W2U3YoXu@_{+h^'.p+m=Ԅ߀-Kz*'vUx~άߩXC º$~dOC853@J2ѷz`ݘâJgE*hYswuJBBnw:#ڲ !(A8ƉC{hټΦ0< REELI',rͼZ/p/7=p6_jzt^]NE\EwP ݖ?e$/=Y ? ܾEbXMntyf (at\jre_AlApdd,6_ CxRזS A+j!z (w"@I?6<D&ȁ^5b;F^wOF?^e,iEi˳N51˝aIl5wg(:#\9u lY{,>/[ꗅ~}cU]c+X'=)+m*ľ=kL;Abؾ&Ճ&7COz?C6@`~?v)oR߻ TώQKmCJPmTr 2enIʹĨ~/ffOć To|p[/!S"bFڅǖ_d"x'[WdpT$A'JB BN /r5=y~q,:5b[9ZBሰ0#2[%V^]v{!{e/T0-*rU8L}.keC=qb] 3JK7\521 z:KKr`R*gg+^1'֐N{SUaFO̩7!'67A|ܣ0v]|Mn.'fěh?"pYӗm=f/j#V[zqPÏd;ðz0}|f*/FsGˑKi=U-\oyNxzGcAH=TJ)mWI7jƳwjXסi{ؓмx?VTd_;χ2QNOsu^șTD@P#.ب%D9[ީ{mţ8|;cz{7w| +PqxJ*>n +J(-ƒ<okH Mg-9v%`~,bj$sPyےЕ<솛j͛^M̚):ȧgnJwLblk&s|;-9p(؇q_07C>stream +HUnG~YɖK_ +c$$ oY9Fk9=@,!%H{Ƽg3ɽKM[K {\jեbΗߗŏ'gח׷7Czһ߾x_.qzN^Ha<9N?S]ZJ:J$tW/ϒTMx8QjOu<5\F&8)[˥&[j7lc-Wk.ɢa4֑?*_u;Rlp%Q)$렆;:k &X YF92 l7 t' +@X[u24sPyV?0bV l!M7봶4H˅^x +7]o^^.JVސ$8 CYA=3hӊ` +j R9)3!\$-wC%h0ͳĞRiljVz3`lS ?үYcCB=H$1&|A^#+Ӯ7 w>@DMhDN~ "Ca" 0d\Az'BHprbھ|-F-еQ #ʼWsǠF}J!ym9j)5<`X,\bu9X ^_/:Z7ƬU_nzAl >Wd11NuG::N~[Z6'/织a]6׻\>~w'갿=ݟY7Wϗ3l endstream endobj 5 0 obj <> endobj 28 0 obj <> endobj 50 0 obj <> endobj 60 0 obj [/View/Design] endobj 61 0 obj <>>> endobj 38 0 obj [/View/Design] endobj 39 0 obj <>>> endobj 16 0 obj [/View/Design] endobj 17 0 obj <>>> endobj 73 0 obj [72 0 R] endobj 94 0 obj <> endobj xref 0 95 0000000004 65535 f +0000000016 00000 n +0000000187 00000 n +0000057205 00000 n +0000000006 00000 f +0000175537 00000 n +0000000008 00000 f +0000057256 00000 n +0000000009 00000 f +0000000010 00000 f +0000000011 00000 f +0000000012 00000 f +0000000013 00000 f +0000000014 00000 f +0000000015 00000 f +0000000018 00000 f +0000175981 00000 n +0000176012 00000 n +0000000019 00000 f +0000000020 00000 f +0000000021 00000 f +0000000022 00000 f +0000000023 00000 f +0000000024 00000 f +0000000025 00000 f +0000000026 00000 f +0000000027 00000 f +0000000029 00000 f +0000175607 00000 n +0000000030 00000 f +0000000031 00000 f +0000000032 00000 f +0000000033 00000 f +0000000034 00000 f +0000000035 00000 f +0000000036 00000 f +0000000037 00000 f +0000000040 00000 f +0000175865 00000 n +0000175896 00000 n +0000000041 00000 f +0000000042 00000 f +0000000043 00000 f +0000000044 00000 f +0000000045 00000 f +0000000046 00000 f +0000000047 00000 f +0000000048 00000 f +0000000049 00000 f +0000000000 00000 f +0000175678 00000 n +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000175749 00000 n +0000175780 00000 n +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000000000 00000 f +0000060505 00000 n +0000176097 00000 n +0000057630 00000 n +0000061161 00000 n +0000060805 00000 n +0000060983 00000 n +0000060692 00000 n +0000059086 00000 n +0000059944 00000 n +0000059992 00000 n +0000060576 00000 n +0000060607 00000 n +0000061235 00000 n +0000061543 00000 n +0000062721 00000 n +0000082146 00000 n +0000107842 00000 n +0000132259 00000 n +0000137352 00000 n +0000142455 00000 n +0000159547 00000 n +0000174275 00000 n +0000176122 00000 n +trailer <<33F4B4E6B4824F3C934AA9C126304E54>]>> startxref 176316 %%EOF \ No newline at end of file diff --git a/utils/logo.c b/utils/logo.c index 9e3078f..ef6ba95 100644 --- a/utils/logo.c +++ b/utils/logo.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 2001-2006 Jamie Zawinski +/* xscreensaver, Copyright © 2001-2021 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 @@ -21,13 +21,13 @@ #include "utils.h" #include "resources.h" -#include "visual.h" #include "minixpm.h" #include #include "images/logo-50.xpm" #include "images/logo-180.xpm" +#include "images/logo-360.xpm" /* Returns a pixmap of the xscreensaver logo. */ @@ -37,39 +37,41 @@ xscreensaver_logo (Screen *screen, Visual *visual, unsigned long background_color, unsigned long **pixels_ret, int *npixels_ret, Pixmap *mask_ret, - Bool big_p) + int size) { Display *dpy = DisplayOfScreen (screen); - int depth = visual_depth (screen, visual); - int iw, ih; + int x, y; + unsigned int w, h, bw; + Window root; XImage *image; Pixmap p = 0; unsigned char *mask = 0; + unsigned int depth; + XGCValues gcv; + GC gc; + + XGetGeometry (dpy, drawable, &root, &x, &y, &w, &h, &bw, &depth); image = minixpm_to_ximage (dpy, visual, cmap, depth, background_color, - (big_p ? logo_180_xpm : logo_50_xpm), - &iw, &ih, pixels_ret, npixels_ret, + (size == 0 ? logo_50_xpm : + size == 1 ? logo_180_xpm : logo_360_xpm), + &w, &h, pixels_ret, npixels_ret, (mask_ret ? &mask : 0)); + if (! image) return 0; - if (image) - { - XGCValues gcv; - GC gc; - p = XCreatePixmap (dpy, drawable, iw, ih, depth); - gc = XCreateGC (dpy, p, 0, &gcv); - XPutImage (dpy, p, gc, image, 0, 0, 0, 0, iw, ih); - free (image->data); - image->data = 0; - XDestroyImage (image); - XFreeGC (dpy, gc); + p = XCreatePixmap (dpy, drawable, w, h, depth); + gc = XCreateGC (dpy, p, 0, &gcv); + XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h); + XDestroyImage (image); + XFreeGC (dpy, gc); - if (mask_ret && mask) - { - *mask_ret = (Pixmap) - XCreatePixmapFromBitmapData (dpy, drawable, (char *) mask, - iw, ih, 1L, 0L, 1); - free (mask); - } + if (mask_ret && mask) + { + *mask_ret = (Pixmap) + XCreatePixmapFromBitmapData (dpy, drawable, (char *) mask, + w, h, 1, 0, 1); + free (mask); } + return p; } diff --git a/utils/minixpm.c b/utils/minixpm.c index 997e628..55f25be 100644 --- a/utils/minixpm.c +++ b/utils/minixpm.c @@ -60,7 +60,7 @@ XImage * minixpm_to_ximage (Display *dpy, Visual *visual, Colormap colormap, int depth, unsigned long transparent_color, const char * const * data, - int *width_ret, int *height_ret, + unsigned int *width_ret, unsigned int *height_ret, unsigned long **pixels_ret, int *npixels_ret, unsigned char **mask_ret) { diff --git a/utils/minixpm.h b/utils/minixpm.h index f027894..8777f36 100644 --- a/utils/minixpm.h +++ b/utils/minixpm.h @@ -20,7 +20,8 @@ extern XImage * minixpm_to_ximage (Display *, Visual *, Colormap, int depth, unsigned long transparent_color, const char * const * data, - int *width_ret, int *height_ret, + unsigned int *width_ret, + unsigned int *height_ret, unsigned long **pixels_ret, int *npixels_ret, unsigned char **mask_ret); diff --git a/utils/queue.h b/utils/queue.h new file mode 100644 index 0000000..94bbf23 --- /dev/null +++ b/utils/queue.h @@ -0,0 +1,638 @@ +/* $NetBSD: queue.h,v 1.68 2014/11/19 08:10:01 uebayasi Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _QUEUE_H_ +#define _QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * A singly-linked list is headed by a single forward pointer. The + * elements are singly linked for minimum space and pointer manipulation + * overhead at the expense of O(n) removal for arbitrary elements. New + * elements can be added to the list after an existing element or at the + * head of the list. Elements being removed from the head of the list + * should use the explicit macro for this purpose for optimum + * efficiency. A singly-linked list may only be traversed in the forward + * direction. Singly-linked lists are ideal for applications with large + * datasets and few or no removals or for implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = (head)->slh_first; \ + (var) != SLIST_END(head); \ + (var) = (var)->field.sle_next) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) != SLIST_END(head) && \ + ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) do { \ + (head)->slh_first = SLIST_END(head); \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE_AFTER(slistelm, field) do { \ + (slistelm)->field.sle_next = \ + SLIST_NEXT(SLIST_NEXT((slistelm), field), field); \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while(curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (/*CONSTCOND*/0) + + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods. + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) ((head)->lh_first == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = ((head)->lh_first); \ + (var) != LIST_END(head); \ + (var) = ((var)->field.le_next)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) != LIST_END(head) && \ + ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_MOVE(head1, head2) do { \ + LIST_INIT((head2)); \ + if (!LIST_EMPTY((head1))) { \ + (head2)->lh_first = (head1)->lh_first; \ + LIST_INIT((head1)); \ + } \ +} while (/*CONSTCOND*/0) + +/* + * List functions. + */ +#if defined(QUEUEDEBUG) +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \ + if ((head)->lh_first && \ + (head)->lh_first->field.le_prev != &(head)->lh_first) \ + QUEUEDEBUG_ABORT("LIST_INSERT_HEAD %p %s:%d", (head), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_OP(elm, field) \ + if ((elm)->field.le_next && \ + (elm)->field.le_next->field.le_prev != \ + &(elm)->field.le_next) \ + QUEUEDEBUG_ABORT("LIST_* forw %p %s:%d", (elm), \ + __FILE__, __LINE__); \ + if (*(elm)->field.le_prev != (elm)) \ + QUEUEDEBUG_ABORT("LIST_* back %p %s:%d", (elm), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \ + (elm)->field.le_next = (void *)1L; \ + (elm)->field.le_prev = (void *)1L; +#else +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_LIST_OP(elm, field) +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) +#endif + +#define LIST_INIT(head) do { \ + (head)->lh_first = LIST_END(head); \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + if (((elm)->field.le_next = (listelm)->field.le_next) != \ + LIST_END(head)) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.le_next = (head)->lh_first) != LIST_END(head))\ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (/*CONSTCOND*/0) + +#define LIST_REMOVE(elm, field) do { \ + QUEUEDEBUG_LIST_OP((elm), field) \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ +} while (/*CONSTCOND*/0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ + QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ +} while (/*CONSTCOND*/0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->sqh_first); \ + (var) != SIMPLEQ_END(head); \ + (var) = ((var)->field.sqe_next)) + +#define SIMPLEQ_FOREACH_SAFE(var, head, field, next) \ + for ((var) = ((head)->sqh_first); \ + (var) != SIMPLEQ_END(head) && \ + ((next = ((var)->field.sqe_next)), 1); \ + (var) = (next)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ + == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE(head, elm, type, field) do { \ + if ((head)->sqh_first == (elm)) { \ + SIMPLEQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->sqh_first; \ + while (curelm->field.sqe_next != (elm)) \ + curelm = curelm->field.sqe_next; \ + if ((curelm->field.sqe_next = \ + curelm->field.sqe_next->field.sqe_next) == NULL) \ + (head)->sqh_last = &(curelm)->field.sqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_CONCAT(head1, head2) do { \ + if (!SIMPLEQ_EMPTY((head2))) { \ + *(head1)->sqh_last = (head2)->sqh_first; \ + (head1)->sqh_last = (head2)->sqh_last; \ + SIMPLEQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_LAST(head, type, field) \ + (SIMPLEQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->sqh_last) - offsetof(struct type, field)))) + +/* + * Tail queue definitions. + */ +#define _TAILQ_HEAD(name, type, qual) \ +struct name { \ + qual type *tqh_first; /* first element */ \ + qual type *qual *tqh_last; /* addr of last next element */ \ +} +#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,) + +#define TAILQ_HEAD_INITIALIZER(head) \ + { TAILQ_END(head), &(head).tqh_first } + +#define _TAILQ_ENTRY(type, qual) \ +struct { \ + qual type *tqe_next; /* next element */ \ + qual type *qual *tqe_prev; /* address of previous next element */\ +} +#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,) + +/* + * Tail queue access methods. + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) (NULL) +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) (TAILQ_FIRST(head) == TAILQ_END(head)) + + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->tqh_first); \ + (var) != TAILQ_END(head); \ + (var) = ((var)->field.tqe_next)) + +#define TAILQ_FOREACH_SAFE(var, head, field, next) \ + for ((var) = ((head)->tqh_first); \ + (var) != TAILQ_END(head) && \ + ((next) = TAILQ_NEXT(var, field), 1); (var) = (next)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last));\ + (var) != TAILQ_END(head); \ + (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, prev) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) != TAILQ_END(head) && \ + ((prev) = TAILQ_PREV((var), headname, field), 1); (var) = (prev)) + +/* + * Tail queue functions. + */ +#if defined(QUEUEDEBUG) +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \ + if ((head)->tqh_first && \ + (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \ + QUEUEDEBUG_ABORT("TAILQ_INSERT_HEAD %p %s:%d", (head), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \ + if (*(head)->tqh_last != NULL) \ + QUEUEDEBUG_ABORT("TAILQ_INSERT_TAIL %p %s:%d", (head), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_OP(elm, field) \ + if ((elm)->field.tqe_next && \ + (elm)->field.tqe_next->field.tqe_prev != \ + &(elm)->field.tqe_next) \ + QUEUEDEBUG_ABORT("TAILQ_* forw %p %s:%d", (elm), \ + __FILE__, __LINE__); \ + if (*(elm)->field.tqe_prev != (elm)) \ + QUEUEDEBUG_ABORT("TAILQ_* back %p %s:%d", (elm), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) \ + if ((elm)->field.tqe_next == NULL && \ + (head)->tqh_last != &(elm)->field.tqe_next) \ + QUEUEDEBUG_ABORT("TAILQ_PREREMOVE head %p elm %p %s:%d",\ + (head), (elm), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \ + (elm)->field.tqe_next = (void *)1L; \ + (elm)->field.tqe_prev = (void *)1L; +#else +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) +#define QUEUEDEBUG_TAILQ_OP(elm, field) +#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) +#endif + +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = TAILQ_END(head); \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.tqe_next = (head)->tqh_first) != TAILQ_END(head))\ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \ + (elm)->field.tqe_next = TAILQ_END(head); \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != \ + TAILQ_END(head)) \ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field) \ + QUEUEDEBUG_TAILQ_OP((elm), field) \ + if (((elm)->field.tqe_next) != TAILQ_END(head)) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ +} while (/*CONSTCOND*/0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != \ + TAILQ_END(head)) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ + QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ +} while (/*CONSTCOND*/0) + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first; /* first element */ \ + struct type **stqh_last; /* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue access methods. + */ +#define STAILQ_FIRST(head) ((head)->stqh_first) +#define STAILQ_END(head) NULL +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) +#define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head)) + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_INIT(head) do { \ + (head)->stqh_first = NULL; \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (head)->stqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.stqe_next = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &(elm)->field.stqe_next; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (listelm)->field.stqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if ((head)->stqh_first == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->stqh_first; \ + while (curelm->field.stqe_next != (elm)) \ + curelm = curelm->field.stqe_next; \ + if ((curelm->field.stqe_next = \ + curelm->field.stqe_next->field.stqe_next) == NULL) \ + (head)->stqh_last = &(curelm)->field.stqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define STAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->stqh_first); \ + (var); \ + (var) = ((var)->field.stqe_next)) + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->stqh_last) - offsetof(struct type, field)))) + +#endif /* !_QUEUE_H_ */ diff --git a/utils/textclient-mobile.c b/utils/textclient-mobile.c index 4d0d891..8745cb0 100644 --- a/utils/textclient-mobile.c +++ b/utils/textclient-mobile.c @@ -16,7 +16,7 @@ #include "utils.h" -#if defined(USE_IPHONE) || defined(HAVE_ANDROID) /* whole file */ +#if defined(HAVE_IPHONE) || defined(HAVE_ANDROID) /* whole file */ #include "textclient.h" #include "resources.h" diff --git a/utils/textclient.c b/utils/textclient.c index fe40928..4a4c895 100644 --- a/utils/textclient.c +++ b/utils/textclient.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 2012-2016 Jamie Zawinski +/* xscreensaver, Copyright © 2012-2021 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 @@ -22,7 +22,7 @@ #include "utils.h" -#if !defined(USE_IPHONE) && !defined(HAVE_ANDROID) /* whole file */ +#if !defined(HAVE_IPHONE) && !defined(HAVE_ANDROID) /* whole file */ #include "textclient.h" #include "resources.h" @@ -117,6 +117,72 @@ escape_str (char *s, const char *src) } #endif + +/* Let's see if we're able to fork and exec at all. Thanks, macOS. + */ +static Bool +selftest (void) +{ + static Bool done = False; + pid_t pid; + char buf [255]; + if (done) return True; + + pid = fork (); + switch ((int) pid) + { + case -1: + sprintf (buf, "%s: textclient: selftest: couldn't fork", progname); + perror (buf); + return False; + + case 0: /* child */ + { + char * const av[] = { "/bin/sh", "-c", "true", 0 }; + execvp (av[0], av); + exit (1); /* exits child fork */ + break; + } + + default: /* parent */ + { + int status = -1; + int i; + /* Busy-loops are bad mmmmkayyyy */ + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 100000L; /* 0.1 sec */ + for (i = 0; i < 50; i++) { /* 5 sec max */ + pid_t pid2 = waitpid (pid, &status, 0); + if (pid == pid2) break; + (void) select (0, 0, 0, 0, &tv); + } + + if (status != 0) + { +# ifdef DEBUG + fprintf (stderr, "%s: selftest: textclient: status %d\n", + progname, status); +# endif + return False; + } + else + { +# ifdef DEBUG + fprintf (stderr, "%s: textclient: selftest ok\n", progname); +# endif + done = True; + } + break; + } + } + + return True; +} + + +static void start_timer (text_data *d); + static void launch_text_generator (text_data *d) { @@ -142,6 +208,15 @@ launch_text_generator (text_data *d) char *cmd = s = malloc ((strlen(oprogram)) * 2 + 100); # endif + if (!selftest()) + { + if (!d->out_buffer || !*d->out_buffer) + d->out_buffer = "Can't exec; Gatekeeper problem?\r\n\r\n"; + start_timer (d); + free (cmd); + return; + } + strcpy (s, "( "); strcat (s, oprogram); s += strlen (s); @@ -204,26 +279,39 @@ launch_text_generator (text_data *d) free (text_mode); } - strcpy (s, text_mode_flag); - s += strlen (s); - - if (value_res) + /* If the 'program' resource already has a text-mode option in it, + don't override that! 'gltext' does this. */ + if (! (strstr (cmd, " --date") || + strstr (cmd, " --text") || + strstr (cmd, " --file") || + strstr (cmd, " --url") || + strstr (cmd, " --program"))) { - size_t old_s = s - cmd; - char *value = get_string_resource (d->dpy, value_res, ""); - if (!value) - value = strdup(""); - cmd = realloc(cmd, cmd_capacity + strlen(value) * 2); - s = cmd + old_s; - *s = ' '; - ++s; - s = escape_str(s, value); - free(value); + strcpy (s, text_mode_flag); + s += strlen (s); + + if (value_res) + { + size_t old_s = s - cmd; + char *value = get_string_resource (d->dpy, value_res, ""); + if (!value) + value = strdup(""); + cmd = realloc(cmd, cmd_capacity + strlen(value) * 2); + s = cmd + old_s; + *s = ' '; + ++s; + s = escape_str(s, value); + free(value); + } } # endif /* HAVE_COCOA */ } +# if 1 strcpy (s, " ) 2>&1"); +# else + strcpy (s, " )"); +# endif # ifdef DEBUG fprintf (stderr, "%s: textclient: launch %s: %s\n", progname, @@ -477,6 +565,13 @@ textclient_open (Display *dpy) d->program = get_string_resource (dpy, "program", "Program"); + /* Just in case the resource is blank, e.g. gltext. */ + if (!d->program || !*d->program || !strcmp(d->program, "(default)")) + { + if (d->program) free (d->program); + d->program = strdup ("xscreensaver-text"); + } + # ifdef HAVE_FORKPTY /* Kludge for MacOS standalone mode: see OSX/SaverRunner.m. */ @@ -670,4 +765,4 @@ textclient_putc (text_data *d, XKeyEvent *k) return False; } -#endif /* !USE_IPHONE -- whole file */ +#endif /* !HAVE_IPHONE -- whole file */ diff --git a/utils/textclient.h b/utils/textclient.h index 0fe582d..5be8079 100644 --- a/utils/textclient.h +++ b/utils/textclient.h @@ -14,7 +14,7 @@ #ifndef __TEXTCLIENT_H__ #define __TEXTCLIENT_H__ -# ifdef USE_IPHONE +# ifdef HAVE_IPHONE # undef HAVE_FORKPTY # endif @@ -29,7 +29,7 @@ extern void textclient_reshape (text_data *, extern int textclient_getc (text_data *); extern Bool textclient_putc (text_data *, XKeyEvent *); -# if defined(USE_IPHONE) || defined(HAVE_ANDROID) +# if defined(HAVE_IPHONE) || defined(HAVE_ANDROID) extern char *textclient_mobile_date_string (void); extern char *textclient_mobile_url_string (Display *, const char *url); # endif diff --git a/utils/thread_util.c b/utils/thread_util.c index 71c8633..7d7fd10 100644 --- a/utils/thread_util.c +++ b/utils/thread_util.c @@ -28,13 +28,8 @@ implied warranty. # include #endif -#if HAVE_UNISTD_H -# include -#endif - #if defined __MACH__ && defined __APPLE__ /* OS X, iOS */ # include -# include #endif #include "thread_util.h" @@ -42,238 +37,12 @@ implied warranty. #include "aligned_malloc.h" #include "resources.h" -#define IS_POWER_OF_2(x) ((x) > 0 && !((x) & ((x) - 1))) - -/* - arraysize(a). Also known as countof(x), XtNumber(x), NELEMS(x), LEN(x), - NUMOF(x), ARRAY_SIZE(x), etc., since the fine folks behind C never got - around to including this incredibly useful macro in the standard library, - which is where it belongs. - - Much of the code here assumes that multiple processors in a system all use - the same cache line size...which might be wrong on occasion. -*/ - -#define arraysize(a) (sizeof(a) / sizeof(*(a))) -#define arrayend(a) ((a) + arraysize(a)) - -/* -These numbers are from: -- Linux: arch/(arch name)/include/asm/cache.h, note - L1_CACHE_BYTES/L1_CACHE_SHIFT/SMP_CACHE_BYTES. -- FreeBSD: sys/(sys name)/include/param.h, note - CACHE_LINE_SHIFT/CACHE_LINE_SIZE. - -Preprocessor symbols come from: -- TARGET_CPU_CPP_BUILTINS() in the GNU C preprocessor - -- http://predef.sourceforge.net/ -*/ - -/* -Several architectures need preprocessor symbols. - -Qualcomm Hexagon: 1 << 5 -Imagination Technologies META: 1 << 6 -OpenRISC: 16 (Linux has the cache line size as a todo.) -Unicore: 1 << 5 -*/ - #if HAVE_PTHREAD # if !HAVE_UNISTD_H # error unistd.h must be present whenever pthread.h is. # endif -# if defined __MACH__ && defined __APPLE__ /* OS X, iOS */ -# include /* For TARGET_OS_IPHONE. */ -# ifdef TARGET_OS_IPHONE -# define _CACHE_LINE_SIZE 64 -# endif -# endif - -# if defined __FreeBSD__ && !defined _CACHE_LINE_SIZE -# include -# ifdef CACHE_LINE_SIZE -# define _CACHE_LINE_SIZE CACHE_LINE_SIZE -# endif -# endif - -# if !defined _CACHE_LINE_SIZE -# if defined __alpha || defined __alpha__ -/* DEC Alpha */ -# define _CACHE_LINE_SIZE 64 /* EV6 and above. EV4 and EV5 use 32 bytes. */ -# elif defined __arm__ -/* ARM architecture */ -# define _CACHE_LINE_SIZE (1 << 6) -# elif defined __AVR || defined __AVR__ -/* Atmel AVR32 */ -# define _CACHE_LINE_SIZE (1 << 5) -# elif defined __bfin || defined __BFIN__ -/* Analog Devices Blackfin */ -# define _CACHE_LINE_SIZE (1 << 5) -# elif defined _TMS320C6X || defined __TMS320C6X__ -/* Texas Instruments TMS320C6x */ -# define _CACHE_LINE_SIZE (1 << 7) /* From L2. L1 data cache line is 1 << 6. */ -# elif defined __cris -/* Axis Communications ETRAX CRIS */ -# define _CACHE_LINE_SIZE 32 -# elif defined __ia64__ || defined _IA64 -/* Intel Itanium */ -# define _CACHE_LINE_SIZE (1 << 7) -# elif defined __M32R__ || defined __m32r__ -/* Mitsubishi/Renesas M32R */ -# define _CACHE_LINE_SIZE (1 << 4) -# elif defined __m68k__ || defined M68000 || defined __MC68K__ -/* Motorola 68000 */ -# define _CACHE_LINE_SIZE (1 << 4) -# elif defined __MICROBLAZE__ || defined __microblaze__ -/* Xilinx MicroBlaze */ -# define _CACHE_LINE_SIZE (1 << 5) -# elif defined __mips__ || defined __mips || defined __MIPS__ -/* MIPS */ -# define _CACHE_LINE_SIZE (1 << 6) -# elif defined __mn10300__ || defined __MN10300__ -/* Matsushita/Panasonic MN103 */ -# define _CACHE_LINE_SIZE 32 /* MN103E010 has 16 bytes. */ -# elif defined __hppa || defined __hppa__ -/* Hewlett-Packard PA-RISC */ -# define _CACHE_LINE_SIZE 64 /* PA-RISC 2.0 uses 64 bytes, PA-RISC 1.1 uses 32. */ -# elif defined __powerpc || defined _ARCH_PPC -/* Power Architecture (a.k.a. PowerPC) */ -# define _CACHE_LINE_SIZE (1 << 7) /* Linux has a list of PPC models with associated L1_CACHE_SHIFT values. */ -# elif defined __s390__ || defined __370__ || defined __zarch__ || defined __SYSC_ZARCH__ -/* IBM System/390 */ -# define _CACHE_LINE_SIZE 256 -# elif defined SUNPLUS || defined __SCORE__ || defined __score__ -/* Sunplus S+core */ -# define _CACHE_LINE_SIZE (1 << 4) -# elif defined __sh__ -/* Hitachi SuperH */ -# define _CACHE_LINE_SIZE (1 << 5) /* SH3 and earlier used 1 << 4. */ -# elif defined __sparc__ || defined __sparc -/* SPARC */ -# define _CACHE_LINE_SIZE (1 << 7) /* Linux and FreeBSD disagree as to what this should be. */ -# elif defined __tile__ -/* Tilera TILE series */ -# define _CACHE_LINE_SIZE (1 << 6) /* TILEPro uses different sizes for L1 and L2. */ -# elif defined __i386 || defined __x86_64 -/* x86(-64) */ -# define _CACHE_LINE_SIZE (1 << 7) -# elif defined __xtensa__ || defined __XTENSA__ -/* Cadence Design Systems/Tensilica Xtensa */ -# define _CACHE_LINE_SIZE (1 << 5) /* 1 << 4 on some models. */ -# endif -# endif /* !defined _CACHE_LINE_SIZE */ - -# if defined __NetBSD__ && !defined _CACHE_LINE_SIZE -/* -NetBSD defines COHERENCY_UNIT to be 32 on MIPS, and 64 for all other platforms -- which is wrong. Still, this is what the kernel -uses; if this value didn't work, the system wouldn't run. -*/ -# include -# ifdef COHERENCY_UNIT -# define _CACHE_LINE_SIZE COHERENCY_UNIT -# endif -# endif - -# ifndef _CACHE_LINE_SIZE -# define _CACHE_LINE_SIZE 256 /* Fallback cache line size. */ -# endif - -static unsigned _get_cache_line_size(void) -{ - /* - The general idea: - - Try to get the actual cache line size from the operating system. - - In the interest of keeping things simple, this only checks with - glibc and OS X. - - A few other methods that could be added: - - Query x86 CPUs directly with the CPUID instruction. - - Query various ELF systems through the auxillary vector. - (Power, Alpha, SuperH) - - Query Linux through - /sys/devices/system/cpu/cpu?/cache/index?/coherency_line_size - (x86 only, AFAIK) - - Query Linux through cache_alignment in /proc/cpuinfo - - Query Solaris through PICL. - - If that fails, return a value appropriate for the current CPU - architecture. - - Otherwise, return a sufficiently large number. - */ - - /* - sysconf(3) is not a syscall, it's a glibc call that, for cache line sizes, - uses CPUID on x86 and returns 0 on other platforms. If it were to work on - most other platforms, it would have to get cache information from the - kernel, since that information is usually made available by the processor - only in privileged mode. - https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/i386/sysconf.c;hb=HEAD - */ - - /* uClibc, newlib, dietlibc, musl, Bionic do not have this. */ - -# if HAVE_UNISTD_H && ( \ - defined _SC_LEVEL1_DCACHE_LINESIZE || \ - defined _SC_LEVEL2_CACHE_LINESIZE || \ - defined _SC_LEVEL3_CACHE_LINESIZE || \ - defined _SC_LEVEL4_CACHE_LINESIZE) - { - static const int names[] = - { -# ifdef _SC_LEVEL1_DCACHE_LINESIZE - _SC_LEVEL1_DCACHE_LINESIZE, -# endif -# ifdef _SC_LEVEL2_CACHE_LINESIZE - _SC_LEVEL2_CACHE_LINESIZE, -# endif -# ifdef _SC_LEVEL3_CACHE_LINESIZE - _SC_LEVEL3_CACHE_LINESIZE, -# endif -# ifdef _SC_LEVEL4_CACHE_LINESIZE - _SC_LEVEL4_CACHE_LINESIZE -# endif - }; - - const int *name; - long result = 0; - - for(name = names; name != arrayend(names); ++name) - { - long sysconf_result = sysconf(*name); /* Can return -1 or 0 on - failure. */ - - if(sysconf_result > result) - result = sysconf_result; - } - - if(result) - return result; - - /* Currently, this fails for every platform that isn't x86. Perhaps - future versions will support other processors? */ - } -# endif - -# if defined __MACH__ && defined __APPLE__ - { - uint32_t result; /* sysctl.h says that hw.cachelinesize is a - CTLTYPE_INT. */ - size_t size = sizeof(result); - static const int name[] = {CTL_HW, HW_CACHELINE}; - - if(!sysctl((int *)name, 2, &result, &size, NULL, 0)) /* (int *) is for OS X. */ - { - assert(size == sizeof(result)); - return result; - }; - } -# endif - - /* Guess based on the CPU type. */ - return _CACHE_LINE_SIZE; -} - const pthread_mutex_t mutex_initializer = # if defined _GNU_SOURCE && !defined NDEBUG PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP @@ -304,15 +73,9 @@ int threads_available(Display *dpy) if(_has_pthread >= 0) { if(get_boolean_resource(dpy, "useThreads", "Boolean")) - { - _cache_line_size = _get_cache_line_size(); - assert(_cache_line_size >= sizeof(void *)); - assert(IS_POWER_OF_2(_cache_line_size)); - } + _cache_line_size = get_cache_line_size(); else - { _has_pthread = -1; - } } } diff --git a/utils/thread_util.h b/utils/thread_util.h index 6dff8de..a8faf2d 100644 --- a/utils/thread_util.h +++ b/utils/thread_util.h @@ -161,7 +161,7 @@ unsigned thread_memory_alignment(Display *dpy); /* int thread_malloc(void **ptr, Display *dpy, unsigned size); */ #define thread_malloc(ptr, dpy, size) \ - (aligned_malloc((ptr), thread_memory_alignment(dpy), (size))) + (aligned_malloc((ptr), 0, (size))) /* This simply does a malloc aligned to thread_memory_alignment(). See diff --git a/utils/usleep.h b/utils/usleep.h index 3225d66..f32a9c9 100644 --- a/utils/usleep.h +++ b/utils/usleep.h @@ -12,10 +12,6 @@ #ifndef __SCREENHACK_USLEEP_H__ #define __SCREENHACK_USLEEP_H__ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - #ifdef HAVE_UNISTD_H # include #endif diff --git a/utils/utf8wc.c b/utils/utf8wc.c index c948f22..b445f30 100644 --- a/utils/utf8wc.c +++ b/utils/utf8wc.c @@ -266,7 +266,7 @@ utf8_split (const char *string, int *length_ret) unsigned long uc; long len2 = utf8_decode (in, len, &uc); char tmp[10]; - strncpy (tmp, (char *) in, len2); + memcpy (tmp, (char *) in, len2); tmp[len2] = 0; ret[i++] = strdup (tmp); in += len2; @@ -299,8 +299,8 @@ utf8_split (const char *string, int *length_ret) long L1 = strlen(ret[i-2]); long L2 = strlen(ret[i-1]); char *s2 = (char *) malloc (L1 + L2 + 1); - strncpy (s2, ret[i-2], L1); - strncpy (s2 + L1, ret[i-1], L2); + memcpy (s2, ret[i-2], L1); + memcpy (s2 + L1, ret[i-1], L2); s2[L1 + L2] = 0; free (ret[i-2]); ret[i-2] = s2; diff --git a/utils/version.h b/utils/version.h index 7a20c66..8e38585 100644 --- a/utils/version.h +++ b/utils/version.h @@ -1,2 +1,4 @@ static const char screensaver_id[] = - "@(#)xscreensaver 5.44 (20-Mar-2020), by Jamie Zawinski (jwz@jwz.org)"; + "@(#)xscreensaver 6.00 (01-Apr-2021), by Jamie Zawinski (jwz@jwz.org)"; +#define XSCREENSAVER_VERSION "6.00" +#define XSCREENSAVER_RELEASED 1617303600 diff --git a/utils/visual-gl.c b/utils/visual-gl.c index c4b940b..cca4ecf 100644 --- a/utils/visual-gl.c +++ b/utils/visual-gl.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 1999-2018 by Jamie Zawinski +/* xscreensaver, Copyright © 1999-2021 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 @@ -21,21 +21,78 @@ */ #include "utils.h" -#include "visual.h" #include "resources.h" #ifdef HAVE_GL # include -# include +# ifdef HAVE_EGL +# include +# include +# else +# include +# endif #endif /* HAVE_GL */ +#include "visual.h" /* after EGL/egl.h */ + +#undef countof +#define countof(x) (sizeof(x)/sizeof(*(x))) + + extern char *progname; + Visual * get_gl_visual (Screen *screen) { #ifdef HAVE_GL + Display *dpy = DisplayOfScreen (screen); + +# ifdef HAVE_EGL + + Visual *v; + EGLint vid; + EGLDisplay *egl_display; + EGLConfig egl_config = 0; + int egl_major = -1, egl_minor = -1; + + /* This is re-used, no need to close it. */ + egl_display = eglGetDisplay ((EGLNativeDisplayType) dpy); + if (!egl_display) + { + fprintf (stderr, "%s: eglGetDisplay failed\n", progname); + return 0; + } + + if (! eglInitialize (egl_display, &egl_major, &egl_minor)) + { + fprintf (stderr, "%s: eglInitialize failed\n", progname); + abort(); + } + + /* Get the best EGL config on any visual, then see what visual it used. */ + get_egl_config (dpy, egl_display, -1, &egl_config); + if (!egl_config) abort(); + + if (!eglGetConfigAttrib (egl_display, egl_config, + EGL_NATIVE_VISUAL_ID, &vid)) + { + fprintf (stderr, "%s: EGL: no native visual ID\n", progname); + abort(); + } + + v = id_to_visual (screen, vid); + if (!v) + { + fprintf (stderr, "%s: EGL: no X11 visual 0x%x\n", progname, vid); + abort(); + } + + return v; + +# else /* !HAVE_EGL -- GLX */ + int screen_num = screen_number (screen); # define R GLX_RED_SIZE @@ -61,7 +118,6 @@ get_gl_visual (Screen *screen) # define SM GL_SAMPLES # endif - int attrs[][40] = { # ifdef SB /* rgba double stencil multisample */ { GLX_RGBA, R,8, G,8, B,8, A,8, D,8, DB, ST,1, SB,1, SM,8, 0 }, @@ -107,12 +163,134 @@ get_gl_visual (Screen *screen) return v; } } + +# endif /* !HAVE_EGL -- GLX */ #endif /* !HAVE_GL */ return 0; } +#ifdef HAVE_EGL +/* An X11 "visual" is an object that encapsulates the available display + formats: color mapping, depth and so on. So is a GLE "config". + So why isn't there a one-to-one mapping between visuals and configs? + Once again, I can only assume the answer is that the people responsible + for every post-2002 version of the OpenGL specification are incompetent. + + Under GLX, there will be multiple X11 visuals that appear (and behave) + identically to X11 programs, code but that have different GL parameters. + Two RGB visuals might have different alpha, depth, or stencil sizes, + for example, that would matter to a GL program but not to an X11 program. + + But EGL has dozens of 'configs' for each visual, so we can't just pass + around a Window and a Visual to describe the display parameters: we need + to pass around the 'config' as well. Or, do what we do here, have both + sides use the same code to convert a visual to the best 'config'. + (It goes down a list of parameter settings, from higher quality to lower, + and takes the first config that matches.) + + SGI solved this problem in like 1988, and like so many things, GLES went + and fucked it all up again. + */ +void +get_egl_config (Display *dpy, EGLDisplay *egl_display, + EGLint x11_visual_id, EGLConfig *egl_config) +{ +# define COMMON \ + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, \ + EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER +# define R EGL_RED_SIZE +# define G EGL_GREEN_SIZE +# define B EGL_BLUE_SIZE +# define A EGL_ALPHA_SIZE +# define D EGL_DEPTH_SIZE +# define I EGL_BUFFER_SIZE +# define ST EGL_STENCIL_SIZE +# define SB EGL_SAMPLE_BUFFERS +# define SM EGL_SAMPLES + + const EGLint templates[][40] = { +# ifdef SB + { COMMON, R,8, G,8, B,8, A,8, D,8, ST,1, SB,1, SM,8, EGL_NONE }, + { COMMON, R,8, G,8, B,8, A,8, D,8, ST,1, SB,1, SM,6, EGL_NONE }, + { COMMON, R,8, G,8, B,8, A,8, D,8, ST,1, SB,1, SM,4, EGL_NONE }, + { COMMON, R,8, G,8, B,8, A,8, D,8, ST,1, SB,1, SM,2, EGL_NONE }, +# define SB_COUNT 4 /* #### Kludgey count of preceeding lines! */ +# endif + { COMMON, R,8, G,8, B,8, A,8, D,8, ST,1, EGL_NONE }, /* rgba stencil */ + { COMMON, R,8, G,8, B,8, D,8, ST,1, EGL_NONE }, /* rgb stencil */ + { COMMON, R,4, G,4, B,4, D,4, ST,1, EGL_NONE }, + { COMMON, R,2, G,2, B,2, D,2, ST,1, EGL_NONE }, + { COMMON, R,8, G,8, B,8, A,8, D,8, EGL_NONE }, /* rgba */ + { COMMON, R,8, G,8, B,8, D,8, EGL_NONE }, /* rgb */ + { COMMON, R,4, G,4, B,4, D,4, EGL_NONE }, + { COMMON, R,2, G,2, B,2, D,2, EGL_NONE }, + { COMMON, R,1, G,1, B,1, D,1, EGL_NONE } /* monochrome */ + }; + EGLint attrs[40]; + EGLint nconfig; + int i, j, k, iter, pass; + Bool glslp; + + i = 0; +# ifdef SB + if (! get_boolean_resource (dpy, "multiSample", "MultiSample")) + i = SB_COUNT; /* skip over the multibuffer entries in 'attrs' */ +# endif /* SB */ + + glslp = get_boolean_resource (dpy, "prefersGLSL", "PrefersGLSL"); + iter = (glslp ? 2 : 1); + + *egl_config = 0; + for (pass = 0; pass < iter; pass++) + { + for (; i < countof(templates); i++) + { + for (j = 0, k = 0; templates[i][j] != EGL_NONE; j += 2) + { + attrs[k++] = templates[i][j]; + attrs[k++] = templates[i][j+1]; + } + + attrs[k++] = EGL_RENDERABLE_TYPE; +# ifdef HAVE_GLES3 + if (glslp && pass == 0) + attrs[k++] = EGL_OPENGL_ES3_BIT; + else + attrs[k++] = EGL_OPENGL_ES_BIT; +# elif defined(HAVE_JWZGLES) + attrs[k++] = EGL_OPENGL_ES_BIT; +# else + attrs[k++] = EGL_OPENGL_BIT; +# endif + + if (x11_visual_id != -1) + { + attrs[k++] = EGL_NATIVE_VISUAL_ID; + attrs[k++] = x11_visual_id; + } + + attrs[k++] = EGL_NONE; + + nconfig = -1; + if (eglChooseConfig (egl_display, attrs, egl_config, 1, &nconfig) + && nconfig == 1) + break; + } + if (i < countof(templates)) + break; + } + + if (! *egl_config) + { + fprintf (stderr, "%s: eglChooseConfig: no configs found\n", progname); + abort(); + } +} +#endif /* HAVE_EGL */ + + void describe_gl_visual (FILE *f, Screen *screen, Visual *visual, Bool private_cmap_p) @@ -121,8 +299,13 @@ describe_gl_visual (FILE *f, Screen *screen, Visual *visual, #ifdef HAVE_GL { +# ifdef HAVE_EGL + EGLDisplay *egl_display; + EGLConfig config = 0; +# else /* !HAVE_EGL -- GLX */ int status; int value = False; +# endif /* !HAVE_EGL -- GLX */ Display *dpy = DisplayOfScreen (screen); XVisualInfo vi_in, *vi_out; @@ -134,6 +317,108 @@ describe_gl_visual (FILE *f, Screen *screen, Visual *visual, &vi_in, &out_count); if (! vi_out) abort (); +# ifdef HAVE_EGL + + /* This is re-used, no need to close it. */ + egl_display = eglGetPlatformDisplay (EGL_PLATFORM_X11_KHR, + (EGLNativeDisplayType) dpy, NULL); + if (!egl_display) + { + fprintf (stderr, "%s: eglGetPlatformDisplay failed\n", progname); + return; + } + + get_egl_config (dpy, egl_display, vi_out->visualid, &config); + if (!config) + { + fprintf (stderr, "%s: no matching EGL config for X11 visual 0x%lx\n", + progname, vi_out->visualid); + abort(); + } + + { + int i; + const struct { int hexp; EGLint i; const char *s; } fields[] = { + { 1, EGL_CONFIG_ID, "config ID:" }, + { 1, EGL_CONFIG_CAVEAT, "caveat:" }, + { 1, EGL_CONFORMANT, "conformant:" }, + { 0, EGL_COLOR_BUFFER_TYPE, "buffer type:" }, + { 0, EGL_RED_SIZE, "color size:" }, + { 0, EGL_TRANSPARENT_RED_VALUE, "transparency:" }, + { 0, EGL_BUFFER_SIZE, "buffer size:" }, + { 0, EGL_DEPTH_SIZE, "depth size:" }, + { 0, EGL_LUMINANCE_SIZE, "lum size:" }, + { 0, EGL_STENCIL_SIZE, "stencil size:" }, + { 0, EGL_ALPHA_MASK_SIZE, "alpha mask:" }, + { 0, EGL_LEVEL, "level:" }, + { 0, EGL_SAMPLES, "samples:" }, + { 0, EGL_SAMPLE_BUFFERS, "sample bufs:" }, + { 0, EGL_NATIVE_RENDERABLE, "native render:" }, + { 1, EGL_NATIVE_VISUAL_TYPE, "native type:" }, + { 1, EGL_RENDERABLE_TYPE, "render type:" }, + { 0, EGL_SURFACE_TYPE, "surface type:" }, + { 0, EGL_BIND_TO_TEXTURE_RGB, "bind RGB:" }, + { 0, EGL_BIND_TO_TEXTURE_RGBA, "bind RGBA:" }, + { 0, EGL_MAX_PBUFFER_WIDTH, "buffer width:" }, + { 0, EGL_MAX_PBUFFER_HEIGHT, "buffer height:" }, + { 0, EGL_MAX_PBUFFER_PIXELS, "buffer pixels:" }, + { 0, EGL_MAX_SWAP_INTERVAL, "max swap:" }, + { 0, EGL_MIN_SWAP_INTERVAL, "min swap:" }, + }; + EGLint r=0, g=0, b=0, a=0, tt=0, tr=0, tg=0, tb=0; + eglGetConfigAttrib (egl_display, config, EGL_RED_SIZE, &r); + eglGetConfigAttrib (egl_display, config, EGL_GREEN_SIZE, &g); + eglGetConfigAttrib (egl_display, config, EGL_BLUE_SIZE, &b); + eglGetConfigAttrib (egl_display, config, EGL_ALPHA_SIZE, &a); + eglGetConfigAttrib (egl_display, config, EGL_TRANSPARENT_TYPE, &tt); + eglGetConfigAttrib (egl_display, config, EGL_TRANSPARENT_RED_VALUE, &tr); + eglGetConfigAttrib (egl_display, config, EGL_TRANSPARENT_GREEN_VALUE,&tg); + eglGetConfigAttrib (egl_display, config, EGL_TRANSPARENT_BLUE_VALUE, &tb); + for (i = 0; i < countof(fields); i++) + { + EGLint v = 0; + char s[100]; + eglGetConfigAttrib (egl_display, config, fields[i].i, &v); + if (fields[i].i == EGL_RED_SIZE) + sprintf (s, "%d, %d, %d, %d", r, g, b, a); + else if (fields[i].i == EGL_TRANSPARENT_RED_VALUE && tt != EGL_NONE) + sprintf (s, "%d, %d, %d", tr, tg, tb); + else if (fields[i].i == EGL_CONFIG_CAVEAT) + strcpy (s, (v == EGL_NONE ? "none" : + v == EGL_SLOW_CONFIG ? "slow" : +# ifdef EGL_NON_CONFORMANT + v == EGL_NON_CONFORMANT ? "non-conformant" : +# endif + "???")); + else if (fields[i].i == EGL_COLOR_BUFFER_TYPE) + strcpy (s, (v == EGL_RGB_BUFFER ? "RGB" : + v == EGL_LUMINANCE_BUFFER ? "luminance" : + "???")); + else if (fields[i].i == EGL_CONFORMANT || + fields[i].i == EGL_RENDERABLE_TYPE) + { + sprintf (s, "0x%02x", v); + if (v & EGL_OPENGL_BIT) strcat (s, " OpenGL"); + if (v & EGL_OPENGL_ES_BIT) strcat (s, " GLES-1.x"); + if (v & EGL_OPENGL_ES2_BIT) strcat (s, " GLES-2.0"); +# ifdef EGL_OPENGL_ES3_BIT + if (v & EGL_OPENGL_ES3_BIT) strcat (s, " GLES-3.0"); +# endif + if (v & EGL_OPENVG_BIT) strcat (s, " OpenVG"); + } + else if (fields[i].hexp) + sprintf (s, "0x%02x", v); + else if (v) + sprintf (s, "%d", v); + else + *s = 0; + + if (*s) fprintf (f, " EGL %-14s %s\n", fields[i].s, s); + } + } + +# else /* !HAVE_EGL -- GLX */ + status = glXGetConfig (dpy, vi_out, GLX_USE_GL, &value); if (status == GLX_NO_EXTENSION) @@ -146,7 +431,7 @@ describe_gl_visual (FILE *f, Screen *screen, Visual *visual, if (!glXGetConfig (dpy, vi_out, GLX_LEVEL, &value) && value != 0) - printf (" GLX level: %d\n", value); + fprintf (f, " GLX level: %d\n", value); if (!glXGetConfig (dpy, vi_out, GLX_RGBA, &value) && value) { @@ -155,22 +440,22 @@ describe_gl_visual (FILE *f, Screen *screen, Visual *visual, glXGetConfig (dpy, vi_out, GLX_GREEN_SIZE, &g); glXGetConfig (dpy, vi_out, GLX_BLUE_SIZE, &b); glXGetConfig (dpy, vi_out, GLX_ALPHA_SIZE, &a); - printf (" GLX type: RGBA (%2d, %2d, %2d, %2d)\n", - r, g, b, a); + fprintf (f, " GLX type: RGBA (%2d, %2d, %2d, %2d)\n", + r, g, b, a); r=0, g=0, b=0, a=0; glXGetConfig (dpy, vi_out, GLX_ACCUM_RED_SIZE, &r); glXGetConfig (dpy, vi_out, GLX_ACCUM_GREEN_SIZE, &g); glXGetConfig (dpy, vi_out, GLX_ACCUM_BLUE_SIZE, &b); glXGetConfig (dpy, vi_out, GLX_ACCUM_ALPHA_SIZE, &a); - printf (" GLX accum: RGBA (%2d, %2d, %2d, %2d)\n", - r, g, b, a); + fprintf (f, " GLX accum: RGBA (%2d, %2d, %2d, %2d)\n", + r, g, b, a); } else { value = 0; glXGetConfig (dpy, vi_out, GLX_BUFFER_SIZE, &value); - printf (" GLX type: indexed (%d)\n", value); + fprintf (f, " GLX type: indexed (%d)\n", value); } # ifndef GLX_NONE_EXT /* Hooray for gratuitious name changes. */ @@ -189,36 +474,36 @@ describe_gl_visual (FILE *f, Screen *screen, Visual *visual, if (!glXGetConfig (dpy, vi_out, GLX_VISUAL_CAVEAT_EXT, &value) && value != GLX_NONE_EXT) # ifdef GLX_NON_CONFORMANT_EXT - printf (" GLX rating: %s\n", - (value == GLX_NONE_EXT ? "none" : - value == GLX_SLOW_VISUAL_EXT ? "slow" : - value == GLX_NON_CONFORMANT_EXT ? "non-conformant" : - "???")); + fprintf (f, " GLX rating: %s\n", + (value == GLX_NONE_EXT ? "none" : + value == GLX_SLOW_VISUAL_EXT ? "slow" : + value == GLX_NON_CONFORMANT_EXT ? "non-conformant" : + "???"); # else - printf (" GLX rating: %s\n", - (value == GLX_NONE_EXT ? "none" : - value == GLX_SLOW_VISUAL_EXT ? "slow" : - "???")); + fprintf (f, " GLX rating: %s\n", + (value == GLX_NONE_EXT ? "none" : + value == GLX_SLOW_VISUAL_EXT ? "slow" : + "???")); # endif /* GLX_NON_CONFORMANT_EXT */ # endif /* GLX_VISUAL_CAVEAT_EXT */ if (!glXGetConfig (dpy, vi_out, GLX_DOUBLEBUFFER, &value)) - printf (" GLX double-buffer: %s\n", (value ? "yes" : "no")); + fprintf (f, " GLX double-buffer: %s\n", (value ? "yes" : "no")); if (!glXGetConfig (dpy, vi_out, GLX_STEREO, &value) && value) - printf (" GLX stereo: %s\n", (value ? "yes" : "no")); + fprintf (f, " GLX stereo: %s\n", (value ? "yes" : "no")); if (!glXGetConfig (dpy, vi_out, GLX_AUX_BUFFERS, &value) && value != 0) - printf (" GLX aux buffers: %d\n", value); + fprintf (f, " GLX aux buffers: %d\n", value); if (!glXGetConfig (dpy, vi_out, GLX_DEPTH_SIZE, &value)) - printf (" GLX depth size: %d\n", value); + fprintf (f, " GLX depth size: %d\n", value); if (!glXGetConfig (dpy, vi_out, GLX_STENCIL_SIZE, &value) && value != 0) - printf (" GLX stencil size: %d\n", value); + fprintf (f, " GLX stencil size: %d\n", value); # ifdef SB /* GL_SAMPLE_BUFFERS || GLX_SAMPLE_BUFFERS_* */ if (!glXGetConfig (dpy, vi_out, SB, &value) && @@ -226,7 +511,7 @@ describe_gl_visual (FILE *f, Screen *screen, Visual *visual, { int bufs = value; if (!glXGetConfig (dpy, vi_out, SM, &value)) - printf (" GLX multisample: %d, %d\n", bufs, value); + fprintf (f, " GLX multisample: %d, %d\n", bufs, value); } # endif /* GL_SAMPLE_BUFFERS || GLX_SAMPLE_BUFFERS_* */ @@ -234,12 +519,12 @@ describe_gl_visual (FILE *f, Screen *screen, Visual *visual, value != GLX_NONE_EXT) { if (value == GLX_NONE_EXT) - printf (" GLX transparency: none\n"); + fprintf (f, " GLX transparency: none\n"); else if (value == GLX_TRANSPARENT_INDEX_EXT) { if (!glXGetConfig (dpy, vi_out, GLX_TRANSPARENT_INDEX_VALUE_EXT, &value)) - printf (" GLX transparency: indexed (%d)\n", value); + fprintf (f, " GLX transparency: indexed (%d)\n", value); } else if (value == GLX_TRANSPARENT_RGB_EXT) { @@ -248,10 +533,11 @@ describe_gl_visual (FILE *f, Screen *screen, Visual *visual, glXGetConfig (dpy, vi_out, GLX_TRANSPARENT_GREEN_VALUE_EXT, &g); glXGetConfig (dpy, vi_out, GLX_TRANSPARENT_BLUE_VALUE_EXT, &b); glXGetConfig (dpy, vi_out, GLX_TRANSPARENT_ALPHA_VALUE_EXT, &a); - printf (" GLX transparency: RGBA (%2d, %2d, %2d, %2d)\n", - r, g, b, a); + fprintf (f, " GLX transparency: RGBA (%2d, %2d, %2d, %2d)\n", + r, g, b, a); } } +# endif /* !HAVE_EGL */ } #endif /* HAVE_GL */ } @@ -262,13 +548,15 @@ validate_gl_visual (FILE *out, Screen *screen, const char *window_desc, Visual *visual) { #ifdef HAVE_GL +# ifndef HAVE_EGL int status; int value = False; + unsigned int id; +# endif Display *dpy = DisplayOfScreen (screen); XVisualInfo vi_in, *vi_out; int out_count; - unsigned int id; vi_in.screen = screen_number (screen); vi_in.visualid = XVisualIDFromVisual (visual); @@ -276,6 +564,7 @@ validate_gl_visual (FILE *out, Screen *screen, const char *window_desc, &vi_in, &out_count); if (! vi_out) abort (); +# ifndef HAVE_EGL status = glXGetConfig (dpy, vi_out, GLX_USE_GL, &value); id = (unsigned int) vi_out->visualid; @@ -295,6 +584,7 @@ validate_gl_visual (FILE *out, Screen *screen, const char *window_desc, return False; } else +# endif /* !HAVE_EGL */ { return True; } diff --git a/utils/visual.h b/utils/visual.h index 18ff7a6..4fd1b95 100644 --- a/utils/visual.h +++ b/utils/visual.h @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 1993-2014 by Jamie Zawinski +/* xscreensaver, Copyright © 1993-2021 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 @@ -33,4 +33,8 @@ extern Visual *get_gl_visual (Screen *); extern void describe_gl_visual (FILE *, Screen *, Visual *, Bool priv_cmap_p); extern Bool validate_gl_visual (FILE *, Screen *, const char *, Visual *); +#ifdef __egl_h_ /* EGL/egl.h included */ +extern void get_egl_config (Display *, EGLDisplay *, EGLint vid, EGLConfig *); +#endif + #endif /* __VISUAL_H__ */ diff --git a/utils/vms-gtod.c b/utils/vms-gtod.c deleted file mode 100644 index 7b1df5a..0000000 --- a/utils/vms-gtod.c +++ /dev/null @@ -1,31 +0,0 @@ -/* - * UNIX-style Time Functions, by pmoreau@cena.dgac.fr - * (picked up from XVMSUTILS unix emulation routines for VMS by - * Trevor Taylor, Patrick Mahans and Martin P.J. Zinser) - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation. No representations are made about the suitability of this - * software for any purpose. It is provided "as is" without express or - * implied warranty. - */ -#include -#include -#include -#include "vms-gtod.h" - -/* - * gettimeofday(2) - Returns the current time - */ - -int gettimeofday(tv) -struct timeval *tv; -{ - timeb_t tmp_time; - ftime(&tmp_time); - tv->tv_sec = tmp_time.time; - tv->tv_usec = tmp_time.millitm * 1000; - return (0); -} diff --git a/utils/vms-gtod.h b/utils/vms-gtod.h deleted file mode 100644 index ffd6586..0000000 --- a/utils/vms-gtod.h +++ /dev/null @@ -1,85 +0,0 @@ -/* @(#)time.h 2.9 87/01/17 SMI; from UCB 7.1 6/4/86 */ - -/* - Definitions of various structures used on UNIX for - time-related syscalls. -*/ - -/* - * Copyright (c) 1982, 1986 Regents of the University of California. - * All rights reserved. The Berkeley software License Agreement - * specifies the terms and conditions for redistribution. - */ - -#ifndef _VMS_GTOD_ -#define _VMS_GTOD_ - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Structure returned by gettimeofday(2) system call, - * and used in other calls. - */ -#ifndef __DECC -struct timeval -{ - long tv_sec; /* seconds */ - long tv_usec; /* and microseconds */ -}; -#else -#if __DECC_VER < 50200000 -struct timeval -{ - long tv_sec; /* seconds */ - long tv_usec; /* and microseconds */ -}; -#endif /* __DECC_VER */ -#endif /* __DECC */ - -/* - * Operations on timevals. - * - * NB: timercmp does not work for >= or <=. - */ -#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) -#define timercmp(tvp, uvp, cmp) \ - ((tvp)->tv_sec cmp (uvp)->tv_sec || \ - (tvp)->tv_sec == (uvp)->tv_sec && (tvp)->tv_usec cmp (uvp)->tv_usec) -#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 - -/* - * Names of the interval timers, and structure - * defining a timer setting. - */ -#define ITIMER_REAL 0 -#define ITIMER_VIRTUAL 1 -#define ITIMER_PROF 2 - -#ifndef __DECC -struct itimerval -{ - struct timeval it_interval; /* timer interval */ - struct timeval it_value; /* current value */ -}; -#else -#if __DECC_VER < 50200000 -struct itimerval -{ - struct timeval it_interval; /* timer interval */ - struct timeval it_value; /* current value */ -}; -#endif /* __DECC_VER */ -#endif /* __DECC */ - -#ifndef KERNEL -#include -#endif - -#ifdef __cplusplus -} -#endif - -#endif /*!_VMS_GTOD_*/ - diff --git a/utils/vms-strdup.c b/utils/vms-strdup.c deleted file mode 100644 index 1afc257..0000000 --- a/utils/vms-strdup.c +++ /dev/null @@ -1,25 +0,0 @@ -/* - * strdup.c - * - * Simple version of strdup for machines without it (ie DEC Ultrix 4.2) - * Apparently VMS only got strdup in 1995 (v5.2...) - * - * By David Chatterton - * 29 July 1993 - * - * You can do anything you like to this... :) - * I've stolen it from xpilot and added it to the xvmstuils MPJZ ;-) - */ - -#if (__VMS_VER < 70000000) -#include -#include - -char* strdup (const char* s1) -{ - char* s2; - if (s2 = (char*)malloc(strlen(s1)+1)) - strcpy(s2,s1); - return s2; -} -#endif diff --git a/utils/xdbe.c b/utils/xdbe.c index d62183e..347e625 100644 --- a/utils/xdbe.c +++ b/utils/xdbe.c @@ -18,13 +18,10 @@ #include "utils.h" #include "xdbe.h" #include "resources.h" /* for get_string_resource() */ +#include "xmu.h" /* #define DEBUG */ -#ifdef DEBUG -# include -#endif - extern char *progname; #ifdef HAVE_DOUBLE_BUFFER_EXTENSION /* whole file */ diff --git a/utils/xft.c b/utils/xft.c index 9245752..6decd22 100644 --- a/utils/xft.c +++ b/utils/xft.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 2014-2018 Jamie Zawinski +/* xscreensaver, Copyright © 2014-2021 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 @@ -37,6 +37,50 @@ struct _XftDraw { }; +Display * +XftDrawDisplay (XftDraw *draw) +{ + return draw->dpy; +} + + +Bool +XftDrawSetClipRectangles (XftDraw *draw, int x, int y, + _Xconst XRectangle *rects, int n) +{ + if (!n) + return XSetClipMask (draw->dpy, draw->gc, 0); +# if 0 + else + /* #### unimplemented in jwxyz, so clipping is a no-op */ + return XSetClipRectangles (draw->dpy, draw->gc, x, y, + (XRectangle *) rects, /* discard const */ + n, Unsorted); +# else + return False; +# endif +} + + +Bool +XftDrawSetClip (XftDraw *draw, Region region) +{ + if (!region) + return XSetClipMask (draw->dpy, draw->gc, 0); +# if 0 + else + /* #### unimplemented in jwxyz, so clipping is a no-op */ + { + XRectangle rect; /* #### untested */ + XClipBox (region, &rect); /* #### unimplemented in jwxyz */ + return XftDrawSetClipRectangles (draw, 0, 0, &rect, 1); + } +# else + return False; +# endif +} + + XftFont * XftFontOpenXlfd (Display *dpy, int screen, _Xconst char *xlfd) { @@ -60,8 +104,9 @@ XftFontOpenXlfd (Display *dpy, int screen, _Xconst char *xlfd) { unsigned i; - // In the event of -*-random-* (under JWXYZ), get the actual XLFD, - // otherwise we'll get another random font that doesn't match ff->xfont. + /* In the event of -*-random-* (under JWXYZ), get the actual XLFD, + otherwise we'll get another random font that doesn't match ff->xfont. + */ char *xlfd_resolved = NULL; char **missing_charset_list_return; @@ -111,6 +156,62 @@ XftFontOpenXlfd (Display *dpy, int screen, _Xconst char *xlfd) } +/* Very approximately convert an XFT-style name to an XLFD style name + and then call XftFontOpenXlfd on that. + */ +XftFont * +XftFontOpenName (Display *dpy, int screen, _Xconst char *xft_name) +{ + XftFont *font; + char *name = strdup (xft_name); + char *xlfd = 0; + char *s, *b, *i, *o, *c; + int size; + char dummy; + + /* Ignore any ":keywords=" */ + c = strchr (name, ':'); + if (c) *c = 0; + + /* Downcase ASCII */ + for (s = name; *s; s++) + if (*s >= 'A' && *s <= 'Z') + *s += 'a'-'A'; + + /* "Family-NN" */ + s = strrchr (name, '-'); + if (!s) goto FAIL; + if (1 != sscanf (s+1, " %d %c", &size, &dummy)) goto FAIL; + if (size <= 0) goto FAIL; + *s = 0; + + /* "Family Bold", etc. */ + b = strstr (name, " bold"); + i = strstr (name, " italic"); + o = strstr (name, " oblique"); + if (b) *b = 0; + if (i) *i = 0; + if (o) *o = 0; + + xlfd = (char *) malloc (strlen(name) + 80); + sprintf (xlfd, "-*-%s-%s-%s-*-*-*-%d-*-*-*-*-*-*", + name, + (b ? "bold" : "medium"), + (i ? "i" : o ? "o" : "r"), + size * 10); + font = XftFontOpenXlfd (dpy, screen, xlfd); + free (name); + free (xlfd); + return font; + + FAIL: + fprintf (stderr, "%s: XFT: unparsable: \"%s\"\n", progname, xft_name); + if (name) free (name); + if (xlfd) free (xlfd); + return 0; +} + + void XftFontClose (Display *dpy, XftFont *font) { @@ -132,7 +233,8 @@ XftColorAllocName (Display *dpy, XftColor *result) { XColor color; - if (!dpy || !visual || !name || !result) abort(); + if (!dpy || !visual || !result) abort(); + if (!name || !*name) name = "#FFFFFF"; if (! XParseColor (dpy, cmap, name, &color)) { diff --git a/utils/xft.h b/utils/xft.h index 385e28b..0da0188 100644 --- a/utils/xft.h +++ b/utils/xft.h @@ -118,7 +118,7 @@ typedef unsigned char FcChar8; XftFont *XftFontOpenXlfd (Display *dpy, int screen, _Xconst char *xlfd); -#define XftFontOpenName XftFontOpenXlfd +XftFont *XftFontOpenName (Display *dpy, int screen, _Xconst char *name); void XftFontClose (Display *dpy, XftFont *font); @@ -143,7 +143,10 @@ XftDraw *XftDrawCreate (Display *dpy, Drawable drawable, Visual *visual, Colormap colormap); - +Display *XftDrawDisplay (XftDraw *); +Bool XftDrawSetClipRectangles (XftDraw *, int x, int y, + _Xconst XRectangle *rects, int n); +Bool XftDrawSetClip (XftDraw *draw, Region region); void XftDrawDestroy (XftDraw *draw); void diff --git a/utils/xftwrap.c b/utils/xftwrap.c new file mode 100644 index 0000000..e880407 --- /dev/null +++ b/utils/xftwrap.c @@ -0,0 +1,206 @@ +/* xftwrap.c --- XftDrawStringUtf8 with multi-line strings. + * xscreensaver, Copyright © 2021 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 + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "utils.h" +#include "xft.h" +#include "xftwrap.h" + +#undef MAX +#undef MIN +#define MAX(a,b) ((a)>(b)?(a):(b)) +#define MIN(a,b) ((a)<(b)?(a):(b)) + + +#ifdef DEBUG +static void +LOG(const char *ss, const char *s, int n) +{ + int i; + fprintf(stderr,"####%s [", ss); + for (i = 0; i < n; i++) fprintf(stderr, "%c", s[i]); + fprintf(stderr,"]\n"); +} +#else +# define LOG(ss,s,n) /**/ +#endif + + +/* Returns a new string word-wrapped to fit in the width in pixels. + */ +char * +xft_word_wrap (Display *dpy, XftFont *font, const char *str, int pixels) +{ + const char *in = str; + char *ret = (char *) malloc (strlen(in) + 2); + char *out = ret; + const char *line_out = out; + char *word_out = 0; + while (1) + { + if (*in == ' ' || *in == '\t' || + *in == '\r' || *in == '\n' || + *in == 0) + { + Bool done = (*in == 0); /* To wrap the *last* word. */ + XGlyphInfo overall; + Bool nl = (*in == '\r' || *in == '\n'); + + if (nl) in++; + while (*in == ' ' || *in == '\t') in++; + in--; + + XftTextExtentsUtf8 (dpy, font, + (FcChar8 *) line_out, + out - line_out, + &overall); + if (overall.width - overall.x >= pixels && + word_out) + { + word_out[0] = '\n'; + line_out = word_out + 1; + word_out = 0; + if (done) break; + *out++ = *in; + } + else + { + if (done) break; + word_out = out; + *out++ = *in; + if (nl) + { + line_out = out + 1; + word_out = 0; + } + } + + if (done) break; + } + else + { + *out++ = *in; + } + in++; + } + + *out = 0; + + return ret; +} + + +/* Like XftTextExtentsUtf8, but handles multi-line strings. + XGlyphInfo will contain the bounding box that encloses all of the text. + Return value is the number of lines in the text, >= 1. + */ +int +XftTextExtentsUtf8_multi (Display *dpy, XftFont *font, + const FcChar8 *str, int len, XGlyphInfo *overall) +{ + int i, start = 0; + int lines = 0; + int line_y = 0; + for (i = 0; i <= len; i++) + { + if (i == len || str[i] == '\r' || str[i] == '\n') + { + XGlyphInfo gi; + XftTextExtentsUtf8 (dpy, font, + str + start, + i - start, + &gi); + if (lines == 0) + *overall = gi; + else + { + /* Find the union of the two bounding boxes, placed at their + respective origins. */ + int ox1, oy1, ox2, oy2; /* bbox of 'overall' */ + int nx1, ny1, nx2, ny2; /* bbox of 'gi' */ + int ux1, uy1, ux2, uy2; /* union */ + + ox1 = overall->x; + oy1 = overall->y; + ox2 = ox1 + overall->width; + oy2 = oy1 + overall->height; + + line_y += font->ascent + font->descent; /* advance origin */ + + nx1 = gi.x; + ny1 = gi.y + line_y; + nx2 = nx1 + gi.width; + ny2 = ny1 + gi.height + line_y; + + ux1 = MIN (ox1, nx1); /* upper left */ + uy1 = MIN (oy1, ny1); + ux2 = MAX (ox2, nx2); /* bottom right */ + uy2 = MAX (oy2, ny2); + + overall->x = ux1; + overall->y = uy1; + overall->width = ux2 - ux1; + overall->height = uy2 - uy1; + } + lines++; + start = i+1; + } + } + + return lines; +} + + +/* Like XftDrawStringUtf8, but handles multi-line strings. + Alignment is 1, 0 or -1 for left, center, right. + */ +void +XftDrawStringUtf8_multi (XftDraw *xftdraw, const XftColor *color, + XftFont *font, int x, int y, const FcChar8 *str, + int len, + int alignment) +{ + Display *dpy = XftDrawDisplay (xftdraw); + int i, start = 0; + int lines = 0; + XGlyphInfo overall; + if (len == 0) return; + + XftTextExtentsUtf8_multi (dpy, font, str, len, &overall); + + for (i = 0; i <= len; i++) + { + if (i == len || str[i] == '\r' || str[i] == '\n') + { + XGlyphInfo gi; + int x2 = x; + XftTextExtentsUtf8 (dpy, font, str + start, i - start, &gi); + switch (alignment) { + case 1: break; + case 0: x2 += (overall.width - gi.width) / 2; break; + case -1: x2 += (overall.width - gi.width); break; + default: abort(); break; + } + + XftDrawStringUtf8 (xftdraw, color, font, x2, y, + str + start, + i - start); + y += font->ascent + font->descent; + lines++; + start = i+1; + } + } +} + diff --git a/utils/xftwrap.h b/utils/xftwrap.h new file mode 100644 index 0000000..25a55bb --- /dev/null +++ b/utils/xftwrap.h @@ -0,0 +1,35 @@ +/* xftwrap.h --- XftDrawStringUtf8 with multi-line strings. + * xscreensaver, Copyright © 2021 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 + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + */ + +#ifndef __XFTWRAP_H__ +#define __XFTWRAP_H__ + +/* Returns a new string word-wrapped to fit in the width in pixels. + */ +extern char *xft_word_wrap (Display *, XftFont *, const char *, int pixels); + +/* Like XftTextExtentsUtf8, but handles multi-line strings. + XGlyphInfo will contain the bounding box that encloses all of the text. + Return value is the number of lines in the text, >= 1. + */ +extern int XftTextExtentsUtf8_multi (Display *, XftFont *, + const FcChar8 *, int L, XGlyphInfo *); + +/* Like XftDrawStringUtf8, but handles multi-line strings. + Alignment is 1, 0 or -1 for left, center, right. + */ +extern void XftDrawStringUtf8_multi (XftDraw *, const XftColor *, + XftFont *, int x, int y, + const FcChar8 *, int L, + int alignment); + +#endif /* __XFTWRAP_H__ */ diff --git a/utils/xmu.c b/utils/xmu.c index 2f273d4..051ac95 100644 --- a/utils/xmu.c +++ b/utils/xmu.c @@ -1,173 +1,46 @@ -/* This file contains compatibility routines for systems without Xmu. - * You would be better served by installing Xmu on your machine or - * yelling at your vendor to ship it. +/* xscreensaver, Copyright © 1991-2021 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 + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. No representations are made about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif -#ifndef HAVE_XMU -/* - * Copyright 1989 Massachusetts Institute of Technology - * - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose and without fee is hereby granted, provided - * that the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of M.I.T. not be used in advertising - * or publicity pertaining to distribution of the software without specific, - * written prior permission. M.I.T. makes no representations about the - * suitability of this software for any purpose. It is provided "as is" - * without express or implied warranty. - * - * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T. - * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - +#include +#include #include "xmu.h" -#ifndef NEED_EVENTS -# define NEED_EVENTS /* to make Xproto.h define xEvent */ -#endif -#ifndef VMS -# include /* for xEvent (used by Xlibint.h) */ -# include /* for _XExtension */ -#else /* VMS */ -# include -#endif /* VMS */ -#include /* for XtSpecificationRelease */ +extern const char *progname; -/* - * XmuPrintDefaultErrorMessage - print a nice error that looks like the usual - * message. Returns 1 if the caller should consider exitting else 0. - */ -int XmuPrintDefaultErrorMessage (Display *dpy, XErrorEvent *event, FILE *fp) +int +XmuPrintDefaultErrorMessage (Display *dpy, XErrorEvent *event, FILE *fp) { - char buffer[BUFSIZ]; - char mesg[BUFSIZ]; - char number[32]; - char *mtype = "XlibMessage"; - _XExtension *ext = (_XExtension *)NULL; - XGetErrorText(dpy, event->error_code, buffer, BUFSIZ); - XGetErrorDatabaseText(dpy, mtype, "XError", "X Error", mesg, BUFSIZ); - (void) fprintf(fp, "%s: %s\n ", mesg, buffer); - XGetErrorDatabaseText(dpy, mtype, "MajorCode", "Request Major code %d", - mesg, BUFSIZ); - (void) fprintf(fp, mesg, event->request_code); - if (event->request_code < 128) { - sprintf(number, "%d", event->request_code); - XGetErrorDatabaseText(dpy, "XRequest", number, "", buffer, BUFSIZ); - } else { - /* XXX this is non-portable */ - for (ext = dpy->ext_procs; - ext && (ext->codes.major_opcode != event->request_code); - ext = ext->next) - ; - if (ext) - strcpy(buffer, ext->name); - else - buffer[0] = '\0'; - } - (void) fprintf(fp, " (%s)", buffer); - fputs("\n ", fp); -#if (XtSpecificationRelease >= 5) - if (event->request_code >= 128) { - XGetErrorDatabaseText(dpy, mtype, "MinorCode", "Request Minor code %d", - mesg, BUFSIZ); - (void) fprintf(fp, mesg, event->minor_code); - if (ext) { - sprintf(mesg, "%s.%d", ext->name, event->minor_code); - XGetErrorDatabaseText(dpy, "XRequest", mesg, "", buffer, BUFSIZ); - (void) fprintf(fp, " (%s)", buffer); - } - fputs("\n ", fp); - } - if (event->error_code >= 128) { - /* let extensions try to print the values */ - /* XXX this is non-portable code */ - for (ext = dpy->ext_procs; ext; ext = ext->next) { - if (ext->error_values) - (*ext->error_values)(dpy, event, fp); - } - /* the rest is a fallback, providing a simple default */ - /* kludge, try to find the extension that caused it */ - buffer[0] = '\0'; - for (ext = dpy->ext_procs; ext; ext = ext->next) { - if (ext->error_string) - (*ext->error_string)(dpy, event->error_code, &ext->codes, - buffer, BUFSIZ); - if (buffer[0]) - break; - } - if (buffer[0]) - sprintf(buffer, "%s.%d", ext->name, - event->error_code - ext->codes.first_error); - else - strcpy(buffer, "Value"); - XGetErrorDatabaseText(dpy, mtype, buffer, "", mesg, BUFSIZ); - if (*mesg) { - (void) fprintf(fp, mesg, event->resourceid); - fputs("\n ", fp); - } - } else if ((event->error_code == BadWindow) || - (event->error_code == BadPixmap) || - (event->error_code == BadCursor) || - (event->error_code == BadFont) || - (event->error_code == BadDrawable) || - (event->error_code == BadColor) || - (event->error_code == BadGC) || - (event->error_code == BadIDChoice) || - (event->error_code == BadValue) || - (event->error_code == BadAtom)) { - if (event->error_code == BadValue) - XGetErrorDatabaseText(dpy, mtype, "Value", "Value 0x%x", - mesg, BUFSIZ); - else if (event->error_code == BadAtom) - XGetErrorDatabaseText(dpy, mtype, "AtomID", "AtomID 0x%x", - mesg, BUFSIZ); - else - XGetErrorDatabaseText(dpy, mtype, "ResourceID", "ResourceID 0x%x", - mesg, BUFSIZ); - (void) fprintf(fp, mesg, event->resourceid); - fputs("\n ", fp); - } -#elif (XtSpecificationRelease == 4) - XGetErrorDatabaseText(dpy, mtype, "MinorCode", "Request Minor code %d", - mesg, BUFSIZ); - (void) fprintf(fp, mesg, event->minor_code); - fputs("\n ", fp); - if (ext) { - sprintf(mesg, "%s.%d", ext->name, event->minor_code); - XGetErrorDatabaseText(dpy, "XRequest", mesg, "", buffer, BUFSIZ); - (void) fprintf(fp, " (%s)", buffer); - } - XGetErrorDatabaseText(dpy, mtype, "ResourceID", "ResourceID 0x%x", - mesg, BUFSIZ); - (void) fprintf(fp, mesg, event->resourceid); - fputs("\n ", fp); -#else -ERROR! Unsupported release of X11 -#endif - XGetErrorDatabaseText(dpy, mtype, "ErrorSerial", "Error Serial #%d", - mesg, BUFSIZ); - (void) fprintf(fp, mesg, event->serial); - fputs("\n ", fp); - XGetErrorDatabaseText(dpy, mtype, "CurrentSerial", "Current Serial #%d", - mesg, BUFSIZ); - (void) fprintf(fp, mesg, NextRequest(dpy)-1); - fputs("\n", fp); - if (event->error_code == BadImplementation) return 0; - return 1; + /* The real libXmu version of this does localization and uses private, + undocumented fields inside the Display struct to print things about + errors from server-extension events. But the following is sufficient. + */ + char xerr[1024], opname[1024], num[20]; + const char *b = progname; + + /* Convert the error code to its name, e.g. "BadWindow" */ + XGetErrorText (dpy, event->error_code, xerr, sizeof(xerr)-1); + + /* Convert the request's ID number to its name, e.g., "XGetProperty" */ + sprintf (num, "%d", event->request_code); + XGetErrorDatabaseText (dpy, "XRequest", num, "", opname, sizeof(opname)-1); + + fprintf (fp, "%s: Failed request: %s\n", b, xerr); + fprintf (fp, "%s: Major opcode: %d (%s)\n", b, event->request_code, + opname); + fprintf (fp, "%s: Resource id: 0x%lx\n", b, event->resourceid); + fprintf (fp, "%s: Serial number: %ld / %ld\n\n", b, + event->serial, NextRequest(dpy)-1); + return 1; } - -#else /* HAVE_XMU */ - -/* Shut up the stupid "gcc -pedantic" warning */ -int _I_dont_care_that_ISO_C_forbids_an_empty_source_file_ = 1; - -#endif /* HAVE_XMU */ diff --git a/utils/xmu.h b/utils/xmu.h index 48084f7..e4cf7e6 100644 --- a/utils/xmu.h +++ b/utils/xmu.h @@ -1,14 +1,8 @@ -/* This file contains compatibility routines for systems without Xmu. - * You would be better served by installing Xmu on your machine or - * yelling at your vendor to ship it. - */ +/* A relic of the past. */ #ifndef __XMU_H__ #define __XMU_H__ -#include -#include - int XmuPrintDefaultErrorMessage (Display *dpy, XErrorEvent *event, FILE *fp); #endif /* __XMU_H__ */ diff --git a/utils/xshm.c b/utils/xshm.c index 8992140..14e5e43 100644 --- a/utils/xshm.c +++ b/utils/xshm.c @@ -33,6 +33,7 @@ /* #define DEBUG */ +#include #include /* for perror() */ #ifdef HAVE_JWXYZ @@ -42,12 +43,8 @@ #endif #include "xshm.h" +#include "aligned_malloc.h" #include "resources.h" /* for get_string_resource() */ -#include "thread_util.h" /* for thread_malloc() */ - -#ifdef DEBUG -# include -#endif extern char *progname; @@ -68,7 +65,7 @@ shm_ehandler (Display *dpy, XErrorEvent *error) { shm_got_x_error = True; -#ifdef DEBUG +#if 0 fprintf (stderr, "\n%s: ignoring X error from XSHM:\n", progname); XmuPrintDefaultErrorMessage (dpy, error, stderr); fprintf (stderr, "\n"); @@ -89,7 +86,7 @@ shm_ehandler (Display *dpy, XErrorEvent *error) XSync((DPY), False); \ if (old_handler) \ XSetErrorHandler (old_handler); \ - old_handler = 0; \ + old_handler = 0; \ } while(0) #endif /* HAVE_XSHM_EXTENSION */ @@ -117,8 +114,8 @@ create_fallback (Display *dpy, Visual *visual, /* Sometimes the XImage data needs to be aligned, such as for SIMD (SSE2 in Fireworkx), or multithreading (AnalogTV). */ - int error = thread_malloc ((void **)&image->data, dpy, - image->height * image->bytes_per_line); + int error = aligned_malloc ((void **)&image->data, get_cache_line_size(), + image->height * image->bytes_per_line); if (error) { print_error (error); XDestroyImage (image); @@ -291,11 +288,13 @@ void destroy_xshm_image (Display *dpy, XImage *image, XShmSegmentInfo *shm_info) { #ifdef HAVE_XSHM_EXTENSION + Status status; + if (shm_info->shmid == -1) { #endif /* HAVE_XSHM_EXTENSION */ /* Don't let XDestroyImage free image->data. */ - thread_free (image->data); + aligned_free (image->data); image->data = NULL; XDestroyImage (image); return; @@ -303,8 +302,6 @@ destroy_xshm_image (Display *dpy, XImage *image, XShmSegmentInfo *shm_info) #ifdef HAVE_XSHM_EXTENSION } - Status status; - CATCH_X_ERROR(dpy); status = XShmDetach (dpy, shm_info); UNCATCH_X_ERROR(dpy); @@ -312,10 +309,10 @@ destroy_xshm_image (Display *dpy, XImage *image, XShmSegmentInfo *shm_info) status = False; if (!status) fprintf (stderr, "%s: XShmDetach failed!\n", progname); -#ifdef DEBUG +# ifdef DEBUG else fprintf (stderr, "%s: XShmDetach(dpy, shm_info) ==> True\n", progname); -#endif +# endif XDestroyImage (image); XSync(dpy, False); @@ -329,10 +326,10 @@ destroy_xshm_image (Display *dpy, XImage *image, XShmSegmentInfo *shm_info) (unsigned long) shm_info->shmaddr); perror(buf); } -#ifdef DEBUG +# ifdef DEBUG else fprintf (stderr, "%s: shmdt(shm_info->shmaddr) ==> 0\n", progname); -#endif +# endif XSync(dpy, False); diff --git a/utils/yarandom.c b/utils/yarandom.c index f450735..0f51cb6 100644 --- a/utils/yarandom.c +++ b/utils/yarandom.c @@ -118,9 +118,9 @@ ya_rand_init(unsigned int seed) #define ROT(X,N) (((X)<<(N)) | ((X)>>((sizeof(unsigned int)*8)-(N)))) seed = (999U * (unsigned int) tp.tv_sec); seed = ROT (seed, 11); - seed += (1001 * tp.tv_usec); + seed += (1001 * (unsigned int) tp.tv_usec); seed = ROT (seed, 7); - seed += (1003 * getpid()); + seed += (1003 * (unsigned int) getpid()); seed = ROT (seed, 13); } -- cgit v1.2.3-55-g7522