1 /*****************************************************************************
2  *
3  * Sunclock version 3.xx by Jean-Pierre Demailly
4  *
5  * Is derived from the previous versions whose notices appear below.
6  * See CHANGES for more explanation on the (quite numerous changes and
7  * improvements). Version 3.xx is now published under the GPL.
8  */
9 
10 /*****************************************************************************
11  *
12  * Sun clock.  X11 version by John Mackin.
13  *
14  * This program was derived from, and is still in part identical with, the
15  * Suntools Sun clock program whose author's comment appears immediately
16  * below.  Please preserve both notices.
17  *
18  * The X11R3/4 version of this program was written by John Mackin, at the
19  * Basser Department of Computer Science, University of Sydney, Sydney,
20  * New South Wales, Australia; <john@cs.su.oz.AU>.  This program, like
21  * the one it was derived from, is in the public domain: `Love is the
22  * law, love under will.'
23  */
24 
25 /*****************************************************************************
26 
27         Sun clock
28 
29         Designed and implemented by John Walker in November of 1988.
30 
31         Version for the Sun Workstation.
32 
33     The algorithm used to calculate the position of the Sun is given in
34     Chapter 18 of:
35 
36     "Astronomical  Formulae for Calculators" by Jean Meeus, Third Edition,
37     Richmond: Willmann-Bell, 1985.  This book can be obtained from:
38 
39        Willmann-Bell
40        P.O. Box 35025
41        Richmond, VA  23235
42        USA
43        Phone: (804) 320-7016
44 
45     This program was written by:
46 
47        John Walker
48        Autodesk, Inc.
49        2320 Marinship Way
50        Sausalito, CA  94965
51        USA
52        Fax:   (415) 389-9418
53        Voice: (415) 332-2344 Ext. 2829
54        Usenet: {sun,well,uunet}!acad!kelvin
55            or: kelvin@acad.uu.net
56 
57     modified for interactive maps by
58 
59         Stephen Martin
60         Fujitsu Systems Business of Canada
61         smartin@fujitsu.ca
62 
63     This  program is in the public domain: "Do what thou wilt shall be the
64     whole of the law".  I'd appreciate  receiving  any  bug  fixes  and/or
65     enhancements,  which  I'll  incorporate  in  future  versions  of  the
66     program.  Please leave the original attribution information intact  so
67     that credit and blame may be properly apportioned.
68 
69     Revision history:
70 
71         1.0  12/21/89  Initial version.
72               8/24/89  Finally got around to submitting.
73 
74         1.1   8/31/94  Version with interactive map.
75         1.2  10/12/94  Fixes for HP and Solaris, new icon bitmap
76         1.3  11/01/94  Timezone now shown in icon
77 
78     next minor revisions by Jean-Pierre Demailly (demailly@ujf-grenoble.fr)
79         1.4  04/03/99  Change in city management
80         1.5  15/03/99  Color support
81         1.6  28/03/99  Iconic stuff fixed
82         1.7  17/08/99  Calculation of city distances
83         1.8  20/08/99  Fixed small bug in mono mode
84         1.9  21/01/00  Iconic stuff fixed again (didn't work for all WMHints!)
85         2.0  21/04/00  Added XFlush in doTimeout to force update
86         2.1  06/06/00  Updated latlong.txt with a better source
87 
88     major rewrite of GUI (Jean-Pierre Demailly, demailly@ujf-grenoble.fr)
89        3.00  08/26/00  Many bug corrections, GUI improvements (keyboard and
90                        mouse controls), new functions, resulting in a much
91                        more powerful program.
92        3.04  09/07/00  Final bug fix release of 3.0x
93        3.10  10/03/00  Menu window and a lot of new improvements as well.
94        3.13  10/26/00  Final bug fix release of 3.1x
95        3.20  11/20/00  Drastic GUI and features improvements (color maps,
96                        loadable xpm maps,...), hopefully sunclock now
97                        looks nice!
98        3.21  12/04/00  Final bug fix release of 3.2x x<5
99        3.25  12/21/00  Dockable, multi-window version
100        3.28  01/15/01  Final bug fix release of 3.2x x>=5
101        3.30  02/20/01  Sunclock now loads JPG images, and images are resizable
102        3.32  03/19/01  Implementation of the zoom widget
103        3.34  03/28/01  Vast improvements in the zoom widget
104        3.35  04/11/01  Correctly handles bigendian machines + option widget
105        3.38  04/17/01  Substantial improvements in option handling
106                        still buggy though (depending on WM)
107        3.41  05/19/01  The Moon is shown on the map
108        3.43  06/14/01  Cities can be managed at runtime and changed with zoom
109        3.44  06/22/01  Sunclock can write the map to the root window
110 */
111 
112 #include <unistd.h>
113 #include <stdlib.h>
114 #include <stdio.h>
115 #include <sys/types.h>
116 #include <sys/stat.h>
117 #include <string.h>
118 #include <X11/Xatom.h>
119 
120 #define DEFVAR
121 #include "sunclock.h"
122 #include "langdef.h"
123 #include "bitmaps.h"
124 
125 /*
126  *  external routines
127  */
128 
129 #ifdef NEW_CTIME
130 extern char *   timezone();
131 #endif
132 
133 extern double   jtime();
134 extern double   gmst();
135 extern long     jdate();
136 extern void     sunpos();
137 extern double   phase();
138 
139 extern char *   salloc();
140 extern int      readVMF();
141 extern int      readGIF();
142 extern int      readJPEG();
143 extern int      readXPM();
144 extern int      readPNG();
145 
146 extern void free_dirlist();
147 
148 extern char *tildepath();       /* Returns path to ~/<file> */
149 
150 extern int  getPlacement();
151 extern int  getState();
152 extern int getNumCmd();
153 
154 extern Window newWindow();
155 
156 extern void showManual();
157 
158 extern void checkGeom();
159 extern void adjustGeom();
160 extern void setSizeHints();
161 extern void setClassHints();
162 extern void shutDown();
163 extern void setProtocols();
164 extern void destroyGCs();
165 
166 extern void setupMenu();
167 extern void PopMenu();
168 extern void showMenuHint();
169 extern void processMenuAction();
170 
171 extern void setupFilesel();
172 extern void PopFilesel();
173 extern void processFileselAction();
174 
175 extern void checkZoomSettings();
176 extern void setZoomDimension();
177 extern void setZoomAspect();
178 extern void showZoomHint();
179 extern void setupZoom();
180 extern void PopZoom();
181 extern void activateZoom();
182 extern void processZoomAction();
183 
184 extern void showOptionHint();
185 extern void resetStringLength();
186 extern void setupOption();
187 extern void PopOption();
188 extern void activateOption();
189 extern void processOptionAction();
190 
191 extern void showUrbanHint();
192 extern void updateUrbanEntries();
193 extern void setupUrban();
194 extern void PopUrban();
195 extern void activateUrban();
196 extern void processUrbanAction();
197 
198 extern void setAuxilWins();
199 extern void RaiseAndFocus();
200 
201 void doTimeout();
202 
203 char share_i18n[] = SHAREDIR"/i18n/Sunclock.**";
204 char app_default[] = SHAREDIR"/Sunclockrc";
205 
206 char * ProgName;
207 char * Title = NULL;
208 
209 char * widget_type[7] =
210            { "clock", "map", "menu", "file selector",
211              "zoom", "option", "urban selector" };
212 
213 char * ClassName = NULL;
214 char * ClockClassName = NULL;
215 char * MapClassName = NULL;
216 char * AuxilClassName = NULL;
217 
218 char * Default_img_file = NULL;
219 char * Clock_img_file = NULL;
220 char * Map_img_file = NULL;
221 char * Zoom_img_file = NULL;
222 
223 char * share_maps_dir = NULL;
224 char * image_dir = NULL;
225 char * rc_file = NULL;
226 char * vmfcolors = NULL;
227 char * vmfrange = NULL;
228 char * vmfcoordformat = NULL;
229 
230 char * ExternAction = NULL;
231 char * EditorCommand = NULL;
232 char **DateFormat = NULL;
233 char * ListFormats = NULL;
234 
235 char **dirtable = NULL;
236 char * freq_filter = "";
237 
238 char oldlanguage[4] = "en";
239 char language[4] = "";
240 
241 Pixmap textpix = 0, zoompix = 0, rootpix = 0;
242 ;
243 
244 struct Sundata *Seed = NULL,
245                *MenuCaller = NULL,
246                *FileselCaller = NULL,
247                *ZoomCaller = NULL,
248                *OptionCaller = NULL,
249                *UrbanCaller = NULL,
250                *RootCaller = NULL;
251 
252 char    *Color[NUMPIXELS];
253 
254 char    *DefaultColor[NUMPIXELS] = {
255 "White", "White", "Grey92", "Grey92", "Grey92",
256 "White", "White", "Grey84", "White",
257 "Black", "Black", "Black",
258 "Black", "Grey50", "Grey95", "White",
259 "Black", "Black",
260 "Black", "Black", "Red", "Black",
261 "SkyBlue2", "Brown", "SkyBlue2", "Blue", "Magenta",
262 "Red", "Orange", "Red", "Red3", "Pink1", "Pink2",
263 "Yellow", "Khaki", "Black", "Black", "Black", "Black"};
264 
265 #define FALLBACKTOWHITE 9
266 
267 char *colorfield[NUMPIXELS] =
268 { "clockbg", "mapbg", "menubg", "clockstripbg", "mapstripbg",
269   "zoombg", "optionbg", "buttonbg", "star",
270   "clockfg", "mapfg", "menufg",
271   "buttonfg1", "buttonfg2", "buttonfg3", "buttonfg4",
272   "clockstripfg", "mapstripfg",
273   "zoomfg", "optionfg", "weak", "root",
274   "caret", "change", "choice", "directory", "image", "cityname",
275   "city0", "city1", "city2", "mark1", "mark2", "sun", "moon",
276   "line", "meridian", "parallel", "tropic"};
277 
278 char * SunFont[NUMFONTS];
279 char * DefaultFont[NUMFONTS]= {
280   "6x10",  "6x13",  "6x13",  "6x10",  "6x10", "6x13"};
281 char * fontfield[NUMFONTS] = {
282   "clockstrip", "mapstrip", "coord", "city", "label", "menu"};
283 
284 
285 char *          Display_name = NULL;
286 char *          CityInit = NULL;
287 char *          SpotSizes = NULL;
288 char *          SizeLimits = NULL;
289 
290 Display *       dpy;
291 Visual *        visual;
292 Colormap        cmap0, tmp_cmap;
293 
294 Flags           gflags;
295 ZoomSettings    gzoom;
296 
297 int             scr;
298 int             bigendian;
299 int             color_depth;
300 int             color_pad;
301 int             bytes_per_pixel;
302 int             total_colors;
303 int             text_input = NULL_INPUT;
304 
305 int             textheight = 0;
306 int             textwidth = 40;
307 int             coordvalheight;
308 int             coordvalwidth;
309 int             extra_width = 10;
310 
311 int             focus_in = 0;
312 int             button_pressed = 0;
313 int             control_key = 0;
314 int             precedence = 0;
315 int             option_changes = 1;
316 int             auxil_changed = 0;
317 int             erase_obj = 0;
318 int             root_period = 30;
319 int             screen_saver = 0;
320 int             random_rootpos = 0;
321 
322 int             zoom_mode = 0;
323 int             zoom_active = 1;
324 
325 KeySym          menu_newhint = ' ';
326 char            zoom_newhint = ' ';
327 char            option_newhint = ' ';
328 char            urban_newhint = ' ';
329 
330 char            menu_lasthint = '\0';
331 char            zoom_lasthint = '\0';
332 char            option_lasthint = '\0';
333 char            urban_lasthint = '\0';
334 
335 TextEntry       option_entry;
336 TextEntry       urban_entry[5];
337 
338 Pixel           black, white;
339 
340 Atom            wm_delete_window, wm_protocols;
341 
342 Window          Root,
343                 Menu = 0, Filesel = 0, Zoom = 0, Option = 0, Urban = 0;
344 
345 struct Geometry MapGeom    = { 0, 30,  30, 792, 396, 320, 160 };
346 struct Geometry ClockGeom  = { 0, 30,  30, 128,  64,  48,  24 };
347 struct Geometry MenuGeom   = { 0,  0,  30, 792,  40, 792,  40 };
348 struct Geometry FileselGeom= { 0,  0,  30, 450, 180, 400,  80 };
349 struct Geometry ZoomGeom   = { 0,  0,  30, 500, 320, 360, 250 };
350 struct Geometry OptionGeom = { 0,  0,  30, 630,  80, 630,  80 };
351 struct Geometry UrbanGeom  = { 0,  0,  30, 640, 120, 360, 120 };
352 
353 int             city_cat = 5;
354 int             *city_spotsizes;
355 int             *city_sizelimits;
356 
357 int             urban_t[5], urban_x[5], urban_y[5], urban_w[5];
358 
359 int             win_type = 0;
360 int             placement = -1;
361 int             place_shiftx = 0;
362 int             place_shifty = 0;
363 int             color_alloc_failed = 0;
364 int             num_formats;
365 int             runlevel;
366 int             verbose = 1;
367 int             reformat = 0;
368 int		min_zoomwidth = 0;
369 int		min_zoomheight = 0;
370 int             citycheck = 0;
371 int             num_lines;
372 int             num_table_entries;
373 
374 int             label_shift = 0;
375 int             filesel_shift = 0;
376 int             zoom_shift = 0;
377 
378 int             horiz_drift = 0;
379 int             vert_drift =  0;
380 
381 int             do_menu = 0;
382 int             do_filesel = 0;
383 int             do_zoom = 0;
384 int             do_option = 0;
385 int             do_urban = 0;
386 int             do_root = 0;
387 
388 int             do_hint = 0;
389 int             do_dock = 0;
390 int             do_sync = 0;
391 int             do_zoomsync = 0;
392 
393 int             time_jump = 0;
394 
395 long            last_time = 0;
396 long            progress_value[6] = { 60, 3600, 86400, 604800, 2592000, 0 };
397 
398 double          darkness = 0.5;
399 double          rootdx = 0.5;
400 double          rootdy = 0.5;
401 double          atm_refraction = ATM_REFRACTION;
402 double          atm_diffusion = ATM_DIFFUSION;
403 
404 /* Root and last records for cities */
405 
406 City position, *cityroot = NULL, *citylast = NULL, *cityinit = NULL;
407 
408 /*
409  * String copy and reallocation/deallocation routine
410  */
411 
412 void
StringReAlloc(t,s)413 StringReAlloc(t, s)
414 char **t, *s;
415 {
416        if (*t) free(*t);
417 
418        if (s) {
419           *t = (char *) salloc((strlen(s)+1)*sizeof(char));
420           strcpy(*t, s);
421        } else
422           *t = NULL;
423 }
424 
425 /*
426  * Read language i18n file
427  */
428 
429 void
readLanguage()430 readLanguage()
431 {
432     FILE *rc;           /* File pointer for rc file */
433     char buf[1028];      /* Buffer to hold input lines */
434     int i, j, k, l, m, p, tot;
435 
436     i = strlen(share_i18n);
437 
438     for (j=0; j<=1; j++) share_i18n[i+j-2] = tolower(language[j]);
439 
440     j = k = l = m = 0;
441     tot = 2;
442     if ((rc = fopen(share_i18n, "r")) != NULL) {
443       while (fgets(buf, 1024, rc)) {
444         if (buf[0] != '#') {
445                 p = strlen(buf) - 1;
446                 if (p>=0 && buf[p]=='\n') buf[p] = '\0';
447                 if (j<7) { strcpy(Day_name[j], buf); j++; } else
448                 if (k<12) { strcpy(Month_name[k], buf); k++; } else
449                 if (l<L_END) {
450                    StringReAlloc(&Label[l], buf);
451                    l++;
452                 } else
453                 if (m<N_HELP) {
454 		   StringReAlloc(&Help[m], buf);
455                    m++;
456                 } else break;
457         }
458       }
459       strncpy(oldlanguage, language, 2);
460 
461       fclose(rc);
462     } else {
463         fprintf(stderr,
464              "Unable to open language in %s\n", share_i18n);
465 	strncpy(language, oldlanguage, 2);
466     }
467 }
468 
469 
470 /*
471  * Usage output
472  */
473 
474 void
Usage()475 Usage()
476 {
477 #define SP		"\t"
478      fprintf(stderr,
479      "%s: version %s, %s\n\nUsage:  %s [-options ...]\n\n%s\n\n"
480      SP"[-help] [-listmenu] [-version] [-citycheck]\n"
481      SP"[-display name] [-sharedir directory] [-citycategories value]\n"
482      SP"[-clock] [-map] [-dock] [-undock]\n"
483      SP"[-menu] [-nomenu] [-filesel] [-nofilesel]\n"
484      SP"[-zoom] [-nozoom] [-option] [-nooption] [-urban] [-nourban]\n"
485 "**" SP"[-language name] [-rcfile file]\n"
486      SP"[-command string] [-helpcommand string]\n"
487      SP"[-mapmode * <L,C,S,D,E>] [-dateformat string1|string2|...]\n"
488      SP"[-image file] [-clockimage file] [-mapimage file] [-zoomimage file]\n"
489      SP"[-clockgeom <geom>] [-mapgeom <geom>]\n"
490      SP"[-auxilgeom <geom>] [-menugeom <geom>] [-selgeom <geom>]\n"
491      SP"[-zoomgeom <geom>] [-optiongeom <geom>] [-urbangeom <geom>]\n"
492      SP"[-title name] [-mapclassname name] [-clockclassname name]\n"
493      SP"[-auxilclassname name] [-classname name]\n"
494      SP"[-setfont <field>|<fontsetting>{|<languages>}]\twhere\n"
495      SP"\t<field> = clockstrip, mapstrip, coord, city, label, menu\n"
496      SP"\t<languages> = comma separated list (optional)\n"
497      SP"[-verbose] [-silent] [-synchro] [-nosynchro] [-zoomsync] [-nozoomsync]\n"
498      SP"[-colorlevel level] [-aspect mode]\n"
499      SP"[-placement (random, fixed, center, NW, NE, SW, SE)]\n"
500      SP"[-placementshift x, y] [-extrawidth value]\n"
501      SP"[-decimal] [-dms] [-city name] [-position latitude|longitude]\n"
502      SP"[-addcity size|name|lat|lon|tz] [-removecity name (name|lat|lon)]\n"
503      SP"[-rootdx value] [-rootdy value] [-fixedrootpos] [-randomrootpos]\n"
504      SP"[-screensaver] [-noscreensaver] [-rootperiod value (in seconds)]\n"
505      SP"[-animation] [-noanimation] [-animateperiod value (in seconds)]\n"
506      SP"[-progress number[s,m,h,d,M,Y]] [-jump number[s,m,h,d,M,Y]]\n"
507      SP"[-shading mode=0,1,2,3,4,5] [-diffusion value] [-refraction value]\n"
508      SP"[-night] [-terminator] [-twilight] [-luminosity] [-lightgradient]\n"
509      SP"[-nonight] [-darkness value<=1.0] [-colorscale number>=1]\n"
510      SP"[-coastlines] [-contour] [-landfill] [-fillmode number=0,1,2]\n"
511      SP"[-mag value] [-magx value] [-magy value] [-dx value ] [-dy value]\n"
512      SP"[-spotsizes s1|s2|s3|... (0<=si<=5, 1<=i<=citycategories)]\n"
513      SP"[-sizelimits w1|w2|w3|... (wi = zoom width values)]\n"
514      SP"[-citymode mode=0,1,2,3] [-objectmode mode=0,1,2]\n"
515      SP"[-sun] [-nosun] [-moon] [-nomoon] [-tropics] [-notropics]\n"
516      SP"[-meridianmode mode=0,1,2,3] [-parallelmode mode=0,1,2,3]\n"
517      SP"[-meridianspacing value] [-parallelspacing value]\n"
518      SP"[-dottedlines] [-plainlines] [-bottomline] [-nobottomline]\n"
519      SP"[-reformat] [-vmfcolors color1|color2|color3...]\n"
520      SP"[-vmfrange a|b|c|d] [-vmfcoordformat format] [-vmfflags integer]\n"
521      SP"[-setcolor field|color]\n\n"
522      SP"field = clockbg, clockfg, mapbg, mapfg, menubg, menufg,\n"
523      SP"buttonbg, buttonfg1, buttonfg2, buttonfg3, buttonfg4,\n"
524      SP"clockstripbg, clockstripfg, mapstripbg, mapstripfg,\n"
525      SP"cityname, zoombg, zoomfg, optionbg, optionfg, caret, change, choice,\n"
526      SP"directory, image, city0, city1, city2, mark1, mark2, line,\n"
527      SP"meridian, parallel, tropic, sun, moon,\n\n"
528      SP"color = red, yellow, ... (rgb.txt) / hexadecimal #ijklmn\n\n%s\n\n",
529         ProgName, VERSION, COPYRIGHT, ProgName, Label[L_LISTOPTIONS],
530         Label[L_CONFIG]);
531 }
532 
533 void
ListMenu()534 ListMenu()
535 {
536         int i;
537 
538         fprintf(stderr, "%s\n", Label[L_SHORTHELP]);
539         for (i=0; i<N_HELP; i++)
540         fprintf(stderr, "%s %c : %s\n", Label[L_KEY], CommandKey[i], Help[i]);
541         fprintf(stderr, "\n");
542 }
543 
544 void
initValues()545 initValues()
546 {
547         int i;
548 
549         gflags.colorlevel = FULLCOLORS;
550         gflags.fillmode = 2;
551         gflags.vmfflags = -1;
552         gflags.dotted = 0;
553         gflags.colorscale = 16;
554 
555 	gflags.animate = 0;
556 	gflags.animperiod = 0;
557         gflags.progress = 0;
558 
559         gflags.update = 4;
560         gflags.bottom = 0;
561         gflags.map_mode = LEGALTIME;
562         gflags.clock_mode = 0;
563         gflags.hours_shown = 0;
564         gflags.dms = 0;
565         gflags.shading = 2;
566         gflags.citymode = 1;
567         gflags.objectmode = 1;
568         gflags.objects = 3;
569         gflags.meridian = 0;
570         gflags.parallel = 0;
571 
572         gzoom.mode = 0;
573         gzoom.fx = 1.0;
574         gzoom.fy = 1.0;
575         gzoom.fdx = 0.5;
576         gzoom.fdy = 0.5;
577         gzoom.meridspacing = 0.0;
578         gzoom.paralspacing = 0.0;
579 
580         option_entry.string = NULL;
581         for (i=0; i<=4; i++) urban_entry[i].string = NULL;
582 
583         for (i=0; i<NUMPIXELS; i++)
584             Color[i] = strdup(DefaultColor[i]);
585         for (i=0; i<NUMFONTS; i++)
586             SunFont[i] = strdup(DefaultFont[i]);
587 
588         position.lat = 100.0;
589         position.tz = NULL;
590 
591         StringReAlloc(&share_maps_dir, SHAREDIR"/earthmaps/");
592         StringReAlloc(&image_dir, share_maps_dir);
593 
594         StringReAlloc(&Default_img_file, SHAREDIR"/earthmaps/vmf/timezones.vmf");
595         StringReAlloc(&Zoom_img_file, SHAREDIR"/earthmaps/vmf/landwater.vmf");
596         StringReAlloc(&Clock_img_file, Default_img_file);
597         StringReAlloc(&Map_img_file, Default_img_file);
598 
599         StringReAlloc(&ListFormats, STDFORMATS);
600         StringReAlloc(&EditorCommand, EDITORCOMMAND);
601         StringReAlloc(&SpotSizes, "1|2|3|4|5");
602         StringReAlloc(&SizeLimits, "0|580|2500|6000|12000");
603 
604 	for (i=0; i<L_END; i++) Label[i] = strdup(Label[i]);
605 	for (i=0; i<N_HELP; i++) Help[i] = strdup(Help[i]);
606 }
607 
608 /*
609  *  Fill line procedure for scanned images
610  */
611 #define RANGE 252
612 long lr[RANGE], lg[RANGE], lb[RANGE], lnum[RANGE];
613 
614 void
fill_line(char * scan,char * c,int w,int zw,int wp,int dx)615 fill_line(char *scan, char* c, int w, int zw, int wp, int dx)
616 {
617 unsigned char r, g, b;
618 int i, j, k, m;
619   k = 0;
620   if (color_depth>16) {
621     if (bigendian)
622        k = bytes_per_pixel - 3;
623     for (i = 0; i < w; i++) {
624        j = 3 * (((i+dx) * wp)/zw);
625        if (bigendian) {
626           c[k] = scan[j];
627           c[k+1] = scan[j+1];
628           c[k+2] = scan[j+2];
629        } else {
630           c[k] = scan[j+2];
631           c[k+1] = scan[j+1];
632           c[k+2] = scan[j];
633        }
634        k +=  bytes_per_pixel;
635     }
636   } else
637   if (color_depth==16)
638      for (i = 0; i < w; i++) {
639        j = 3 * (((i+dx) * wp)/zw);
640        r = scan[j];
641        g = scan[j+1];
642        b = scan[j+2];
643        /* blue  c[k] = 31;  c[k+1] = 0;
644           green c[k] = 224  (low weight) c[k+1] = 7 (high weight)
645           red   c[k] = 0;   c[k+1] = 248; */
646        if (bigendian) {
647           c[k+1] = (((b&248)>>3) | ((g&28)<<3));
648           c[k] = (((g&224)>>5) | (r&248));
649        } else {
650           c[k] = (((b&248)>>3) | ((g&28)<<3));
651           c[k+1] = (((g&224)>>5) | (r&248));
652        }
653        k += 2;
654      }
655   else
656   if (color_depth==15)
657      for (i = 0; i < w; i++) {
658        j = 3 * (((i+dx) * wp)/zw);
659        r = scan[j];
660        g = scan[j+1];
661        b = scan[j+2];
662        /* blue  c[k] = 31;  c[k+1] = 0;
663           green c[k] = 224  (low weight) c[k+1] = 7 (high weight)
664           red   c[k] = 0;   c[k+1] = 248; */
665        if (bigendian) {
666           c[k+1] = (b&248)>>3 | (g&56)<<2;
667           c[k] = (g&192)>>6 | (r&248)>>1;
668        } else {
669           c[k] = (b&248)>>3 | (g&56)<<2;
670           c[k+1] = (g&192)>>6 | (r&248)>>1;
671        }
672        k += 2;
673      }
674   else {
675      for (i = 0; i < w; i++) {
676        j = 3 * (((i+dx) * wp)/zw);
677        r = scan[j];
678        g = scan[j+1];
679        b = scan[j+2];
680        c[k] = (unsigned char)
681        (((7*g)/256)*36)+(((6*r)/256)*6)+((6*b)/256);
682        m = (unsigned char)c[k];
683        lr[m] += r;
684        lg[m] += g;
685        lb[m] += b;
686        lnum[m] += 1;
687        k += 1;
688      }
689   }
690 }
691 
692 void
str2numval(s,val,max)693 str2numval(s, val, max)
694 char *s;
695 int *val;
696 int max;
697 {
698 int i, j, l;
699 char *ptr;
700 
701     l = strlen(s);
702 
703     j = 0;
704     ptr = s;
705     for (i=0; i<=l; i++) {
706         if (s[i] == '|' || i == l) {
707 	   s[i] = '\0';
708 	   if (j>=city_cat) break;
709            val[j] = atoi(ptr);
710 	   if (max>0 && val[j]>max) val[j] = max;
711 	   ++j;
712 	   ptr = s+i+1;
713            if (i<l) s[i] = '|';
714 	}
715     }
716 
717     for (i=j; i<city_cat; i++) val[i] = val[j-1];
718 }
719 
720 void
correctValues()721 correctValues()
722 {
723 	if (color_depth<=8 && gflags.colorscale>256)
724            gflags.colorscale = 256;
725 
726         if ((gflags.colorlevel<FULLCOLORS) && (gflags.shading>=2))
727            gflags.shading = 1;
728 
729         if (color_depth<=16)
730            gflags.darkness = (unsigned short) ((1.0-darkness) * 255.25);
731 	else
732            gflags.darkness = (unsigned short) ((1.0-darkness) * 32767.25);
733 
734 	if (do_dock) win_type = 0;
735 
736         str2numval(SpotSizes, city_spotsizes, CITYBITMAPS);
737         str2numval(SizeLimits, city_sizelimits, -1);
738 }
739 
740 int
needMore(argc,argv)741 needMore(argc, argv)
742 int *                  argc;
743 char **                argv;
744 {
745 	-- *argc;
746         if (*argc == 0) {
747                 if (runlevel == PARSECMDLINE) {
748 		   Usage();
749 		   exit(1);
750 		}
751 		   else
752                 if (runlevel == RUNTIMEOPTION) {
753                    fprintf(stderr, "Invalid option specification\n");
754 		   runlevel = FAILEDOPTION;
755 		} else
756                    fprintf(stderr, "Error in config file : \n");
757 
758                 fprintf(stderr, "%s: option `%s' (with no argument) incorrect\n",
759                         ProgName, *argv);
760 
761 		if (runlevel == PARSECMDLINE) exit(1);
762 	        return 1;
763         }
764 	return 0;
765 }
766 
767 void
getGeom(s,g)768 getGeom(s, g)
769 register char *                 s;
770 register struct Geometry *      g;
771 {
772         register int            mask;
773 
774         mask = XParseGeometry(s, &g->x, &g->y, &g->width, &g->height);
775         if (mask == 0) {
776                 fprintf(stderr, "%s: `%s' is a bad geometry specification\n",
777                         ProgName, s);
778                 return;
779         }
780         if (g->width<g->w_mini) g->width = g->w_mini;
781         if (g->height<g->h_mini) g->height = g->h_mini;
782         g->mask = mask;
783         if ( placement<=RANDOM && (mask & ( XValue | YValue)) )
784            placement = FIXED;
785 }
786 
787 void
parseFormats(char * format)788 parseFormats(char * format)
789 {
790         int i, j, l;
791 
792         l = strlen(format);
793 
794         j = 1;
795         while (j>0) {
796            j = 0;
797            if (format[0]=='|') { ++format; --l; j = 1;}
798            if (l>=2 && format[l-1]=='|' && format[l-2]!= '%')
799                                { format[l-1]='\0'; --l; j = 1; }
800         }
801 
802         num_formats = 1;
803         for (i=1; i<l-1; i++)
804             if (format[i]=='|' && format[i-1]!='%') ++num_formats;
805 
806         DateFormat = (char **) salloc(num_formats*sizeof(char *));
807 
808         DateFormat[0] = format;
809         j = 0;
810 
811         for (i=1; i<l-1; i++)
812           if (format[i]=='|' && format[i-1]!='%') {
813             ++j;
814             format[i] = '\0';
815             DateFormat[j] = format+i+1;
816           }
817 }
818 
setWindowAspect(Context)819 int setWindowAspect(Context)
820 struct Sundata * Context;
821 {
822    unsigned int b, i, j;
823    int change;
824    struct Geometry *       Geom;
825    double f;
826 
827    if (Context->newzoom.mode <= 1)
828       f = 1.0;
829    else
830       f = sin(M_PI*Context->newzoom.fdy);
831 
832    change = 0;
833    i = Context->geom.width;
834 
835    j = (unsigned int) (0.5 + Context->newzoom.fx *
836           (double)Context->geom.width / ( 2.0 * Context->newzoom.fy * f) );
837    b = DisplayHeight(dpy,scr) - Context->hstrip - vert_drift - 5;
838    if (j<=0) j=1;
839    if (j>b) {
840       i = (Context->geom.width * b) / j;
841       j = b;
842    }
843 
844    Geom = (Context->wintype)? &MapGeom : &ClockGeom;
845 
846    if (j<Geom->h_mini) {
847       i = (i * Geom->h_mini)/j;
848       j = Geom->h_mini;
849    }
850 
851    if (Context->geom.width != i) {
852       change = 1;
853       Context->geom.width = Geom->width = i;
854    }
855    if (Context->geom.height != j) {
856       change = 1;
857       Context->geom.height = Geom->height = j;
858    }
859 
860    if (change && verbose)
861       fprintf(stderr, "Resizing window to width = %d , height = %d\n", i, j);
862 
863    return change;
864 }
865 
866 void
checkRCfile(argc,argv)867 checkRCfile(argc, argv)
868 register int                    argc;
869 register char **                argv;
870 {
871         int i;
872 
873         for (i=1; i<argc; i++) {
874            if (i<argc-1 && !strcasecmp(argv[i], "-rcfile"))
875               rc_file = argv[i+1];
876            if (i<argc-1 && !strcasecmp(argv[i], "-language")) {
877               strncpy(language, argv[i+1], 2);
878 	      language[2] = '\0';
879 	   }
880            if (strcasecmp(argv[i], "-verbose") == 0)
881 	      verbose = 1;
882 	}
883 
884         if (strcmp(language, oldlanguage)) readLanguage();
885 }
886 
887 double
dms2decim(string)888 dms2decim(string)
889 char *string;
890 {
891 double deg=0.0, min=0.0, sec=0.0;
892 
893     if (index(string, '�')) {
894        sscanf(string, "%lf�%lf'%lf", &deg, &min, &sec);
895        return deg+(min+sec/60.0)/60.0;
896     } else
897        return atof(string);
898 }
899 
900 void
scanPosition(string,city)901 scanPosition(string, city)
902 char * string;
903 City * city;
904 {
905 int i, l;
906 char lat[80], lon[80];
907 
908      if (!string || !*string) return;
909 
910      l = strlen(string);
911      for (i=0; i<l; i++) if (string[i] == '|') string[i] = ' ';
912 
913      if (sscanf(string, "%s %s", lat, lon)<2) {
914         city->lat = -100.0;
915         return;
916      }
917      city->lat = dms2decim(lat);
918      if (fabs(city->lat)>90.0) city->lat = -100.0;
919      city->lon = dms2decim(lon);
920      city->lon = fixangle(city->lon + 180.0) - 180.0;
921 }
922 
923 City *
searchCityLocation(params)924 searchCityLocation(params)
925 char *params;
926 {
927      City *c;
928      char name[80], lat[80], lon[80];
929      double dlat=0.0, dlon=0.0;
930      int i, l, complete = 0;
931 
932      if (!params || !*params) return NULL;
933 
934      if (index(params, '|')) {
935         l =strlen(params);
936         for (i=0; i<l; i++) {
937             if (params[i] == ' ') params[i]= '\037';
938             if (params[i] == '|') params[i]= ' ';
939 	}
940         sscanf(params, "%s %s %s", name, lat, lon);
941         l = strlen(lat); lat[l-1] = '\0';
942         l = strlen(lon); lon[l-1] = '\0';
943 	dlat = dms2decim(lat);
944 	dlon = dms2decim(lon);
945         l = strlen(name);
946 	while (l>0 && name[l-1]=='\037') {
947 	   --l;
948 	   name[l] = '\0';
949 	}
950         for (i=0; i<l; i++) if (name[i] == '\037') name[i] = ' ';
951         complete = 1;
952      } else
953         strncpy(name, params, 80);
954 
955      c = cityroot;
956      while (c) {
957         if ((!*name || !strcasecmp(c->name, name)) &&
958             (!complete || (fabs(c->lon-dlon)<0.5 && fabs(c->lat-dlat)<0.5)))
959 	   return c;
960         c = c->next;
961      }
962      return NULL;
963 }
964 
965 void
freecity(c)966 freecity(c)
967 City * c;
968 {
969      if (cityinit==c) cityinit = NULL;
970      free(c->name);
971      free(c);
972 }
973 
974 void
removeCity(c)975 removeCity(c)
976 City *c;
977 {
978      City *cp, *cn;
979 
980      if (!c) return;
981      cn = c->next;
982      freecity(c);
983 
984      if (c == cityroot) {
985         cityroot = cn;
986 	return;
987      }
988 
989      cp = cityroot;
990      while (cp) {
991         if (cp->next == c) {
992 	    cp->next = cn;
993             if (c == citylast) citylast = cp;
994 	    return;
995 	}
996 	cp = cp->next;
997      }
998 }
999 
1000 City *
addCity(longparams)1001 addCity(longparams)
1002 char *longparams;
1003 {
1004      City *city;
1005      char name[80], lat[80], lon[80], tz[80];
1006      char params[256];
1007      int i, size;
1008 
1009      i = 0;
1010      if (longparams) {
1011         while (longparams[i] != '\0') {
1012            if (longparams[i]==' ') longparams[i] = '\037';
1013            if (longparams[i]=='|') longparams[i] = ' ';
1014            ++i;
1015         }
1016         if (sscanf(longparams,"%d %s %s %s %s", &size, name, lat, lon, tz)<5) {
1017            fprintf(stderr, "Incorrect number of parameters in -addcity\n");
1018 	   return NULL;
1019 	}
1020         i = 0;
1021         while (name[i] != '\0') {
1022            if (name[i]=='\037') name[i] = ' ';
1023            ++i;
1024         }
1025      } else {
1026        if (do_urban) {
1027           if (!*urban_entry[0].string) {
1028 	     strcpy(urban_entry[0].string, "???");
1029 	     return NULL;
1030 	  }
1031           strcpy(name, urban_entry[0].string);
1032           if (!*urban_entry[1].string) {
1033 	     strcpy(urban_entry[1].string, "???");
1034 	     return NULL;
1035 	  }
1036           strcpy(tz, urban_entry[1].string);
1037           if (!*urban_entry[2].string) return NULL;
1038           strcpy(lat, urban_entry[2].string);
1039           if (!*urban_entry[3].string) return NULL;
1040           strcpy(lon, urban_entry[3].string);
1041           if (!*urban_entry[4].string) return NULL;
1042           if (!sscanf(urban_entry[4].string, "%d", &size)) {
1043 	     strcpy(urban_entry[4].string, "?");
1044              return NULL;
1045 	  }
1046        } else
1047 	  return NULL;
1048      }
1049 
1050      if (citycheck || runlevel>READSYSRC) {
1051         sprintf(params, "%s\037|%s\037|%s\037", name, lat, lon);
1052 	if ((city=searchCityLocation(params))) {
1053 	   sprintf(params, Label[L_CITYWARNING1], name, lat, lon, "\n  ");
1054 	   if (verbose || runlevel != READUSERRC)
1055 	      fprintf(stderr, "%s\n", params);
1056 	   if (do_urban) {
1057 	      urban_newhint = '?';
1058 	      showUrbanHint(params);
1059 	      return NULL;
1060 	   }
1061 	   if (verbose || runlevel != READUSERRC) {
1062 	      sprintf(params, Label[L_CITYWARNING2], name);
1063 	      fprintf(stderr, "%s\n", params);
1064 	   }
1065            removeCity(city);
1066 	}
1067      }
1068 
1069      /* Create the record for the city */
1070      if ((city = (City *) calloc(1, sizeof(City))) == NULL) return NULL;
1071 
1072      /* Set up the record */
1073 
1074      city->name = strdup(name);
1075      city->lat = dms2decim(lat);
1076      city->lon = dms2decim(lon);
1077      city->size = size;
1078      city->tz = strdup(tz);
1079 
1080      /* Link it into the list */
1081 
1082      if (!cityroot)
1083         cityroot = citylast = city;
1084      else {
1085         citylast->next = city;
1086 	citylast = city;
1087      }
1088      city ->next = NULL;
1089      return city;
1090 }
1091 
1092 void
deleteMarkedCity()1093 deleteMarkedCity()
1094 {
1095      City *c, *cp = NULL;
1096 
1097      if (!do_urban || !UrbanCaller) return;
1098 
1099      c = cityroot;
1100      while (c) {
1101         if (c == UrbanCaller->mark1.city) {
1102 	   UrbanCaller->mark1.city = NULL;
1103 	   if (cp) {
1104 	      cp->next = c->next;
1105 	   } else
1106 	      cityroot = c->next;
1107 	   if (c == citylast) citylast = cp;
1108 	   if (c == UrbanCaller->lastmarked) UrbanCaller->lastmarked = NULL;
1109            freecity(c);
1110 	   return;
1111 	}
1112         cp = c;
1113         c = c->next;
1114      }
1115 }
1116 
1117 int
1118 readRC();
1119 
1120 int
parseColor(s)1121 parseColor(s)
1122 char * s;
1123 {
1124 int i, l, done = 0;
1125 char *ptr = NULL;
1126 
1127     l = strlen(s);
1128     for (i=0; i<l; i++) {
1129         if (s[i]=='|') {
1130 	   s[i] = '\0';
1131 	   ptr = s+i+1;
1132 	   break;
1133 	}
1134     }
1135     l = i;
1136 
1137     if (ptr) {
1138        for (i=0; i<NUMPIXELS; i++)
1139           if (!strcasecmp(s, colorfield[i])) {
1140              StringReAlloc(&Color[i], ptr);
1141              done = 1;
1142 	     break;
1143           }
1144        s[l] = '|';
1145     }
1146 
1147     if (!ptr || !done) {
1148        fprintf(stderr, "Incorrect -setcolor specification\n");
1149        return 1;
1150     }
1151     return 0;
1152 }
1153 
1154 int
parseFont(s)1155 parseFont(s)
1156 char * s;
1157 {
1158 int i, l1 = 0, l2 = 0, done;
1159 char *ptr1 = NULL, *ptr2 = NULL;
1160 
1161     done = strlen(s);
1162     for (i=0; i<done; i++) {
1163         if (s[i]=='|') {
1164 	   s[i] = '\0';
1165 	   if (!ptr1) {
1166 	      ptr1 = s+i+1;
1167 	      l1 = i;
1168 	   } else {
1169 	      ptr2 = s+i+1;
1170 	      l2 = i;
1171   	      break;
1172 	   }
1173 	}
1174     }
1175 
1176     /* Do not take font setting into account if selected language is
1177        not specified in list of languages */
1178     if (ptr2 && !strstr(ptr2, language)) return 0;
1179 
1180     if (!strcasecmp(s, "menu") && ptr1 && strcmp(ptr1, SunFont[4]))
1181        option_changes |= 1;
1182 
1183     done = 0;
1184     if (ptr1) {
1185        for (i=0; i<NUMFONTS; i++)
1186           if (!strcasecmp(s, fontfield[i])) {
1187              StringReAlloc(&SunFont[i], ptr1);
1188              done = 1;
1189 	     break;
1190           }
1191        s[l1] = '|';
1192        if (l2) s[l2] = '|';
1193     }
1194 
1195     if (!ptr1 || !done) {
1196        fprintf(stderr, "Incorrect -setfont specification\n");
1197        return 1;
1198     }
1199 
1200     return 0;
1201 }
1202 
1203 int
parseArgs(argc,argv)1204 parseArgs(argc, argv)
1205 int                    argc;
1206 char **                argv;
1207 {
1208         int     opt;
1209 
1210         while (--argc > 0) {
1211                 ++argv;
1212                 if (!strcasecmp(*argv, "-verbose"))
1213                         verbose = 1;
1214                 else if (!strcasecmp(*argv, "-reformat"))
1215                         reformat = 1;
1216                 else if (!strcasecmp(*argv, "-silent"))
1217                         verbose = 0;
1218                 else if (!strcasecmp(*argv, "-synchro"))
1219                         do_sync = 1;
1220                 else if (!strcasecmp(*argv, "-nosynchro"))
1221                         do_sync = 0;
1222                 else if (!strcasecmp(*argv, "-zoomsync"))
1223                         do_zoomsync = 1;
1224                 else if (!strcasecmp(*argv, "-nozoomsync"))
1225                         do_zoomsync = 0;
1226                 else if (!strcasecmp(*argv, "-animation"))
1227                         gflags.animate = 1;
1228                 else if (!strcasecmp(*argv, "-noanimation"))
1229                         gflags.animate = 0;
1230                 else if (!strcasecmp(*argv, "-coastlines"))
1231                         gflags.fillmode = 0;
1232                 else if (!strcasecmp(*argv, "-contour"))
1233                         gflags.fillmode = 1;
1234                 else if (!strcasecmp(*argv, "-landfill"))
1235                         gflags.fillmode = 2;
1236                 else if (!strcasecmp(*argv, "-dottedlines"))
1237                         gflags.dotted = 0;
1238                 else if (!strcasecmp(*argv, "-plainlines"))
1239                         gflags.dotted = 1;
1240                 else if (!strcasecmp(*argv, "-bottomline"))
1241                         gflags.bottom = 1;
1242                 else if (!strcasecmp(*argv, "-nobottomline"))
1243                         gflags.bottom = 0;
1244                 else if (!strcasecmp(*argv, "-decimal"))
1245                         gflags.dms = 0;
1246                 else if (!strcasecmp(*argv, "-dms"))
1247                         gflags.dms = gflags.dms = 1;
1248                 else if (!strcasecmp(*argv, "-nonight"))
1249                         gflags.shading = 0;
1250                 else if (!strcasecmp(*argv, "-night"))
1251                         gflags.shading = 1;
1252                 else if (!strcasecmp(*argv, "-terminator"))
1253                         gflags.shading = 2;
1254                 else if (!strcasecmp(*argv, "-twilight"))
1255                         gflags.shading = 3;
1256                 else if (!strcasecmp(*argv, "-luminosity"))
1257                         gflags.shading = 4;
1258                 else if (!strcasecmp(*argv, "-lightgradient"))
1259                         gflags.shading = 5;
1260                 else if (!strcasecmp(*argv, "-tropics"))
1261                         gflags.parallel |= 8;
1262                 else if (!strcasecmp(*argv, "-notropics"))
1263                         gflags.parallel &= 3;
1264                 else if (!strcasecmp(*argv, "-sun"))
1265                         gflags.objects |= 1;
1266                 else if (!strcasecmp(*argv, "-nosun"))
1267                         gflags.objects &= ~1;
1268                 else if (!strcasecmp(*argv, "-moon"))
1269                         gflags.objects |= 2;
1270 		else if (!strcasecmp(*argv, "-nomoon"))
1271                         gflags.objects &= ~2;
1272                 else if (!strcasecmp(*argv, "-dock"))
1273                         do_dock = 1;
1274                 else if (!strcasecmp(*argv, "-undock"))
1275                         do_dock = 0;
1276                 else if (runlevel == RUNTIMEOPTION) {
1277                         if (needMore(&argc, argv)) return(1);
1278                         goto options_with_parameter;
1279 		}
1280                 else if (!strcasecmp(*argv, "-citycheck"))
1281                         citycheck = 1;
1282                 else if (!strcasecmp(*argv, "-clock"))
1283                         win_type = 0;
1284                 else if (!strcasecmp(*argv, "-map"))
1285                         win_type = 1;
1286                 else if (!strcasecmp(*argv, "-screensaver") &&
1287 			 runlevel <= PARSECMDLINE) {
1288                         screen_saver = 1;
1289 			random_rootpos = 1;
1290 			win_type = 1;
1291 		}
1292                 else if (!strcasecmp(*argv, "-noscreensaver") &&
1293 			 runlevel <= PARSECMDLINE) {
1294                         screen_saver = 0;
1295 			random_rootpos = 0;
1296 		}
1297                 else if (!strcasecmp(*argv, "-fixedrootpos"))
1298                         random_rootpos = 0;
1299                 else if (!strcasecmp(*argv, "-randomrootpos"))
1300                         random_rootpos = 1;
1301                 else if (!strcasecmp(*argv, "-menu"))
1302                         do_menu = 1;
1303                 else if (!strcasecmp(*argv, "-nomenu"))
1304                         do_menu = 0;
1305                 else if (!strcasecmp(*argv, "-filesel"))
1306                         do_filesel = 1;
1307                 else if (!strcasecmp(*argv, "-nofilesel"))
1308                         do_filesel = 0;
1309                 else if (!strcasecmp(*argv, "-zoom"))
1310                         do_zoom = 1;
1311                 else if (!strcasecmp(*argv, "-nozoom"))
1312                         do_zoom = 0;
1313                 else if (!strcasecmp(*argv, "-option"))
1314                         do_option = 1;
1315                 else if (!strcasecmp(*argv, "-nooption"))
1316                         do_option = 0;
1317                 else if (!strcasecmp(*argv, "-urban"))
1318                         do_urban = 1;
1319                 else if (!strcasecmp(*argv, "-nourban"))
1320                         do_urban = 0;
1321                 else if (!strcasecmp(*argv, "-help")) {
1322 		        if (runlevel == PARSECMDLINE) {
1323 			   Usage();
1324 			   exit(0);
1325 			}
1326 		}
1327                 else if (!strcasecmp(*argv, "-listmenu")) {
1328                         if (runlevel == PARSECMDLINE) {
1329                            ListMenu();
1330 			   exit(0);
1331 			}
1332                 }
1333                 else if (!strcasecmp(*argv, "-version")) {
1334                         fprintf(stderr, "%s: version %s, %s\n",
1335                                 ProgName, VERSION, COPYRIGHT);
1336                         if (runlevel == PARSECMDLINE)
1337                           exit(0);
1338                         else
1339                           return(0);
1340 		}
1341              else {
1342                 if (needMore(&argc, argv)) return(1);
1343                 if (!strcasecmp(*argv, "-display"))
1344                         StringReAlloc(&Display_name, *++argv);
1345                 else if (!strcasecmp(*argv, "-sharedir")) {
1346                         StringReAlloc(&share_maps_dir, *++argv);
1347                         strncpy(image_dir, *argv, 1020);
1348 		}
1349                 else if (!strcasecmp(*argv, "-citycategories")) {
1350                         city_cat = atoi(*++argv);
1351 			if (city_cat <= 0) city_cat = 1;
1352 			if (city_cat > 100) city_cat = 100;
1353 		}
1354                 else
1355 	        options_with_parameter :
1356                      if (!strcasecmp(*argv, "-rcfile")) {
1357 		        if (runlevel == RUNTIMEOPTION) {
1358 			   runlevel = READUSERRC;
1359                            if (readRC(*++argv)) runlevel = FAILEDOPTION;
1360                            if (runlevel != FAILEDOPTION)
1361                               runlevel = RUNTIMEOPTION;
1362 			}
1363 		}
1364                 else if (!strcasecmp(*argv, "-language")) {
1365                         strncpy(language, *++argv, 2);
1366 		        language[2] = '\0';
1367 			if (strcmp(language, oldlanguage)) readLanguage();
1368                 }
1369 	        else if (!strcasecmp(*argv, "-title"))
1370                         StringReAlloc(&Title, *++argv);
1371 	        else if (!strcasecmp(*argv, "-clockclassname"))
1372                         StringReAlloc(&ClockClassName, *++argv);
1373 	        else if (!strcasecmp(*argv, "-mapclassname"))
1374                         StringReAlloc(&MapClassName, *++argv);
1375 	        else if (!strcasecmp(*argv, "-auxilclassname"))
1376                         StringReAlloc(&AuxilClassName, *++argv);
1377 	        else if (!strcasecmp(*argv, "-classname"))
1378                         StringReAlloc(&ClassName, *++argv);
1379                 else if (!strcasecmp(*argv, "-colorlevel")) {
1380                         gflags.colorlevel = atoi(*++argv);
1381 			if (gflags.colorlevel < 0) gflags.colorlevel = 0;
1382 			if (gflags.colorlevel >= FULLCOLORS) {
1383                            gflags.colorlevel = FULLCOLORS;
1384 			   gflags.fillmode = 2;
1385 			} else
1386  			   gflags.fillmode = 1;
1387   	        }
1388 		else if (!strcasecmp(*argv, "-vmfflags")) {
1389 		        gflags.vmfflags = atoi(*++argv);
1390 			option_changes |= 4;
1391 		}
1392 		else if (!strcasecmp(*argv, "-vmfrange")) {
1393                         StringReAlloc(&vmfrange, *++argv);
1394 		}
1395 		else if (!strcasecmp(*argv, "-vmfcoordformat")) {
1396                         StringReAlloc(&vmfcoordformat, *++argv);
1397 		}
1398 		else if (!strcasecmp(*argv, "-vmfcolors")) {
1399                         StringReAlloc(&vmfcolors, *++argv);
1400                         if (!strcmp(vmfcolors, "|")) {
1401 			   free(vmfcolors);
1402 			   vmfcolors = NULL;
1403 			}
1404 			option_changes |= 4;
1405 		}
1406 		else if (!strcasecmp(*argv, "-clockgeom")) {
1407                         getGeom(*++argv, &ClockGeom);
1408 			option_changes |= 8;
1409                 }
1410                 else if (!strcasecmp(*argv, "-mapgeom")) {
1411                         getGeom(*++argv, &MapGeom);
1412 			option_changes |= 16;
1413                 }
1414                 else if (!strcasecmp(*argv, "-image")) {
1415                         StringReAlloc(&Clock_img_file, *++argv);
1416                         StringReAlloc(&Map_img_file, *argv);
1417 			option_changes |= 32|64;
1418                 }
1419                 else if (!strcasecmp(*argv, "-clockimage")) {
1420                         StringReAlloc(&Clock_img_file, *++argv);
1421 			option_changes |= 32;
1422                 }
1423                 else if (!strcasecmp(*argv, "-mapimage")) {
1424                         StringReAlloc(&Map_img_file, *++argv);
1425 			option_changes |= 64;
1426                 }
1427                 else if (!strcasecmp(*argv, "-zoomimage")) {
1428 		        StringReAlloc(&Zoom_img_file, *++argv);
1429 		        option_changes |= 2;
1430 		}
1431                 else if (!strcasecmp(*argv, "-auxilgeom")) {
1432                         getGeom(*++argv, &MenuGeom);
1433 			option_changes |= 2;
1434 			ZoomGeom.x = FileselGeom.x
1435                                    = OptionGeom.x
1436                                    = UrbanGeom.x
1437                                    = MenuGeom.x;
1438 			ZoomGeom.y = FileselGeom.y
1439                                    = OptionGeom.y
1440                                    = UrbanGeom.y
1441                                    = MenuGeom.y;
1442                 }
1443                 else if (!strcasecmp(*argv, "-menugeom")) {
1444                         getGeom(*++argv, &MenuGeom);
1445 			option_changes |= 2;
1446                 }
1447                 else if (!strcasecmp(*argv, "-selgeom")) {
1448                         getGeom(*++argv, &FileselGeom);
1449 			option_changes |= 2;
1450                 }
1451                 else if (!strcasecmp(*argv, "-zoomgeom")) {
1452                         getGeom(*++argv, &ZoomGeom);
1453 			option_changes |= 2;
1454                 }
1455                 else if (!strcasecmp(*argv, "-optiongeom")) {
1456                         getGeom(*++argv, &OptionGeom);
1457 			option_changes |= 2;
1458                 }
1459                 else if (!strcasecmp(*argv, "-urbangeom")) {
1460                         getGeom(*++argv, &UrbanGeom);
1461 			option_changes |= 2;
1462                 }
1463                 else if (!strcasecmp(*argv, "-mag")) {
1464                         gzoom.fx = atof(*++argv);
1465                         if (gzoom.fx < 1) gzoom.fx = 1.0;
1466                         if (gzoom.fx > 100.0) gzoom.fx = 100.0;
1467                         gzoom.fy = gzoom.fx;
1468 			option_changes |= 4;
1469                 }
1470                 else if (!strcasecmp(*argv, "-magx")) {
1471                         gzoom.fx = atof(*++argv);
1472                         if (gzoom.fx < 1) gzoom.fx = 1.0;
1473 			option_changes |= 4;
1474                 }
1475                 else if (!strcasecmp(*argv, "-magy")) {
1476                         gzoom.fy = atof(*++argv);
1477                         if (gzoom.fy < 1) gzoom.fy = 1.0;
1478 			option_changes |= 4;
1479                 }
1480                 else if (!strcasecmp(*argv, "-dx")) {
1481                         gzoom.fdx = atof(*++argv)/360.0+0.5;
1482                         checkZoomSettings(&gzoom);
1483 			option_changes |= 4;
1484                 }
1485                 else if (!strcasecmp(*argv, "-dy")) {
1486                         gzoom.fdy = 0.5-atof(*++argv)/180.0;
1487                         checkZoomSettings(&gzoom);
1488 			option_changes |= 4;
1489                 }
1490                 else if (!strcasecmp(*argv, "-rootdx"))
1491 		        rootdx = atof(*++argv);
1492                 else if (!strcasecmp(*argv, "-rootdy"))
1493 		        rootdy = atof(*++argv);
1494                 else if (!strcasecmp(*argv, "-setfont"))
1495                         parseFont(*++argv);
1496                 else if (!strcasecmp(*argv, "-mapmode")) {
1497                         if (!strcasecmp(*++argv, "c"))
1498                            gflags.map_mode = COORDINATES;
1499                         if (!strcasecmp(*argv, "d"))
1500                            gflags.map_mode = DISTANCES;
1501                         if (!strcasecmp(*argv, "e"))
1502                            gflags.map_mode = EXTENSION;
1503                         if (!strcasecmp(*argv, "l")) {
1504                            StringReAlloc(&CityInit, NULL);
1505                            gflags.map_mode = LEGALTIME;
1506                         }
1507                         if (!strcasecmp(*argv, "s"))
1508                            gflags.map_mode = SOLARTIME;
1509                 }
1510                 else if (!strcasecmp(*argv, "-parallelmode")) {
1511                         opt = atoi(*++argv);
1512                         if (opt<0) opt = 0;
1513                         if (opt>3) opt = 3;
1514 			gflags.parallel = opt + (gflags.parallel & 8);
1515                 }
1516 		else if (!strcasecmp(*argv, "-parallelspacing")) {
1517                         gzoom.paralspacing = atof(*++argv);
1518                         if (gzoom.paralspacing<0) gzoom.paralspacing = 0;
1519                         if (gzoom.paralspacing>30.0) gzoom.paralspacing = 30.0;
1520                         if (gzoom.paralspacing<0.1) gzoom.paralspacing = 0.1;
1521                 }
1522 		else if (!strcasecmp(*argv, "-meridianmode")) {
1523                         gflags.meridian = atoi(*++argv);
1524                         if (gflags.meridian<0) gflags.meridian = 0;
1525                         if (gflags.meridian>3) gflags.meridian = 3;
1526                 }
1527 		else if (!strcasecmp(*argv, "-meridianspacing")) {
1528                         gzoom.meridspacing = atof(*++argv);
1529                         if (gzoom.meridspacing<0) gzoom.meridspacing = 0;
1530                         if (gzoom.meridspacing>30.0) gzoom.meridspacing = 30.0;
1531                         if (gzoom.meridspacing<0.1) gzoom.meridspacing = 0.1;
1532                 }
1533 		else if (!strcasecmp(*argv, "-citymode")) {
1534                         gflags.citymode = atoi(*++argv);
1535                         if (gflags.citymode<0) gflags.citymode = 0;
1536                         if (gflags.citymode>3) gflags.citymode = 3;
1537                 }
1538 		else if (!strcasecmp(*argv, "-objectmode")) {
1539                         gflags.objectmode = atoi(*++argv);
1540                         if (gflags.objectmode<0) gflags.objectmode = 0;
1541                         if (gflags.objectmode>=2) gflags.objectmode = 2;
1542 		}
1543                 else if (!strcasecmp(*argv, "-spotsizes"))
1544                         StringReAlloc(&SpotSizes, *++argv);
1545                 else if (!strcasecmp(*argv, "-sizelimits"))
1546                         StringReAlloc(&SizeLimits, *++argv);
1547                 else if (!strcasecmp(*argv, "-fillmode")) {
1548                         gflags.fillmode = atoi(*++argv);
1549                         if (gflags.fillmode<0) gflags.fillmode = 0;
1550                         if (gflags.fillmode>3) gflags.fillmode = 3;
1551                 }
1552                 else if (!strcasecmp(*argv, "-darkness")) {
1553                         darkness = atof(*++argv);
1554                         if (darkness<0.0) darkness = 0.0;
1555                         if (darkness>1.0) darkness = 1.0;
1556                 }
1557                 else if (!strcasecmp(*argv, "-diffusion")) {
1558                         atm_diffusion = atof(*++argv);
1559                         if (atm_diffusion<0.0) atm_diffusion = 0.0;
1560                 }
1561                 else if (!strcasecmp(*argv, "-refraction")) {
1562                         atm_refraction = atof(*++argv);
1563                         if (atm_refraction<0.0) atm_refraction = 0.0;
1564                 }
1565                 else if (!strcasecmp(*argv, "-colorscale")) {
1566 			opt = atoi(*++argv);
1567 			if (opt<=0) opt = 1;
1568 			if (opt>32767) opt = 32767;
1569                         gflags.colorscale = opt;
1570                 }
1571 		else if (!strcasecmp(*argv, "-setcolor")) {
1572 		        if (parseColor(*++argv)) return(1);
1573 		}
1574                 else if (!strcasecmp(*argv, "-addcity"))
1575 		        (void) addCity(*++argv);
1576 		else if (!strcasecmp(*argv, "-removecity")) {
1577 		        City * c = searchCityLocation(*++argv);
1578 		        removeCity(c);
1579 		}
1580 		else if (!strcasecmp(*argv, "-position")) {
1581                         StringReAlloc(&CityInit, NULL);
1582                         scanPosition(*++argv, &position);
1583 			if (position.lat < -90.0) {
1584 			  fprintf(stderr,
1585                              "Error in -position parameters\n");
1586 			  return(1);
1587 			}
1588 		}
1589                 else if (!strcasecmp(*argv, "-city")) {
1590                         StringReAlloc(&CityInit, *++argv);
1591                         position.lat = 100.0;
1592                         gflags.map_mode = COORDINATES;
1593                 }
1594                 else if (!strcasecmp(*argv, "-placement")) {
1595 		        option_changes |= 2;
1596                         if (strcasecmp(*++argv, "random")==0)
1597                            placement = RANDOM;
1598                         if (strcasecmp(*argv, "fixed")==0) {
1599                            placement = FIXED;
1600                            MapGeom.mask = XValue | YValue |
1601                                           WidthValue | HeightValue;
1602                         }
1603                         if (!strcasecmp(*argv, "center"))
1604                            placement = CENTER;
1605                         if (!strcasecmp(*argv, "nw"))
1606                            placement = NW;
1607                         if (!strcasecmp(*argv, "ne"))
1608                            placement = NE;
1609                         if (!strcasecmp(*argv, "sw"))
1610                            placement = SW;
1611                         if (!strcasecmp(*argv, "se"))
1612                            placement = SE;
1613                 }
1614                 else if (!strcasecmp(*argv, "-extrawidth"))
1615 			extra_width = atol(*++argv);
1616                 else if (!strcasecmp(*argv, "-placementshift")) {
1617 		        option_changes |= 2;
1618 			if (sscanf(*++argv, "%d %d",
1619                             &place_shiftx, &place_shifty) < 2) {
1620 			  fprintf(stderr,
1621                              "Error in -placementshift parameters\n");
1622 			  return(1);
1623 			}
1624 		}
1625                 else if (!strcasecmp(*argv, "-command"))
1626                         StringReAlloc(&ExternAction, *++argv);
1627                 else if (!strcasecmp(*argv, "-editorcommand"))
1628                         StringReAlloc(&EditorCommand, *++argv);
1629                 else if (!strcasecmp(*argv, "-dateformat"))
1630                         StringReAlloc(&ListFormats, *++argv);
1631                 else if (!strcasecmp(*argv, "-shading")) {
1632                         gflags.shading = atoi(*++argv);
1633                         if (gflags.shading < 0) gflags.shading = 0;
1634                         if (gflags.shading > 5) gflags.shading = 5;
1635                 }
1636                 else if (!strcasecmp(*argv, "-progress") ||
1637                          !strcasecmp(*argv, "-jump")) {
1638                         char *str, *invalid, c;
1639                         long value;
1640 			opt = ((*argv)[5]!='\0');
1641                         str=*++argv;
1642                         value = strtol(str, &invalid, 10);
1643                         if (invalid)
1644                            c = *invalid;
1645 			else
1646 			   c = 's';
1647                         if (c>='0' && c<='9') c='s';
1648                         switch(c) {
1649                           case 's': break;
1650                           case 'm': value *= 60 ; break;
1651                           case 'h': value *= 3600 ; break;
1652                           case 'd': value *= 86400 ; break;
1653                           case 'M': value *= 2592000 ; break;
1654                           case 'y':
1655                           case 'Y': value *= 31536000 ; break;
1656                           default : c = ' '; break;
1657                         }
1658                         if (c == ' ') Usage();
1659                         if (opt) {
1660                            progress_value[5] = abs(value);
1661                            if (value)
1662                               gflags.progress = 5;
1663                            else
1664                               gflags.progress = 0;
1665                         } else
1666                            time_jump = value;
1667                 }
1668                 else if (!strcasecmp(*argv, "-rootperiod")) {
1669                         root_period = atoi(*++argv);
1670                         if (root_period<=0) root_period = 1;
1671                         if (root_period>120) root_period = 120;
1672                 }
1673                 else if (!strcasecmp(*argv, "-animateperiod")) {
1674                         gflags.animperiod = atoi(*++argv);
1675                         if (gflags.animperiod<0) gflags.animperiod = 0;
1676                         if (gflags.animperiod>5) gflags.animperiod = 5;
1677                 }
1678                 else if (!strcasecmp(*argv, "-aspect")) {
1679                         gzoom.mode = atoi(*++argv);
1680                         if (gzoom.mode<0) gzoom.mode = 0;
1681                         if (gzoom.mode>2) gzoom.mode = 2;
1682                 }
1683 	        else {
1684                    fprintf(stderr, "%s: unknown option !!\n\n", *argv);
1685                    if (runlevel == PARSECMDLINE) {
1686                         Usage();
1687                         exit(1);
1688                    }
1689 		   else if (runlevel == RUNTIMEOPTION) {
1690                         fprintf(stderr, "Option %s : incorrect or not "
1691 				        "available at runtime !!\n", *argv);
1692                         runlevel = FAILEDOPTION;
1693 		   }
1694                    else {
1695                         fprintf(stderr, "Trying to recover.\n");
1696                         return(1);
1697                    }
1698 		}
1699 	     }
1700 	}
1701         return(0);
1702 }
1703 
1704 int
parseCmdLine(buf)1705 parseCmdLine(buf)
1706 char * buf;
1707 {
1708     int i, j, l;
1709     char *dup, *str;
1710     char ** argv;
1711     int argc;
1712 
1713     l = strlen(buf);
1714     dup = (char *) salloc((l+2)*sizeof(char));
1715     if (dup) {
1716        if (*buf == '-')
1717           strcpy(dup, buf);
1718        else {
1719 	  strcpy(dup+1, buf);
1720 	  *dup = '-';
1721        }
1722     } else return 1;
1723 
1724     j = 0;
1725     for (i=0 ; dup[i] ; ++i)
1726        if (isspace(dup[i])) ++j;
1727 
1728     argv = (char **) salloc((j+2)*sizeof(char *));
1729 
1730     i = argc = 0;
1731 
1732  rescan:
1733     while(dup[i] && isspace(dup[i])) {
1734       dup[i] = '\0';
1735       ++i;
1736     }
1737 
1738     if (dup[i]) {
1739        str = argv[argc] = dup+i;
1740        while(dup[i] && !isspace(dup[i])) ++i;
1741        if (str[0]==':' && (!str[1] || isspace(str[1]))) {
1742 	  str[0] = '\0';
1743 	  goto rescan;
1744        }
1745        ++argc;
1746        goto rescan;
1747     } else
1748        argv[argc] = NULL;
1749 
1750     for (i=0; i<l+1; i++)
1751        if (dup[i] && dup[i]<' ') dup[i] = ' ';
1752 
1753     for (i=0; i<argc; i++) {
1754        str = argv[i];
1755        l = strlen(str)-1;
1756        if (*str == '-' && str[l] == ':') str[l] = '\0';
1757     }
1758 
1759     l = parseArgs(argc+1, argv-1);
1760 
1761     free(dup);
1762     free(argv);
1763     return l;
1764 }
1765 
1766 /*
1767  * readRC() - Read a config file (app-default or user .sunclockrc or whatever)
1768  */
1769 
1770 int
readRC(fname,verbosity)1771 readRC(fname, verbosity)
1772 char *fname;        /* Path to .sunclockrc file */
1773 int verbosity;
1774 {
1775     /*
1776      * Local Variables
1777      */
1778 
1779     FILE *rc;           /* File pointer for rc file */
1780     char buf[1028];     /* Buffer to hold input lines */
1781     int  j;
1782 
1783     /* Open the RC file */
1784 
1785     if ((rc = fopen(fname, "r")) == NULL) {
1786         if (verbosity)
1787            fprintf(stderr,
1788                "Unable to find the config file  %s \n", fname);
1789         return(1);
1790     }
1791 
1792     /* Read and parse lines from the file */
1793 
1794     while (fgets(buf, 1024, rc)) {
1795 
1796         /* Look for blank lines or comments */
1797 
1798         j=0;
1799         while (j<1024 && isspace(buf[j]) && buf[j] != '0') ++j;
1800         if ((buf[j] == '#') || (buf[j] == '\0')) continue;
1801 
1802         if (parseCmdLine(buf))
1803 	   fprintf(stderr,"Recheck syntax of config file %s !!\n\n", fname);
1804            continue;
1805         }
1806 
1807     if (rc) fclose(rc);
1808     return(0);
1809 }
1810 
1811 struct Sundata *
getContext(win)1812 getContext(win)
1813 Window win;
1814 {
1815    struct Sundata * Context;
1816    if (win==Menu)
1817      return MenuCaller;
1818    if (win==Filesel)
1819      return FileselCaller;
1820    if (win==Zoom)
1821      return ZoomCaller;
1822    if (win==Option)
1823      return OptionCaller;
1824    if (win==Urban)
1825      return UrbanCaller;
1826 
1827    Context = Seed;
1828    while (Context && Context->win != win) Context = Context->next;
1829    return Context;
1830 }
1831 
1832 XFontStruct *
getFont(num)1833 getFont(num)
1834 int num;
1835 {
1836 XFontStruct * font;
1837 
1838     font = XLoadQueryFont(dpy, SunFont[num]);
1839     if (font == (XFontStruct *)NULL)
1840        fprintf(stderr, "%s: can't open font `%s', using `%s'\n",
1841                 ProgName, SunFont[num], FAILFONT);
1842     else
1843        return font;
1844     font = XLoadQueryFont(dpy, FAILFONT);
1845     if (font == (XFontStruct *)NULL) {
1846        fprintf(stderr, "%s: can't open font `%s', giving up\n",
1847                ProgName, FAILFONT);
1848        return NULL;
1849     }
1850     return font;
1851 }
1852 
1853 void
getFonts(Context)1854 getFonts(Context)
1855 Sundata * Context;
1856 {
1857 int i, h, hp;
1858 
1859         for (i=0; i<NUMFONTS; i++)
1860 	    if (!(Context->gdata->font[i] = getFont(i))) exit(1);
1861 
1862         Context->gdata->clockstrip =
1863             Context->gdata->font[CLOCKSTRIPFONT]->max_bounds.ascent +
1864             Context->gdata->font[CLOCKSTRIPFONT]->max_bounds.descent + 4;
1865 
1866         Context->gdata->mapstrip =
1867             Context->gdata->font[MAPSTRIPFONT]->max_bounds.ascent +
1868             Context->gdata->font[MAPSTRIPFONT]->max_bounds.descent + 8;
1869 
1870         Context->gdata->menustrip =
1871             Context->gdata->font[MENUFONT]->max_bounds.ascent +
1872             Context->gdata->font[MENUFONT]->max_bounds.descent + 8;
1873 
1874 	Context->gdata->charspace = Context->gdata->menustrip+4;
1875 
1876 	if (option_changes & 1) {
1877 	    FileselGeom.width = SEL_WIDTH * Context->gdata->menustrip;
1878             FileselGeom.height = (11+4*SEL_HEIGHT)*Context->gdata->menustrip/5;
1879 	}
1880 
1881 	if (Context->flags.colorlevel < FULLCOLORS) return;
1882 
1883         h = Context->gdata->font[CITYFONT]->max_bounds.ascent +
1884             Context->gdata->font[CITYFONT]->max_bounds.descent;
1885         hp = Context->gdata->font[COORDFONT]->max_bounds.ascent +
1886              Context->gdata->font[COORDFONT]->max_bounds.descent;
1887         if (hp>h) h = hp;
1888 	if (h>textheight) {
1889            textheight = h;
1890 	   if (textpix) {
1891 	      XFreePixmap(dpy, textpix);
1892 	      textpix = 0;
1893 	   }
1894 	}
1895         if (!textpix)
1896            textpix = XCreatePixmap(dpy, Root, textwidth, textheight, 1);
1897 }
1898 
1899 unsigned long
getColor(Context,num)1900 getColor(Context, num)
1901 Sundata *        Context;
1902 int              num;
1903 {
1904 
1905         XColor                  c;
1906         XColor                  e;
1907         register Status         s;
1908 
1909         ++total_colors;
1910 
1911         if (Context->flags.colorlevel == MONOCHROME) goto monochrome;
1912 
1913         s = XAllocNamedColor(dpy, Context->gdata->cmap, Color[num], &c, &e);
1914 
1915         if (s != (Status)1) {
1916             fprintf(stderr, "%s: warning: can't allocate color `%s'\n",
1917                     ProgName, Color[num]);
1918             color_alloc_failed = 1;
1919  	 monochrome:
1920 	    s = XAllocNamedColor(dpy, Context->gdata->cmap,
1921                 (num<FALLBACKTOWHITE)? "White" : "Black", &c, &e);
1922 	}
1923 
1924         return(c.pixel);
1925 }
1926 
1927 void
createGData(Context,private)1928 createGData(Context, private)
1929 struct Sundata * Context;
1930 int private;
1931 {
1932         GraphicData *           graphdata;
1933         Sundata *               OtherContext;
1934         int                     prec, i, j;
1935 
1936         /* Try to use already defined GCs and Pixels in default colormap
1937            if already defined */
1938 
1939         if (!private && runlevel == RUNNING) {
1940 
1941 	   graphdata = NULL;
1942   	   prec = -1;
1943 
1944            for (OtherContext = Seed; OtherContext;
1945                 OtherContext = OtherContext->next) {
1946               if (OtherContext != Context &&
1947                   OtherContext->flags.colorlevel == gflags.colorlevel &&
1948                   OtherContext->gdata->cmap == cmap0 &&
1949                   OtherContext->gdata->precedence > prec) {
1950 	         graphdata = OtherContext->gdata;
1951 	         prec = OtherContext->gdata->precedence;
1952               }
1953            }
1954 
1955 	   if (graphdata) {
1956               Context->gdata = graphdata;
1957               ++Context->gdata->links;
1958 	      return;
1959 	   }
1960 	}
1961 
1962     newcmap:
1963 
1964         /* Otherwise, define new adhoc graphical data */
1965         Context->gdata = (GraphicData *)salloc(sizeof(GraphicData));
1966         Context->gdata->wingc = 0;
1967         Context->gdata->pixgc = 0;
1968         Context->gdata->links = 0;
1969 	Context->gdata->precedence = precedence;
1970 	++precedence;
1971         if (color_depth>8 || Context->flags.colorlevel==MONOCHROME ||
1972            (Context->flags.colorlevel>MONOCHROME && !private))
1973            Context->gdata->cmap = cmap0;
1974         else
1975            Context->gdata->cmap =
1976                 XCreateColormap(dpy, Root, visual, AllocNone);
1977 
1978         color_alloc_failed = 0;
1979 
1980         total_colors = 0;
1981         for (i=0; i<NUMPIXELS; i++)
1982             Context->gdata->pixel[i] = getColor(Context, i);
1983 
1984         if (color_depth<=8 && color_alloc_failed &&
1985            Context->gdata->cmap==cmap0) {
1986               private = 1;
1987               fprintf(stderr,
1988                  "Color allocation failed with default colormap.\n"
1989                  "Trying instead private colormap...\n");
1990               goto newcmap;
1991 	}
1992 
1993         if (color_alloc_failed)
1994            fprintf(stderr, "Color allocation failed !!!\n");
1995 	else
1996 	   if (verbose)
1997               fprintf(stderr, "Color allocation successful:\n");
1998 
1999         Context->gdata->usedcolors = total_colors;
2000         for (i=0; i<total_colors; i++) {
2001            for (j=0; j<i; j++) if (Context->gdata->pixel[i]==Context->gdata->pixel[j]) {
2002               --Context->gdata->usedcolors;
2003               break;
2004            }
2005         }
2006         if (verbose && !color_alloc_failed)
2007            fprintf(stderr,
2008               "  %d basic colors allocated in %s colormap.\n",
2009               Context->gdata->usedcolors,
2010               (Context->gdata->cmap==cmap0)? "default":"private");
2011 }
2012 
2013 void
createGCs(Context)2014 createGCs(Context)
2015 struct Sundata * Context;
2016 {
2017         XGCValues               gcv;
2018 	int                     mask;
2019 
2020         if (!Context->mappix && (Context->flags.colorlevel<FULLCOLORS))
2021            Context->mappix = XCreatePixmap(dpy, Root,
2022               Context->geom.width, Context->geom.height, 1);
2023 
2024         if (Context->gdata->links==0) {
2025            if (verbose)
2026               fprintf(stderr, "Creating new GCs, mode = %d\n",
2027                      Context->flags.colorlevel);
2028            getFonts(Context);
2029         } else {
2030            if (verbose)
2031               fprintf(stderr, "Using previous GC settings (%d links)\n",
2032                      Context->gdata->links);
2033 	   return;
2034         }
2035 
2036         mask = GCForeground | GCBackground | GCFont;
2037 	gcv.background = white;
2038 	gcv.foreground = black;
2039 
2040         if (!Context->gdata->wingc) {
2041            gcv.font = Context->gdata->font[MENUFONT]->fid;
2042            Context->gdata->wingc = XCreateGC(dpy, Root, mask, &gcv);
2043 	}
2044 
2045         gcv.font = Context->gdata->font[COORDFONT]->fid;
2046         if (Context->flags.colorlevel == FULLCOLORS) {
2047            gcv.background = black;
2048 	   gcv.foreground = white;
2049            Context->gdata->pixgc = XCreateGC(dpy, textpix, mask, &gcv);
2050         } else {
2051            mask |= GCFunction;
2052 	   gcv.function = GXinvert;
2053            Context->gdata->pixgc = XCreateGC(dpy, Context->mappix, mask, &gcv);
2054 	}
2055 }
2056 
2057 void
clearStrip(Context)2058 clearStrip(Context)
2059 struct Sundata * Context;
2060 {
2061         XSetForeground(dpy, Context->gdata->wingc,
2062 		    Context->gdata->pixel[CLOCKSTRIPBGCOLOR+Context->wintype]);
2063         XFillRectangle(dpy, Context->win, Context->gdata->wingc,
2064               0, Context->geom.height+(Context->flags.bottom&1)-1,
2065               Context->geom.width,
2066               Context->hstrip-(Context->flags.bottom&1)+1);
2067 }
2068 
2069 /*
2070  *  Set the timezone of selected location.
2071  *  This is definitely the most tricky point in the whole sunclock stuff
2072  *  because of the incompatible Unix implementations !
2073  */
2074 
2075 void
setTZ(cptr)2076 setTZ(cptr)
2077 City    *cptr;
2078 {
2079 #ifndef _OS_LINUX_
2080         char buf[80];
2081 #endif
2082 
2083         if (cptr && cptr->tz) {
2084 #ifdef _OS_LINUX_
2085            setenv("TZ", cptr->tz, 1);
2086 #else
2087            sprintf(buf, "TZ=%s", cptr->tz);
2088 #ifdef _OS_HPUX_
2089            putenv(strdup(buf));
2090 #else
2091            putenv(buf);
2092 #endif
2093 #endif
2094            }
2095         else
2096 #ifdef _OS_LINUX_
2097            unsetenv("TZ");
2098 #else
2099            {
2100            /* This is supposed to reset timezone to default localzone */
2101            strcpy(buf, "TZ");
2102            /* Another option would be to set LOCALZONE adequately and put:
2103            strcpy(buf, "TZ="LOCALZONE); */
2104 #ifdef _OS_HPUX_
2105            putenv(strdup(buf));
2106 #else
2107            putenv(buf);
2108 #endif
2109            }
2110 #endif
2111         tzset();
2112 }
2113 
2114 /*
2115  *   Sets sun position (longitude, declination) and returns
2116  *   solartime at given city
2117  *   CAUTION: sunlong is in fact given as longitude+180 in interval 0..360
2118  */
2119 
2120 time_t
sunParams(gtime,sunlong,sundec,city)2121 sunParams(gtime, sunlong, sundec, city)
2122 time_t gtime;
2123 double *sunlong;
2124 double *sundec;
2125 City *city;
2126 {
2127         struct tm               ctp, stp;
2128         time_t                  stime;
2129         double                  jt, gt;
2130         double                  sunra, sunrv, junk;
2131         long                    diff;
2132 
2133         ctp = *gmtime(&gtime);
2134         jt = jtime(&ctp);
2135         sunpos(jt, False, &sunra, sundec, &sunrv, &junk);
2136         gt = gmst(jt);
2137         *sunlong = fixangle(180.0 + (sunra - (gt * 15)));
2138 
2139         if (city) {
2140            stime = (long) ((city->lon - *sunlong) * 240.0);
2141            stp = *gmtime(&stime);
2142            diff = stp.tm_sec-ctp.tm_sec
2143                   +60*(stp.tm_min-ctp.tm_min)+3600*(stp.tm_hour-ctp.tm_hour);
2144            if (city->lon>0.0) while(diff<-40000) diff += 86400;
2145            if (city->lon<0.0) while(diff>40000) diff -= 86400;
2146            stime = gtime+diff;
2147         } else
2148            stime = 0;
2149         return(stime);
2150 }
2151 
2152 /* get "daylength" at a location of latitude lat, when it is noon at
2153    GMT time gt at that location */
2154 
2155 double
dayLength(gtime,lat)2156 dayLength(gtime, lat)
2157 time_t  gtime;
2158 double  lat;
2159 {
2160         double                  duration;
2161         double                  sundec, junk;
2162         double                  sinsun, sinpos, sinapp, num;
2163 
2164         sinpos = sin(torad(lat));
2165 
2166         /* Get Sun declination */
2167         (void) sunParams(gtime, &junk, &sundec, NULL);
2168         sinsun = sin(torad(sundec));
2169 
2170         /* Correct for the sun apparent diameter and atmospheric diffusion */
2171         sinapp = sin(torad(SUN_APPRADIUS + atm_refraction));
2172 
2173         num = 1 - sinsun*sinsun - sinpos*sinpos - sinapp*sinapp
2174                 - 2*sinsun*sinpos*sinapp;
2175         if (num<=0) {
2176            if (sinsun*sinpos>0)
2177              duration = 24.0;
2178            else
2179              duration = 0.0;
2180         } else
2181              duration = 12.0 + 24.0*atan((sinsun*sinpos+sinapp)/sqrt(num))/PI;
2182 
2183         return duration*3600;
2184 }
2185 
2186 void
setDayParams(Context)2187 setDayParams(Context)
2188 struct Sundata * Context;
2189 {
2190         struct tm               gtm, ltm;
2191         double                  duration, junk;
2192         time_t                  gtime, stime, sr, ss, dl;
2193 
2194         menu_lasthint = ' ';
2195 
2196         if (!Context->mark1.city) return;
2197 
2198         time(&Context->footime);
2199         gtime = Context->footime + Context->jump;
2200 
2201         /* Get local time at given location */
2202         setTZ(Context->mark1.city);
2203         ltm = *localtime(&gtime);
2204 
2205         /* Get solar time at given location */
2206         stime = sunParams(gtime, &junk, &junk, Context->mark1.city);
2207 
2208         /* Go to time when it is noon in solartime at Context->mark1.city */
2209         gtime += 43200 - (stime % 86400);
2210         gtm = *gmtime(&gtime);
2211 
2212         if (gtm.tm_mday < ltm.tm_mday) gtime +=86400;
2213         if (gtm.tm_mday > ltm.tm_mday) gtime -=86400;
2214 
2215         /* Iterate, just in case of a day shift */
2216         stime = sunParams(gtime, &junk, &junk, Context->mark1.city);
2217 
2218         gtime += 43200 - (stime % 86400);
2219 
2220         /* get day length at that time and location*/
2221         duration = dayLength(gtime, Context->mark1.city->lat);
2222 
2223         /* compute sunrise and sunset in legal time */
2224         sr = gtime - (time_t)(0.5*duration);
2225         ss = gtime + (time_t)(0.5*duration);
2226         dl = ss-sr;
2227         Context->mark1.full = 1;
2228         if (dl<=0) {dl = 0; Context->mark1.full = 0;}
2229         if (dl>86380) {dl=86400; Context->mark1.full = 0;}
2230 
2231         Context->mark1.dl = *gmtime(&dl);
2232         Context->mark1.dl.tm_hour += (Context->mark1.dl.tm_mday-1) * 24;
2233 
2234         setTZ(Context->mark1.city);
2235         Context->mark1.sr = *localtime(&sr);
2236         Context->mark1.ss = *localtime(&ss);
2237 }
2238 
2239 char *
num2str(value,string,dms)2240 num2str(value, string, dms)
2241 double value;
2242 char *string;
2243 short dms;
2244 {
2245         int eps, d, m, s;
2246 
2247         if (dms) {
2248           if (value<0) {
2249             value = -value;
2250             eps = -1;
2251           } else
2252             eps = 1;
2253 	  value = value+1/7200.0;
2254           d = (int) value;
2255           value = 60 * (value - d);
2256           m = (int) value;
2257           value = 60 * (value - m);
2258           s = (int) value;
2259           sprintf(string, "%s%d�%02d'%02d\"", (eps==1)?"":"-", d, m, s);
2260         } else
2261           sprintf(string, "%.3f", value);
2262         return string;
2263 }
2264 
2265 /*
2266  *  Produce bottom strip of hours
2267  */
2268 
2269 void
showHours(Context)2270 showHours(Context)
2271 struct Sundata * Context;
2272 {
2273         int i, x;
2274         char s[128];
2275 
2276         if (Context->flags.hours_shown) return;
2277         clearStrip(Context);
2278         for (i=0; i<24; i++) {
2279             sprintf(s, "%d", i);
2280             x = ((i*Context->zoom.width)/24 + 2*Context->zoom.width
2281                 - (Context->gdata->mapstrip+5)*strlen(s)/8
2282                 + (int)(Context->sunlon*Context->zoom.width/360.0))%
2283                   Context->zoom.width + 1 - Context->zoom.dx;
2284             if (x>=0 && x<Context->geom.width) {
2285                XSetBackground(dpy, Context->gdata->wingc,
2286                    Context->gdata->pixel[MAPSTRIPBGCOLOR]);
2287                XSetForeground(dpy, Context->gdata->wingc,
2288                    Context->gdata->pixel[MAPSTRIPFGCOLOR]);
2289                XSetFont(dpy, Context->gdata->wingc,
2290                    Context->gdata->font[MAPSTRIPFONT]->fid);
2291                XDrawImageString(dpy, Context->win,
2292                   Context->gdata->wingc,
2293                   x, Context->gdata->font[MENUFONT]->max_bounds.ascent +
2294                      Context->geom.height + 4,
2295                   s, strlen(s));
2296 	    }
2297         }
2298         Context->flags.hours_shown = 1;
2299 }
2300 
2301 
2302 void
drawTextStrip(Context,s,l)2303 drawTextStrip(Context, s, l)
2304 struct Sundata * Context;
2305 char *s;
2306 int l;
2307 {
2308       XSetBackground(dpy, Context->gdata->wingc,
2309           Context->gdata->pixel[CLOCKSTRIPBGCOLOR+Context->wintype]);
2310       XSetForeground(dpy, Context->gdata->wingc,
2311           Context->gdata->pixel[CLOCKSTRIPFGCOLOR+Context->wintype]);
2312       XSetFont(dpy, Context->gdata->wingc,
2313              (Context->wintype)?
2314 	       Context->gdata->font[MAPSTRIPFONT]->fid :
2315 	       Context->gdata->font[CLOCKSTRIPFONT]->fid);
2316       XDrawImageString(dpy, Context->win, Context->gdata->wingc,
2317            2+2*Context->wintype, Context->geom.height + ((Context->wintype)?
2318              Context->gdata->font[MAPSTRIPFONT]->max_bounds.ascent + 4 :
2319              Context->gdata->font[CLOCKSTRIPFONT]->max_bounds.ascent + 3),
2320            s+label_shift, l-label_shift);
2321 }
2322 
2323 void
writeStrip(Context)2324 writeStrip(Context)
2325 struct Sundata * Context;
2326 {
2327         register struct tm      ltp;
2328         register struct tm      gtp;
2329         register struct tm      stp;
2330         time_t                  gtime;
2331         time_t                  stime;
2332         int                     i, l;
2333         char            s[128];
2334         char            slat[20], slon[20], slatp[20], slonp[20];
2335         double          dist;
2336 
2337 	if (!Context->flags.mapped) return;
2338 
2339         time(&Context->footime);
2340         gtime = Context->footime + Context->jump;
2341 
2342         if (!Context->wintype) {
2343                 char num[80];
2344                 int hour;
2345                 char ampm;
2346 
2347                 setTZ(NULL);
2348                 ltp = *localtime(&gtime);
2349                 gtp = *gmtime(&gtime);
2350 
2351 	        hour = ltp.tm_hour;
2352 	        if (hour<12)
2353 	           ampm = 'A';
2354 	        else
2355 	           ampm = 'P';
2356                 if (hour > 12)
2357                    hour -= 12;
2358                 l = strlen(DateFormat[Context->flags.clock_mode]);
2359                 *s = '\0';
2360                 for (i=0; i<l; i++) {
2361                    char c = DateFormat[Context->flags.clock_mode][i];
2362                    if (c != '%') {
2363                       num[0] = c;
2364                       num[1] = '\0';
2365                    }
2366                    if (c == '%' && i<l-1) {
2367                       ++i;
2368                       c = DateFormat[Context->flags.clock_mode][i];
2369                       switch(c) {
2370                         case 'G': sprintf(num, "%02d", gtp.tm_hour); break;
2371                         case 'H': sprintf(num, "%02d", ltp.tm_hour); break;
2372                         case 'M': sprintf(num, "%02d", ltp.tm_min); break;
2373                         case 'N': sprintf(num, "%02d", gtp.tm_min); break;
2374                         case 'P': num[0]=ampm; num[1]='\0'; break;
2375                         case 'S': sprintf(num, "%02d", ltp.tm_sec); break;
2376 #ifdef NEW_CTIME
2377                         case 'Z': strcpy(num, ltp.tm_zone); break;
2378 #else
2379                         case 'Z': strcpy(num, tzname[ltp.tm_isdst]); break;
2380 #endif
2381                         case 'a': strcpy(num, Day_name[ltp.tm_wday]); break;
2382                         case 'd': sprintf(num, "%02d", ltp.tm_mday); break;
2383                         case 'h': sprintf(num, "%02d", hour); break;
2384                         case 'j': sprintf(num, "%02d", 1+ltp.tm_yday); break;
2385                         case 'b': strcpy(num, Month_name[ltp.tm_mon]); break;
2386                         case 'm': sprintf(num, "%02d", 1+ltp.tm_mon); break;
2387                         case 't': {
2388                            int w = ltp.tm_year+1900;
2389                            if (w % 4==0 && (w % 100!=0 || w % 400 == 0))
2390                              w = 366;
2391                            else
2392                              w = 365;
2393                            sprintf(num, "%d", w);
2394                            break;
2395                            }
2396                         case 'y': sprintf(num, "%02d", ltp.tm_year%100); break;
2397                         case 'Y': sprintf(num, "%d", ltp.tm_year+1900); break;
2398                         case 'U': {
2399                            struct tm ftm;
2400                            time_t ftime;
2401                            int w;
2402                            /*
2403                             * define weeknumber
2404                             * week #1 = week with the first thursday
2405                             */
2406                            /* set reference date to 1st of january, 12:00:00 */
2407                            (void) memset(&ftm, 0, sizeof(struct tm));
2408                            ftm.tm_isdst = -1;
2409                            ftm.tm_mon = 0;
2410                            ftm.tm_mday = 1;
2411                            ftm.tm_year = ltp.tm_year;
2412                            ftm.tm_hour = 12;
2413                            ftm.tm_min = 0;
2414                            ftm.tm_sec = 0;
2415                            ftime = mktime(&ftm);
2416                            ftm = *localtime(&ftime);
2417                            /* get first sunday (start of week) */
2418                            if (ftm.tm_wday < 5)
2419                               w = 1 - ftm.tm_wday;
2420                            else
2421                               w = 8 - ftm.tm_wday;
2422                            /* get weeknumber */
2423                            sprintf(num, "%02d",
2424                                 ((ltp.tm_yday+1-ltp.tm_wday-w)/7)+1);
2425                            break;
2426                            }
2427                         case '_': c = ' ';
2428                         default: num[0] = c; num[1] = '\0'; break;
2429                       }
2430                    }
2431                    strcat(s, num);
2432                 }
2433                 l = strlen(s);
2434                 if (l<72) {
2435                   for (i=l; i<72; i++) s[i] = ' ';
2436                   s[72] = '\0';
2437                   l = 72;
2438                 }
2439                 drawTextStrip(Context, s, l);
2440 		return;
2441         }
2442 
2443         switch(Context->flags.map_mode) {
2444 
2445         case LEGALTIME:
2446            gtp = *gmtime(&gtime);
2447            setTZ(Context->mark1.city);
2448            ltp = *localtime(&gtime);
2449            sprintf(s,
2450                 " %s %02d:%02d:%02d %s %s %02d %s %04d    %s %02d:%02d:%02d UTC %s %02d %s %04d",
2451                 Label[L_LEGALTIME], ltp.tm_hour, ltp.tm_min,
2452                 ltp.tm_sec,
2453 #ifdef NEW_CTIME
2454                 ltp.tm_zone,
2455 #else
2456                 tzname[ltp.tm_isdst],
2457 #endif
2458                 Day_name[ltp.tm_wday], ltp.tm_mday,
2459                 Month_name[ltp.tm_mon], 1900 + ltp.tm_year ,
2460                 Label[L_GMTTIME],
2461                 gtp.tm_hour, gtp.tm_min,
2462                 gtp.tm_sec, Day_name[gtp.tm_wday], gtp.tm_mday,
2463                 Month_name[gtp.tm_mon], 1900 + gtp.tm_year);
2464            break;
2465 
2466         case COORDINATES:
2467            setTZ(Context->mark1.city);
2468            ltp = *localtime(&gtime);
2469            if (ltp.tm_mday != Context->local_day)
2470               setDayParams(Context);
2471            Context->local_day = ltp.tm_mday;
2472            if ((Context->mark1.city) && Context->mark1.full)
2473            sprintf(s,
2474                 " %s (%s,%s)  %02d:%02d:%02d %s %s %02d %s %04d   %s %02d:%02d:%02d   %s %02d:%02d:%02d",
2475                 Context->mark1.city->name,
2476                 num2str(Context->mark1.city->lat, slat, Context->flags.dms),
2477                 num2str(Context->mark1.city->lon, slon, Context->flags.dms),
2478                 ltp.tm_hour, ltp.tm_min, ltp.tm_sec,
2479 #ifdef NEW_CTIME
2480                 ltp.tm_zone,
2481 #else
2482                 tzname[ltp.tm_isdst],
2483 #endif
2484                 Day_name[ltp.tm_wday], ltp.tm_mday,
2485                 Month_name[ltp.tm_mon], 1900 + ltp.tm_year ,
2486                 Label[L_SUNRISE],
2487                 Context->mark1.sr.tm_hour, Context->mark1.sr.tm_min, Context->mark1.sr.tm_sec,
2488                 Label[L_SUNSET],
2489                 Context->mark1.ss.tm_hour, Context->mark1.ss.tm_min, Context->mark1.ss.tm_sec);
2490                 else
2491            if ((Context->mark1.city) && !Context->mark1.full)
2492            sprintf(s,
2493                 " %s (%s,%s)  %02d:%02d:%02d %s %s %02d %s %04d   %s %02d:%02d:%02d",
2494                 Context->mark1.city->name,
2495                 num2str(Context->mark1.city->lat, slat, Context->flags.dms),
2496                 num2str(Context->mark1.city->lon, slon, Context->flags.dms),
2497                 ltp.tm_hour, ltp.tm_min, ltp.tm_sec,
2498 #ifdef NEW_CTIME
2499                 ltp.tm_zone,
2500 #else
2501                 tzname[ltp.tm_isdst],
2502 #endif
2503                 Day_name[ltp.tm_wday], ltp.tm_mday,
2504                 Month_name[ltp.tm_mon], 1900 + ltp.tm_year ,
2505                 Label[L_DAYLENGTH],
2506                 Context->mark1.dl.tm_hour, Context->mark1.dl.tm_min, Context->mark1.dl.tm_sec);
2507                 else
2508                   sprintf(s," %s", Label[L_CLICKCITY]);
2509                 break;
2510 
2511         case SOLARTIME:
2512            if (Context->mark1.city) {
2513              double junk;
2514              stime = sunParams(gtime, &junk, &junk, Context->mark1.city);
2515              stp = *gmtime(&stime);
2516              if (stp.tm_mday != Context->solar_day)
2517                 setDayParams(Context);
2518              Context->solar_day = stp.tm_mday;
2519              sprintf(s, " %s (%s,%s)  %s %02d:%02d:%02d   %s %02d %s %04d   %s %02d:%02d:%02d",
2520                   Context->mark1.city->name,
2521                   num2str(Context->mark1.city->lat,slat, Context->flags.dms),
2522                   num2str(Context->mark1.city->lon,slon, Context->flags.dms),
2523                   Label[L_SOLARTIME],
2524                   stp.tm_hour, stp.tm_min, stp.tm_sec,
2525                   Day_name[stp.tm_wday], stp.tm_mday,
2526                   Month_name[stp.tm_mon], 1900 + stp.tm_year,
2527                   Label[L_DAYLENGTH],
2528                   Context->mark1.dl.tm_hour, Context->mark1.dl.tm_min, Context->mark1.dl.tm_sec);
2529            } else
2530                   sprintf(s," %s", Label[L_CLICKLOC]);
2531            break;
2532 
2533         case DISTANCES:
2534            if(Context->mark1.city && Context->mark2.city) {
2535              dist = sin(torad(Context->mark1.city->lat)) * sin(torad(Context->mark2.city->lat))
2536                     + cos(torad(Context->mark1.city->lat)) * cos(torad(Context->mark2.city->lat))
2537                            * cos(torad(Context->mark1.city->lon-Context->mark2.city->lon));
2538              if (dist >= 1.0)
2539                 dist = 0.0;
2540              else
2541              if (dist <= -1.0)
2542                 dist = M_PI;
2543              else
2544                 dist = acos(dist);
2545              sprintf(s, " %s (%s,%s) --> %s (%s,%s)     "
2546                       "%d km  =  %d miles",
2547                Context->mark2.city->name,
2548                num2str(Context->mark2.city->lat,slatp, Context->flags.dms),
2549                num2str(Context->mark2.city->lon, slonp, Context->flags.dms),
2550                Context->mark1.city->name,
2551                num2str(Context->mark1.city->lat, slat, Context->flags.dms),
2552                num2str(Context->mark1.city->lon, slon, Context->flags.dms),
2553                (int)(EARTHRADIUS_KM*dist), (int)(EARTHRADIUS_ML*dist));
2554            } else
2555              sprintf(s, " %s", Label[L_CLICK2LOC]);
2556            break;
2557 
2558         case EXTENSION:
2559            showHours(Context);
2560 	   return;
2561 
2562         default:
2563            break;
2564         }
2565 
2566         l = strlen(s);
2567         if (l<125) {
2568           for (i=l; i<125; i++) s[i] = ' ';
2569           s[125] = '\0';
2570           l = 125;
2571         }
2572 
2573       drawTextStrip(Context, s, l);
2574 }
2575 
2576 void
initShading(Context)2577 initShading(Context)
2578 struct Sundata * Context;
2579 {
2580       int i;
2581 
2582       if (Context->flags.shading <2)
2583          Context->shadefactor = 1.0;
2584       else {
2585          if (Context->flags.shading == 2)
2586             Context->shadefactor = 180.0/(M_PI*(SUN_APPRADIUS+atm_refraction));
2587          else
2588             Context->shadefactor = 180.0/(M_PI*(SUN_APPRADIUS+atm_diffusion));
2589       }
2590 
2591       Context->shadescale = 0.5 * ((double)Context->flags.colorscale + 0.5);
2592 
2593       if (Context->flags.shading == 0 || Context->flags.shading >= 4) {
2594          if (Context->tr1) {
2595             free(Context->tr1);
2596             Context->tr1 = NULL;
2597          }
2598       }
2599 
2600       if (Context->flags.shading <= 1 && Context->flags.shading >= 4) {
2601          if (Context->tr2) {
2602             free(Context->tr2);
2603             Context->tr2 = NULL;
2604          }
2605       }
2606 
2607       if (Context->flags.shading >= 1 && Context->flags.shading <= 3) {
2608          if (!Context->tr1)
2609             Context->tr1 = (short *)
2610                      salloc(Context->geom.width*sizeof(short));
2611          for (i=0; i< (int)Context->geom.width; i++) Context->tr1[i] = 0;
2612       }
2613 
2614       if (Context->flags.shading >= 2 && Context->flags.shading <= 3) {
2615          if (!Context->tr2)
2616             Context->tr2 = (short *)
2617                      salloc(Context->geom.width*sizeof(short));
2618          for (i=0; i< (int)Context->geom.width; i++)
2619             Context->tr2[i] = -1;
2620       }
2621 
2622       Context->south = -1;
2623 }
2624 
2625 void
makeContext(Context,build)2626 makeContext(Context, build)
2627 struct Sundata * Context;
2628 int build;
2629 {
2630         if (build) {
2631            Context->win = 0;
2632 	   Context->xim = NULL;
2633            Context->ximdata = NULL;
2634 	   Context->label = NULL;
2635 	   Context->vmfpixels = NULL;
2636            Context->mappix = 0;
2637            if (Context->wintype)
2638               Context->geom = MapGeom;
2639            else
2640               Context->geom = ClockGeom;
2641            Context->spotsizes = (int *) salloc(city_cat * sizeof(int));
2642            Context->sizelimits = (int *) salloc(city_cat * sizeof(int));
2643 	   memcpy(Context->spotsizes, city_spotsizes, city_cat*sizeof(int));
2644  	   memcpy(Context->sizelimits, city_sizelimits, city_cat*sizeof(int));
2645            Context->zoom = gzoom;
2646            Context->oldzoom = gzoom;
2647            Context->flags = gflags;
2648            Context->jump = time_jump;
2649            Context->clock_img_file = strdup(Clock_img_file);
2650            Context->map_img_file = strdup(Map_img_file);
2651            Context->mark1.city = NULL;
2652            Context->mark1.flags = 0;
2653            Context->pos1.tz = NULL;
2654            Context->mark2.city = NULL;
2655            Context->mark2.flags = 0;
2656 	   Context->lastmarked = NULL;
2657            Context->pos2.tz = NULL;
2658            Context->tr1 = Context->tr2 = NULL;
2659            if (position.lat<=90.0) {
2660               Context->pos1 = position;
2661               Context->mark1.city = &Context->pos1;
2662            }
2663         }
2664 
2665         Context->newzoom = Context->zoom;
2666         setZoomDimension(Context);
2667         Context->zoom = Context->newzoom;
2668 
2669         Context->local_day = -1;
2670         Context->solar_day = -1;
2671 	Context->sundec = 100.0;
2672 	Context->sunlon = 0.0;
2673 	Context->moondec = 100.0;
2674 	Context->moonlon = 0.0;
2675 
2676         if (runlevel!= IMAGERECYCLE) {
2677            if (color_depth<=8) {
2678               Context->daypixel = (unsigned char *) salloc(256*sizeof(char));
2679               Context->nightpixel = (unsigned char *) salloc(256*sizeof(char));
2680            } else {
2681               Context->daypixel = NULL;
2682               Context->nightpixel = NULL;
2683            }
2684 	}
2685 
2686         Context->bits = 0;
2687         Context->flags.update = 4;
2688         Context->footime = 0L;
2689         Context->projtime = -1L;
2690         Context->roottime = -1L;
2691         Context->animtime = -1L;
2692         Context->daywave = (double *) salloc(
2693               (2*Context->geom.height+Context->geom.width)*sizeof(double));
2694         Context->cosval = Context->daywave + Context->geom.width;
2695         Context->sinval = Context->cosval + Context->geom.height;
2696 
2697         initShading(Context);
2698 }
2699 
2700 void
DarkenPixel(Context,x,y,t)2701 DarkenPixel(Context, x, y, t)
2702 struct Sundata * Context;
2703 int x;
2704 int y;
2705 int t;
2706 {
2707         register int i;
2708         unsigned int factor;
2709         unsigned char u, v, w, r, g, b;
2710 
2711         if (Context->flags.colorlevel<FULLCOLORS) {
2712            XDrawPoint(dpy, Context->mappix,Context->gdata->pixgc, x,y);
2713            return;
2714         }
2715         i = bytes_per_pixel * x + Context->xim->bytes_per_line * y;
2716 
2717         if (color_depth>16) {
2718            if (bigendian)
2719               i += bytes_per_pixel - 3;
2720            u = Context->ximdata[i];
2721            v = Context->ximdata[i+1];
2722            w = Context->ximdata[i+2];
2723            if (t>=0) {
2724               factor = Context->flags.darkness + (t * (MAXSHORT-
2725                   Context->flags.darkness))/Context->flags.colorscale;
2726               u = (u * factor)/MAXSHORT;
2727               v = (v * factor)/MAXSHORT;
2728               w = (w * factor)/MAXSHORT;
2729            }
2730            Context->xim->data[i] = u;
2731            Context->xim->data[i+1] = v;
2732            Context->xim->data[i+2] = w;
2733         } else
2734         if (color_depth==16) {
2735 	   if (bigendian) {
2736               u = Context->ximdata[i+1];
2737               v = Context->ximdata[i];
2738            } else {
2739               u = Context->ximdata[i];
2740               v = Context->ximdata[i+1];
2741            }
2742            if (t>=0) {
2743               factor = Context->flags.darkness + (t * (255 -
2744                  Context->flags.darkness))/Context->flags.colorscale;
2745               r = v>>3;
2746               g = ((v&7)<<3) | (u>>5);
2747               b = u&31;
2748               r = (r * factor)/31;
2749               g = (g * factor)/63;
2750               b = (b * factor)/31;
2751               u = (b&248)>>3 | (g&28)<<3;
2752               v = (g&224)>>5 | (r&248);
2753            }
2754            if (bigendian) {
2755               Context->xim->data[i+1] = u;
2756               Context->xim->data[i] = v;
2757            } else {
2758               Context->xim->data[i] = u;
2759               Context->xim->data[i+1] = v;
2760 	   }
2761         } else
2762         if (color_depth==15) {
2763 	   if (bigendian) {
2764               u = Context->ximdata[i+1];
2765               v = Context->ximdata[i];
2766 	   } else {
2767               u = Context->ximdata[i];
2768               v = Context->ximdata[i+1];
2769 	   }
2770            if (t>=0) {
2771               factor = Context->flags.darkness + (t * (255 -
2772                  Context->flags.darkness))/Context->flags.colorscale;
2773               r = v>>2;
2774               g = (v&3)<<3 | (u>>5);
2775               b = u&31;
2776               r = (r * factor)/31;
2777               g = (g * factor)/31;
2778               b = (b * factor)/31;
2779               u = (b&248)>>3 | (g&56)<<2;
2780               v = (g&192)>>6 | (r&248)>>1;
2781            }
2782            if (bigendian) {
2783               Context->xim->data[i+1] = u;
2784               Context->xim->data[i] = v;
2785 	   } else {
2786               Context->xim->data[i] = u;
2787               Context->xim->data[i+1] = v;
2788 	   }
2789         } else {
2790            if (t>=0) {
2791              if ((277*x+359*y) % Context->flags.colorscale <
2792                  Context->flags.colorscale-t)
2793                Context->xim->data[i] =
2794                  Context->nightpixel[(unsigned char)Context->ximdata[i]];
2795              else
2796                Context->xim->data[i] = Context->ximdata[i];
2797            } else
2798              Context->xim->data[i] = Context->ximdata[i];
2799         }
2800 }
2801 
2802 int
howDark(Context,i,j)2803 howDark(Context, i, j)
2804 struct Sundata * Context;
2805 int i, j;
2806 {
2807       double light;
2808       int k;
2809 
2810       if (Context->flags.shading == 0) {
2811 	 return -1;
2812       }
2813 
2814       light = Context->daywave[i] * Context->cosval[j] + Context->sinval[j];
2815 
2816       if (Context->flags.shading == 1) {
2817          if (light >= 0) k = -1; else k = 0;
2818       } else {
2819          if (Context->flags.shading<=3 ||
2820              (Context->flags.shading==4 && light<0))
2821              light *= Context->shadefactor;
2822          k = (int) ((1.0+light)*Context->shadescale);
2823          if (k < 0) k = 0;
2824          if (k >= Context->flags.colorscale) k = - 1;
2825       }
2826       return k;
2827 }
2828 
2829 void
SetPixelLight(Context,i,j,pixel)2830 SetPixelLight(Context, i, j, pixel)
2831 struct Sundata * Context;
2832 int i, j;
2833 Pixel pixel;
2834 {
2835       if (i<0 || i>= Context->geom.width) return;
2836       if (j<0 || j>= Context->geom.height) return;
2837       if (erase_obj)
2838          DarkenPixel(Context, i, j, howDark(Context, i, j));
2839       else
2840 	 XPutPixel(Context->xim, i, j, pixel);
2841 }
2842 
2843 void
XPutStringImage(Context,x,y,s,l,mode)2844 XPutStringImage(Context, x, y, s, l, mode)
2845 Sundata *Context;
2846 int x, y;
2847 char *s;
2848 int l, mode;
2849 {
2850     XImage *xim;
2851     XFontStruct *font = NULL;
2852     Pixel pixel = 0;
2853 
2854     int i, j, w, h, dy;
2855     char u = 0, test;
2856 
2857     if (!s || !strlen(s)) return;
2858 
2859     if (mode <= 1) {
2860        font = Context->gdata->font[COORDFONT];
2861        pixel = Context->gdata->pixel[PARALLELCOLOR-mode];
2862     } else
2863     if (mode == 2) {
2864        font = Context->gdata->font[CITYFONT];
2865        pixel = Context->gdata->pixel[CITYNAMECOLOR];
2866     } else
2867     if (mode >= 3) {
2868        font = Context->gdata->font[LABELFONT];
2869        if (Context->flags.colorlevel <= MANYCOLORS)
2870           pixel = Context->gdata->pixel[CITYNAMECOLOR];
2871        else
2872           pixel = Context->vmfpixels[mode-3];
2873     }
2874 
2875     if (!font) return;
2876     h = font->max_bounds.ascent + font->max_bounds.descent;
2877     dy = font->max_bounds.ascent;
2878 
2879     w = XTextWidth(font, s, l);
2880     if (w>textwidth || h>textheight) {
2881        textwidth = w;
2882        textheight = h;
2883        if (textpix) {
2884 	  XFreePixmap(dpy, textpix);
2885           textpix = 0;
2886        }
2887     }
2888 
2889     if (!textpix) {
2890        textpix = XCreatePixmap(dpy, Context->win, textwidth, textheight, 1);
2891     }
2892 
2893     XSetForeground(dpy, Context->gdata->pixgc, black);
2894     XFillRectangle(dpy, textpix, Context->gdata->pixgc, 0, 0, w, h);
2895     XSetForeground(dpy, Context->gdata->pixgc, white);
2896     XSetFont(dpy, Context->gdata->pixgc, font->fid);
2897     if (Context->flags.colorlevel <= MANYCOLORS) {
2898        XDrawString(dpy, Context->mappix, Context->gdata->pixgc,
2899 		   x+1, y-dy, s, l);
2900        return;
2901     }
2902     XDrawString(dpy, textpix, Context->gdata->pixgc, 0, dy, s, l);
2903     xim = XGetImage(dpy, textpix, 0, 0, w, h, 1, XYPixmap);
2904     if (!xim) return;
2905     test = (bigendian)? 128 : 1;
2906     for (j=0; j<h; ++j) {
2907        if (y-dy+j >= (int)Context->geom.height) break;
2908        for (i=0; i<w; ++i) {
2909 	  if ((i&7) == 0) u = xim->data[j*xim->bytes_per_line+i/8];
2910           if (u&test)
2911 	     SetPixelLight(Context, x+i+1, y-dy+j, pixel);
2912 	  u = (bigendian)? u<<1 : u>>1;
2913        }
2914     }
2915     XDestroyImage(xim);
2916 }
2917 
2918 int
int_latitude(Context,lat)2919 int_latitude(Context, lat)
2920 Sundata * Context;
2921 double lat;
2922 {
2923     return
2924        (int) (Context->zoom.height - (lat+90.0) * (Context->zoom.height/180.0))
2925              - Context->zoom.dy;
2926 }
2927 
2928 int
int_longitude(Context,lon)2929 int_longitude(Context, lon)
2930 Sundata * Context;
2931 double lon;
2932 {
2933     return
2934        (int) ((180.0+lon) * (Context->zoom.width/360.0)) - Context->zoom.dx;
2935 }
2936 
2937 
2938 /*
2939  * drawObject() - Draw an object (city, mark, sun, moon) on the map.
2940  */
2941 
2942 void
drawObject(Context,lon,lat,mode,color,name)2943 drawObject(Context, lon, lat, mode, color, name)
2944 struct Sundata * Context;
2945 double lon, lat;                /* Latitude and longtitude of the city */
2946 int    mode;
2947 int    color;
2948 char *name;
2949 {
2950     /*
2951      * Local Variables
2952      */
2953     Pixel pixel;
2954 
2955     int ilon, ilat;             /* Screen coordinates of the city */
2956     int i, j, dx, dy, u, which, bool=0;
2957     unsigned short * bits;
2958     char slat[20], slon[20];
2959 
2960     if (mode == 0) return;
2961     if ((!Context->wintype && mode > 0) || mode < -SPECIALBITMAPS) return;
2962     if (mode > city_cat) mode = city_cat;
2963     if (mode > 0) {
2964        which = SPECIALBITMAPS + Context->spotsizes[mode-1] - 1;
2965        if (which < SPECIALBITMAPS) return;
2966        if (Context->flags.colorlevel>0 && !Context->flags.citymode) return;
2967        if (Context->zoom.width < Context->sizelimits[mode-1]
2968            && color==0) return;
2969        if (color<0) color = 0;
2970     } else
2971        which = -mode - 1;
2972 
2973     ilon = int_longitude(Context, lon);
2974     if (ilon<0 || ilon>Context->geom.width) return;
2975 
2976     ilat = int_latitude(Context, lat);
2977     if (ilat<0 || ilat>Context->geom.height) return;
2978 
2979     bits = symbol_bits[which];
2980 
2981     dx = bits[0]/2;
2982     dy = bits[1]/2;
2983 
2984     pixel = Context->gdata->pixel[CITYCOLOR0+color];
2985 
2986     if (Context->flags.colorlevel == FULLCOLORS) {
2987        for (j=0; j<bits[1]; j++) {
2988 	  if (ilat-dy+j >= (int) Context->geom.height) break;
2989           u = bits[j+2];
2990           for (i=0; i<bits[0]; i++) {
2991              if (u&1) SetPixelLight(Context, ilon-dx+i, ilat-dy+j, pixel);
2992              u = u>>1;
2993           }
2994        }
2995     } else
2996     if (Context->flags.colorlevel >= FEWCOLORS) {
2997        XSetForeground(dpy, Context->gdata->wingc, pixel);
2998        for (j=0; j<bits[1]; j++) {
2999 	  if (ilat-dy+j >= (int) Context->geom.height) break;
3000           u = bits[j+2];
3001           for (i=0; i<bits[0]; i++) {
3002 	     if (u&1) XDrawPoint(dpy, Context->win, Context->gdata->wingc,
3003                 ilon-dx+i, ilat-dy+j);
3004 	     u = u>>1;
3005 	  }
3006        }
3007     } else {
3008        XSetForeground(dpy, Context->gdata->pixgc, pixel);
3009        for (j=0; j<bits[1]; j++) {
3010 	  if (ilat-dy+j >= (int) Context->geom.height) break;
3011           u = bits[j+2];
3012           for (i=0; i<bits[0]; i++) {
3013 	     if (u&1) XDrawPoint(dpy, Context->mappix, Context->gdata->pixgc,
3014                 ilon-dx+i, ilat-dy+j);
3015 	     u = u>>1;
3016 	  }
3017        }
3018     }
3019 
3020     if (mode<0) ++dx;
3021 
3022     if (Context->flags.citymode==2 && name) {
3023        if (Context->flags.colorlevel == FULLCOLORS)
3024           XPutStringImage(Context, ilon+dx, ilat-1, name, strlen(name), 2);
3025        else
3026 	 if (Context->flags.colorlevel >= FEWCOLORS) {
3027           XSetForeground(dpy, Context->gdata->wingc,
3028 			     Context->gdata->pixel[CITYNAMECOLOR]);
3029           XDrawString(dpy, Context->win, Context->gdata->wingc,
3030                       ilon+dx, ilat-1, name, strlen(name));
3031        } else
3032           XDrawString(dpy, Context->mappix, Context->gdata->pixgc,
3033                       ilon+dx, ilat-1, name, strlen(name));
3034     }
3035 
3036     if (!Context->wintype) return;
3037     if ((Context->flags.citymode==3 && mode>0) ||
3038         (bool=(Context->flags.objectmode==2 && mode<-1))) {
3039        dy = Context->gdata->mapstrip/2;
3040        (void) num2str(lat, slat, Context->flags.dms);
3041        (void) num2str(lon, slon, Context->flags.dms);
3042        if (Context->flags.colorlevel == FULLCOLORS) {
3043 	  XSetFont(dpy, Context->gdata->pixgc,
3044 		        Context->gdata->font[CITYFONT]->fid);
3045           XPutStringImage(Context, ilon+dx, ilat-1, slat, strlen(slat), 2);
3046           XPutStringImage(Context, ilon+dx, ilat-1+dy, slon, strlen(slon), 2);
3047        } else
3048        if (Context->flags.colorlevel >= FEWCOLORS ) {
3049           XSetBackground(dpy, Context->gdata->wingc, Context->gdata->pixel[MAPBGCOLOR]);
3050           XSetForeground(dpy, Context->gdata->wingc, Context->gdata->pixel[CITYNAMECOLOR]);
3051           XSetFont(dpy, Context->gdata->wingc,
3052                         Context->gdata->font[CITYFONT]->fid);
3053           XDrawString(dpy, Context->win, Context->gdata->wingc,
3054                       ilon+dx, ilat-1, slat, strlen(slat));
3055           XDrawString(dpy, Context->win, Context->gdata->wingc,
3056                       ilon+dx, ilat-1+dy, slon, strlen(slon));
3057        } else {
3058 	  XSetFont(dpy, Context->gdata->pixgc,
3059 		        Context->gdata->font[CITYFONT]->fid);
3060           XDrawString(dpy, Context->mappix, Context->gdata->pixgc,
3061                       ilon+dx, ilat-1, slat, strlen(slat));
3062           XDrawString(dpy, Context->mappix, Context->gdata->pixgc,
3063                       ilon+dx, ilat-1+dy, slon, strlen(slon));
3064        }
3065     }
3066 }
3067 
3068 void
drawCities(Context)3069 drawCities(Context)
3070 struct Sundata * Context;
3071 {
3072 City *c;
3073         if (!Context->wintype || !Context->flags.citymode) return;
3074 
3075 	if (Context->flags.colorlevel==MONOCHROME ||
3076             Context->flags.colorlevel==FULLCOLORS)
3077            XSetFont(dpy, Context->gdata->pixgc,
3078                          Context->gdata->font[CITYFONT]->fid);
3079         else
3080            XSetFont(dpy, Context->gdata->wingc,
3081                          Context->gdata->font[CITYFONT]->fid);
3082 
3083         if (Context->lastmarked && Context->mark1.city != Context->lastmarked
3084             && Context->flags.colorlevel>MONOCHROME) {
3085 	   erase_obj = 1;
3086 	   c = Context->lastmarked;
3087            drawObject(Context, c->lon, c->lat, c->size, 1, c->name);
3088 	   erase_obj = 0;
3089 	}
3090 
3091         for (c = cityroot; c; c = c->next) {
3092 	   if (c!=Context->mark1.city && c!=Context->mark2.city)
3093               drawObject(Context, c->lon, c->lat, c->size,
3094                          -(c==cityinit), c->name);
3095 	}
3096 
3097        	c = Context->mark2.city;
3098 	if (c && c!=&Context->pos2)
3099            drawObject(Context, c->lon, c->lat, c->size, 2, c->name);
3100 
3101        	c = Context->mark1.city;
3102 	if (c && c!=&Context->pos1) {
3103            drawObject(Context, c->lon, c->lat, c->size, 1, c->name);
3104 	   Context->lastmarked = c;
3105 	}
3106 }
3107 
3108 void
drawMarks(Context)3109 drawMarks(Context)
3110 struct Sundata * Context;
3111 {
3112         if (Context->flags.colorlevel==MONOCHROME || !Context->wintype) return;
3113 
3114         /* code for color mode */
3115 	if (erase_obj==0 || (erase_obj&1))
3116         if (Context->mark1.city == &Context->pos1)
3117           drawObject(Context, Context->mark1.city->lon,
3118                               Context->mark1.city->lat,
3119                               -1, 3, NULL);
3120 
3121 	if (erase_obj==0 || (erase_obj&2))
3122         if (Context->mark2.city == &Context->pos2)
3123           drawObject(Context, Context->mark2.city->lon,
3124                               Context->mark2.city->lat,
3125                               -1, 4, NULL);
3126 }
3127 
3128 void
drawLabels(Context)3129 drawLabels(Context)
3130 struct Sundata * Context;
3131 {
3132    int ilon, ilat, width, dw = 0;
3133    struct TextLabel * label;
3134    char *text, *text0, *ptr;
3135 
3136    if(!Context->wintype) return;
3137    label = Context->label;
3138    while (label) if (label->text && *label->text) {
3139       ilon = int_longitude(Context, label->lon);
3140       ilat = int_latitude(Context, label->lat);
3141       text = text0 = strdup(label->text);
3142       while(text) {
3143 	 ptr = index(text,'\n');
3144 	 if (ptr) *ptr = '\0';
3145          width = XTextWidth(Context->gdata->font[LABELFONT],
3146 			    text, strlen(text));
3147          if (label->position==0) dw = width/2;
3148          else
3149 	 if (label->position==-1) dw = width;
3150          XPutStringImage(Context, ilon-dw, ilat, text,
3151    	                 strlen(text), label->color+3);
3152 	 ilat += Context->gdata->font[LABELFONT]->ascent+
3153 	         Context->gdata->font[LABELFONT]->descent+2;
3154 	 if (ptr) text = ptr+1; else text = NULL;
3155       }
3156       free(text0);
3157       label = label->next;
3158    }
3159 }
3160 
3161 double
getSpacing(Context,mode)3162 getSpacing(Context, mode)
3163 Sundata * Context;
3164 int mode;   /* 0=parallel 1=meridian spacing */
3165 {
3166   double val[12] =
3167     { 0.1, 0.2, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0, 30.0, 45.0, 90.0 };
3168   double quot = 0.0;
3169   int i = 0;
3170 
3171   i = 11;
3172 
3173   if (mode == 0)
3174      quot = 4200.0/(double)Context->zoom.height;
3175 
3176   if (mode == 1)
3177      quot = 10800.0/(double)Context->zoom.width;
3178 
3179   if (quot<0.5 && mode) quot = quot*1.2;
3180 
3181   while (i>0 && val[i-1] > quot) --i;
3182 
3183   if (mode==1 && i==8) ++i;
3184   return val[i];
3185 }
3186 
3187 
3188 /*
3189  * drawParallel() - Draw a parallel line
3190  */
3191 
3192 void
drawParallel(Context,pixel,lat,step,thickness,text,numdigits)3193 drawParallel(Context, pixel, lat, step, thickness, text, numdigits)
3194 struct Sundata * Context;
3195 Pixel pixel;
3196 double lat;
3197 int step;
3198 int thickness;
3199 int text;
3200 int numdigits;
3201 {
3202         int ilat, i0, i1, i, j, jp, k, min, max, doit;
3203 	char s[10], format[10];
3204 
3205         if (!Context->wintype) return;
3206 
3207         ilat = int_latitude(Context, lat);
3208 
3209         i = Context->flags.meridian & 3 ;
3210 	min = 0;
3211 	max = Context->geom.height;
3212 	if (i==2) max = max-coordvalheight;
3213 	if (i==3) min = coordvalheight;
3214 
3215         if (ilat<min || ilat>=max) return;
3216 
3217         doit = 1;
3218 	if (text<0) {
3219 	   text = -text;
3220            if (lat != 0.0)
3221 	      doit = 0;
3222 	}
3223 
3224 	if (text>=2) {
3225            sprintf(format, "%%.%df�", numdigits);
3226 	   sprintf(s, format, lat);
3227            min = XTextWidth(Context->gdata->font[COORDFONT], s, strlen(s))+4;
3228            max = (int) Context->geom.width;
3229 	   if (text==2)
3230  	     i0 = 2;
3231 	   else {
3232              max = max-min-2;
3233 	     i0 = max+4;
3234 	     min = 0;
3235 	   }
3236            i1 = (coordvalheight-6)/3;
3237 	   if (doit) {
3238 	      if (Context->flags.colorlevel == FULLCOLORS)
3239                  XPutStringImage(Context, i0, ilat+1, s, strlen(s), 0);
3240 	      else
3241 	      if (Context->flags.colorlevel >= MANYCOLORS)
3242                  XDrawString(dpy, Context->win,
3243                      Context->gdata->wingc, i0, ilat+i1, s, strlen(s));
3244 	      else
3245                  XDrawString(dpy, Context->mappix,
3246                      Context->gdata->pixgc, i0, ilat+i1, s, strlen(s));
3247 	   }
3248 	} else {
3249 	   min = 0;
3250 	   max = (int) Context->geom.width - 1;
3251 	}
3252 
3253         i0 = Context->geom.width/2;
3254         i1 = 1+i0/step;
3255         for (i=-i1; i<i1; i+=1) {
3256           j = i0+step*i-thickness;
3257           jp = i0+step*i+thickness;
3258           if (j<0) j = 0;
3259           if (jp>max) jp = max;
3260 	  if (jp<min) continue;
3261           if (j>max) continue;
3262 	  if (Context->flags.colorlevel == FULLCOLORS) {
3263              for (k=j; k<=jp; k++)
3264                  SetPixelLight(Context, k, ilat, pixel);
3265 	  } else
3266 	  if (Context->flags.colorlevel >= MANYCOLORS)
3267              XDrawLine(dpy, Context->win, Context->gdata->wingc,
3268                        j, ilat, jp,ilat);
3269 	  else
3270              XDrawLine(dpy, Context->mappix, Context->gdata->pixgc,
3271                        j, ilat, jp,ilat);
3272 	}
3273 }
3274 
3275 void
drawParallels(Context)3276 drawParallels(Context)
3277 struct Sundata * Context;
3278 {
3279         Pixel pixel;
3280         static  double val[5] = { -66.55, -23.45, 0.0, 23.45, 66.55 };
3281 	double  f1, f2, spacing;
3282         int     i, b1, b2, parmode, numdigits;
3283 
3284         if (!Context->wintype || !Context->flags.parallel) return;
3285 
3286 	parmode = Context->flags.parallel & 3;
3287 
3288 	if (Context->zoom.paralspacing)
3289 	   spacing = Context->zoom.paralspacing;
3290 	else
3291 	   spacing = getSpacing(Context, 0);
3292 
3293         /* b = (int) (89.9/spacing); */
3294 	f1 = (double)(Context->zoom.dy+Context->geom.height)/
3295                 ((double)Context->zoom.height);
3296 	f2 = (double)Context->zoom.dy/((double)Context->zoom.height);
3297 
3298 	b1 = rint(0.7 + (90.0 - f1*180.0)/spacing);
3299 	b2 = rint(-0.7 + (90.0 - f2*180.0)/spacing);
3300 
3301         pixel = Context->gdata->pixel[PARALLELCOLOR];
3302 
3303         if (Context->flags.colorlevel==FULLCOLORS ||
3304             Context->flags.colorlevel==MONOCHROME) {
3305            XSetFont(dpy, Context->gdata->pixgc,
3306                          Context->gdata->font[COORDFONT]->fid);
3307         } else {
3308            XSetFont(dpy, Context->gdata->wingc,
3309                          Context->gdata->font[COORDFONT]->fid);
3310            XSetForeground(dpy, Context->gdata->wingc, pixel);
3311 	}
3312 
3313 	numdigits = (spacing<1.0);
3314 	if (parmode)
3315         for (i=b1; i<=b2; i++) if (i!=0 || Context->flags.parallel <=3)
3316            drawParallel(Context, pixel, i*spacing, 3, Context->flags.dotted,
3317                        parmode, numdigits);
3318 
3319         pixel = Context->gdata->pixel[TROPICCOLOR];
3320         if (Context->flags.colorlevel==MANYCOLORS)
3321            XSetForeground(dpy, Context->gdata->wingc, pixel);
3322 
3323 	if (Context->flags.parallel & 8) {
3324            for (i=0; i<5; i++)
3325               drawParallel(Context, pixel, val[i], 3, 1, -parmode, numdigits);
3326 	}
3327 }
3328 
3329 /*
3330  * drawMeridian() - Draw a meridian line
3331  */
3332 
3333 void
drawMeridian(Context,lon,step,thickness,numdigits)3334 drawMeridian(Context, lon, step, thickness, numdigits)
3335 struct Sundata * Context;
3336 double lon;
3337 int step;
3338 int thickness;
3339 int numdigits;
3340 {
3341         int ilon, i0, i1, i, j, jp, k, min, max;
3342 	char s[10], format[10];
3343 
3344         ilon = int_longitude(Context, lon);
3345 
3346         i = Context->flags.parallel & 3 ;
3347 	min = 0;
3348 	max = Context->geom.width;
3349 	if (i==2) min = coordvalwidth;
3350 	if (i==3) max = max-coordvalwidth;
3351 
3352         if (ilon<min || ilon>=max) return;
3353 
3354         i0 = Context->geom.height/2;
3355         i1 = 1+i0/step;
3356 	if (Context->flags.meridian>=2) {
3357            sprintf(format, "%%.%df�", numdigits);
3358 	   sprintf(s, format, lon);
3359 	   i = 2*XTextWidth(Context->gdata->font[COORDFONT], s, strlen(s))/5;
3360 	   min = Context->gdata->font[COORDFONT]->max_bounds.ascent +
3361                Context->gdata->font[COORDFONT]->max_bounds.descent + 3;
3362 	   max = (int) Context->geom.height;
3363 	   if (Context->flags.meridian==2) {
3364 	      j = Context->geom.height-3;
3365 	      max = max-min-1;
3366 	      min = 0;
3367 	   } else
3368 	      j = min-4;
3369 	   if (Context->flags.colorlevel==FULLCOLORS)
3370               XPutStringImage(Context, ilon-i, j, s, strlen(s), 1);
3371 	   else
3372 	   if (Context->flags.colorlevel>=MANYCOLORS)
3373               XDrawString(dpy, Context->win, Context->gdata->wingc,
3374                        ilon-i, j, s, strlen(s));
3375 	   else
3376               XDrawString(dpy, Context->mappix, Context->gdata->pixgc,
3377                        ilon-i, j, s, strlen(s));
3378 	} else {
3379 	   min = 0;
3380 	   max = (int) Context->geom.height - 1 ;
3381 	}
3382 
3383         for (i=-i1; i<i1; i+=1) {
3384            j = i0+step*i-thickness;
3385            jp = i0+step*i+thickness;
3386            if (j<0) j = 0;
3387            if (jp>max) jp = max;
3388 	   if (jp<min) continue;
3389 	   if (j>max) continue;
3390 	   if (Context->flags.colorlevel==FULLCOLORS) {
3391 	      for (k=j; k<=jp; k++)
3392 	         SetPixelLight(Context, ilon, k,
3393                                Context->gdata->pixel[MERIDIANCOLOR]);
3394 	   } else
3395 	   if (Context->flags.colorlevel >= MANYCOLORS)
3396               XDrawLine(dpy, Context->win, Context->gdata->wingc,
3397                         ilon, j, ilon, jp);
3398 	   else
3399 	      XDrawLine(dpy, Context->mappix, Context->gdata->pixgc,
3400                         ilon, j, ilon, jp);
3401 	}
3402 }
3403 
3404 void
drawMeridians(Context)3405 drawMeridians(Context)
3406 struct Sundata * Context;
3407 {
3408         int     i, b1, b2, numdigits;
3409 	double  spacing, f1, f2;
3410 
3411         if (!Context->wintype || !Context->flags.meridian) return;
3412 
3413 	if (Context->zoom.meridspacing)
3414 	   spacing = Context->zoom.meridspacing;
3415 	else
3416 	   spacing = getSpacing(Context, 1);
3417 
3418         /* b = (int) (179.9/spacing); */
3419 	f1 = (double)Context->zoom.dx/((double)Context->zoom.width);
3420 	f2 = (double)(Context->zoom.dx+Context->geom.width)/
3421                 ((double)Context->zoom.width);
3422 
3423 	b1 = rint(0.7 + (f1*360.0 - 180.0)/spacing);
3424 	b2 = rint(-0.7 + (f2*360.0 - 180.0)/spacing);
3425 
3426         if (Context->flags.colorlevel==FULLCOLORS ||
3427             Context->flags.colorlevel==MONOCHROME) {
3428            XSetFont(dpy, Context->gdata->pixgc,
3429                          Context->gdata->font[COORDFONT]->fid);
3430         } else {
3431            XSetFont(dpy, Context->gdata->wingc,
3432                          Context->gdata->font[COORDFONT]->fid);
3433            XSetForeground(dpy, Context->gdata->wingc,
3434                           Context->gdata->pixel[MERIDIANCOLOR]);
3435 	}
3436 
3437 	numdigits = (spacing<1.0);
3438         for (i=b1; i<=b2; i++)
3439           drawMeridian(Context, i*spacing, 3, Context->flags.dotted, numdigits);
3440 }
3441 
3442 void
drawLines(Context)3443 drawLines(Context)
3444 Sundata * Context;
3445 {
3446         coordvalwidth =
3447             XTextWidth(Context->gdata->font[COORDFONT], "-45�", 4) + 6;
3448         coordvalheight = Context->gdata->font[COORDFONT]->max_bounds.ascent +
3449             Context->gdata->font[COORDFONT]->max_bounds.descent + 6;
3450         drawMeridians(Context);
3451         drawParallels(Context);
3452 }
3453 
3454 /*
3455  * drawSunAndMoon() - Draw Sun and Moon at position where they stand at zenith
3456  */
3457 
3458 void
drawSunAndMoon(Context)3459 drawSunAndMoon(Context)
3460 struct Sundata * Context;
3461 {
3462     if (Context->flags.objectmode) {
3463        if (Context->flags.colorlevel>=FEWCOLORS &&
3464            Context->flags.colorlevel<=MANYCOLORS) {
3465 	 if (Context->flags.objects & 4) return;
3466        }
3467        if (Context->flags.objects & 1)
3468           drawObject(Context, Context->sunlon, Context->sundec, -2, 5, NULL);
3469        if (Context->flags.objects & 2)
3470           drawObject(Context, Context->moonlon, Context->moondec, -3, 6, NULL);
3471        Context->flags.objects |= 4;
3472     }
3473 }
3474 
3475 void
drawBottomline(Context)3476 drawBottomline(Context)
3477 struct Sundata * Context;
3478 {
3479     if (Context->flags.bottom & 2) return;
3480     if (Context->flags.bottom & 1) {
3481        XSetForeground(dpy, Context->gdata->wingc,
3482           Context->gdata->pixel[CLOCKSTRIPFGCOLOR+Context->wintype]);
3483        XDrawLine(dpy, Context->win, Context->gdata->wingc,
3484                    0, Context->geom.height,
3485                    Context->geom.width-1, Context->geom.height);
3486     }
3487     Context->flags.bottom |= 2;
3488 }
3489 
3490 void
drawAll(Context)3491 drawAll(Context)
3492 struct Sundata * Context;
3493 {
3494         if (Context->flags.colorlevel == FULLCOLORS) {
3495            if (!Context->xim) return;
3496         } else {
3497            if (!Context->mappix) return;
3498         }
3499         if (Context->flags.colorlevel != FEWCOLORS) drawLines(Context);
3500         drawBottomline(Context);
3501         drawCities(Context);
3502         drawMarks(Context);
3503         drawLabels(Context);
3504 }
3505 
3506 void
showMapImage(Context)3507 showMapImage(Context)
3508 struct Sundata * Context;
3509 {
3510         if (button_pressed) return;
3511 
3512         if (!Context->flags.mapped) {
3513 	   Context->flags.update = 0;
3514 	   return;
3515 	}
3516 
3517         if (Context->flags.update>=2) {
3518            if (Context->flags.colorlevel == FULLCOLORS) {
3519 	       drawAll(Context);
3520                XPutImage(dpy, Context->win, Context->gdata->wingc,
3521                     Context->xim, 0, 0, 0, 0,
3522                     Context->geom.width, Context->geom.height);
3523            } else {
3524 	       XSetBackground(dpy, Context->gdata->wingc,
3525                      Context->gdata->pixel[CLOCKBGCOLOR+Context->wintype]);
3526 	       XSetForeground(dpy, Context->gdata->wingc,
3527                      Context->gdata->pixel[CLOCKFGCOLOR+Context->wintype]);
3528                XCopyPlane(dpy, Context->mappix, Context->win,
3529 		    Context->gdata->wingc,
3530                     0, 0, Context->geom.width, Context->geom.height, 0, 0, 1);
3531 	       Context->flags.objects &= 3;
3532 	   }
3533         }
3534 
3535         if (Context->flags.update) {
3536            Context->flags.update = 0;
3537            if (Context->flags.colorlevel >= FEWCOLORS &&
3538                Context->flags.colorlevel <= MANYCOLORS) drawAll(Context);
3539         }
3540 }
3541 
3542 void
pulseMarks(Context)3543 pulseMarks(Context)
3544 struct Sundata * Context;
3545 {
3546 int     done = 0;
3547 
3548         if (!Context->wintype) return;
3549         if (Context->flags.colorlevel == FULLCOLORS) {
3550            if (!Context->xim) return;
3551         } else {
3552            if (!Context->mappix) return;
3553         }
3554         if (Context->mark1.city && Context->mark1.flags<0) {
3555            if (Context->mark1.pulse) {
3556              drawObject(Context, Context->mark1.save_lon,
3557                    Context->mark1.save_lat, -1, 0, NULL);
3558              done = 1;
3559            }
3560            Context->mark1.save_lat = Context->mark1.city->lat;
3561            Context->mark1.save_lon = Context->mark1.city->lon;
3562            if (Context->mark1.city == &Context->pos1) {
3563               done = 1;
3564               drawObject(Context, Context->mark1.save_lon,
3565                                 Context->mark1.save_lat, -1, 0, NULL);
3566               Context->mark1.pulse = 1;
3567            } else
3568               Context->mark1.pulse = 0;
3569            Context->mark1.flags = 1;
3570         }
3571         else
3572         if (Context->mark1.flags>0) {
3573            if (Context->mark1.city|| Context->mark1.pulse) {
3574               drawObject(Context, Context->mark1.save_lon,
3575                                 Context->mark1.save_lat, -1, 0, NULL);
3576               Context->mark1.pulse = 1-Context->mark1.pulse;
3577               done = 1;
3578            }
3579            if (Context->mark1.city == NULL) Context->mark1.flags = 0;
3580         }
3581 
3582         if (Context->mark2.city && Context->mark2.flags<0) {
3583            if (Context->mark2.pulse) {
3584              drawObject(Context, Context->mark2.save_lon,
3585                                Context->mark2.save_lat, -1, 0, NULL);
3586              done = 1;
3587            }
3588            Context->mark2.save_lat = Context->mark2.city->lat;
3589            Context->mark2.save_lon = Context->mark2.city->lon;
3590            if (Context->mark2.city == &Context->pos2) {
3591               drawObject(Context, Context->mark2.save_lon,
3592                        Context->mark2.save_lat, -1, 0, NULL);
3593               done = 1;
3594               Context->mark2.pulse = 1;
3595            } else
3596               Context->mark2.pulse = 0;
3597            Context->mark2.flags = 1;
3598         }
3599         else
3600         if (Context->mark2.flags>0) {
3601            if (Context->mark2.city || Context->mark2.pulse) {
3602               drawObject(Context, Context->mark2.save_lon,
3603                                 Context->mark2.save_lat, -1, 0, NULL);
3604               Context->mark2.pulse = 1 - Context->mark2.pulse;
3605               done = 1;
3606            }
3607            if (Context->mark2.city == NULL) Context->mark2.flags = 0;
3608         }
3609         if (done) {
3610            Context->flags.update = 2;
3611            showMapImage(Context);
3612         }
3613 }
3614 
3615 /* To be used in case of mono mode only, to clear night area */
3616 
3617 void
clearNightArea(Context)3618 clearNightArea(Context)
3619 struct Sundata * Context;
3620 {
3621       int i, j;
3622 
3623       /* if (Context->flags.colorlevel < FULLCOLORS) return; */
3624 
3625       for (i = 0; i < (int)Context->geom.width; i++) {
3626         if (Context->south==0)
3627           for (j = Context->tr1[i]; j< (int)Context->geom.height; j++)
3628              DarkenPixel(Context, i, j, -1);
3629         else
3630           for (j = 0; j <Context->tr1[i]; j++)
3631              DarkenPixel(Context, i, j, -1);
3632       }
3633 }
3634 
3635 /*  moveNightArea  --  Update illuminated portion of the globe.  */
3636 
3637 void
moveNightArea(Context)3638 moveNightArea(Context)
3639 struct Sundata * Context;
3640 {
3641       int i, j, k, l, jmin, jmax, j0;
3642       int midcolor, south, north;
3643       short tr1, tr2;
3644       double shift, shiftp, quot, cd, sd;
3645       double f1, f2, f3;
3646 
3647       Context->flags.hours_shown = 0;
3648       if (!Context->flags.shading) return;
3649 
3650       f1 = M_PI/((double) Context->zoom.height);
3651       f2 = ((double) Context->zoom.height)/M_PI;
3652       f3 = 1E-8 * f2;
3653 
3654       shift = f1 * (double)Context->zoom.dy;
3655       shiftp = 0.5*(Context->zoom.height+1) - (double) Context->zoom.dy;
3656       quot = torad(Context->sundec);
3657       cd = cos(quot);
3658       sd = sin(quot);
3659       if (quot>0) south = 0; else south = -1;
3660       north = -1-south;
3661 
3662       quot = 2.0*M_PI/(double)Context->zoom.width;
3663       for (i = 0; i < (int)Context->geom.width; i++)
3664          Context->daywave[i] = cos(quot *(((double)i)-Context->fnoon));
3665 
3666       for (j = 0; j < (int)Context->geom.height; j++) {
3667          quot = shift + f1 * (double)j;
3668          Context->cosval[j] = sin(quot)*cd;
3669          Context->sinval[j] = cos(quot)*sd;
3670       }
3671 
3672       /* Shading = 1 uses tr1 as j-integer value of transition day/night */
3673       /* Context->south means color value near South pole */
3674       /* which is updated as south, with north = -1-south = opposite color */
3675 
3676       if (Context->flags.shading == 1) {
3677          for (i = 0; i < (int)Context->geom.width; i++) {
3678             if (fabs(sd)>f3)
3679                tr1 = (int) (shiftp+f2*atan(Context->daywave[i]*cd/sd));
3680             else
3681                tr1 = 0;
3682             if (tr1<0) tr1 = 0;
3683             if (tr1>(int)Context->geom.height) tr1 = (int)Context->geom.height;
3684             if (south==Context->south) {
3685                for (j = tr1; j<(int)Context->tr1[i]; j++)
3686                      DarkenPixel(Context, i, j, south);
3687                for (j = (int)Context->tr1[i]; j<tr1; j++)
3688                      DarkenPixel(Context, i, j, north);
3689             } else {
3690                if (tr1 <= (int)Context->tr1[i]) {
3691                   for (j = 0; j<tr1; j++)
3692                      DarkenPixel(Context, i, j, north);
3693                   for (j = (int)Context->tr1[i];
3694                            j<(int)Context->geom.height; j++)
3695                      DarkenPixel(Context, i, j, south);
3696                } else {
3697                   for (j = 0; j<(int)Context->tr1[i]; j++)
3698                      DarkenPixel(Context, i, j, north);
3699                   for (j = tr1; j<(int)Context->geom.height; j++)
3700                      DarkenPixel(Context, i, j, south);
3701                }
3702             }
3703             Context->tr1[i] = tr1;
3704          }
3705          Context->south = south;
3706          return;
3707       }
3708 
3709       /* Shading = 4,5 are quite straightforward... compute everything! */
3710 
3711       if (Context->flags.shading >= 4) {
3712          for (i = 0; i < (int)Context->geom.width; i++)
3713             for (j = 0; j< (int)Context->geom.height; j++) {
3714                DarkenPixel(Context, i, j, howDark(Context, i, j));
3715             }
3716          return;
3717       }
3718 
3719       /* Shading = 2,3 uses both tr1 and tr2 and is very tricky... */
3720       /* If both tr1,tr2 >=0 then normal transition
3721             day -> shadow -> night  (or night -> shadow -> day)
3722          Otherwise we have an "exceptional transition"
3723             shadow near North pole -> (day or night) -> shadow near South pole
3724          Day or night is encoded midcolor, determined as follow:
3725               if tr1<0 then midcolor = Context->south
3726               if tr2<0 then midcolor = -1-Context->south (opposite color)
3727          Renormalize integres by  tr1=-2-tr1 if <0 tr2=-2-tr2 if <0.
3728          Then tr1>tr2 are the limits for the interval where color=midcolor */
3729 
3730       for (i = 0; i < (int)Context->geom.width; i++) {
3731          if (fabs(sd)>f3)
3732             j0 = (int) (shiftp+f2*atan(Context->daywave[i]*cd/sd));
3733          else
3734             j0 = 0;
3735          if (j0<0) j0 = 0;
3736          if (j0>(int)Context->geom.height) j0 = (int)Context->geom.height;
3737 
3738          tr1 = 0;
3739          tr2 = (short)(Context->geom.height-1);
3740          midcolor = -2;
3741          if (Context->tr1[i] < 0) {
3742             Context->tr1[i] = -Context->tr1[i]-2;
3743             midcolor = Context->south;
3744          }
3745          if (Context->tr2[i] < 0 && midcolor==-2) {
3746             Context->tr2[i] = -Context->tr2[i]-2;
3747             midcolor = -1-Context->south;
3748          }
3749 
3750          for (j=j0; j<(int)Context->geom.height; j++) {
3751             k = howDark(Context, i, j);
3752             if (k!=south)
3753                DarkenPixel(Context, i, j, k);
3754             else {
3755                tr2 = (short)(j-1);
3756                jmax = (int)Context->geom.height-1;
3757                if (j<jmax && howDark(Context, i, jmax) != south) {
3758                   for (l=jmax; l>tr2 ; l--) {
3759                      k = howDark(Context, i, l);
3760                      if (k!=south)
3761                         DarkenPixel(Context, i, l, k);
3762                      else {
3763                         jmax = l;
3764                         break;
3765                      }
3766                   }
3767                   tr1 = (short)(-jmax-1);
3768                }
3769                if (Context->tr1[i]<=Context->tr2[i]) {
3770                   if (Context->south == south) {
3771                      if ((int)Context->tr2[i]<jmax) jmax=(int)Context->tr2[i];
3772                   } else {
3773                      if ((int)Context->tr1[i]>j) j=(int)Context->tr1[i];
3774                   }
3775                } else {
3776                   if (midcolor == south) {
3777                      for (l=j; l<=jmax && l<= (int)Context->tr2[i]; l++)
3778                          DarkenPixel(Context, i, l, south);
3779                      for (l=jmax; l>=j && l>= (int)Context->tr1[i]; l--)
3780                          DarkenPixel(Context, i, l, south);
3781                      break;
3782                   }
3783                }
3784                for (l=j; l<=jmax; l++) DarkenPixel(Context, i, l, south);
3785                break;
3786             }
3787          }
3788 
3789          for (j=j0-1; j>=0; j--) {
3790             k = howDark(Context, i, j);
3791             if (k!=north)
3792                DarkenPixel(Context, i, j, k);
3793             else {
3794                tr1 = (short) j+1;
3795                jmin = 0;
3796                if (j>0 && howDark(Context, i, 0) != north) {
3797                   for (l=0; l<tr1; l++) {
3798                      k = howDark(Context, i, l);
3799                      if (k!=north)
3800                         DarkenPixel(Context, i, l, k);
3801                      else {
3802                         jmin = l;
3803                         break;
3804                      }
3805                   }
3806                   tr2 = (short)(-jmin-1);
3807                }
3808                if (Context->tr1[i]<=Context->tr2[i]) {
3809                   if (Context->south == south) {
3810                      if ((int)Context->tr1[i]>jmin) jmin=(int)Context->tr1[i];
3811                   } else {
3812                      if ((int)Context->tr2[i]<j) j=(int)Context->tr2[i];
3813                   }
3814                } else {
3815                   if (midcolor == north) {
3816                      for (l=jmin; l<=j && l<= (int)Context->tr2[i]; l++)
3817                          DarkenPixel(Context, i, l, north);
3818                      for (l=j; l>=jmin && l>= (int)Context->tr1[i]; l--)
3819                          DarkenPixel(Context, i, l, north);
3820                      break;
3821                   }
3822                }
3823                for (l=jmin; l<=j; l++) DarkenPixel(Context, i, l, north);
3824                break;
3825             }
3826          }
3827 
3828          Context->tr1[i] = tr1;
3829          Context->tr2[i] = tr2;
3830       }
3831 
3832       Context->south = south;
3833 }
3834 
3835 void
drawShadedArea(Context)3836 drawShadedArea (Context)
3837 Sundata * Context;
3838 {
3839      int size;
3840 
3841      if ((Context->flags.colorlevel<FULLCOLORS) ||
3842          (Context->flags.colorscale == 1)) {
3843         if (Context->flags.shading) {
3844            initShading(Context);
3845            moveNightArea(Context);
3846         } else {
3847            clearNightArea(Context);
3848            if (Context->tr1) {
3849               free(Context->tr1);
3850               Context->tr1 = NULL;
3851            }
3852         }
3853      } else {
3854         size = Context->xim->bytes_per_line*Context->xim->height;
3855         memcpy(Context->xim->data, Context->ximdata, size);
3856         initShading(Context);
3857      }
3858 }
3859 
3860 City *
markLocation(Context,name)3861 markLocation(Context, name)
3862 struct Sundata * Context;
3863 char *  name;
3864 {
3865 City *c;
3866 
3867         c = searchCityLocation(name);
3868         if (c) {
3869 	   Context->mark1.city = c;
3870            if (Context->flags.colorlevel==MONOCHROME)
3871               Context->mark1.flags = -1;
3872         }
3873 	return c;
3874 }
3875 
3876 void
checkLocation(Context,name)3877 checkLocation(Context, name)
3878 struct Sundata * Context;
3879 char *  name;
3880 {
3881         (void) markLocation(Context, name);
3882 
3883         if (Context->mark1.city == &Context->pos1) {
3884                 Context->flags.map_mode = SOLARTIME;
3885                 Context->mark1.city = NULL;
3886                 setTZ(NULL);
3887                 Context->mark1.city = &Context->pos1;
3888                 if (Context->flags.colorlevel==MONOCHROME) pulseMarks(Context);
3889                 Context->pos1.name = Label[L_POINT];
3890         } else
3891 	    cityinit = Context->mark1.city;
3892 }
3893 
3894 /* --- */
3895 /*  UPDIMAGE  --  Update current displayed image.  */
3896 
3897 void
updateImage(Context)3898 updateImage(Context)
3899 struct Sundata * Context;
3900 {
3901         int                     noon;
3902         double                  fnoon;
3903 	double junk;
3904 
3905         /* If this is a full repaint of the window, force complete
3906            recalculation. */
3907 
3908         if (button_pressed) return;
3909 
3910         time(&Context->footime);
3911 
3912 	erase_obj = 1;
3913 	if (Context->flags.colorlevel == MONOCHROME ||
3914             Context->flags.colorlevel == FULLCOLORS)
3915             drawSunAndMoon(Context);
3916         erase_obj = 0;
3917 
3918         (void) sunParams(Context->footime + Context->jump,
3919               &Context->sunlon, &Context->sundec, NULL);
3920 
3921         (void) phase(Context->footime + Context->jump,
3922               &Context->moondec, &Context->moonlon,
3923               &junk,  &junk, &junk, &junk, &junk, &junk );
3924 	Context->moonlon = fixangle(Context->moonlon+180.0) - 180.0;
3925 
3926         fnoon = Context->sunlon * (Context->zoom.width / 360.0)
3927                          - (double) Context->zoom.dx;
3928         noon = (int) fnoon;
3929         Context->sunlon -= 180.0;
3930 
3931         /* Projecting the illumination curve  for the current seasonal
3932            instant is costly.  If we're running in real time, only  do
3933            it every PROJINT seconds.
3934            If the subsolar point has moved at least one pixel, also
3935            update the illuminated area on the screen.   */
3936 
3937         if (Context->projtime < 0 ||
3938             (Context->footime - Context->projtime) > PROJINT ||
3939             Context->noon != noon || Context->flags.update>=4) {
3940                 Context->flags.update = 2;
3941                 Context->projtime = Context->footime;
3942                 Context->noon = noon;
3943                 Context->fnoon = fnoon;
3944                 moveNightArea(Context);
3945 		if (Context->flags.colorlevel==FULLCOLORS) {
3946 		   drawAll(Context);
3947 		   drawCities(Context);
3948 		}
3949         }
3950 
3951         drawSunAndMoon(Context);
3952 }
3953 
3954 void
setPosition1(Context,x,y)3955 setPosition1(Context, x, y)
3956 Sundata *Context;
3957 int x, y;
3958 {
3959     Context->pos1.name = Label[L_POINT];
3960     Context->pos1.lat = 90.0-((double)(y+Context->zoom.dy)/
3961                               (double)Context->zoom.height)*180.0 ;
3962     Context->pos1.lon = ((double)(x+Context->zoom.dx)/
3963                          (double)Context->zoom.width)*360.0-180.0 ;
3964     Context->mark1.city = &Context->pos1;
3965 }
3966 
3967 void
updateUrban(Context,city)3968 updateUrban(Context, city)
3969 Sundata *Context;
3970 City *city;
3971 {
3972     if (!do_urban) {
3973        if (city!=NULL && city == Context->mark1.city)
3974           PopUrban(Context);
3975     } else {
3976        XMapWindow(dpy, Urban);
3977        XMapRaised(dpy, Urban);
3978     }
3979     if (do_urban) {
3980        updateUrbanEntries(Context, city);
3981        setupUrban(0);
3982     }
3983 }
3984 
3985 /*
3986  * processPoint() - This is kind of a cheesy way to do it but it works. What happens
3987  *                  is that when a different city is picked, the TZ environment
3988  *                  variable is set to the timezone of the city and then tzset().
3989  *                  is called to reset the system.
3990  */
3991 
3992 void
processPoint(Context,x,y)3993 processPoint(Context, x, y)
3994 struct Sundata * Context;
3995 int x, y;      /* Screen co-ordinates of mouse */
3996 {
3997     /*
3998      * Local Variables
3999      */
4000 
4001     City *city;    /* Used to search for a city */
4002     int cx, cy;    /* Screen coordinates of the city */
4003 
4004     /* Loop through the cities until on close to the pointer is found */
4005 
4006     for (city = cityroot; city; city = city->next) {
4007 
4008         /* Convert the latitude and longitude of the cities to integer */
4009 
4010         if (city->size == 0) continue;
4011         if (Context->zoom.width <
4012                Context->sizelimits[city->size-1] && city!=cityinit) continue;
4013         cx = int_longitude(Context, city->lon)-x;
4014 	cy = int_latitude(Context, city->lat)-y;
4015 
4016         /* Check to see if we are close enough */
4017 
4018         if (cx*cx+cy*cy <= 13) break;
4019     }
4020 
4021     if (Context->flags.map_mode == LEGALTIME) {
4022       if (city)
4023         Context->flags.map_mode = COORDINATES;
4024       else
4025         Context->flags.map_mode = SOLARTIME;
4026     }
4027 
4028     updateUrban(Context, city);
4029 
4030     switch(Context->flags.map_mode) {
4031 
4032       case COORDINATES:
4033       case EXTENSION:
4034         if (city)
4035            Context->mark1.city = city;
4036         Context->flags.update = 1;
4037         break;
4038 
4039       case DISTANCES:
4040         if (Context->mark2.city) {
4041 	    if (Context->flags.colorlevel==FULLCOLORS) {
4042 	       erase_obj = 2;
4043 	       drawMarks(Context);
4044                erase_obj = 0;
4045 	    }
4046 	}
4047         if (Context->mark1.city == &Context->pos1) {
4048             Context->pos2 = Context->pos1;
4049             Context->mark2.city = &Context->pos2;
4050         } else
4051             Context->mark2.city = Context->mark1.city;
4052         if (city)
4053            Context->mark1.city = city;
4054         else
4055            setPosition1(Context, x, y);
4056         Context->flags.update = 2;
4057         break;
4058 
4059       case SOLARTIME:
4060         if (Context->mark1.city) {
4061 	   if (Context->flags.colorlevel==FULLCOLORS) {
4062 	      erase_obj = 1;
4063 	      drawMarks(Context);
4064               erase_obj = 0;
4065 	   }
4066 	}
4067         if (city)
4068            Context->mark1.city = city;
4069         else
4070            setPosition1(Context, x, y);
4071         Context->flags.update = 2;
4072         break;
4073 
4074       default:
4075         break;
4076     }
4077 
4078     setDayParams(Context);
4079 
4080     if (Context->flags.colorlevel==MONOCHROME) {
4081       if (Context->mark1.city) Context->mark1.flags = -1;
4082       if (Context->mark2.city) Context->mark2.flags = -1;
4083     } else {
4084       drawAll(Context);
4085       showMapImage(Context);
4086       Context->flags.update = 2;
4087     }
4088 
4089     if (do_urban && !city) updateUrban(Context, Context->mark1.city);
4090 }
4091 
4092 void
report_failure(path,code)4093 report_failure(path, code)
4094 char *path;
4095 int code;
4096 {
4097   switch(code) {
4098     case -1: fprintf(stderr, "%s:\nUnknown image format !!\n", path);
4099              break;
4100 
4101     case 0:  break;
4102 
4103     case 1:  fprintf(stderr, "Cannot read file %s !!\n", path);
4104              break;
4105 
4106     case 2:  fprintf(stderr, "File %s has corrupted data!!\n", path);
4107              break;
4108 
4109     case 3:  fprintf(stderr, "Cannot decode format specification of %s !!\n",
4110              path);
4111              break;
4112 
4113     case 4:  fprintf(stderr,
4114                 "Image creation failed (memory alloc. problem?) !!\n");
4115              break;
4116 
4117     case 5:  fprintf(stderr, "Header of file %s corrupted !!\n", path);
4118              break;
4119 
4120     case 6:  fprintf(stderr, "Color allocation failed !!\n");
4121              break;
4122 
4123     case 7:  fprintf(stderr, "Trying instead default  %s\n", path);
4124              break;
4125 
4126     default:
4127              fprintf(stderr, "Unknown error in  %s !!\n", path);
4128              break;
4129   }
4130 }
4131 
4132 #define COMPARE 260
4133 
4134 void
quantize(Context)4135 quantize(Context)
4136 Sundata * Context;
4137 {
4138      int i, j, k, l, compare, quantum, change;
4139      unsigned short r[256], g[256], b[256];
4140      int count[256], done[256];
4141      char substit[256], value[256], inverse[256], compose[256];
4142      int d[COMPARE], v1[COMPARE], v2[COMPARE];
4143      int size, dist;
4144      XColor xc;
4145 
4146      if (verbose)
4147         fprintf(stderr, "Number of distinct colors in the map: %d colors\n",
4148                 Context->ncolors);
4149 
4150      xc.flags = DoRed | DoGreen | DoBlue;
4151 
4152      for (i=0; i<256; i++) inverse[i] = '\0';
4153 
4154      for (i=0; i<Context->ncolors; i++) {
4155          count[i] = 0;
4156          xc.pixel = Context->daypixel[i];
4157          XQueryColor(dpy, tmp_cmap, &xc);
4158          r[i] = xc.red; g[i] = xc.green; b[i] = xc.blue;
4159 	 inverse[(unsigned char)xc.pixel] = i;
4160      }
4161 
4162      if (tmp_cmap != cmap0)
4163         XFreeColormap(dpy, tmp_cmap);
4164 
4165      size = Context->xim->bytes_per_line * Context->xim->height;
4166 
4167      for (i=0; i<Context->ncolors; i++) {
4168          substit[i] = (char)i;
4169          value[i] = (char)i;
4170      }
4171 
4172      createGData(Context, 0);
4173      quantum = (256-Context->gdata->usedcolors)/2;
4174 
4175      if (Context->ncolors<=quantum) goto finish;
4176 
4177      if (verbose)
4178         fprintf(stderr, "That's too much, quantizing to %d colors...\n",
4179                  quantum);
4180 
4181      for (i=0; i<size; i++) ++count[(unsigned char)Context->xim->data[i]];
4182 
4183      compare = COMPARE;
4184      for (i=0; i<compare; i++) d[i] = 2147483647;
4185 
4186      for(i=0; i<Context->ncolors; i++)
4187         for(j=i+1; j<Context->ncolors; j++) {
4188             dist = abs((int)(r[i]-r[j]))+abs((int)(g[i]-g[j]))+
4189                                         +abs((int)(b[i]-b[j]));
4190             k=compare-1;
4191             while (k>=0 && dist<d[k]) k--;
4192             ++k;
4193             if (k<compare) {
4194               for (l=compare-1; l>k; l--) {
4195                   d[l] = d[l-1];
4196                   v1[l] = v1[l-1];
4197                   v2[l] = v2[l-1];
4198               }
4199               d[k] = dist;
4200               if (count[i]>count[j] || (count[i]==count[j] && i<j)) {
4201                  v1[k] = i;
4202                  v2[k] = j;
4203               } else {
4204                  v1[k] = j;
4205                  v2[k] = i;
4206               }
4207             }
4208         }
4209 
4210      l = 0;
4211      for (i=0; i<compare; i++) {
4212          j = v1[i];
4213          k = v2[i];
4214          if (substit[k]==(char) k) {
4215            substit[k] = j;
4216            ++l;
4217          }
4218          if (l>=Context->ncolors-quantum) break;
4219      }
4220      if (verbose)
4221         fprintf(stderr,
4222            "%d substitutions from %d pairs of similar colors\n", l, i);
4223 
4224      change = 1;
4225      while (change) {
4226         change = 0;
4227         l = 0;
4228         for (i=0; i<Context->ncolors; i++) {
4229            j = (unsigned char) substit[i];
4230            if (substit[i]==(char)i) l++;
4231            if (substit[j] != (char)j) {
4232               substit[i] = substit[j];
4233               change = 1;
4234            }
4235         }
4236      }
4237 
4238   finish:
4239 
4240      if (verbose) {
4241         if (Context->gdata->cmap==cmap0)
4242            fprintf(stderr, "Allocating map colors in default colormap:\n");
4243         else
4244            fprintf(stderr, "Allocating map colors in private colormap:\n");
4245      }
4246 
4247      for (i=0; i<Context->ncolors; i++) done[i] = 0;
4248      for (i=0; i<256; i++) Context->nightpixel[i] = (unsigned char)i;
4249 
4250      k=0;
4251      for (i=0; i<Context->ncolors; i++) {
4252         j = (unsigned char)substit[i];
4253         if (!done[j]) {
4254            xc.red = r[j];
4255            xc.green = g[j];
4256            xc.blue = b[j];
4257            if (!XAllocColor(dpy, Context->gdata->cmap, &xc)) {
4258               color_alloc_failed = 1;
4259               value[j] = 0;
4260            } else
4261               value[j] = (char)xc.pixel;
4262            xc.red = (unsigned int) (xc.red * Context->flags.darkness) / 255;
4263            xc.green = (unsigned int) (xc.green * Context->flags.darkness) / 255;
4264            xc.blue = (unsigned int) (xc.blue * Context->flags.darkness) / 255;
4265            if (!XAllocColor(dpy, Context->gdata->cmap, &xc))
4266               color_alloc_failed = 1;
4267            if (value[j]) {
4268 	      Context->daypixel[k] = value[j];
4269               Context->nightpixel[(unsigned char)value[j]] = (char)xc.pixel;
4270 	   }
4271            done[j] = 1;
4272            k++;
4273         }
4274      }
4275 
4276      if (Context->gdata->cmap==cmap0 && color_alloc_failed) {
4277         if (verbose) fprintf(stderr, "Failed !!\n");
4278         if (Context->gdata->links==0) free(Context->gdata);
4279         createGData(Context, 1);
4280         goto finish;
4281      }
4282 
4283      Context->ncolors = k;
4284 
4285      if (verbose)
4286         fprintf(stderr, "  2*%d+%d=%d colors allocated in colormap\n",
4287 	    k, Context->gdata->usedcolors, 2*k+Context->gdata->usedcolors);
4288 
4289      for (i=0; i<256; i++) compose[i] =
4290        value[(unsigned char)substit[(unsigned char)inverse[(unsigned char)i]]];
4291 
4292      for (i=0; i<size; i++)
4293        Context->xim->data[i] = compose[(unsigned char)Context->xim->data[i]];
4294 }
4295 
4296 int
createImage(Context)4297 createImage(Context)
4298 struct Sundata * Context;
4299 {
4300    FILE *fd;
4301    char *file, path[1024]="";
4302    int code;
4303 
4304    if (runlevel == IMAGERECYCLE) {
4305       if (verbose)
4306          fprintf(stderr,
4307            "Recycling image (XID %ld) and changing requested parameters...\n",
4308 	   (gflags.colorlevel==FULLCOLORS)? (long) Context->xim :
4309                                        (long) Context->mappix);
4310       code = 0;
4311       if (gflags.colorlevel)
4312 	 goto run_direct2;
4313       else
4314          goto run_direct1;
4315    }
4316 
4317    if (color_depth<=8 && Context->flags.colorlevel>0)
4318      tmp_cmap = XCreateColormap(dpy, Root, visual, AllocNone);
4319    else
4320      tmp_cmap = cmap0;
4321 
4322    file = (Context->wintype)? Context->map_img_file : Context->clock_img_file;
4323 
4324  do_path:
4325 
4326    Context->xim = NULL;
4327    code = -1;
4328 
4329    if (file) {
4330       strcpy(path, file);
4331       if (*file != '/' && *file != '.' ) {
4332          if ((fd=fopen(file, "r"))) {
4333             fclose(fd);
4334          } else {
4335             if (verbose)
4336               fprintf(stderr, "%s not in current directory ...\n"
4337                       "Trying to load %s from share directory instead\n",
4338                       file, file);
4339             sprintf(path, "%s%s", share_maps_dir, file);
4340          }
4341       }
4342    }
4343 
4344    if (*path && strcmp(path, Default_img_file)) {
4345       if ((fd = fopen(path, "r")))
4346          fclose(fd);
4347       else {
4348          file = Default_img_file;
4349          fprintf(stderr, "File %s doesn't seem to exist !!\n"
4350                          "Trying default  %s\n",
4351                          path, file);
4352          goto do_path;
4353       }
4354    }
4355 
4356    if (Context->wintype) {
4357      if (Context->map_img_file && file!=Context->map_img_file)
4358         StringReAlloc(&Context->map_img_file, file);
4359    } else {
4360      if (Context->clock_img_file && file!=Context->clock_img_file) {
4361         StringReAlloc(&Context->clock_img_file, file);
4362      }
4363    }
4364 
4365    if (gflags.colorlevel < FULLCOLORS) {
4366    retry:
4367      code = readVMF(path, Context);
4368      if (code==0 && Context->bits) {
4369        Context->mappix = XCreatePixmapFromBitmapData(dpy, Root,
4370           Context->bits, Context->geom.width,
4371           Context->geom.height, 0, 1, 1);
4372      run_direct1:
4373        createGData(Context, 0);
4374        if (color_alloc_failed) report_failure(path, 6);
4375        if (Context->bits) free(Context->bits);
4376        createGCs(Context);
4377        return 0;
4378      } else {
4379        if (strcmp(path, Default_img_file)) {
4380           report_failure(path, 1);
4381           strcpy(path, Default_img_file);
4382           report_failure(path, 7);
4383           goto retry;
4384        }
4385        report_failure(path, 1);
4386        return 1;
4387      }
4388    }
4389 
4390    if (strstr(path, ".gif"))
4391       code = readGIF(path, Context);
4392    else
4393    if (strstr(path, ".jpg"))
4394       code = readJPEG(path, Context);
4395    else
4396    if (strstr(path, ".png"))
4397       code = readPNG(path, Context);
4398    else
4399    if (strstr(path, ".vmf"))
4400       code = readVMF(path, Context);
4401    else
4402    if (strstr(path, ".xpm"))
4403       code = readXPM(path, Context);
4404 
4405    if (code) {
4406       report_failure(path, code);
4407       if (strcmp(path, Default_img_file)) {
4408          file = Default_img_file;
4409          report_failure(file, 7);
4410          goto do_path;
4411       }
4412    }
4413 
4414  run_direct2:
4415    if (color_depth<=8)
4416       quantize(Context);
4417 
4418    if (color_alloc_failed) {
4419          code = 6;
4420 	 if (Context->xim) {
4421             XDestroyImage(Context->xim);
4422             Context->xim = 0;
4423 	 }
4424 	 return code;
4425    }
4426 
4427    createGData(Context, 0);
4428    createGCs(Context);
4429 
4430    return code;
4431 }
4432 
4433 void
createWorkImage(Context)4434 createWorkImage(Context)
4435 struct Sundata * Context;
4436 {
4437    int size;
4438 
4439    if (Context->xim) {
4440      size = Context->xim->bytes_per_line*Context->xim->height;
4441      if (verbose)
4442         fprintf(stderr, "Creating work image data of size "
4443              "%d x %d x %d bpp = %d bytes\n",
4444              Context->xim->width, Context->xim->height,
4445              Context->xim->bytes_per_line/Context->xim->width,size);
4446      if (!Context->ximdata)
4447         Context->ximdata = (char *)salloc(size);
4448      memcpy(Context->ximdata, Context->xim->data, size);
4449    }
4450 
4451 }
4452 
GetVRoot(dpy)4453 Window GetVRoot(dpy)
4454      Display *dpy;
4455 {
4456   int          i;
4457   Window       rootReturn, parentReturn, *children;
4458   unsigned int numChildren;
4459   Atom         __SWM_VROOT = None;
4460   Window       rslt = Root;
4461 
4462   __SWM_VROOT = XInternAtom(dpy, "__SWM_VROOT", False);
4463   XQueryTree(dpy, Root, &rootReturn, &parentReturn, &children, &numChildren);
4464   for (i=0; i<numChildren; i++)
4465     {
4466     Atom          actual_type;
4467     int           actual_format;
4468     unsigned long nitems, bytesafter;
4469     Window       *newRoot = NULL;
4470 
4471     /* item 148 in the FAQ neglects to mention that there is a race
4472      * condition here; consider a child of the root window that
4473      * existed when XQueryTree() was called, but has disappeared
4474      * before XGetWindowProperty() gets called for that window ...
4475      */
4476     if ((XGetWindowProperty(dpy, children[i], __SWM_VROOT, 0, 1,
4477                             False, XA_WINDOW, &actual_type,
4478                             &actual_format, &nitems, &bytesafter,
4479                             (unsigned char **) &newRoot) == Success)
4480         && newRoot)
4481       {
4482       rslt = *newRoot;
4483       break;
4484       }
4485     }
4486 
4487   /* item 148 in the FAQ also neglects to mention that we probably
4488    * want to free the list of children after we're done with it ...
4489    */
4490   XFree((void *) children);
4491 
4492   return rslt;
4493 }
4494 
4495 void
drawDottedRectangle(dpy,w,gc,x,y,a,b,pix1,pix2)4496 drawDottedRectangle(dpy, w, gc, x, y, a, b, pix1, pix2)
4497 Display * dpy;
4498 Drawable w;
4499 GC gc;
4500 int x, y, a, b;
4501 Pixel pix1, pix2;
4502 {
4503 int i, j;
4504 
4505    for (j=y; j<=y+b; j+=b)
4506    for (i=x; i<=x+a; i++) {
4507       XSetForeground(dpy, gc, ((i+j)%2)? pix1 : pix2);
4508       XDrawPoint(dpy, w, gc, i, j);
4509    }
4510 
4511    for (i=x; i<=x+a; i+=a)
4512    for (j=y+1; j<y+b; j++) {
4513       XSetForeground(dpy, gc, ((i+j)%2)? pix1 : pix2);
4514       XDrawPoint(dpy, w, gc, i, j);
4515    }
4516 }
4517 
4518 void
drawImageToRootWindow(Context,mode)4519 drawImageToRootWindow(Context, mode)
4520 Sundata * Context;
4521 int mode;
4522 {
4523 Window Vroot = GetVRoot(dpy);
4524 Window win;
4525 int wr = DisplayWidth(dpy,scr);
4526 int hr = DisplayHeight(dpy,scr);
4527 
4528 int a, b, c, i, j, k, l, ww, hw, mapped, update = 1;
4529 int dx[5] = { 0, 0, 0, 1, -1};
4530 int dy[5] = { 0, 1, -1, 0, 0};
4531 
4532      if (mode>0) {
4533         if (do_root < 1)
4534 	   do_root = 1;
4535 	else
4536 	   do_root = 2;
4537      }
4538      if (mode<0) {
4539         if (do_root > 0)
4540            do_root = 0;
4541 	else
4542            do_root = -1;
4543      }
4544 
4545      ww = Context->geom.width;
4546      hw = Context->geom.height;
4547      if (do_root == 2) hw += Context->hstrip;
4548 
4549      if (abs(Context->footime - Context->roottime) >= root_period)
4550         Context->roottime = Context->footime;
4551      else
4552         if (do_root == 2 && mode==0 && rootpix) update = 0;
4553 
4554      if (!rootpix)
4555         rootpix = XCreatePixmap(dpy, Root, wr, hr, DefaultDepth(dpy, scr));
4556 
4557      if (update) {
4558         XSetForeground(dpy, Context->gdata->wingc,
4559                          Context->gdata->pixel[ROOTCOLOR]);
4560         XFillRectangle(dpy, rootpix, Context->gdata->wingc, 0, 0, wr, hr);
4561         srandom(Context->footime);
4562         if (random_rootpos) {
4563            rootdx = (double)(random() % 10001)/10000.0;
4564            rootdy = (double)(random() % 10001)/10000.0;
4565         }
4566      }
4567 
4568      if (ww>=wr-5) a = 0; else a = (wr-ww-5)*rootdx;
4569      if (hw>=hr-5) b = 0; else b = (hr-hw-5)*rootdy;
4570 
4571      if (do_root >= 0 && update) {
4572         XSetForeground(dpy, Context->gdata->wingc,
4573 	            Context->gdata->pixel[STARCOLOR]);
4574         c = random() % 191;
4575         for (i=0; i<wr; i++)
4576         for (j=0; j<hr; j++) {
4577 	  if ((i*i+j)%971 == (j*j*j+i)%593 + c) {
4578 	     if (((9*i+j*j)%7) == 0) l=4; else l=0;
4579 	     for (k=0; k<=l; k++)
4580                 XDrawPoint(dpy, rootpix,
4581                      Context->gdata->wingc, i+dx[k], j+dy[k]);
4582 	  }
4583 	}
4584 	if (do_root > 0)
4585         for (i=0; i<=5; i++)
4586 	   drawDottedRectangle(dpy, rootpix, Context->gdata->wingc,
4587                                a-i-1, b-i-1, ww+2*i+1, hw+2*i+1,
4588 			       Context->gdata->pixel[MAPBGCOLOR],
4589 			       Context->gdata->pixel[MAPFGCOLOR]);
4590      }
4591 
4592      if (do_root>=1) {
4593 	win = Context->win;
4594         Context->win = XCreatePixmap(dpy, Root, ww, hw, DefaultDepth(dpy,scr));
4595 	mapped = Context->flags.mapped;
4596 	if (update) {
4597 	   Context->flags.mapped = 1;
4598            Context->flags.update = 2;
4599            updateImage(Context);
4600            showMapImage(Context);
4601 	}
4602 	if (do_root == 2) {
4603 	   RootCaller = Context;
4604 	   XSetForeground(dpy, Context->gdata->wingc,
4605                                Context->gdata->pixel[MAPSTRIPBGCOLOR]);
4606            XFillRectangle(dpy, Context->win, Context->gdata->wingc,
4607               0, Context->geom.height, Context->geom.width, Context->hstrip);
4608            Context->flags.bottom &= 1;
4609  	   drawBottomline(Context);
4610 	   if (screen_saver) Context->flags.mapped = 1;
4611            Context->flags.hours_shown = 0;
4612            writeStrip(Context);
4613 	}
4614 	if (update)
4615            XCopyArea(dpy, Context->win, rootpix, Context->gdata->wingc,
4616                      0, 0, ww, hw, a, b);
4617 	else
4618            XCopyArea(dpy, Context->win, Root, Context->gdata->wingc,
4619                      0, Context->geom.height, ww, Context->hstrip,
4620                      a, b+Context->geom.height);
4621         XFlush(dpy);
4622         XFreePixmap(dpy, Context->win);
4623         Context->win = win;
4624         Context->flags.mapped = mapped;
4625      }
4626 
4627      if (update) {
4628         XSetWindowBackgroundPixmap(dpy, Vroot, rootpix);
4629         XClearWindow(dpy, Vroot);
4630      }
4631      XFlush(dpy);
4632      if (mode<0) {
4633         XFreePixmap(dpy, rootpix);
4634 	rootpix = 0;
4635      }
4636 }
4637 
4638 void
warningNew(Context)4639 warningNew(Context)
4640 struct Sundata * Context;
4641 {
4642    XFlush(dpy);
4643    clearStrip(Context);
4644    XFlush(dpy);
4645    usleep(TIMESTEP);
4646    drawTextStrip(Context, Label[L_NEWIMAGE], strlen(Label[L_NEWIMAGE]));
4647    XFlush(dpy);
4648 }
4649 
4650 void
buildMap(Context,wintype,build)4651 buildMap(Context, wintype, build)
4652 struct Sundata * Context;
4653 int wintype, build;
4654 {
4655    Window win;
4656    int old_w, old_h, old_s, resize;
4657 
4658    if (build < 2)
4659       resize = 0;
4660    else {
4661       resize = 1;
4662       build = 0;
4663    }
4664 
4665    if (build) {
4666       struct Sundata * NewContext;
4667       NewContext = (struct Sundata *)salloc(sizeof(struct Sundata));
4668       NewContext->next = NULL;
4669       if (Context) {
4670          if (Context->next) {
4671             NewContext->next = Context->next;
4672             Context->next = NewContext;
4673          } else
4674             Context->next = NewContext;
4675       } else
4676          Seed = NewContext;
4677       Context = NewContext;
4678       Context->wintype = wintype;
4679       if (do_menu<0) {
4680          do_menu = 1;
4681          MenuCaller = Context;
4682       }
4683       if (do_filesel<0) {
4684          do_filesel = 1;
4685          FileselCaller = Context;
4686       }
4687       if (do_zoom<0) {
4688          do_zoom = 1;
4689          ZoomCaller = Context;
4690       }
4691       if (do_option<0) {
4692          do_option = 1;
4693          OptionCaller = Context;
4694       }
4695    }
4696 
4697    makeContext(Context, build);
4698 
4699    win = Context->win;
4700    if (win)
4701       XSelectInput(dpy, Context->win, 0);
4702 
4703    if (createImage(Context)) {
4704      if (Seed->next) {
4705          shutDown(Context, 0);
4706          Context = Seed;
4707          return;
4708      } else
4709          shutDown(Context, -1);
4710    }
4711    checkGeom(Context, 0);
4712 
4713    if (win) {
4714       old_s = Context->hstrip;
4715       Context->hstrip = (wintype)?
4716           Context->gdata->mapstrip : Context->gdata->clockstrip;
4717       setClassHints(Context->win, wintype);
4718       setSizeHints(Context, wintype);
4719       getPlacement(Context->win, &Context->geom.x, &Context->geom.y,
4720                    &old_w, &old_h);
4721       old_h -= Context->hstrip;
4722       if (resize || Context->hstrip != old_s ||
4723           Context->geom.width!=old_w || Context->geom.height!=old_h) {
4724 	 XMapRaised(dpy, Context->win);
4725 	 Context->flags.mapped = 1;
4726 	 XFlush(dpy);
4727          usleep(TIMESTEP);
4728          setAuxilWins(Context, REMAP);
4729       } else
4730          setAuxilWins(Context, RESET);
4731       if (runlevel!=IMAGERECYCLE || color_depth<=8)
4732          createWorkImage(Context);
4733       setProtocols(Context, Context->wintype);
4734    } else {
4735       createWorkImage(Context);
4736       if (screen_saver) {
4737          Context->flags.mapped = 0;
4738          do_root = 2;
4739 	 Context->hstrip = Context->gdata->mapstrip;
4740 	 checkLocation(Context, CityInit);
4741 	 drawImageToRootWindow(Context, 0);
4742 	 return;
4743       }
4744       Context->win = newWindow(Context, &Context->geom, wintype);
4745       setSizeHints(Context, wintype);
4746       XMapWindow(dpy, Context->win);
4747       Context->flags.mapped = 1;
4748       XFlush(dpy);
4749       usleep(TIMESTEP);
4750       setAuxilWins(Context, REATTRIB);
4751       setProtocols(Context, wintype);
4752       Context->prevgeom.width = 0;
4753    }
4754 
4755    checkLocation(Context, CityInit);
4756    if (Context->flags.colorlevel == MONOCHROME) drawAll(Context);
4757    clearStrip(Context);
4758    if (Context->gdata->cmap!=cmap0)
4759       XSetWindowColormap(dpy, Context->win, Context->gdata->cmap);
4760    runlevel = RUNNING;
4761    option_changes = 0;
4762    Context->flags.update = 4;
4763    updateImage(Context);
4764    showMapImage(Context);
4765    do_sync |= 2;
4766 }
4767 
4768 void
processStringEntry(keysym,entry)4769 processStringEntry(keysym, entry)
4770 KeySym keysym;
4771 TextEntry *entry;
4772 {
4773 int i, j;
4774            i = strlen(entry->string);
4775 
4776            switch(keysym) {
4777              case XK_Left:
4778                if (entry->caret>0) --entry->caret;
4779                break;
4780              case XK_Right:
4781                if (entry->caret<i) ++entry->caret;
4782                break;
4783              case XK_Home:
4784                entry->caret = 0;
4785                break;
4786              case XK_End:
4787                entry->caret = strlen(entry->string);
4788                break;
4789              case XK_BackSpace:
4790              case XK_Delete:
4791                if (entry->caret>0) {
4792                   --entry->caret;
4793                   for (j=entry->caret; j<i;j++)
4794                      entry->string[j] = entry->string[j+1];
4795                }
4796                break;
4797              default:
4798                if (control_key) {
4799                   if (keysym==XK_space) {
4800                      keysym = 31;
4801                      goto specialspace;
4802                   }
4803                   if (keysym==XK_a) entry->caret = 0;
4804                   if (keysym==XK_b && entry->caret>0) --entry->caret;
4805                   if (keysym==XK_e) entry->caret = i;
4806                   if (keysym==XK_f && entry->caret<i) ++entry->caret;
4807                   if (keysym==XK_d) {
4808                      for (j=entry->caret; j<i;j++)
4809                         entry->string[j] = entry->string[j+1];
4810                   }
4811                   if (keysym==XK_k) {
4812                      entry->oldcaret = entry->caret;
4813                      entry->oldlength = i;
4814                      entry->oldchar = entry->string[entry->caret];
4815                      entry->string[entry->caret] = '\0';
4816                   }
4817                   if (keysym==XK_y && entry->caret==entry->oldcaret) {
4818                      entry->string[entry->oldcaret] = entry->oldchar;
4819                      entry->string[entry->oldlength] = '\0';
4820                      entry->oldcaret = -1;
4821                   }
4822                   break;
4823                }
4824            specialspace:
4825                if (keysym<31) break;
4826 	       if (keysym>=XK_KP_Multiply && keysym<=XK_KP_9)
4827                    keysym = keysym - XK_KP_0 + '0';
4828                if (keysym>255) break;
4829                if (i<entry->maxlength) {
4830                   for (j=i; j>entry->caret; j--)
4831                      entry->string[j] = entry->string[j-1];
4832                   entry->string[entry->caret] = (char) keysym;
4833                   entry->string[i+1] = '\0';
4834                   ++entry->caret;
4835                }
4836                break;
4837            }
4838 }
4839 
4840 /*
4841  *  Process key events in eventLoop
4842  */
4843 
4844 void
processKey(win,keysym)4845 processKey(win, keysym)
4846 Window  win;
4847 KeySym  keysym;
4848 {
4849         double v;
4850         int i, j, old_mode;
4851         KeySym key;
4852         struct Sundata * Context = NULL;
4853 
4854         Context = getContext(win);
4855         if (!Context) return;
4856         if (Context->flags.colorlevel == FULLCOLORS) {
4857            if (!Context->xim) return;
4858         } else {
4859            if (!Context->mappix) return;
4860         }
4861 
4862         key = keysym;
4863         Context->flags.update = 1;
4864         if (key>=XK_A && key<=XK_Z) key += 32;
4865         old_mode = Context->flags.map_mode;
4866 
4867         if (win==Filesel) {
4868            switch(key) {
4869              case XK_Escape:
4870                 if (do_filesel)
4871                   PopFilesel(Context);
4872                 return;
4873              case XK_Page_Up:
4874                 if (filesel_shift == 0) return;
4875                 filesel_shift -= num_lines/2;
4876                 if (filesel_shift <0) filesel_shift = 0;
4877                 break;
4878              case XK_Page_Down:
4879                 if (num_table_entries-filesel_shift<num_lines) return;
4880                 filesel_shift += num_lines/2;
4881                 break;
4882              case XK_Up:
4883                 if (filesel_shift == 0) return;
4884                 filesel_shift -= 1;
4885                 break;
4886              case XK_Down:
4887                 if (num_table_entries-filesel_shift<num_lines) return;
4888                 filesel_shift += 1;
4889                 break;
4890              case XK_Home:
4891                 if (filesel_shift == 0) return;
4892                 filesel_shift = 0;
4893                 break;
4894              case XK_End:
4895                 if (num_table_entries-filesel_shift<num_lines) return;
4896                 filesel_shift = num_table_entries - num_lines+2;
4897                 break;
4898              case XK_Left:
4899              case XK_Right:
4900                 return;
4901              default :
4902                 goto general;
4903            }
4904            setupFilesel(1);
4905            return;
4906         }
4907 
4908         if (win==Zoom) {
4909            switch(key) {
4910              case XK_Escape:
4911                 if (do_zoom)
4912                   PopZoom(Context);
4913                 return;
4914              default:
4915                 goto general;
4916            }
4917         }
4918 
4919         if (win==Option) {
4920 	   if (text_input!=OPTION_INPUT) goto general;
4921            switch(keysym) {
4922              case XK_Escape:
4923                if (do_option)
4924                   PopOption(Context);
4925                return;
4926              case XK_KP_Enter:
4927              case XK_Return:
4928                   activateOption();
4929                return;
4930 	     default:
4931 	       processStringEntry(keysym, &option_entry);
4932                setupOption(0);
4933 	       return;
4934 	   }
4935         }
4936 
4937         if (win==Urban) {
4938 	   if (text_input<URBAN_INPUT && keysym!=XK_Escape) goto general;
4939 	   i = text_input-URBAN_INPUT;
4940            switch(keysym) {
4941              case XK_Escape:
4942                if (do_urban)
4943                   PopUrban(Context);
4944                return;
4945              case XK_KP_Enter:
4946              case XK_Return:
4947 	       key = keysym = XK_section;
4948 	       goto general;
4949 	       break;
4950 	     default:
4951 	       processStringEntry(keysym, &urban_entry[i]);
4952                setupUrban(0);
4953 	       return;
4954 	   }
4955         }
4956 
4957      general:
4958         switch(key) {
4959            case XK_Escape:
4960              if (do_menu) PopMenu(Context);
4961              return;
4962 	   case XK_percent:
4963 	     if (win==Option && do_option && text_input!=OPTION_INPUT) {
4964 	        option_entry.oldcaret = 0;
4965 	        option_entry.oldlength = strlen(option_entry.string);
4966 	        option_entry.oldchar = *option_entry.string;
4967 	        *option_entry.string = '\0';
4968 	        option_entry.caret = 0;
4969                 setupOption(0);
4970 	     }
4971 	     if (win==Urban && do_urban && text_input<URBAN_INPUT) {
4972 	        for (i=0; i<=4; i++) {
4973 	           urban_entry[i].oldcaret = 0;
4974 	           urban_entry[i].oldlength = strlen(urban_entry[i].string);
4975 	           urban_entry[i].oldchar = *urban_entry[i].string;
4976 	           *urban_entry[i].string = '\0';
4977 	           urban_entry[i].caret = 0;
4978 		}
4979                 setupUrban(0);
4980 		goto erasemarks;
4981 	     }
4982 	     break;
4983            case XK_degree:
4984 	     erase_obj = 1;
4985              if (Context->flags.objectmode == 2) drawSunAndMoon(Context);
4986              if (Context->flags.colorlevel != MANYCOLORS) drawCities(Context);
4987              Context->flags.dms = 1 -Context->flags.dms;
4988 	     erase_obj = 0;
4989              if (Context->flags.objectmode == 2) drawSunAndMoon(Context);
4990              if (Context->flags.colorlevel == MONOCHROME) drawCities(Context);
4991              if (do_urban) {
4992 	        if (Context->mark1.city)
4993                    updateUrban(Context, Context->mark1.city);
4994                 else {
4995 		   for (i=2; i<=3; i++)
4996                       (void) num2str(dms2decim(urban_entry[i].string),
4997 		         urban_entry[i].string, Context->flags.dms);
4998                    setupUrban(0);
4999 		}
5000 	     }
5001 	     Context->flags.update = 2;
5002              return;
5003 	   case XK_section:
5004 	     if (do_urban) {
5005 	        char params[256];
5006 		sprintf(params, "%s\037|%s\037|%s\037",
5007                     urban_entry[0].string,
5008                     urban_entry[2].string,
5009                     urban_entry[3].string);
5010 	        if (!markLocation(Context, params))
5011                    (void) markLocation(Context, urban_entry[0].string);
5012                 updateUrban(Context, Context->mark1.city);
5013 		Context->flags.update = 2;
5014 	     }
5015 	     break;
5016 	   case XK_asciitilde:
5017 	   case XK_parenright:
5018 	     if (win == Urban && Context->mark1.city &&
5019                     Context->mark1.city != &Context->pos1) {
5020                 City *c = Context->mark1.city;
5021 		if (c) {
5022 		   erase_obj = 1;
5023 		   drawObject(Context, c->lon, c->lat, c->size, 1, c->name);
5024 		   erase_obj = 0;
5025 		} else return;
5026 	        deleteMarkedCity();
5027 	        Context->flags.update = 2;
5028 	     }
5029 	     if (keysym==XK_parenright)
5030 	        break;
5031 	   case XK_parenleft:
5032 	     if (win == Urban) {
5033 	        City * c = addCity(NULL);
5034 		if (c) {
5035 	           if (Context->mark1.city) {
5036 		      if (Context->mark1.city == &Context->pos1) {
5037 			 erase_obj = 1;
5038                          drawMarks(Context);
5039                          erase_obj = 0;
5040 		      }
5041 		   }
5042                    Context->mark1.city = c;
5043 		   if (Context->flags.colorlevel==MONOCHROME) {
5044 		      drawObject(Context,
5045                          c->lon, c->lat, c->size, 1, c->name);
5046                       Context->mark1.flags = -1;
5047 		   }
5048 	           Context->flags.update = 2;
5049 		} else
5050 		   setupUrban(0);
5051 	     }
5052 	     break;
5053            case XK_less:
5054              if (Context->prevgeom.width &&
5055                  (Context->prevgeom.width != Context->geom.width ||
5056                   Context->prevgeom.height != Context->geom.height)) {
5057                 Context->geom = Context->prevgeom;
5058                 Context->prevgeom.width = 0;
5059                 adjustGeom(Context, 0);
5060 		XResizeWindow(dpy, Context->win,
5061 		      Context->geom.width,
5062 		      Context->geom.height+Context->gdata->menustrip);
5063                 warningNew(Context);
5064                 shutDown(Context, 0);
5065                 buildMap(Context, Context->wintype, 0);
5066              }
5067              break;
5068            case XK_Home:
5069              label_shift = 0;
5070              return;
5071            case XK_End:
5072              label_shift = 50;
5073              clearStrip(Context);
5074              return;
5075            case XK_Page_Up:
5076              if (label_shift>0)
5077                --label_shift;
5078              return;
5079            case XK_Page_Down:
5080              if (label_shift<50)
5081                ++label_shift;
5082              return;
5083            case XK_equal:
5084 	     if (do_sync & 1)
5085                 do_sync = do_sync & 2;
5086 	     else
5087                 do_sync |= 1;
5088 	     menu_lasthint = '\0';
5089 	     option_lasthint = '\0';
5090 	     option_newhint = keysym;
5091 	     showOptionHint(getNumCmd(key));
5092              break;
5093            case XK_Delete:
5094 	   case XK_BackSpace:
5095 	   case XK_guillemotleft:
5096 	     if (!memcmp(&Context->newzoom, &Context->oldzoom,
5097                          sizeof(ZoomSettings))) return;
5098 	     Context->newzoom = Context->oldzoom;
5099 	     setZoomDimension(Context);
5100              zoom_mode |= 15;
5101              activateZoom(Context, zoom_active);
5102              return;
5103            case XK_Left:
5104              v = 0.5/Context->newzoom.fx;
5105              Context->newzoom.fdx -= v;
5106              if (Context->newzoom.fdx<v) Context->newzoom.fdx = v;
5107              zoom_mode |= 14;
5108              activateZoom(Context, zoom_active);
5109              return;
5110            case XK_Right:
5111              v = 0.5/Context->newzoom.fx;
5112              Context->newzoom.fdx += v;
5113              if (Context->newzoom.fdx>1.0-v) Context->newzoom.fdx = 1.0-v;
5114              zoom_mode |= 14;
5115              activateZoom(Context, zoom_active);
5116              return;
5117            case XK_Up:
5118              v = 0.5/Context->newzoom.fy;
5119              Context->newzoom.fdy -= v;
5120              if (Context->newzoom.fdy<v) Context->newzoom.fdy = v;
5121              zoom_mode |= 14;
5122              activateZoom(Context, zoom_active);
5123              return;
5124            case XK_Down:
5125              v = 0.5/Context->newzoom.fy;
5126              Context->newzoom.fdy += v;
5127              if (Context->newzoom.fdy>1.0-v) Context->newzoom.fdy = 1.0-v;
5128              zoom_mode |= 14;
5129              activateZoom(Context, zoom_active);
5130              return;
5131            case XK_greater:
5132              if (do_dock && Context==Seed) break;
5133              Context->prevgeom = Context->geom;
5134 	     i = DisplayWidth(dpy, scr);
5135              Context->geom.width = i - extra_width;
5136 	     if (Context->geom.width<i/2) Context->geom.width = i/2;
5137 	     if (Context->geom.width>i) Context->geom.width = i;
5138            case XK_KP_Divide:
5139              if (key == XK_KP_Divide) key = XK_slash;
5140            case XK_colon:
5141              if (key == XK_colon) key = XK_slash;
5142            case XK_slash:
5143              if (do_dock && Context==Seed) break;
5144              if (key == XK_slash) {
5145                 Context->prevgeom = Context->geom;
5146                 Context->zoom.mode = 2;
5147                 Context->newzoom.mode = Context->zoom.mode;
5148              }
5149              if (!do_zoom)
5150                 Context->newzoom = Context->zoom;
5151              if (setWindowAspect(Context, &Context->zoom)) {
5152                 if (key == XK_greater || key == XK_slash) {
5153                    adjustGeom(Context, 0);
5154 		   XResizeWindow(dpy, Context->win,
5155 		      Context->geom.width,
5156 		      Context->geom.height+Context->gdata->menustrip);
5157                    Context->geom.x = extra_width/2;
5158                    XMoveWindow(dpy, Context->win,
5159                       Context->geom.x, Context->geom.y);
5160                 }
5161                 warningNew(Context);
5162                 shutDown(Context, 0);
5163                 buildMap(Context, Context->wintype, 0);
5164                 MapGeom = Context->geom;
5165              }
5166              break;
5167            case XK_apostrophe:
5168 	     menu_lasthint = ' ';
5169 	     option_lasthint = ' ';
5170 	     Context->flags.animate = 1 - Context->flags.animate;
5171 	     if (Context->flags.animate == 0)
5172 	         Context->jump -= progress_value[Context->flags.progress];
5173 	     Context->flags.update = 4;
5174 	     break;
5175            case XK_quotedbl:
5176              if (do_zoom) zoom_active = 1 - zoom_active;
5177              zoom_mode = 30;
5178              activateZoom(Context, zoom_active);
5179              break;
5180            case XK_KP_Multiply:
5181            case XK_asterisk:
5182 	     key = XK_asterisk;
5183              if (!memcmp(&Context->newzoom,
5184                          &Context->zoom, sizeof(ZoomSettings))) break;
5185              activateZoom(Context, 1);
5186              break;
5187            case XK_period:
5188              if (Context->mark1.city) {
5189                 Context->newzoom.fdx = 0.5+Context->mark1.city->lon/360.0;
5190                 Context->newzoom.fdy = 0.5-Context->mark1.city->lat/180.0;
5191                 zoom_mode |= 14;
5192                 zoom_lasthint = ' ';
5193                 activateZoom(Context, zoom_active);
5194              }
5195              break;
5196            case XK_at:
5197                 activateOption();
5198 		return;
5199            case XK_space:
5200            case XK_exclam:
5201 	     key = XK_exclam;
5202              menu_newhint = XK_exclam;
5203              if (Context==Seed && do_dock) return;
5204              Context->wintype = 1 - Context->wintype;
5205              if (Context->wintype) {
5206                 Context->geom.width = MapGeom.width;
5207                 Context->geom.height = MapGeom.height;
5208              } else {
5209                 Context->geom.width = ClockGeom.width;
5210                 Context->geom.height = ClockGeom.height;
5211 	     }
5212              adjustGeom(Context, 1);
5213      	     XSelectInput(dpy, Context->win, 0);
5214              setSizeHints(Context, Context->wintype);
5215              setClassHints(Context->win, Context->wintype);
5216              XMoveResizeWindow(dpy, Context->win,
5217                  Context->geom.x, Context->geom.y,
5218                  Context->geom.width,
5219                  Context->geom.height+((Context->wintype)?
5220                      Context->gdata->mapstrip:Context->gdata->clockstrip));
5221              warningNew(Context);
5222              shutDown(Context, 0);
5223              buildMap(Context, Context->wintype, 0);
5224              return;
5225            case XK_1:
5226            case XK_KP_1:
5227 	     key = XK_1;
5228              if (memcmp(&Context->newzoom,
5229                         &gzoom, sizeof(ZoomSettings))) {
5230                 Context->newzoom = gzoom;
5231                 zoom_mode |= 15;
5232                 activateZoom(Context, zoom_active);
5233              }
5234              break;
5235            case XK_numbersign:
5236              if (memcmp(&Context->newzoom,
5237                         &Context->zoom, sizeof(ZoomSettings))) {
5238                 Context->newzoom = Context->zoom;
5239                 zoom_mode |= 15;
5240                 activateZoom(Context, zoom_active);
5241              }
5242              break;
5243            case XK_plus:
5244            case XK_KP_Add:
5245 	     key = XK_plus;
5246              Context->newzoom.fx *= ZFACT;
5247              Context->newzoom.fy *= ZFACT;
5248              setZoomDimension(Context);
5249              zoom_mode |= 14;
5250              activateZoom(Context, zoom_active);
5251              break;
5252            case XK_minus:
5253            case XK_KP_Subtract:
5254 	     key = XK_minus;
5255              Context->newzoom.fx /= ZFACT;
5256              Context->newzoom.fy /= ZFACT;
5257              setZoomDimension(Context);
5258              zoom_mode |= 14;
5259              activateZoom(Context, zoom_active);
5260              break;
5261            case XK_bracketright:
5262 	     menu_lasthint = ' ';
5263 	     option_lasthint = ' ';
5264 	     drawImageToRootWindow(Context, -1);
5265 	     break;
5266            case XK_bracketleft:
5267 	     menu_lasthint = ' ';
5268 	     option_lasthint = ' ';
5269 	     drawImageToRootWindow(Context, 1);
5270 	     break;
5271            case XK_ampersand:
5272              Context->newzoom.mode = (Context->newzoom.mode+1) %3;
5273              setZoomDimension(Context);
5274              zoom_mode |= 13;
5275              activateZoom(Context, zoom_active);
5276              break;
5277            case XK_a:
5278              Context->jump += progress_value[Context->flags.progress];
5279              Context->flags.update = 4;
5280              menu_lasthint = ' ';
5281              break;
5282            case XK_b:
5283              Context->jump -= progress_value[Context->flags.progress];
5284              Context->flags.update = 4;
5285              menu_lasthint = ' ';
5286              break;
5287            case XK_c:
5288              if (!Context->wintype) break;
5289              if (Context->flags.map_mode != COORDINATES)
5290                Context->flags.dms = gflags.dms;
5291              else
5292                Context->flags.dms = 1 - Context->flags.dms;
5293              Context->flags.map_mode = COORDINATES;
5294              if (Context->mark1.city == &Context->pos1) {
5295 		if (Context->flags.colorlevel==FULLCOLORS) {
5296 		   erase_obj = 1;
5297 		   drawMarks(Context);
5298                    erase_obj = 0;
5299 		}
5300                 Context->mark1.city = NULL;
5301 	     }
5302              if (Context->mark1.city)
5303                setDayParams(Context);
5304              if (Context->mark2.city) {
5305 		if (Context->flags.colorlevel==FULLCOLORS) {
5306 		   erase_obj = 2;
5307 		   drawMarks(Context);
5308                    erase_obj = 0;
5309 		}
5310 	     }
5311              Context->mark2.city = NULL;
5312              Context->flags.update = 2;
5313              break;
5314            case XK_d:
5315              if (!Context->wintype) break;
5316              if (Context->flags.map_mode != DISTANCES)
5317                Context->flags.dms = gflags.dms;
5318              else
5319                Context->flags.dms = 1 - Context->flags.dms;
5320              Context->flags.map_mode = DISTANCES;
5321              break;
5322            case XK_e:
5323              if (!Context->wintype) break;
5324              Context->flags.map_mode = EXTENSION;
5325              old_mode = EXTENSION;
5326              Context->flags.hours_shown = 0;
5327              showHours(Context);
5328              break;
5329            case XK_f:
5330              if (!do_filesel)
5331                 PopFilesel(Context);
5332              else {
5333 		if (Filesel) {
5334                    XMapWindow(dpy, Filesel);
5335                    XMapRaised(dpy, Filesel);
5336 		}
5337                 if (FileselCaller != Context) {
5338                    PopFilesel(Context);
5339                    PopFilesel(Context);
5340                 }
5341              }
5342              break;
5343            case XK_g:
5344              if (!do_menu && win != Option)
5345                PopMenu(Context);
5346              else {
5347                menu_lasthint = ' ';
5348 	       option_lasthint = ' ';
5349                if (keysym==XK_g) {
5350                   Context->flags.progress = (Context->flags.progress+1) % 6;
5351                   if (!progress_value[Context->flags.progress])
5352                      Context->flags.progress = 0;
5353                }
5354                if (keysym==XK_G) {
5355                   Context->flags.progress = (Context->flags.progress+5) % 6;
5356                   if (!progress_value[Context->flags.progress])
5357                      Context->flags.progress = 4;
5358                }
5359              }
5360              break;
5361            case XK_h:
5362              if (!do_menu) {
5363                 menu_newhint = XK_space;
5364                 PopMenu(Context);
5365 		return;
5366              } else {
5367                 if (MenuCaller != Context) {
5368                    PopMenu(Context);
5369                    PopMenu(Context);
5370                 } else {
5371 		   if (getState(Menu) == IsViewable) {
5372                       XMapRaised(dpy, Menu);
5373                       showManual();
5374 		   } else
5375                       XMapWindow(dpy, Menu);
5376 		}
5377              }
5378              break;
5379            case XK_i:
5380              setAuxilWins(Context, ICONIFY);
5381              XIconifyWindow(dpy, Context->win, scr);
5382   	     Context->flags.mapped = 0;
5383              break;
5384            case XK_j:
5385              Context->jump = 0;
5386              Context->flags.update = 4;
5387              menu_lasthint = ' ';
5388 	     option_lasthint = ' ';
5389              break;
5390            case XK_k:
5391 	     if (Context==Seed && do_dock) return;
5392              if (do_menu) PopMenu(Context);
5393              if (do_filesel) PopFilesel(Context);
5394              if (Context==Seed && Seed->next==NULL)
5395                 shutDown(Context, -1);
5396              else
5397                 shutDown(Context, 1);
5398              return;
5399            case XK_l:
5400              if (!Context->wintype) {
5401                 clearStrip(Context);
5402                 if (!Context->wintype)
5403                    Context->flags.clock_mode =
5404                      (Context->flags.clock_mode+1) % num_formats;
5405                 Context->flags.update = 1;
5406                 break;
5407              }
5408              Context->flags.map_mode = LEGALTIME;
5409 	erasemarks:
5410 	     if (Context->flags.colorlevel==FULLCOLORS) {
5411 	        erase_obj = 3;
5412 	        drawMarks(Context);
5413                 erase_obj = 0;
5414 	     }
5415              Context->mark1.city = NULL;
5416              Context->mark2.city = NULL;
5417              Context->flags.update = 2;
5418              break;
5419            case XK_m:
5420              if (!Context->wintype) break;
5421              if (Context->flags.colorlevel!=MANYCOLORS) {
5422 	        erase_obj = 1;
5423 	        drawLines(Context);
5424                 erase_obj = 0;
5425 	     }
5426 	     if (keysym == XK_M)
5427                 Context->flags.meridian = (Context->flags.meridian + 3) % 4;
5428 	     else
5429                 Context->flags.meridian = (Context->flags.meridian + 1) % 4;
5430              if (Context->flags.colorlevel<=FEWCOLORS) drawLines(Context);
5431              Context->flags.update = 2;
5432              break;
5433            case XK_n:
5434              if ((Context->flags.colorlevel<FULLCOLORS) ||
5435                  (Context->flags.colorscale == 1))
5436 		Context->flags.shading = 1 - Context->flags.shading;
5437 	     else {
5438                 if (keysym==XK_n)
5439                    Context->flags.shading = (Context->flags.shading + 1) % 6;
5440                 if (keysym==XK_N)
5441                    Context->flags.shading = (Context->flags.shading + 5) % 6;
5442              }
5443              drawShadedArea(Context);
5444              Context->flags.update = 4;
5445              break;
5446            case XK_o:
5447              if (!do_option)
5448                 PopOption(Context);
5449              else {
5450                 XMapWindow(dpy, Option);
5451                 XMapRaised(dpy, Option);
5452                 if (OptionCaller != Context) {
5453                    PopOption(Context);
5454                    PopOption(Context);
5455                 }
5456              }
5457              break;
5458            case XK_p:
5459              if (!Context->wintype) break;
5460              if (Context->flags.colorlevel!=MANYCOLORS) {
5461 	        erase_obj = 1;
5462 	        drawLines(Context);
5463                 erase_obj = 0;
5464 	     }
5465 	     i = Context->flags.parallel;
5466              if (keysym == XK_P)
5467                 Context->flags.parallel = ((i+3)&3) + (i&8);
5468 	     else
5469                 Context->flags.parallel = ((i+1)&3) + (i&8);
5470              if (Context->flags.colorlevel<=FEWCOLORS) drawLines(Context);
5471              Context->flags.update = 2;
5472              break;
5473            case XK_q:
5474 	     if (!do_dock)
5475                 shutDown(Context, -1);
5476              break;
5477            case XK_s:
5478              if (Context->flags.map_mode != SOLARTIME)
5479                Context->flags.dms = gflags.dms;
5480              else
5481                Context->flags.dms = 1 - Context->flags.dms;
5482              Context->flags.map_mode = SOLARTIME;
5483              if (Context->mark2.city) {
5484 		if (Context->flags.colorlevel==FULLCOLORS) {
5485 		   erase_obj = 2;
5486 		   drawMarks(Context);
5487                    erase_obj = 0;
5488 		}
5489 	     }
5490              Context->mark2.city = NULL;
5491              if (Context->mark1.city)
5492                setDayParams(Context);
5493              Context->flags.update = 2;
5494              break;
5495            case XK_t:
5496              if (!Context->wintype) break;
5497              if (Context->flags.colorlevel!=MANYCOLORS) {
5498 	        erase_obj = 1;
5499 	        drawLines(Context);
5500                 erase_obj = 0;
5501 	     }
5502              Context->flags.parallel = (Context->flags.parallel + 8) & 15;
5503              if (Context->flags.colorlevel<=FEWCOLORS) drawLines(Context);
5504              Context->flags.update = 2;
5505              break;
5506            case XK_u:
5507              if (!Context->wintype) break;
5508 	     if (!do_urban) {
5509 	        PopUrban(Context);
5510 	        updateUrban(Context, Context->mark1.city);
5511 		break;
5512 	     } else {
5513 	        if (getState(Urban)!=IsViewable) {
5514                    XMapWindow(dpy, Urban);
5515 	           XMapRaised(dpy, Urban);
5516 	           updateUrban(Context, Context->mark1.city);
5517 		   break;
5518                 } else
5519 	           XMapRaised(dpy, Urban);
5520 	     }
5521              if (Context->flags.colorlevel!=MANYCOLORS) {
5522 	        erase_obj = 1;
5523 	        drawCities(Context);
5524                 erase_obj = 0;
5525 	     }
5526              if (keysym == XK_U)
5527                 Context->flags.citymode = (Context->flags.citymode + 3) % 4;
5528 	     else
5529                 Context->flags.citymode = (Context->flags.citymode + 1) % 4;
5530              if (Context->flags.colorlevel==MONOCHROME) drawCities(Context);
5531              Context->flags.update = 2;
5532              break;
5533            case XK_w:
5534              if (Context->footime<=last_time+2) return;
5535              if (do_menu) do_menu = -1;
5536              if (do_filesel) do_filesel = -1;
5537              if (do_zoom) do_zoom = -1;
5538              if (do_option) do_option = -1;
5539              buildMap(Context, 1, 1);
5540 	     keysym = ' ';
5541              break;
5542            case XK_r:
5543              clearStrip(Context);
5544              Context->flags.update = 4;
5545              break;
5546            case XK_x:
5547              if (ExternAction)
5548                 system(ExternAction);
5549              break;
5550            case XK_y:
5551 	     erase_obj = 1;
5552              drawSunAndMoon(Context);
5553              erase_obj = 0;
5554              if (keysym==XK_y)
5555                 Context->flags.objectmode = (Context->flags.objectmode+1) % 3;
5556              if (keysym==XK_Y)
5557                 Context->flags.objectmode = (Context->flags.objectmode+2) % 3;
5558              drawSunAndMoon(Context);
5559              Context->flags.update = 2;
5560              break;
5561            case XK_z:
5562              if (!do_zoom)
5563                 PopZoom(Context);
5564              else {
5565                 XMapWindow(dpy, Zoom);
5566                 XMapRaised(dpy, Zoom);
5567                 if (ZoomCaller != Context) {
5568                    PopZoom(Context);
5569                    PopZoom(Context);
5570                 }
5571              }
5572              break;
5573            default:
5574              if (!Context->wintype) {
5575                Context->flags.clock_mode =
5576                    (1+Context->flags.clock_mode) % num_formats;
5577                Context->flags.update = 1;
5578              }
5579              break ;
5580         }
5581 
5582         if (old_mode == EXTENSION && Context->flags.map_mode != old_mode)
5583            clearStrip(Context);
5584 
5585         if (do_menu) {
5586 	   if (getNumCmd(toupper(key))>=0)
5587 	      menu_newhint = toupper(key);
5588 	   j = -1;
5589 	   for (i=0; i<N_MENU; i++) if (MenuKey[2*i]==toupper(key)) {
5590 	      j=i;
5591 	      break;
5592 	   }
5593            showMenuHint(j);
5594 	}
5595 
5596         if (do_zoom) {
5597 	   if (getNumCmd(toupper(key))>=0)
5598 	      zoom_newhint = toupper(key);
5599 	   j = -1;
5600 	   for (i=0; i<N_ZOOM; i++) if (ZoomKey[2*i]==toupper(key)) {
5601 	      j=i;
5602 	      break;
5603 	   }
5604            showZoomHint(j);
5605 	}
5606 
5607         if (do_option) {
5608 	   if (getNumCmd(toupper(key))>=0)
5609 	      option_newhint = toupper(key);
5610 	   j = -1;
5611 	   for (i=0; i<N_OPTION; i++) if (OptionKey[2*i]==toupper(key)) {
5612 	      j=i;
5613 	      break;
5614 	   }
5615            showOptionHint(j);
5616 	}
5617 }
5618 
5619 /*
5620  *  Process mouse events from eventLoop
5621  */
5622 
5623 void
processMouseEvent(win,x,y,button,evtype)5624 processMouseEvent(win, x, y, button, evtype)
5625 Window  win;
5626 int     x, y;
5627 int     button;
5628 int     evtype;
5629 {
5630 static int  x0 = -1, y0 = -1, pressed3 = 0;
5631 static int u, v, w = -1, h = -1;
5632 static Pixmap savepix = 0;
5633 struct Sundata * Context = (struct Sundata *) NULL;
5634 
5635         Context = getContext(win);
5636         if (!Context) return;
5637         if (Context->flags.colorlevel==FULLCOLORS) {
5638            if (!Context->xim) return;
5639         } else {
5640            if (!Context->mappix) return;
5641         }
5642 
5643         if (evtype!=MotionNotify) RaiseAndFocus(win);
5644 
5645         if (evtype == ButtonPress) {
5646 	   if (win == Context->win && !Context->wintype) return;
5647 	}
5648 
5649         if (win == Menu) {
5650 	   processMenuAction(MenuCaller, x, y, button, evtype);
5651 	   return;
5652         }
5653 
5654         if (win == Filesel) {
5655            processFileselAction(FileselCaller, x, y, evtype);
5656            return;
5657         }
5658 
5659         if (win == Zoom) {
5660            processZoomAction(ZoomCaller, x, y, button, evtype);
5661            return;
5662         }
5663 
5664         if (win == Option) {
5665            processOptionAction(OptionCaller, x, y, button, evtype);
5666            return;
5667         }
5668 
5669         if (win == Urban) {
5670            processUrbanAction(UrbanCaller, x, y, button, evtype);
5671            return;
5672         }
5673 
5674         /* Click on bottom strip of window */
5675         if (y >= Context->geom.height) {
5676            if (button==1) {
5677 	      if (evtype==ButtonPress) return;
5678 	      if (do_menu && getState(Menu)==IsViewable)
5679                  processKey(win, XK_o);
5680 	      else
5681                  processKey(win, XK_h);
5682               return;
5683            }
5684            if (button==2) {
5685               processKey(win, XK_l);
5686               return;
5687            }
5688            if (button==3) {
5689               /* Open new window */
5690               if (evtype==ButtonPress || !focus_in) return;
5691 	      if (pressed3) {
5692 		 y = Context->geom.height-1;
5693 		 goto rect;
5694 	      }
5695               processKey(win, XK_w);
5696               return;
5697            }
5698         }
5699 
5700         /* Click on the map with button 2*/
5701         if (button==2) {
5702            processKey(win, XK_f);
5703            return;
5704         }
5705 
5706    	if (!Context->wintype) pressed3 = 0;
5707 
5708         if (evtype == MotionNotify && pressed3 && !do_zoom &&
5709             x0!=-1 && y0!= -1) {
5710 	   if (w>0 && h>0) {
5711 	      if (savepix) {
5712                  XCopyArea(dpy, savepix, Context->win, Context->gdata->wingc,
5713                         0, 0, w+1, h+1, u, v);
5714 		 XFreePixmap(dpy, savepix);
5715 		 savepix = 0;
5716 	      }
5717 	   }
5718 	   if (x0<x) { u = x0; w = x-x0; } else { u = x; w = x0-x; }
5719 	   if (y0<y) { v = y0; h = y-y0; } else { v = y; h = y0-y; }
5720 	   if (w>0 && h>0)
5721 	   savepix = XCreatePixmap(dpy, Root, w+1, h+1, DefaultDepth(dpy,scr));
5722 	   if (savepix)
5723               XCopyArea(dpy, Context->win, savepix, Context->gdata->wingc,
5724                         u, v, w+1, h+1, 0, 0);
5725            XDrawRectangle(dpy, Context->win, Context->gdata->wingc,
5726               u, v, w, h);
5727            Context->flags.update = 4;
5728 	   return;
5729         }
5730 
5731         /* Click on the map with button 3*/
5732         if (button==3) {
5733 	   if (!Context->wintype) {
5734 	      if (evtype == ButtonRelease)
5735                  processKey(win, XK_z);
5736 	      return;
5737 	   }
5738            if (do_zoom && win==ZoomCaller->win) {
5739               Context->newzoom.fdx = ((double)(x+Context->zoom.dx))
5740                          /((double)Context->zoom.width);
5741               Context->newzoom.fdy = ((double)(y+Context->zoom.dy))
5742                          /((double)Context->zoom.height);
5743               setZoomAspect(Context, 3);
5744               setZoomDimension(Context);
5745               zoom_mode = 14;
5746               zoom_lasthint = ' ';
5747               activateZoom(Context, zoom_active);
5748 	   } else {
5749 	      if (evtype == ButtonPress) {
5750 	         pressed3 = 1;
5751 	         x0 = x;
5752 	         y0 = y;
5753 		 return;
5754 	      }
5755 	      if (x0<x) { u = x0; w = x-x0; } else { u = x; w = x0-x; }
5756 	      if (y0<y) { v = y0; h = y-y0; } else { v = y; h = y0-y; }
5757 	      if (evtype == ButtonRelease) {
5758 		 double fact;
5759 	         pressed3 = 0;
5760 		 if (savepix) {
5761 		    XFreePixmap(dpy, savepix);
5762 		    savepix = 0;
5763 		 }
5764 	         if (x==x0 && y==y0) {
5765                     /* Open zoom filesel */
5766                     processKey(win, XK_z);
5767 		    return;
5768 		 }
5769 	       rect:
5770                  Context->newzoom.fdx +=
5771                     (((double)(u+w/2)/(double)Context->geom.width)-0.5)/
5772                        Context->newzoom.fx;
5773                  Context->newzoom.fdy +=
5774                     (((double)(v+h/2)/(double)Context->geom.height)-0.5)/
5775                        Context->newzoom.fy;
5776                  fact = sqrt( ((double)Context->geom.width)/((double)w) *
5777                           ((double)Context->geom.height)/((double)h) );
5778                  Context->newzoom.fx *= fact;
5779                  Context->newzoom.fy *= fact;
5780                  setZoomDimension(Context);
5781                  zoom_mode |= 14;
5782                  activateZoom(Context, zoom_active);
5783 	         x0 = -1;
5784 	         y0 = -1;
5785 		 return;
5786 	      }
5787 	   }
5788            return;
5789         }
5790 
5791         if (evtype == MotionNotify) return;
5792         /* Click with button 1 on the map */
5793 
5794         /* It's a clock, just execute predefined command */
5795         if (!Context->wintype) {
5796            if (ExternAction)
5797               system(ExternAction);
5798 	   else
5799 	   if (!do_menu) {
5800 	      menu_lasthint = '\0';
5801 	      menu_newhint = ' ';
5802 	      PopMenu(Context);
5803 	   }
5804            return;
5805         }
5806 
5807         /* Otherwise, user wants to get info on a city or a location */
5808         Context->flags.update = 1;
5809 
5810         /* Set the timezone, marks, etc, on a button press */
5811         if (evtype==ButtonPress) return;
5812         processPoint(Context, x, y);
5813 }
5814 
5815 void
processResize(win)5816 processResize(win)
5817 Window win;
5818 {
5819            int i, x, y, w, h, num = 0;
5820            struct Sundata * Context = NULL;
5821            struct Geometry * Geom = NULL;
5822 
5823            if (win == Menu) return;
5824 
5825            if (win == Filesel) {
5826 	      if (!do_filesel) return;
5827               Geom = &FileselGeom;
5828               num = 3;
5829            }
5830            if (win == Zoom) {
5831 	      if (!do_zoom) return;
5832               Geom = &ZoomGeom;
5833               num = 4;
5834            }
5835            if (win == Option) {
5836 	      if (!do_option) return;
5837               Geom = &OptionGeom;
5838               num = 5;
5839            }
5840            if (win == Urban) {
5841 	      if (!do_urban) return;
5842               Geom = &UrbanGeom;
5843               num = 6;
5844            }
5845 
5846            if (num) {
5847               if (getPlacement(win, &x, &y, &w, &h)) return;
5848               if (w==Geom->width && h==Geom->height) return;
5849               if (w<Geom->w_mini) w = Geom->w_mini;
5850               if (h<Geom->h_mini) h = Geom->h_mini;
5851               Geom->width = w;
5852               Geom->height = h;
5853               if (verbose)
5854                  fprintf(stderr, "Resizing %s to %d %d\n",
5855                     widget_type[num], w, h);
5856               XSelectInput(dpy, win, 0);
5857               setSizeHints(NULL, num);
5858               setProtocols(NULL, num);
5859               if (num==3)
5860                  setupFilesel(-1);
5861               if (num==4) {
5862                  if (zoompix) {
5863                     XFreePixmap(dpy, zoompix);
5864                     zoompix = 0;
5865                  }
5866                  setupZoom(-1);
5867               }
5868               if (num==5) {
5869    	         w = ((OptionGeom.width-86) /
5870                    XTextWidth(OptionCaller->gdata->font[MENUFONT], "_", 1))-2;
5871 		 resetStringLength(w, &option_entry);
5872                  setupOption(-1);
5873               }
5874               if (num==6) {
5875 		 text_input = NULL_INPUT;
5876 		 setupUrban(-2);
5877                  for (i=0; i<=4; i++) {
5878 	            w = (urban_w[i]/
5879                       XTextWidth(UrbanCaller->gdata->font[MENUFONT],"_",1))-2;
5880 	            resetStringLength(w, &urban_entry[i]);
5881 		    urban_entry[i].string[urban_entry[i].maxlength] = '\0';
5882 		 }
5883 		 setupUrban(-1);
5884 	      }
5885               return;
5886            }
5887 
5888            Context = getContext(win);
5889            if(!Context) return;
5890 
5891            if (Context==Seed && !Context->wintype && do_dock) return;
5892 
5893            if (getPlacement(win, &x, &y, &w, &h)) return;
5894            h -= Context->hstrip;
5895            if (w==Context->geom.width && h==Context->geom.height) return;
5896            Context->prevgeom = Context->geom;
5897            if (w<Context->geom.w_mini) w = Context->geom.w_mini;
5898            if (h<Context->geom.h_mini) h = Context->geom.h_mini;
5899 	   Context->flags.update=2;
5900 	   showMapImage(Context);
5901 	   clearStrip(Context);
5902 	   writeStrip(Context);
5903 	   XFlush(dpy);
5904            Context->geom.width = w;
5905            Context->geom.height = h;
5906            if (Context->wintype) {
5907               MapGeom.width = w;
5908               MapGeom.height = h;
5909            } else {
5910               ClockGeom.width = w;
5911               ClockGeom.height = h;
5912            }
5913            adjustGeom(Context, 0);
5914 	   warningNew(Context);
5915            shutDown(Context, 0);
5916            setZoomAspect(Context, 3);
5917            buildMap(Context, Context->wintype, 2);
5918            XFlush(dpy);
5919            usleep(2*TIMESTEP);
5920 }
5921 
5922 /*
5923  * Got an expose event for window w.  Do the right thing if it's not
5924  * currently the one we're displaying.
5925  */
5926 
5927 void
doTimeout(Context)5928 doTimeout(Context)
5929 struct Sundata * Context;
5930 {
5931         if (!Context) return;
5932 
5933         if (QLength(dpy) && Context->flags.update <= 2)
5934                 return;         /* ensure events processed first */
5935 
5936         if (Context->flags.update)
5937            Context->count = 0;
5938         else
5939            Context->count = (Context->count+1) % TIMECOUNT;
5940 
5941         if (Context->count==0) {
5942            updateImage(Context);
5943            showMapImage(Context);
5944            writeStrip(Context);
5945            if (Context->flags.colorlevel==MONOCHROME) pulseMarks(Context);
5946            if (do_root == 2 && Context == RootCaller)
5947               drawImageToRootWindow(Context, 0);
5948 	   XFlush(dpy);
5949 	   if (Context->flags.animate) {
5950 	      if (abs(Context->footime-Context->animtime) >=
5951                   Context->flags.animperiod) {
5952 		 Context->animtime = Context->footime;
5953 	         Context->jump += progress_value[Context->flags.progress];
5954                  Context->flags.update = 4;
5955 	      }
5956 	   }
5957         }
5958 }
5959 
5960 void
doExpose(w)5961 doExpose(w)
5962 Window w;
5963 {
5964         struct Sundata * Context;
5965 
5966         Context = getContext(w);
5967         if (!Context) return;
5968 
5969         if (w == Menu) { setupMenu(-1); return; }
5970         if (w == Filesel) { setupFilesel(-1); return; }
5971         if (w == Zoom) { setupZoom(-1); return; }
5972         if (w == Option) { setupOption(-1); return; }
5973         if (w == Urban) { setupUrban(-1); return; }
5974 
5975         Context->flags.update = 2;
5976         showMapImage(Context);
5977 
5978 	Context->flags.bottom &= 1;
5979 	drawBottomline(Context);
5980 
5981 	clearStrip(Context);
5982 	Context->flags.hours_shown = 0;
5983         writeStrip(Context);
5984 }
5985 
5986 /*
5987  * Someone is sure to wonder why the event loop is coded this way, without
5988  * using select().  The answer is that this was developed on a System V
5989  * kernel, which has select() but the call has bugs; so, I was inspired
5990  * to make it portable to systems without select().  The slight delay in
5991  * expose event processing that results from using sleep(1) rather than
5992  * alarm() is a fine payoff for not having to worry about interrupted
5993  * system calls.
5994  *
5995  * I've got to use XCheckIfEvent with a degenerate predicate rather than
5996  * XCheckMaskEvent with a mask of -1L because the latter won't collect all
5997  * types of events, notably ClientMessage and Selection events.  Sigh.
5998  */
5999 
6000 Bool
evpred(d,e,a)6001 evpred(d, e, a)
6002 Display *              d;
6003 XEvent *               e;
6004 XPointer               a;
6005 {
6006         return (True);
6007 }
6008 
6009 void
eventLoop()6010 eventLoop()
6011 {
6012         XEvent                  ev;
6013         Sundata *               Context;
6014 	Sundata *               Which;
6015         char                    buffer[1];
6016         KeySym                  keysym;
6017 
6018         for (;;) {
6019 	   if (XCheckIfEvent(dpy, &ev, evpred, (XPointer)0)) {
6020 
6021 	      /*
6022               fprintf(stderr, "Event %d, Window %d \n"
6023                    "  (Main %d, Menu %d, Sel %d, Zoom %d, Option %d)\n",
6024                    ev.type, ev.xexpose.window,
6025                    Seed->win, Menu, Filesel, Zoom, Option);
6026 	      */
6027 
6028               switch(ev.type) {
6029 
6030                  case EnterNotify:
6031 		      focus_in = 1;
6032 		      break;
6033 
6034                  case LeaveNotify:
6035 		      focus_in = 0;
6036 		      break;
6037 
6038                  case FocusOut:
6039 		      if (do_option && text_input == OPTION_INPUT &&
6040                           ev.xexpose.window == Option) {
6041 			 text_input = NULL_INPUT;
6042 			 setupOption(0);
6043 		      }
6044 		      if (do_urban && text_input >= URBAN_INPUT &&
6045                           ev.xexpose.window == Urban) {
6046 			 text_input = NULL_INPUT;
6047 			 setupUrban(0);
6048 		      }
6049 		      break;
6050 
6051                  case VisibilityNotify:
6052                       doExpose(ev.xexpose.window);
6053                       break;
6054 
6055                  case MapNotify:
6056                       Context = getContext(ev.xexpose.window);
6057 		      if (!Context) break;
6058 		      if (Context->win!=ev.xexpose.window) break;
6059                       setAuxilWins(Context, DEICONIFY);
6060    	              Context->flags.mapped = 1;
6061                       updateImage(Context);
6062    	              Context->flags.update = 2;
6063 		      showMapImage(Context);
6064 		      writeStrip(Context);
6065 		      break;
6066 
6067                  case UnmapNotify:
6068                       Context = getContext(ev.xexpose.window);
6069 		      if (!Context) break;
6070 		      if (Context->win!=ev.xexpose.window) break;
6071                       setAuxilWins(Context, ICONIFY);
6072    	              Context->flags.mapped = 0;
6073 		      break;
6074 
6075                  case Expose:
6076                       if (ev.xexpose.count == 0)
6077                       doExpose(ev.xexpose.window);
6078                       break;
6079 
6080                  case ClientMessage:
6081                       if (ev.xclient.message_type == wm_protocols &&
6082                           ev.xclient.format == 32 &&
6083                           ev.xclient.data.l[0] == wm_delete_window) {
6084                             if (ev.xexpose.window == Menu) PopMenu(MenuCaller);
6085                                else
6086                             if (ev.xexpose.window == Filesel)
6087                                   PopFilesel(FileselCaller);
6088                                else
6089                             if (ev.xexpose.window == Zoom) PopZoom(ZoomCaller);
6090                                else
6091                             if (ev.xexpose.window == Option)
6092                                             PopOption(OptionCaller);
6093 			       else
6094                             if (ev.xexpose.window == Urban)
6095                                             PopUrban(UrbanCaller);
6096 			       else {
6097 			         Context = getContext(ev.xexpose.window);
6098 		                 if (!Context) break;
6099 				 if (Context!=Seed || !do_dock)
6100                                     shutDown(Context, 1);
6101 				 }
6102 		      }
6103                       break;
6104 
6105                  case KeyPress:
6106                  case KeyRelease:
6107                       XLookupString((XKeyEvent *) &ev, buffer, 1, &keysym, NULL);
6108                       if (keysym==XK_Control_L || keysym==XK_Control_R) {
6109                          if (ev.type == KeyPress) control_key = 1;
6110                          if (ev.type == KeyRelease) control_key = 0;
6111                       } else
6112                          if (ev.type == KeyPress && keysym != XK_Mode_switch)
6113                             processKey(ev.xexpose.window, keysym);
6114                       break;
6115 
6116                  case ButtonPress:
6117 		 case ButtonRelease:
6118                  case MotionNotify:
6119                       if (ev.type==ButtonPress)
6120                             button_pressed = ev.xbutton.button;
6121                       if (ev.type==ButtonRelease) button_pressed = 0;
6122                       processMouseEvent(ev.xexpose.window,
6123                             ev.xbutton.x, ev.xbutton.y,
6124                             ev.xbutton.button, ev.type);
6125                       break;
6126 
6127 		 /* case ResizeRequest: */
6128 		 case PropertyNotify:
6129 		 case ConfigureNotify:
6130                       processResize(ev.xexpose.window);
6131                       break;
6132 
6133                  default:
6134 		      break;
6135 	      }
6136 	   } else {
6137               Which = getContext(ev.xexpose.window);
6138               usleep(TIMESTEP);
6139               if (Which == NULL) Which = Seed;
6140 	      if (Which->win==ev.xexpose.window)
6141 	         processResize(Which->win);
6142               for (Context = Seed; Context; Context = Context->next)
6143                  if (do_sync || Context == Which || Context == RootCaller ||
6144                      (do_dock && Context == Seed)) {
6145 		    if (do_sync & 2) Context->flags.update = 2;
6146                     doTimeout(Context);
6147 		 }
6148 	      do_sync &= 1;
6149 	   }
6150         }
6151 }
6152 
6153 int
main(argc,argv)6154 main(argc, argv)
6155 int             argc;
6156 char **         argv;
6157 {
6158         char * p;
6159         int i;
6160 
6161         ProgName = *argv;
6162         if ((p = strrchr(ProgName, '/'))) ProgName = ++p;
6163 
6164         /* Set default values */
6165         initValues();
6166 
6167         /* Set Language */
6168         if (getenv("LANG")) {
6169            strncpy(language, getenv("LANG"), 2);
6170 	   language[2] = '\0';
6171 	}
6172         if (!*language)
6173            strcpy (language,"en");
6174 
6175         /* Check if options define some new language */
6176         for (i=1; i<argc-1; i++)
6177             if (!strcasecmp(argv[i], "-language")) {
6178 	       strncpy(language, argv[++i], 2);
6179 	       language[2] = '\0';
6180 	    }
6181 
6182         /* Read the app-default config file */
6183         runlevel = READSYSRC;
6184         if (readRC(app_default, 1)) exit(1);
6185 
6186         /* Check if user has provided another config file */
6187         runlevel = READUSERRC;
6188 	verbose = 0;
6189         checkRCfile(argc, argv);
6190 
6191         if (rc_file) p = rc_file;
6192            else
6193         if ((p = tildepath("~/.sunclockrc")) == NULL) {
6194            fprintf(stderr,
6195                "Unable to get path to ~/.sunclockrc\n");
6196 	}
6197 
6198 	if (p && *p) (void) readRC(p, 0);
6199 
6200         runlevel = PARSECMDLINE;
6201         (void) parseArgs(argc, argv);
6202         runlevel = RUNNING;
6203 
6204         /* Open Display */
6205 
6206         dpy = XOpenDisplay(Display_name);
6207         if (dpy == (Display *)NULL) {
6208                 fprintf(stderr, "%s: can't open display `%s'\n", ProgName,
6209                         Display_name);
6210                 exit(1);
6211         }
6212 
6213         scr = DefaultScreen(dpy);
6214         Root = RootWindow(dpy, scr);
6215 
6216         color_depth = DefaultDepth(dpy, scr);
6217 	bigendian = (ImageByteOrder(dpy) == MSBFirst);
6218         visual = DefaultVisual(dpy, scr);
6219         cmap0 = DefaultColormap(dpy, scr);
6220 
6221         black = BlackPixel(dpy, scr);
6222         white = WhitePixel(dpy, scr);
6223 
6224         if (color_depth == 16 && visual->green_mask==992) color_depth = 15;
6225         if (color_depth > 16)
6226             color_pad = 32;
6227         else if (color_depth > 8)
6228             color_pad = 16;
6229         else
6230             color_pad = 8;
6231 
6232         wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
6233         wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
6234 
6235         if (verbose) {
6236            fprintf(stderr,
6237               "%s: version %s, %s\nDepth %d    Bits/RGB %d   Bigendian : %s\n"
6238               "Red mask %ld    Green mask %ld    Blue mask %ld\n",
6239                 ProgName, VERSION, COPYRIGHT,
6240                 color_depth, visual->bits_per_rgb, (bigendian)? "yes":"no",
6241                 visual->red_mask, visual->green_mask, visual->blue_mask);
6242         }
6243 
6244         /* Correct some option parameters */
6245         if (placement<0) placement = NW;
6246         city_spotsizes = (int *) salloc(city_cat * sizeof(int));
6247         city_sizelimits = (int *) salloc(city_cat * sizeof(int));
6248         correctValues();
6249 
6250         parseFormats(ListFormats);
6251         buildMap(NULL, win_type, 1);
6252 
6253         eventLoop();
6254         exit(0);
6255 }
6256 
6257