]>
Commit | Line | Data |
---|---|---|
1 | /* -copyright- | |
2 | #-# Copyright © 2021 Eric Bina, Dave Black, TJ Phan, | |
3 | #-# Vincent Renardias, Willem Vermin | |
4 | #-# | |
5 | #-# Permission is hereby granted, free of charge, to any person | |
6 | #-# obtaining a copy of this software and associated documentation | |
7 | #-# files (the “Software”), to deal in the Software without | |
8 | #-# restriction, including without limitation the rights to use, | |
9 | #-# copy, modify, merge, publish, distribute, sublicense, and/or | |
10 | #-# sell copies of the Software, and to permit persons to whom | |
11 | #-# the Software is furnished to do so, subject to the following | |
12 | #-# conditions: | |
13 | #-# | |
14 | #-# The above copyright notice and this permission notice shall | |
15 | #-# be included in all copies or substantial portions of the Software. | |
16 | #-# | |
17 | #-# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, | |
18 | #-# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | |
19 | #-# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
20 | #-# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
21 | #-# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
22 | #-# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
23 | #-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
24 | #-# OTHER DEALINGS IN THE SOFTWARE. | |
25 | #-# | |
26 | */ | |
27 | ||
28 | /* | |
29 | ||
30 | * Original Author Unknow. | |
31 | ||
32 | * 8/10/88 - Ported from X10 to X11R3 by: | |
33 | ||
34 | Jonathan Greenblatt (jonnyg@rover.umd.edu) | |
35 | ||
36 | * Cleaned up by Dave Lemke (lemke@sun.com) | |
37 | ||
38 | * Ported to monocrome by Jonathan Greenblatt (jonnyg@rover.umd.edu) | |
39 | ||
40 | * 05/02/1996 Added TrueColor support by TJ Phan (phan@aur.alcatel.com) | |
41 | ||
42 | */ | |
43 | ||
44 | #include <sys/types.h> | |
45 | #include <sys/time.h> | |
46 | ||
47 | #include <stdio.h> | |
48 | #include <stdlib.h> | |
49 | #include <unistd.h> | |
50 | #include <string.h> | |
51 | #include <signal.h> | |
52 | #include <gtk/gtk.h> | |
53 | #include <X11/Xlib.h> | |
54 | #include <X11/Xutil.h> | |
55 | #include <X11/xpm.h> | |
56 | #include <ctype.h> | |
57 | #include <assert.h> | |
58 | ||
59 | #include "xfishtank.h" | |
60 | #include "vroot.h" | |
61 | #include "bubbles.h" | |
62 | #include "debug.h" | |
63 | #include "transwindow.h" | |
64 | #include "utils.h" | |
65 | #include "fishes.h" | |
66 | #include "ixpm.h" | |
67 | #include "ui.h" | |
68 | ||
69 | ||
70 | #ifdef DOUBLE_BUFFER | |
71 | #include <X11/extensions/Xdbe.h> | |
72 | static int UseXdbe; | |
73 | static XdbeSwapAction BMETHOD; | |
74 | static int XdbeAvailable = -42; | |
75 | #endif | |
76 | ||
77 | extern unsigned char *ReadBitmap(); | |
78 | ||
79 | /* externals for pixmap and bimaps from xfishy.h */ | |
80 | ||
81 | ||
82 | /* typedefs for bubble and fish structures, also caddr_t (not used in X.h) */ | |
83 | typedef struct _bubble { | |
84 | float x; | |
85 | float y; | |
86 | int size; // size of bubble | |
87 | float bstep; // increment in placement | |
88 | } bubble; | |
89 | ||
90 | typedef struct _fish { | |
91 | float x; | |
92 | float y; | |
93 | int direction; // direction: 0: r->l | |
94 | int frame; // animation frames: 0 .. NUM_FRAMES | |
95 | int type; | |
96 | float fstep; // increment in placement | |
97 | int animtime; // # drawings between change of frame | |
98 | } fish; | |
99 | ||
100 | typedef struct _fishtype { | |
101 | Pixmap pix; | |
102 | GC gc; | |
103 | int w; | |
104 | int h; | |
105 | } fishtype; | |
106 | ||
107 | ||
108 | static int binc[] = { 0, 64, 56, 48, 40, 32, 24, 16, 8 }; /* bubble increment and yes check tables */ | |
109 | static int DoubleBuf = 0; /* Should we use double buffering */ | |
110 | static int width; /* width of initial window in pixels */ | |
111 | static int height; /* height of initial window in pixels */ | |
112 | static int screen; /* Default screen of this display */ | |
113 | static int inxscreensaver = 0; /* are we running in xscreensaver? */ | |
114 | static float smooth = 0.02; /* smoothness increment multiplier */ | |
115 | static bubble *binfo = NULL; /* bubble info structures, allocated dynamically */ | |
116 | static fish *finfo = NULL; /* fish info structures, allocated dynamically */ | |
117 | static Window root_window; | |
118 | static fishtype xfish[NUM_FISH][2][NUM_FRAMES]; /* [type][left/right][frame] */ | |
119 | ||
120 | static Pixmap xbubbles[9]; /* bubbles bitmaps (1 to 8, by size in pixels) */ | |
121 | static Window wid; /* aquarium window */ | |
122 | static Window wid1; | |
123 | static Window UserWindow = 0; | |
124 | static Pixel white; | |
125 | static Pixel black; | |
126 | static Pixel bcolor; | |
127 | static Pixel bgcolor; | |
128 | //static GC pgc; | |
129 | //static GC gc; | |
130 | static GC bgc; | |
131 | //static GC draw_gc; | |
132 | static int xfishtank_trans = 0; | |
133 | //static int rwidth[NUM_FISH]; | |
134 | //static int rheight[NUM_FISH]; | |
135 | static XdbeBackBuffer backbuf = 0; | |
136 | static int WantXdbe = 1; | |
137 | static const int maxanimtime = 20; | |
138 | static int nomenu = 0; | |
139 | static int ForceRoot = 0; | |
140 | static int HaltedByInterrupt = 0; | |
141 | static int Done = 0; | |
142 | ||
143 | static Pixel AllocNamedColor(Display *display, char *colorName, Pixel dfltPix); | |
144 | static char *display_name=NULL; | |
145 | static int do_move(void *dummy); | |
146 | static int do_testfish(void *dummy); | |
147 | static int do_write_flags(void *dummy); | |
148 | static void erasebubble(bubble *b); | |
149 | static void erasefish(fish *f); | |
150 | static void init_pixmap(void); | |
151 | static void init_signals(void); | |
152 | static void initialize(void); | |
153 | static void move_fish(void); | |
154 | static void movefish(fish *f); | |
155 | static void new_bubble(bubble *b, int init); | |
156 | static void new_fish(fish *f0, int init); | |
157 | static void parse(int argc, char **argv); | |
158 | static void putbubble(bubble *b, unsigned long c); | |
159 | static void remove_all_bubbles(void); | |
160 | static void remove_all_fishes(void); | |
161 | static void setanimtime(fish*f); | |
162 | static void step_bubbles(void); | |
163 | static void Usage(void); | |
164 | static void Thanks(void); | |
165 | static void SigHandler(int signum); | |
166 | static void print_changelog(void); | |
167 | static void selfrep(void); | |
168 | ||
169 | ||
170 | Display *Dpy = NULL; | |
171 | Window xfishtankWin; | |
172 | int counter; | |
173 | int blimit = 32; /* bubble limit */ | |
174 | int flimit = 10; /* fish limit */ | |
175 | char *bcolorstring = NULL; | |
176 | char *bgcolorstring = NULL; | |
177 | float speedfactor; | |
178 | ||
179 | ||
180 | ||
181 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
182 | parse command line | |
183 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
184 | void parse(int argc, char *argv[]) | |
185 | { | |
186 | ||
187 | int p = 0; | |
188 | ||
189 | #define CHECK do{if(++p >= argc){ Usage(); Thanks(); exit(1);}}while(0) | |
190 | while (++p < argc) | |
191 | { | |
192 | P("argv[p] %s\n",argv[p]); | |
193 | if (!strcmp(argv[p],"-b")) | |
194 | { | |
195 | CHECK; | |
196 | blimit = strtod(argv[p],NULL); | |
197 | } | |
198 | else if (!strcmp(argv[p],"-f")) | |
199 | { | |
200 | CHECK; | |
201 | flimit = strtod(argv[p],NULL); | |
202 | } | |
203 | else if (!strcmp(argv[p],"-display")) | |
204 | { | |
205 | CHECK; | |
206 | display_name = strdup(argv[p]); | |
207 | } | |
208 | else if (!strcmp(argv[p],"-bc")) | |
209 | { | |
210 | CHECK; | |
211 | if(bcolorstring) | |
212 | free(bcolorstring); | |
213 | bcolorstring = strdup(argv[p]); | |
214 | } | |
215 | else if (!strcmp(argv[p],"-bgc")) | |
216 | { | |
217 | CHECK; | |
218 | if(bgcolorstring) | |
219 | free(bgcolorstring); | |
220 | bgcolorstring = strdup(argv[p]); | |
221 | } | |
222 | else if (!strcmp(argv[p],"-double")) | |
223 | { | |
224 | CHECK; | |
225 | WantXdbe = (strtod(argv[p],NULL)>0); | |
226 | } | |
227 | else if (!strcmp(argv[p],"-speed")) | |
228 | { | |
229 | CHECK; | |
230 | speedfactor = (strtod(argv[p],NULL)/100.0); | |
231 | } | |
232 | else if (!strcmp(argv[p],"-h")) | |
233 | { | |
234 | Usage(); | |
235 | Thanks(); | |
236 | } | |
237 | else if (!strcmp(argv[p],"-defaults")) | |
238 | { | |
239 | set_defaults(); | |
240 | } | |
241 | else if (!strcmp(argv[p],"-nomenu")) | |
242 | { | |
243 | nomenu = 1; | |
244 | } | |
245 | else if (!strcmp(argv[p],"-root")) | |
246 | { | |
247 | ForceRoot = 1; | |
248 | } | |
249 | else if (!strcmp(argv[p],"-window-id")) | |
250 | { | |
251 | CHECK; | |
252 | UserWindow = strtod(argv[p],NULL); | |
253 | } | |
254 | else if (!strcmp(argv[p],"-changelog")) | |
255 | { | |
256 | print_changelog(); | |
257 | Thanks(); | |
258 | } | |
259 | #ifdef SELFREP | |
260 | else if (!strcmp(argv[p],"-selfrep")) | |
261 | { | |
262 | selfrep(); | |
263 | exit(0); | |
264 | } | |
265 | #endif | |
266 | else | |
267 | { | |
268 | printf("Not understood: %s\n",argv[p]); | |
269 | Usage(); | |
270 | Thanks(); | |
271 | } | |
272 | } | |
273 | } | |
274 | ||
275 | void set_defaults() | |
276 | { | |
277 | flimit = 10; | |
278 | blimit = 32; | |
279 | if(bcolorstring) | |
280 | free(bcolorstring); | |
281 | bcolorstring = strdup("lightblue"); | |
282 | if(bgcolorstring) | |
283 | free(bgcolorstring); | |
284 | bgcolorstring = strdup("darkblue"); | |
285 | speedfactor = 1.0; | |
286 | } | |
287 | ||
288 | void Usage() | |
289 | { | |
290 | printf("\nUsage: xfishtank [options]\n\n"); | |
291 | printf("Options:\n"); | |
292 | printf(" -b n number of bubbles (default 32)\n"); | |
293 | printf(" -f n number of fish (default 10)\n"); | |
294 | printf(" -bc color color of bubbles (default \"lightblue\")\n"); | |
295 | printf(" -double n 1: double buffering, 0: do not use (default: 1)\n"); | |
296 | printf(" -speed speed of fishes (default: 100)\n"); | |
297 | printf(" -defaults all options to default\n"); | |
298 | printf(" -nomenu do not show menu\n"); | |
299 | printf(" -display DISPLAY to draw on (default \"\")\n"); | |
300 | printf(" -root use root-window or xscreensaver-provided window to draw in\n"); | |
301 | printf(" -window-id window to draw on\n"); | |
302 | printf(" -h show this info\n"); | |
303 | printf(" -changelog show ChangeLog and exit\n"); | |
304 | printf(" -selfrep output gzipped tarfile of the source and exit\n"); | |
305 | printf("\n"); | |
306 | } | |
307 | ||
308 | int do_write_flags(void *dummy) | |
309 | { | |
310 | if (FlagsChanged) | |
311 | { | |
312 | FlagsChanged = 0; | |
313 | WriteFlags(); | |
314 | } | |
315 | return TRUE; | |
316 | (void)dummy; | |
317 | } | |
318 | ||
319 | ||
320 | void erasefish(fish *f) | |
321 | { | |
322 | /* | |
323 | * for something as small as a bubble, it was never worth the | |
324 | * effort of using clipmasks to only turn of the bubble itself, so | |
325 | * we just clear the whole rectangle. | |
326 | */ | |
327 | //XClearArea(Dpy, wid, f->x, f->y, rwidth[f->type], rheight[f->type], False); | |
328 | fishtype *ft = &xfish[f->type][f->direction][f->frame]; | |
329 | XClearArea(Dpy, wid, f->x, f->y, ft->w, ft->h, False); | |
330 | } | |
331 | ||
332 | ||
333 | /* | |
334 | * This function can only be called if DoClipping is True. It is used to | |
335 | * move a clipmasked fish. First the area under the fish is cleared, | |
336 | * and then the new fish is masked in. | |
337 | * The parameters x, y, amd d are from the old fish that is being | |
338 | * erased before the new fish is drawn. | |
339 | */ | |
340 | void movefish(fish *f) | |
341 | { | |
342 | fishtype *ft = &xfish[f->type][f->direction][f->frame]; | |
343 | XSetClipOrigin(Dpy, ft->gc, f->x, f->y); | |
344 | XCopyArea(Dpy, ft->pix, wid,ft->gc,0,0, ft->w, ft->h, f->x, f->y); | |
345 | ||
346 | P("movefish: %#lx %d %d %d %d %d %d %d\n",wid,f->type,f->direction,f->frame,(int)f->x,(int)f->y,ft->w,ft->h); | |
347 | f->animtime --; | |
348 | if (f->animtime <= 0) | |
349 | { | |
350 | setanimtime(f); | |
351 | f->frame = (f->frame + 1)%NUM_FRAMES; | |
352 | } | |
353 | } | |
354 | ||
355 | void setanimtime(fish*f) | |
356 | { | |
357 | f->animtime = 0.75*(drand48()+0.333)*maxanimtime; | |
358 | } | |
359 | ||
360 | void erasebubble(bubble *b) | |
361 | { | |
362 | XClearArea(Dpy, wid, b->x, b->y, b->size, b->size, 0); | |
363 | P("erasebubble: %d %d %d\n",b->x, b->y, b->size); | |
364 | } | |
365 | ||
366 | ||
367 | void putbubble(bubble *b, unsigned long c) | |
368 | { | |
369 | XGCValues gcv; | |
370 | ||
371 | int s = b->size; | |
372 | gcv.foreground = c; | |
373 | gcv.clip_mask = xbubbles[s]; | |
374 | gcv.clip_x_origin = b->x; | |
375 | gcv.clip_y_origin = b->y; | |
376 | XChangeGC(Dpy, bgc, GCForeground | GCClipMask | GCClipXOrigin | GCClipYOrigin, &gcv); | |
377 | XFillRectangle(Dpy, wid, bgc, b->x, b->y, s, s); | |
378 | P("putbubble: %d %d\n", b->x, b->y); | |
379 | } | |
380 | ||
381 | ||
382 | /* | |
383 | Allocate a color by name. | |
384 | */ | |
385 | Pixel AllocNamedColor(Display *display, char *colorName, Pixel dfltPix) | |
386 | { | |
387 | Pixel pix; | |
388 | XColor exactcolor; | |
389 | XColor scrncolor; | |
390 | ||
391 | if (XAllocNamedColor(display, | |
392 | DefaultColormap(display, DefaultScreen(Dpy)), | |
393 | colorName, | |
394 | &scrncolor, | |
395 | &exactcolor)) | |
396 | pix = scrncolor.pixel; | |
397 | else | |
398 | pix = dfltPix; | |
399 | ||
400 | return pix; | |
401 | } | |
402 | ||
403 | ||
404 | ||
405 | //static unsigned char bits[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }; | |
406 | ||
407 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
408 | Calibrate the pixmaps and bitmaps. The right-fish data is coded in xfishy.h, | |
409 | this is transformed to create the left-fish. The eight bubbles are coded | |
410 | in bubbles.h as a two dimensional array. | |
411 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
412 | void init_pixmap() | |
413 | { | |
414 | ||
415 | XWindowAttributes attr; | |
416 | XGetWindowAttributes(Dpy, wid, &attr); | |
417 | ||
418 | XpmAttributes attributes; | |
419 | attributes.valuemask = ( | |
420 | XpmExactColors | XpmCloseness | XpmDepth); | |
421 | ||
422 | attributes.exactColors = False; // does not matter, true or false | |
423 | attributes.closeness = 40000; // recommended in xpm manual | |
424 | attributes.depth = attr.depth; // essential | |
425 | ||
426 | int k; | |
427 | // debugging : print all fishes | |
428 | if(0) | |
429 | for (k=0; k<NUM_FISH*2; k++) | |
430 | { | |
431 | int w,h,c,m,i; | |
432 | sscanf(fishes[k][0],"%d %d %d %d",&w,&h,&c,&m); | |
433 | printf("k %d %d %d %d %d\n",k,w,h,c,m); | |
434 | for (i=0; i<h+c+1; i++) | |
435 | printf("k i: %d %d %s\n",k,i,fishes[k][i]); | |
436 | } | |
437 | //XGCValues gc_values; | |
438 | //gc_values.function = GXcopy; | |
439 | //gc_values.graphics_exposures = False; | |
440 | //gc_values.fill_style = FillTiled; | |
441 | //int gmask = GCFunction | GCFillStyle | GCGraphicsExposures; | |
442 | //(void)gmask; | |
443 | //(void)gc_values; | |
444 | for (k = 0; k < NUM_FISH; k++) | |
445 | { | |
446 | int i; | |
447 | for (i=0; i<2; i++) | |
448 | { | |
449 | int j; | |
450 | for(j=0; j<NUM_FRAMES; j++) | |
451 | { | |
452 | Pixmap pix; | |
453 | fishtype *ft = &xfish[k][i][j]; | |
454 | iXpmCreatePixmapFromData(Dpy, wid, fishes[2*k+j], &ft->pix, &pix, &attributes, i); | |
455 | ft->gc = XCreateGC(Dpy, wid, 0, NULL); | |
456 | //XChangeGC(Dpy, draw_gc, gmask, &gc_values); | |
457 | XSetClipMask(Dpy, ft->gc, pix); | |
458 | XFreePixmap(Dpy,pix); | |
459 | sscanf(fishes[2*k][0],"%d %d",&(ft->w),&(ft->h)); | |
460 | } | |
461 | } | |
462 | } | |
463 | ||
464 | int i; | |
465 | for (i = 1; i <= 8; i++) | |
466 | xbubbles[i] = XCreateBitmapFromData(Dpy, wid, (char *) xbBits[i], i, i); | |
467 | } | |
468 | ||
469 | ||
470 | ||
471 | ||
472 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
473 | Initialize signal so that SIGUSR1 causes secure mode to toggle. | |
474 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
475 | void init_signals() | |
476 | { | |
477 | } | |
478 | ||
479 | ||
480 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
481 | Variety of initialization calls, including getting the window up and running. | |
482 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
483 | void initialize() | |
484 | { | |
485 | XWindowAttributes winfo; | |
486 | XSetWindowAttributes attr; | |
487 | (void)attr; | |
488 | XGCValues vals; | |
489 | int i,j,k; | |
490 | ||
491 | root_window = VirtualRootWindowOfScreen(DefaultScreenOfDisplay(Dpy)); | |
492 | ||
493 | XGetWindowAttributes(Dpy, root_window, &winfo); | |
494 | width = winfo.width; | |
495 | height = winfo.height; | |
496 | ||
497 | DoubleBuf=0; | |
498 | ||
499 | int x,y; | |
500 | unsigned int w,h,b,depth; | |
501 | Window root, searchWin; | |
502 | GtkWidget *gtkwin; | |
503 | XGetGeometry(Dpy,root_window,&root, | |
504 | &x, &y, &w, &h, &b, &depth); | |
505 | gtkwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
506 | gtk_window_set_title (GTK_WINDOW(gtkwin),"Xfishtank-A"); | |
507 | gtk_window_set_skip_taskbar_hint (GTK_WINDOW(gtkwin),TRUE); | |
508 | gtk_window_set_skip_pager_hint (GTK_WINDOW(gtkwin),TRUE); | |
509 | ||
510 | if (ForceRoot) | |
511 | { | |
512 | searchWin = DefaultRootWindow(Dpy); | |
513 | // Are we started by xscreensaver? | |
514 | if (getenv("XSCREENSAVER_WINDOW")) | |
515 | { | |
516 | searchWin = strtol(getenv("XSCREENSAVER_WINDOW"),NULL,0); | |
517 | inxscreensaver = 1; | |
518 | } | |
519 | } | |
520 | else if(UserWindow) | |
521 | { | |
522 | searchWin = UserWindow; | |
523 | } | |
524 | else | |
525 | { | |
526 | int HaveTrans = make_trans_window(gtkwin, | |
527 | 1 /*fullscreen*/, | |
528 | 1 /*sticky*/, | |
529 | 1 /* below*/, | |
530 | 1 /* dock*/ , | |
531 | NULL, | |
532 | &searchWin); | |
533 | if (HaveTrans) | |
534 | { | |
535 | xfishtank_trans = 1; | |
536 | } | |
537 | else | |
538 | { | |
539 | searchWin = root_window; | |
540 | // Maybe, it is LXDE: find window with name pcmanfm | |
541 | char *DesktopSession = NULL; | |
542 | if (getenv("DESKTOP_SESSION")) | |
543 | { | |
544 | DesktopSession = strdup(getenv("DESKTOP_SESSION")); | |
545 | char *a = DesktopSession; | |
546 | while (*a) { *a = toupper(*a); a++; } | |
547 | if (!strncmp(DesktopSession,"LXDE",4)) | |
548 | { | |
549 | Window w = Window_With_Name(Dpy, root_window, "pcmanfm"); | |
550 | if(w) | |
551 | { | |
552 | searchWin = w; | |
553 | printf("LXDE session found, using window pcmanfm\n"); | |
554 | } | |
555 | } | |
556 | } | |
557 | if(DesktopSession) | |
558 | free(DesktopSession); | |
559 | } | |
560 | if(xfishtank_trans) | |
561 | { | |
562 | XMoveWindow(Dpy,searchWin,0,0); | |
563 | XSetWindowBackground(Dpy,searchWin,0); | |
564 | } | |
565 | } | |
566 | ||
567 | P("searchWin: %#lx\n",searchWin); | |
568 | ||
569 | wid = searchWin; | |
570 | ||
571 | xfishtankWin = searchWin; | |
572 | ||
573 | ||
574 | vals.foreground = vals.background = bcolor; | |
575 | vals.graphics_exposures = False; | |
576 | //gc = XCreateGC(Dpy, wid, GCForeground | GCBackground | GCGraphicsExposures, &vals); | |
577 | //pgc = XCreateGC(Dpy, wid, GCForeground | GCBackground | GCGraphicsExposures, &vals); | |
578 | bgc = XCreateGC(Dpy, wid, GCForeground | GCBackground | GCGraphicsExposures, &vals); | |
579 | //draw_gc = XCreateGC(Dpy, wid, 0, NULL); | |
580 | //XGCValues gc_values; | |
581 | //gc_values.function = GXcopy; | |
582 | //gc_values.graphics_exposures = False; | |
583 | //gc_values.fill_style = FillTiled; | |
584 | //XChangeGC(Dpy, draw_gc, GCFunction | GCFillStyle | GCGraphicsExposures, &gc_values); | |
585 | ||
586 | for (i = 0; i < NUM_FISH; i++) | |
587 | for (j=0; j<2; j++) | |
588 | for (k=0; k<NUM_FRAMES; k++) | |
589 | { | |
590 | fishtype *ft = &xfish[i][j][k]; | |
591 | ft->pix = 0; | |
592 | ft->gc = 0; | |
593 | ft->w = 42; | |
594 | ft->h = 42; | |
595 | } | |
596 | ||
597 | init_pixmap(); | |
598 | init_signals(); | |
599 | ||
600 | binfo = (bubble *) malloc(blimit * sizeof(bubble)); | |
601 | //finfo = (fish *) malloc(flimit * sizeof(fish)); | |
602 | ||
603 | wid1 = wid; | |
604 | #ifdef DOUBLE_BUFFER | |
605 | int xdbemajor; | |
606 | int xdbeminor; | |
607 | if (XdbeAvailable == -42) | |
608 | XdbeAvailable = XdbeQueryExtension(Dpy,&xdbemajor,&xdbeminor); | |
609 | #else | |
610 | XdbeAvailable = 0; | |
611 | #endif | |
612 | UseXdbe = XdbeAvailable && WantXdbe; | |
613 | ||
614 | #ifdef DOUBLE_BUFFER | |
615 | //BMETHOD = XdbeBackground; | |
616 | BMETHOD = XdbeBackground; | |
617 | // BMETHOD = XdbeUndefined; | |
618 | // BMETHOD = XdbeUntouched; | |
619 | // BMETHOD = XdbeCopied; // use this to check if dbe works; fishes and bubbles will not be erased | |
620 | ||
621 | if (UseXdbe) | |
622 | { | |
623 | if (backbuf) | |
624 | XdbeDeallocateBackBufferName(Dpy,backbuf); | |
625 | backbuf = XdbeAllocateBackBufferName(Dpy, wid1, BMETHOD); | |
626 | wid = backbuf; | |
627 | printf("Using double buffer: %#lx window: %#lx\n",backbuf,wid1); | |
628 | } | |
629 | else | |
630 | printf("Using window: %#lx\n",wid); | |
631 | #endif | |
632 | ||
633 | if(xfishtank_trans) | |
634 | printf("This is a new transparent window.\n"); | |
635 | else | |
636 | { | |
637 | if(UserWindow) | |
638 | printf("This is an existing window.\n"); | |
639 | else | |
640 | printf("This is an existing window, probably the root window.\n"); | |
641 | } | |
642 | XClearWindow(Dpy,wid1); | |
643 | ||
644 | // drawing on wid, XClearWindow, XSetWindowBackground on wid1 | |
645 | } | |
646 | ||
647 | ||
648 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
649 | Create a new bubble. Placement along the x axis is random, as is the size of | |
650 | the bubble. Increment value is determined by speed. | |
651 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
652 | void new_bubble(bubble *b, int init) // init=0: postions at bottom, else random | |
653 | { | |
654 | int s; | |
655 | ||
656 | b->x = 5+(width-10) * drand48(); | |
657 | if (init) | |
658 | b->y = (height / 16) * (16*drand48() + 1) - 1; | |
659 | else | |
660 | b->y = height - 1; | |
661 | b->size = s = 1.0 + 8*drand48(); | |
662 | if ((b->bstep = smooth * height / (float) binc[s]) == 0) | |
663 | b->bstep = 1; | |
664 | P("newbubble %d %d\n",b->x, b->y); | |
665 | } | |
666 | ||
667 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
668 | Erase old bubbles, move and draw new bubbles. Random left-right factor | |
669 | can move bubble one size-unit in either direction. | |
670 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
671 | void step_bubbles() | |
672 | { | |
673 | int i, j, s; | |
674 | (void)j; | |
675 | (void)s; | |
676 | bubble *b; | |
677 | ||
678 | for (i = 0; i < blimit; i++) | |
679 | { | |
680 | b = &binfo[i]; | |
681 | s = b->size; | |
682 | /* clear */ | |
683 | /* | |
684 | if ((b->y > 0) && (b->erased == 0)) | |
685 | { | |
686 | erasebubble(b, s); | |
687 | } | |
688 | */ | |
689 | if ((b->y -= b->bstep) > 0) | |
690 | { | |
691 | double r = drand48(); | |
692 | if (r < 0.25) { | |
693 | b->x -= 1; | |
694 | } else if (r > 0.75) { | |
695 | b->x += 1; | |
696 | } | |
697 | putbubble(b, bcolor); | |
698 | } else { | |
699 | if (drand48() < 0.25) { | |
700 | new_bubble(b,0); | |
701 | } | |
702 | } | |
703 | } | |
704 | XFlush(Dpy); | |
705 | } | |
706 | ||
707 | ||
708 | ||
709 | ||
710 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
711 | Create a new fish. Placement along the y axis is random, as is the side | |
712 | >from which the fish appears. Direction is determined from side. Increment | |
713 | is also random. | |
714 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
715 | void new_fish(fish *f0, int init) // init 0: position at edge, else position at random | |
716 | { | |
717 | int i, collide; | |
718 | fish *f = f0; | |
719 | ||
720 | f->type = drand48() * NUM_FISH; | |
721 | fishtype *ft = &xfish[f->type][0][0]; | |
722 | for (i = 0, collide = 1; (i < 16) && (collide); i++) | |
723 | { | |
724 | f->y = (height - ft->h) * drand48(); | |
725 | if ((f->fstep = smooth * width / (8.0 * (1.0 + 8*drand48()))) == 0) | |
726 | f->fstep = 1; | |
727 | } | |
728 | if (drand48() < 0.5) | |
729 | { | |
730 | f->direction = 0; | |
731 | if (init) | |
732 | f->x = ft->w + drand48()*width; | |
733 | else | |
734 | f->x = width; | |
735 | } | |
736 | else | |
737 | { | |
738 | f->direction = 1; | |
739 | if (init) | |
740 | f->x = drand48()*width-ft->w; | |
741 | else | |
742 | f->x = -ft->w; | |
743 | } | |
744 | ||
745 | f->frame = 0; | |
746 | setanimtime(f); | |
747 | P("newfish %d %d\n",(int)f->x,(int)f->y); | |
748 | } | |
749 | ||
750 | void remove_all_fishes() | |
751 | { | |
752 | int i; | |
753 | for (i = 0; i < flimit; i++) | |
754 | erasefish(&finfo[i]); | |
755 | XFlush(Dpy); | |
756 | } | |
757 | ||
758 | void remove_all_bubbles() | |
759 | { | |
760 | int i; | |
761 | for (i = 0; i < blimit; i++) | |
762 | erasebubble(&binfo[i]); | |
763 | XFlush(Dpy); | |
764 | } | |
765 | ||
766 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
767 | Move all the fish. Clearing old fish is accomplished by masking only the | |
768 | exposed areas of the old fish. Random up-down factor can move fish 1/4 a | |
769 | fish height in either direction, if no collisions are caused. | |
770 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
771 | void move_fish() | |
772 | { | |
773 | int i, y, done = 0; | |
774 | fish *f; | |
775 | ||
776 | for (i = 0; i < flimit; i++) | |
777 | { | |
778 | f = &finfo[i]; | |
779 | fishtype *ft = &xfish[f->type][f->direction][f->frame]; | |
780 | if (f->direction == 0) | |
781 | done = ((f->x -= speedfactor*f->fstep) < -ft->w); | |
782 | else if (f->direction == 1) | |
783 | done = ((f->x += speedfactor*f->fstep) > width); | |
784 | ||
785 | if(0) // spontaneous change direction now and then | |
786 | if (f->x > ft->w && f->x < width - 2*ft->w && drand48() < 0.001) | |
787 | { | |
788 | f->direction = 1-f->direction; | |
789 | P("direction: %d\n",f->direction); | |
790 | } | |
791 | if (1) | |
792 | { | |
793 | if (!done) | |
794 | { | |
795 | double r = drand48(); | |
796 | if (r < 0.25) | |
797 | y = f->fstep / 4; | |
798 | else if (r > 0.75) | |
799 | y = f->fstep / -4; | |
800 | else | |
801 | y = 0; | |
802 | ||
803 | if (y) | |
804 | f->y += y; | |
805 | movefish(f); | |
806 | } | |
807 | else | |
808 | { | |
809 | erasefish(f); | |
810 | new_fish(f,0); | |
811 | } | |
812 | } | |
813 | } | |
814 | XFlush(Dpy); | |
815 | } | |
816 | ||
817 | int do_move(void *dummy) | |
818 | { | |
819 | (void)dummy; | |
820 | ||
821 | if (Done) | |
822 | { | |
823 | gtk_main_quit(); | |
824 | return FALSE; | |
825 | } | |
826 | ||
827 | #ifdef DOUBLE_BUFFER | |
828 | if (UseXdbe) | |
829 | { | |
830 | XdbeSwapInfo swapInfo; | |
831 | swapInfo.swap_window = wid1; | |
832 | swapInfo.swap_action = BMETHOD; | |
833 | XdbeSwapBuffers(Dpy, &swapInfo, 1); | |
834 | if(xfishtank_trans) | |
835 | XSetWindowBackground(Dpy,wid1,0); | |
836 | } | |
837 | else | |
838 | { | |
839 | XFlush(Dpy); | |
840 | remove_all_bubbles(); | |
841 | remove_all_fishes(); | |
842 | } | |
843 | #else | |
844 | XFlush(Dpy); | |
845 | remove_all_bubbles(); | |
846 | remove_all_fishes(); | |
847 | #endif | |
848 | move_fish(); | |
849 | step_bubbles(); | |
850 | return TRUE; | |
851 | ||
852 | } | |
853 | ||
854 | int do_testfish(void *dummy) | |
855 | { | |
856 | (void)dummy; | |
857 | ||
858 | static int x = 100; | |
859 | static int y = 100; | |
860 | int w = 64; | |
861 | int h = 41; | |
862 | w= 64; | |
863 | h=59; | |
864 | XClearArea(Dpy, wid, x,y,w,h,0); | |
865 | x += 1; | |
866 | y += 1; | |
867 | if (x > 500) | |
868 | { | |
869 | x = 100; | |
870 | y = 100; | |
871 | } | |
872 | ||
873 | GC gc = xfish[0][1][0].gc; | |
874 | XSetClipOrigin(Dpy, gc, x, y); | |
875 | XCopyArea(Dpy, xfish[0][1][0].pix, wid, gc,0,0,w,h,x,y); | |
876 | XFlush(Dpy); | |
877 | return TRUE; | |
878 | } | |
879 | ||
880 | void create_fishes(int n) | |
881 | { | |
882 | int i; | |
883 | static int prev = 0; | |
884 | flimit = n; | |
885 | finfo = (fish *)realloc(finfo,sizeof(fish)*flimit); | |
886 | for (i = prev; i < flimit; i++) | |
887 | new_fish(&finfo[i],1); | |
888 | prev = flimit; | |
889 | } | |
890 | ||
891 | void create_bubbles(int n) | |
892 | { | |
893 | int i; | |
894 | static int prev = 0; | |
895 | blimit = n; | |
896 | binfo = (bubble *)realloc(binfo,sizeof(bubble)*blimit); | |
897 | for (i = prev; i < blimit; i++) | |
898 | new_bubble(&binfo[i],1); | |
899 | prev = blimit; | |
900 | } | |
901 | ||
902 | void setspeed(float s) | |
903 | { | |
904 | speedfactor = s; | |
905 | } | |
906 | ||
907 | void setbcolor() | |
908 | { | |
909 | bcolor = AllocNamedColor(Dpy,bcolorstring,white)|0xff000000; | |
910 | } | |
911 | ||
912 | void setbgcolor() | |
913 | { | |
914 | if (inxscreensaver && !xfishtank_trans && bgcolorstring && strlen(bgcolorstring)) | |
915 | { | |
916 | P("bgcolorstring '%s'\n",bgcolorstring); | |
917 | bgcolor = AllocNamedColor(Dpy,bgcolorstring,black)|0xff000000; | |
918 | XSetWindowBackground(Dpy, wid1, bgcolor); | |
919 | XClearWindow(Dpy,wid1); | |
920 | } | |
921 | } | |
922 | ||
923 | ||
924 | void Thanks() | |
925 | { | |
926 | if (HaltedByInterrupt) | |
927 | printf("\nxfishtank: Caught signal %d\n",HaltedByInterrupt); | |
928 | if (Dpy) | |
929 | { | |
930 | XClearWindow(Dpy,wid1); | |
931 | XFlush(Dpy); | |
932 | } | |
933 | printf("\nThank you for using xfishtank\n"); | |
934 | exit(0); | |
935 | } | |
936 | ||
937 | void SigHandler(int signum) | |
938 | { | |
939 | HaltedByInterrupt = signum; | |
940 | Done = 1; | |
941 | } | |
942 | ||
943 | ||
944 | ||
945 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | |
946 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
947 | int main(int argc, char *argv[]) | |
948 | { | |
949 | // Circumvent wayland problems:before starting gtk: make sure that the | |
950 | // gdk-x11 backend is used. | |
951 | ||
952 | setenv("GDK_BACKEND","x11",1); | |
953 | signal(SIGINT, SigHandler); | |
954 | signal(SIGTERM, SigHandler); | |
955 | signal(SIGHUP, SigHandler); | |
956 | ||
957 | parse(argc, argv); | |
958 | printf("xfishtank version %s\n",VERSION); | |
959 | printf("Comments to: %s\n",PACKAGE_BUGREPORT); | |
960 | printf("Url: %s\n",PACKAGE_URL); | |
961 | srand48(time(NULL)); | |
962 | counter = 0; | |
963 | gtk_init(&argc, &argv); | |
964 | set_defaults(); | |
965 | ReadFlags(); | |
966 | Dpy = XOpenDisplay(display_name); | |
967 | if(!Dpy) | |
968 | { | |
969 | char *name = display_name; | |
970 | if (!display_name) | |
971 | { | |
972 | if(getenv("DISPLAY")) | |
973 | name = getenv("DISPLAY"); | |
974 | } | |
975 | printf("Cannot open display: '%s'\n",name); | |
976 | Thanks(); | |
977 | exit(1); | |
978 | } | |
979 | screen = DefaultScreen(Dpy); | |
980 | ||
981 | white = WhitePixel(Dpy, screen); | |
982 | black = BlackPixel(Dpy, screen); | |
983 | ||
984 | initialize(); | |
985 | ||
986 | setbcolor(); | |
987 | setbgcolor(); | |
988 | ||
989 | srand((unsigned) getpid()); | |
990 | ||
991 | create_bubbles(blimit); | |
992 | ||
993 | create_fishes(flimit); | |
994 | ||
995 | ui(); | |
996 | ||
997 | g_timeout_add_full(G_PRIORITY_DEFAULT, 30, do_move ,NULL, NULL); | |
998 | g_timeout_add_full(G_PRIORITY_DEFAULT, 500, do_write_flags ,NULL, NULL); | |
999 | ||
1000 | if(0) | |
1001 | g_timeout_add_full(G_PRIORITY_DEFAULT, 40, do_testfish ,NULL, NULL); | |
1002 | ||
1003 | if (nomenu) | |
1004 | iconify(); | |
1005 | gtk_main(); | |
1006 | ||
1007 | Thanks(); | |
1008 | ||
1009 | return 0; | |
1010 | } | |
1011 | ||
1012 | void print_changelog() | |
1013 | { | |
1014 | #include "changelog.inc" | |
1015 | } | |
1016 | ||
1017 | #ifdef SELFREP | |
1018 | static unsigned char tarfile[] = { | |
1019 | #include "tarfile.inc" | |
1020 | }; | |
1021 | void selfrep() | |
1022 | { | |
1023 | if(sizeof(tarfile) > 1000 && isatty(fileno(stdout))) | |
1024 | { | |
1025 | printf("Not sending tar file to terminal.\n"); | |
1026 | printf("Try redirecting to a file (e.g: xpenguins -selfrep > xpenguins.tar.gz),\n"); | |
1027 | printf("or use a pipe (e.g: xpenguins -selfrep | tar zxf -).\n"); | |
1028 | } | |
1029 | else | |
1030 | { | |
1031 | ssize_t rc = mywrite(fileno(stdout),tarfile,sizeof(tarfile)); | |
1032 | if (rc < 0) | |
1033 | fprintf(stderr,"xpenguins: Problems encountered during production of the tar ball.\n"); | |
1034 | } | |
1035 | } | |
1036 | #endif | |
1037 |