New upstream version 3.1.1
[xfishtank.git] / src / ui.c
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 #include <gtk/gtk.h>
29 #include <math.h>
30 #include "ui_xml.h"
31 #include "ui.h"
32 #include "debug.h"
33 #include "xfishtank.h"
34 #include "utils.h"
35 #include "fishes.h"
36
37 //#include "xfishtank.xpm"
38
39 #ifdef HAVE_CONFIG_H
40 #include "config.h"
41 #endif
42
43 #ifdef __cplusplus
44 #define MODULE_EXPORT extern "C" G_MODULE_EXPORT
45 #else
46 #define MODULE_EXPORT G_MODULE_EXPORT
47 #endif
48
49 int FlagsChanged = 1;
50
51 static int human_action = 1; /* is it a human who is playing with the buttons? */
52
53 #define NTYPES 16
54 static GtkBuilder *builder;
55 static GtkWidget *hauptfenster;
56 static GtkStyleContext *hauptfenstersc;
57 static char buf[100];
58 static GtkWidget *id_fishes;
59 static GtkWidget *id_fishesvalue;
60 static GtkWidget *id_speed;
61 static GtkWidget *id_speedvalue;
62 static GtkWidget *id_bubbles;
63 //static GtkWidget *id_squish;
64 static GtkWidget *id_logo1;
65 static GtkWidget *id_logo2;
66 static GtkWidget *id_logo3;
67 static GtkWidget *id_bubble_color;
68 static GtkWidget *id_bg_color;
69 static GtkWidget *id_bubblesvalue;
70 //static GtkWidget *id_squish_picture;
71
72 static int logo[3];
73 static int flip[3];
74
75 static void handle_css(void);
76 static void init_ids(void);
77 static void init_pixmaps(void);
78 static FILE *openflagsfile(char *mode);
79 static float scale_to_bubblesfactor(float scale);
80 static void ab(float Max, float Min, float *a, float *b);
81 static float fishes_to_scale(float n);
82 static float bubblesfactor_to_scale(float n);
83 static float scale_to_fishes(float scale);
84 static float speedfactor_to_scale(float n);
85 static float scale_to_speedfactor(float scale);
86 static void init_pixmap(GtkWidget *id, int n, int flip);
87 static int do_animate(void *dummy);
88
89
90 // Sometimes it is good to have a logarithmic scale, such that
91 //
92 // V = a*M*10**s + b
93 //
94 // where: V = parameter (e.g. SpeedFactor)
95 // M = desired maximum of V (e.g. 4.0)
96 // s = value of the gtkscale (0 .. 1.0)
97 // Furthermore: m = desired minimum of V (e.g. 0.2)
98 //
99 // Then:
100 // a = (M - m)/(9*M)
101 // b = m - a*M
102 //
103 // The placement of a is a logical choice, the placement of b is
104 // more or less random. I need a constant next to a, because I want
105 // to define minimum V AND maximum V.
106 //
107 // Given V, compute s:
108 //
109 // s = log10((V-b)/(a*M))
110 //
111
112 void ab(float Max, float Min, float *a, float *b)
113 {
114 *a = (Max - Min)/(9.0*Max);
115 *b = Min - (*a)*Max;
116 }
117
118 static const float MaxBubbles = 500;
119 static const float MinBubbles = 1;
120
121 static const float MaxFishes = 200;
122 static const float MinFishes = 1.0;
123
124 static const float MaxSpeed = 400;
125 static const float MinSpeed = 30;
126
127 void handle_css()
128 {
129 }
130
131
132 void show_dialog(int type, const char *format, const char *text)
133 {
134 GtkMessageType message_type;
135 if (type == 1)
136 message_type = GTK_MESSAGE_ERROR;
137 else
138 message_type = GTK_MESSAGE_INFO;
139
140 GtkWidget *m = gtk_message_dialog_new(GTK_WINDOW(hauptfenster),
141 GTK_DIALOG_MODAL,
142 message_type,
143 GTK_BUTTONS_OK,
144 format,
145 text
146 );
147 g_signal_connect(m,"response",G_CALLBACK(gtk_main_quit),NULL);
148 gtk_widget_show_all(m);
149 }
150
151 void init_ids()
152 {
153 id_fishes = GTK_WIDGET(gtk_builder_get_object(builder, "id-fishes"));
154 id_fishesvalue = GTK_WIDGET(gtk_builder_get_object(builder, "id-fishesvalue"));
155 id_speed = GTK_WIDGET(gtk_builder_get_object(builder, "id-speed"));
156 id_speedvalue = GTK_WIDGET(gtk_builder_get_object(builder, "id-speedvalue"));
157 id_logo1 = GTK_WIDGET(gtk_builder_get_object(builder, "id-logo1"));
158 id_logo2 = GTK_WIDGET(gtk_builder_get_object(builder, "id-logo2"));
159 id_logo3 = GTK_WIDGET(gtk_builder_get_object(builder, "id-logo3"));
160 id_bubble_color = GTK_WIDGET(gtk_builder_get_object(builder, "id-bubble-color"));
161 id_bg_color = GTK_WIDGET(gtk_builder_get_object(builder, "id-bg-color"));
162 id_bubbles = GTK_WIDGET(gtk_builder_get_object(builder, "id-bubbles"));
163 //id_squish = GTK_WIDGET(gtk_builder_get_object(builder, "id-squish"));
164 id_bubblesvalue = GTK_WIDGET(gtk_builder_get_object(builder, "id-bubblesvalue"));
165 //id_squish_picture = GTK_WIDGET(gtk_builder_get_object(builder, "id-squish-picture"));
166 }
167
168 int do_animate(void *dummy)
169 {
170 (void)dummy;
171 int m = drand48()*3;
172 P("m: %d %d\n",m, logo[m]);
173 // needs adjustment if NUM_FRAMES != 2
174 if (logo[m]%NUM_FRAMES)
175 logo[m]--;
176 else
177 logo[m]++;
178 GtkWidget *id;
179 switch(m)
180 {
181 case 0:
182 id = id_logo1;
183 break;
184 case 1:
185 id = id_logo2;
186 break;
187 case 2:
188 id = id_logo3;
189 break;
190 }
191 init_pixmap(id, logo[m], flip[m]);
192
193 return TRUE;
194 }
195
196 void init_pixmap(GtkWidget *id, int n, int flip)
197 {
198 int w,h;
199 float w0,h0;
200
201 GdkPixbuf *pixbuf, *pixbuf1, *pixbuf2;
202 pixbuf = gdk_pixbuf_new_from_xpm_data ((const char **)fishes[n]);
203 w0 = gdk_pixbuf_get_width(pixbuf);
204 h0 = gdk_pixbuf_get_height(pixbuf);
205 if (w0 > h0)
206 {
207 w = 64;
208 h = h0/w0*w;
209 }
210 else
211 {
212 h = 64;
213 w = w0/h0*h;
214 }
215
216 pixbuf1 = gdk_pixbuf_scale_simple(pixbuf,w,h,GDK_INTERP_BILINEAR);
217 g_object_unref(pixbuf);
218
219 if(flip)
220 {
221 pixbuf2 = gdk_pixbuf_flip(pixbuf1,1);
222 gtk_image_set_from_pixbuf(GTK_IMAGE(id),pixbuf2);
223 g_object_unref(pixbuf2);
224 }
225 else
226 gtk_image_set_from_pixbuf(GTK_IMAGE(id),pixbuf1);
227
228 g_object_unref(pixbuf1);
229 }
230
231 void init_pixmaps()
232 {
233 int i;
234 for (i=0; i<3; i++)
235 {
236 logo[i] = NUM_FRAMES*(int)(drand48()*NUM_FISH);
237 flip[i] = drand48()*2;
238 }
239 init_pixmap(id_logo1,logo[0],flip[0]);
240 init_pixmap(id_logo2,logo[1],flip[1]);
241 init_pixmap(id_logo3,logo[2],flip[2]);
242 }
243
244 void set_buttons()
245 {
246 int h = human_action;
247 human_action = 0;
248
249 gtk_range_set_value(GTK_RANGE(id_fishes), fishes_to_scale(flimit));
250 sprintf(buf,"%d",flimit);
251 gtk_label_set_text(GTK_LABEL(id_fishesvalue),buf);
252
253 gtk_range_set_value(GTK_RANGE(id_bubbles),bubblesfactor_to_scale(blimit));
254 sprintf(buf,"%d",(int)(blimit));
255 gtk_label_set_text(GTK_LABEL(id_bubblesvalue),buf);
256
257 gtk_range_set_value(GTK_RANGE(id_speed),speedfactor_to_scale(100*speedfactor));
258 P("speedfactor: %f %f\n",speedfactor, speedfactor_to_scale(100*speedfactor));
259 sprintf(buf,"%d",(int)(100*speedfactor));
260 gtk_label_set_text(GTK_LABEL(id_speedvalue),buf);
261
262 GdkRGBA color;
263 P("bcolor: %s\n",bcolorstring);
264 gdk_rgba_parse(&color, bcolorstring);
265 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(id_bubble_color),&color);
266
267 P("bgcolor: %s\n",bgcolorstring);
268 gdk_rgba_parse(&color, bgcolorstring);
269 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(id_bg_color),&color);
270
271 human_action = h;
272
273 FlagsChanged = 1;
274 }
275
276
277 MODULE_EXPORT void button_fishes(GtkWidget *w)
278 {
279 if (!human_action)
280 return;
281 float value = gtk_range_get_value(GTK_RANGE(w));
282 P("fishes: %f\n",value);
283 create_fishes(scale_to_fishes(value));
284 FlagsChanged = 1;
285
286 sprintf(buf,"%d",flimit);
287 gtk_label_set_text(GTK_LABEL(id_fishesvalue),buf);
288 }
289
290 MODULE_EXPORT void button_bubbles(GtkWidget *w)
291 {
292 if (!human_action)
293 return;
294 float value = gtk_range_get_value(GTK_RANGE(w));
295 P("bubbles: %f %d\n",value,blimit);
296 create_bubbles(scale_to_bubblesfactor(value));
297 FlagsChanged = 1;
298
299 sprintf(buf,"%d",blimit);
300 gtk_label_set_text(GTK_LABEL(id_bubblesvalue),buf);
301 }
302
303 MODULE_EXPORT void button_speed(GtkWidget *w)
304 {
305 if (!human_action)
306 return;
307 float value = gtk_range_get_value(GTK_RANGE(w));
308 P("speedfactor: %f %f\n",value,speedfactor);
309 setspeed(scale_to_speedfactor(value));
310 P("speedfactor: %f\n",speedfactor);
311 FlagsChanged = 1;
312
313 sprintf(buf,"%d",(int)(100*speedfactor));
314 gtk_label_set_text(GTK_LABEL(id_speedvalue),buf);
315 }
316
317
318 MODULE_EXPORT void button_defaults()
319 {
320 set_defaults();
321 create_fishes(flimit);
322 create_bubbles(blimit);
323 setbcolor();
324 setbgcolor();
325 setspeed(speedfactor);
326 if (!human_action)
327 return;
328 set_buttons();
329 }
330
331 #if 0
332 MODULE_EXPORT void button_squish(GtkWidget *w)
333 {
334 if (!human_action)
335 return;
336 int value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w));
337 P("squish: %d\n",value);
338 handle_squish(value);
339 FlagsChanged = 1;
340 }
341 #endif
342 MODULE_EXPORT void button_iconify()
343 {
344 if (!human_action)
345 return;
346 gtk_window_iconify(GTK_WINDOW(hauptfenster));
347 }
348
349 MODULE_EXPORT void button_bubble_color(GtkWidget *w)
350 {
351 GdkRGBA color;
352 if (!human_action)
353 return;
354 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(w),&color);
355 free(bcolorstring);
356 rgba2color(&color,&bcolorstring);
357 setbcolor();
358 FlagsChanged = 1;
359 }
360
361 MODULE_EXPORT void button_bg_color(GtkWidget *w)
362 {
363 GdkRGBA color;
364 if (!human_action)
365 return;
366 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(w),&color);
367 free(bgcolorstring);
368 rgba2color(&color,&bgcolorstring);
369 setbgcolor();
370 FlagsChanged = 1;
371 }
372
373 #if 0
374 MODULE_EXPORT void button_guts_color(GtkWidget *w)
375 {
376 GdkRGBA color;
377 if (!human_action)
378 return;
379 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(w),&color);
380 free(gutsColor);
381 rgba2color(&color,&gutsColor);
382 setgutsgcColor();
383 FlagsChanged = 1;
384 }
385 #endif
386
387 #if 0
388 MODULE_EXPORT void button_squish_color(GtkWidget *w)
389 {
390 GdkRGBA color;
391 if (!human_action)
392 return;
393 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(w),&color);
394 free(gutsColor);
395 rgba2color(&color,&gutsColor);
396 }
397 #endif
398
399 FILE *openflagsfile(char *mode)
400 {
401 char *h = getenv("HOME");
402 if (h == NULL)
403 return NULL;
404 char *flagsfile = (char*)malloc((strlen(h)+strlen(FLAGSFILE)+2)*sizeof(char));
405 flagsfile[0] = 0;
406 strcat(flagsfile,h);
407 strcat(flagsfile,"/");
408 strcat(flagsfile,FLAGSFILE);
409 FILE *f = fopen(flagsfile,mode);
410 P("openflagsfile %s %s\n",flagsfile,mode);
411 free(flagsfile);
412 return f;
413 }
414
415 void ReadFlags()
416 {
417 FILE *f = openflagsfile(_("r"));
418 if (f == NULL)
419 {
420 I("Cannot read $HOME/%s\n",FLAGSFILE);
421 return;
422 }
423 int lineno = 1;
424 while(1)
425 {
426 char *line = NULL;
427 size_t n = 0;
428 int m = getline(&line,&n,f);
429 if (m<0)
430 break;
431 P("ReadFlags: %d [%s]\n",lineno,line);
432 char *flag = (char*)malloc((strlen(line)+1)*sizeof(char));
433 m = sscanf(line, "%s", flag);
434 if (m == EOF || m == 0)
435 continue;
436 char *rest = line + strlen(flag);
437
438 char *p;
439 p = rest;
440 while (*p == ' ' || *p == '\t' || *p == '\n')
441 p++;
442 rest = p;
443 p = &line[strlen(line)-1];
444 while (*p == ' ' || *p == '\t' || *p == '\n')
445 p--;
446 *(p+1) = 0;
447
448 P("ReadFlags: %s [%s]\n",flag,rest);
449 if(!strcmp(flag,"fishes"))
450 {
451 flimit = atoi(rest);
452 P("flimit: %d\n",flimit);
453 }
454 else if(!strcmp(flag,"bubbles"))
455 {
456 blimit = atoi(rest);
457 P("bubbles: %d\n",blimit);
458 }
459 else if(!strcmp(flag,"speed"))
460 {
461 speedfactor = atoi(rest)/100.0;
462 P("speedfactor: %d\n",(int)(100*speedfactor));
463 }
464 else if(!strcmp(flag,"bc"))
465 {
466 if(bcolorstring)
467 free(bcolorstring);
468 bcolorstring = strdup(rest);
469 P("bcolorstring: %s\n",bcolorstring);
470 }
471 else if(!strcmp(flag,"bgc"))
472 {
473 if(bgcolorstring)
474 free(bgcolorstring);
475 bgcolorstring = strdup(rest);
476 P("bcolorstring: %s\n",bgcolorstring);
477 }
478 lineno++;
479 free(line);
480 free(flag);
481 }
482 fclose(f);
483 }
484
485 void WriteFlags()
486 {
487 FILE *f = openflagsfile(_("w"));
488 if (f == NULL)
489 {
490 I("Cannot write $HOME/%s\n",FLAGSFILE);
491 return;
492 }
493 fprintf(f, "fishes %d\n", flimit);
494 fprintf(f, "bubbles %d\n", blimit);
495 if (bcolorstring && strlen(bcolorstring))
496 fprintf(f, "bc %s\n", bcolorstring);
497 if(bgcolorstring && strlen(bgcolorstring))
498 fprintf(f, "bgc %s\n", bgcolorstring);
499 fprintf(f, "speed %d\n", (int)(100*speedfactor));
500 fclose(f);
501 init_pixmaps();
502 }
503
504 float bubblesfactor_to_scale(float n)
505 {
506 float a,b;
507 ab(MaxBubbles, MinBubbles,&a,&b);
508 return mylog10f((n-b)/(a*MaxBubbles));
509 }
510
511
512 float scale_to_bubblesfactor(float scale)
513 {
514 float a,b;
515 ab(MaxBubbles, MinBubbles,&a,&b);
516 return a * MaxBubbles*myexp10f(scale) + b;
517 }
518
519 float fishes_to_scale(float n)
520 {
521 float a,b;
522 ab(MaxFishes, MinFishes, &a, &b);
523 float rc = mylog10f((n-b)/(a*MaxFishes));
524 P("ftos %f %f\n",n,rc);
525 return rc;
526 }
527
528 float scale_to_fishes(float scale)
529 {
530 float a,b;
531 ab(MaxFishes, MinFishes, &a, &b);
532 return a * MaxFishes*myexp10f(scale) + b;
533 }
534
535 float speedfactor_to_scale(float n)
536 {
537 float a,b;
538 ab(MaxSpeed, MinSpeed, &a, &b);
539 float rc = mylog10f((n-b)/(a*MaxSpeed));
540 P("stos %f %f\n",n,rc);
541 return rc;
542 }
543
544 float scale_to_speedfactor(float scale)
545 {
546 float a,b;
547 ab(MaxSpeed, MinSpeed, &a, &b);
548 return 0.01*(a * MaxSpeed*myexp10f(scale) + b);
549 }
550
551 void iconify()
552 {
553 gtk_window_iconify(GTK_WINDOW(hauptfenster));
554 }
555
556 void ui()
557 {
558 P("here is uiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii\n");
559
560 builder = gtk_builder_new_from_string (xfishtank_xml, -1);
561 gtk_builder_connect_signals (builder, builder);
562 hauptfenster = GTK_WIDGET(gtk_builder_get_object(builder, "hauptfenster"));
563
564 hauptfenstersc = gtk_widget_get_style_context(hauptfenster);
565
566 handle_css();
567 char wtitle[100];
568 wtitle[0] = 0;
569 strcat(wtitle,"XfishtanK");
570 #ifdef HAVE_CONFIG_H
571 strcat(wtitle,"-");
572 strncat(wtitle,VERSION,99 - strlen(wtitle));
573 #endif
574 gtk_window_set_title(GTK_WINDOW(hauptfenster),wtitle);
575 gtk_window_set_resizable(GTK_WINDOW(hauptfenster),False);
576 gtk_widget_show_all (hauptfenster);
577 g_signal_connect (GTK_WINDOW(hauptfenster), "delete-event", G_CALLBACK (gtk_main_quit), NULL);
578 g_signal_connect (GTK_WINDOW(hauptfenster), "destroy", G_CALLBACK (gtk_main_quit), NULL);
579
580 init_ids();
581
582 init_pixmaps();
583 set_buttons();
584 g_timeout_add_full(G_PRIORITY_DEFAULT, 150, do_animate ,NULL, NULL);
585 }
586
This page took 0.039879 seconds and 4 git commands to generate.