From http://www.jwz.org/xscreensaver/xscreensaver-5.35.tar.gz
[xscreensaver] / hacks / glx / unicrud.c
1 /* unicrud, Copyright (c) 2016 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 #define DEFAULTS        "*delay:        20000       \n" \
13                         "*showFPS:      False       \n" \
14                         "*suppressRotationAnimation: True\n" \
15        "*titleFont: -*-helvetica-bold-r-normal-*-*-180-*-*-*-*-*-*\n" \
16        "*font:      -*-helvetica-bold-r-normal-*-*-2400-*-*-*-*-iso10646-1\n" \
17
18 # define refresh_unicrud 0
19 # define release_unicrud 0
20 #undef countof
21 #define countof(x) (sizeof((x))/sizeof((*x)))
22
23 #include "xlockmore.h"
24 #include "rotator.h"
25 #include "gltrackball.h"
26 #include "texfont.h"
27 #include "utf8wc.h"
28 #include <ctype.h>
29
30 #ifdef USE_GL /* whole file */
31
32 #define DEF_SPIN        "True"
33 #define DEF_WANDER      "True"
34 #define DEF_SPEED       "1.0"
35 #define DEF_BLOCK       "ALL"
36
37 typedef struct {
38   GLXContext *glx_context;
39   rotator *rot;
40   trackball_state *trackball;
41   Bool button_down_p;
42
43   GLfloat color[4];
44   texture_font_data *title_font, *char_font;
45   unsigned long unichar;
46   const char *charplane, *charblock, *charname;
47   enum { IN, LINGER, OUT } state;
48   int spin_direction;
49   GLfloat ratio;
50
51 } unicrud_configuration;
52
53 static unicrud_configuration *bps = NULL;
54
55 static Bool do_spin;
56 static GLfloat speed;
57 static Bool do_wander;
58 static char *do_block;
59
60 static XrmOptionDescRec opts[] = {
61   { "-spin",   ".spin",   XrmoptionNoArg, "True" },
62   { "+spin",   ".spin",   XrmoptionNoArg, "False" },
63   { "-wander", ".wander", XrmoptionNoArg, "True" },
64   { "+wander", ".wander", XrmoptionNoArg, "False" },
65   { "-speed",  ".speed",  XrmoptionSepArg, 0 },
66   { "-block",  ".block",  XrmoptionSepArg, 0 },
67 };
68
69 static argtype vars[] = {
70   {&do_spin,   "spin",   "Spin",   DEF_SPIN,   t_Bool},
71   {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
72   {&speed,     "speed",  "Speed",  DEF_SPEED,  t_Float},
73   {&do_block,  "block",  "Block",  DEF_BLOCK,  t_String},
74 };
75
76 ENTRYPOINT ModeSpecOpt unicrud_opts = {countof(opts), opts, countof(vars), vars, NULL};
77
78 static const struct {
79   unsigned long start;
80   const char * const name;
81 } unicode_block_names[] = {
82
83   /* Through Unicode 8.0, early 2016. */
84
85   { 0x0000, "*Basic Multilingual" },
86   { 0x0000, "Unassigned" },
87   { 0x0021, "ASCII" },
88   { 0x0080, "Unassigned" },
89   { 0x00A1, "Latin1" },
90   { 0x0100, "Latin Extended-A" },
91   { 0x0180, "Latin Extended-B" },
92   { 0x0250, "IPA Extensions" },
93   { 0x02B0, "Spacing Modifier Letters" },
94   { 0x0300, "Combining Diacritical Marks" },
95   { 0x0370, "Greek and Coptic" },
96   { 0x0400, "Cyrillic" },
97   { 0x0500, "Cyrillic Supplement" },
98   { 0x0530, "Armenian" },
99   { 0x058B, "Unassigned" },
100   { 0x0590, "Hebrew" },
101   { 0x05F5, "Unassigned" },
102   { 0x0600, "Arabic" },
103   { 0x0700, "Syriac" },
104   { 0x0750, "Arabic Supplement" },
105   { 0x0780, "Thaana" },
106   { 0x07C0, "N'Ko" },
107   { 0x0800, "Samaritan" },
108   { 0x0840, "Mandaic" },
109   { 0x0860, "Unassigned" },
110   { 0x08A0, "Arabic Extended-A" },
111   { 0x0900, "Devanagari" },
112   { 0x0980, "Bengali" },
113   { 0x09FC, "Unassigned" },
114   { 0x0A00, "Gurmukhi" },
115   { 0x0A76, "Unassigned" },
116   { 0x0A80, "Gujarati" },
117   { 0x0AF2, "Unassigned" },
118   { 0x0B00, "Oriya" },
119   { 0x0B80, "Tamil" },
120   { 0x0BFB, "Unassigned" },
121   { 0x0C00, "Telugu" },
122   { 0x0C80, "Kannada" },
123   { 0x0CF0, "Unassigned" },
124   { 0x0D00, "Malayalam" },
125   { 0x0D80, "Sinhala" },
126   { 0x0DF5, "Unassigned" },
127   { 0x0E00, "Thai" },
128   { 0x0E5C, "Unassigned" },
129   { 0x0E80, "Lao" },
130   { 0x0EE0, "Unassigned" },
131   { 0x0F00, "Tibetan" },
132   { 0x0FDB, "Unassigned" },
133   { 0x1000, "Myanmar" },
134   { 0x10A0, "Georgian" },
135   { 0x1100, "Hangul Jamo" },
136   { 0x1200, "Ethiopic" },
137   { 0x1380, "Ethiopic Supplement" },
138   { 0x13A0, "Cherokee" },
139   { 0x1400, "Unified Canadian Aboriginal Syllabics" },
140   { 0x1677, "Unassigned" },
141   { 0x1680, "Ogham" },
142   { 0x16A0, "Runic" },
143   { 0x16F1, "Unassigned" },
144   { 0x1700, "Tagalog" },
145   { 0x1715, "Unassigned" },
146   { 0x1720, "Hanunoo" },
147   { 0x1737, "Unassigned" },
148   { 0x1740, "Buhid" },
149   { 0x1754, "Unassigned" },
150   { 0x1760, "Tagbanwa" },
151   { 0x1774, "Unassigned" },
152   { 0x1780, "Khmer" },
153   { 0x17FA, "Unassigned" },
154   { 0x1800, "Mongolian" },
155   { 0x18AB, "Unassigned" },
156   { 0x18B0, "Unified Canadian Aboriginal Syllabics Extended" },
157   { 0x18F6, "Unassigned" },
158   { 0x1900, "Limbu" },
159   { 0x1950, "Tai Le" },
160   { 0x1975, "Unassigned" },
161   { 0x1980, "Tai Lue" },
162   { 0x19E0, "Khmer Symbols" },
163   { 0x1A00, "Buginese" },
164   { 0x1A20, "Tai Tham" },
165   { 0x1AAE, "Unassigned" },
166   { 0x1AB0, "Combining Diacritical Marks Extended" },
167   { 0x1ABF, "Unassigned" },
168   { 0x1B00, "Balinese" },
169   { 0x1B80, "Sundanese" },
170   { 0x1BC0, "Batak" },
171   { 0x1C00, "Lepcha" },
172   { 0x1C50, "Ol Chiki" },
173   { 0x1C80, "Unassigned" },
174   { 0x1CC0, "Sundanese Supplement" },
175   { 0x1CC8, "Unassigned" },
176   { 0x1CD0, "Vedic Extensions" },
177   { 0x1CFA, "Unassigned" },
178   { 0x1D00, "Phonetic Extensions" },
179   { 0x1D80, "Phonetic Extensions Supplement" },
180   { 0x1DC0, "Combining Diacritical Marks Supplement" },
181   { 0x1E00, "Latin Extended Additional" },
182   { 0x1F00, "Greek Extended" },
183   { 0x2000, "General Punctuation" },
184   { 0x2070, "Superscripts and Subscripts" },
185   { 0x2095, "Unassigned" },
186   { 0x20A0, "Currency Symbols" },
187   { 0x20BE, "Unassigned" },
188   { 0x20D0, "Combining Diacritical Marks for Symbols" },
189   { 0x20F1, "Unassigned" },
190   { 0x2100, "Letterlike Symbols" },
191   { 0x2150, "Number Forms" },
192   { 0x2190, "Arrows" },
193   { 0x2200, "Mathematical Operators" },
194   { 0x2300, "Miscellaneous Technical" },
195   { 0x2400, "Control Pictures" },
196   { 0x2437, "Unassigned" },
197   { 0x2440, "Optical Character Recognition" },
198   { 0x244B, "Unassigned" },
199   { 0x2460, "Enclosed Alphanumerics" },
200   { 0x2500, "Box Drawing" },
201   { 0x2580, "Block Elements" },
202   { 0x25A0, "Geometric Shapes" },
203   { 0x2600, "Miscellaneous Symbols" },
204   { 0x2700, "Dingbats" },
205   { 0x27C0, "Miscellaneous Mathematical Symbols-A" },
206   { 0x27F0, "Supplemental Arrows-A" },
207   { 0x2800, "Braille Patterns" },
208   { 0x2900, "Supplemental Arrows-B" },
209   { 0x2980, "Miscellaneous Mathematical Symbols-B" },
210   { 0x2A00, "Supplemental Mathematical Operators" },
211   { 0x2B00, "Miscellaneous Symbols and Arrows" },
212   { 0x2B56, "Unassigned" }, /* Unicode 5.1 */
213   { 0x2BF0, "Unassigned" },
214   { 0x2C00, "Glagolitic" },
215   { 0x2C60, "Latin Extended-C" },
216   { 0x2C80, "Coptic" },
217   { 0x2D00, "Georgian Supplement" },
218   { 0x2D26, "Unassigned" },
219   { 0x2D30, "Tifinagh" },
220   { 0x2D80, "Ethiopic Extended" },
221   { 0x2DE0, "Cyrillic Extended-A" },
222   { 0x2E00, "Supplemental Punctuation" },
223   { 0x2E43, "Unassigned" },
224   { 0x2E80, "CJK Radicals Supplement" },
225   { 0x2EF5, "Unassigned" },
226   { 0x2F00, "Kangxi Radicals" },
227   { 0x2FD6, "Unassigned" },
228   { 0x2FE0, "Unassigned" },
229   { 0x2FF0, "Ideographic Description Characters" },
230   { 0x2FFC, "Unassigned" },
231   { 0x3000, "CJK Symbols and Punctuation" },
232   { 0x3040, "Hiragana" },
233   { 0x30A0, "Katakana" },
234   { 0x3100, "Bopomofo" },
235   { 0x3130, "Hangul Compatibility Jamo" },
236   { 0x3190, "Kanbun" },
237   { 0x31A0, "Bopomofo Extended" },
238   { 0x31BB, "Unassigned" },
239   { 0x31C0, "CJK Strokes" },
240   { 0x31E4, "Unassigned" },
241   { 0x31F0, "Katakana Phonetic Extensions" },
242   { 0x3200, "Enclosed CJK Letters and Months" },
243   { 0x3300, "CJK Compatibility" },
244   { 0x3400, "CJK Unified Ideographs Extension A" },
245   { 0x4DB6, "Unassigned" },
246   { 0x4DC0, "Yijing Hexagram Symbols" },
247   { 0x4E00, "CJK Unified Ideographs" },
248   { 0x9FD6, "Unassigned" },
249   { 0xA000, "Yi Syllables" },
250   { 0xA48D, "Unassigned" },
251   { 0xA490, "Yi Radicals" },
252   { 0xA4C7, "Unassigned" },
253   { 0xA4D0, "Lisu" },
254   { 0xA500, "Vai" },
255   { 0xA62C, "Unassigned" },
256   { 0xA640, "Cyrillic Extended-B" },
257   { 0xA6A0, "Bamum" },
258   { 0xA700, "Modifier Tone Letters" },
259   { 0xA720, "Latin Extended-D" },
260   { 0xA800, "Syloti Nagri" },
261   { 0xA82C, "Unassigned" },
262   { 0xA830, "Common Indic Number Forms" },
263   { 0xA83A, "Unassigned" },
264   { 0xA840, "Phags-pa" },
265   { 0xA878, "Unassigned" },
266   { 0xA880, "Saurashtra" },
267   { 0xA8DA, "Unassigned" },
268   { 0xA8E0, "Devanagari Extended" },
269   { 0xA8FE, "Unassigned" },
270   { 0xA900, "Kayah Li" },
271   { 0xA930, "Rejang" },
272   { 0xA960, "Hangul Jamo Extended-A" },
273   { 0xA97D, "Unassigned" },
274   { 0xA980, "Javanese" },
275   { 0xA9E0, "Myanmar Extended-B" },
276   { 0xAA00, "Cham" },
277   { 0xAA60, "Myanmar Extended-A" },
278   { 0xAA80, "Tai Viet" },
279   { 0xAAE0, "Meetei Mayek Extensions" },
280   { 0xAB00, "Ethiopic Extended-A" },
281   { 0xAB30, "Latin Extended-E" },
282   { 0xAB66, "Unassigned" },
283   { 0xAB70, "Cherokee Supplement" },
284   { 0xABC0, "Meetei Mayek" },
285   { 0xABFA, "Unassigned" },
286   { 0xAC00, "Hangul Syllables" },
287   { 0xD7B0, "Hangul Jamo Extended-B" },
288   { 0xD7FC, "Unassigned" },
289 /*{ 0xD800, "High Surrogates" }, */  /* UTF-16 compatibility */
290 /*{ 0xDC00, "Low Surrogates" },  */  /* UTF-16 compatibility */
291   { 0xE000, "Private Use Area" },
292   { 0xF900, "CJK Compatibility Ideographs" },
293   { 0xFAEA, "Unassigned" },
294   { 0xFB00, "Alphabetic Presentation Forms" },
295   { 0xFB50, "Arabic Presentation Forms-A" },
296   { 0xFE00, "Variation Selectors" },
297   { 0xFE10, "Vertical Forms" },
298   { 0xFE1A, "Unassigned" },
299   { 0xFE20, "Combining Half Marks" },
300   { 0xFE30, "CJK Compatibility Forms" },
301   { 0xFE50, "Small Form Variants" },
302   { 0xFE6C, "Unassigned" },
303   { 0xFE70, "Arabic Presentation Forms-B" },
304   { 0xFF00, "Halfwidth and Fullwidth Forms" },
305   { 0xFFF0, "Unassigned" },
306   { 0xFFF9, "Specials" },
307   { 0xFFFE, "Unassigned" },
308
309   { 0x10000, "*Supplementary Multilingual" },
310   { 0x10000, "Linear B Syllabary" },
311   { 0x1007E, "Unassigned" },
312   { 0x10080, "Linear B Ideograms" },
313   { 0x100FB, "Unassigned" },
314   { 0x10100, "Aegean Numbers" },
315   { 0x10140, "Ancient Greek Numbers" },
316   { 0x1018D, "Unassigned" },
317   { 0x10190, "Ancient Symbols" },
318   { 0x1019C, "Unassigned" },
319   { 0x101D0, "Phaistos Disc" },
320   { 0x10200, "Unassigned" },
321   { 0x10280, "Lycian" },
322   { 0x1029D, "Unassigned" },
323   { 0x102A0, "Carian" },
324   { 0x102D1, "Unassigned" },
325   { 0x102E0, "Coptic Epact Numbers" },
326   { 0x102FC, "Unassigned" },
327   { 0x10300, "Old Italic" },
328   { 0x10324, "Unassigned" },
329   { 0x10330, "Gothic" },
330   { 0x1034B, "Unassigned" },
331   { 0x10350, "Old Permic" },
332   { 0x10380, "Ugaritic" },
333   { 0x103A0, "Old Persian" },
334   { 0x103D6, "Unassigned" },
335   { 0x103E0, "Unassigned" },
336   { 0x10400, "Deseret" },
337   { 0x10450, "Shavian" },
338   { 0x10480, "Osmanya" },
339   { 0x104AA, "Unassigned" },
340   { 0x104B0, "Unassigned" },
341   { 0x10500, "Elbasan" },
342   { 0x10528, "Unassigned" },
343   { 0x10530, "Caucasian Albanian" },
344   { 0x10570, "Unassigned" },
345   { 0x10600, "Linear A" },
346   { 0x10768, "Unassigned" },
347   { 0x10780, "Unassigned" },
348   { 0x10800, "Cypriot Syllabary" },
349   { 0x10840, "Imperial Aramaic" },
350   { 0x10860, "Palmyrene" },
351   { 0x10880, "Nabataean" },
352   { 0x108B0, "Unassigned" },
353   { 0x108E0, "Hatran" },
354   { 0x10900, "Phoenician" },
355   { 0x10920, "Lydian" },
356   { 0x10940, "Unassigned" },
357   { 0x10980, "Meroitic Hieroglyphs" },
358   { 0x109A0, "Meroitic Cursive" },
359   { 0x10A00, "Kharoshthi" },
360   { 0x10A59, "Unassigned" },
361   { 0x10A60, "Old South Arabian" },
362   { 0x10A80, "Old North Arabian" },
363   { 0x10AA0, "Unassigned" },
364   { 0x10AC0, "Manichaean" },
365   { 0x10AF7, "Unassigned" },
366   { 0x10B00, "Avestan" },
367   { 0x10B40, "Inscriptional Parthian" },
368   { 0x10B60, "Inscriptional Pahlavi" },
369   { 0x10B80, "Psalter Pahlavi" },
370   { 0x10BB0, "Unassigned" },
371   { 0x10C00, "Old Turkic" },
372   { 0x10C49, "Unassigned" },
373   { 0x10C50, "Unassigned" },
374   { 0x10C80, "Old Hungarian" },
375   { 0x10D00, "Unassigned" },
376   { 0x10E60, "Rumi Numeral Symbols" },
377   { 0x10E80, "Unassigned" },
378   { 0x11000, "Brahmi" },
379   { 0x11070, "Unassigned" },
380   { 0x11080, "Kaithi" },
381   { 0x110C2, "Unassigned" },
382   { 0x110D0, "Sora Sompeng" },
383   { 0x110FA, "Unassigned" },
384   { 0x11100, "Chakma" },
385   { 0x11144, "Unassigned" },
386   { 0x11150, "Mahajani" },
387   { 0x11177, "Unassigned" },
388   { 0x11180, "Sharada" },
389   { 0x111E0, "Sinhala Archaic Numbers" },
390   { 0x111F5, "Unassigned" },
391   { 0x11200, "Khojki" },
392   { 0x1123E, "Unassigned" },
393   { 0x11250, "Unassigned" },
394   { 0x11280, "Multani" },
395   { 0x112AA, "Unassigned" },
396   { 0x112B0, "Khudawadi" },
397   { 0x112FA, "Unassigned" },
398   { 0x11300, "Grantha" },
399   { 0x11375, "Unassigned" },
400   { 0x11380, "Unassigned" },
401   { 0x11480, "Tirhuta" },
402   { 0x114DA, "Unassigned" },
403   { 0x114E0, "Unassigned" },
404   { 0x11580, "Siddham" },
405   { 0x115DE, "Unassigned" },
406   { 0x11600, "Modi" },
407   { 0x1165A, "Unassigned" },
408   { 0x11660, "Unassigned" },
409   { 0x11680, "Takri" },
410   { 0x116CA, "Unassigned" },
411   { 0x116D0, "Unassigned" },
412   { 0x11700, "Ahom" },
413   { 0x11740, "Unassigned" },
414   { 0x118A0, "Warang Citi" },
415   { 0x11900, "Unassigned" },
416   { 0x11AC0, "Pau Cin Hau" },
417   { 0x11AF9, "Unassigned" },
418   { 0x11B00, "Unassigned" },
419   { 0x12000, "Cuneiform" },
420   { 0x1239A, "Unassigned" },
421   { 0x12400, "Cuneiform Numbers and Punctuation" },
422   { 0x12475, "Unassigned" },
423   { 0x12480, "Early Dynastic Cuneiform" },
424   { 0x12544, "Unassigned" },
425   { 0x12550, "Unassigned" },
426   { 0x13000, "Egyptian Hieroglyphs" },
427   { 0x13430, "Unassigned" },
428   { 0x14400, "Anatolian Hieroglyphs" },
429   { 0x14647, "Unassigned" },
430   { 0x14680, "Unassigned" },
431   { 0x16800, "Bamum Supplement" },
432   { 0x16A39, "Unassigned" },
433   { 0x16A40, "Mro" },
434   { 0x16A70, "Unassigned" },
435   { 0x16AD0, "Bassa Vah" },
436   { 0x16AF6, "Unassigned" },
437   { 0x16B00, "Pahawh Hmong" },
438   { 0x16B90, "Unassigned" },
439   { 0x16F00, "Miao" },
440   { 0x16FA0, "Unassigned" },
441   { 0x1B000, "Kana Supplement" },
442   { 0x1B002, "Unassigned" },
443   { 0x1B100, "Unassigned" },
444   { 0x1BC00, "Duployan Shorthand" },
445   { 0x1BCA4, "Unassigned" },
446   { 0x1BCB0, "Unassigned" },
447   { 0x1D000, "Byzantine Musical Symbols" },
448   { 0x1D0F5, "Unassigned" },
449   { 0x1D100, "Musical Symbols" },
450   { 0x1D1E9, "Unassigned" },
451   { 0x1D200, "Ancient Greek Musical Notation" },
452   { 0x1D246, "Unassigned" },
453   { 0x1D250, "Unassigned" },
454   { 0x1D300, "Tai Xuan Jing Symbols" },
455   { 0x1D357, "Unassigned" },
456   { 0x1D360, "Counting Rod Numerals" },
457   { 0x1D372, "Unassigned" },
458   { 0x1D380, "Unassigned" },
459   { 0x1D400, "Mathematical Alphanumeric Symbols" },
460   { 0x1D800, "Sutton SignWriting" },
461   { 0x1DAB0, "Unassigned" },
462   { 0x1E800, "Mende Kikakui" },
463   { 0X1E8D7, "Unassigned" },
464   { 0x1E8E0, "Unassigned" },
465   { 0x1EE00, "Arabic Mathematical Alphabetic Symbols" },
466   { 0X1EEFC, "Unassigned" },
467   { 0x1EF00, "Unassigned" },
468   { 0x1F000, "Mahjong Tiles" },
469   { 0x1F02C, "Unassigned" },
470   { 0x1F030, "Domino Tiles" },
471   { 0x1F094, "Unassigned" },
472   { 0x1F0A0, "Playing Cards" },
473   { 0x1F0F6, "Unassigned" },
474   { 0x1F100, "Enclosed Alphanumeric Supplement" },
475   { 0x1F19B, "Unassigned" },
476   { 0x1F1E6, "Enclosed Alphanumeric Supplement" },
477   { 0x1F200, "Enclosed Ideographic Supplement" },
478   { 0x1F252, "Unassigned" },
479   { 0x1F300, "Miscellaneous Symbols and Pictographs" },
480   { 0x1F600, "Emoticons" },
481   { 0x1F650, "Ornamental Dingbats" },
482   { 0x1F680, "Transport and Map Symbols" },
483   { 0x1F6F4, "Unassigned" },
484   { 0x1F700, "Alchemical Symbols" },
485   { 0x1F774, "Unassigned" },
486   { 0x1F780, "Geometric Shapes Extended" },
487   { 0x1F7D5, "Unassigned" },
488   { 0x1F800, "Supplemental Arrows-C" },
489   { 0x1F8AD, "Unassigned" },
490   { 0x1F910, "Supplemental Symbols and Pictographs" },
491   { 0x1F9C1, "Unassigned" },
492   { 0x1FA00, "Unassigned" },
493
494   { 0x20000, "*Supplementary Ideographic" },
495   { 0x20000, "CJK Unified Ideographs Extension B" },
496   { 0x2A6D7, "Unassigned" },
497   { 0x2A6E0, "Unassigned" },
498   { 0x2A700, "CJK Unified Ideographs Extension C" },
499   { 0x2B740, "Unassigned" },
500   { 0x2B740, "CJK Unified Ideographs Extension D" },
501   { 0x2B820, "Unassigned" },
502   { 0x2B820, "CJK Unified Ideographs Extension E" },
503   { 0x2CEA2, "Unassigned" },
504   { 0x2CEB0, "Unassigned" },
505   { 0x2F800, "CJK Compatibility Ideographs Supplement" },
506   { 0x2FA20, "Unassigned" },
507
508   { 0x30000, "*Unassigned Plane 3" },
509   { 0x40000, "*Unassigned Plane 4" },
510   { 0x50000, "*Unassigned Plane 5" },
511   { 0x60000, "*Unassigned Plane 6" },
512   { 0x70000, "*Unassigned Plane 7" },
513   { 0x80000, "*Unassigned Plane 8" },
514   { 0x90000, "*Unassigned Plane 9" },
515   { 0xA0000, "*Unassigned Plane 10" },
516   { 0xB0000, "*Unassigned Plane 11" },
517   { 0xC0000, "*Unassigned Plane 12" },
518   { 0xD0000, "*Unassigned Plane 13" },
519
520   { 0xE0000, "*Supplementary Special-Purpose" },
521   { 0xE0080, "Unassigned" },
522   { 0xE0020, "Tags" },
523   { 0xE0080, "Unassigned" },
524   { 0xE0100, "Variation Selectors Supplement" },
525   { 0xE01F0, "Unassigned" },
526
527   { 0xF0000,  "*Supplementary Private Use Area A" },
528   { 0x100000, "*Supplementary Private Use Area B" },
529 };
530
531
532 static char 
533 *strip (char *s)
534 {
535   int L;
536   while (*s == ' ' || *s == '\t' || *s == '\n')
537     s++;
538   L = strlen (s);
539   while (s[L-1] == ' ' || s[L-1] == '\t' || s[L-1] == '\n')
540     s[--L] = 0;
541   return s;
542 }
543
544
545 /* matches ("AA, BB, CC", "CC")  => True
546    matches ("AA, BB, CC", "CCx") => False
547  */
548 static Bool
549 matches (const char *pattern, const char *string)
550 {
551   char *token = strdup (pattern ? pattern : "");
552   char *otoken = token;
553   char *name;
554   Bool match = False;
555   while ((name = strtok (token, ","))) {
556     token = 0;
557     name = strip (name);
558     if (*name && !strcasecmp (name, string))
559       {
560         match = True;
561         break;
562       }
563   }
564
565   free (otoken);
566   return match;
567 }
568
569
570 static void
571 pick_unichar (ModeInfo *mi)
572 {
573   unicrud_configuration *bp = &bps[MI_SCREEN(mi)];
574   int i;
575   unsigned long min = 0;
576   unsigned long max = 0x2F800;
577   unsigned long last = 0;
578   int retries = 0;
579
580  AGAIN:
581   bp->unichar = min + (random() % (max - min));
582
583   if (++retries > 0xF0000 / 2)
584     {
585       fprintf (stderr, "%s: internal error: too many retries\n", progname);
586       exit (1);
587     }
588
589   /* bp->unichar = 0x1F4A9; */
590
591   last = 0;
592   bp->charplane = "Unassigned";
593   bp->charblock = "Unassigned";
594   for (i = 0; i < countof(unicode_block_names); i++)
595     {
596       if (unicode_block_names[i].start < last)
597         {
598           fprintf (stderr, "%s: progname: internal error: misordered: 0x%lX\n",
599                    progname, unicode_block_names[i].start);
600           exit (1);
601         }
602       last = unicode_block_names[i].start;
603       if (bp->unichar >= unicode_block_names[i].start)
604         {
605           if (unicode_block_names[i].name[0] == '*')
606             {
607               bp->charplane = unicode_block_names[i].name + 1;
608               bp->charblock = "Unassigned";
609             }
610           else
611             bp->charblock = unicode_block_names[i].name;
612         }
613       else
614         break;
615     }
616
617   if (!strncmp (bp->charblock, "Unassigned", 10) ||
618       !strncmp (bp->charblock, "Combining", 9))
619     goto AGAIN;
620
621   if (*do_block && !matches (do_block, bp->charblock))
622     goto AGAIN;
623
624   /* Skip blank characters */
625   {
626     XCharStruct e;
627     char text[10];
628     int i;
629     i = utf8_encode (bp->unichar, text, sizeof(text) - 1);
630     text[i] = 0;
631     texture_string_metrics (bp->char_font, text, &e, 0, 0);
632     if (e.width < 2 || e.ascent + e.descent < 2)
633       goto AGAIN;
634   }
635
636 # ifdef HAVE_JWXYZ
637   bp->charname = texfont_unicode_character_name (bp->char_font, bp->unichar);
638 # endif
639
640   bp->color[0] = 0.5 + frand(0.5);
641   bp->color[1] = 0.5 + frand(0.5);
642   bp->color[2] = 0.5 + frand(0.5);
643   bp->color[3] = 1;
644 }
645
646
647 static void
648 draw_unichar (ModeInfo *mi)
649 {
650   unicrud_configuration *bp = &bps[MI_SCREEN(mi)];
651
652   char text[10];
653   char title[400];
654   XCharStruct e;
655   int w, h, i, j;
656   GLfloat s;
657
658   i = utf8_encode (bp->unichar, text, sizeof(text) - 1);
659   text[i] = 0;
660
661   *title = 0;
662   sprintf (title + strlen(title), "Plane:\t%s\n", bp->charplane);
663   sprintf (title + strlen(title), "Block:\t%s\n", bp->charblock);
664 # ifdef HAVE_JWXYZ
665   sprintf (title + strlen(title), "Name:\t%s\n",
666            (bp->charname ? bp->charname : ""));
667 #endif
668   sprintf (title + strlen(title), "Unicode:\t%04lX\n", bp->unichar);
669   sprintf (title + strlen(title), "UTF-8:\t");
670   for (j = 0; j < i; j++)
671     sprintf (title + strlen(title), "%02X ", ((unsigned char *)text)[j]);
672
673   texture_string_metrics (bp->char_font, text, &e, 0, 0);
674   w = e.width;
675   h = e.ascent;
676
677   s = 9;
678   glScalef (s, s, s);
679   
680   s = 1.0 / (h > w ? h : w);    /* Scale to unit */
681   glScalef (s, s, s);
682
683   glTranslatef (-w/2, -h/2, 0);
684   glColor4fv (bp->color);
685   print_texture_string (bp->char_font, text);
686
687   glColor3f (1, 1, 0);
688   print_texture_label (mi->dpy, bp->title_font,
689                        mi->xgwa.width, mi->xgwa.height,
690                        1, title);
691 }
692
693
694 /* Window management, etc
695  */
696 ENTRYPOINT void
697 reshape_unicrud (ModeInfo *mi, int width, int height)
698 {
699   GLfloat h = (GLfloat) height / (GLfloat) width;
700
701   glViewport (0, 0, (GLint) width, (GLint) height);
702
703   glMatrixMode(GL_PROJECTION);
704   glLoadIdentity();
705   gluPerspective (30.0, 1/h, 1.0, 100.0);
706
707   glMatrixMode(GL_MODELVIEW);
708   glLoadIdentity();
709   gluLookAt( 0.0, 0.0, 30.0,
710              0.0, 0.0, 0.0,
711              0.0, 1.0, 0.0);
712
713 # ifdef HAVE_MOBILE     /* Keep it the same relative size when rotated. */
714   {
715     int o = (int) current_device_rotation();
716     if (o != 0 && o != 180 && o != -180)
717       glScalef (1/h, 1/h, 1/h);
718   }
719 # endif
720
721   glClear(GL_COLOR_BUFFER_BIT);
722 }
723
724
725 ENTRYPOINT Bool
726 unicrud_handle_event (ModeInfo *mi, XEvent *event)
727 {
728   unicrud_configuration *bp = &bps[MI_SCREEN(mi)];
729
730   if (gltrackball_event_handler (event, bp->trackball,
731                                  MI_WIDTH (mi), MI_HEIGHT (mi),
732                                  &bp->button_down_p))
733     return True;
734   else if (event->xany.type == KeyPress)
735     {
736       KeySym keysym;
737       char c = 0;
738       XLookupString (&event->xkey, &c, 1, &keysym, 0);
739       if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
740         {
741           if (bp->state == LINGER) bp->ratio = 1;
742           return True;
743         }
744     }
745
746   return False;
747 }
748
749
750 ENTRYPOINT void 
751 init_unicrud (ModeInfo *mi)
752 {
753   unicrud_configuration *bp;
754
755   if (!bps) {
756     bps = (unicrud_configuration *)
757       calloc (MI_NUM_SCREENS(mi), sizeof (unicrud_configuration));
758     if (!bps) {
759       fprintf(stderr, "%s: out of memory\n", progname);
760       exit(1);
761     }
762   }
763
764   bp = &bps[MI_SCREEN(mi)];
765
766   bp->glx_context = init_GL(mi);
767
768   reshape_unicrud (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
769
770   {
771     double spin_speed   = 0.05;
772     double wander_speed = 0.01;
773     double spin_accel   = 1.0;
774
775     bp->rot = make_rotator (do_spin ? spin_speed : 0,
776                             do_spin ? spin_speed : 0,
777                             do_spin ? spin_speed : 0,
778                             spin_accel,
779                             do_wander ? wander_speed : 0,
780                             False);
781     bp->trackball = gltrackball_init (True);
782   }
783
784   bp->title_font = load_texture_font (mi->dpy, "titleFont");
785   bp->char_font  = load_texture_font (mi->dpy, "font");
786   bp->state = IN;
787   bp->ratio = 0;
788   bp->spin_direction = (random() & 1) ? 1 : -1;
789
790
791
792   if (matches ("all", do_block))
793     do_block = "";
794
795   {
796     char *s;
797     for (s = do_block; *s; s++)
798       if (*s == '_') *s = ' ';
799   }
800
801   if (matches ("help", do_block))
802     {
803       int i;
804       fprintf (stderr,
805                "%s: --blocks must contain one or more of these,"
806                " separated by commas:\n\n", progname);
807       for (i = 0; i < countof(unicode_block_names); i++)
808         {
809           const char *n = unicode_block_names[i].name;
810           if (*n == '*')
811             continue;
812           if (!strncmp (n, "Unassigned", 10) ||
813               !strncmp (n, "Combining", 9))
814             continue;
815           fprintf (stderr, "\t%s\n", n);
816         }
817       fprintf (stderr, "\n");
818       exit (1);
819     }
820
821
822   /* Make sure all elements in --block are valid.
823    */
824   if (*do_block)
825     {
826       char *token = strdup (do_block ? do_block : "");
827       char *otoken = token;
828       char *name;
829       while ((name = strtok (token, ","))) {
830         token = 0;
831         name = strip (name);
832         if (*name)
833           {
834             Bool match = False;
835             int i;
836             for (i = 0; i < countof(unicode_block_names); i++)
837               {
838                 const char *n = unicode_block_names[i].name;
839                 if (*n == '*')
840                   continue;
841                 if (!strncmp (n, "Unassigned", 10) ||
842                     !strncmp (n, "Combining", 9))
843                   continue;
844                 if (!strcasecmp (name, n))
845                   {
846                     match = True;
847                     break;
848                   }
849               }
850             if (! match)
851               {
852                 fprintf (stderr, "%s: unknown block name: \"%s\"\n", 
853                          progname, name);
854                 fprintf (stderr, "%s: use '--block help' for a list\n", 
855                          progname);
856                 exit (1);
857               }
858           }
859       }
860       free (otoken);
861     }
862
863   pick_unichar (mi);
864 }
865
866
867 ENTRYPOINT void
868 draw_unicrud (ModeInfo *mi)
869 {
870   unicrud_configuration *bp = &bps[MI_SCREEN(mi)];
871   Display *dpy = MI_DISPLAY(mi);
872   Window window = MI_WINDOW(mi);
873
874   if (!bp->glx_context)
875     return;
876
877   glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
878
879   glShadeModel (GL_FLAT);
880   glEnable (GL_NORMALIZE);
881   glDisable (GL_CULL_FACE);
882   glDisable (GL_LIGHTING);
883
884   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
885
886   if (! bp->button_down_p)
887     switch (bp->state) {
888     case IN:     bp->ratio += speed * 0.05;  break;
889     case OUT:    bp->ratio += speed * 0.05;  break;
890     case LINGER: bp->ratio += speed * 0.005; break;
891     default:     abort();
892     }
893
894   if (bp->ratio > 1.0)
895     {
896       bp->ratio = 0;
897       switch (bp->state) {
898       case IN:
899         bp->state = LINGER;
900         break;
901       case LINGER:
902         bp->state = OUT;
903         bp->spin_direction = (random() & 1) ? 1 : -1;
904         break;
905       case OUT:
906         bp->state = IN;
907         pick_unichar(mi);
908         break;
909       default:     abort();
910       }
911     }
912
913   glPushMatrix ();
914
915   {
916     double x, y, z;
917     get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
918     glTranslatef((x - 0.5) * 6,
919                  (y - 0.5) * 6,
920                  (z - 0.5) * 6);
921
922     gltrackball_rotate (bp->trackball);
923
924     get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
925     x = y = 0;
926     glRotatef (x * 360, 1.0, 0.0, 0.0);
927     glRotatef (y * 360, 0.0, 1.0, 0.0);
928     glRotatef (z * 360, 0.0, 0.0, 1.0);
929   }
930
931 # define SINOID(N) (sin(M_PI - (N) / 2 * M_PI))
932   {
933     GLfloat s;
934     switch (bp->state) {
935     case IN:  s = SINOID (bp->ratio);   break;
936     case OUT: s = SINOID (1-bp->ratio); break;
937     default:  s = 1; break;
938     }
939     glScalef (s, s, s);
940     glRotatef (360 * s * bp->spin_direction * (bp->state == IN ? -1 : 1),
941                0, 0, 1);
942   }
943
944   draw_unichar (mi);
945
946   glPopMatrix ();
947
948   if (mi->fps_p) do_fps (mi);
949   glFinish();
950
951   glXSwapBuffers(dpy, window);
952 }
953
954 XSCREENSAVER_MODULE_2 ("Unicrud", unicrud, unicrud)
955
956 #endif /* USE_GL */