1     // FLTK GUI related functions
2 //
3 // Copyright 2007-2018 by Daniel Noethen.
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2, or (at your option)
8 // any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 #include <stdlib.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <unistd.h>
19 #include <signal.h>
20 #include <time.h>
21 #include <sys/stat.h>
22 
23 #ifndef WIN32
24  #include <sys/wait.h>
25 #endif
26 
27 #include "gettext.h"
28 #include "config.h"
29 
30 #include "cfg.h"
31 #include "butt.h"
32 #include "util.h"
33 #include "port_audio.h"
34 #include "timer.h"
35 #include "flgui.h"
36 #include "fl_funcs.h"
37 #include "shoutcast.h"
38 #include "icecast.h"
39 #include "strfuncs.h"
40 
41 
fill_cfg_widgets(void)42 void fill_cfg_widgets(void)
43 {
44     int i;
45 
46     int bitrate[] = { 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112,
47                     128, 160, 192, 224, 256, 320, 0 };
48 
49 #ifndef HAVE_LIBFDK_AAC
50     fl_g->menu_item_cfg_aac->hide();
51     fl_g->menu_item_rec_aac->hide();
52     Fl::check();
53 #endif
54 
55     //fill the main section
56     for(i = 0; i < cfg.audio.dev_count; i++)
57         fl_g->choice_cfg_dev->add(cfg.audio.pcm_list[i]->name);
58 
59     fl_g->choice_cfg_dev->value(cfg.audio.dev_num);
60 
61     fl_g->choice_cfg_act_srv->clear();
62     fl_g->choice_cfg_act_srv->redraw();
63     for(i = 0; i < cfg.main.num_of_srv; i++)
64         fl_g->choice_cfg_act_srv->add(cfg.srv[i]->name);
65 
66     if(cfg.main.num_of_srv > 0)
67     {
68         fl_g->button_cfg_edit_srv->activate();
69         fl_g->button_cfg_del_srv->activate();
70         fl_g->choice_cfg_act_srv->activate();
71     }
72     else
73     {
74         fl_g->button_cfg_edit_srv->deactivate();
75         fl_g->button_cfg_del_srv->deactivate();
76         fl_g->choice_cfg_act_srv->deactivate();
77     }
78 
79     fl_g->choice_cfg_act_icy->clear();
80     fl_g->choice_cfg_act_icy->redraw();
81     for(i = 0; i < cfg.main.num_of_icy; i++)
82         fl_g->choice_cfg_act_icy->add(cfg.icy[i]->name);
83 
84     if(cfg.main.num_of_icy > 0)
85     {
86         fl_g->button_cfg_edit_icy->activate();
87         fl_g->button_cfg_del_icy->activate();
88         fl_g->choice_cfg_act_icy->activate();
89 
90     }
91     else
92     {
93         fl_g->button_cfg_edit_icy->deactivate();
94         fl_g->button_cfg_del_icy->deactivate();
95         fl_g->choice_cfg_act_icy->deactivate();
96     }
97 
98     fl_g->choice_cfg_act_srv->value(cfg.selected_srv);
99     fl_g->choice_cfg_act_icy->value(cfg.selected_icy);
100 
101 
102     fl_g->check_cfg_connect->value(cfg.main.connect_at_startup);
103     fl_g->check_cfg_force_reconnecting->value(cfg.main.force_reconnecting);
104 
105     fl_g->input_log_filename->value(cfg.main.log_file);
106 
107     fl_g->check_update_at_startup->value(cfg.main.check_for_update);
108 
109     //fill the audio section
110     if(!strcmp(cfg.audio.codec, "mp3"))
111         fl_g->choice_cfg_codec->value(CHOICE_MP3);
112     else if(!strcmp(cfg.audio.codec, "ogg"))
113         fl_g->choice_cfg_codec->value(CHOICE_OGG);
114     else if(!strcmp(cfg.audio.codec, "opus"))
115         fl_g->choice_cfg_codec->value(CHOICE_OPUS);
116     else if(!strcmp(cfg.audio.codec, "aac"))
117         fl_g->choice_cfg_codec->value(CHOICE_AAC);
118     else if(!strcmp(cfg.audio.codec, "flac"))
119     {
120         fl_g->choice_cfg_codec->value(CHOICE_FLAC);
121         fl_g->choice_cfg_bitrate->hide();
122 
123     }
124 
125     if(cfg.audio.channel == 1)
126         fl_g->choice_cfg_channel->value(CHOICE_MONO);
127     else
128         fl_g->choice_cfg_channel->value(CHOICE_STEREO);
129 
130     for(i = 0; bitrate[i] != 0; i++)
131     {
132         if(cfg.audio.bitrate == bitrate[i])
133         {
134             fl_g->choice_cfg_bitrate->value(i);
135             break;
136         }
137     }
138 
139     fl_g->input_cfg_present_level->value(-cfg.audio.signal_level);
140     fl_g->input_cfg_absent_level->value(-cfg.audio.silence_level);
141 
142     fl_g->check_cfg_mono_to_stereo->value(cfg.audio.mono_to_stereo);
143 
144     fl_g->input_cfg_buffer->value(cfg.audio.buffer_ms);
145 
146     fl_g->choice_cfg_resample_mode->value(cfg.audio.resample_mode);
147 
148     // Fill stream section
149     fl_g->check_song_update_active->value(cfg.main.song_update);
150     fl_g->check_read_last_line->value(cfg.main.read_last_line);
151 
152     fl_g->input_cfg_song_file->value(cfg.main.song_path);
153     fl_g->input_cfg_signal->value(cfg.main.signal_threshold);
154     fl_g->input_cfg_silence->value(cfg.main.silence_threshold);
155 
156     fl_g->input_cfg_song_prefix->value(cfg.main.song_prefix);
157     fl_g->input_cfg_song_suffix->value(cfg.main.song_suffix);
158 
159 
160 #if __APPLE__ && __MACH__
161     fl_g->choice_cfg_app->add("iTunes\\/Music");
162     fl_g->choice_cfg_app->add("Spotify");
163     fl_g->choice_cfg_app->add("VOX");
164     fl_g->check_cfg_use_app->value(cfg.main.app_update);
165     fl_g->choice_cfg_app->value(cfg.main.app_update_service);
166 #elif (__linux__ || __FreeBSD__) && HAVE_DBUS
167     fl_g->choice_cfg_app->add("Rhythmbox");
168     fl_g->choice_cfg_app->add("Banshee");
169     fl_g->choice_cfg_app->add("Clementine");
170     fl_g->choice_cfg_app->add("Spotify");
171     fl_g->choice_cfg_app->add("Cantata");
172     fl_g->choice_cfg_app->add("Strawberry");
173     fl_g->check_cfg_use_app->value(cfg.main.app_update);
174     fl_g->choice_cfg_app->value(cfg.main.app_update_service);
175 #elif WIN32
176     fl_g->choice_cfg_app->add(_("Not supported on Windows"));
177     fl_g->choice_cfg_app->value(0);
178     fl_g->check_cfg_use_app->value(0);
179     fl_g->check_cfg_use_app->deactivate();
180     fl_g->choice_cfg_app->deactivate();
181 #endif
182 
183 
184 
185     //fill the record section
186     fl_g->input_rec_filename->value(cfg.rec.filename);
187     fl_g->input_rec_folder->value(cfg.rec.folder);
188     fl_g->input_rec_split_time->value(cfg.rec.split_time);
189 
190     if(!strcmp(cfg.rec.codec, "mp3"))
191         fl_g->choice_rec_codec->value(CHOICE_MP3);
192     else if(!strcmp(cfg.rec.codec, "ogg"))
193         fl_g->choice_rec_codec->value(CHOICE_OGG);
194     else if(!strcmp(cfg.rec.codec, "opus"))
195         fl_g->choice_rec_codec->value(CHOICE_OPUS);
196     else if(!strcmp(cfg.rec.codec, "aac"))
197         fl_g->choice_rec_codec->value(CHOICE_AAC);
198     else if(!strcmp(cfg.rec.codec, "flac"))
199     {
200         fl_g->choice_rec_codec->value(CHOICE_FLAC);
201         fl_g->choice_rec_bitrate->hide();
202     }
203     else //wav
204     {
205         fl_g->choice_rec_codec->value(CHOICE_WAV);
206         fl_g->choice_rec_bitrate->hide();
207     }
208 
209     for(i = 0; bitrate[i] != 0; i++)
210         if(cfg.rec.bitrate == bitrate[i])
211             fl_g->choice_rec_bitrate->value(i);
212 
213     if(cfg.rec.start_rec)
214         fl_g->check_cfg_auto_start_rec->value(1);
215     else
216         fl_g->check_cfg_auto_start_rec->value(0);
217 
218     if(cfg.rec.stop_rec)
219         fl_g->check_cfg_auto_stop_rec->value(1);
220     else
221         fl_g->check_cfg_auto_stop_rec->value(0);
222 
223 
224     if(cfg.rec.rec_after_launch)
225         fl_g->check_cfg_rec_after_launch->value(1);
226     else
227         fl_g->check_cfg_rec_after_launch->value(0);
228 
229     if(cfg.rec.sync_to_hour)
230         fl_g->check_sync_to_full_hour->value(1);
231     else
232         fl_g->check_sync_to_full_hour->value(0);
233 
234     fl_g->input_rec_signal->value(cfg.rec.signal_threshold);
235     fl_g->input_rec_silence->value(cfg.rec.silence_threshold);
236 
237     //fill the ssl/tls section
238     fl_g->input_tls_cert_file->value(cfg.tls.cert_file);
239     fl_g->input_tls_cert_dir->value(cfg.tls.cert_dir);
240 
241 
242     update_samplerates();
243     update_channel_lists();
244 
245     //fill the DSP section
246     fl_g->check_activate_eq->value(cfg.dsp.equalizer);
247 
248     slider_equalizer1_cb(cfg.dsp.gain1);
249     fl_g->equalizerSlider1->value(cfg.dsp.gain1);
250 
251     slider_equalizer2_cb(cfg.dsp.gain2);
252     fl_g->equalizerSlider2->value(cfg.dsp.gain2);
253 
254     slider_equalizer3_cb(cfg.dsp.gain3);
255     fl_g->equalizerSlider3->value(cfg.dsp.gain3);
256 
257     slider_equalizer4_cb(cfg.dsp.gain4);
258     fl_g->equalizerSlider4->value(cfg.dsp.gain4);
259 
260     slider_equalizer5_cb(cfg.dsp.gain5);
261     fl_g->equalizerSlider5->value(cfg.dsp.gain5);
262 
263 	fl_g->check_activate_drc->value(cfg.dsp.compressor);
264 
265 	slider_threshold_cb(cfg.dsp.threshold);
266     fl_g->thresholdSlider->value(cfg.dsp.threshold);
267 
268 	slider_ratio_cb(cfg.dsp.ratio);
269     fl_g->ratioSlider->value(cfg.dsp.ratio);
270 
271 	slider_attack_cb(cfg.dsp.attack);
272     fl_g->attackSlider->value(cfg.dsp.attack);
273 
274 	slider_release_cb(cfg.dsp.release);
275     fl_g->releaseSlider->value(cfg.dsp.release);
276 
277 	slider_makeup_cb(cfg.dsp.makeup_gain);
278     fl_g->makeupSlider->value(cfg.dsp.makeup_gain);
279 
280     //fill the GUI section
281     fl_g->button_gui_bg_color->color(cfg.main.bg_color,
282             fl_lighter((Fl_Color)cfg.main.bg_color));
283     fl_g->button_gui_text_color->color(cfg.main.txt_color,
284             fl_lighter((Fl_Color)cfg.main.txt_color));
285     fl_g->check_gui_attach->value(cfg.gui.attach);
286     fl_g->check_gui_ontop->value(cfg.gui.ontop);
287     if(cfg.gui.ontop)
288     {
289         fl_g->window_main->stay_on_top(1);
290         fl_g->window_cfg->stay_on_top(1);
291     }
292     fl_g->check_gui_hide_log_window->value(cfg.gui.hide_log_window);
293     fl_g->check_gui_remember_pos->value(cfg.gui.remember_pos);
294 
295 
296     fl_g->check_gui_lcd_auto->value(cfg.gui.lcd_auto);
297 
298     fl_g->choice_gui_language->value(cfg.gui.lang);
299 
300 }
301 
302 //Updates the samplerate drop down menu for the audio
303 //device the user has selected
update_samplerates(void)304 void update_samplerates(void)
305 {
306     int i;
307     int *sr_list;
308     char sr_asc[10];
309 
310     fl_g->choice_cfg_samplerate->clear();
311 
312     sr_list = cfg.audio.pcm_list[cfg.audio.dev_num]->sr_list;
313 
314     for(i = 0; sr_list[i] != 0; i++)
315     {
316         snprintf(sr_asc, sizeof(sr_asc), "%dHz", sr_list[i]);
317         fl_g->choice_cfg_samplerate->add(sr_asc);
318 
319         if(cfg.audio.samplerate == sr_list[i])
320             fl_g->choice_cfg_samplerate->value(i);
321     }
322     if(i == 0)
323     {
324         fl_g->choice_cfg_samplerate->add("dev. not supported");
325         fl_g->choice_cfg_samplerate->value(0);
326     }
327 }
328 
update_channel_lists(void)329 void update_channel_lists(void)
330 {
331 
332     int i;
333     char ch_num_txt[8];
334 
335     int dev_num = fl_g->choice_cfg_dev->value();
336     char* dev_name = cfg.audio.pcm_list[dev_num]->name;
337     int num_of_channels = cfg.audio.pcm_list[dev_num]->num_of_channels;
338 
339     fl_g->choice_cfg_left_channel->clear();
340     fl_g->choice_cfg_right_channel->clear();
341 
342     for (i = 1; i <= num_of_channels; i++)
343     {
344         snprintf(ch_num_txt, sizeof(ch_num_txt), "%d", i);
345         fl_g->choice_cfg_left_channel->add(ch_num_txt);
346         fl_g->choice_cfg_right_channel->add(ch_num_txt);
347     }
348 
349     fl_g->choice_cfg_left_channel->value(cfg.audio.left_ch-1);
350     fl_g->choice_cfg_right_channel->value(cfg.audio.right_ch-1);
351 
352 }
353 
print_info(const char * info,int info_type)354 void print_info(const char* info, int info_type)
355 {
356     char timebuf[10];
357     char logtimestamp[21];
358     char* infotxt;
359     FILE *log_fd;
360     int len;
361 
362     time_t test;
363     struct tm  *mytime;
364     static struct tm time_bak;
365 
366     infotxt = strdup(info);
367 
368 
369     test = time(NULL);
370     mytime = localtime(&test);
371 
372     if( (time_bak.tm_min != mytime->tm_min) || (time_bak.tm_hour != mytime->tm_hour) )
373     {
374         time_bak.tm_min = mytime->tm_min;
375         time_bak.tm_hour = mytime->tm_hour;
376         strftime(timebuf, sizeof(timebuf), "\n%H:%M:", mytime);
377         fl_g->info_buffer->append(timebuf);
378     }
379 
380     fl_g->info_buffer->append((const char*)"\n");
381     fl_g->info_buffer->append((const char*)info);
382 
383     //always scroll to the last line
384     fl_g->info_output->scroll(fl_g->info_buffer->count_lines(0,     //count the lines from char 0 to the last character
385                             fl_g->info_buffer->length()),           //returns the number of characters in the buffer
386                             0);
387 
388     // log to log_file if defined
389     if ((cfg.main.log_file != NULL) && (strlen(cfg.main.log_file) > 0))
390     {
391         strftime(logtimestamp, sizeof(logtimestamp), "%Y-%m-%d %H:%M:%S", mytime);
392         log_fd = fl_fopen(cfg.main.log_file, "ab");
393         if (log_fd != NULL)
394         {
395             if(strchr(infotxt, ':'))
396                 strrpl(&infotxt, (char*)"\n", (char*)", ", MODE_ALL);
397             else
398                 strrpl(&infotxt, (char*)"\n", (char*)" ", MODE_ALL);
399 
400             strrpl(&infotxt, (char*)":,", (char*)": ", MODE_ALL);
401             strrpl(&infotxt, (char*)"\t", (char*)"", MODE_ALL);
402 
403             len = strlen(infotxt)-1;
404             // remove trailing commas and spaces
405             while (infotxt[len] == ',' || infotxt[len] == ' ')
406             {
407                 infotxt[len--] = '\0';
408             }
409 
410             fprintf(log_fd, "%s %s\n", logtimestamp, infotxt);
411             fclose(log_fd);
412         }
413 
414     }
415 
416     free(infotxt);
417 }
418 
print_lcd(const char * text,int len,int home,int clear)419 void print_lcd(const char *text, int len, int home, int clear)
420 {
421     if(clear)
422         fl_g->lcd->clear();
423 
424     fl_g->lcd->print((const uchar*)text, len);
425 
426     if(home)
427         fl_g->lcd->cursor_pos(0);
428 }
429 
430 
expand_string(char ** str)431 void expand_string(char **str)
432 {
433     char m[3], d[3], y[5], hh[3], mi[3], ss[3];
434     struct tm *date;
435     const time_t t = time(NULL);
436 
437     date = localtime(&t);
438 
439     snprintf(ss,  3, "%02d", date->tm_sec);
440     snprintf(mi,  3, "%02d", date->tm_min);
441     snprintf(hh,  3, "%02d", date->tm_hour);
442     snprintf(d,   3, "%02d", date->tm_mday);
443     snprintf(m,   3, "%02d", date->tm_mon+1);
444     snprintf(y,   5, "%d",   date->tm_year+1900);
445 
446     strrpl(str, (char*)"%S", ss, MODE_ALL);
447     strrpl(str, (char*)"%M", mi, MODE_ALL);
448     strrpl(str, (char*)"%H", hh, MODE_ALL);
449     strrpl(str, (char*)"%d", d, MODE_ALL);
450     strrpl(str, (char*)"%m", m, MODE_ALL);
451     strrpl(str, (char*)"%y", y, MODE_ALL);
452     strrpl(str, (char*)"%Y", y, MODE_ALL); // %Y and %y both work as year
453 }
454 
test_file_extension(void)455 void test_file_extension(void)
456 {
457     char *current_ext;
458 
459     current_ext = util_get_file_extension(cfg.rec.filename);
460 
461     // Append extension
462     if(current_ext == NULL)
463     {
464         cfg.rec.filename = (char*)realloc(cfg.rec.filename, strlen(cfg.rec.filename)+strlen(cfg.rec.codec)+2);
465         strcat(cfg.rec.filename, ".");
466         strcat(cfg.rec.filename, cfg.rec.codec);
467         fl_g->input_rec_filename->value(cfg.rec.filename);
468     }
469     // Replace extension
470     else if(strcmp(current_ext, cfg.rec.codec))
471     {
472         strrpl(&cfg.rec.filename, current_ext, cfg.rec.codec, MODE_LAST);
473         fl_g->input_rec_filename->value(cfg.rec.filename);
474     }
475 }
476 
477 
478 
init_main_gui_and_audio(void)479 void init_main_gui_and_audio(void)
480 {
481     if(cfg.gui.remember_pos)
482         fl_g->window_main->position(cfg.gui.x_pos, cfg.gui.y_pos);
483 
484     fl_g->slider_gain->value(util_factor_to_db(cfg.main.gain));
485     fl_g->window_main->redraw();
486 
487     if(cfg.gui.ontop)
488         fl_g->window_main->stay_on_top(1);
489 
490     fl_g->button_info->label(_("Hide log"));
491     if(cfg.gui.hide_log_window)
492        button_info_cb();
493 
494 
495     lame_stream.channel = cfg.audio.channel;
496     lame_stream.bitrate = cfg.audio.bitrate;
497     lame_stream.samplerate = cfg.audio.samplerate;
498     lame_enc_reinit(&lame_stream);
499 
500     lame_rec.channel = cfg.audio.channel;
501     lame_rec.bitrate = cfg.rec.bitrate;
502     lame_rec.samplerate = cfg.audio.samplerate;
503     lame_enc_reinit(&lame_rec);
504 
505     vorbis_stream.channel = cfg.audio.channel;
506     vorbis_stream.bitrate = cfg.audio.bitrate;
507     vorbis_stream.samplerate = cfg.audio.samplerate;
508     vorbis_enc_reinit(&vorbis_stream);
509 
510     vorbis_rec.channel = cfg.audio.channel;
511     vorbis_rec.bitrate = cfg.rec.bitrate;
512     vorbis_rec.samplerate = cfg.audio.samplerate;
513     vorbis_enc_reinit(&vorbis_rec);
514 
515     opus_stream.channel = cfg.audio.channel;
516     opus_stream.bitrate = cfg.audio.bitrate*1000;
517     opus_stream.samplerate = cfg.audio.samplerate;
518     opus_enc_reinit(&opus_stream);
519 
520     opus_rec.channel = cfg.audio.channel;
521     opus_rec.bitrate = cfg.rec.bitrate*1000;
522     opus_rec.samplerate = cfg.audio.samplerate;
523     opus_enc_reinit(&opus_rec);
524 
525 #ifdef HAVE_LIBFDK_AAC
526     if (g_aac_lib_available == 1)
527     {
528         aac_stream.channel = cfg.audio.channel;
529         aac_stream.bitrate = cfg.audio.bitrate;
530         aac_stream.samplerate = cfg.audio.samplerate;
531         aac_stream.aot = cfg.audio.aac_aot;
532         aac_enc_reinit(&aac_stream);
533 
534         aac_rec.channel = cfg.audio.channel;
535         aac_rec.bitrate = cfg.rec.bitrate;
536         aac_rec.samplerate = cfg.audio.samplerate;
537         aac_rec.aot = cfg.audio.aac_aot;
538         aac_enc_reinit(&aac_rec);
539     }
540 #endif
541 
542     flac_stream.channel = cfg.audio.channel;
543     flac_stream.samplerate = cfg.audio.samplerate;
544     flac_stream.enc_type = FLAC_ENC_TYPE_STREAM;
545     flac_enc_reinit(&flac_stream);
546 
547     flac_rec.channel = cfg.audio.channel;
548     flac_rec.samplerate = cfg.audio.samplerate;
549     flac_rec.enc_type = FLAC_ENC_TYPE_REC;
550     flac_enc_reinit(&flac_rec);
551 }
552 
553 
554 char *ask_user_msg = NULL;
555 char *ask_user_hash = NULL;
556 int ask_user_has_clicked = 0;
557 int ask_user_answer = 0;
558 
ask_user_reset(void)559 void ask_user_reset(void)
560 {
561     if (ask_user_msg != NULL)
562     {
563         free(ask_user_msg);
564         ask_user_msg = NULL;
565     }
566     if (ask_user_hash != NULL)
567     {
568         free(ask_user_hash);
569         ask_user_hash = NULL;
570     }
571     ask_user_has_clicked = 0;
572 }
573 
ask_user_get_has_clicked(void)574 int ask_user_get_has_clicked(void)
575 {
576     return ask_user_has_clicked;
577 }
578 
ask_user_get_answer(void)579 int ask_user_get_answer(void)
580 {
581     return ask_user_answer;
582 }
583 
ask_user_set_msg(char * msg)584 void ask_user_set_msg(char *msg)
585 {
586     int len = strlen(msg);
587     ask_user_msg = (char*)calloc(len+1, sizeof(char));
588     strncpy(ask_user_msg, msg, len);
589 }
590 
ask_user_set_hash(char * hash)591 void ask_user_set_hash(char *hash)
592 {
593     int len = strlen(hash);
594     ask_user_hash = (char*)calloc(len+1, sizeof(char));
595     strncpy(ask_user_hash, hash, len);
596 }
597 
ask_user_ask(void)598 void ask_user_ask(void)
599 {
600     if (fl_choice("%s", _("TRUST"), _("No"), NULL, ask_user_msg) == 1)
601     { // No
602         ask_user_answer = IC_ABORT;
603     }
604     else
605     { // TRUST
606 	int len = strlen(ask_user_hash);
607         cfg.srv[cfg.selected_srv]->cert_hash =
608             (char*)realloc(cfg.srv[cfg.selected_srv]->cert_hash, len+1);
609 
610         memset(cfg.srv[cfg.selected_srv]->cert_hash, 0, len+1);
611         strncpy(cfg.srv[cfg.selected_srv]->cert_hash, ask_user_hash, len);
612         ask_user_answer = IC_RETRY;
613     }
614 
615     ask_user_has_clicked = 1;
616 }
617