From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / compass.c
1 /* xscreensaver, Copyright (c) 1999 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 #include <math.h>
13 #include "screenhack.h"
14
15 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
16 # include "xdbe.h"
17 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
18
19 #define countof(x) (sizeof(x)/sizeof(*(x)))
20 #define ABS(x) ((x)<0?-(x):(x))
21 #define MAX(x,y) ((x)<(y)?(y):(x))
22 #define MIN(x,y) ((x)>(y)?(y):(x))
23 #define RAND(n) ((long) ((random() & 0x7fffffff) % ((long) (n))))
24 #define RANDSIGN() ((random() & 1) ? 1 : -1)
25
26 struct state {
27   Display *dpy;
28   Window window;
29
30   int delay;
31   Bool dbuf;
32   struct disc *discs[4];
33   int x, y, size, size2;
34   GC ptr_gc;
35   GC erase_gc;
36   XWindowAttributes xgwa;
37   Pixmap b, ba, bb;     /* double-buffer to reduce flicker */
38 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
39   XdbeBackBuffer backb;
40 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
41 };
42
43
44
45 struct disc {
46   int theta;            /* 0 - 360*64 */
47   int velocity;
48   int acceleration;
49   int limit;
50   GC gc;
51   void (*draw) (struct state *, Drawable, struct disc *,
52                 int x, int y, int radius);
53 };
54
55
56 static void
57 draw_letters (struct state *st, Drawable d, struct disc *disc,
58               int x, int y, int radius)
59 {
60   XPoint points[50];
61   double th2 = 2 * M_PI * (disc->theta / ((double) 360*64));
62   double th;
63
64   /* W */
65
66   th = th2;
67
68   points[0].x = x + radius * 0.8 * cos(th - 0.07);
69   points[0].y = y + radius * 0.8 * sin(th - 0.07);
70
71   points[1].x = x + radius * 0.7 * cos(th - 0.05);
72   points[1].y = y + radius * 0.7 * sin(th - 0.05);
73
74   points[2].x = x + radius * 0.78 * cos(th);
75   points[2].y = y + radius * 0.78 * sin(th);
76
77   points[3].x = x + radius * 0.7 * cos(th + 0.05);
78   points[3].y = y + radius * 0.7 * sin(th + 0.05);
79
80   points[4].x = x + radius * 0.8 * cos(th + 0.07);
81   points[4].y = y + radius * 0.8 * sin(th + 0.07);
82
83   XDrawLines (st->dpy, d, disc->gc, points, 5, CoordModeOrigin);
84
85   /* 30 (1) */
86
87   th = th2 + (2 * M_PI * 0.08333);
88
89   points[0].x = x + radius * 0.78 * cos(th - 0.13);
90   points[0].y = y + radius * 0.78 * sin(th - 0.13);
91
92   points[1].x = x + radius * 0.8  * cos(th - 0.08);
93   points[1].y = y + radius * 0.8  * sin(th - 0.08);
94
95   points[2].x = x + radius * 0.78 * cos(th - 0.03);
96   points[2].y = y + radius * 0.78 * sin(th - 0.03);
97
98   points[3].x = x + radius * 0.76 * cos(th - 0.03);
99   points[3].y = y + radius * 0.76 * sin(th - 0.03);
100
101   points[4].x = x + radius * 0.75 * cos(th - 0.08);
102   points[4].y = y + radius * 0.75 * sin(th - 0.08);
103
104   points[5].x = x + radius * 0.74 * cos(th - 0.03);
105   points[5].y = y + radius * 0.74 * sin(th - 0.03);
106
107   points[6].x = x + radius * 0.72 * cos(th - 0.03);
108   points[6].y = y + radius * 0.72 * sin(th - 0.03);
109
110   points[7].x = x + radius * 0.7  * cos(th - 0.08);
111   points[7].y = y + radius * 0.7  * sin(th - 0.08);
112
113   points[8].x = x + radius * 0.72  * cos(th - 0.13);
114   points[8].y = y + radius * 0.72  * sin(th - 0.13);
115
116   XDrawLines (st->dpy, d, disc->gc, points, 9, CoordModeOrigin);
117
118   /* 30 (2) */
119
120   points[0].x = x + radius * 0.78 * cos(th + 0.03);
121   points[0].y = y + radius * 0.78 * sin(th + 0.03);
122
123   points[1].x = x + radius * 0.8  * cos(th + 0.08);
124   points[1].y = y + radius * 0.8  * sin(th + 0.08);
125
126   points[2].x = x + radius * 0.78 * cos(th + 0.13);
127   points[2].y = y + radius * 0.78 * sin(th + 0.13);
128
129   points[3].x = x + radius * 0.72 * cos(th + 0.13);
130   points[3].y = y + radius * 0.72 * sin(th + 0.13);
131
132   points[4].x = x + radius * 0.7  * cos(th + 0.08);
133   points[4].y = y + radius * 0.7  * sin(th + 0.08);
134
135   points[5].x = x + radius * 0.72  * cos(th + 0.03);
136   points[5].y = y + radius * 0.72  * sin(th + 0.03);
137
138   points[6] = points[0];
139
140   XDrawLines (st->dpy, d, disc->gc, points, 7, CoordModeOrigin);
141
142   /* 33 (1) */
143
144   th = th2 + (2 * M_PI * 0.16666);
145
146   points[0].x = x + radius * 0.78 * cos(th - 0.13);
147   points[0].y = y + radius * 0.78 * sin(th - 0.13);
148
149   points[1].x = x + radius * 0.8  * cos(th - 0.08);
150   points[1].y = y + radius * 0.8  * sin(th - 0.08);
151
152   points[2].x = x + radius * 0.78 * cos(th - 0.03);
153   points[2].y = y + radius * 0.78 * sin(th - 0.03);
154
155   points[3].x = x + radius * 0.76 * cos(th - 0.03);
156   points[3].y = y + radius * 0.76 * sin(th - 0.03);
157
158   points[4].x = x + radius * 0.75 * cos(th - 0.08);
159   points[4].y = y + radius * 0.75 * sin(th - 0.08);
160
161   points[5].x = x + radius * 0.74 * cos(th - 0.03);
162   points[5].y = y + radius * 0.74 * sin(th - 0.03);
163
164   points[6].x = x + radius * 0.72 * cos(th - 0.03);
165   points[6].y = y + radius * 0.72 * sin(th - 0.03);
166
167   points[7].x = x + radius * 0.7  * cos(th - 0.08);
168   points[7].y = y + radius * 0.7  * sin(th - 0.08);
169
170   points[8].x = x + radius * 0.72  * cos(th - 0.13);
171   points[8].y = y + radius * 0.72  * sin(th - 0.13);
172
173   XDrawLines (st->dpy, d, disc->gc, points, 9, CoordModeOrigin);
174
175   /* 33 (2) */
176
177   points[0].x = x + radius * 0.78 * cos(th + 0.03);
178   points[0].y = y + radius * 0.78 * sin(th + 0.03);
179
180   points[1].x = x + radius * 0.8  * cos(th + 0.08);
181   points[1].y = y + radius * 0.8  * sin(th + 0.08);
182
183   points[2].x = x + radius * 0.78 * cos(th + 0.13);
184   points[2].y = y + radius * 0.78 * sin(th + 0.13);
185
186   points[3].x = x + radius * 0.76 * cos(th + 0.13);
187   points[3].y = y + radius * 0.76 * sin(th + 0.13);
188
189   points[4].x = x + radius * 0.75 * cos(th + 0.08);
190   points[4].y = y + radius * 0.75 * sin(th + 0.08);
191
192   points[5].x = x + radius * 0.74 * cos(th + 0.13);
193   points[5].y = y + radius * 0.74 * sin(th + 0.13);
194
195   points[6].x = x + radius * 0.72 * cos(th + 0.13);
196   points[6].y = y + radius * 0.72 * sin(th + 0.13);
197
198   points[7].x = x + radius * 0.7  * cos(th + 0.08);
199   points[7].y = y + radius * 0.7  * sin(th + 0.08);
200
201   points[8].x = x + radius * 0.72  * cos(th + 0.03);
202   points[8].y = y + radius * 0.72  * sin(th + 0.03);
203
204   XDrawLines (st->dpy, d, disc->gc, points, 9, CoordModeOrigin);
205
206   /* N */
207
208   th = th2 + (2 * M_PI * 0.25);
209
210   points[0].x = x + radius * 0.7 * cos(th - 0.05);
211   points[0].y = y + radius * 0.7 * sin(th - 0.05);
212
213   points[1].x = x + radius * 0.8 * cos(th - 0.05);
214   points[1].y = y + radius * 0.8 * sin(th - 0.05);
215
216   points[2].x = x + radius * 0.7 * cos(th + 0.05);
217   points[2].y = y + radius * 0.7 * sin(th + 0.05);
218
219   points[3].x = x + radius * 0.8 * cos(th + 0.05);
220   points[3].y = y + radius * 0.8 * sin(th + 0.05);
221
222   XDrawLines (st->dpy, d, disc->gc, points, 4, CoordModeOrigin);
223
224   /* 3 */
225
226   th = th2 + (2 * M_PI * 0.33333);
227
228   points[0].x = x + radius * 0.78 * cos(th - 0.05);
229   points[0].y = y + radius * 0.78 * sin(th - 0.05);
230
231   points[1].x = x + radius * 0.8  * cos(th);
232   points[1].y = y + radius * 0.8  * sin(th);
233
234   points[2].x = x + radius * 0.78 * cos(th + 0.05);
235   points[2].y = y + radius * 0.78 * sin(th + 0.05);
236
237   points[3].x = x + radius * 0.76 * cos(th + 0.05);
238   points[3].y = y + radius * 0.76 * sin(th + 0.05);
239
240   points[4].x = x + radius * 0.75 * cos(th);
241   points[4].y = y + radius * 0.75 * sin(th);
242
243   points[5].x = x + radius * 0.74 * cos(th + 0.05);
244   points[5].y = y + radius * 0.74 * sin(th + 0.05);
245
246   points[6].x = x + radius * 0.72 * cos(th + 0.05);
247   points[6].y = y + radius * 0.72 * sin(th + 0.05);
248
249   points[7].x = x + radius * 0.7  * cos(th);
250   points[7].y = y + radius * 0.7  * sin(th);
251
252   points[8].x = x + radius * 0.72  * cos(th - 0.05);
253   points[8].y = y + radius * 0.72  * sin(th - 0.05);
254
255   XDrawLines (st->dpy, d, disc->gc, points, 9, CoordModeOrigin);
256
257   /* 6 */
258
259   th = th2 + (2 * M_PI * 0.41666);
260
261   points[0].x = x + radius * 0.78 * cos(th + 0.05);
262   points[0].y = y + radius * 0.78 * sin(th + 0.05);
263
264   points[1].x = x + radius * 0.8  * cos(th);
265   points[1].y = y + radius * 0.8  * sin(th);
266
267   points[2].x = x + radius * 0.78 * cos(th - 0.05);
268   points[2].y = y + radius * 0.78 * sin(th - 0.05);
269
270   points[3].x = x + radius * 0.72 * cos(th - 0.05);
271   points[3].y = y + radius * 0.72 * sin(th - 0.05);
272
273   points[4].x = x + radius * 0.7  * cos(th);
274   points[4].y = y + radius * 0.7  * sin(th);
275
276   points[5].x = x + radius * 0.72 * cos(th + 0.05);
277   points[5].y = y + radius * 0.72 * sin(th + 0.05);
278
279   points[6].x = x + radius * 0.74 * cos(th + 0.05);
280   points[6].y = y + radius * 0.74 * sin(th + 0.05);
281
282   points[7].x = x + radius * 0.76 * cos(th);
283   points[7].y = y + radius * 0.76 * sin(th);
284
285   points[8].x = x + radius * 0.74 * cos(th - 0.05);
286   points[8].y = y + radius * 0.74 * sin(th - 0.05);
287
288   XDrawLines (st->dpy, d, disc->gc, points, 9, CoordModeOrigin);
289
290
291   /* E */
292
293   th = th2 + (2 * M_PI * 0.5);
294
295   points[0].x = x + radius * 0.8 * cos(th + 0.05);
296   points[0].y = y + radius * 0.8 * sin(th + 0.05);
297
298   points[1].x = x + radius * 0.8 * cos(th - 0.05);
299   points[1].y = y + radius * 0.8 * sin(th - 0.05);
300
301   points[2].x = x + radius * 0.75 * cos(th - 0.05);
302   points[2].y = y + radius * 0.75 * sin(th - 0.05);
303
304   points[3].x = x + radius * 0.75 * cos(th + 0.025);
305   points[3].y = y + radius * 0.75 * sin(th + 0.025);
306
307   points[4].x = x + radius * 0.75 * cos(th - 0.05);
308   points[4].y = y + radius * 0.75 * sin(th - 0.05);
309
310   points[5].x = x + radius * 0.7 * cos(th - 0.05);
311   points[5].y = y + radius * 0.7 * sin(th - 0.05);
312
313   points[6].x = x + radius * 0.7 * cos(th + 0.05);
314   points[6].y = y + radius * 0.7 * sin(th + 0.05);
315
316   XDrawLines (st->dpy, d, disc->gc, points, 7, CoordModeOrigin);
317
318   /* 12 (1) */
319
320   th = th2 + (2 * M_PI * 0.58333);
321
322   points[0].x = x + radius * 0.77 * cos(th - 0.06);
323   points[0].y = y + radius * 0.77 * sin(th - 0.06);
324
325   points[1].x = x + radius * 0.8  * cos(th - 0.03);
326   points[1].y = y + radius * 0.8  * sin(th - 0.03);
327
328   points[2].x = x + radius * 0.7  * cos(th - 0.03);
329   points[2].y = y + radius * 0.7  * sin(th - 0.03);
330
331   XDrawLines (st->dpy, d, disc->gc, points, 3, CoordModeOrigin);
332
333   /* 12 (2) */
334
335   points[0].x = x + radius * 0.78 * cos(th + 0.02);
336   points[0].y = y + radius * 0.78 * sin(th + 0.02);
337
338   points[1].x = x + radius * 0.8  * cos(th + 0.07);
339   points[1].y = y + radius * 0.8  * sin(th + 0.07);
340
341   points[2].x = x + radius * 0.78 * cos(th + 0.11);
342   points[2].y = y + radius * 0.78 * sin(th + 0.11);
343
344   points[3].x = x + radius * 0.76 * cos(th + 0.11);
345   points[3].y = y + radius * 0.76 * sin(th + 0.11);
346
347   points[4].x = x + radius * 0.74 * cos(th + 0.02);
348   points[4].y = y + radius * 0.74 * sin(th + 0.02);
349
350   points[5].x = x + radius * 0.71 * cos(th + 0.03);
351   points[5].y = y + radius * 0.71 * sin(th + 0.03);
352
353   points[6].x = x + radius * 0.7  * cos(th + 0.03);
354   points[6].y = y + radius * 0.7  * sin(th + 0.03);
355
356   points[7].x = x + radius * 0.7  * cos(th + 0.13);
357   points[7].y = y + radius * 0.7  * sin(th + 0.13);
358
359   XDrawLines (st->dpy, d, disc->gc, points, 8, CoordModeOrigin);
360
361   /* 15 (1) */
362
363   th = th2 + (2 * M_PI * 0.66666);
364
365   points[0].x = x + radius * 0.77 * cos(th - 0.06);
366   points[0].y = y + radius * 0.77 * sin(th - 0.06);
367
368   points[1].x = x + radius * 0.8  * cos(th - 0.03);
369   points[1].y = y + radius * 0.8  * sin(th - 0.03);
370
371   points[2].x = x + radius * 0.7  * cos(th - 0.03);
372   points[2].y = y + radius * 0.7  * sin(th - 0.03);
373
374   XDrawLines (st->dpy, d, disc->gc, points, 3, CoordModeOrigin);
375
376   /* 15 (2) */
377
378   points[0].x = x + radius * 0.8  * cos(th + 0.11);
379   points[0].y = y + radius * 0.8  * sin(th + 0.11);
380
381   points[1].x = x + radius * 0.8  * cos(th + 0.02);
382   points[1].y = y + radius * 0.8  * sin(th + 0.02);
383
384   points[2].x = x + radius * 0.76 * cos(th + 0.02);
385   points[2].y = y + radius * 0.76 * sin(th + 0.02);
386
387   points[3].x = x + radius * 0.77 * cos(th + 0.06);
388   points[3].y = y + radius * 0.77 * sin(th + 0.06);
389
390   points[4].x = x + radius * 0.76 * cos(th + 0.10);
391   points[4].y = y + radius * 0.76 * sin(th + 0.10);
392
393   points[5].x = x + radius * 0.73 * cos(th + 0.11);
394   points[5].y = y + radius * 0.73 * sin(th + 0.11);
395
396   points[6].x = x + radius * 0.72 * cos(th + 0.10);
397   points[6].y = y + radius * 0.72 * sin(th + 0.10);
398
399   points[7].x = x + radius * 0.7  * cos(th + 0.06);
400   points[7].y = y + radius * 0.7  * sin(th + 0.06);
401
402   points[8].x = x + radius * 0.72 * cos(th + 0.02);
403   points[8].y = y + radius * 0.72 * sin(th + 0.02);
404
405   XDrawLines (st->dpy, d, disc->gc, points, 9, CoordModeOrigin);
406
407   /* S */
408
409   th = th2 + (2 * M_PI * 0.75);
410
411   points[0].x = x + radius * 0.78 * cos(th + 0.05);
412   points[0].y = y + radius * 0.78 * sin(th + 0.05);
413
414   points[1].x = x + radius * 0.8  * cos(th);
415   points[1].y = y + radius * 0.8  * sin(th);
416
417   points[2].x = x + radius * 0.78 * cos(th - 0.05);
418   points[2].y = y + radius * 0.78 * sin(th - 0.05);
419
420   points[3].x = x + radius * 0.76 * cos(th - 0.05);
421   points[3].y = y + radius * 0.76 * sin(th - 0.05);
422
423   points[4].x = x + radius * 0.74 * cos(th + 0.05);
424   points[4].y = y + radius * 0.74 * sin(th + 0.05);
425
426   points[5].x = x + radius * 0.72 * cos(th + 0.05);
427   points[5].y = y + radius * 0.72 * sin(th + 0.05);
428
429   points[6].x = x + radius * 0.7  * cos(th);
430   points[6].y = y + radius * 0.7  * sin(th);
431
432   points[7].x = x + radius * 0.72  * cos(th - 0.05);
433   points[7].y = y + radius * 0.72  * sin(th - 0.05);
434
435   XDrawLines (st->dpy, d, disc->gc, points, 8, CoordModeOrigin);
436
437   /* 21 (1) */
438
439   th = th2 + (2 * M_PI * 0.83333);
440
441   points[0].x = x + radius * 0.78 * cos(th - 0.13);
442   points[0].y = y + radius * 0.78 * sin(th - 0.13);
443
444   points[1].x = x + radius * 0.8  * cos(th - 0.08);
445   points[1].y = y + radius * 0.8  * sin(th - 0.08);
446
447   points[2].x = x + radius * 0.78 * cos(th - 0.03);
448   points[2].y = y + radius * 0.78 * sin(th - 0.03);
449
450   points[3].x = x + radius * 0.76 * cos(th - 0.03);
451   points[3].y = y + radius * 0.76 * sin(th - 0.03);
452
453   points[4].x = x + radius * 0.74 * cos(th - 0.12);
454   points[4].y = y + radius * 0.74 * sin(th - 0.12);
455
456   points[5].x = x + radius * 0.71 * cos(th - 0.13);
457   points[5].y = y + radius * 0.71 * sin(th - 0.13);
458
459   points[6].x = x + radius * 0.7  * cos(th - 0.13);
460   points[6].y = y + radius * 0.7  * sin(th - 0.13);
461
462   points[7].x = x + radius * 0.7  * cos(th - 0.02);
463   points[7].y = y + radius * 0.7  * sin(th - 0.02);
464
465   XDrawLines (st->dpy, d, disc->gc, points, 8, CoordModeOrigin);
466
467   /* 21 (2) */
468
469   points[0].x = x + radius * 0.77 * cos(th + 0.03);
470   points[0].y = y + radius * 0.77 * sin(th + 0.03);
471
472   points[1].x = x + radius * 0.8  * cos(th + 0.06);
473   points[1].y = y + radius * 0.8  * sin(th + 0.06);
474
475   points[2].x = x + radius * 0.7  * cos(th + 0.06);
476   points[2].y = y + radius * 0.7  * sin(th + 0.06);
477
478   XDrawLines (st->dpy, d, disc->gc, points, 3, CoordModeOrigin);
479
480   /* 24 (1) */
481
482   th = th2 + (2 * M_PI * 0.91666);
483
484   points[0].x = x + radius * 0.78 * cos(th - 0.13);
485   points[0].y = y + radius * 0.78 * sin(th - 0.13);
486
487   points[1].x = x + radius * 0.8  * cos(th - 0.08);
488   points[1].y = y + radius * 0.8  * sin(th - 0.08);
489
490   points[2].x = x + radius * 0.78 * cos(th - 0.03);
491   points[2].y = y + radius * 0.78 * sin(th - 0.03);
492
493   points[3].x = x + radius * 0.76 * cos(th - 0.03);
494   points[3].y = y + radius * 0.76 * sin(th - 0.03);
495
496   points[4].x = x + radius * 0.74 * cos(th - 0.12);
497   points[4].y = y + radius * 0.74 * sin(th - 0.12);
498
499   points[5].x = x + radius * 0.71 * cos(th - 0.13);
500   points[5].y = y + radius * 0.71 * sin(th - 0.13);
501
502   points[6].x = x + radius * 0.7  * cos(th - 0.13);
503   points[6].y = y + radius * 0.7  * sin(th - 0.13);
504
505   points[7].x = x + radius * 0.7  * cos(th - 0.02);
506   points[7].y = y + radius * 0.7  * sin(th - 0.02);
507
508   XDrawLines (st->dpy, d, disc->gc, points, 8, CoordModeOrigin);
509
510   /* 24 (2) */
511
512   points[0].x = x + radius * 0.69 * cos(th + 0.09);
513   points[0].y = y + radius * 0.69 * sin(th + 0.09);
514
515   points[1].x = x + radius * 0.8  * cos(th + 0.09);
516   points[1].y = y + radius * 0.8  * sin(th + 0.09);
517
518   points[2].x = x + radius * 0.72 * cos(th + 0.01);
519   points[2].y = y + radius * 0.72 * sin(th + 0.01);
520
521   points[3].x = x + radius * 0.72 * cos(th + 0.13);
522   points[3].y = y + radius * 0.72 * sin(th + 0.13);
523
524   XDrawLines (st->dpy, d, disc->gc, points, 4, CoordModeOrigin);
525 }
526
527
528 static void
529 draw_ticks (struct state *st, Drawable d, struct disc *disc,
530             int x, int y, int radius)
531 {
532   XSegment segs[72];
533   int i;
534   double tick = (M_PI * 2) / 72;
535
536   for (i = 0; i < 72; i++)
537     {
538       int radius2 = radius;
539       double th = (i * tick) + (2 * M_PI * (disc->theta / ((double) 360*64)));
540
541       if (i % 6)
542         radius2 -= radius / 16;
543       else
544         radius2 -= radius / 8;
545
546       segs[i].x1 = x + radius  * cos(th);
547       segs[i].y1 = y + radius  * sin(th);
548       segs[i].x2 = x + radius2 * cos(th);
549       segs[i].y2 = y + radius2 * sin(th);
550     }
551   XDrawSegments (st->dpy, d, disc->gc, segs, countof(segs));
552
553   draw_letters (st, d, disc, x, y, radius);
554 }
555
556
557 static void
558 draw_thin_arrow (struct state *st, Drawable d, struct disc *disc,
559                  int x, int y, int radius)
560 {
561   XPoint points[3];
562   double th;
563   int radius2;
564   double tick = ((M_PI * 2) / 72) * 2;
565
566   radius *= 0.9;
567   radius2 = radius - (radius / 8) * 3;
568
569   th = 2 * M_PI * (disc->theta / ((double) 360*64));
570
571   points[0].x = x + radius * cos(th);           /* tip */
572   points[0].y = y + radius * sin(th);
573
574   points[1].x = x + radius2 * cos(th - tick);   /* tip left */
575   points[1].y = y + radius2 * sin(th - tick);
576
577   points[2].x = x + radius2 * cos(th + tick);   /* tip right */
578   points[2].y = y + radius2 * sin(th + tick);
579
580   XDrawLine (st->dpy, d, disc->gc,
581              (int) (x + radius2 * cos(th)),
582              (int) (y + radius2 * sin(th)),
583              (int) (x + -radius * cos(th)),
584              (int) (y + -radius * sin(th)));
585
586   XFillPolygon (st->dpy, d, disc->gc, points, 3, Convex, CoordModeOrigin);
587 }
588
589
590 static void
591 draw_thick_arrow (struct state *st, Drawable d, struct disc *disc,
592                   int x, int y, int radius)
593 {
594   XPoint points[10];
595   double th;
596   int radius2, radius3;
597   double tick = ((M_PI * 2) / 72) * 2;
598
599   radius *= 0.9;
600   radius2 = radius - (radius / 8) * 3;
601   radius3 = radius - (radius / 8) * 2;
602   th = 2 * M_PI * (disc->theta / ((double) 360*64));
603
604   points[0].x = x + radius * cos(th);           /* tip */
605   points[0].y = y + radius * sin(th);
606
607   points[1].x = x + radius2 * cos(th - tick);   /* tip left */
608   points[1].y = y + radius2 * sin(th - tick);
609
610   points[2].x = x + radius2 * cos(th + tick);   /* tip right */
611   points[2].y = y + radius2 * sin(th + tick);
612
613   points[3] = points[0];
614
615   XDrawLines (st->dpy, d, disc->gc, points, 4, CoordModeOrigin);
616
617   points[0].x = x + radius2 * cos(th - tick/2);  /* top left */
618   points[0].y = y + radius2 * sin(th - tick/2);
619
620   points[1].x = x + -radius2 * cos(th + tick/2); /* bottom left */
621   points[1].y = y + -radius2 * sin(th + tick/2);
622
623   points[2].x = x + -radius3 * cos(th);          /* bottom */
624   points[2].y = y + -radius3 * sin(th);
625
626   points[3].x = x + -radius * cos(th);           /* bottom spike */
627   points[3].y = y + -radius * sin(th);
628
629   points[4] = points[2];                         /* return */
630
631   points[5].x = x + -radius2 * cos(th - tick/2); /* bottom right */
632   points[5].y = y + -radius2 * sin(th - tick/2);
633
634   points[6].x = x + radius2 * cos(th + tick/2);  /* top right */
635   points[6].y = y + radius2 * sin(th + tick/2);
636
637   XDrawLines (st->dpy, d, disc->gc, points, 7, CoordModeOrigin);
638 }
639
640
641
642 static void
643 roll_disc (struct disc *disc)
644 {
645   double th = disc->theta;
646   if (th < 0)
647     th = -(th + disc->velocity);
648   else
649     th = (th + disc->velocity);
650
651   if (th > (360*64))
652     th -= (360*64);
653   else if (th < 0)
654     th += (360*64);
655
656   disc->theta = (disc->theta > 0 ? th : -th);
657
658   disc->velocity += disc->acceleration;
659
660   if (disc->velocity > disc->limit || 
661       disc->velocity < -disc->limit)
662     disc->acceleration = -disc->acceleration;
663
664   /* Alter direction of rotational acceleration randomly. */
665   if (! (random() % 120))
666     disc->acceleration = -disc->acceleration;
667
668   /* Change acceleration very occasionally. */
669   if (! (random() % 200))
670     {
671       if (random() & 1)
672         disc->acceleration *= 1.2;
673       else
674         disc->acceleration *= 0.8;
675     }
676 }
677
678
679 static void
680 init_spin (struct disc *disc)
681 {
682   disc->limit = 5*64;
683   disc->theta = RAND(360*64);
684   disc->velocity = RAND(16) * RANDSIGN();
685   disc->acceleration = RAND(16) * RANDSIGN();
686 }
687
688
689 static void
690 draw_compass (struct state *st)
691 {
692   int i = 0;
693   while (st->discs[i])
694     {
695       st->discs[i]->draw (st, st->b, st->discs[i], st->x, st->y, st->size);
696       roll_disc (st->discs[i]);
697       i++;
698     }
699 }
700
701 static void
702 draw_pointer (struct state *st)
703 {
704   int radius = st->size;
705   GC dot_gc = st->discs[0]->gc;
706   XPoint points[3];
707   int size = radius * 0.1;
708
709   /* top */
710
711   points[0].x = st->x - size;
712   points[0].y = st->y - radius - size;
713
714   points[1].x = st->x + size;
715   points[1].y = st->y - radius - size;
716
717   points[2].x = st->x;
718   points[2].y = st->y - radius;
719   
720   XFillPolygon (st->dpy, st->b, st->ptr_gc, points, 3, Convex, CoordModeOrigin);
721
722   /* top right */
723
724   points[0].x = st->x - (radius * 0.85);
725   points[0].y = st->y - (radius * 0.8);
726
727   points[1].x = st->x - (radius * 1.1);
728   points[1].y = st->y - (radius * 0.55);
729
730   points[2].x = st->x - (radius * 0.6);
731   points[2].y = st->y - (radius * 0.65);
732   
733   XFillPolygon (st->dpy, st->b, st->ptr_gc, points, 3, Convex, CoordModeOrigin);
734
735   /* left */
736
737   points[0].x = st->x - (radius * 1.05);
738   points[0].y = st->y;
739
740   points[1].x = st->x - (radius * 1.1);
741   points[1].y = st->y - (radius * 0.025);
742
743   points[2].x = st->x - (radius * 1.1);
744   points[2].y = st->y + (radius * 0.025);
745   
746   XFillPolygon (st->dpy, st->b, dot_gc, points, 3, Convex, CoordModeOrigin);
747
748   /* right */
749
750   points[0].x = st->x + (radius * 1.05);
751   points[0].y = st->y;
752
753   points[1].x = st->x + (radius * 1.1);
754   points[1].y = st->y - (radius * 0.025);
755
756   points[2].x = st->x + (radius * 1.1);
757   points[2].y = st->y + (radius * 0.025);
758   
759   XFillPolygon (st->dpy, st->b, dot_gc, points, 3, Convex, CoordModeOrigin);
760
761   /* bottom */
762
763   points[0].x = st->x;
764   points[0].y = st->y + (radius * 1.05);
765
766   points[1].x = st->x - (radius * 0.025);
767   points[1].y = st->y + (radius * 1.1);
768
769   points[2].x = st->x + (radius * 0.025);
770   points[2].y = st->y + (radius * 1.1);
771   
772   XFillPolygon (st->dpy, st->b, dot_gc, points, 3, Convex, CoordModeOrigin);
773
774   /* bottom left */
775
776   points[0].x = st->x + (radius * 0.74);
777   points[0].y = st->y + (radius * 0.74);
778
779   points[1].x = st->x + (radius * 0.78);
780   points[1].y = st->y + (radius * 0.75);
781
782   points[2].x = st->x + (radius * 0.75);
783   points[2].y = st->y + (radius * 0.78);
784   
785   XFillPolygon (st->dpy, st->b, dot_gc, points, 3, Convex, CoordModeOrigin);
786
787   /* top left */
788
789   points[0].x = st->x + (radius * 0.74);
790   points[0].y = st->y - (radius * 0.74);
791
792   points[1].x = st->x + (radius * 0.78);
793   points[1].y = st->y - (radius * 0.75);
794
795   points[2].x = st->x + (radius * 0.75);
796   points[2].y = st->y - (radius * 0.78);
797   
798   XFillPolygon (st->dpy, st->b, dot_gc, points, 3, Convex, CoordModeOrigin);
799
800   /* bottom right */
801
802   points[0].x = st->x - (radius * 0.74);
803   points[0].y = st->y + (radius * 0.74);
804
805   points[1].x = st->x - (radius * 0.78);
806   points[1].y = st->y + (radius * 0.75);
807
808   points[2].x = st->x - (radius * 0.75);
809   points[2].y = st->y + (radius * 0.78);
810   
811   XFillPolygon (st->dpy, st->b, dot_gc, points, 3, Convex, CoordModeOrigin);
812 }
813
814
815 static void *
816 compass_init (Display *dpy, Window window)
817 {
818   struct state *st = (struct state *) calloc (1, sizeof(*st));
819   XGCValues gcv;
820   st->dpy = dpy;
821   st->window = window;
822   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
823   st->dbuf = get_boolean_resource (st->dpy, "doubleBuffer", "Boolean");
824
825 # ifdef HAVE_COCOA      /* Don't second-guess Quartz's double-buffering */
826   st->dbuf = False;
827 # endif
828
829   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
830   st->size2 = MIN(st->xgwa.width, st->xgwa.height);
831
832   if (st->size2 > 600) st->size2 = 600;
833
834   st->size = (st->size2 / 2) * 0.8;
835
836   st->x = st->xgwa.width/2;
837   st->y = st->xgwa.height/2;
838
839   if (st->dbuf)
840     {
841 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
842       st->b = st->backb = xdbe_get_backbuffer (st->dpy, st->window, XdbeUndefined);
843 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
844
845       if (!st->b)
846         {
847           st->x = st->size2/2;
848           st->y = st->size2/2;
849           st->ba = XCreatePixmap (st->dpy, st->window, st->size2, st->size2, st->xgwa.depth);
850           st->bb = XCreatePixmap (st->dpy, st->window, st->size2, st->size2, st->xgwa.depth);
851           st->b = st->ba;
852         }
853     }
854   else
855     {
856       st->b = st->window;
857     }
858
859   st->discs[0] = (struct disc *) calloc (1, sizeof (struct disc));
860   st->discs[1] = (struct disc *) calloc (1, sizeof (struct disc));
861   st->discs[2] = (struct disc *) calloc (1, sizeof (struct disc));
862   st->discs[3] = 0;
863
864   gcv.foreground = get_pixel_resource (st->dpy, st->xgwa.colormap,
865                                        "foreground", "Foreground");
866   gcv.line_width = MAX(2, (st->size/60));
867   gcv.join_style = JoinBevel;
868   st->discs[0]->draw = draw_ticks;
869   st->discs[0]->gc = XCreateGC (st->dpy, st->b, GCForeground|GCLineWidth|GCJoinStyle,
870                             &gcv);
871   init_spin (st->discs[0]);
872
873   gcv.foreground = get_pixel_resource (st->dpy, st->xgwa.colormap,
874                                        "arrow2Foreground", "Foreground");
875   gcv.line_width = MAX(4, (st->size / 30));
876   st->discs[1]->draw = draw_thick_arrow;
877   st->discs[1]->gc = XCreateGC (st->dpy, st->b, GCForeground|GCLineWidth, &gcv);
878   init_spin (st->discs[1]);
879
880   gcv.foreground = get_pixel_resource (st->dpy, st->xgwa.colormap,
881                                        "arrow1Foreground", "Foreground");
882   gcv.line_width = MAX(4, (st->size / 30));
883   st->discs[2]->draw = draw_thin_arrow;
884   st->discs[2]->gc = XCreateGC (st->dpy, st->b, GCForeground|GCLineWidth, &gcv);
885   init_spin (st->discs[2]);
886
887   gcv.foreground = get_pixel_resource (st->dpy, st->xgwa.colormap,
888                                        "pointerForeground", "Foreground");
889   st->ptr_gc = XCreateGC (st->dpy, st->b, GCForeground|GCLineWidth, &gcv);
890
891   gcv.foreground = get_pixel_resource (st->dpy, st->xgwa.colormap,
892                                        "background", "Background");
893   st->erase_gc = XCreateGC (st->dpy, st->b, GCForeground, &gcv);
894
895   if (st->ba) XFillRectangle (st->dpy, st->ba, st->erase_gc, 0, 0, st->size2, st->size2);
896   if (st->bb) XFillRectangle (st->dpy, st->bb, st->erase_gc, 0, 0, st->size2, st->size2);
897
898   return st;
899 }
900
901 static unsigned long
902 compass_draw (Display *dpy, Window window, void *closure)
903 {
904   struct state *st = (struct state *) closure;
905   XFillRectangle (st->dpy, st->b, st->erase_gc, 0, 0, st->xgwa.width, st->xgwa.height);
906
907   draw_compass (st);
908   draw_pointer (st);
909
910 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
911   if (st->backb)
912     {
913       XdbeSwapInfo info[1];
914       info[0].swap_window = st->window;
915       info[0].swap_action = XdbeUndefined;
916       XdbeSwapBuffers (st->dpy, info, 1);
917     }
918   else
919 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
920     if (st->dbuf)
921       {
922         XCopyArea (st->dpy, st->b, st->window, st->erase_gc, 0, 0,
923                    st->size2, st->size2,
924                    st->xgwa.width/2 - st->x,
925                    st->xgwa.height/2 - st->y);
926         st->b = (st->b == st->ba ? st->bb : st->ba);
927       }
928
929   return st->delay;
930 }
931
932 static void
933 compass_reshape (Display *dpy, Window window, void *closure, 
934                  unsigned int w, unsigned int h)
935 {
936   struct state *st = (struct state *) closure;
937   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
938   st->size2 = MIN(st->xgwa.width, st->xgwa.height);
939   st->x = st->xgwa.width/2;
940   st->y = st->xgwa.height/2;
941 }
942
943 static Bool
944 compass_event (Display *dpy, Window window, void *closure, XEvent *event)
945 {
946   return False;
947 }
948
949 static void
950 compass_free (Display *dpy, Window window, void *closure)
951 {
952 }
953
954 \f
955
956 static const char *compass_defaults [] = {
957   ".background:         #000000",
958   ".foreground:         #DDFFFF",
959   "*arrow1Foreground:   #FFF66A",
960   "*arrow2Foreground:   #F7D64A",
961   "*pointerForeground:  #FF0000",
962   "*delay:              20000",
963   "*doubleBuffer:       True",
964 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
965   "*useDBE:             True",
966 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
967   0
968 };
969
970 static XrmOptionDescRec compass_options [] = {
971   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
972   { "-db",              ".doubleBuffer", XrmoptionNoArg,  "True" },
973   { "-no-db",           ".doubleBuffer", XrmoptionNoArg,  "False" },
974   { 0, 0, 0, 0 }
975 };
976
977
978 XSCREENSAVER_MODULE ("Compass", compass)