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", °, &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(>ime);
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(>ime);
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(>ime);
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(>ime);
2349 gtp = *gmtime(>ime);
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(>ime);
2447 setTZ(Context->mark1.city);
2448 ltp = *localtime(>ime);
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(>ime);
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