1 /*
2 * Copyright (c) 1993, 1999 Alexandre Wennmacher (wennmach@geo.Uni-Koeln.DE)
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Alexandre Wennmacher.
16 * 4. The name of Alexandre Wennmacher may not be used to endorse or promote
17 * products derived from this software without specific prior written
18 * permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY ALEXANDRE WENNMACHER AND CONTRIBUTORS ``AS IS''
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL ALEXANDRE WENNMACHER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34 #include <sys/types.h>
35 #include <sys/signal.h>
36 #include <sys/time.h>
37 #include <sys/mman.h>
38 #include <stdio.h>
39 #include <math.h>
40 #include <time.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <X11/Xatom.h>
45 #include <X11/Xlib.h>
46 #include <X11/Xutil.h>
47 #include "const.h"
48 #include "Sphere.h"
49 #include "SphereDim.h"
50 #include "SphereData.h"
51 #include "color.h"
52 #include "level.h"
53
54 /* Solaris hack */
55 #ifndef MAP_FILE
56 #define MAP_FILE 0x0
57 #endif
58
59 #define SIZE 150
60 #define MIN_SIZE 50
61 #define X_POS +0
62 #define Y_POS +0
63 #define BORDER 5
64 #define SLEEP 120
65
66 #define MIN(x,y) (((x)<(y))?(x):(y))
67
68 char *MainTitle[] = {"Hello, world"};
69 char *IconTitle[] = {"xworld"};
70
71 XWMHints xwmh = {
72 (InputHint|StateHint),
73 False,
74 NormalState,
75 0,
76 0,
77 0, 0,
78 0,
79 0,
80 };
81
82 int GetSpherePoint(Sphere *, SPHERETYPE *, double, double);
83 long int JulianDate(int, int, int);
84 double EqunofTime(double, double *, double *);
85 char *getmyabspath(char *, char *);
86 int usage(void);
87 int main(int, char **);
88
89 int
usage()90 usage()
91 {
92 fprintf(stderr, "usage: xworld [-geometry [=]widthxheight+x+y]"
93 " [-display dpy]\n");
94 exit(1);
95 }
96
97
98 static char *
alloc_image(XImage * image)99 alloc_image(XImage *image)
100 {
101 char *Map;
102 int ix, iy, j;
103 Map = (char *)malloc(image->height * image->bytes_per_line);
104 if (Map == NULL) {
105 fprintf(stderr, "xworld: not enough memory\n");
106 exit(1);
107 }
108 image->data = Map;
109
110 for(j = 0, ix = 0; ix < image->width; ix++)
111 for (iy = 0; iy < image->height; iy++)
112 XPutPixel(image, ix, iy, j++ % NCOLORS);
113
114 return Map;
115 }
116
117 int
main(int argc,char ** argv)118 main(int argc, char **argv)
119 {
120 Display *display;
121 char *displayname = NULL;
122 Window win;
123 GC gc;
124 Visual *visual;
125 unsigned int class;
126 unsigned int depth;
127 int screen;
128 XEvent event;
129 XConfigureEvent *ConfigureEvent;
130 Mask InputSelectMask;
131 XSizeHints xsh;
132 XSetWindowAttributes xswa;
133 unsigned int xswamask;
134 XTextProperty xtptitle;
135 XColor xcolor[NCOLORS];
136 Colormap cmap;
137 int format;
138 int bitmap_pad;
139 XImage *image;
140 unsigned long foreground, background;
141 int mask;
142 int x_return, y_return;
143 unsigned int width_return, height_return;
144
145 unsigned int size, old_size;
146 unsigned int x_pos, y_pos;
147 unsigned int border = 1;
148 int n;
149 int r1, r2;
150
151 int jr, m, d, hr, min, sec;
152 double juldat, sunra, sundec;
153 double daypart, phi;
154 struct tm *gmt;
155 time_t clock;
156
157 int i, j, ix, iy;
158 double tTheta, tPhi;
159 double x, y, z;
160 double x2, y2, z2;
161 double xp, yp, zp;
162 double cost, sint;
163 double cosp, sinp;
164 double m11, m12, m13, m21, m22, m23, m32, m33;
165 SPHERETYPE Dummy = 1;
166 SPHERETYPE Value;
167 Sphere Earth;
168 char *Map;
169
170 char *progname;
171 FILE *f_o;
172 register_t *addr;
173 const size_t len = NPOINTS*sizeof(register_t);
174 off_t offset;
175 char **argp;
176 char *geometry;
177 int opt_geometry = 0;
178 int opt_display = 0;
179
180 /*
181 * Check if xworld is installed correctly.
182 * Determine the absolute path with which xworld was invoked,
183 * open and mmap the data portion, appended to the executable.
184 */
185 progname = getmyabspath(argv[0], getenv("PATH"));
186 if (progname == NULL) {
187 fprintf(stderr, "xworld: could not determine my absolute filename.\n"
188 "Abort.\n");
189 exit(1);
190 }
191 f_o = fopen(progname, "r");
192 if (f_o == NULL) {
193 perror("xworld: could not open myself");
194 exit(1);
195 }
196 offset = lseek(fileno(f_o), (off_t)0, SEEK_END) - len;
197 addr = (register_t *)mmap(NULL, len, PROT_READ, MAP_FILE | MAP_SHARED,
198 fileno(f_o), offset);
199 if (addr == (register_t *)MAP_FAILED) {
200 perror("xworld: could not mmap");
201 exit(1);
202 }
203
204 /* Next, parse command line */
205 argp = argv;
206 while (*++argp != NULL) {
207 if (strcmp(*argp, "-geometry") == 0) {
208 geometry = *++argp;
209 if (geometry == NULL) usage();
210 mask = XParseGeometry(geometry, &x_return, &y_return,
211 &width_return, &height_return);
212 if (mask == 0) usage();
213 if (mask & WidthValue && mask & HeightValue)
214 if (width_return != height_return) {
215 fprintf(stderr, "xworld: error: width must equal height!\n");
216 exit(1);
217 }
218 opt_geometry++;
219 break;
220 }
221 if (strcmp(*argp, "-display") == 0) {
222 displayname = *++argp;
223 if (displayname == NULL) usage();
224 opt_display++;
225 break;
226 }
227 usage();
228 }
229
230
231 /* Open display */
232 if ((display = XOpenDisplay(displayname)) == NULL) {
233 fprintf(stderr, "xworld: can't open display %s\n",
234 XDisplayName(displayname));
235 exit(1);
236 }
237
238 screen = DefaultScreen(display);
239 depth = DefaultDepth(display, screen);
240 if (depth == 1) {
241 fprintf(stderr, "xworld: Sorry, I can't yet work with a display of"
242 " depth 1\n");
243 exit(1);
244 } else {
245 format = ZPixmap;
246 bitmap_pad = 8;
247 if (depth > 8) bitmap_pad = 32;
248 }
249
250 visual = DefaultVisual(display, screen);
251
252 /* Set default values */
253 size = SIZE;
254 x_pos = X_POS;
255 y_pos = Y_POS;
256
257 if (opt_geometry > 0) {
258 if (mask & WidthValue) size = width_return;
259 if (mask & HeightValue) size = height_return;
260 if (size < MIN_SIZE) {
261 fprintf(stderr, "xworld: warning: using minumum size of %d in"
262 " lieu of user supplied value.\n", MIN_SIZE);
263 size = MIN_SIZE;
264 }
265 if (mask & XValue) {
266 if (mask & XNegative)
267 x_pos = DisplayWidth(display, screen) - size + x_return;
268 else
269 x_pos = x_return;
270 }
271 if (mask & YValue) {
272 if (mask & YNegative)
273 y_pos = DisplayHeight(display, screen) - size + y_return;
274 else
275 y_pos = y_return;
276 }
277 }
278 old_size = size;
279 n = size - 2*BORDER;
280 r1 = n/2;
281 r2 = size/2;
282
283 foreground = WhitePixel(display, screen);
284 background = BlackPixel(display, screen);
285
286 image = XCreateImage(display, visual, depth, format, 0, 0, size, size,
287 bitmap_pad, 0);
288 if (image == 0) {
289 fprintf(stderr, "xworld: can't create XImage\n");
290 exit(1);
291 }
292
293 xswa.event_mask = ExposureMask | StructureNotifyMask;
294 xswa.background_pixel = BlackPixel(display, screen);
295 xswamask = CWEventMask | CWBackPixel;
296
297 class = InputOutput;
298 win = XCreateWindow(display, RootWindow(display, screen), x_pos, y_pos,
299 size, size, border, depth, class, visual, xswamask, &xswa);
300
301 if (XStringListToTextProperty(MainTitle, 1, &xtptitle))
302 XSetWMName(display, win, &xtptitle);
303
304 if (XStringListToTextProperty(IconTitle, 1, &xtptitle))
305 XSetWMIconName(display, win, &xtptitle);
306
307 xsh.flags = USPosition | USSize | PPosition | PSize | PMinSize | PAspect;
308 xsh.x = x_pos;
309 xsh.y = y_pos;
310 xsh.min_width = MIN_SIZE;
311 xsh.base_width = size;
312 xsh.min_height = MIN_SIZE;
313 xsh.base_height = size;
314 xsh.min_aspect.x = 1;
315 xsh.min_aspect.y = 1;
316 xsh.max_aspect.x = 1;
317 xsh.max_aspect.y = 1;
318
319 XSetWMHints(display, win, &xwmh);
320 XSetWMNormalHints(display, win, &xsh);
321
322 /* Allocate colors */
323 cmap = DefaultColormap(display, screen);
324 for (i = 0; i < NCOLORS; i++) {
325 xcolor[i].red = color[i].red;
326 xcolor[i].green = color[i].green;
327 xcolor[i].blue = color[i].blue;
328 if (XAllocColor(display, cmap, &xcolor[i]) == 0) {
329 fprintf(stderr, "xworld: can't allocate colors\n");
330 #if 0
331 exit(-1);
332 #endif
333 }
334 }
335
336 /*
337 * generate startup image
338 */
339 Map = alloc_image(image);
340
341 gc = XCreateGC(display, win, 0, 0);
342
343 InputSelectMask = ExposureMask | StructureNotifyMask;
344 XSelectInput(display, win, InputSelectMask);
345 XPutImage(display, win, gc, image, 0, 0, 0, 0, size, size);
346 XMapWindow(display, win);
347
348 Earth.SphereBuffer = addr;
349 Earth.CoLatitude = CoLatitude;
350 Earth.PointSeparation = PointSeparation;
351 Earth.FirstPointOfLatitude = FirstPointOfLatitude;
352 Earth.PointsPerLatitude = PointsPerLatitude;
353 Earth.DeltaLatitude = PI/NINTERVALS;
354 Earth.LastPoint = Earth.FirstPointOfLatitude[NINTERVALS];
355 Earth.Dummy = Dummy;
356
357 /*
358 * begin of main loop -- xworld will run until the process is killed
359 */
360 for ( ; ; ) {
361
362 /*
363 * read the clock
364 */
365 clock = time((time_t *)NULL);
366 if (clock == (time_t)-1) {
367 fprintf(stderr, "xworld: can't read clock\n");
368 exit(1);
369 }
370
371 /*
372 * deal with gm time
373 */
374 gmt = gmtime(&clock);
375 jr = gmt->tm_year + 1900;
376 m = gmt->tm_mon + 1;
377 d = gmt->tm_mday;
378 hr = gmt->tm_hour;
379 min = gmt->tm_min;
380 sec = gmt->tm_sec;
381
382 /*
383 * do some basic astronomy
384 */
385 juldat = (double)JulianDate(jr, m, d) - 0.5;
386 daypart = (sec + 60*(min + 60*hr))/86400.0;
387 juldat += daypart;
388 phi = daypart*TWO_PI + EqunofTime(juldat, &sunra, &sundec);
389
390 sint = sin(sundec);
391 cost = cos(sundec);
392 sinp = sin(PI - phi);
393 cosp = cos(PI - phi);
394 m11 = -sinp;
395 m12 = -cosp*sint;
396 m13 = cosp*cost;
397 m21 = cosp;
398 m22 = -sinp*sint;
399 m23 = sinp*cost;
400 /* m31 = 0.0; therfore we omit it here */
401 m32 = cost;
402 m33 = sint;
403
404 j = -1;
405 for (iy = 0; iy < size; iy++) {
406 y = (double)(r2 - iy)/r1;
407 y2 = y*y;
408 for (ix = 0; ix < size; ix++) {
409 j++;
410 x = (double)(ix - r2)/r1;
411 x2 = x*x;
412 z2 = (double)1.0 - (x2 + y2);
413 if (z2 >= (double)0.0) {
414 z = sqrt(z2);
415 xp = m11*x + m12*y + m13*z;
416 yp = m21*x + m22*y + m23*z;
417 zp = m32*y + m33*z;
418 tTheta = acos(zp);
419 tPhi = atan2(yp, xp);
420 i = GetSpherePoint(&Earth, &Value, tTheta, tPhi);
421
422 /*
423 * Map elevations to colors
424 */
425 if (i != SOK) {
426 XPutPixel(image, ix, iy, xcolor[BLACK].pixel);
427 continue;
428 }
429 if (Value > level[NLEVELS - 1]) {
430 XPutPixel(image, ix, iy, xcolor[HIGH].pixel);
431 continue;
432 }
433 for (i = 0; i <= NLEVELS - 1; i++) {
434 if (Value <= level[i]) {
435 XPutPixel(image, ix, iy, xcolor[i].pixel);
436 break;
437 }
438 }
439 } else
440 XPutPixel(image, ix, iy, xcolor[BLACK].pixel);
441 }
442 }
443 XPutImage(display, win, gc, image, 0, 0, 0, 0, size, size);
444 for (i = 0; i < SLEEP; i++) {
445 sleep(1);
446 if (XCheckMaskEvent(display, InputSelectMask, &event)) {
447 if (event.type == Expose && event.xexpose.count == 0) {
448 while (XCheckTypedEvent(display, Expose, &event));
449 XPutImage(display, win, gc, image, 0, 0, 0, 0, size, size);
450 }
451 if (event.type == ConfigureNotify) {
452 while (XCheckTypedEvent(display, ConfigureNotify, &event));
453 ConfigureEvent = (XConfigureEvent *)&event;
454 size = ConfigureEvent->width;
455 if (ConfigureEvent->height != size) {
456 #if 0
457 fprintf(stderr, "xworld: error: width != height\n");
458 exit(1);
459 #else
460 size = MIN(size, ConfigureEvent->height);
461 #endif
462 }
463 if (size != old_size) {
464 old_size = size;
465 n = size - 2*BORDER;
466 r1 = n/2;
467 r2 = size/2;
468 XDestroyImage(image);
469 image = XCreateImage(display, visual, depth, format,
470 0, 0, size, size, bitmap_pad, 0);
471 if (image == NULL) {
472 fprintf(stderr, "xworld: can't create XImage\n");
473 exit(1);
474 }
475 Map = alloc_image(image);
476
477 XPutImage(display, win, gc, image, 0, 0, 0, 0,
478 size, size);
479 break;
480 }
481 }
482 }
483 }
484 }
485 }
486