1 // dialogs.c
2 // LiVES (lives-exe)
3 // (c) G. Finch 2003 - 2020 <salsaman+lives@gmail.com>
4 // Released under the GPL 3 or later
5 // see file ../COPYING for licensing details
6 
7 /// TODO: split into player, progress, dialogs
8 
9 #include "main.h"
10 
11 #include <fcntl.h>
12 #include <unistd.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <string.h>
16 
17 #include "interface.h"
18 #include "cvirtual.h"
19 #include "resample.h"
20 #include "rte_window.h"
21 #include "paramwindow.h"
22 #include "ce_thumbs.h"
23 #include "callbacks.h"
24 #include "diagnostics.h"
25 
26 extern void reset_frame_and_clip_index(void);
27 
28 #define ANIM_LIMIT 0
29 
30 static int extra_cb_key = 0;
31 static int del_cb_key = 0;
32 
33 // processing
34 static uint64_t event_start;
35 static double audio_start;
36 static boolean accelerators_swapped;
37 static int frames_done;
38 static double disp_fraction_done;
39 static ticks_t proc_start_ticks;
40 static ticks_t last_open_check_ticks;
41 static ticks_t last_anim_ticks;
42 static uint64_t spare_cycles, last_spare_cycles;
43 static ticks_t last_kbd_ticks;
44 //static ticks_t last_cpuload_ticks = 0;
45 static frames_t getahead = -1, test_getahead = -1, bungle_frames;
46 
47 static boolean recalc_bungle_frames = 0;
48 static boolean shown_paused_frames;
49 static boolean td_had_focus;
50 static boolean cleanup_preload;
51 static boolean init_timers = TRUE;
52 static boolean drop_off = FALSE;
53 static int dropped;
54 
55 // how often to we count frames when opening
56 #define OPEN_CHECK_TICKS (TICKS_PER_SECOND/10l)
57 
58 static volatile boolean display_ready;
59 
60 static int64_t sttime;
61 
62 static lives_time_source_t last_time_source;
63 
64 static int cache_hits = 0, cache_misses = 0;
65 static double jitter = 0.;
66 
67 
get_cache_stats(void)68 const char *get_cache_stats(void) {
69   static char buff[1024];
70   lives_snprintf(buff, 1024, "preload caches = %d, hits = %d "
71                  "misses = %d,\nframe jitter = %.03f milliseconds.",
72                  cache_hits + cache_misses, cache_hits, cache_misses, jitter * 1000.);
73   return buff;
74 }
75 
76 
on_warn_mask_toggled(LiVESToggleButton * togglebutton,livespointer user_data)77 void on_warn_mask_toggled(LiVESToggleButton *togglebutton, livespointer user_data) {
78   LiVESWidget *tbutton;
79 
80   if (lives_toggle_button_get_active(togglebutton)) prefs->warning_mask |= LIVES_POINTER_TO_INT(user_data);
81   else prefs->warning_mask ^= LIVES_POINTER_TO_INT(user_data);
82   set_int_pref(PREF_LIVES_WARNING_MASK, prefs->warning_mask);
83 
84   if ((tbutton = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(togglebutton), "auto")) != NULL) {
85     // this is for the cds window - disable autoreload if we are not gonna show this window
86     if (lives_toggle_button_get_active(togglebutton)) {
87       lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(tbutton), FALSE);
88       lives_widget_set_sensitive(tbutton, FALSE);
89     } else {
90       lives_widget_set_sensitive(tbutton, TRUE);
91     }
92   }
93 }
94 
95 
add_xlays_widget(LiVESBox * box)96 static void add_xlays_widget(LiVESBox *box) {
97   char *tmp = (_("Show affected _layouts"));
98   add_list_expander(box, tmp, ENC_DETAILS_WIN_H, ENC_DETAILS_WIN_V, mainw->xlays);
99   lives_free(tmp);
100 }
101 
102 
add_warn_check(LiVESBox * box,int warn_mask_number)103 void add_warn_check(LiVESBox *box, int warn_mask_number) {
104   LiVESWidget *checkbutton = lives_standard_check_button_new(
105                                _("Do _not show this warning any more\n(can be turned back on from Preferences/Warnings)"),
106                                FALSE, LIVES_BOX(box), NULL);
107 
108   lives_signal_sync_connect(LIVES_GUI_OBJECT(checkbutton), LIVES_WIDGET_TOGGLED_SIGNAL,
109                             LIVES_GUI_CALLBACK(on_warn_mask_toggled), LIVES_INT_TO_POINTER(warn_mask_number));
110 }
111 
112 
add_clear_ds_button(LiVESDialog * dialog)113 static void add_clear_ds_button(LiVESDialog *dialog) {
114   LiVESWidget *button = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_CLEAR, _("_Recover disk space"),
115                         LIVES_RESPONSE_RETRY);
116   if (mainw->tried_ds_recover) lives_widget_set_sensitive(button, FALSE);
117   lives_dialog_make_widget_first(LIVES_DIALOG(dialog), button);
118 
119   lives_signal_sync_connect(LIVES_GUI_OBJECT(button), LIVES_WIDGET_CLICKED_SIGNAL,
120                             LIVES_GUI_CALLBACK(on_cleardisk_activate), (livespointer)button);
121 }
122 
123 
add_clear_ds_adv(LiVESBox * box)124 static void add_clear_ds_adv(LiVESBox *box) {
125   // add a button which opens up  Recover/Repair widget
126   LiVESWidget *button = lives_standard_button_new_with_label(_(" _Advanced Settings >>"),
127                         DEF_BUTTON_WIDTH * 2,
128                         DEF_BUTTON_HEIGHT);
129   LiVESWidget *hbox = lives_hbox_new(FALSE, 0);
130 
131   lives_box_pack_start(LIVES_BOX(hbox), button, FALSE, FALSE, widget_opts.packing_width * 2);
132   lives_box_pack_start(box, hbox, FALSE, FALSE, widget_opts.packing_height);
133   add_fill_to_box(LIVES_BOX(box));
134 
135   lives_signal_sync_connect(LIVES_GUI_OBJECT(button), LIVES_WIDGET_CLICKED_SIGNAL,
136                             LIVES_GUI_CALLBACK(on_cleardisk_advanced_clicked), NULL);
137 }
138 
139 
add_perminfo(LiVESWidget * dialog)140 static void add_perminfo(LiVESWidget *dialog) {
141   LiVESWidget *dialog_vbox = lives_dialog_get_content_area(LIVES_DIALOG(dialog));
142   LiVESWidget *hbox = lives_hbox_new(FALSE, 0), *vbox;
143   LiVESWidget *label;
144   char *txt;
145 
146   lives_box_pack_end(LIVES_BOX(dialog_vbox), hbox, FALSE, FALSE, 0);
147   vbox = lives_vbox_new(FALSE, 0);
148   widget_opts.justify = LIVES_JUSTIFY_CENTER;
149   lives_standard_expander_new(_("_Show complete details"), LIVES_BOX(hbox), vbox);
150   widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
151   lives_widget_apply_theme(hbox, LIVES_WIDGET_STATE_NORMAL);
152   lives_widget_apply_theme(vbox, LIVES_WIDGET_STATE_NORMAL);
153 
154   if (mainw->permmgr->cmdlist) {
155     txt = lives_strdup_printf(_("Should you agree, the following commands will be run:\n%s"),
156                               mainw->permmgr->cmdlist);
157     label = lives_standard_label_new(txt);
158     lives_box_pack_start(LIVES_BOX(vbox), label, FALSE, TRUE, widget_opts.packing_height);
159     add_fill_to_box(LIVES_BOX(vbox));
160 
161     lives_free(txt);
162   }
163 
164   if (mainw->permmgr->futures) {
165     txt = lives_strdup_printf(_("After this you will need to update using:\n%s\n\n"
166                                 "Please make a note of this.\n"),
167                               mainw->permmgr->futures);
168     label = lives_standard_label_new(txt);
169     lives_box_pack_start(LIVES_BOX(vbox), label, FALSE, TRUE, widget_opts.packing_height);
170     lives_free(txt);
171   }
172 }
173 
174 
scan_for_sets(LiVESWidget * button,livespointer data)175 static void scan_for_sets(LiVESWidget *button, livespointer data) {
176   LiVESWidget *entry = (LiVESWidget *)data;
177   LiVESWidget *label =
178     (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(button), "disp_label");
179   LiVESList *list;
180   char *txt;
181   const char *dir = lives_entry_get_text(LIVES_ENTRY(entry));
182   if (dir) list = get_set_list(dir, TRUE);
183   txt = lives_strdup_printf("%d", lives_list_length(list));
184   if (list) lives_list_free_all(&list);
185   lives_label_set_text(LIVES_LABEL(label), txt);
186   lives_free(txt);
187 }
188 
189 
extra_cb(LiVESWidget * dialog,int key)190 static void extra_cb(LiVESWidget *dialog, int key) {
191   LiVESWidget *dialog_vbox = lives_dialog_get_content_area(LIVES_DIALOG(dialog));
192   LiVESWidget *bbox = lives_dialog_get_action_area(LIVES_DIALOG(dialog));
193   LiVESWidget *layout, *button, *entry, *hbox, *label;
194   char *tmp;
195   switch (key) {
196   case 1:
197     /// no sets found
198     lives_label_chomp(LIVES_LABEL(widget_opts.last_label));
199 
200     hbox = lives_hbox_new(FALSE, 0);
201     lives_box_pack_start(LIVES_BOX(dialog_vbox), hbox, FALSE, TRUE, 0);
202 
203     entry = lives_standard_direntry_new(NULL, prefs->workdir, MEDIUM_ENTRY_WIDTH, PATH_MAX,
204                                         LIVES_BOX(hbox), NULL);
205 
206     layout = lives_layout_new(LIVES_BOX(dialog_vbox));
207     lives_layout_add_fill(LIVES_LAYOUT(layout), TRUE);
208     lives_layout_add_label(LIVES_LAYOUT(layout), _("Sets detected: "), TRUE);
209     label = lives_layout_add_label(LIVES_LAYOUT(layout), _("0"), TRUE);
210 
211     lives_layout_add_fill(LIVES_LAYOUT(layout), TRUE);
212     lives_layout_add_fill(LIVES_LAYOUT(layout), TRUE);
213 
214     hbox = lives_layout_hbox_new(LIVES_LAYOUT(layout));
215 
216     button =
217       lives_standard_button_new_from_stock_full(LIVES_STOCK_REFRESH,
218           _("Scan other directory"), DEF_BUTTON_WIDTH,
219           DEF_BUTTON_HEIGHT, LIVES_BOX(hbox), TRUE,
220           (tmp = lives_strdup(H_("Scan other directories for "
221                                  "LiVES Clip Sets. May be slow "
222                                  "for some directories."))));
223     lives_free(tmp);
224     lives_widget_object_set_data(LIVES_WIDGET_OBJECT(button), "disp_label", label);
225     lives_signal_sync_connect(LIVES_GUI_OBJECT(button), LIVES_WIDGET_CLICKED_SIGNAL,
226                               LIVES_GUI_CALLBACK(scan_for_sets), entry);
227 
228     layout = lives_layout_new(LIVES_BOX(dialog_vbox));
229     widget_opts.justify = LIVES_JUSTIFY_CENTER;
230     widget_opts.expand = LIVES_EXPAND_DEFAULT_HEIGHT | LIVES_EXPAND_EXTRA_WIDTH;
231     lives_layout_add_label(LIVES_LAYOUT(layout), _("If you believe there should be clips in the "
232                            "current directory,\n"
233                            "you can try to recover them by launching\n"
234                            " 'Clean up Diskspace / Recover Missing Clips' "
235                            "from the File menu.\n"), FALSE);
236     widget_opts.expand = LIVES_EXPAND_DEFAULT;
237     widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
238     lives_label_set_selectable(LIVES_LABEL(widget_opts.last_label), TRUE);
239     break;
240   case 2:
241     trash_rb(LIVES_BUTTON_BOX(bbox));
242     break;
243   default: break;
244   }
245 }
246 
247 
del_event_cb(LiVESWidget * dialog,livespointer data)248 static void del_event_cb(LiVESWidget *dialog, livespointer data) {
249   int key = LIVES_POINTER_TO_INT(data);
250   switch (key) {
251   default: break;
252   }
253 }
254 
255 //Warning or yes/no dialog
256 
257 // the type of message box here is with 2 or more buttons (e.g. OK/CANCEL, YES/NO, ABORT/CANCEL/RETRY)
258 // if a single OK button is needed, use create_message_dialog() in inteface.c instead
259 
create_message_dialog(lives_dialog_t diat,const char * text,int warn_mask_number)260 LiVESWidget *create_message_dialog(lives_dialog_t diat, const char *text, int warn_mask_number) {
261   LiVESWidget *dialog;
262   LiVESWidget *dialog_vbox;
263   LiVESWidget *label;
264   LiVESWidget *cancelbutton = NULL;
265   LiVESWidget *okbutton = NULL, *defbutton = NULL;
266   LiVESWidget *abortbutton = NULL;
267 
268   LiVESAccelGroup *accel_group = NULL;
269   LiVESWindow *transient = widget_opts.transient;
270 
271   char *colref;
272 
273   int cb_key = extra_cb_key;
274   int del_key = del_cb_key;
275   extra_cb_key = 0;
276   del_cb_key = 0;
277 
278   if (!transient) transient = get_transient_full();
279 
280   switch (diat) {
281   case LIVES_DIALOG_WARN:
282     dialog = lives_message_dialog_new(transient, (LiVESDialogFlags)0,
283                                       LIVES_MESSAGE_WARNING, LIVES_BUTTONS_NONE, NULL);
284 
285     lives_window_set_title(LIVES_WINDOW(dialog), _("Warning !"));
286 
287     if (palette && widget_opts.apply_theme)
288       lives_widget_set_fg_color(dialog, LIVES_WIDGET_STATE_NORMAL, &palette->dark_orange);
289 
290     defbutton = okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_OK, NULL,
291                            LIVES_RESPONSE_OK);
292 
293     lives_signal_sync_connect(LIVES_GUI_OBJECT(okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
294                               LIVES_GUI_CALLBACK(lives_general_button_clicked), NULL);
295 
296     break;
297   case LIVES_DIALOG_ERROR:
298     dialog = lives_message_dialog_new(transient, (LiVESDialogFlags)0,
299                                       LIVES_MESSAGE_ERROR, LIVES_BUTTONS_NONE, NULL);
300     if (palette && widget_opts.apply_theme)
301       lives_widget_set_fg_color(dialog, LIVES_WIDGET_STATE_NORMAL, &palette->dark_red);
302 
303     lives_window_set_title(LIVES_WINDOW(dialog), _("Error !"));
304 
305     defbutton = okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_OK, NULL,
306                            LIVES_RESPONSE_OK);
307 
308     lives_signal_sync_connect(LIVES_GUI_OBJECT(okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
309                               LIVES_GUI_CALLBACK(lives_general_button_clicked), NULL);
310     break;
311   case LIVES_DIALOG_INFO:
312     dialog = lives_message_dialog_new(transient, (LiVESDialogFlags)0,
313                                       LIVES_MESSAGE_INFO, LIVES_BUTTONS_NONE, NULL);
314 
315     lives_window_set_title(LIVES_WINDOW(dialog), _("Information"));
316 
317     if (palette && widget_opts.apply_theme)
318       lives_widget_set_fg_color(dialog, LIVES_WIDGET_STATE_NORMAL, &palette->light_green);
319 
320     defbutton = okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_OK, NULL,
321                            LIVES_RESPONSE_OK);
322 
323     lives_signal_sync_connect(LIVES_GUI_OBJECT(okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
324                               LIVES_GUI_CALLBACK(lives_general_button_clicked), NULL);
325     break;
326 
327   case LIVES_DIALOG_WARN_WITH_CANCEL:
328     dialog = lives_message_dialog_new(transient, (LiVESDialogFlags)0, LIVES_MESSAGE_WARNING,
329                                       LIVES_BUTTONS_NONE, NULL);
330 
331     if (palette && widget_opts.apply_theme)
332       lives_widget_set_fg_color(dialog, LIVES_WIDGET_STATE_NORMAL, &palette->dark_orange);
333 
334     if (mainw && mainw->add_clear_ds_button) {
335       mainw->add_clear_ds_button = FALSE;
336       add_clear_ds_button(LIVES_DIALOG(dialog));
337     }
338 
339     lives_window_set_title(LIVES_WINDOW(dialog), _("Warning !"));
340 
341     if (palette && widget_opts.apply_theme)
342       lives_widget_set_fg_color(dialog, LIVES_WIDGET_STATE_NORMAL, &palette->dark_orange);
343 
344     cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_CANCEL, NULL,
345                    LIVES_RESPONSE_CANCEL);
346 
347     defbutton = okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_OK, NULL,
348                            LIVES_RESPONSE_OK);
349     break;
350 
351   case LIVES_DIALOG_YESNO:
352     dialog = lives_message_dialog_new(transient, (LiVESDialogFlags)0, LIVES_MESSAGE_QUESTION,
353                                       LIVES_BUTTONS_NONE, NULL);
354 
355     lives_window_set_title(LIVES_WINDOW(dialog), _("Question"));
356 
357     if (palette && widget_opts.apply_theme)
358       lives_widget_set_fg_color(dialog, LIVES_WIDGET_STATE_NORMAL, &palette->light_red);
359 
360     cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_NO, NULL,
361                    LIVES_RESPONSE_NO);
362 
363     defbutton = okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_YES, NULL,
364                            LIVES_RESPONSE_YES);
365     break;
366 
367   case LIVES_DIALOG_QUESTION:
368     dialog = lives_message_dialog_new(transient, (LiVESDialogFlags)0, LIVES_MESSAGE_QUESTION,
369                                       LIVES_BUTTONS_NONE, NULL);
370     // caller will set title and buttons
371     if (palette && widget_opts.apply_theme)
372       lives_widget_set_fg_color(dialog, LIVES_WIDGET_STATE_NORMAL, &palette->light_red);
373     break;
374 
375   case LIVES_DIALOG_ABORT_CANCEL_RETRY:
376   case LIVES_DIALOG_RETRY_CANCEL:
377   case LIVES_DIALOG_ABORT_RETRY:
378   case LIVES_DIALOG_ABORT_OK:
379   case LIVES_DIALOG_ABORT:
380     dialog = lives_message_dialog_new(transient, (LiVESDialogFlags)0, LIVES_MESSAGE_ERROR, LIVES_BUTTONS_NONE, NULL);
381 
382     if (diat != LIVES_DIALOG_RETRY_CANCEL) {
383       abortbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog),
384                     LIVES_STOCK_QUIT, _("_Abort"), LIVES_RESPONSE_ABORT);
385 
386       if (diat == LIVES_DIALOG_ABORT_CANCEL_RETRY) {
387         lives_window_set_title(LIVES_WINDOW(dialog), _("File Error"));
388         cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_CANCEL, NULL,
389                        LIVES_RESPONSE_CANCEL);
390         okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_REFRESH,
391                    _("_Retry"), LIVES_RESPONSE_RETRY);
392       }
393       if (diat == LIVES_DIALOG_ABORT_OK) {
394         okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_OK, NULL,
395                    LIVES_RESPONSE_OK);
396       }
397     } else {
398       okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_REFRESH,
399                  _("_Retry"), LIVES_RESPONSE_RETRY);
400     }
401     if (diat == LIVES_DIALOG_RETRY_CANCEL) {
402       cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_CANCEL, NULL,
403                      LIVES_RESPONSE_CANCEL);
404     }
405     if (palette && widget_opts.apply_theme)
406       lives_widget_set_fg_color(dialog, LIVES_WIDGET_STATE_NORMAL, &palette->dark_red);
407     break;
408 
409   case LIVES_DIALOG_SKIP_RETRY_BROWSE:
410   case LIVES_DIALOG_CANCEL_RETRY_BROWSE:
411     dialog = lives_message_dialog_new(transient, (LiVESDialogFlags)0, LIVES_MESSAGE_ERROR,
412                                       LIVES_BUTTONS_NONE, NULL);
413 
414     lives_window_set_title(LIVES_WINDOW(dialog), _("Missing File or Directory"));
415 
416     if (diat == LIVES_DIALOG_CANCEL_RETRY_BROWSE)
417       cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_CANCEL, NULL,
418                      LIVES_RESPONSE_CANCEL);
419 
420     if (diat == LIVES_DIALOG_SKIP_RETRY_BROWSE)
421       cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_CANCEL,
422                      LIVES_STOCK_LABEL_SKIP, LIVES_RESPONSE_CANCEL);
423 
424     okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_REFRESH,
425                _("_Retry"), LIVES_RESPONSE_RETRY);
426 
427     abortbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_QUIT,
428                   _("_Browse"), LIVES_RESPONSE_BROWSE);
429 
430     if (palette && widget_opts.apply_theme)
431       lives_widget_set_fg_color(dialog, LIVES_WIDGET_STATE_NORMAL, &palette->dark_red);
432     break;
433 
434   default:
435     cancelbutton = abortbutton; // stop compiler complaining
436     return NULL;
437   }
438 
439   if (del_key)
440     lives_signal_sync_connect(LIVES_GUI_OBJECT(dialog), LIVES_WIDGET_DESTROY_SIGNAL,
441                               LIVES_GUI_CALLBACK(del_event_cb), LIVES_INT_TO_POINTER(del_key));
442 
443   lives_window_set_default_size(LIVES_WINDOW(dialog), MIN_MSGBOX_WIDTH, -1);
444   lives_widget_set_minimum_size(dialog, MIN_MSGBOX_WIDTH, -1);
445 
446   lives_window_set_deletable(LIVES_WINDOW(dialog), FALSE);
447   lives_window_set_resizable(LIVES_WINDOW(dialog), FALSE);
448 
449   if (mainw && mainw->mgeom)
450     lives_window_set_monitor(LIVES_WINDOW(dialog), widget_opts.monitor);
451 
452   if (widget_opts.apply_theme) {
453     lives_dialog_set_has_separator(LIVES_DIALOG(dialog), FALSE);
454     if (palette)
455       lives_widget_set_bg_color(dialog, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
456   }
457 
458   if (widget_opts.apply_theme) {
459     funkify_dialog(dialog);
460   } else {
461     lives_container_set_border_width(LIVES_CONTAINER(dialog), widget_opts.border_width * 2);
462   }
463 
464   label = lives_standard_formatted_label_new(text);
465   widget_opts.last_label = label;
466   colref = gdk_rgba_to_string(&palette->normal_back);
467   set_css_value_direct(label, LIVES_WIDGET_STATE_NORMAL, "",
468                        "caret-color", colref);
469   lives_free(colref);
470   dialog_vbox = lives_dialog_get_content_area(LIVES_DIALOG(dialog));
471   lives_box_pack_start(LIVES_BOX(dialog_vbox), label, TRUE, TRUE, 0);
472   lives_label_set_selectable(LIVES_LABEL(label), TRUE);
473 
474   if (mainw && mainw->permmgr && (mainw->permmgr->cmdlist || mainw->permmgr->futures))
475     add_perminfo(dialog);
476 
477   if (mainw) {
478     if (mainw->add_clear_ds_adv) {
479       mainw->add_clear_ds_adv = FALSE;
480       add_clear_ds_adv(LIVES_BOX(dialog_vbox));
481     }
482 
483     if (warn_mask_number > 0) {
484       add_warn_check(LIVES_BOX(dialog_vbox), warn_mask_number);
485     }
486 
487     if (mainw->xlays) {
488       add_xlays_widget(LIVES_BOX(dialog_vbox));
489     }
490 
491     if (mainw->iochan && !widget_opts.non_modal) {
492       LiVESWidget *details_button = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), NULL, _("Show _Details"),
493                                     LIVES_RESPONSE_SHOW_DETAILS);
494 
495       lives_signal_sync_connect(LIVES_GUI_OBJECT(details_button), LIVES_WIDGET_CLICKED_SIGNAL,
496                                 LIVES_GUI_CALLBACK(lives_general_button_clicked), NULL);
497     }
498   }
499 
500   if (okbutton || cancelbutton) {
501     accel_group = LIVES_ACCEL_GROUP(lives_accel_group_new());
502     lives_window_add_accel_group(LIVES_WINDOW(dialog), accel_group);
503   }
504 
505   if (cancelbutton) {
506     lives_widget_add_accelerator(cancelbutton, LIVES_WIDGET_CLICKED_SIGNAL, accel_group,
507                                  LIVES_KEY_Escape, (LiVESXModifierType)0, (LiVESAccelFlags)0);
508   }
509 
510   if (okbutton && mainw && mainw->iochan) {
511     lives_button_grab_default_special(okbutton);
512     lives_widget_grab_focus(okbutton);
513   } else if (defbutton) lives_button_grab_default_special(defbutton);
514 
515   lives_widget_show_all(dialog);
516   //gdk_window_show_unraised(lives_widget_get_xwindow(dialog));
517 
518   if (mainw->mgeom)
519     lives_window_center(LIVES_WINDOW(dialog));
520 
521   if (!widget_opts.non_modal)
522     lives_window_set_modal(LIVES_WINDOW(dialog), TRUE);
523 
524   if (!transient) {
525     char *wid = lives_strdup_printf("0x%08lx", (uint64_t)LIVES_XWINDOW_XID(lives_widget_get_xwindow(dialog)));
526 
527     /// MUST check if execs are MISSING, else we can get stuck in a loop of warning dialogs !!!
528     if (!wid || (capable->has_xdotool == MISSING && capable->has_wmctrl == MISSING) || !activate_x11_window(wid))
529       lives_window_set_keep_above(LIVES_WINDOW(dialog), TRUE);
530   }
531   if (cb_key) extra_cb(dialog, cb_key);
532 
533   if (mainw && mainw->add_trash_rb)
534     trash_rb(LIVES_BUTTON_BOX(lives_dialog_get_action_area(LIVES_DIALOG(dialog))));
535 
536   return dialog;
537 }
538 
539 
create_question_dialog(const char * title,const char * text)540 LIVES_GLOBAL_INLINE LiVESWidget *create_question_dialog(const char *title, const char *text) {
541   LiVESWidget *dialog;
542   char *xtitle = (char *)title;
543   if (!xtitle) xtitle = _("Question");
544   dialog = create_message_dialog(LIVES_DIALOG_QUESTION, text, 0);
545   lives_window_set_title(LIVES_WINDOW(dialog), xtitle);
546   if (xtitle != title) lives_free(xtitle);
547   return dialog;
548 }
549 
550 
do_warning_dialogf(const char * fmt,...)551 boolean do_warning_dialogf(const char *fmt, ...) {
552   va_list xargs;
553   boolean resb;
554   char *textx;
555   va_start(xargs, fmt);
556   textx = lives_strdup_vprintf(fmt, xargs);
557   va_end(xargs);
558   resb = do_warning_dialog_with_check(textx, 0);
559   lives_free(textx);
560   return resb;
561 }
562 
563 
do_warning_dialog(const char * text)564 LIVES_GLOBAL_INLINE boolean do_warning_dialog(const char *text) {
565   return do_warning_dialog_with_check(text, 0);
566 }
567 
568 
do_warning_dialog_with_check(const char * text,uint64_t warn_mask_number)569 boolean do_warning_dialog_with_check(const char *text, uint64_t warn_mask_number) {
570   // show OK/CANCEL, returns FALSE if cancelled
571   LiVESWidget *warning;
572   int response = 1;
573   char *mytext;
574 
575   if (warn_mask_number >= (1ul << 48)) {
576     if (!(prefs->warning_mask & warn_mask_number)) return TRUE;
577   } else {
578     if (prefs->warning_mask & warn_mask_number) return TRUE;
579   }
580 
581   mytext = lives_strdup(text); // must copy this because of translation issues
582 
583   warning = create_message_dialog(LIVES_DIALOG_WARN_WITH_CANCEL, mytext, warn_mask_number);
584 
585   response = lives_dialog_run(LIVES_DIALOG(warning));
586   lives_widget_destroy(warning);
587 
588   lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
589   lives_freep((void **)&mytext);
590 
591   return (response == LIVES_RESPONSE_OK);
592 }
593 
594 
do_yesno_dialog_with_check(const char * text,uint64_t warn_mask_number)595 boolean do_yesno_dialog_with_check(const char *text, uint64_t warn_mask_number) {
596   // show YES/NO, returns TRUE for YES
597   LiVESWidget *warning;
598   int response = 1;
599   char *mytext;
600 
601   if (warn_mask_number >= (1ul << 48)) {
602     if (!(prefs->warning_mask & warn_mask_number)) return TRUE;
603   } else {
604     if (prefs->warning_mask & warn_mask_number) return TRUE;
605   }
606 
607   mytext = lives_strdup(text); // must copy this because of translation issues
608 
609   do {
610     warning = create_message_dialog(LIVES_DIALOG_YESNO, mytext, warn_mask_number);
611     response = lives_dialog_run(LIVES_DIALOG(warning));
612     lives_widget_destroy(warning);
613   } while (response == LIVES_RESPONSE_RETRY);
614 
615   lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
616   lives_freep((void **)&mytext);
617 
618   return (response == LIVES_RESPONSE_YES);
619 }
620 
621 
get_transient_full(void)622 LIVES_GLOBAL_INLINE LiVESWindow *get_transient_full(void) {
623   LiVESWindow *transient = NULL;
624   if (!prefs) return NULL;
625   if (prefs->show_gui) {
626     if (rdet && rdet->dialog) transient = LIVES_WINDOW(rdet->dialog);
627     else if (prefsw && prefsw->prefs_dialog) transient = LIVES_WINDOW(prefsw->prefs_dialog);
628     else if (!rte_window_hidden()) transient = LIVES_WINDOW(rte_window);
629     else if (LIVES_MAIN_WINDOW_WIDGET && mainw->is_ready) transient = LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET);
630   }
631   return transient;
632 }
633 
634 
do_yesno_dialogf(const char * fmt,...)635 boolean do_yesno_dialogf(const char *fmt, ...) {
636   // show Yes/No, returns TRUE if Yes
637   LiVESWidget *warning;
638   int response;
639   va_list xargs;
640   char *textx;
641 
642   va_start(xargs, fmt);
643   textx = lives_strdup_vprintf(fmt, xargs);
644   va_end(xargs);
645 
646   warning = create_message_dialog(LIVES_DIALOG_YESNO, textx, 0);
647   lives_free(textx);
648   response = lives_dialog_run(LIVES_DIALOG(warning));
649   lives_widget_destroy(warning);
650   lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
651   return (response == LIVES_RESPONSE_YES);
652 }
653 
654 
do_yesno_dialog(const char * text)655 boolean do_yesno_dialog(const char *text) {
656   // show Yes/No, returns TRUE if Yes
657   LiVESWidget *warning;
658   int response;
659 
660   warning = create_message_dialog(LIVES_DIALOG_YESNO, text, 0);
661   lives_widget_show_all(warning);
662   response = lives_dialog_run(LIVES_DIALOG(warning));
663   lives_widget_destroy(warning);
664   lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
665   return (response == LIVES_RESPONSE_YES);
666 }
667 
668 
_do_abort_cancel_retry_dialog(const char * mytext,lives_dialog_t dtype)669 static LiVESResponseType _do_abort_cancel_retry_dialog(const char *mytext, lives_dialog_t dtype) {
670   LiVESResponseType response;
671   LiVESWidget *warning;
672 
673   do {
674     warning = create_message_dialog(dtype, mytext, 0);
675     lives_widget_show_all(warning);
676     response = lives_dialog_run(LIVES_DIALOG(warning)); // looping on retry
677     lives_widget_destroy(warning);
678     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
679 
680     if (response == LIVES_RESPONSE_ABORT) {
681       if (mainw->is_ready) {
682         if (dtype == LIVES_DIALOG_ABORT || do_abort_check()) {
683           if (CURRENT_CLIP_IS_VALID) {
684             if (cfile->handle) {
685               // stop any processing
686               lives_kill_subprocesses(cfile->handle, TRUE);
687             }
688           }
689           if (mainw->abort_hook_func)(*mainw->abort_hook_func)(NULL);
690           LIVES_FATAL("Aborted");
691           lives_notify(LIVES_OSC_NOTIFY_QUIT, mytext);
692           exit(1);
693         }
694       } else {
695         if (mainw->abort_hook_func)(*mainw->abort_hook_func)(NULL);
696         LIVES_FATAL("Aborted");
697         lives_notify(LIVES_OSC_NOTIFY_QUIT, mytext);
698         exit(0);
699       }
700     }
701   } while (response == LIVES_RESPONSE_ABORT);
702 
703   return response;
704 }
705 
706 
707 // returns LIVES_RESPONSE_CANCEL or LIVES_RESPONSE_RETRY
do_abort_cancel_retry_dialog(const char * text)708 LIVES_GLOBAL_INLINE LiVESResponseType do_abort_cancel_retry_dialog(const char *text) {
709   return _do_abort_cancel_retry_dialog(text, LIVES_DIALOG_ABORT_CANCEL_RETRY);
710 }
711 
712 
713 // always returns LIVES_RESPONSE_RETRY
do_abort_retry_dialog(const char * text)714 LIVES_GLOBAL_INLINE LiVESResponseType do_abort_retry_dialog(const char *text) {
715   return _do_abort_cancel_retry_dialog(text, LIVES_DIALOG_ABORT_RETRY);
716 }
717 
718 
719 // always returns LIVES_RESPONSE_OK
do_abort_ok_dialog(const char * text)720 LIVES_GLOBAL_INLINE LiVESResponseType do_abort_ok_dialog(const char *text) {
721   return _do_abort_cancel_retry_dialog(text, LIVES_DIALOG_ABORT_OK);
722 }
723 
724 // does not return
do_abort_dialog(const char * text)725 LIVES_GLOBAL_INLINE void do_abort_dialog(const char *text) {
726   _do_abort_cancel_retry_dialog(text, LIVES_DIALOG_ABORT);
727 }
728 
729 
do_retry_cancel_dialog(const char * text)730 LIVES_GLOBAL_INLINE LiVESResponseType do_retry_cancel_dialog(const char *text) {
731   return _do_abort_cancel_retry_dialog(text, LIVES_DIALOG_RETRY_CANCEL);
732 }
733 
734 
do_error_dialogf(const char * fmt,...)735 LiVESResponseType do_error_dialogf(const char *fmt, ...) {
736   // show error box
737   LiVESResponseType resi;
738   char *textx;
739   va_list xargs;
740   va_start(xargs, fmt);
741   textx = lives_strdup_vprintf(fmt, xargs);
742   va_end(xargs);
743   resi = do_error_dialog_with_check(textx, 0);
744   lives_free(textx);
745   return resi;
746 }
747 
748 
do_error_dialog(const char * text)749 LIVES_GLOBAL_INLINE LiVESResponseType do_error_dialog(const char *text) {
750   // show error box
751   return do_error_dialog_with_check(text, 0);
752 }
753 
754 
_do_info_dialog(const char * text,const char * exp_title,LiVESList * exp_list)755 static LiVESResponseType _do_info_dialog(const char *text, const char *exp_title, LiVESList *exp_list) {
756   LiVESResponseType ret = LIVES_RESPONSE_NONE;
757   LiVESWidget *info_box = create_message_dialog(LIVES_DIALOG_INFO, text, 0);
758 
759   if (exp_list) {
760     LiVESWidget *dab = lives_dialog_get_content_area(LIVES_DIALOG(info_box));
761     add_list_expander(LIVES_BOX(dab), exp_title, ENC_DETAILS_WIN_H, ENC_DETAILS_WIN_V, exp_list);
762     lives_widget_show_all(info_box);
763   }
764 
765   if (!widget_opts.non_modal) {
766     ret = lives_dialog_run(LIVES_DIALOG(info_box));
767   }
768 
769   return ret;
770 }
771 
772 
do_info_dialogf(const char * fmt,...)773 LiVESResponseType do_info_dialogf(const char *fmt, ...) {
774   // show info box
775   LiVESResponseType resi;
776   char *textx;
777   va_list xargs;
778   va_start(xargs, fmt);
779   textx = lives_strdup_vprintf(fmt, xargs);
780   va_end(xargs);
781   resi = _do_info_dialog(textx, NULL, NULL);
782   lives_free(textx);
783   return resi;
784 }
785 
786 
do_info_dialog(const char * text)787 LIVES_GLOBAL_INLINE LiVESResponseType do_info_dialog(const char *text) {
788   // show info box - blocks until OK is pressed
789   return _do_info_dialog(text, NULL, NULL);
790 }
791 
792 
do_info_dialog_with_expander(const char * text,const char * exp_text,LiVESList * list)793 LIVES_GLOBAL_INLINE LiVESResponseType do_info_dialog_with_expander(const char *text,
794     const char *exp_text, LiVESList *list) {
795   // show info box - blocks until OK is pressed
796   return _do_info_dialog(text, exp_text, list);
797 }
798 
799 
do_error_dialog_with_check(const char * text,uint64_t warn_mask_number)800 LiVESResponseType do_error_dialog_with_check(const char *text, uint64_t warn_mask_number) {
801   // show error box
802   LiVESWidget *err_box;
803   LiVESResponseType ret = LIVES_RESPONSE_NONE;
804 
805   if (warn_mask_number >= (1ul << 48)) {
806     if (!(prefs->warning_mask & warn_mask_number)) return TRUE;
807   } else {
808     if (prefs->warning_mask & warn_mask_number) return TRUE;
809   }
810 
811   err_box = create_message_dialog(warn_mask_number == 0 ? LIVES_DIALOG_ERROR :
812                                   LIVES_DIALOG_WARN, text, warn_mask_number);
813 
814   if (!widget_opts.non_modal) {
815     ret = lives_dialog_run(LIVES_DIALOG(err_box));
816   }
817 
818   return ret;
819 }
820 
821 
ds_critical_msg(const char * dir,char ** mountpoint,uint64_t dsval)822 char *ds_critical_msg(const char *dir, char **mountpoint, uint64_t dsval) {
823   char *msg, *msgx, *tmp, *tmp2, *mp, *mpstr;
824   char *dscr = lives_format_storage_space_string(prefs->ds_crit_level); ///< crit level
825   char *dscu = lives_format_storage_space_string(dsval); ///< current level
826   if (!mountpoint || !*mountpoint) mp = get_mountpoint_for(dir);
827   else mp = *mountpoint;
828   if (mp) {
829     tmp = lives_markup_escape_text(mp, -1);
830     mpstr = lives_strdup_printf("(%s)\n", tmp);
831     lives_free(tmp);
832   } else mpstr = lives_strdup("");
833   tmp = lives_filename_to_utf8(dir, -1, NULL, NULL, NULL);
834   tmp2 = lives_markup_escape_text(tmp, -1);
835   lives_free(tmp);
836   msg = lives_strdup_printf(
837           _("<b>FREE SPACE IN THE PARTITION CONTAINING\n%s\n%sHAS FALLEN BELOW THE CRITICAL LEVEL OF %s\n"
838             "CURRENT FREE SPACE IS %s\n</b>\n\n(Disk warning levels can be configured in Preferences / Warnings.)"),
839           tmp2, mpstr, dscr, dscu);
840   msgx = insert_newlines(msg, MAX_MSG_WIDTH_CHARS);
841   lives_free(msg); lives_free(tmp2); lives_free(dscr);
842   lives_free(dscu); lives_free(mpstr);
843   if (mountpoint) {
844     if (!*mountpoint) *mountpoint = mp;
845   } else if (mp) lives_free(mp);
846   return msgx;
847 }
848 
849 
ds_warning_msg(const char * dir,char ** mountpoint,uint64_t dsval,uint64_t cwarn,uint64_t nwarn)850 char *ds_warning_msg(const char *dir, char **mountpoint, uint64_t dsval, uint64_t cwarn, uint64_t nwarn) {
851   char *msg, *msgx, *tmp, *mp, *mpstr;
852   char *dscw = lives_format_storage_space_string(cwarn); ///< warn level
853   char *dscu = lives_format_storage_space_string(dsval); ///< current level
854   char *dscn = lives_format_storage_space_string(nwarn); ///< next warn level
855   if (!mountpoint || !*mountpoint) mp = get_mountpoint_for(dir);
856   else mp = *mountpoint;
857   if (mp) mpstr = lives_strdup_printf("(%s)\n", mp);
858   else mpstr = lives_strdup("");
859   msg = lives_strdup_printf(
860           _("Free space in the partition containing\n%s\nhas fallen below the warning level of %s\nCurrent free space is %s\n\n"
861             "(Next warning will be shown at %s. Disk warning levels can be configured in Preferences / Warnings.)"),
862           (tmp = lives_filename_to_utf8(dir, -1, NULL, NULL, NULL)), mpstr, dscw, dscu, dscn);
863   msgx = insert_newlines(msg, MAX_MSG_WIDTH_CHARS);
864   lives_free(msg); lives_free(dscw); lives_free(mpstr);
865   lives_free(dscu); lives_free(dscn);
866   if (mountpoint) {
867     if (!*mountpoint) *mountpoint = mp;
868   } else if (mp) lives_free(mp);
869   return msgx;
870 }
871 
872 
do_abortblank_error(const char * what)873 LIVES_GLOBAL_INLINE void do_abortblank_error(const char *what) {
874   char *msg = lives_strdup_printf(_("%s may not be blank.\nClick Abort to exit LiVES immediately or Ok "
875                                     "to continue with the default value."), what);
876   do_abort_ok_dialog(msg);
877   lives_free(msg);
878 }
879 
880 
do_optarg_blank_err(const char * what)881 LIVES_GLOBAL_INLINE void do_optarg_blank_err(const char *what) {
882   char *msg = lives_strdup_printf(_("-%s requires an argument, ignoring it\n"), what);
883   LIVES_WARN(msg);
884   lives_free(msg);
885 }
886 
887 
do_clip_divergence_error(int fileno)888 LIVES_GLOBAL_INLINE void do_clip_divergence_error(int fileno) {
889   char *msg = lives_strdup_printf(_("Errors were encountered when reloading LiVES' copy of the clip %s\n"
890                                     "Please click Abort if you wish to exit from LiVES,\n"
891                                     "or OK to update the clip details in LiVES and continue anyway.\n"),
892                                   IS_VALID_CLIP(fileno) ? mainw->files[fileno]->name : "??????");
893   do_abort_ok_dialog(msg);
894   lives_free(msg);
895   check_storage_space(fileno, FALSE);
896 }
897 
898 
do_aud_during_play_error(void)899 LIVES_GLOBAL_INLINE void do_aud_during_play_error(void) {
900   do_error_dialog(_("Audio players cannot be switched during playback."));
901 }
902 
903 
do_memory_error_dialog(char * op,size_t bytes)904 LiVESResponseType do_memory_error_dialog(char *op, size_t bytes) {
905   LiVESResponseType response;
906   char *sizestr, *msg;
907   if (bytes > 0) {
908     sizestr = lives_strdup_printf(_(" with size %ld bytes "), bytes);
909   } else {
910     sizestr = lives_strdup("");
911   }
912   msg = lives_strdup_printf(_("\n\nLiVES encountered a memory error when %s%s.\n"
913                               "Click Abort to exit from LiVES, Cancel to abandon the operation\n"
914                               "or Retry to try again. You may need to close some other applications first.\n"), op, sizestr);
915   lives_free(sizestr);
916   response = do_abort_cancel_retry_dialog(msg);
917   lives_free(msg);
918   return response;
919 }
920 
921 
handle_backend_errors(boolean can_retry)922 LiVESResponseType handle_backend_errors(boolean can_retry) {
923   /// handle error conditions returned from the back end
924 
925   char **array;
926   char *addinfo;
927   LiVESResponseType response = LIVES_RESPONSE_NONE;
928   int pxstart = 1;
929   int numtok;
930   int i;
931 
932   if (mainw->cancelled != CANCEL_NONE) return LIVES_RESPONSE_ACCEPT; // if the user/system cancelled we can expect errors !
933 
934   numtok = get_token_count(mainw->msg, '|');
935 
936   array = lives_strsplit(mainw->msg, "|", numtok);
937 
938   if (numtok > 2 && !strcmp(array[1], "read")) {
939     /// got read error from backend
940     if (numtok > 3 && *(array[3])) addinfo = array[3];
941     else addinfo = NULL;
942     if (mainw->current_file == -1 || cfile == NULL || !cfile->no_proc_read_errors)
943       do_read_failed_error_s(array[2], addinfo);
944     pxstart = 3;
945     THREADVAR(read_failed) = TRUE;
946     THREADVAR(read_failed_file) = lives_strdup(array[2]);
947     mainw->cancelled = CANCEL_ERROR;
948     response = LIVES_RESPONSE_CANCEL;
949   }
950 
951   else if (numtok > 2 && !strcmp(array[1], "write")) {
952     /// got write error from backend
953     if (numtok > 3 && *(array[3])) addinfo = array[3];
954     else addinfo = NULL;
955     if (mainw->current_file == -1 || cfile == NULL || !cfile->no_proc_write_errors)
956       do_write_failed_error_s(array[2], addinfo);
957     pxstart = 3;
958     THREADVAR(write_failed) = TRUE;
959     THREADVAR(write_failed_file) = lives_strdup(array[2]);
960     mainw->cancelled = CANCEL_ERROR;
961     response = LIVES_RESPONSE_CANCEL;
962   }
963 
964   else if (numtok > 3 && !strcmp(array[1], "system")) {
965     /// got (sub) system error from backend
966     if (numtok > 4 && *(array[4])) addinfo = array[4];
967     else addinfo = NULL;
968     if (!CURRENT_CLIP_IS_VALID || !cfile->no_proc_sys_errors) {
969       if (numtok > 5 && strstr(addinfo, "_ASKPERM_")) {
970         /// sys error is possibly recoverable, but requires user PERMS
971         /// ask for them and then return either LIVES_RESPONSE_CANCEL
972         /// or LIVES_RESPONSE_ACCEPT, as well as setting mainw->perm_idx and mainw->perm_key
973         if (lives_ask_permission(array, numtok, 5)) response = LIVES_RESPONSE_ACCEPT;
974         else {
975           response = LIVES_RESPONSE_CANCEL;
976           mainw->cancelled = CANCEL_USER;
977           goto handled;
978         }
979       } else {
980         boolean trysudo = FALSE;
981         if (addinfo && strstr(addinfo, "_TRY_SUDO_")) trysudo = TRUE;
982         response = do_system_failed_error(array[2], atoi(array[3]), addinfo,
983                                           can_retry, trysudo);
984         if (response == LIVES_RESPONSE_RETRY) return response;
985       }
986     }
987     pxstart = 3;
988     mainw->cancelled = CANCEL_ERROR;
989     response = LIVES_RESPONSE_CANCEL;
990   }
991 
992   // for other types of errors...more info....
993   /// set mainw->error but not mainw->cancelled
994   lives_snprintf(mainw->msg, MAINW_MSG_SIZE, "\n\n");
995   for (i = pxstart; i < numtok; i++) {
996     if (!*(array[i]) || !strcmp(array[i], "ERROR")) break;
997     lives_strappend(mainw->msg, MAINW_MSG_SIZE, _(array[i]));
998     lives_strappend(mainw->msg, MAINW_MSG_SIZE, "\n");
999   }
1000 
1001 handled:
1002   lives_strfreev(array);
1003 
1004   mainw->error = TRUE;
1005   return response;
1006 }
1007 
1008 
check_backend_return(lives_clip_t * sfile)1009 boolean check_backend_return(lives_clip_t *sfile) {
1010   // check return code after synchronous (foreground) backend commands
1011   lives_fread_string(mainw->msg, MAINW_MSG_SIZE, sfile->info_file);
1012 
1013   // TODO: consider permitting retry here
1014   if (!strncmp(mainw->msg, "error", 5)) handle_backend_errors(FALSE);
1015 
1016   return TRUE;
1017 }
1018 
1019 
pump_io_chan(LiVESIOChannel * iochan)1020 void pump_io_chan(LiVESIOChannel *iochan) {
1021   // pump data from stdout to textbuffer
1022   char *str_return = NULL;
1023   size_t retlen = 0;
1024   size_t plen;
1025   LiVESTextBuffer *optextbuf = lives_text_view_get_buffer(mainw->optextview);
1026 
1027 #ifdef GUI_GTK
1028   LiVESError *gerr = NULL;
1029 
1030   if (!iochan->is_readable) return;
1031   g_io_channel_read_to_end(iochan, &str_return, &retlen, &gerr);
1032   if (gerr) lives_error_free(gerr);
1033 #endif
1034 
1035 #ifdef GUI_QT
1036   QByteArray qba = iochan->readAll();
1037   str_return = strdup(qba.constData());
1038   retlen = strlen(str_return);
1039 #endif
1040   // check each line of str_return, if it contains ptext, (whitespace), then number get the number and set percentage
1041 
1042   if (retlen > 0 && mainw->proc_ptr) {
1043     double max;
1044     LiVESAdjustment *adj = lives_scrolled_window_get_vadjustment(LIVES_SCROLLED_WINDOW(((xprocess *)(
1045                              mainw->proc_ptr))->scrolledwindow));
1046     lives_text_buffer_insert_at_end(optextbuf, str_return);
1047     max = lives_adjustment_get_upper(adj);
1048     lives_adjustment_set_value(adj, max);
1049 
1050     if ((plen = strlen(prefs->encoder.ptext)) > 0) {
1051       boolean linebrk = FALSE;
1052       char *cptr = str_return;
1053       int ispct = 0;
1054 
1055       if (prefs->encoder.ptext[0] == '%') {
1056         ispct = 1;
1057         plen--;
1058       }
1059 
1060       while (cptr < (str_return + retlen - plen)) {
1061         if (!lives_strncmp(cptr, prefs->encoder.ptext + ispct, plen)) {
1062           cptr += plen;
1063           while (*cptr == ' ' || *cptr == '\n' || *cptr == '=') {
1064             if (*cptr == '\n') {
1065               linebrk = TRUE;
1066               break;
1067             }
1068             cptr++;
1069           }
1070           if (!linebrk) {
1071             if (ispct) mainw->proc_ptr->frames_done = (int)(strtod(cptr, NULL)
1072                   * (mainw->proc_ptr->progress_end - mainw->proc_ptr->progress_start + 1.) / 100.);
1073             else mainw->proc_ptr->frames_done = atoi(cptr);
1074           }
1075           break;
1076         }
1077         cptr++;
1078 	// *INDENT-OFF*
1079       }}}
1080   // *INDENT-ON*
1081 
1082   lives_freep((void **)&str_return);
1083 }
1084 
1085 
check_storage_space(int clipno,boolean is_processing)1086 boolean check_storage_space(int clipno, boolean is_processing) {
1087   // check storage space in prefs->workdir
1088   lives_clip_t *sfile = NULL;
1089 
1090   int64_t dsval = -1;
1091 
1092   lives_storage_status_t ds;
1093   int retval;
1094   boolean did_pause = FALSE;
1095 
1096   char *msg, *tmp;
1097   char *pausstr = (_("Processing has been paused."));
1098 
1099   if (IS_VALID_CLIP(clipno)) sfile = mainw->files[clipno];
1100 
1101   do {
1102     if (mainw->dsu_valid && capable->ds_used > -1) {
1103       dsval = capable->ds_used;
1104     } else if (prefs->disk_quota) {
1105       dsval = disk_monitor_check_result(prefs->workdir);
1106       if (dsval >= 0) capable->ds_used = dsval;
1107     }
1108     ds = get_storage_status(prefs->workdir, mainw->next_ds_warn_level, &dsval, 0);
1109     capable->ds_free = dsval;
1110     if (ds == LIVES_STORAGE_STATUS_WARNING) {
1111       uint64_t curr_ds_warn = mainw->next_ds_warn_level;
1112       mainw->next_ds_warn_level >>= 1;
1113       if (mainw->next_ds_warn_level > (dsval >> 1)) mainw->next_ds_warn_level = dsval >> 1;
1114       if (mainw->next_ds_warn_level < prefs->ds_crit_level) mainw->next_ds_warn_level = prefs->ds_crit_level;
1115       if (is_processing && sfile && mainw->proc_ptr && !mainw->effects_paused &&
1116           lives_widget_is_visible(mainw->proc_ptr->pause_button)) {
1117         on_effects_paused(LIVES_BUTTON(mainw->proc_ptr->pause_button), NULL);
1118         did_pause = TRUE;
1119       }
1120 
1121       tmp = ds_warning_msg(prefs->workdir, &capable->mountpoint, dsval, curr_ds_warn, mainw->next_ds_warn_level);
1122       if (!did_pause)
1123         msg = lives_strdup_printf("\n%s\n", tmp);
1124       else
1125         msg = lives_strdup_printf("\n%s\n%s\n", tmp, pausstr);
1126       lives_free(tmp);
1127       mainw->add_clear_ds_button = TRUE; // gets reset by do_warning_dialog()
1128       if (!do_warning_dialog(msg)) {
1129         lives_free(msg);
1130         lives_free(pausstr);
1131         mainw->cancelled = CANCEL_USER;
1132         if (is_processing) {
1133           if (sfile) sfile->nokeep = TRUE;
1134           on_cancel_keep_button_clicked(NULL, NULL); // press the cancel button
1135         }
1136         return FALSE;
1137       }
1138       lives_free(msg);
1139     } else if (ds == LIVES_STORAGE_STATUS_CRITICAL) {
1140       if (is_processing && sfile && mainw->proc_ptr && !mainw->effects_paused &&
1141           lives_widget_is_visible(mainw->proc_ptr->pause_button)) {
1142         on_effects_paused(LIVES_BUTTON(mainw->proc_ptr->pause_button), NULL);
1143         did_pause = TRUE;
1144       }
1145       tmp = ds_critical_msg(prefs->workdir, &capable->mountpoint, dsval);
1146       if (!did_pause)
1147         msg = lives_strdup_printf("\n%s\n", tmp);
1148       else {
1149         char *xpausstr = lives_markup_escape_text(pausstr, -1);
1150         msg = lives_strdup_printf("\n%s\n%s\n", tmp, xpausstr);
1151         lives_free(xpausstr);
1152       }
1153       lives_free(tmp);
1154       widget_opts.use_markup = TRUE;
1155       retval = do_abort_cancel_retry_dialog(msg);
1156       widget_opts.use_markup = FALSE;
1157       lives_free(msg);
1158       if (retval == LIVES_RESPONSE_CANCEL) {
1159         if (is_processing) {
1160           if (sfile) sfile->nokeep = TRUE;
1161           on_cancel_keep_button_clicked(NULL, NULL); // press the cancel button
1162         }
1163         mainw->cancelled = CANCEL_ERROR;
1164         lives_free(pausstr);
1165         return FALSE;
1166       }
1167     }
1168   } while (ds == LIVES_STORAGE_STATUS_CRITICAL);
1169 
1170   if (ds == LIVES_STORAGE_STATUS_OVER_QUOTA && !mainw->is_processing) {
1171     run_diskspace_dialog();
1172   }
1173 
1174   if (did_pause && mainw->effects_paused) {
1175     on_effects_paused(LIVES_BUTTON(mainw->proc_ptr->pause_button), NULL);
1176   }
1177 
1178   lives_free(pausstr);
1179 
1180   return TRUE;
1181 }
1182 
1183 
cancel_process(boolean visible)1184 static void cancel_process(boolean visible) {
1185   if (visible) {
1186     if (mainw->preview_box && !mainw->preview) lives_widget_set_tooltip_text(mainw->p_playbutton, _("Play all"));
1187     if (accelerators_swapped) {
1188       if (!mainw->preview) lives_widget_set_tooltip_text(mainw->m_playbutton, _("Play all"));
1189       lives_widget_remove_accelerator(mainw->proc_ptr->preview_button, mainw->accel_group, LIVES_KEY_p, (LiVESXModifierType)0);
1190       lives_widget_add_accelerator(mainw->playall, LIVES_WIDGET_ACTIVATE_SIGNAL, mainw->accel_group, LIVES_KEY_p,
1191                                    (LiVESXModifierType)0,
1192                                    LIVES_ACCEL_VISIBLE);
1193     }
1194     if (mainw->proc_ptr) {
1195       lives_widget_destroy(LIVES_WIDGET(mainw->proc_ptr->processing));
1196       lives_free(mainw->proc_ptr);
1197       mainw->proc_ptr = NULL;
1198     }
1199     mainw->is_processing = FALSE;
1200     if (!(cfile->menuentry == NULL)) {
1201       sensitize();
1202     }
1203   } else {
1204     /// ????
1205     mainw->is_processing = TRUE;
1206   }
1207   if (visible && mainw->current_file > -1 && cfile->clip_type == CLIP_TYPE_DISK && ((mainw->cancelled != CANCEL_NO_MORE_PREVIEW
1208       && mainw->cancelled != CANCEL_PREVIEW_FINISHED && mainw->cancelled != CANCEL_USER) || !cfile->opening)) {
1209     lives_rm(cfile->info_file);
1210   }
1211 }
1212 
1213 
disp_fraction(double fraction_done,double timesofar,xprocess * proc)1214 static void disp_fraction(double fraction_done, double timesofar, xprocess * proc) {
1215   // display fraction done and estimated time remaining
1216 #ifdef PROGBAR_IS_ENTRY
1217   char *tmp;
1218 #endif
1219   char *prog_label;
1220   double est_time;
1221 
1222   if (fraction_done > 1.) fraction_done = 1.;
1223   if (fraction_done < 0.) fraction_done = 0.;
1224 
1225   if (fraction_done > disp_fraction_done + .0001)
1226     lives_progress_bar_set_fraction(LIVES_PROGRESS_BAR(proc->progressbar), fraction_done);
1227 
1228   est_time = timesofar / fraction_done - timesofar;
1229   prog_label = lives_strdup_printf(_("\n%d%% done. Time remaining: %u sec\n"), (int)(fraction_done * 100.),
1230                                    (uint32_t)(est_time + .5));
1231 #ifdef PROGBAR_IS_ENTRY
1232   tmp = lives_strtrim(prog_label);
1233   widget_opts.justify = LIVES_JUSTIFY_CENTER;
1234   lives_entry_set_text(LIVES_ENTRY(mainw->proc_ptr->label3), tmp);
1235   widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
1236   lives_free(tmp);
1237 #else
1238   if (LIVES_IS_LABEL(proc->label3)) lives_label_set_text(LIVES_LABEL(proc->label3), prog_label);
1239 #endif
1240   lives_free(prog_label);
1241 
1242   disp_fraction_done = fraction_done;
1243 }
1244 
1245 
1246 static int progress_count;
1247 static double progress_speed;
1248 static int prog_fs_check;
1249 
1250 #define PROG_LOOP_VAL 200
1251 
progbar_pulse_or_fraction(lives_clip_t * sfile,int frames_done,double fraction_done)1252 static void progbar_pulse_or_fraction(lives_clip_t *sfile, int frames_done, double fraction_done) {
1253   double timesofar;
1254 
1255   if ((progress_count++ * progress_speed) >= PROG_LOOP_VAL) {
1256     if (frames_done <= mainw->proc_ptr->progress_end && mainw->proc_ptr->progress_end > 0 && !mainw->effects_paused &&
1257         frames_done > 0) {
1258       timesofar = (lives_get_current_ticks() - proc_start_ticks - mainw->timeout_ticks) / TICKS_PER_SECOND_DBL;
1259       if (fraction_done < 0.)
1260         fraction_done = (double)(frames_done - mainw->proc_ptr->progress_start)
1261                         / (double)(mainw->proc_ptr->progress_end - mainw->proc_ptr->progress_start + 1.);
1262       disp_fraction(fraction_done, timesofar, mainw->proc_ptr);
1263       progress_count = 0;
1264       progress_speed = 4.;
1265     } else {
1266       lives_progress_bar_pulse(LIVES_PROGRESS_BAR(mainw->proc_ptr->progressbar));
1267       progress_count = 0;
1268       if (!mainw->is_rendering)  progress_speed = 2.;
1269     }
1270   }
1271   lives_widget_context_update();
1272 }
1273 
1274 
update_progress(boolean visible)1275 void update_progress(boolean visible) {
1276   double fraction_done, timesofar;
1277   static double est_time = 0., frac_done = -1;
1278   char *prog_label;
1279 
1280   if (cfile->opening && cfile->clip_type == CLIP_TYPE_DISK && !cfile->opening_only_audio &&
1281       (cfile->hsize > 0 || cfile->vsize > 0 || cfile->frames > 0) && (!mainw->effects_paused || !shown_paused_frames)) {
1282     uint32_t apxl;
1283     ticks_t currticks = lives_get_current_ticks();
1284     if ((currticks - last_open_check_ticks) > OPEN_CHECK_TICKS *
1285         ((apxl = get_approx_ln((uint32_t)cfile->opening_frames)) < 50 ? apxl : 50) ||
1286         (mainw->effects_paused && !shown_paused_frames)) {
1287       mainw->proc_ptr->frames_done = cfile->end = cfile->opening_frames = get_frame_count(mainw->current_file,
1288                                      cfile->opening_frames > 1 ? cfile->opening_frames : 1);
1289       last_open_check_ticks = currticks;
1290       if (cfile->opening_frames > 1) {
1291         if (cfile->frames > 0 && cfile->frames != 123456789) {
1292           fraction_done = (double)(cfile->opening_frames - 1.) / (double)cfile->frames;
1293           if (fraction_done > 1.) fraction_done = 1.;
1294           if (!mainw->effects_paused) {
1295             timesofar = (currticks - proc_start_ticks - mainw->timeout_ticks) / TICKS_PER_SECOND_DBL;
1296             est_time = timesofar / fraction_done - timesofar;
1297           }
1298           lives_progress_bar_set_fraction(LIVES_PROGRESS_BAR(mainw->proc_ptr->progressbar), fraction_done);
1299           if (est_time != -1.) prog_label = lives_strdup_printf(_("\n%d/%d frames opened. Time remaining %u sec.\n"),
1300                                               cfile->opening_frames - 1, cfile->frames, (uint32_t)(est_time + .5));
1301           else prog_label = lives_strdup_printf(_("\n%d/%d frames opened.\n"), cfile->opening_frames - 1, cfile->frames);
1302         } else {
1303           lives_progress_bar_pulse(LIVES_PROGRESS_BAR(mainw->proc_ptr->progressbar));
1304           prog_label = lives_strdup_printf(_("\n%d frames opened.\n"), cfile->opening_frames - 1);
1305         }
1306 #ifdef PROGBAR_IS_ENTRY
1307         widget_opts.justify = LIVES_JUSTIFY_CENTER;
1308         lives_entry_set_text(LIVES_ENTRY(mainw->proc_ptr->label3), prog_label);
1309         widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
1310 #else
1311         lives_label_set_text(LIVES_LABEL(mainw->proc_ptr->label3), prog_label);
1312 #endif
1313         lives_free(prog_label);
1314         cfile->start = cfile->end > 0 ? 1 : 0;
1315         showclipimgs();
1316       }
1317     }
1318     shown_paused_frames = mainw->effects_paused;
1319   } else {
1320     if (visible && mainw->proc_ptr->frames_done >= mainw->proc_ptr->progress_start) {
1321       if (progress_count == 0) check_storage_space(mainw->current_file, TRUE);
1322       // display progress fraction or pulse bar
1323       progbar_pulse_or_fraction(cfile, mainw->proc_ptr->frames_done, frac_done);
1324       //sched_yield();
1325       lives_usleep(prefs->sleep_time);
1326     }
1327   }
1328 }
1329 
1330 //#define SHOW_CACHE_PREDICTIONS
1331 
1332 #define ENABLE_PRECACHE
1333 static short scratch = SCRATCH_NONE;
1334 
1335 #define ANIM_LIM 1000000
1336 
process_one(boolean visible)1337 int process_one(boolean visible) {
1338   // INTERNAL PLAYER
1339   static frames_t last_req_frame = 0;
1340   static int last_pwidth = 0, last_pheight = 0;
1341   static int64_t last_seek_pos = 0;
1342   lives_clip_t *sfile = cfile;
1343   _vid_playback_plugin *old_vpp;
1344   ticks_t new_ticks;
1345   lives_time_source_t time_source;
1346   frames_t requested_frame = 0;
1347   boolean show_frame = FALSE;
1348   boolean did_switch = FALSE;
1349   int old_current_file, old_playing_file;
1350 #ifdef ENABLE_PRECACHE
1351   int delta = 0;
1352 #endif
1353   int aplay_file = 0;
1354 #ifdef RT_AUDIO
1355   double audio_stretch = 1.0;
1356   ticks_t audio_ticks = 0;
1357 #endif
1358   old_current_file = mainw->current_file;
1359 
1360   if (visible) goto proc_dialog;
1361 
1362   sfile = mainw->files[mainw->playing_file];
1363 
1364   old_playing_file = mainw->playing_file;
1365   old_vpp = mainw->vpp;
1366 
1367   // time is obtained as follows:
1368   // -  if there is an external transport or clock active, we take our time from that
1369   // -  else if we have a fixed output framerate (e.g. we are streaming) we take our time from
1370   //         the system clock
1371   //  in these cases we adjust our audio rate slightly to keep in synch with video
1372   // - otherwise, we take the time from soundcard by counting samples played (the normal case),
1373   //   and we synch video with that; however, the soundcard time only updates when samples are played -
1374   //   so, between updates we interpolate with the system clock and then adjust when we get a new value
1375   //   from the card
1376   //g_print("process_one @ %f\n", lives_get_current_ticks() / TICKS_PER_SECOND_DBL);
1377 
1378   time_source = LIVES_TIME_SOURCE_NONE;
1379 
1380   mainw->currticks = lives_get_current_playback_ticks(mainw->origsecs, mainw->orignsecs, &time_source);
1381   if (mainw->currticks == -1) {
1382     if (time_source == LIVES_TIME_SOURCE_SOUNDCARD) handle_audio_timeout();
1383     mainw->cancelled = CANCEL_ERROR;
1384     return mainw->cancelled;
1385   }
1386 
1387   if (init_timers) {
1388     init_timers = FALSE;
1389     mainw->last_startticks = mainw->startticks = last_anim_ticks = mainw->fps_mini_ticks = mainw->currticks;
1390     mainw->last_startticks--;
1391     mainw->fps_mini_measure = 0;
1392     last_req_frame = sfile->frameno - 1;
1393     getahead = test_getahead = -1;
1394     mainw->actual_frame = sfile->frameno;
1395     mainw->offsetticks -= mainw->currticks;
1396     last_anim_ticks = mainw->currticks;
1397   }
1398 
1399   /* if (mainw->wall_ticks > last_cpuload_ticks + 10 * TICKS_PER_SECOND_DBL) { */
1400   /*   g_print("CPU LOAD IS %.4f\n", (double)get_cpu_load(0) / 10000.); */
1401   /*   last_cpuload_ticks = mainw->wall_ticks; */
1402   /* } */
1403 
1404   mainw->audio_stretch = 1.0;
1405 
1406 #define ADJUST_AUDIO_RATE
1407 #ifdef ADJUST_AUDIO_RATE
1408   // adjust audio rate slightly if we are behind or ahead
1409   // shouldn't need this since normally we sync video to soundcard
1410   // - unless we are controlled externally (e.g. jack transport) or system clock is forced
1411   if (time_source != LIVES_TIME_SOURCE_SOUNDCARD) {
1412 #ifdef ENABLE_JACK
1413     if (prefs->audio_src == AUDIO_SRC_INT && prefs->audio_player == AUD_PLAYER_JACK && mainw->jackd &&
1414         IS_VALID_CLIP(mainw->playing_file)  &&
1415         sfile->achans > 0 && (!mainw->is_rendering || (mainw->multitrack && !mainw->multitrack->is_rendering)) &&
1416         (mainw->currticks - mainw->offsetticks) > TICKS_PER_SECOND * 10 && ((audio_ticks = lives_jack_get_time(mainw->jackd)) >
1417             mainw->offsetticks || audio_ticks == -1)) {
1418       if (audio_ticks == -1) {
1419         if (mainw->cancelled == CANCEL_NONE) {
1420           if (IS_VALID_CLIP(mainw->playing_file)  && !sfile->is_loaded) mainw->cancelled = CANCEL_NO_PROPOGATE;
1421           else mainw->cancelled = CANCEL_AUDIO_ERROR;
1422           return mainw->cancelled;
1423         }
1424       }
1425       if ((audio_stretch = (double)(audio_ticks - mainw->offsetticks) / (double)(mainw->currticks - mainw->offsetticks)) < 2. &&
1426           audio_stretch > 0.5) {
1427         // if audio_stretch is > 1. it means that audio is playing too fast
1428         // < 1. it is playing too slow
1429 
1430         // if too fast we increase the apparent sample rate so that it gets downsampled more
1431         // if too slow we decrease the apparent sample rate so that it gets upsampled more
1432         mainw->audio_stretch = audio_stretch;
1433       }
1434     }
1435 #endif
1436 
1437 #ifdef HAVE_PULSE_AUDIO
1438     if (prefs->audio_src == AUDIO_SRC_INT && prefs->audio_player == AUD_PLAYER_PULSE && mainw->pulsed &&
1439         CURRENT_CLIP_IS_VALID &&
1440         sfile->achans > 0 && (!mainw->is_rendering || (mainw->multitrack && !mainw->multitrack->is_rendering)) &&
1441         (mainw->currticks - mainw->offsetticks) > TICKS_PER_SECOND * 10 &&
1442         ((audio_ticks = lives_pulse_get_time(mainw->pulsed)) >
1443          mainw->offsetticks || audio_ticks == -1)) {
1444       if (audio_ticks == -1) {
1445         if (mainw->cancelled == CANCEL_NONE) {
1446           if (sfile && !sfile->is_loaded) mainw->cancelled = CANCEL_NO_PROPOGATE;
1447           else mainw->cancelled = CANCEL_AUDIO_ERROR;
1448           return mainw->cancelled;
1449         }
1450       }
1451       // fps is synched to external source, so we adjust the audio rate to fit
1452       if ((audio_stretch = (double)(audio_ticks - mainw->offsetticks) / (double)(mainw->clock_ticks
1453                            + mainw->offsetticks)) < 2. &&  audio_stretch > 0.5) {
1454         // if audio_stretch is > 1. it means that audio is playing too fast
1455         // < 1. it is playing too slow
1456 
1457         // if too fast we increase the apparent sample rate so that it gets downsampled more
1458         // if too slow we decrease the apparent sample rate so that it gets upsampled more
1459         mainw->audio_stretch = audio_stretch;
1460       }
1461     }
1462 #endif
1463   }
1464 #endif
1465 
1466   /// SWITCH POINT
1467 
1468   /// during playback this is the only place to update certain variables,
1469   /// e.g. current / playing file, playback plugin. Anywhere else it should be deferred by setting the appropriate
1470   /// update value (e.g. mainw->new_clip, mainw->new_vpp)
1471   /// the code will enforce this so that setting the values directly will cause playback to end
1472 
1473   // we allow an exception only when starting or stopping a generator
1474 
1475   if (mainw->current_file != old_current_file || mainw->playing_file != old_playing_file || mainw->vpp != old_vpp) {
1476     if (!mainw->ignore_clipswitch) {
1477       mainw->cancelled = CANCEL_INTERNAL_ERROR;
1478       return FALSE;
1479     }
1480     old_current_file = mainw->current_file;
1481     old_playing_file = mainw->playing_file;
1482     mainw->ignore_clipswitch = FALSE;
1483   }
1484 
1485   if (mainw->new_vpp) {
1486     mainw->vpp = open_vid_playback_plugin(mainw->new_vpp, TRUE);
1487     mainw->new_vpp = NULL;
1488     old_vpp = mainw->vpp;
1489   }
1490 
1491 switch_point:
1492 
1493   mainw->noswitch = FALSE;
1494 
1495   if (mainw->new_clip != -1) {
1496     mainw->deltaticks = 0;
1497 
1498     if (IS_VALID_CLIP(mainw->playing_file)) {
1499       if (sfile->arate)
1500         /* g_print("HIB %d %d %d %d %ld %f %f %ld %ld %d %f\n", sfile->frameno, last_req_frame, mainw->playing_file, */
1501         /* 	aplay_file, sfile->aseek_pos, */
1502         /* 	sfile->fps * lives_pulse_get_pos(mainw->pulsed) + 1., (double)sfile->aseek_pos */
1503         /* 	/ (double)sfile->arps / 4. * sfile->fps + 1., */
1504         /* 	mainw->currticks, mainw->startticks, sfile->arps, sfile->fps); */
1505 
1506         sfile->frameno = sfile->last_frameno = last_req_frame + sig(sfile->pb_fps);
1507 
1508       if (mainw->frame_layer_preload) {
1509         check_layer_ready(mainw->frame_layer_preload);
1510         weed_layer_free(mainw->frame_layer_preload);
1511         mainw->frame_layer_preload = NULL;
1512         cleanup_preload = FALSE;
1513       }
1514 
1515       //g_print("ASEEK is %ld\n", sfile->aseek_pos);
1516     }
1517 
1518     do_quick_switch(mainw->new_clip);
1519     did_switch = TRUE;
1520 
1521     if (!IS_VALID_CLIP(mainw->playing_file)) {
1522       if (IS_VALID_CLIP(mainw->new_clip)) goto switch_point;
1523       mainw->cancelled = CANCEL_INTERNAL_ERROR;
1524       cancel_process(visible);
1525       return ONE_MILLION + mainw->cancelled;
1526     }
1527 
1528     sfile = mainw->files[mainw->playing_file];
1529     sfile->last_frameno = sfile->frameno;
1530 
1531     /* if (sfile->arate) */
1532     /*   g_print("HIB2 %d %d %d %d %d %ld %f %f %ld %ld %d %f\n", mainw->actual_frame, sfile->frameno, last_req_frame, */
1533     /* 	      mainw->playing_file, aplay_file, sfile->aseek_pos, */
1534     /* 	      sfile->fps * lives_pulse_get_pos(mainw->pulsed) + 1., */
1535     /* 	      (double)sfile->aseek_pos / (double)sfile->arate / 4. * sfile->fps + 1., */
1536     /* 	      mainw->currticks, mainw->startticks, sfile->arps, sfile->fps); */
1537 
1538     cache_hits = cache_misses = 0;
1539     mainw->force_show = TRUE;
1540     mainw->actual_frame = mainw->files[mainw->new_clip]->frameno;
1541     mainw->new_clip = -1;
1542     mainw->blend_palette = WEED_PALETTE_END;
1543     getahead = -1;
1544     if (prefs->pbq_adaptive) reset_effort();
1545     // TODO: add a few to bungle_frames in case of decoder unchilling
1546 
1547     /// switch compensation allows us to give a brief impulse to the audio when switching
1548     // this may be adjusted for accuracy | a value > 1.0 will slow audio down on switch
1549 #define SWITCH_COMPENSATION 1.0
1550 
1551     mainw->audio_stretch = SWITCH_COMPENSATION;
1552     scratch = SCRATCH_JUMP_NORESYNC;
1553     drop_off = TRUE;
1554     last_req_frame = sfile->frameno - 1;
1555     /// playing file should == current_file, but just in case store separate values.
1556     old_current_file = mainw->current_file;
1557     old_playing_file = mainw->playing_file;
1558 
1559     /* if (sfile->arate) */
1560     /*   g_print("seek vals: vid %d %ld = %f %d %f\n", sfile->frameno, sfile->aseek_pos, */
1561     /* 	      (double)sfile->aseek_pos / (double)sfile->arate / 4. * sfile->fps + 1., */
1562     /* 	      sfile->arate, sfile->fps); */
1563   }
1564 
1565   mainw->noswitch = TRUE;
1566 
1567   /// end SWITCH POINT
1568 
1569   // playing back an event_list
1570   // here we need to add mainw->offsetticks, to get the correct position whe playing back in multitrack
1571   if (!mainw->proc_ptr && cfile->next_event) {
1572     // playing an event_list
1573     if (mainw->scratch != SCRATCH_NONE && mainw->multitrack) {
1574 #ifdef ENABLE_JACK_TRANSPORT
1575 
1576       // handle transport jump in multitrack : end current playback and restart it from the new position
1577       // TODO: retest this and enable in the clip_editor
1578       ticks_t transtc = q_gint64(jack_transport_get_current_ticks(), cfile->fps);
1579       mainw->multitrack->pb_start_event = get_frame_event_at(mainw->multitrack->event_list, transtc, NULL, TRUE);
1580       if (mainw->cancelled == CANCEL_NONE) mainw->cancelled = CANCEL_EVENT_LIST_END;
1581 #endif
1582     } else {
1583       if (mainw->multitrack) mainw->currticks += mainw->offsetticks; // add the offset of playback start time
1584       if (mainw->currticks >= event_start) {
1585         // see if we are playing a selection and reached the end
1586         if (mainw->multitrack && mainw->multitrack->playing_sel &&
1587             get_event_timecode(cfile->next_event) / TICKS_PER_SECOND_DBL >=
1588             mainw->multitrack->region_end) mainw->cancelled = CANCEL_EVENT_LIST_END;
1589         else {
1590           mainw->noswitch = FALSE;
1591           cfile->next_event = process_events(cfile->next_event, FALSE, mainw->currticks);
1592           mainw->noswitch = TRUE;
1593           if (!cfile->next_event) mainw->cancelled = CANCEL_EVENT_LIST_END;
1594         }
1595       }
1596     }
1597 
1598     lives_widget_context_update();
1599 
1600     if (mainw->cancelled == CANCEL_NONE) return 0;
1601     cancel_process(visible);
1602     return mainw->cancelled;
1603   }
1604 
1605   // free playback
1606 
1607   //#define SHOW_CACHE_PREDICTIONS
1608 #define TEST_TRIGGER 9999
1609 
1610   /// Values may need tuning for each clip - possible future targets for the autotuner
1611 #define DROPFRAME_TRIGGER 4
1612 #define JUMPFRAME_TRIGGER 99999999 // we should retain cdata->jump_limit from the initial file open
1613 
1614   if (mainw->currticks - last_kbd_ticks > KEY_RPT_INTERVAL * 100000) {
1615     // if we have a cached key (ctrl-up, ctrl-down, ctrl-left, crtl-right) trigger it here
1616     // this is to avoid the keyboard repeat delay (dammit !) so we get smooth trickplay
1617     // BUT we need a timer sufficiently large so it isn't triggered on every loop, to produce a constant repeat rate
1618     // but not so large so it doesn't get triggered enough
1619     if (last_kbd_ticks > 0) handle_cached_keys();
1620     last_kbd_ticks = mainw->currticks;
1621   }
1622 
1623   new_ticks = mainw->currticks;
1624   if (mainw->scratch != SCRATCH_JUMP)
1625     if (new_ticks < mainw->startticks) new_ticks = mainw->startticks;
1626 
1627   show_frame = FALSE;
1628   requested_frame = sfile->frameno;
1629 
1630   if (sfile->pb_fps != 0.) {
1631     // calc_new_playback_postion returns a frame request based on the player mode and the time delta
1632     //
1633     // mainw->startticks is the timecode of the last frame shown
1634     // new_ticks is the (adjusted) current time
1635     // sfile->last_frameno, sfile->pb_fps (if playing) or sfile->fps (if not) are also used in the calculation
1636     // as well as selection bounds and loop mode settings (if appropriate)
1637     //
1638     // on return, new_ticks is set to either mainw->starticks or the timecode of the next frame to show
1639     // which will be <= the current time
1640     // and requested_frame is set to the frame to show. By default this is the frame we show, but we may vary
1641     // this depending on the cached frame
1642 
1643     requested_frame = calc_new_playback_position(mainw->current_file, mainw->startticks, &new_ticks);
1644     if (mainw->foreign) {
1645       if (requested_frame > sfile->frameno) {
1646         load_frame_image(sfile->frameno++);
1647       }
1648       lives_widget_context_update();
1649       if (mainw->cancelled != CANCEL_NONE) return mainw->cancelled;
1650       return 0;
1651     }
1652     if (requested_frame < 1 || requested_frame > sfile->frames)
1653       requested_frame = sfile->frameno;
1654     else sfile->frameno = requested_frame;
1655 
1656     if (mainw->scratch != SCRATCH_NONE) scratch  = mainw->scratch;
1657     mainw->scratch = SCRATCH_NONE;
1658 
1659 #ifdef ENABLE_PRECACHE
1660     if (scratch != SCRATCH_NONE) {
1661       getahead = test_getahead = -1;
1662       cleanup_preload = TRUE;
1663       mainw->pred_frame = -1;
1664       // fix for a/v sync
1665       if (!did_switch) sfile->last_play_sequence = mainw->play_sequence;
1666     }
1667 #endif
1668 
1669     if (new_ticks != mainw->startticks && new_ticks != mainw->last_startticks
1670         && (requested_frame != last_req_frame || sfile->frames == 1
1671             || (mainw->playing_sel && sfile->start == sfile->end))) {
1672       //g_print("%ld %ld %ld %d %d %d\n", mainw->currticks, mainw->startticks, new_ticks,
1673       //sfile->last_frameno, requested_frame, last_req_frame);
1674       if (mainw->fixed_fpsd <= 0. && (!mainw->vpp || mainw->vpp->fixed_fpsd <= 0. || !mainw->ext_playback)) {
1675         show_frame = TRUE;
1676       }
1677       if (prefs->show_dev_opts) jitter = (double)(mainw->currticks - new_ticks) / TICKS_PER_SECOND_DBL;
1678 #ifdef ENABLE_PRECACHE
1679       if (test_getahead > 0) {
1680         if (recalc_bungle_frames) {
1681           /// we want to avoid the condition where we are constantly seeking ahead and because the seek may take a while
1682           /// to happen, we immediately need to seek again. This will cause the video stream to stutter. So to try to avoid this
1683           /// we will do an an EXTRA jump forwads which ideally will give the player a chance to catch up
1684           /// - in this condition, instead of showing the reqiested frame we will do the following:
1685           /// - if we have a cached frame, we will show that; otherwise we will advance the frame by 1 from the last frame.
1686           ///   and show that, since we can decode it quickly.
1687           /// - following this we will cache the "getahead" frame. The player will then render the getahead frame
1688           //     and keep reshowing it until the time catches up.
1689           /// (A future update will implement a more flexible caching system which will enable the possibility
1690           /// of caching further frames while we waut)
1691           /// - if we did not advance enough, we show the getahead frame and then do a larger jump.
1692           // ..'bungle frames' is a rough estimate of how far ahead we need to jump so that we land exaclty
1693           /// on the player's frame. 'getahead' is the target frame.
1694           /// after a jump, we adjust bungle_frames to try to jump more acurately the next tine
1695           /// however, it is impossible to get it right 100% of the time, as the actual value can vary unpredictably
1696           /// 'test_getahead' is used so that we can sometimes recalibrate without actually jumping the frame
1697           /// in future, we could also get a more accurate estimate by integrating statistics from the decoder.
1698           /// - useful values would be the frame decode time, keyframe positions, seek time to keyframe,  keyframe decode time.
1699           int dir = sig(sfile->pb_fps);
1700           delta = (test_getahead - requested_frame) * dir;
1701 #ifdef SHOW_CACHE_PREDICTIONS
1702           g_print("gah (%d) %d, act %d %d, bungle %d, shouldabeen %d %s", mainw->effort, test_getahead,
1703                   sfile->frameno, requested_frame,
1704                   bungle_frames, bungle_frames - delta, getahead == -1 ? "(calibrating)" : "");
1705           if (delta < 0) g_print(" !!!!!\n");
1706           if (delta == 0) g_print(" EXACT\n");
1707           if (delta > 0) g_print(" >>>>\n");
1708 #endif
1709           if (delta == 0)  bungle_frames++;
1710           if (delta > 0 && delta < 3 && bungle_frames > 1) bungle_frames--;
1711           else bungle_frames += (requested_frame - test_getahead) * dir;
1712           if (bungle_frames <= -dir) bungle_frames = 0;
1713           if (delta >= 0 && getahead > -1) drop_off = TRUE;
1714         }
1715         recalc_bungle_frames = FALSE;
1716         test_getahead = -1;
1717       }
1718 #endif
1719 
1720 #ifdef USE_GDK_FRAME_CLOCK
1721       if (display_ready) {
1722         show_frame = TRUE;
1723         /// not used
1724         display_ready = FALSE;
1725       }
1726 #endif
1727     }
1728   }
1729 
1730   // play next frame
1731   if (LIVES_LIKELY(mainw->cancelled == CANCEL_NONE)) {
1732     // calculate the audio 'frame' for non-realtime audio players
1733     // for realtime players, we did this in calc_new_playback_position()
1734     if (!is_realtime_aplayer(prefs->audio_player)) {
1735       if (LIVES_UNLIKELY(mainw->loop_cont && (mainw->aframeno > (mainw->audio_end ? mainw->audio_end :
1736                                               sfile->laudio_time * sfile->fps)))) {
1737         mainw->firstticks = mainw->clock_ticks;
1738       }
1739     }
1740 
1741     if (mainw->force_show) {
1742       show_frame = TRUE;
1743     } else {
1744       if (mainw->fixed_fpsd > 0. || (mainw->vpp  && mainw->vpp->fixed_fpsd > 0. && mainw->ext_playback)) {
1745         ticks_t dticks;
1746         dticks = (mainw->clock_ticks - mainw->last_display_ticks) / TICKS_PER_SECOND_DBL;
1747         if ((mainw->fixed_fpsd > 0. && (dticks >= 1. / mainw->fixed_fpsd)) ||
1748             (mainw->vpp && mainw->vpp->fixed_fpsd > 0. && mainw->ext_playback &&
1749              dticks >= 1. / mainw->vpp->fixed_fpsd)) {
1750           show_frame = TRUE;
1751         }
1752       }
1753     }
1754 
1755     if (show_frame) {
1756       // time to show a new frame
1757       last_spare_cycles = spare_cycles;
1758       dropped = 0;
1759       if (LIVES_IS_PLAYING && (!mainw->event_list  || mainw->record || mainw->preview_rendering)) {
1760         /// calculate dropped frames, this is ABS(frame - last_frame) - 1
1761         if (scratch != SCRATCH_NONE || getahead > -1 || drop_off) dropped = 0;
1762         else {
1763           if (mainw->frame_layer_preload && !cleanup_preload && mainw->pred_clip == mainw->playing_file
1764               && mainw->pred_frame != 0 && (labs(mainw->pred_frame) - mainw->actual_frame) * sig(sfile->pb_fps) > 0
1765               && is_layer_ready(mainw->frame_layer_preload))
1766             dropped = ABS(requested_frame - mainw->pred_frame) - 1;
1767           else
1768             dropped = ABS(requested_frame - mainw->actual_frame) - 1;
1769           if (dropped < 0) dropped = 0;
1770         }
1771 #ifdef ENABLE_PRECACHE
1772         if (getahead > -1) {
1773           if (mainw->pred_frame == -getahead) {
1774             if (is_layer_ready(mainw->frame_layer_preload))
1775               sfile->frameno = mainw->pred_frame = -mainw->pred_frame;
1776           } else {
1777             if (mainw->pred_frame == getahead) {
1778               if ((sfile->pb_fps > 0. && sfile->frameno >= getahead)
1779                   || (sfile->pb_fps < 0. && sfile->frameno <= getahead)) {
1780                 if (sfile->frameno != getahead) {
1781                   getahead = -1;
1782                   mainw->pred_frame = 0;
1783                   cleanup_preload = TRUE;
1784                   if (sfile->pb_fps > 0.)  /// not sure why yet but this doesnt work for rev. pb
1785                     sfile->last_frameno = requested_frame;
1786                   drop_off = FALSE;
1787                 }
1788               } else {
1789                 sfile->frameno = getahead;
1790 		// *INDENT-OFF*
1791               }}}}
1792 	// *INDENT-ON*
1793         else {
1794           lives_direction_t dir;
1795           if (sfile->clip_type == CLIP_TYPE_FILE && scratch == SCRATCH_NONE
1796               && is_virtual_frame(mainw->playing_file, sfile->frameno)
1797               && dropped > 0 && ((sfile->pb_fps < 0. && !clip_can_reverse(mainw->playing_file))
1798                                  || abs(sfile->frameno - sfile->last_vframe_played) >= JUMPFRAME_TRIGGER
1799                                  || dropped >= MIN(TEST_TRIGGER, DROPFRAME_TRIGGER))) {
1800 #ifdef SHOW_CACHE_PREDICTIONS
1801             if (abs(sfile->frameno - sfile->last_vframe_played) >= JUMPFRAME_TRIGGER) {
1802               lives_clip_data_t *cdata = ((lives_decoder_t *)sfile->ext_src)->cdata;
1803               if (cdata) {
1804                 g_print("decoder: seek flags = %d, jump_limit = %ld, max_fps = %.4f\n", cdata->seek_flag,
1805                         cdata->jump_limit, cdata->max_decode_fps);
1806                 g_print("vframe jump will be %d\n", requested_frame - sfile->last_vframe_played);
1807               }
1808             }
1809 #endif
1810             dir = LIVES_DIRECTION_SIG(sfile->pb_fps);
1811             if (bungle_frames <= -dir || bungle_frames == 0) bungle_frames = dir;
1812             //bungle_frames += requested_frame - mainw->actual_frame - dir;
1813             test_getahead = requested_frame + bungle_frames * dir;
1814             if (test_getahead < 1 || test_getahead > sfile->frames) test_getahead = -1;
1815             else {
1816 #ifdef SHOW_CACHE_PREDICTIONS
1817               g_print("getahead jumping to %d\n", test_getahead);
1818 #endif
1819               recalc_bungle_frames = TRUE;
1820               if (dropped > 0 && delta <= 0 && ((sfile->pb_fps < 0. && (!clip_can_reverse(mainw->current_file)))
1821                                                 || (abs(sfile->frameno - sfile->last_vframe_played) >= JUMPFRAME_TRIGGER)
1822                                                 || dropped >= DROPFRAME_TRIGGER)) {
1823                 getahead = test_getahead;
1824                 if (mainw->pred_frame > 0 && (mainw->pred_frame - mainw->actual_frame) * dir > 0
1825                     && mainw->frame_layer_preload && is_layer_ready(mainw->frame_layer_preload))
1826                   sfile->frameno = mainw->pred_frame;
1827                 else sfile->frameno = getahead;
1828 		// *INDENT-OFF*
1829 	      }}}
1830 	  // *INDENT-ON*
1831 #else
1832         if (1) {
1833 #endif
1834         }
1835 
1836 #ifdef HAVE_PULSE_AUDIO
1837         if (prefs->audio_player == AUD_PLAYER_PULSE) {
1838           if (getahead < 0 && new_ticks != mainw->startticks
1839               && (!mainw->pulsed || (mainw->pulsed->seek_pos == last_seek_pos
1840                                      && CLIP_HAS_AUDIO(mainw->pulsed->playing_file)))) {
1841             mainw->startticks = new_ticks;
1842             sfile->frameno = mainw->actual_frame;
1843           }
1844         }
1845 #endif
1846 
1847 #ifdef ENABLE_PRECACHE
1848         if (mainw->pred_clip == -1) {
1849           /// failed load, just reset
1850           mainw->frame_layer_preload = NULL;
1851           cleanup_preload = FALSE;
1852         } else if (cleanup_preload) {
1853           if (mainw->frame_layer_preload && is_layer_ready(mainw->frame_layer_preload)) {
1854             check_layer_ready(mainw->frame_layer_preload);
1855             weed_layer_free(mainw->frame_layer_preload);
1856             mainw->frame_layer_preload = NULL;
1857             cleanup_preload = FALSE;
1858           }
1859           if (mainw->frame_layer_preload) {
1860             cleanup_preload = FALSE;
1861           }
1862         }
1863 
1864         if (mainw->frame_layer_preload && !cleanup_preload) {
1865           if (mainw->pred_clip == mainw->current_file) {
1866             frames_t pframe = labs(mainw->pred_frame);
1867             if (((sfile->pb_fps > 0. && pframe >= mainw->actual_frame &&
1868                   (pframe <= requested_frame || is_virtual_frame(mainw->playing_file, sfile->frameno))) ||
1869                  (sfile->pb_fps < 0. && pframe <= mainw->actual_frame &&
1870                   (pframe >= requested_frame || is_virtual_frame(mainw->playing_file, sfile->frameno))))
1871                 && ((getahead > -1 || pframe == requested_frame || is_layer_ready(mainw->frame_layer_preload)))) {
1872               sfile->frameno = pframe;
1873             }
1874             if (pframe == sfile->frameno) cache_hits++;
1875             else if (getahead == -1) {
1876               if ((sfile->pb_fps > 0. && pframe <= mainw->actual_frame)
1877                   || (sfile->pb_fps < 0. && pframe >= mainw->actual_frame)) {
1878                 cleanup_preload = TRUE;
1879                 if (pframe != mainw->actual_frame) {
1880 #ifdef SHOW_CACHE_PREDICTIONS
1881                   g_print("WASTED cache frame %ld !!!! range was %d to %d or was not ready\n",
1882                           mainw->pred_frame, mainw->actual_frame,
1883                           sfile->frameno);
1884 #endif
1885                   cache_misses++;
1886                 }
1887 		  // *INDENT-OFF*
1888 		}}}}
1889 	  // *INDENT-ON*
1890 
1891         if (sfile->clip_type == CLIP_TYPE_FILE && is_virtual_frame(mainw->playing_file, sfile->frameno))
1892           sfile->last_vframe_played = sfile->frameno;
1893 #endif
1894       }
1895 
1896       if (prefs->pbq_adaptive && scratch == SCRATCH_NONE) {
1897         if (requested_frame != last_req_frame) {
1898           /// update the effort calculation with dropped frames and spare_cycles
1899           if (dropped > 0) update_effort(abs(requested_frame - last_req_frame - 1), TRUE);
1900           update_effort(spare_cycles + 1, FALSE);
1901         }
1902       }
1903 
1904       if (getahead < 0) {
1905         /// this is where we rebase the time for the next frame calculation
1906         /// if getahead >= 0 then we want to keep the base at the last "played" frame,
1907         //   and keep repeating getahead until we reach it
1908         /// but we did update last_start_ticks
1909         sfile->last_frameno = requested_frame;
1910         // set
1911         if (new_ticks > mainw->startticks) mainw->startticks = new_ticks;
1912         if (scratch != SCRATCH_NONE) {
1913           mainw->currticks = lives_get_current_playback_ticks(mainw->origsecs, mainw->orignsecs, NULL);
1914           mainw->startticks = mainw->currticks;
1915         }
1916       }
1917 
1918       mainw->last_startticks = mainw->startticks;
1919 
1920 #ifdef SHOW_CACHE_PREDICTIONS
1921       //g_print("dropped = %d, %d scyc = %ld %d %d\n", dropped, mainw->effort, spare_cycles, requested_frame, sfile->frameno);
1922 #endif
1923       if (mainw->pwidth != last_pwidth || mainw->pheight != last_pheight) {
1924         mainw->pred_frame = 0;
1925         cleanup_preload = TRUE;
1926         getahead = -1;
1927       }
1928 
1929       drop_off = FALSE;
1930       last_pwidth = mainw->pwidth;
1931       last_pheight = mainw->pheight;
1932       if (mainw->force_show || ((sfile->frameno != mainw->actual_frame || (sfile->frames == 1 && sfile->frameno == 1)
1933                                  || (mainw->playing_sel && sfile->start == sfile->end))
1934 #ifdef ENABLE_PRECACHE
1935                                 && getahead < 0)
1936           || (getahead > -1 && mainw->frame_layer_preload && !cleanup_preload && requested_frame != last_req_frame
1937 #endif
1938              )) {
1939         spare_cycles = 0;
1940         mainw->record_frame = requested_frame;
1941 
1942         /// note the audio seek position at the current frame. We will use this when switching clips
1943         /// we don;t know if the last audio buffer has been played or not yet, so we compensate by subtracting half the buffer length
1944 #ifdef ENABLE_JACK
1945         // note the audio seek position
1946         if (prefs->audio_player == AUD_PLAYER_JACK && mainw->jackd) {
1947           aplay_file = mainw->jackd->playing_file;
1948           if (IS_VALID_CLIP(aplay_file)) {
1949             int qnt = mainw->files[aplay_file]->achans * (mainw->files[aplay_file]->asampsize >> 3);
1950             mainw->files[aplay_file]->aseek_pos =
1951               (double)((off_t)((double) mainw->jackd->seek_pos / (double)mainw->files[aplay_file]->arps
1952                                / (mainw->files[aplay_file]->achans * mainw->files[aplay_file]->asampsize / 8)
1953                                * mainw->files[aplay_file]->fps + .5)) / mainw->files[aplay_file]->fps
1954               * mainw->files[aplay_file]->arps * qnt;
1955           }
1956         }
1957 #endif
1958 
1959 #ifdef HAVE_PULSE_AUDIO
1960         // note the audio seek position
1961         if (prefs->audio_player == AUD_PLAYER_PULSE && mainw->pulsed) {
1962           aplay_file = mainw->pulsed->playing_file;
1963           if (IS_VALID_CLIP(aplay_file)) {
1964             int qnt = mainw->files[aplay_file]->achans * (mainw->files[aplay_file]->asampsize >> 3);
1965             mainw->files[aplay_file]->aseek_pos = (int64_t)((double)((frames_t)((((double)mainw->pulsed->seek_pos
1966                                                   / (double)mainw->files[aplay_file]->arps / (double)qnt)
1967                                                   - ((double)(mainw->currticks - new_ticks) / TICKS_PER_SECOND_DBL))
1968                                                   * mainw->files[aplay_file]->fps))
1969                                                   / mainw->files[aplay_file]->fps * (double)mainw->files[aplay_file]->arps) * qnt;
1970 
1971             /* g_print("SPOS = %ld %d %d %ld %f\n", mainw->currticks, mainw->files[aplay_file]->frameno, */
1972             /* requested_frame, mainw->pulsed->seek_pos, */
1973             /* 	  (double) mainw->pulsed->seek_pos / (double)mainw->files[aplay_file]->arps / 4. * mainw->files[aplay_file]->fps + 1.); */
1974           }
1975         }
1976 #endif
1977 
1978 #if 0
1979         if (prefs->audio_player == AUD_PLAYER_NONE) {
1980           aplay_file = mainw->nullaudio->playing_file;
1981           if (IS_VALID_CLIP(aplay_file))
1982             mainw->files[aplay_file]->aseek_pos = nullaudio_get_seek_pos();
1983         }
1984 #endif
1985 
1986         // load and display the new frame
1987 #ifdef SHOW_CACHE_PREDICTIONS
1988         g_print("playing frame %d / %d at %ld (%ld : %ld) %.2f %ld %ld\n", sfile->frameno, requested_frame, mainw->currticks,
1989                 mainw->startticks, new_ticks, (mainw->pulsed->in_use && IS_VALID_CLIP(mainw->pulsed->playing_file)
1990                                                && mainw->files[mainw->pulsed->playing_file]->arate != 0)
1991                 ? (double)mainw->pulsed->seek_pos
1992                 / (double)mainw->files[mainw->pulsed->playing_file]->arate / 4. * sfile->fps + 1. : 0. * sfile->fps + 1,
1993                 lives_get_relative_ticks(mainw->origsecs, mainw->orignsecs), mainw->pulsed->seek_pos);
1994         last_seek_pos = mainw->pulsed->seek_pos;
1995 #endif
1996 
1997         load_frame_image(sfile->frameno);
1998         mainw->inst_fps = get_inst_fps();
1999         if (prefs->show_player_stats) {
2000           mainw->fps_measure++;
2001         }
2002 
2003         /// ignore actual value of actual_frame, since it can be messed with (e.g. nervous mode)
2004         mainw->actual_frame = sfile->frameno;
2005         mainw->fps_mini_measure++;
2006       } // end load_frame_image()
2007 
2008       else spare_cycles++;
2009 
2010       last_req_frame = requested_frame;
2011 
2012 #ifdef ENABLE_PRECACHE
2013       if (mainw->frame_layer_preload) {
2014         if (mainw->pred_clip != -1) {
2015           frames_t pframe = labs(mainw->pred_frame);
2016           if (mainw->pred_clip != mainw->current_file
2017               || (sfile->pb_fps >= 0. && (pframe <= requested_frame || pframe < sfile->frameno))
2018               || (sfile->pb_fps < 0. && (pframe >= requested_frame || pframe > sfile->frameno))) {
2019             cleanup_preload = TRUE;
2020             getahead = -1;
2021             drop_off = FALSE;
2022           }
2023         } else mainw->frame_layer_preload = NULL;
2024       }
2025 #endif
2026 
2027       if (!prefs->vj_mode) {
2028         //g_print("lfi done @ %f\n", lives_get_current_ticks() / TICKS_PER_SECOND_DBL);
2029         if (mainw->last_display_ticks == 0) mainw->last_display_ticks = mainw->clock_ticks;
2030         else {
2031           if (mainw->vpp && mainw->ext_playback && mainw->vpp->fixed_fpsd > 0.)
2032             mainw->last_display_ticks += TICKS_PER_SECOND_DBL / mainw->vpp->fixed_fpsd;
2033           else if (mainw->fixed_fpsd > 0.)
2034             mainw->last_display_ticks += TICKS_PER_SECOND_DBL / mainw->fixed_fpsd;
2035           else mainw->last_display_ticks = mainw->clock_ticks;
2036         }
2037       }
2038 
2039       mainw->force_show = FALSE;
2040       /// set this in case we switch
2041       sfile->frameno = requested_frame;
2042       scratch = SCRATCH_NONE;
2043     } // end show_frame
2044     else spare_cycles++;
2045   }
2046 
2047 #ifdef ENABLE_PRECACHE
2048   if (cleanup_preload) {
2049     if (mainw->frame_layer_preload) {
2050       if (getahead > -1 || is_layer_ready(mainw->frame_layer_preload)) {
2051         //wait_for_cleaner();
2052         if (mainw->pred_clip > 0) {
2053           check_layer_ready(mainw->frame_layer_preload);
2054           weed_layer_free(mainw->frame_layer_preload);
2055         }
2056         mainw->frame_layer_preload = NULL;
2057         mainw->pred_frame = 0;
2058         mainw->pred_clip = 0;
2059         cleanup_preload = FALSE;
2060 	  // *INDENT-OFF*
2061 	}}}
2062     // *INDENT-ON*
2063 #endif
2064 
2065   // paused
2066   if (LIVES_UNLIKELY(sfile->play_paused)) {
2067     mainw->startticks = mainw->currticks;
2068     mainw->video_seek_ready = TRUE;
2069   } else {
2070     if (!mainw->multitrack && scratch == SCRATCH_NONE && IS_NORMAL_CLIP(mainw->playing_file) && mainw->playing_file > 0
2071         && ((spare_cycles > 0ul && last_spare_cycles > 0ul) || (getahead > -1 && mainw->pred_frame != -getahead))) {
2072 #ifdef SHOW_CACHE_PREDICTIONS
2073       //g_print("PRELOADING (%d %d %lu %p):", sfile->frameno, dropped, spare_cycles, mainw->frame_layer_preload);
2074 #endif
2075 #ifdef ENABLE_PRECACHE
2076       if (!mainw->frame_layer_preload) {
2077         if (!mainw->preview) {
2078           mainw->pred_clip = mainw->playing_file;
2079           if (getahead > -1) mainw->pred_frame = getahead;
2080           else {
2081             if (sfile->pb_fps > 0.)
2082               mainw->pred_frame = sfile->frameno + 1 + dropped;
2083             else
2084               mainw->pred_frame = sfile->frameno - 1 - dropped;
2085           }
2086           if (mainw->pred_frame > 0 && mainw->pred_frame < sfile->frames) {
2087             const char *img_ext = get_image_ext_for_type(sfile->img_type);
2088             if (!is_virtual_frame(mainw->pred_clip, mainw->pred_frame)) {
2089               mainw->frame_layer_preload = lives_layer_new_for_frame(mainw->pred_clip, mainw->pred_frame);
2090               pull_frame_threaded(mainw->frame_layer_preload, img_ext, (weed_timecode_t)mainw->currticks, 0, 0);
2091             } else {
2092               // if the target is a clip-frame we have to decode it now, since we cannot simply decode 2 frames simultaneously
2093               // (although it could be possible in the future to have 2 clone decoders and have them leapfrog...)
2094               // NOTE: change to labs when frames_t updated
2095               mainw->frame_layer_preload = lives_layer_new_for_frame(mainw->pred_clip, mainw->pred_frame);
2096               if (!pull_frame_at_size(mainw->frame_layer_preload, img_ext,
2097                                       (weed_timecode_t)mainw->currticks,
2098                                       sfile->hsize, sfile->vsize, WEED_PALETTE_END)) {
2099                 if (mainw->frame_layer_preload) {
2100                   weed_layer_free(mainw->frame_layer_preload);
2101                   mainw->frame_layer_preload = NULL;
2102                   mainw->pred_clip = -1;
2103                 }
2104 #ifdef SHOW_CACHE_PREDICTIONS
2105                 g_print("failed to load frame %ld\n", mainw->pred_frame);
2106 #endif
2107               }
2108             }
2109             if (mainw->pred_clip != -1) {
2110 #ifdef SHOW_CACHE_PREDICTIONS
2111               g_print("cached frame %ld\n", mainw->pred_frame);
2112 #endif
2113               if (getahead > 0) {
2114                 mainw->pred_frame = -getahead;
2115                 mainw->force_show = TRUE;
2116               }
2117 		// *INDENT-OFF*
2118 	      }}
2119 	    else mainw->pred_frame = 0;
2120 	  }}
2121 #ifdef SHOW_CACHE_PREDICTIONS
2122 	//g_print("frame %ld already in cache\n", mainw->pred_frame);
2123 #endif
2124 #endif
2125       }}
2126     // *INDENT-ON*
2127 
2128 proc_dialog:
2129 
2130   if (visible) {
2131     if (!mainw->proc_ptr) {
2132       // fixes a problem with opening preview with bg generator
2133       if (mainw->cancelled == CANCEL_NONE) mainw->cancelled = CANCEL_NO_PROPOGATE;
2134     } else {
2135       if (LIVES_IS_SPIN_BUTTON(mainw->framedraw_spinbutton))
2136         lives_spin_button_set_range(LIVES_SPIN_BUTTON(mainw->framedraw_spinbutton), 1,
2137                                     mainw->proc_ptr->frames_done);
2138       // set the progress bar %
2139       update_progress(visible);
2140     }
2141 
2142     frames_done = mainw->proc_ptr->frames_done;
2143 
2144     if (cfile->clip_type == CLIP_TYPE_FILE && cfile->fx_frame_pump > 0) {
2145       if (virtual_to_images(mainw->current_file, cfile->fx_frame_pump, cfile->fx_frame_pump, FALSE, NULL) > 0) {
2146         cfile->fx_frame_pump++;
2147       } else mainw->cancelled = CANCEL_ERROR;
2148       if (cfile->fx_frame_pump >= cfile->end) cfile->fx_frame_pump = 0; // all frames were realised
2149     }
2150   }
2151 
2152   if (LIVES_LIKELY(mainw->cancelled == CANCEL_NONE)) {
2153     lives_rfx_t *xrfx;
2154 
2155     if ((xrfx = (lives_rfx_t *)mainw->vrfx_update) != NULL && fx_dialog[1]) {
2156       // the audio thread wants to update the parameter window
2157       mainw->vrfx_update = NULL;
2158       update_visual_params(xrfx, FALSE);
2159     }
2160 
2161     // the audio thread wants to update the parameter scroll(s)
2162     if (mainw->ce_thumbs) ce_thumbs_apply_rfx_changes();
2163 
2164     /// we are permitted to switch clips here under very restricitive circumstances, e.g when opening a clip
2165     if (mainw->cs_permitted) {
2166       mainw->cs_is_permitted = TRUE;
2167       old_current_file = mainw->current_file;
2168     }
2169 
2170     if (mainw->currticks - last_anim_ticks > ANIM_LIM) {
2171       // a segfault here can indicate memory corruption in an FX plugin
2172       last_anim_ticks = mainw->currticks;
2173       lives_widget_context_update();
2174     }
2175 
2176     /// if we did switch clips then cancel the dialog without cancelling the background process
2177     if (mainw->cs_is_permitted) {
2178       mainw->cs_is_permitted = FALSE;
2179       if (mainw->current_file != old_current_file) mainw->cancelled = CANCEL_NO_PROPOGATE;
2180     }
2181 
2182     if (!CURRENT_CLIP_IS_VALID) {
2183       if (IS_VALID_CLIP(mainw->new_clip)) goto switch_point;
2184       mainw->cancelled = CANCEL_INTERNAL_ERROR;
2185     }
2186 
2187     if (mainw->cancelled != CANCEL_NONE) {
2188       cancel_process(visible);
2189       return ONE_MILLION + mainw->cancelled;
2190     }
2191     return 0;
2192   }
2193 
2194   if (LIVES_IS_PLAYING) {
2195     if (mainw->record && !mainw->record_paused)
2196       event_list_add_end_events(mainw->event_list, TRUE);
2197     if (mainw->jack_can_stop) mainw->jack_can_start = FALSE;
2198     mainw->jack_can_stop = FALSE;
2199   }
2200 
2201   cancel_process(visible);
2202 
2203   return 2000000 + mainw->cancelled;
2204 }
2205 #if 0
2206 }
2207 #endif
2208 
2209 #ifdef USE_GDK_FRAME_CLOCK
2210 static boolean using_gdk_frame_clock;
2211 static GdkFrameClock *gclock;
clock_upd(GdkFrameClock * clock,gpointer user_data)2212 static void clock_upd(GdkFrameClock * clock, gpointer user_data) {
2213   display_ready = TRUE;
2214 }
2215 #endif
2216 
2217 
reset_timebase(void)2218 static boolean reset_timebase(void) {
2219   // [IMPORTANT] we subtract these from every calculation to make the numbers smaller
2220 #if _POSIX_TIMERS
2221   ticks_t originticks = lives_get_current_ticks();
2222   originticks *= TICKS_TO_NANOSEC;
2223   mainw->origsecs = originticks / ONE_BILLION;
2224   mainw->orignsecs = originticks - mainw->origsecs * ONE_BILLION;
2225 #else
2226 
2227 #ifdef USE_MONOTONIC_TIME
2228   mainw->origsecs = 0; // not used
2229   mainw->orignsecs = lives_get_monotonic_time() * 1000;
2230 #else
2231 
2232   /***************************************************/
2233   gettimeofday(&tv, NULL);
2234   /***************************************************/
2235 
2236   mainw->origsecs = tv.tv_sec;
2237   mainw->orignsecs = tv.tv_usec * 1000;
2238 #endif
2239 #endif
2240 
2241 #ifdef HAVE_PULSE_AUDIO
2242   if (prefs->audio_player == AUD_PLAYER_PULSE) {
2243     boolean pa_reset = TRUE;
2244     if (prefs->audio_src == AUDIO_SRC_INT) {
2245       if (mainw->pulsed && !pa_time_reset(mainw->pulsed, 0)) {
2246         pa_reset = FALSE;
2247       }
2248     } else {
2249       if (mainw->pulsed_read && !pa_time_reset(mainw->pulsed_read, 0)) {
2250         pa_reset = FALSE;
2251       }
2252     }
2253     if (!pa_reset) {
2254       handle_audio_timeout();
2255       return FALSE;
2256     }
2257   }
2258 #endif
2259 
2260 #ifdef ENABLE_JACK
2261   if (mainw->jackd) {
2262     jack_time_reset(mainw->jackd, 0);
2263   }
2264   if (mainw->jackd_read) {
2265     jack_time_reset(mainw->jackd_read, 0);
2266   }
2267 #endif
2268 
2269   reset_playback_clock();
2270   return TRUE;
2271 }
2272 
2273 
do_progress_dialog(boolean visible,boolean cancellable,const char * text)2274 boolean do_progress_dialog(boolean visible, boolean cancellable, const char *text) {
2275   // monitor progress, return FALSE if the operation was cancelled
2276 
2277   // this is the outer loop for playback and all kinds of processing
2278 
2279   // visible is set for processing (progress dialog is visible)
2280   // or unset for video playback (progress dialog is not shown)
2281   char *mytext = NULL;
2282   frames_t frames_done, frames;
2283   boolean got_err = FALSE;
2284   boolean markup = widget_opts.use_markup;
2285 
2286   widget_opts.use_markup = FALSE;
2287 
2288   // translation issues
2289   if (visible && text) mytext = lives_strdup(text);
2290 
2291   if (visible) {
2292     // check we have sufficient storage space
2293     if (mainw->next_ds_warn_level != 0 &&
2294         !check_storage_space(mainw->current_file, FALSE)) {
2295       lives_cancel_t cancelled = mainw->cancelled;
2296       on_cancel_keep_button_clicked(NULL, NULL);
2297       if (mainw->cancelled != CANCEL_NONE) mainw->cancelled = cancelled;
2298       d_print_cancelled();
2299       if ((mainw->disk_mon & MONITOR_QUOTA) && prefs->disk_quota) disk_monitor_forget();
2300       return FALSE;
2301     }
2302   }
2303 
2304   event_start = 0;
2305   audio_start = mainw->play_start;
2306   if (visible) accelerators_swapped = FALSE;
2307   frames_done = 0;
2308   disp_fraction_done = 0.;
2309   mainw->last_display_ticks = 0;
2310   shown_paused_frames = FALSE;
2311 
2312   mainw->cevent_tc = -1;
2313 
2314   progress_count = 0;
2315   progress_speed = 4.;
2316   prog_fs_check = 0;
2317 
2318   mainw->render_error = LIVES_RENDER_ERROR_NONE;
2319 
2320   if (!visible) {
2321     reset_frame_and_clip_index();
2322     mainw->force_show = TRUE;
2323     cleanup_preload = FALSE;
2324   } else mainw->force_show = FALSE;
2325 
2326   mainw->cancelled = CANCEL_NONE;
2327   mainw->error = FALSE;
2328   clear_mainw_msg();
2329   if (!mainw->preview || cfile->opening) mainw->timeout_ticks = 0;
2330 
2331   if (visible) {
2332     mainw->noswitch = TRUE;
2333     mainw->is_processing = TRUE;
2334     desensitize();
2335     procw_desensitize();
2336     lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
2337 
2338     widget_opts.use_markup = markup;
2339     mainw->proc_ptr = create_processing(mytext);
2340     mainw->proc_ptr->owner = mainw->current_file;
2341 
2342     if (CURRENT_CLIP_IS_VALID) {
2343       mainw->proc_ptr->progress_start = cfile->progress_start;
2344       mainw->proc_ptr->progress_end = cfile->progress_end;
2345     } else {
2346       mainw->proc_ptr->progress_start = mainw->proc_ptr->progress_end = 0;
2347     }
2348 
2349     lives_freep((void **)&mytext);
2350 
2351     lives_progress_bar_set_pulse_step(LIVES_PROGRESS_BAR(mainw->proc_ptr->progressbar), .01);
2352 
2353     mainw->proc_ptr->frames_done = 0;
2354 
2355     if (!cancellable) {
2356       lives_widget_hide(mainw->proc_ptr->cancel_button);
2357     }
2358 
2359     if (!LIVES_IS_INTERACTIVE) {
2360       lives_widget_set_sensitive(mainw->proc_ptr->cancel_button, FALSE);
2361       if (mainw->proc_ptr->stop_button)
2362         lives_widget_set_sensitive(mainw->proc_ptr->stop_button, FALSE);
2363       lives_widget_set_sensitive(mainw->proc_ptr->pause_button, FALSE);
2364       lives_widget_set_sensitive(mainw->proc_ptr->preview_button, FALSE);
2365     }
2366 
2367     if (cfile->opening && (capable->has_sox_play || (prefs->audio_player == AUD_PLAYER_JACK && mainw->jackd) ||
2368                            (prefs->audio_player == AUD_PLAYER_PULSE && mainw->pulsed)) && !LIVES_IS_PLAYING) {
2369       if (mainw->preview_box) lives_widget_set_tooltip_text(mainw->p_playbutton, _("Preview"));
2370       lives_widget_set_tooltip_text(mainw->m_playbutton, _("Preview"));
2371       lives_widget_remove_accelerator(mainw->playall, mainw->accel_group, LIVES_KEY_p, (LiVESXModifierType)0);
2372       lives_widget_add_accelerator(mainw->proc_ptr->preview_button, LIVES_WIDGET_CLICKED_SIGNAL, mainw->accel_group, LIVES_KEY_p,
2373                                    (LiVESXModifierType)0, (LiVESAccelFlags)0);
2374       accelerators_swapped = TRUE;
2375     }
2376   }
2377 
2378   if (cfile->next_event) event_start = get_event_timecode(cfile->next_event);
2379 
2380   /// INIT here
2381   cache_hits = cache_misses = 0;
2382   dropped = 0;
2383   init_timers = TRUE;
2384 
2385   // mainw->origsecs, mainw->orignsecs is our base for quantising
2386   // (and is constant for each playback session)
2387 
2388   // firstticks is to do with the audio "frame" for sox, mplayer
2389   // startticks is the ticks value of the last frame played
2390 
2391   last_open_check_ticks = mainw->offsetticks = mainw->deltaticks = mainw->adjticks = 0;
2392 
2393 #ifdef ENABLE_JACK
2394   if (mainw->record && prefs->audio_src == AUDIO_SRC_EXT && prefs->audio_player == AUD_PLAYER_JACK &&
2395       mainw->jackd_read && prefs->ahold_threshold > 0.) {
2396     // if recording with external audio, wait for audio threshold before commencing
2397     mainw->jackd_read->abs_maxvol_heard = 0.;
2398     cfile->progress_end = 0;
2399     do_threaded_dialog(_("Waiting for external audio"), TRUE);
2400     while (mainw->jackd_read->abs_maxvol_heard < prefs->ahold_threshold && mainw->cancelled == CANCEL_NONE) {
2401       lives_usleep(prefs->sleep_time);
2402       threaded_dialog_spin(0.);
2403       lives_widget_context_update();
2404     }
2405     end_threaded_dialog();
2406     mainw->proc_ptr = NULL;
2407     if (mainw->cancelled != CANCEL_NONE) {
2408       if ((mainw->disk_mon & MONITOR_QUOTA) && prefs->disk_quota) disk_monitor_forget();
2409       return FALSE;
2410     }
2411   }
2412 #endif
2413 
2414 #ifdef HAVE_PULSE_AUDIO
2415   // start audio recording now
2416   if (mainw->pulsed_read) {
2417     pulse_driver_uncork(mainw->pulsed_read);
2418   }
2419   if (mainw->record && prefs->audio_src == AUDIO_SRC_EXT && prefs->audio_player == AUD_PLAYER_PULSE &&
2420       prefs->ahold_threshold > 0.) {
2421     cfile->progress_end = 0;
2422     do_threaded_dialog(_("Waiting for external audio"), TRUE);
2423     while (mainw->pulsed_read->abs_maxvol_heard < prefs->ahold_threshold && mainw->cancelled == CANCEL_NONE) {
2424       lives_usleep(prefs->sleep_time);
2425       threaded_dialog_spin(0.);
2426       lives_widget_context_update();
2427     }
2428     end_threaded_dialog();
2429     if (mainw->cancelled != CANCEL_NONE) {
2430       if ((mainw->disk_mon & MONITOR_QUOTA) && prefs->disk_quota) disk_monitor_forget();
2431       return FALSE;
2432     }
2433   }
2434 #endif
2435   last_kbd_ticks = 0;
2436   mainw->scratch = SCRATCH_NONE;
2437   if (mainw->iochan) lives_widget_show_all(mainw->proc_ptr->pause_button);
2438   display_ready = TRUE;
2439 
2440   last_time_source = LIVES_TIME_SOURCE_NONE;
2441 
2442   /////////////////////////
2443   if (!reset_timebase()) {
2444     mainw->cancelled = CANCEL_INTERNAL_ERROR;
2445     return FALSE;
2446   }
2447   //////////////////////////
2448   //if (mainw->disk_mon & MONITOR_GROWTH) disk_monitor_start(mainw->monitor_dir);
2449   if ((mainw->disk_mon & MONITOR_QUOTA && prefs->disk_quota)) disk_monitor_start(prefs->workdir);
2450 
2451   if (visible) {
2452     proc_start_ticks = lives_get_current_ticks();
2453   } else {
2454     // video playback
2455     if (mainw->event_list || !CLIP_HAS_VIDEO(mainw->playing_file)) mainw->video_seek_ready = TRUE;
2456     if (mainw->event_list || !CLIP_HAS_AUDIO(mainw->playing_file)) mainw->audio_seek_ready = TRUE;
2457     cfile->last_frameno = cfile->frameno = mainw->play_start;
2458   }
2459 
2460   if (!mainw->playing_sel) mainw->play_start = 1;
2461 
2462   if (mainw->multitrack && !mainw->multitrack->is_rendering) {
2463     // playback start from middle of multitrack
2464     // calculate when we "would have started" at time 0
2465     mainw->offsetticks = get_event_timecode(mainw->multitrack->pb_start_event);
2466   }
2467 
2468   // set initial audio seek position for current file
2469   if (cfile->achans) {
2470     mainw->aframeno = calc_frame_from_time4(mainw->current_file,
2471                                             cfile->aseek_pos / cfile->arps / cfile->achans / (cfile->asampsize >> 3));
2472   }
2473   frames = cfile->frames;
2474   cfile->frames = 0; // allow seek beyond video length
2475   // MUST do re-seek after setting origsecs in order to set our clock properly
2476   // re-seek to new playback start
2477 #ifdef ENABLE_JACK
2478   if (prefs->audio_player == AUD_PLAYER_JACK && cfile->achans > 0 && cfile->laudio_time > 0. &&
2479       !mainw->is_rendering && !(cfile->opening && !mainw->preview) && mainw->jackd
2480       && mainw->jackd->playing_file > -1) {
2481     if (!jack_audio_seek_frame(mainw->jackd, mainw->aframeno)) {
2482       if ((mainw->disk_mon & MONITOR_QUOTA) && prefs->disk_quota) disk_monitor_forget();
2483       return FALSE;
2484       /* if (jack_try_reconnect()) jack_audio_seek_frame(mainw->jackd, mainw->aframeno); */
2485       /* else mainw->video_seek_ready = mainw->audio_seek_ready = TRUE; */
2486     }
2487 
2488     if (!(mainw->record && (prefs->audio_src == AUDIO_SRC_EXT || mainw->agen_key != 0 || mainw->agen_needs_reinit)))
2489       jack_get_rec_avals(mainw->jackd);
2490     else {
2491       mainw->rec_aclip = mainw->ascrap_file;
2492       mainw->rec_avel = 1.;
2493       mainw->rec_aseek = 0;
2494     }
2495     /* if (prefs->audio_src == AUDIO_SRC_INT) */
2496     /*   mainw->jackd->in_use = TRUE; */
2497   }
2498 #endif
2499 #ifdef HAVE_PULSE_AUDIO
2500   if (prefs->audio_player == AUD_PLAYER_PULSE && cfile->achans > 0 && cfile->laudio_time > 0. &&
2501       !mainw->is_rendering && !(cfile->opening && !mainw->preview) && mainw->pulsed
2502       && mainw->pulsed->playing_file > -1) {
2503     if (!pulse_audio_seek_frame(mainw->pulsed, mainw->aframeno)) {
2504       handle_audio_timeout();
2505       if ((mainw->disk_mon & MONITOR_QUOTA) && prefs->disk_quota) disk_monitor_forget();
2506       return FALSE;
2507     }
2508 
2509     if (!(mainw->record && (prefs->audio_src == AUDIO_SRC_EXT || mainw->agen_key != 0 || mainw->agen_needs_reinit)))
2510       pulse_get_rec_avals(mainw->pulsed);
2511     else {
2512       mainw->rec_aclip = mainw->ascrap_file;
2513       mainw->rec_avel = 1.;
2514       mainw->rec_aseek = 0;
2515     }
2516   }
2517 #endif
2518   cfile->frames = frames;
2519 
2520 #ifdef USE_GDK_FRAME_CLOCK
2521   using_gdk_frame_clock = FALSE;
2522   if (prefs->show_gui) {
2523     using_gdk_frame_clock = TRUE;
2524     display_ready = FALSE;
2525     gclock = gtk_widget_get_frame_clock(LIVES_MAIN_WINDOW_WIDGET);
2526     gdk_frame_clock_begin_updating(gclock);
2527     lives_signal_sync_connect(LIVES_GUI_OBJECT(gclock), "update",
2528                               LIVES_GUI_CALLBACK(clock_upd), NULL);
2529   }
2530 #endif
2531 
2532 #ifdef HAVE_PULSE_AUDIO
2533   if (mainw->pulsed_read) {
2534     mainw->pulsed_read->is_paused = FALSE;
2535   }
2536 #endif
2537 #ifdef ENABLE_JACK
2538   if (mainw->jackd_read) {
2539     mainw->jackd_read->is_paused = FALSE;
2540   }
2541 #endif
2542 
2543   if (mainw->record) mainw->record_paused = FALSE;
2544 
2545   if (!mainw->proc_ptr && cfile->next_event) {
2546     /// reset dropped frame count etc
2547     process_events(NULL, FALSE, 0);
2548   }
2549 
2550   if (prefs->pbq_adaptive) reset_effort();
2551   if (mainw->multitrack && !mainw->multitrack->is_rendering) mainw->effort = EFFORT_RANGE_MAX;
2552   getahead = -1;
2553   drop_off = FALSE;
2554   bungle_frames = -1;
2555   recalc_bungle_frames = FALSE;
2556   spare_cycles = 0ul;
2557 
2558   //try to open info file - or if internal_messaging is TRUE, we get mainw->msg
2559   // from the mainw->progress_fn function
2560   while (1) {
2561     while (!mainw->internal_messaging && ((!visible && (mainw->whentostop != STOP_ON_AUD_END ||
2562                                            is_realtime_aplayer(prefs->audio_player))) ||
2563                                           !lives_file_test(cfile->info_file, LIVES_FILE_TEST_EXISTS))) {
2564       // just pulse the progress bar, or play video
2565       // returns a code if pb stopped
2566       int ret;
2567       if ((ret = process_one(visible))) {
2568         if (visible) mainw->noswitch = FALSE;
2569         //g_print("pb stopped, reason %d\n", ret);
2570         lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
2571 #ifdef USE_GDK_FRAME_CLOCK
2572         if (using_gdk_frame_clock) {
2573           gdk_frame_clock_end_updating(gclock);
2574         }
2575 #endif
2576         if ((mainw->disk_mon & MONITOR_QUOTA) && prefs->disk_quota) disk_monitor_forget();
2577         return FALSE;
2578       }
2579 
2580       /* if (mainw->disk_mon & MONITOR_GROWTH) { */
2581       /* 	int64_t dsused = disk_monitor_check_result(mainw->monitor_dir); */
2582       /* 	if (dsused >= 0) mainw->monitor_size = dsused; */
2583       /* 	disk_monitor_start(mainw->monitor_dir); */
2584       /* } */
2585       if ((mainw->disk_mon & MONITOR_QUOTA) && prefs->disk_quota) {
2586         int64_t dsused = disk_monitor_check_result(prefs->workdir);
2587         if (dsused >= 0) {
2588           capable->ds_used = dsused;
2589         }
2590         disk_monitor_start(prefs->workdir);
2591         mainw->dsu_valid = FALSE;
2592       }
2593 
2594       if (LIVES_UNLIKELY(mainw->agen_needs_reinit)) {
2595         // we are generating audio from a plugin and it needs reinit
2596         // - we do it in this thread so as not to hold up the player thread
2597         reinit_audio_gen();
2598       }
2599 
2600       if ((visible && !mainw->internal_messaging)
2601           || (LIVES_IS_PLAYING && CURRENT_CLIP_IS_VALID && cfile->play_paused)) lives_usleep(prefs->sleep_time);
2602 
2603       // normal playback, wth realtime audio player
2604       if (!visible && (mainw->whentostop != STOP_ON_AUD_END || is_realtime_aplayer(prefs->audio_player))) continue;
2605 
2606       if (mainw->iochan && !progress_count) {
2607         // pump data from stdout to textbuffer
2608         // this is for encoder output
2609         pump_io_chan(mainw->iochan);
2610       }
2611       if (!mainw->internal_messaging) {
2612         // background processing (e.g. rendered effects)
2613         progbar_pulse_or_fraction(cfile, mainw->proc_ptr->frames_done, mainw->proc_ptr->frac_done);
2614       }
2615     }
2616 
2617     if (!mainw->internal_messaging) {
2618       // background processing (e.g. rendered effects)
2619       lives_fread_string(mainw->msg, MAINW_MSG_SIZE, cfile->info_file);
2620       progbar_pulse_or_fraction(cfile, mainw->proc_ptr->frames_done, mainw->proc_ptr->frac_done);
2621     }
2622     // else call realtime effect pass
2623     else {
2624       mainw->render_error = (*mainw->progress_fn)(FALSE);
2625 
2626       if (mainw->render_error >= LIVES_RENDER_ERROR) {
2627         got_err = TRUE;
2628         goto finish;
2629       }
2630 
2631       // display progress fraction or pulse bar
2632       if (*mainw->msg && (frames_done = atoi(mainw->msg)) > 0) {
2633         if (mainw->msg[lives_strlen(mainw->msg) - 1] == '%')
2634           mainw->proc_ptr->frac_done = atof(mainw->msg);
2635         else
2636           mainw->proc_ptr->frames_done = atoi(mainw->msg);
2637       } else
2638         mainw->proc_ptr->frames_done = 0;
2639       if (!mainw->effects_paused) {
2640         if (prog_fs_check-- <= 0) {
2641           check_storage_space(mainw->current_file, TRUE);
2642           prog_fs_check = PROG_LOOP_VAL;
2643         }
2644         progbar_pulse_or_fraction(cfile, mainw->proc_ptr->frames_done, mainw->proc_ptr->frac_done);
2645       } else lives_widget_context_update();
2646     }
2647 
2648     if (mainw->preview_req) {
2649       mainw->preview_req = FALSE;
2650       mainw->noswitch = FALSE;
2651       on_preview_clicked(LIVES_BUTTON(mainw->proc_ptr->preview_button), NULL);
2652       mainw->noswitch = TRUE;
2653     }
2654 
2655     //    #define DEBUG
2656 #ifdef DEBUG
2657     if (*(mainw->msg)) g_print("%s msg %s\n", cfile->info_file, mainw->msg);
2658 #endif
2659 
2660     // we got a message from the backend...
2661 
2662     if (visible && (!accelerators_swapped || cfile->opening) && cancellable
2663         && (!cfile->nopreview || cfile->keep_without_preview)) {
2664       if (!cfile->nopreview && !(cfile->opening && mainw->multitrack)) {
2665         lives_widget_set_no_show_all(mainw->proc_ptr->preview_button, FALSE);
2666         lives_widget_show_all(mainw->proc_ptr->preview_button);
2667         lives_button_grab_default_special(mainw->proc_ptr->preview_button);
2668         lives_widget_grab_focus(mainw->proc_ptr->preview_button);
2669       }
2670 
2671       // show buttons
2672       if (cfile->opening_loc) {
2673         lives_widget_hide(mainw->proc_ptr->pause_button);
2674         if (mainw->proc_ptr->stop_button)
2675           lives_widget_show_all(mainw->proc_ptr->stop_button);
2676       } else {
2677         lives_widget_show_all(mainw->proc_ptr->pause_button);
2678         if (mainw->proc_ptr->stop_button)
2679           lives_widget_hide(mainw->proc_ptr->stop_button);
2680       }
2681 
2682       if (!cfile->opening && !cfile->nopreview) {
2683         lives_button_grab_default_special(mainw->proc_ptr->preview_button);
2684         lives_widget_grab_focus(mainw->proc_ptr->preview_button);
2685         if (mainw->preview_box) lives_widget_set_tooltip_text(mainw->p_playbutton, _("Preview"));
2686         lives_widget_set_tooltip_text(mainw->m_playbutton, _("Preview"));
2687         lives_widget_remove_accelerator(mainw->playall, mainw->accel_group, LIVES_KEY_p, (LiVESXModifierType)0);
2688         lives_widget_add_accelerator(mainw->proc_ptr->preview_button, LIVES_WIDGET_CLICKED_SIGNAL,
2689                                      mainw->accel_group, LIVES_KEY_p,
2690                                      (LiVESXModifierType)0, (LiVESAccelFlags)0);
2691         accelerators_swapped = TRUE;
2692       }
2693     }
2694 
2695     //    g_print("MSG is %s\n", mainw->msg);
2696 
2697     if (lives_strncmp(mainw->msg, "completed", 8) && strncmp(mainw->msg, "error", 5) &&
2698         strncmp(mainw->msg, "killed", 6) && (visible ||
2699             ((lives_strncmp(mainw->msg, "video_ended", 11) || mainw->whentostop != STOP_ON_VID_END)
2700              && (lives_strncmp(mainw->msg, "audio_ended", 11) || mainw->preview ||
2701                  mainw->whentostop != STOP_ON_AUD_END)))) {
2702       // processing not yet completed...
2703       if (visible) {
2704         // last frame processed ->> will go from cfile->start to cfile->end
2705         int numtok = get_token_count(mainw->msg, '|');
2706         // get progress count from backend
2707         if (numtok > 1) {
2708           char **array = lives_strsplit(mainw->msg, "|", numtok);
2709           mainw->proc_ptr->frames_done = atoi(array[0]);
2710           if (numtok == 2 && *(array[1])) cfile->progress_end = atoi(array[1]);
2711           else if (numtok == 5 && *(array[4])) {
2712             // rendered generators
2713             cfile->start = cfile->undo_start = 1;
2714             cfile->frames = cfile->end = cfile->undo_end = atoi(array[0]);
2715             cfile->hsize = atoi(array[1]);
2716             cfile->vsize = atoi(array[2]);
2717             cfile->fps = cfile->pb_fps = strtod(array[3], NULL);
2718             if (cfile->fps == 0.) cfile->fps = cfile->pb_fps = prefs->default_fps;
2719             cfile->progress_end = atoi(array[4]);
2720           }
2721           lives_strfreev(array);
2722         } else {
2723           if (*mainw->msg && mainw->msg[lives_strlen(mainw->msg) - 1] == '%')
2724             mainw->proc_ptr->frac_done = atof(mainw->msg);
2725           else
2726             mainw->proc_ptr->frames_done = atoi(mainw->msg);
2727         }
2728       }
2729 
2730       // do a processing pass
2731       if (process_one(visible)) {
2732         lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
2733 #ifdef USE_GDK_FRAME_CLOCK
2734         if (using_gdk_frame_clock) {
2735           gdk_frame_clock_end_updating(gclock);
2736         }
2737 #endif
2738         if ((mainw->disk_mon & MONITOR_QUOTA) && prefs->disk_quota) disk_monitor_forget();
2739         if (visible) mainw->noswitch = FALSE;
2740         return FALSE;
2741       }
2742 
2743       if ((mainw->disk_mon & MONITOR_QUOTA) && prefs->disk_quota) {
2744         int64_t dsused = disk_monitor_check_result(prefs->workdir);
2745         if (dsused >= 0) {
2746           capable->ds_used = dsused;
2747         }
2748         disk_monitor_start(prefs->workdir);
2749         mainw->dsu_valid = FALSE;
2750       }
2751 
2752 
2753       if (LIVES_UNLIKELY(mainw->agen_needs_reinit)) {
2754         // we are generating audio from a plugin and it needs reinit
2755         // - we do it in this thread so as not to hold up the player thread
2756         reinit_audio_gen();
2757       }
2758 
2759       if (mainw->iochan && progress_count == 0) {
2760         // pump data from stdout to textbuffer
2761         pump_io_chan(mainw->iochan);
2762       }
2763 
2764       if (!mainw->internal_messaging) lives_nanosleep(1000);
2765     } else break;
2766   }
2767 
2768 #ifdef USE_GDK_FRAME_CLOCK
2769   if (using_gdk_frame_clock) {
2770     gdk_frame_clock_end_updating(gclock);
2771   }
2772 #endif
2773 
2774 #ifdef DEBUG
2775   g_print("exit pt 3 %s\n", mainw->msg);
2776 #endif
2777 
2778 finish:
2779   if ((mainw->disk_mon & MONITOR_QUOTA) && prefs->disk_quota) disk_monitor_forget();
2780 
2781   //play/operation ended
2782   if (visible) {
2783     if (cfile->clip_type == CLIP_TYPE_DISK && (mainw->cancelled != CANCEL_NO_MORE_PREVIEW || !cfile->opening)) {
2784       lives_rm(cfile->info_file);
2785     }
2786     if (mainw->preview_box && !mainw->preview) lives_widget_set_tooltip_text(mainw->p_playbutton, _("Play all"));
2787     if (accelerators_swapped) {
2788       if (!mainw->preview) lives_widget_set_tooltip_text(mainw->m_playbutton, _("Play all"));
2789       lives_widget_remove_accelerator(mainw->proc_ptr->preview_button, mainw->accel_group, LIVES_KEY_p, (LiVESXModifierType)0);
2790       lives_widget_add_accelerator(mainw->playall, LIVES_WIDGET_ACTIVATE_SIGNAL, mainw->accel_group, LIVES_KEY_p,
2791                                    (LiVESXModifierType)0, LIVES_ACCEL_VISIBLE);
2792       accelerators_swapped = FALSE;
2793     }
2794     if (mainw->proc_ptr) {
2795       const char *btext = NULL;
2796       if (mainw->iochan) btext = lives_text_view_get_text(mainw->optextview);
2797       if (mainw->proc_ptr->processing) lives_widget_destroy(mainw->proc_ptr->processing);
2798       lives_free(mainw->proc_ptr);
2799       mainw->proc_ptr = NULL;
2800       if (btext) {
2801         lives_text_view_set_text(mainw->optextview, btext, -1);
2802         lives_free((char *)btext);
2803       }
2804     }
2805     mainw->is_processing = FALSE;
2806     if (cfile->menuentry) {
2807       // note - for operations to/from clipboard (file 0) we
2808       // should manually call sensitize() after operation
2809       sensitize();
2810     }
2811   } else {
2812     mainw->is_processing = TRUE;
2813   }
2814 
2815   lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
2816   // get error message (if any)
2817   if (!strncmp(mainw->msg, "error", 5)) {
2818     handle_backend_errors(FALSE);
2819     if (mainw->cancelled || mainw->error) {
2820       if (visible) mainw->noswitch = FALSE;
2821       return FALSE;
2822     }
2823   } else {
2824     if (!check_storage_space(mainw->current_file, FALSE)) {
2825       if (visible) mainw->noswitch = FALSE;
2826       return FALSE;
2827     }
2828   }
2829 
2830   if (got_err) {
2831     if (visible) mainw->noswitch = FALSE;
2832     return FALSE;
2833   }
2834 #ifdef DEBUG
2835   g_print("exiting progress dialog\n");
2836 #endif
2837   if (visible) mainw->noswitch = FALSE;
2838   return TRUE;
2839 }
2840 
2841 
2842 #define MIN_FLASH_TIME MILLIONS(100)
2843 
do_auto_dialog(const char * text,int type)2844 boolean do_auto_dialog(const char *text, int type) {
2845   // type 0 = normal auto_dialog
2846   // type 1 = countdown dialog for audio recording
2847   // type 2 = normal with cancel
2848 
2849   FILE *infofile = NULL;
2850 
2851   uint64_t time = 0, stime = 0;
2852 
2853   char *label_text;
2854   char *mytext = lives_strdup(text);
2855 
2856   int time_rem, last_time_rem = 10000000;
2857   lives_alarm_t alarm_handle = 0;
2858 
2859   mainw->cancelled = CANCEL_NONE;
2860 
2861   if (type == 1 && mainw->rec_end_time != -1.) {
2862     stime = lives_get_current_ticks();
2863   }
2864 
2865   mainw->error = FALSE;
2866 
2867   mainw->proc_ptr = create_processing(mytext);
2868 
2869   lives_freep((void **)&mytext);
2870   if (mainw->proc_ptr->stop_button)
2871     lives_widget_hide(mainw->proc_ptr->stop_button);
2872 
2873   if (type == 2) {
2874     lives_widget_show_all(mainw->proc_ptr->cancel_button);
2875     lives_widget_hide(mainw->proc_ptr->pause_button);
2876     mainw->cancel_type = CANCEL_SOFT;
2877   }
2878   if (type == 0) {
2879     lives_widget_hide(mainw->proc_ptr->cancel_button);
2880   }
2881 
2882   lives_progress_bar_set_pulse_step(LIVES_PROGRESS_BAR(mainw->proc_ptr->progressbar), .01);
2883 
2884   lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
2885   lives_set_cursor_style(LIVES_CURSOR_BUSY, mainw->proc_ptr->processing);
2886 
2887   //lives_widget_context_update();
2888 
2889   if (type == 0 || type == 2) {
2890     clear_mainw_msg();
2891     alarm_handle = lives_alarm_set(MIN_FLASH_TIME); // don't want to flash too fast...
2892   } else if (type == 1) {
2893     // show buttons
2894     if (mainw->proc_ptr->stop_button)
2895       lives_widget_show_all(mainw->proc_ptr->stop_button);
2896     lives_widget_show_all(mainw->proc_ptr->cancel_button);
2897 #ifdef HAVE_PULSE_AUDIO
2898     if (mainw->pulsed_read) {
2899       pulse_driver_uncork(mainw->pulsed_read);
2900     }
2901 #endif
2902     if (mainw->rec_samples != 0) {
2903 
2904       lives_usleep(prefs->sleep_time);
2905     }
2906   }
2907 
2908   while (mainw->cancelled == CANCEL_NONE && !(infofile = fopen(cfile->info_file, "r"))) {
2909     lives_progress_bar_pulse(LIVES_PROGRESS_BAR(mainw->proc_ptr->progressbar));
2910     lives_widget_context_update();
2911     //lives_widget_process_updates(mainw->proc_ptr->processing);
2912     lives_usleep(prefs->sleep_time);
2913     if (type == 1 && mainw->rec_end_time != -1.) {
2914       time = lives_get_current_ticks();
2915 
2916       // subtract start time
2917       time -= stime;
2918 
2919       time_rem = (int)(mainw->rec_end_time - (double)time / TICKS_PER_SECOND_DBL + .5);
2920       if (time_rem >= 0 && time_rem < last_time_rem) {
2921         label_text = lives_strdup_printf(_("\nTime remaining: %d sec"), time_rem);
2922         lives_label_set_text(LIVES_LABEL(mainw->proc_ptr->label2), label_text);
2923         lives_free(label_text);
2924         last_time_rem = time_rem;
2925       } else if (time_rem < 0) break;
2926     }
2927   }
2928 
2929   if (!mainw->cancelled) {
2930     if (infofile) {
2931       if (type == 0 || type == 2) {
2932         size_t bread;
2933         THREADVAR(read_failed) = FALSE;
2934         bread = lives_fread(mainw->msg, 1, MAINW_MSG_SIZE, infofile);
2935         fclose(infofile);
2936         lives_memset(mainw->msg + bread, 0, 1);
2937         if (cfile->clip_type == CLIP_TYPE_DISK) lives_rm(cfile->info_file);
2938         if (alarm_handle > 0) {
2939           ticks_t tl;
2940           while ((tl = lives_alarm_check(alarm_handle)) > 0 && !mainw->cancelled) {
2941             lives_progress_bar_pulse(LIVES_PROGRESS_BAR(mainw->proc_ptr->progressbar));
2942             lives_widget_process_updates(mainw->proc_ptr->processing);
2943             lives_usleep(prefs->sleep_time);
2944           }
2945           lives_alarm_clear(alarm_handle);
2946         }
2947       } else fclose(infofile);
2948     }
2949   }
2950 
2951   if (mainw->proc_ptr) {
2952     lives_widget_destroy(mainw->proc_ptr->processing);
2953     lives_free(mainw->proc_ptr);
2954     mainw->proc_ptr = NULL;
2955   }
2956 
2957   if (type == 2) mainw->cancel_type = CANCEL_KILL;
2958   lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
2959   if (mainw->cancelled) return FALSE;
2960 
2961   // get error message (if any)
2962   if (type != 1 && !strncmp(mainw->msg, "error", 5)) {
2963     handle_backend_errors(FALSE);
2964     if (mainw->cancelled || mainw->error) return FALSE;
2965   } else {
2966     if (CURRENT_CLIP_IS_VALID)
2967       if (!check_storage_space(mainw->current_file, FALSE)) return FALSE;
2968   }
2969   return TRUE;
2970 }
2971 
2972 
2973 ///// TODO: split warnings etc into separate file   ///
2974 
do_save_clipset_warn(void)2975 boolean do_save_clipset_warn(void) {
2976   char *extra;
2977   char *msg;
2978 
2979   if (!mainw->no_exit && !mainw->only_close) extra = lives_strdup(", and LiVES will exit");
2980   else extra = lives_strdup("");
2981 
2982   msg = lives_strdup_printf(
2983           _("Saving the set will cause copies of all loaded clips to remain on the disk%s.\n\n"
2984             "Please press 'Cancel' if that is not what you want.\n"), extra);
2985   lives_free(extra);
2986 
2987   if (!do_warning_dialog_with_check(msg, WARN_MASK_SAVE_SET)) {
2988     lives_free(msg);
2989     return FALSE;
2990   }
2991   lives_free(msg);
2992   return TRUE;
2993 }
2994 
2995 
too_many_files(void)2996 LIVES_GLOBAL_INLINE void too_many_files(void) {
2997   do_error_dialogf(_("\nSorry, LiVES can only open %d files at once.\nPlease close a file and then try again."), MAX_FILES);
2998 }
2999 
3000 
workdir_warning(void)3001 void workdir_warning(void) {
3002   char *tmp, *com = lives_strdup_printf(
3003                       _("LiVES was unable to write to its working directory.\n\nThe current working directory is:\n\n%s\n\n"
3004                         "Please make sure you can write to this directory."),
3005                       (tmp = lives_filename_to_utf8(prefs->workdir, -1, NULL, NULL, NULL)));
3006   lives_free(tmp);
3007   if (mainw && mainw->is_ready) {
3008     do_error_dialog(com);
3009   }
3010   lives_free(com);
3011 }
3012 
3013 
do_no_mplayer_sox_error(void)3014 LIVES_GLOBAL_INLINE void do_no_mplayer_sox_error(void) {
3015   do_error_dialog(_("\nLiVES currently requires either 'mplayer', 'mplayer2', or 'sox' to function. "
3016                     "Please install one or other of these, and try again.\n"));
3017 }
3018 
3019 
do_need_mplayer_dialog(void)3020 LIVES_GLOBAL_INLINE void do_need_mplayer_dialog(void) {
3021   do_error_dialog(
3022     _("\nThis function requires either mplayer or mplayer2 to operate.\nYou may wish to install "
3023       "one or other of these and try again.\n"));
3024 }
3025 
3026 
do_need_mplayer_mpv_dialog(void)3027 LIVES_GLOBAL_INLINE void do_need_mplayer_mpv_dialog(void) {
3028   do_error_dialog(
3029     _("\nThis function requires either mplayer, mplayer2 or mpv to operate.\nYou may wish to install one or other of these "
3030       "and try again.\n"));
3031 }
3032 
3033 
do_audio_warning(void)3034 LIVES_GLOBAL_INLINE void do_audio_warning(void) {
3035   do_error_dialog(_("Audio was not loaded; please install mplayer or mplayer2 if you expected audio for this clip.\n"));
3036 }
3037 
3038 
do_encoder_sox_error(void)3039 LIVES_GLOBAL_INLINE void do_encoder_sox_error(void) {
3040   do_error_dialog(
3041     _("Audio resampling is required for this format.\nPlease install 'sox'\nOr switch to another encoder format in "
3042       "Tools | Preferences | Encoding\n"));
3043 }
3044 
3045 
do_encoder_acodec_error(void)3046 LIVES_GLOBAL_INLINE void do_encoder_acodec_error(void) {
3047   do_error_dialog(
3048     _("\n\nThis encoder/format cannot use the requested audio codec.\n"
3049       "Please set the audio codec in Tools|Preferences|Encoding\n"));
3050 }
3051 
3052 
do_layout_scrap_file_error(void)3053 LIVES_GLOBAL_INLINE void do_layout_scrap_file_error(void) {
3054   do_error_dialog(
3055     _("This layout includes generated frames.\nIt cannot be saved, you must render it to a clip first.\n"));
3056 }
3057 
3058 
do_layout_ascrap_file_error(void)3059 LIVES_GLOBAL_INLINE void do_layout_ascrap_file_error(void) {
3060   do_error_dialog(
3061     _("This layout includes generated or recorded audio.\nIt cannot be saved, you must render it to a clip first.\n"));
3062 }
3063 
3064 
rdet_suggest_values(int width,int height,double fps,int fps_num,int fps_denom,int arate,int asigned,boolean swap_endian,boolean anr,boolean ignore_fps)3065 boolean rdet_suggest_values(int width, int height, double fps, int fps_num, int fps_denom, int arate, int asigned,
3066                             boolean swap_endian, boolean anr, boolean ignore_fps) {
3067   LiVESWidget *prep_dialog;
3068 
3069   char *msg1 = lives_strdup_printf(_("\n\nDue to restrictions in the %s format\n"), prefs->encoder.of_desc);
3070   char *msg2 = lives_strdup(""), *msg3 = lives_strdup(""), *msg4 = lives_strdup("");
3071   char *msg5 = lives_strdup(""), *msg6 = lives_strdup(""), *msg7 = lives_strdup("");
3072   char *msg8 = lives_strdup("");
3073   char *msg_a;
3074 
3075   boolean ochange = FALSE;
3076   boolean ret;
3077 
3078   mainw->fx1_bool = FALSE;
3079 
3080   if (swap_endian || (asigned == 1 && rdet->aendian == AFORM_UNSIGNED) || (asigned == 2 && rdet->aendian == AFORM_SIGNED) ||
3081       (fps > 0. && fps != rdet->fps) || (fps_denom > 0 && (fps_num * 1.) / (fps_denom * 1.) != rdet->fps) ||
3082       (!anr && (rdet->width != width || rdet->height != height) && height * width > 0) ||
3083       (arate != rdet->arate && arate > 0)) {
3084     lives_free(msg2);
3085     msg2 = (_("LiVES recommends the following settings:\n\n"));
3086     if (swap_endian || (asigned == 1 && rdet->aendian == AFORM_UNSIGNED) || (asigned == 2 && rdet->aendian == AFORM_SIGNED)
3087         || (arate > 0 && arate != rdet->arate)) {
3088       char *sstring;
3089       char *estring;
3090 
3091       if (asigned == 1 && rdet->aendian == AFORM_UNSIGNED) sstring = (_(", signed"));
3092       else if (asigned == 2 && rdet->aendian == AFORM_SIGNED) sstring = (_(", unsigned"));
3093       else sstring = lives_strdup("");
3094 
3095       if (swap_endian) {
3096         if (mainw->endian != AFORM_BIG_ENDIAN) estring = (_(", little-endian"));
3097         else estring = (_(", big-endian"));
3098       } else estring = lives_strdup("");
3099 
3100       ochange = TRUE;
3101       lives_free(msg3);
3102       msg3 = lives_strdup_printf(_("Use an audio rate of %d Hz%s%s\n"), arate, sstring, estring);
3103       lives_free(sstring);
3104       lives_free(estring);
3105     }
3106     if (!ignore_fps) {
3107       ochange = TRUE;
3108       if (fps > 0 && fps != rdet->fps) {
3109         lives_free(msg4);
3110         msg4 = lives_strdup_printf(_("Set video rate to %.3f frames per second\n"), fps);
3111       } else if (fps_denom > 0 && (fps_num * 1.) / (fps_denom * 1.) != rdet->fps) {
3112         lives_free(msg4);
3113         msg4 = lives_strdup_printf(_("Set video rate to %d:%d frames per second\n"), fps_num, fps_denom);
3114       }
3115     }
3116     if (!anr && ((rdet->width != width || rdet->height != height) && height * width > 0)) {
3117       lives_free(msg5);
3118       msg5 = lives_strdup_printf(_("Set video size to %d x %d pixels\n"), width, height);
3119       mainw->fx1_bool = TRUE;
3120     }
3121   }
3122   if (anr || arate < 0) {
3123     if (arate < 1 || ((rdet->width != width || rdet->height != height) && height * width > 0)) {
3124       lives_free(msg6);
3125       if (!ochange) anr = FALSE;
3126       msg6 = (_("\nYou may wish to:\n"));
3127       if ((rdet->width != width || rdet->height != height) && height * width > 0) {
3128         lives_free(msg7);
3129         msg7 = lives_strdup_printf(_("resize video to %d x %d pixels\n"), width, height);
3130       } else anr = FALSE;
3131       if (arate < 1) {
3132         lives_free(msg8);
3133         msg8 = (_("disable audio, since the target encoder cannot encode audio\n"));
3134       }
3135     } else anr = FALSE;
3136   }
3137   msg_a = lives_strconcat(msg1, msg2, msg3, msg4, msg5, msg6, msg7, msg8, NULL);
3138   lives_free(msg1); lives_free(msg2); lives_free(msg3); lives_free(msg4);
3139   lives_free(msg5); lives_free(msg6); lives_free(msg7); lives_free(msg8);
3140   prep_dialog = create_encoder_prep_dialog(msg_a, NULL, anr);
3141   lives_free(msg_a);
3142   ret = (lives_dialog_run(LIVES_DIALOG(prep_dialog)) == LIVES_RESPONSE_OK);
3143   lives_widget_destroy(prep_dialog);
3144   return ret;
3145 }
3146 
3147 
do_encoder_restrict_dialog(int width,int height,double fps,int fps_num,int fps_denom,int arate,int asigned,boolean swap_endian,boolean anr,boolean save_all)3148 boolean do_encoder_restrict_dialog(int width, int height, double fps, int fps_num, int fps_denom, int arate, int asigned,
3149                                    boolean swap_endian, boolean anr, boolean save_all) {
3150   LiVESWidget *prep_dialog;
3151 
3152   char *msg1 = lives_strdup_printf(_("\n\nDue to restrictions in the %s format\n"), prefs->encoder.of_desc);
3153   char *msg2 = lives_strdup(""), *msg3 = lives_strdup(""), *msg4 = lives_strdup("");
3154   char *msg5 = lives_strdup(""), *msg6 = lives_strdup(""), *msg7 = lives_strdup("");
3155   char *msg_a, *msg_b = NULL;
3156 
3157   double cfps;
3158 
3159   boolean ret;
3160 
3161   int carate, chsize, cvsize;
3162 
3163   if (rdet) {
3164     carate = rdet->arate;
3165     chsize = rdet->width;
3166     cvsize = rdet->height;
3167     cfps = rdet->fps;
3168   } else {
3169     carate = cfile->arate;
3170     chsize = cfile->hsize;
3171     cvsize = cfile->vsize;
3172     cfps = cfile->fps;
3173   }
3174 
3175   if (swap_endian || asigned != 0 || (arate > 0 && arate != carate) || (fps > 0. && fps != cfps) ||
3176       (fps_denom > 0 && (fps_num * 1.) / (fps_denom * 1.) != cfps) || (!anr &&
3177           (chsize != width || cvsize != height) && height * width > 0)) {
3178     lives_free(msg2);
3179     msg2 = (_("LiVES must:\n"));
3180     if (swap_endian || asigned != 0 || (arate > 0 && arate != carate)) {
3181       char *sstring;
3182       char *estring;
3183       if (asigned == 1) sstring = (_(", signed"));
3184       else if (asigned == 2) sstring = (_(", unsigned"));
3185       else sstring = lives_strdup("");
3186 
3187       if (swap_endian) {
3188         if (cfile->signed_endian & AFORM_BIG_ENDIAN) estring = (_(", little-endian"));
3189         else estring = (_(", big-endian"));
3190       } else estring = lives_strdup("");
3191 
3192       lives_free(msg3);
3193       msg3 = lives_strdup_printf(_("resample audio to %d Hz%s%s\n"), arate, sstring, estring);
3194       lives_free(sstring);
3195       lives_free(estring);
3196 
3197     }
3198     if (fps > 0 && fps != cfps) {
3199       lives_free(msg4);
3200       msg4 = lives_strdup_printf(_("resample video to %.3f frames per second\n"), fps);
3201     } else if (fps_denom > 0 && (fps_num * 1.) / (fps_denom * 1.) != cfps) {
3202       lives_free(msg4);
3203       msg4 = lives_strdup_printf(_("resample video to %d:%d frames per second\n"), fps_num, fps_denom);
3204     }
3205     if (!anr && ((chsize != width || cvsize != height) && height * width > 0)) {
3206       lives_free(msg5);
3207       msg5 = lives_strdup_printf(_("resize video to %d x %d pixels\n"), width, height);
3208       mainw->fx1_bool = TRUE;
3209     }
3210   }
3211   if (anr) {
3212     if ((chsize != width || cvsize != height) && height * width > 0) {
3213       lives_free(msg6);
3214       lives_free(msg7);
3215       msg6 = (_("\nYou may wish to:\n"));
3216       msg7 = lives_strdup_printf(_("Set video size to %d x %d pixels\n"), width, height);
3217     } else anr = FALSE;
3218   }
3219   msg_a = lives_strconcat(msg1, msg2, msg3, msg4, msg5, msg6, msg7, NULL);
3220   if (save_all) {
3221     msg_b = lives_strdup(
3222               _("\nYou will be able to undo these changes afterwards.\n\nClick `OK` to proceed, `Cancel` to abort.\n\n"));
3223   } else {
3224     msg_b = (_("\nChanges applied to the selection will not be permanent.\n\n"));
3225   }
3226   lives_free(msg1); lives_free(msg2); lives_free(msg3); lives_free(msg4);
3227   lives_free(msg5); lives_free(msg6); lives_free(msg7);
3228   prep_dialog = create_encoder_prep_dialog(msg_a, msg_b, anr);
3229   lives_free(msg_a);
3230   if (msg_b) lives_free(msg_b);
3231   ret = (lives_dialog_run(LIVES_DIALOG(prep_dialog)) == LIVES_RESPONSE_OK);
3232   lives_widget_destroy(prep_dialog);
3233   return ret;
3234 }
3235 
3236 
perf_mem_warning(void)3237 LIVES_GLOBAL_INLINE void perf_mem_warning(void) {
3238   do_error_dialog(
3239     _("\n\nLiVES was unable to record a performance. There is currently insufficient memory available.\n"
3240       "Try recording for just a selection of the file."));
3241 }
3242 
3243 
do_clipboard_fps_warning(void)3244 boolean do_clipboard_fps_warning(void) {
3245   if (prefs->warning_mask & WARN_MASK_FPS) {
3246     return TRUE;
3247   }
3248   return do_warning_dialog_with_check(
3249            _("The playback speed (fps), or the audio rate\n of the clipboard does not match\n"
3250              "the playback speed or audio rate of the clip you are inserting into.\n\n"
3251              "The insertion will be adjusted to fit into the clip.\n\n"
3252              "Please press Cancel to abort the insert, or OK to continue."), WARN_MASK_FPS);
3253 }
3254 
3255 
do_reload_set_query(void)3256 LIVES_GLOBAL_INLINE boolean do_reload_set_query(void) {
3257   return do_yesno_dialog(_("Current clips will be added to the clip set.\nIs that what you want ?\n"));
3258 }
3259 
3260 
findex_bk_dialog(const char * fname_back)3261 LIVES_GLOBAL_INLINE boolean findex_bk_dialog(const char *fname_back) {
3262   return do_yesno_dialogf(_("I can attempt to restore the frame index from a backup.\n(%s)\nShall I try ?\n"), fname_back);
3263 }
3264 
3265 
paste_enough_dlg(int lframe)3266 LIVES_GLOBAL_INLINE boolean paste_enough_dlg(int lframe) {
3267   return do_yesno_dialogf(P_("\nPaste %d frame ?\n", "Paste %d frames ?\n", lframe), lframe);
3268 }
3269 
do_yuv4m_open_warning(void)3270 boolean do_yuv4m_open_warning(void) {
3271   char *msg;
3272   boolean resp;
3273   if (prefs->warning_mask & WARN_MASK_OPEN_YUV4M) {
3274     return TRUE;
3275   }
3276   msg = lives_strdup_printf(
3277           _("When opening a yuvmpeg stream, you should first create a fifo file, and then write yuv4mpeg frames to it.\n"
3278             "Now you will get a chance to browse for the fifo file here.\nFollowing that,\n"
3279             "LiVES will pause briefly until frames are received.\nYou should only click OK if you understand what you are doing, "
3280             "otherwise, click Cancel."),
3281           prefs->workdir);
3282   resp = do_warning_dialog_with_check(msg, WARN_MASK_OPEN_YUV4M);
3283   lives_free(msg);
3284   return resp;
3285 }
3286 
3287 
do_comments_dialog(int fileno,char * filename)3288 boolean do_comments_dialog(int fileno, char *filename) {
3289   lives_clip_t *sfile = mainw->files[fileno];
3290 
3291   boolean response;
3292   boolean encoding = FALSE;
3293 
3294   commentsw = create_comments_dialog(sfile, filename);
3295 
3296   if (sfile == NULL) sfile = cfile;
3297   else encoding = TRUE;
3298 
3299   while (1) {
3300     if ((response = (lives_dialog_run(LIVES_DIALOG(commentsw->comments_dialog)) == LIVES_RESPONSE_OK))) {
3301       lives_snprintf(sfile->title, 1024, "%s", lives_entry_get_text(LIVES_ENTRY(commentsw->title_entry)));
3302       lives_snprintf(sfile->author, 1024, "%s", lives_entry_get_text(LIVES_ENTRY(commentsw->author_entry)));
3303       lives_snprintf(sfile->comment, 1024, "%s", lives_entry_get_text(LIVES_ENTRY(commentsw->comment_entry)));
3304 
3305       save_clip_value(fileno, CLIP_DETAILS_TITLE, sfile->title);
3306       save_clip_value(fileno, CLIP_DETAILS_AUTHOR, sfile->author);
3307       save_clip_value(fileno, CLIP_DETAILS_COMMENT, sfile->comment);
3308 
3309       if (encoding && sfile->subt && lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(commentsw->subt_checkbutton))) {
3310         char *ext = get_extension(lives_entry_get_text(LIVES_ENTRY(commentsw->subt_entry)));
3311         if (strcmp(ext, LIVES_FILE_EXT_SUB) && strcmp(ext, LIVES_FILE_EXT_SRT)) {
3312           if (!do_sub_type_warning(ext, sfile->subt->type == SUBTITLE_TYPE_SRT ? LIVES_FILE_EXT_SRT : LIVES_FILE_EXT_SUB)) {
3313             lives_entry_set_text(LIVES_ENTRY(commentsw->subt_entry), mainw->subt_save_file);
3314             lives_free(ext);
3315             continue;
3316           }
3317         }
3318         lives_free(ext);
3319         lives_freep((void **)&mainw->subt_save_file);
3320         mainw->subt_save_file = lives_strdup(lives_entry_get_text(LIVES_ENTRY(commentsw->subt_entry)));
3321       } else {
3322         lives_freep((void **)&mainw->subt_save_file);
3323         mainw->subt_save_file = NULL;
3324       }
3325       lives_widget_destroy(commentsw->comments_dialog);
3326     }
3327     break;
3328   }
3329 
3330   lives_free(commentsw);
3331   return response;
3332 }
3333 
3334 
do_messages_window(boolean is_startup)3335 LIVES_GLOBAL_INLINE void do_messages_window(boolean is_startup) {
3336   text_window *textwindow;
3337   char *text = dump_messages(-1, -1);
3338   widget_opts.expand = LIVES_EXPAND_EXTRA;
3339   textwindow = create_text_window(_("Message History"), text, NULL, TRUE);
3340   widget_opts.expand = LIVES_EXPAND_DEFAULT;
3341   lives_free(text);
3342   if (is_startup) {
3343     LiVESWidget *area =
3344       lives_dialog_get_action_area((LIVES_DIALOG(textwindow->dialog)));
3345     LiVESWidget *cb = lives_standard_check_button_new(_("Show messages on startup"), TRUE,
3346                       LIVES_BOX(area), NULL);
3347     lives_signal_sync_connect(LIVES_GUI_OBJECT(cb), LIVES_WIDGET_TOGGLED_SIGNAL,
3348                               LIVES_GUI_CALLBACK(toggle_sets_pref),
3349                               (livespointer)PREF_MSG_START);
3350     lives_button_box_make_first(LIVES_BUTTON_BOX(area), widget_opts.last_container);
3351     lives_widget_show_all(textwindow->dialog);
3352   }
3353   lives_widget_context_update();
3354   lives_scrolled_window_scroll_to(LIVES_SCROLLED_WINDOW(textwindow->scrolledwindow), LIVES_POS_BOTTOM);
3355 }
3356 
3357 
do_upgrade_error_dialog(void)3358 LIVES_GLOBAL_INLINE void do_upgrade_error_dialog(void) {
3359   char *tmp;
3360   char *msg = lives_strdup_printf(
3361                 _("After upgrading/installing, you may need to adjust the <prefix_dir> setting in your %s file"),
3362                 (tmp = lives_filename_to_utf8(prefs->configfile, -1, NULL, NULL, NULL)));
3363   startup_message_info(msg);
3364   lives_free(msg); lives_free(tmp);
3365 }
3366 
3367 
do_rendered_fx_dialog(void)3368 LIVES_GLOBAL_INLINE void do_rendered_fx_dialog(void) {
3369   char *tmp;
3370   char *msg = lives_strdup_printf(
3371                 _("\n\nLiVES could not find any rendered effect plugins.\nPlease make sure you have them installed in\n"
3372                   "%s%s%s\nor change the value of <lib_dir> in %s\n"),
3373                 prefs->lib_dir, PLUGIN_EXEC_DIR, PLUGIN_RENDERED_EFFECTS_BUILTIN,
3374                 (tmp = lives_filename_to_utf8(prefs->configfile, -1, NULL, NULL, NULL)));
3375   do_error_dialog_with_check(msg, WARN_MASK_RENDERED_FX);
3376   lives_free(msg);
3377   lives_free(tmp);
3378 }
3379 
3380 
do_audio_import_error(void)3381 void do_audio_import_error(void) {
3382   char *msg = (_("Sorry, unknown audio type.\n\n (Filenames must end in"));
3383   char *tmp;
3384 
3385   char *filt[] = LIVES_AUDIO_LOAD_FILTER;
3386 
3387   register int i = 0;
3388 
3389   while (filt[i]) {
3390     if (filt[i + 1]) {
3391       tmp = lives_strdup_printf("%s or .%s)", msg, filt[i] + 2);
3392     } else if (i == 0) {
3393       tmp = lives_strdup_printf("%s .%s)", msg, filt[i] + 2);
3394     } else {
3395       tmp = lives_strdup_printf("%s, .%s)", msg, filt[i] + 2);
3396     }
3397     lives_free(msg);
3398     msg = tmp;
3399     i++;
3400   }
3401 
3402   do_error_dialog(msg);
3403   lives_free(msg);
3404   d_print(_("failed (unknown type)\n"));
3405 }
3406 
3407 
prompt_remove_layout_files(void)3408 LIVES_GLOBAL_INLINE boolean prompt_remove_layout_files(void) {
3409   return (do_yesno_dialog(
3410             _("\nDo you wish to remove the layout files associated with this set ?\n"
3411               "(They will not be usable without the set).\n")));
3412 }
3413 
3414 
do_set_duplicate_warning(const char * new_set)3415 boolean do_set_duplicate_warning(const char *new_set) {
3416   char *msg = lives_strdup_printf(
3417                 _("\nA set entitled %s already exists.\n"
3418                   "Click OK to add the current clips and layouts to the existing set.\n"
3419                   "Click Cancel to pick a new name.\n"), new_set);
3420   boolean retcode = do_warning_dialog_with_check(msg, WARN_MASK_DUPLICATE_SET);
3421   lives_free(msg);
3422   return retcode;
3423 }
3424 
3425 
do_layout_alter_frames_warning(void)3426 LIVES_GLOBAL_INLINE boolean do_layout_alter_frames_warning(void) {
3427   return do_warning_dialog(
3428            _("\nFrames from this clip are used in some multitrack layouts.\n"
3429              "Are you sure you wish to continue ?\n."));
3430 }
3431 
3432 
do_layout_alter_audio_warning(void)3433 LIVES_GLOBAL_INLINE boolean do_layout_alter_audio_warning(void) {
3434   return do_warning_dialog(
3435            _("\nAudio from this clip is used in some multitrack layouts.\n"
3436              "Are you sure you wish to continue ?\n."));
3437 }
3438 
3439 
do_gamma_import_warn(uint64_t fv,int gamma_type)3440 LIVES_GLOBAL_INLINE boolean do_gamma_import_warn(uint64_t fv, int gamma_type) {
3441   char *fvx = unhash_version(fv);
3442   boolean ret = do_yesno_dialogf(_("This clip is saved with a gamma type of %s\n"
3443                                    "from a future version of LiVES (%s)\n"
3444                                    "Opening it with the current version may result in a loss of quality\n"
3445                                    "Do you wish to continue ?"), weed_gamma_get_name(gamma_type), fvx);
3446   lives_free(fvx);
3447   return ret;
3448 }
3449 
3450 
do_mt_lb_warn(boolean lb)3451 boolean do_mt_lb_warn(boolean lb) {
3452   char *tmp, *msg, *endis, *endised;
3453   boolean ret;
3454 
3455   if (lb) {
3456     endis = _("enable");
3457     endised = _("enabled");
3458   } else {
3459     endis = _("disable");
3460     endised = _("disabled");
3461   }
3462   msg = lives_strdup_printf((tmp = _("This layout was saved with letterboxing %s\n"
3463                                      "To preserve the original appearance, I can override\n"
3464                                      "the current setting and %s letterboxing for this layout\n\n"
3465                                      "Click 'Yes' to proceed, or 'No' to keep the current setting\n\n"
3466                                      "(Note: the value for the current layout can be modified at any time\n"
3467                                      "via the menu option 'Tools' / 'Change Width, Height and Audio Values')\n")),
3468                             endised, endis);
3469   lives_free(tmp); lives_free(endised); lives_free(endis);
3470   ret = do_yesno_dialog_with_check(msg, WARN_MASK_LAYOUT_LB);
3471   lives_free(msg);
3472   return ret;
3473 }
3474 
3475 
_do_df_notfound_dialog(const char * detail,const char * dfname,boolean is_dir)3476 static LiVESResponseType _do_df_notfound_dialog(const char *detail, const char *dfname, boolean is_dir) {
3477   LiVESWidget *warning;
3478   LiVESResponseType response;
3479   char *xdetail, *msg, *whatitis, *extra;
3480 
3481   if (detail) xdetail = (char *)detail;
3482 
3483   if (!is_dir) {
3484     if (!detail) {
3485       xdetail = lives_strdup(_("The file"));
3486       extra = _("could not be found.");
3487     } else extra = lives_strdup("");
3488     whatitis = (_("this file"));
3489   } else {
3490     if (!detail) {
3491       xdetail = lives_strdup(_("The directory"));
3492       extra = _("could not be found.");
3493     } else extra = lives_strdup("");
3494     whatitis = (_("this directory"));
3495   }
3496   msg = lives_strdup_printf(_("\n%s\n%s\n%s\n"
3497                               "Click Retry to try again, Browse to browse to the new location.\n"
3498                               "otherwise click Skip to skip loading %s.\n"), xdetail, dfname, extra, whatitis);
3499   warning = create_message_dialog(LIVES_DIALOG_SKIP_RETRY_BROWSE, msg, 0);
3500   response = lives_dialog_run(LIVES_DIALOG(warning));
3501   lives_widget_destroy(warning);
3502   lives_widget_context_update();
3503   lives_free(msg); lives_free(whatitis);
3504   if (xdetail != detail) lives_free(xdetail);
3505   return response;
3506 }
3507 
3508 
do_dir_notfound_dialog(const char * detail,const char * dirname)3509 LiVESResponseType do_dir_notfound_dialog(const char *detail, const char *dirname) {
3510   return _do_df_notfound_dialog(detail, dirname, TRUE);
3511 }
3512 
do_file_notfound_dialog(const char * detail,const char * filename)3513 LiVESResponseType do_file_notfound_dialog(const char *detail, const char *filename) {
3514   return _do_df_notfound_dialog(detail, filename, FALSE);
3515 }
3516 
3517 
do_no_decoder_error(const char * fname)3518 LIVES_GLOBAL_INLINE void do_no_decoder_error(const char *fname) {
3519   lives_widget_context_update();
3520   do_error_dialogf(
3521     _("\n\nLiVES could not find a required decoder plugin for the clip\n%s\n"
3522       "The clip could not be loaded.\n"), fname);
3523 }
3524 
3525 
do_no_loadfile_error(const char * fname)3526 LIVES_GLOBAL_INLINE void do_no_loadfile_error(const char *fname) {
3527   do_error_dialogf(_("\n\nThe file\n%s\nCould not be found.\n"), fname);
3528 }
3529 
3530 
3531 #ifdef ENABLE_JACK
do_jack_noopen_warn(void)3532 LIVES_GLOBAL_INLINE void do_jack_noopen_warn(void) {
3533   do_error_dialogf(_("\nUnable to start up jack. "
3534                      "Please ensure that %s is set up correctly on your machine\n"
3535                      "and also that the soundcard is not in use by another program\n"
3536                      "Automatic jack startup will be disabled now.\n"),
3537                    JACK_DRIVER_NAME);
3538 }
3539 
do_jack_noopen_warn3(void)3540 LIVES_GLOBAL_INLINE void do_jack_noopen_warn3(void) {
3541   do_error_dialog(_("\nUnable to connect to jack server. "
3542                     "Please start jack before starting LiVES\n"));
3543 }
3544 
do_jack_noopen_warn4(void)3545 LIVES_GLOBAL_INLINE void do_jack_noopen_warn4(void) {
3546 #ifdef HAVE_PULSE_AUDIO
3547   const char *otherbit = "\"lives -aplayer pulse\"";
3548 #else
3549   const char *otherbit = "\"lives -aplayer sox\"";
3550 #endif
3551   do_info_dialogf(_("\nAlternatively, try to start lives with either:\n\n"
3552                     "\"lives -jackopts 16\", or\n\n%s\n"), otherbit);
3553 }
3554 
do_jack_noopen_warn2(void)3555 LIVES_GLOBAL_INLINE void do_jack_noopen_warn2(void) {
3556   do_info_dialog(_("\nAlternately, you can restart LiVES and select another audio player.\n"));
3557 }
3558 #endif
3559 
do_mt_backup_space_error(lives_mt * mt,int memreq_mb)3560 LIVES_GLOBAL_INLINE void do_mt_backup_space_error(lives_mt * mt, int memreq_mb) {
3561   char *msg = lives_strdup_printf(
3562                 _("\n\nLiVES needs more backup space for this layout.\nYou can increase "
3563                   "the value in Preferences/Multitrack.\n"
3564                   "It is recommended to increase it to at least %d MB"),
3565                 memreq_mb);
3566   do_error_dialog_with_check(msg, WARN_MASK_MT_BACKUP_SPACE);
3567   lives_free(msg);
3568 }
3569 
3570 
do_set_rename_old_layouts_warning(const char * new_set)3571 LIVES_GLOBAL_INLINE boolean do_set_rename_old_layouts_warning(const char *new_set) {
3572   return do_yesno_dialogf(
3573            _("\nSome old layouts for the set %s already exist.\n"
3574              "It is recommended that you delete them.\nDo you wish to delete them ?\n"),
3575            new_set);
3576 }
3577 
3578 
do_mt_undo_mem_error(void)3579 LIVES_GLOBAL_INLINE void do_mt_undo_mem_error(void) {
3580   do_error_dialog(
3581     _("\nLiVES was unable to reserve enough memory for multitrack undo.\n"
3582       "Either close some other applications, or reduce the undo memory\n"
3583       "using Preferences/Multitrack/Undo Memory\n"));
3584 }
3585 
3586 
do_mt_undo_buf_error(void)3587 LIVES_GLOBAL_INLINE void do_mt_undo_buf_error(void) {
3588   do_error_dialog(_("\nOut of memory for undo.\nYou may need to increase the undo memory\n"
3589                     "using Preferences/Multitrack/Undo Memory\n"));
3590 }
3591 
3592 
do_mt_set_mem_error(boolean has_mt)3593 LIVES_GLOBAL_INLINE void do_mt_set_mem_error(boolean has_mt) {
3594   char *msg1 = (_("\nLiVES was unable to reserve enough memory for the multitrack undo buffer.\n"));
3595   char *msg2;
3596   char *msg3 = (_("or enter a smaller value.\n"));
3597 
3598   if (has_mt) msg2 = (_("Try again from the clip editor, try closing some other applications\n"));
3599   else msg2 = (_("Try closing some other applications\n"));
3600 
3601   do_error_dialogf("%s%s%s", msg1, msg2, msg3);
3602   lives_free(msg1); lives_free(msg2); lives_free(msg3);
3603 }
3604 
3605 
do_mt_audchan_error(int warn_mask)3606 LIVES_GLOBAL_INLINE void do_mt_audchan_error(int warn_mask) {
3607   do_error_dialog_with_check(
3608     _("Multitrack is set to 0 audio channels, but this layout has audio.\n"
3609       "You should adjust the audio settings from the Tools menu.\n"),
3610     warn_mask);
3611 }
3612 
3613 
do_mt_no_audchan_error(void)3614 LIVES_GLOBAL_INLINE void do_mt_no_audchan_error(void) {
3615   do_error_dialog(_("The current layout has audio, so audio channels may not be set to zero.\n"));
3616 }
3617 
3618 
do_mt_no_jack_error(int warn_mask)3619 LIVES_GLOBAL_INLINE void do_mt_no_jack_error(int warn_mask) {
3620   do_error_dialog_with_check(
3621     _("Multitrack audio preview is only available with the\n\"jack\" or \"pulseaudio\" audio player.\n"
3622       "You can set this in Tools|Preferences|Playback."),
3623     warn_mask);
3624 }
3625 
3626 
do_mt_rect_prompt(void)3627 LIVES_GLOBAL_INLINE boolean do_mt_rect_prompt(void) {
3628   return do_yesno_dialog(
3629            _("Errors were detected in the layout (which may be due to transferring from another system, "
3630              "or from an older version of LiVES).\n"
3631              "Should I try to repair the disk copy of the layout ?\n"));
3632 }
3633 
3634 
do_bad_layout_error(void)3635 LIVES_GLOBAL_INLINE void do_bad_layout_error(void) {
3636   do_error_dialog(_("LiVES was unable to load the layout.\nSorry.\n"));
3637 }
3638 
3639 
do_program_not_found_error(const char * progname)3640 LIVES_GLOBAL_INLINE void do_program_not_found_error(const char *progname) {
3641   do_error_dialogf(_("The program %s is required to use this feature.\nPlease install it and try again."), progname);
3642 }
3643 
3644 
do_lb_composite_error(void)3645 LIVES_GLOBAL_INLINE void do_lb_composite_error(void) {
3646   do_error_dialog(
3647     _("LiVES currently requires composite from ImageMagick to do letterboxing.\n"
3648       "Please install 'imagemagick' and try again."));
3649 }
3650 
3651 
do_lb_convert_error(void)3652 LIVES_GLOBAL_INLINE void do_lb_convert_error(void) {
3653   do_error_dialog(
3654     _("LiVES currently requires convert from ImageMagick to do letterboxing.\n"
3655       "Please install 'imagemagick' and try again."));
3656 }
3657 
3658 
do_please_install(const char * exec,uint64_t gflags)3659 LIVES_GLOBAL_INLINE boolean do_please_install(const char *exec, uint64_t gflags) {
3660   char *extra = lives_strdup(""), *msg;
3661   if (gflags & INSTALL_CANLOCAL) {
3662     lives_free(extra);
3663     extra = lives_strdup(_("\n\nAlternately, LiVES may be able to install\na local user copy "
3664                            "of the program.\n"));
3665   }
3666 
3667   msg = lives_strdup_printf(_("'%s' is necessary for this feature to work.\n"
3668                               "If possible, kindly install it before continuing.%s"), exec, extra);
3669 
3670   if (gflags & INSTALL_CANLOCAL) {
3671     LiVESWidget *dlg = create_question_dialog(NULL, msg);
3672     LiVESResponseType ret;
3673     lives_free(msg);
3674     widget_opts.expand = LIVES_EXPAND_EXTRA_WIDTH | LIVES_EXPAND_DEFAULT_HEIGHT;
3675     lives_dialog_add_button_from_stock(LIVES_DIALOG(dlg), LIVES_STOCK_CANCEL,
3676                                        _("Cancel / Install Later"), LIVES_RESPONSE_CANCEL);
3677     lives_dialog_add_button_from_stock(LIVES_DIALOG(dlg), LIVES_STOCK_ADD,
3678                                        _("Continue"), LIVES_RESPONSE_YES);
3679     widget_opts.expand = LIVES_EXPAND_DEFAULT;
3680 
3681     lives_dialog_set_button_layout(LIVES_DIALOG(dlg), LIVES_BUTTONBOX_SPREAD);
3682 
3683     ret = lives_dialog_run(LIVES_DIALOG(dlg));
3684     lives_widget_destroy(dlg);
3685     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
3686     return (ret == LIVES_RESPONSE_YES);
3687   }
3688   do_info_dialog(msg);
3689   lives_free(msg);
3690   return FALSE;
3691 }
3692 
3693 
do_please_install_either(const char * exec,const char * exec2)3694 LIVES_GLOBAL_INLINE boolean do_please_install_either(const char *exec, const char *exec2) {
3695   do_info_dialogf(_("Either '%s' or '%s' must be installed for this feature to work.\n"
3696                     "If possible, kindly install one or other of these before continuing\n"),
3697                   exec, exec2);
3698   return FALSE;
3699 }
3700 
3701 
do_audrate_error_dialog(void)3702 LIVES_GLOBAL_INLINE void do_audrate_error_dialog(void) {
3703   do_error_dialog(_("\n\nAudio rate must be greater than 0.\n"));
3704 }
3705 
3706 
do_event_list_warning(void)3707 LIVES_GLOBAL_INLINE boolean do_event_list_warning(void) {
3708   return do_yesno_dialog(
3709            _("\nEvent list will be very large\nand may take a long time to display.\n"
3710              "Are you sure you wish to view it ?\n"));
3711 }
3712 
3713 
do_dvgrab_error(void)3714 LIVES_GLOBAL_INLINE void do_dvgrab_error(void) {
3715   do_error_dialog(_("\n\nYou must install 'dvgrab' to use this function.\n"));
3716 }
3717 
3718 
do_nojack_rec_error(void)3719 LIVES_GLOBAL_INLINE void do_nojack_rec_error(void) {
3720   do_error_dialog(
3721     _("\n\nAudio recording can only be done using either\nthe \"jack\" "
3722       "or the \"pulseaudio\" audio player.\n"
3723       "You may need to select one of these in Tools/Preferences/Playback.\n"));
3724 }
3725 
3726 
do_vpp_palette_error(void)3727 LIVES_GLOBAL_INLINE void do_vpp_palette_error(void) {
3728   do_error_dialog(_("Video playback plugin failed to initialise palette !\n"));
3729 }
3730 
3731 
do_decoder_palette_error(void)3732 LIVES_GLOBAL_INLINE void do_decoder_palette_error(void) {
3733   do_error_dialog(_("Decoder plugin failed to initialise palette !\n"));
3734 }
3735 
3736 
do_vpp_fps_error(void)3737 LIVES_GLOBAL_INLINE void do_vpp_fps_error(void) {
3738   do_error_dialog(_("Unable to set framerate of video plugin\n"));
3739 }
3740 
3741 
do_after_crash_warning(void)3742 LIVES_GLOBAL_INLINE void do_after_crash_warning(void) {
3743   do_error_dialog_with_check(_("After a crash, it is advisable to clean up the disk with\nFile|Clean up disk space\n"),
3744                              WARN_MASK_CLEAN_AFTER_CRASH);
3745 }
3746 
3747 
do_after_invalid_warning(void)3748 LIVES_GLOBAL_INLINE void do_after_invalid_warning(void) {
3749   do_error_dialog_with_check(_("Invalid clips were detected during reload.\nIt is advisable to clean up the disk with\n"
3750                                "File|Clean up disk space\n"),
3751                              WARN_MASK_CLEAN_INVALID);
3752 }
3753 
3754 
do_rmem_max_error(int size)3755 LIVES_GLOBAL_INLINE void do_rmem_max_error(int size) {
3756   do_error_dialogf(_("Stream frame size is too large for your network buffers.\nYou should do the following as root:\n\n"
3757                      "echo %d > /proc/sys/net/core/rmem_max\n"), size);
3758 }
3759 
3760 static LiVESList *tdlglist = NULL;
3761 
threaded_dialog_push(void)3762 void threaded_dialog_push(void) {
3763   if (mainw->proc_ptr) {
3764     tdlglist = lives_list_prepend(tdlglist, mainw->proc_ptr);
3765     if (mainw->proc_ptr->processing) lives_widget_hide(mainw->proc_ptr->processing);
3766     mainw->proc_ptr = NULL;
3767   }
3768   mainw->threaded_dialog = FALSE;
3769 }
3770 
threaded_dialog_pop(void)3771 void threaded_dialog_pop(void) {
3772   end_threaded_dialog();
3773   if (tdlglist) {
3774     LiVESList *xtdlglist;
3775     mainw->proc_ptr = (xprocess *)tdlglist->data;
3776     xtdlglist = tdlglist;
3777     tdlglist = tdlglist->next;
3778     if (tdlglist) tdlglist->prev = NULL;
3779     xtdlglist->next = NULL;
3780     xtdlglist->data = NULL;
3781     lives_list_free(xtdlglist);
3782     if (mainw->proc_ptr && mainw->proc_ptr->processing) {
3783       lives_widget_show(mainw->proc_ptr->processing);
3784       lives_window_set_modal(LIVES_WINDOW(mainw->proc_ptr->processing), TRUE);
3785       lives_widget_process_updates(mainw->proc_ptr->processing);
3786       mainw->threaded_dialog = TRUE;
3787     }
3788   }
3789 }
3790 
3791 
_threaded_dialog_spin(double fraction)3792 static void _threaded_dialog_spin(double fraction) {
3793   double timesofar;
3794   int progress;
3795 
3796   if (fraction > 0.) {
3797     timesofar = (double)(lives_get_current_ticks() - sttime) / TICKS_PER_SECOND_DBL;
3798     disp_fraction(fraction, timesofar, mainw->proc_ptr);
3799   } else {
3800     if (!CURRENT_CLIP_IS_VALID || !mainw->proc_ptr->progress_start || !mainw->proc_ptr->progress_end ||
3801         *(mainw->msg) || !(progress = atoi(mainw->msg))) {
3802       // pulse the progress bar
3803       //#define GDB
3804 #ifndef GDB
3805       if (LIVES_IS_PROGRESS_BAR(mainw->proc_ptr->progressbar)) {
3806         lives_progress_bar_pulse(LIVES_PROGRESS_BAR(mainw->proc_ptr->progressbar));
3807       }
3808 #endif
3809     } else {
3810       // show fraction
3811       double fraction_done = (double)(progress - mainw->proc_ptr->progress_start)
3812                              / (double)(mainw->proc_ptr->progress_end - mainw->proc_ptr->progress_start + 1.);
3813       timesofar = (double)(lives_get_current_ticks() - sttime) / TICKS_PER_SECOND_DBL;
3814       disp_fraction(fraction_done, timesofar, mainw->proc_ptr);
3815     }
3816   }
3817   // necessary
3818   lives_widget_context_update();
3819   //lives_widget_process_updates(mainw->proc_ptr->processing);
3820 }
3821 
3822 
threaded_dialog_spin(double fraction)3823 void threaded_dialog_spin(double fraction) {
3824   if (!mainw->threaded_dialog || mainw->splash_window || !mainw->proc_ptr
3825       || !mainw->is_ready || !prefs->show_gui) return;
3826   if (!mainw->is_exiting) {
3827     if (THREADVAR(no_gui)) return;
3828     main_thread_execute((lives_funcptr_t)_threaded_dialog_spin, 0,
3829                         NULL, "d", fraction);
3830   } else _threaded_dialog_spin(fraction);
3831 }
3832 
_do_threaded_dialog(const char * trans_text,boolean has_cancel)3833 static void _do_threaded_dialog(const char *trans_text, boolean has_cancel) {
3834   // calling this causes a threaded progress dialog to appear
3835   // until end_threaded_dialog() is called
3836   char *copy_text;
3837   mainw->cancelled = CANCEL_NONE;
3838   copy_text = lives_strdup(trans_text);
3839   lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
3840   sttime = lives_get_current_ticks();
3841   mainw->threaded_dialog = TRUE;
3842   clear_mainw_msg();
3843   mainw->proc_ptr = create_threaded_dialog(copy_text, has_cancel, &td_had_focus);
3844   lives_free(copy_text);
3845   lives_widget_process_updates(mainw->proc_ptr->processing);
3846 }
3847 
3848 
do_threaded_dialog(const char * trans_text,boolean has_cancel)3849 void do_threaded_dialog(const char *trans_text, boolean has_cancel) {
3850   if (!prefs->show_gui) return;
3851   if (mainw->threaded_dialog || !prefs->show_gui) return;
3852   if (!mainw->is_exiting)
3853     main_thread_execute((lives_funcptr_t)_do_threaded_dialog, 0,
3854                         NULL, "sb", trans_text, has_cancel);
3855   else
3856     _do_threaded_dialog(trans_text, has_cancel);
3857 }
3858 
3859 
_end_threaded_dialog(void)3860 static void _end_threaded_dialog(void) {
3861   if (!mainw->threaded_dialog) return;
3862   mainw->cancel_type = CANCEL_KILL;
3863 
3864   if (mainw->proc_ptr && mainw->proc_ptr->processing) lives_widget_destroy(mainw->proc_ptr->processing);
3865 
3866   lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
3867   lives_widget_queue_draw(LIVES_MAIN_WINDOW_WIDGET);
3868 
3869   lives_freep((void **)&mainw->proc_ptr);
3870 
3871   mainw->threaded_dialog = FALSE;
3872 
3873   if (prefs->show_msg_area) {
3874     // TODO
3875     if (LIVES_IS_WINDOW(LIVES_MAIN_WINDOW_WIDGET)) {
3876       lives_window_present(LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET));
3877       lives_widget_grab_focus(mainw->msg_area);
3878       gtk_window_set_focus(LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET), mainw->msg_area);
3879     }
3880   }
3881 }
3882 
end_threaded_dialog(void)3883 void end_threaded_dialog(void) {
3884   if (THREADVAR(no_gui)) return;
3885   if (!mainw->threaded_dialog) return;
3886   if (!mainw->is_exiting)
3887     main_thread_execute((lives_funcptr_t)_end_threaded_dialog, 0, NULL, "");
3888   else _end_threaded_dialog();
3889 }
3890 
3891 
response_ok(LiVESButton * button,livespointer user_data)3892 void response_ok(LiVESButton * button, livespointer user_data) {
3893   lives_dialog_response(LIVES_DIALOG(lives_widget_get_toplevel(LIVES_WIDGET(button))), LIVES_RESPONSE_OK);
3894 }
3895 
3896 
do_system_failed_error(const char * com,int retval,const char * addinfo,boolean can_retry,boolean trysudo)3897 LiVESResponseType do_system_failed_error(const char *com, int retval, const char *addinfo,
3898     boolean can_retry, boolean trysudo) {
3899   // if can_retry is set, we can return LIVES_RESPONSE_RETRY
3900   // in all other cases we abort (exit) here.
3901   // if abort_hook_func() fails with a syserror, we don't show the abort / retry dialog, and we return LIVES_RESPONSE_NONE
3902   // from the inner call (otherwise we could get stuck in an infinite recursion)
3903   static boolean norecurse = FALSE;
3904   char *xcom, *xaddbit, *xbit, *xsudomsg;
3905   char *msg, *tmp, *emsg, *msgx, *bit;
3906   char *retstr = lives_strdup_printf("%d", retval >> 8);
3907   char *bit2 = (retval > 255) ? lives_strdup("") : lives_strdup_printf("[%s]", lives_strerror(retval));
3908   char *addbit;
3909   char *dsmsg = lives_strdup("");
3910   char *sudomsg = lives_strdup("");
3911 
3912   int64_t dsval = capable->ds_used;
3913 
3914   lives_storage_status_t ds = get_storage_status(prefs->workdir, prefs->ds_crit_level, &dsval, 0);
3915   LiVESResponseType response = LIVES_RESPONSE_NONE;
3916 
3917   capable->ds_free = dsval;
3918 
3919   if (ds == LIVES_STORAGE_STATUS_CRITICAL) {
3920     lives_free(dsmsg);
3921     tmp = ds_critical_msg(prefs->workdir, &capable->mountpoint, dsval);
3922     dsmsg = lives_strdup_printf("%s\n", tmp);
3923     lives_free(tmp);
3924   }
3925 
3926   if (addinfo) addbit = lives_strdup_printf(_("Additional info: %s\n"), addinfo);
3927   else addbit = lives_strdup("");
3928 
3929   if (retval > 0) bit = lives_strdup_printf(_("The error value was %d%s\n"), retval, bit2);
3930   else bit = lives_strdup("");
3931 
3932   if (trysudo) {
3933     char *retryop;
3934     if (can_retry) retryop = (_("before clicking 'Retry'"));
3935     else retryop = (_("before retrying the operation"));
3936     lives_free(sudomsg);
3937     sudomsg = lives_strdup_printf(_("\n\nYou may be able to fix this by running:\n  %s %s\n"
3938                                     "from the commandline %s"), EXEC_SUDO, com, retryop);
3939     lives_free(retryop);
3940   }
3941 
3942   xcom = lives_markup_escape_text(com, -1);
3943   xbit = lives_markup_escape_text(bit, -1);
3944   xaddbit = lives_markup_escape_text(addbit, -1);
3945   xsudomsg = lives_markup_escape_text(sudomsg, -1);
3946 
3947   msg = lives_strdup_printf(_("\nLiVES failed doing the following:\n%s\nPlease check your system for "
3948                               "errors.\n%s%s%s"),
3949                             xcom, xbit, xaddbit, dsmsg, xsudomsg);
3950 
3951   lives_free(xcom); lives_free(xbit); lives_free(xaddbit); lives_free(xsudomsg);
3952   emsg = lives_strdup_printf("Command failed doing\n%s\n%s%s", com, bit, addbit);
3953   d_print("\n"); d_print(emsg);
3954   LIVES_ERROR(emsg);
3955   lives_free(emsg);
3956 
3957   msgx = insert_newlines(msg, MAX_MSG_WIDTH_CHARS);
3958   if (can_retry) {
3959     if (!norecurse) {
3960       /// we must not fail during the abort hook
3961       norecurse = TRUE;
3962       widget_opts.use_markup = TRUE;
3963       response = do_abort_retry_dialog(msgx);
3964       widget_opts.use_markup = FALSE;
3965       norecurse = FALSE;
3966     }
3967   } else {
3968     widget_opts.use_markup = TRUE;
3969     do_error_dialog(msgx);
3970     widget_opts.use_markup = FALSE;
3971   }
3972   lives_free(msgx); lives_free(msg); lives_free(sudomsg);
3973   lives_free(dsmsg); lives_free(bit); lives_free(bit2);
3974   lives_free(addbit); lives_free(retstr);
3975   return response;
3976 }
3977 
3978 
do_write_failed_error_s(const char * s,const char * addinfo)3979 void do_write_failed_error_s(const char *s, const char *addinfo) {
3980   char *msg, *emsg;
3981   char *addbit, *tmp;
3982   char *dsmsg = lives_strdup("");
3983 
3984   char dirname[PATH_MAX];
3985   char *sutf = lives_filename_to_utf8(s, -1, NULL, NULL, NULL), *xsutf, *xaddbit;
3986 
3987   boolean exists;
3988 
3989   int64_t dsval = capable->ds_used;
3990 
3991   lives_storage_status_t ds;
3992 
3993   lives_snprintf(dirname, PATH_MAX, "%s", s);
3994   get_dirname(dirname);
3995   exists = lives_file_test(dirname, LIVES_FILE_TEST_EXISTS);
3996   ds = get_storage_status(dirname, prefs->ds_crit_level, &dsval, 0);
3997   capable->ds_free = dsval;
3998   if (!exists) lives_rmdir(dirname, FALSE);
3999 
4000   if (ds == LIVES_STORAGE_STATUS_CRITICAL) {
4001     lives_free(dsmsg);
4002     tmp = ds_critical_msg(dirname, &capable->mountpoint, dsval);
4003     dsmsg = lives_strdup_printf("%s\n", tmp);
4004     lives_free(tmp);
4005   }
4006 
4007   if (addinfo) addbit = lives_strdup_printf(_("Additional info: %s\n"), addinfo);
4008   else addbit = lives_strdup("");
4009 
4010   xsutf = lives_markup_escape_text(sutf, -1);
4011   lives_free(sutf);
4012 
4013   xaddbit = lives_markup_escape_text(addbit, -1);
4014 
4015   msg = lives_strdup_printf(_("\nLiVES was unable to write to the file\n%s\n"
4016                               "Please check for possible error causes.\n%s"),
4017                             xsutf, xaddbit, dsmsg);
4018   lives_free(xsutf); lives_free(xaddbit);
4019 
4020   emsg = lives_strdup_printf("Unable to write to file\n%s\n%s", s, addbit);
4021   lives_free(addbit);
4022   d_print("\n"); d_print(emsg);
4023 
4024   LIVES_ERROR(emsg);
4025   lives_free(emsg);
4026 
4027   widget_opts.use_markup = TRUE;
4028   do_error_dialog(msg);
4029   widget_opts.use_markup = FALSE;
4030   lives_free(addbit); lives_free(dsmsg); lives_free(msg);
4031 }
4032 
4033 
do_read_failed_error_s(const char * s,const char * addinfo)4034 void do_read_failed_error_s(const char *s, const char *addinfo) {
4035   char *msg, *emsg;
4036   char *addbit;
4037   char *sutf = lives_filename_to_utf8(s, -1, NULL, NULL, NULL);
4038 
4039   if (addinfo) addbit = lives_strdup_printf(_("Additional info: %s\n"), addinfo);
4040   else addbit = lives_strdup("");
4041 
4042   msg = lives_strdup_printf(_("\nLiVES was unable to read from the file\n%s\n"
4043                               "Please check for possible error causes.\n%s"),
4044                             sutf, addbit);
4045   emsg = lives_strdup_printf("Unable to read from the file\n%s\n%s", s, addbit);
4046   d_print("\n"); d_print(emsg);
4047 
4048   LIVES_ERROR(emsg);
4049   lives_free(emsg);
4050   lives_free(sutf);
4051 
4052   do_error_dialog(msg);
4053   lives_free(msg);
4054   lives_free(addbit);
4055 }
4056 
4057 
do_write_failed_error_s_with_retry(const char * fname,const char * errtext)4058 LiVESResponseType do_write_failed_error_s_with_retry(const char *fname, const char *errtext) {
4059   // err can be errno from open/fopen etc.
4060 
4061   // return same as do_abort_cancel_retry_dialog() - LIVES_RESPONSE_CANCEL or LIVES_RESPONSE_RETRY (both non-zero)
4062 
4063   LiVESResponseType ret;
4064   char *msg, *emsg, *tmp;
4065   char *sutf = lives_filename_to_utf8(fname, -1, NULL, NULL, NULL), *xsutf;
4066   char *dsmsg = lives_strdup("");
4067 
4068   char dirname[PATH_MAX];
4069 
4070   boolean exists;
4071 
4072   int64_t dsval = capable->ds_used;
4073 
4074   lives_storage_status_t ds;
4075 
4076   lives_snprintf(dirname, PATH_MAX, "%s", fname);
4077   get_dirname(dirname);
4078   exists = lives_file_test(dirname, LIVES_FILE_TEST_EXISTS);
4079   ds = get_storage_status(dirname, prefs->ds_crit_level, &dsval, 0);
4080   capable->ds_free = dsval;
4081   if (!exists) lives_rmdir(dirname, FALSE);
4082 
4083   if (ds == LIVES_STORAGE_STATUS_CRITICAL) {
4084     lives_free(dsmsg);
4085     tmp = ds_critical_msg(dirname, &capable->mountpoint, dsval);
4086     dsmsg = lives_strdup_printf("%s\n", tmp);
4087     lives_free(tmp);
4088   }
4089 
4090   xsutf = lives_markup_escape_text(sutf, -1);
4091 
4092   if (errtext) {
4093     emsg = lives_strdup_printf("Unable to write to file %s", fname);
4094     msg = lives_strdup_printf(_("\nLiVES was unable to write to the file\n%s\n"
4095                                 "Please check for possible error causes.\n%s"), xsutf, dsmsg);
4096   } else {
4097     char *xerrtext = lives_markup_escape_text(errtext, -1);
4098     emsg = lives_strdup_printf("Unable to write to file %s, error was %s", fname, errtext);
4099     msg = lives_strdup_printf(_("\nLiVES was unable to write to the file\n%s\nThe error was\n%s.\n%s"),
4100                               xsutf, xerrtext, dsmsg);
4101     lives_free(xerrtext);
4102   }
4103 
4104   lives_free(xsutf);
4105   LIVES_ERROR(emsg);
4106   lives_free(emsg);
4107 
4108   widget_opts.use_markup = TRUE;
4109   ret = do_abort_cancel_retry_dialog(msg);
4110   widget_opts.use_markup = FALSE;
4111 
4112   lives_free(dsmsg);
4113   lives_free(msg);
4114   lives_free(sutf);
4115 
4116   THREADVAR(write_failed) = FALSE; // reset this
4117 
4118   return ret;
4119 }
4120 
4121 
do_read_failed_error_s_with_retry(const char * fname,const char * errtext)4122 LiVESResponseType do_read_failed_error_s_with_retry(const char *fname, const char *errtext) {
4123   // err can be errno from open/fopen etc.
4124 
4125   // return same as do_abort_cancel_retry_dialog() - LIVES_RESPONSE_CANCEL or LIVES_RESPONSE_RETRY (both non-zero)
4126 
4127   LiVESResponseType ret;
4128   char *msg, *emsg;
4129   char *sutf = lives_filename_to_utf8(fname, -1, NULL, NULL, NULL);
4130 
4131   if (!errtext) {
4132     emsg = lives_strdup_printf("Unable to read from file %s", fname);
4133     msg = lives_strdup_printf(_("\nLiVES was unable to read from the file\n%s\n"
4134                                 "Please check for possible error causes.\n"), sutf);
4135   } else {
4136     emsg = lives_strdup_printf("Unable to read from file %s, error was %s", fname, errtext);
4137     msg = lives_strdup_printf(_("\nLiVES was unable to read from the file\n%s\nThe error was\n%s.\n"),
4138                               sutf, errtext);
4139   }
4140 
4141   LIVES_ERROR(emsg);
4142   lives_free(emsg);
4143 
4144   ret = do_abort_cancel_retry_dialog(msg);
4145 
4146   lives_free(msg);
4147   lives_free(sutf);
4148 
4149   THREADVAR(read_failed) = FALSE; // reset this
4150 
4151   return ret;
4152 }
4153 
4154 
do_header_read_error_with_retry(int clip)4155 LiVESResponseType do_header_read_error_with_retry(int clip) {
4156   LiVESResponseType ret;
4157   char *hname;
4158   if (!mainw->files[clip]) return 0;
4159 
4160   hname = lives_build_filename(prefs->workdir, mainw->files[clip]->handle, LIVES_CLIP_HEADER, NULL);
4161 
4162   ret = do_read_failed_error_s_with_retry(hname, NULL);
4163 
4164   lives_free(hname);
4165   return ret;
4166 }
4167 
4168 
do_header_write_error(int clip)4169 boolean do_header_write_error(int clip) {
4170   // returns TRUE if we manage to clear the error
4171 
4172   char *hname;
4173   LiVESResponseType retval;
4174 
4175   if (mainw->files[clip] == NULL) return TRUE;
4176 
4177   hname = lives_build_filename(prefs->workdir, mainw->files[clip]->handle, LIVES_CLIP_HEADER, NULL);
4178   retval = do_write_failed_error_s_with_retry(hname, NULL);
4179   if (retval == LIVES_RESPONSE_RETRY && save_clip_values(clip)) retval = 0; // on retry try to save all values
4180   lives_free(hname);
4181 
4182   return (!retval);
4183 }
4184 
4185 
do_header_missing_detail_error(int clip,lives_clip_details_t detail)4186 LiVESResponseType do_header_missing_detail_error(int clip, lives_clip_details_t detail) {
4187   LiVESResponseType ret;
4188   char *hname, *key, *msg;
4189   if (!mainw->files[clip]) return 0;
4190 
4191   hname = lives_build_filename(prefs->workdir, mainw->files[clip]->handle, LIVES_CLIP_HEADER, NULL);
4192 
4193   key = clip_detail_to_string(detail, NULL);
4194 
4195   if (!key) {
4196     msg = lives_strdup_printf("Invalid detail %d requested from file %s", detail, hname);
4197     LIVES_ERROR(msg);
4198     lives_free(msg);
4199     lives_free(hname);
4200     return 0;
4201   }
4202 
4203   msg = lives_strdup_printf(_("Value for \"%s\" could not be read."), key);
4204   ret = do_read_failed_error_s_with_retry(hname, msg);
4205 
4206   lives_free(msg);
4207   lives_free(key);
4208   lives_free(hname);
4209   return ret;
4210 }
4211 
4212 
do_chdir_failed_error(const char * dir)4213 void do_chdir_failed_error(const char *dir) {
4214   char *dutf, *msg, *emsg = lives_strdup_printf("Failed directory change to\n%s", dir);
4215   LIVES_ERROR(emsg);
4216   lives_free(emsg);
4217   dutf = lives_filename_to_utf8(dir, -1, NULL, NULL, NULL);
4218   msg = lives_strdup_printf(_("\nLiVES failed to change directory to\n%s\n"
4219                               "Please check your system for errors.\n"), dutf);
4220   lives_free(dutf);
4221   do_abort_ok_dialog(msg);
4222   lives_free(msg);
4223 }
4224 
4225 
do_file_perm_error(const char * file_name,boolean allow_cancel)4226 LiVESResponseType  do_file_perm_error(const char *file_name, boolean allow_cancel) {
4227   LiVESResponseType resp;
4228   char *msg, *can_cancel;
4229   if (allow_cancel)
4230     can_cancel = (_(", click Cancel to continue regardless,\n"));
4231   else
4232     can_cancel = lives_strdup("");
4233 
4234   msg = lives_strdup_printf(_("\nLiVES was unable to write to the file:\n%s\n"
4235                               "Please check the file permissions and try again."
4236                               "%sor click Abort to exit from LiVES"), file_name, can_cancel);
4237   resp = do_abort_retry_dialog(msg);
4238   if (!allow_cancel)
4239     resp = do_abort_retry_dialog(msg);
4240   else
4241     resp = do_abort_cancel_retry_dialog(msg);
4242   lives_free(msg);
4243   return resp;
4244 }
4245 
4246 
do_dir_perm_error(const char * dir_name,boolean allow_cancel)4247 LiVESResponseType do_dir_perm_error(const char *dir_name, boolean allow_cancel) {
4248   LiVESResponseType resp;
4249   char *msg, *can_cancel;
4250   if (allow_cancel)
4251     can_cancel = (_("click Cancel to continue regardless, "));
4252   else
4253     can_cancel = lives_strdup("");
4254 
4255   msg = lives_strdup_printf(_("\nLiVES was unable to either create or write to the directory:\n%s\n"
4256                               "Please check the directory permissions and try again,\n"
4257                               "%sor click Abort to exit from LiVES"), dir_name, can_cancel);
4258   lives_free(can_cancel);
4259 
4260   if (!allow_cancel)
4261     resp = do_abort_retry_dialog(msg);
4262   else
4263     resp = do_abort_cancel_retry_dialog(msg);
4264 
4265   lives_free(msg);
4266   return resp;
4267 }
4268 
4269 
do_dir_perm_access_error(const char * dir_name)4270 void do_dir_perm_access_error(const char *dir_name) {
4271   char *msg = lives_strdup_printf(_("\nLiVES was unable to read from the directory:\n%s\n"), dir_name);
4272   do_abort_ok_dialog(msg);
4273   lives_free(msg);
4274 }
4275 
4276 
do_abort_check(void)4277 boolean do_abort_check(void) {
4278   return do_yesno_dialog(_("\nAbort and exit immediately from LiVES\nAre you sure ?\n"));
4279 }
4280 
4281 
do_encoder_img_fmt_error(render_details * rdet)4282 void do_encoder_img_fmt_error(render_details * rdet) {
4283   do_error_dialogf(_("\nThe %s cannot encode clips with image type %s.\n"
4284                      "Please select another encoder from the list.\n"),
4285                    prefs->encoder.name, get_image_ext_for_type(cfile->img_type));
4286 }
4287 
4288 
do_card_in_use_error(void)4289 LIVES_GLOBAL_INLINE void do_card_in_use_error(void) {
4290   do_error_dialog(_("\nThis card is already in use and cannot be opened multiple times.\n"));
4291 }
4292 
4293 
do_dev_busy_error(const char * devstr)4294 LIVES_GLOBAL_INLINE void do_dev_busy_error(const char *devstr) {
4295   do_error_dialogf(_("\nThe device %s is in use or unavailable.\n"
4296                      "- Check the device permissions\n"
4297                      "- Check if this device is in use by another program.\n"
4298                      "- Check if the device actually exists.\n"), devstr);
4299 }
4300 
4301 
do_existing_subs_warning(void)4302 LIVES_GLOBAL_INLINE boolean do_existing_subs_warning(void) {
4303   return do_yesno_dialog(_("\nThis file already has subtitles loaded.\n"
4304                            "Do you wish to overwrite the existing subtitles ?\n"));
4305 }
4306 
4307 
do_invalid_subs_error(void)4308 void do_invalid_subs_error(void) {
4309   char *msg = (_("\nLiVES currently only supports subtitles of type"));
4310   char *tmp;
4311 
4312   char *filt[] = LIVES_SUBS_FILTER;
4313 
4314   register int i = 0;
4315 
4316   while (filt[i]) {
4317     if (!filt[i + 1]) {
4318       tmp = lives_strdup_printf("%s or .%s\n", msg, filt[i] + 2);
4319     } else if (i > 0) {
4320       tmp = lives_strdup_printf("%s, .%s)", msg, filt[i] + 2);
4321     } else {
4322       tmp = lives_strdup_printf("%s .%s)", msg, filt[i] + 2);
4323     }
4324     lives_free(msg);
4325     msg = tmp;
4326     i++;
4327   }
4328 
4329   do_error_dialog(msg);
4330   lives_free(msg);
4331 }
4332 
4333 
do_erase_subs_warning(void)4334 LIVES_GLOBAL_INLINE boolean do_erase_subs_warning(void) {
4335   return do_yesno_dialog(_("\nErase all subtitles from this clip.\nAre you sure ?\n"));
4336 }
4337 
4338 
do_sub_type_warning(const char * ext,const char * type_ext)4339 boolean do_sub_type_warning(const char *ext, const char *type_ext) {
4340   boolean ret;
4341   char *msg = lives_strdup_printf(
4342                 _("\nLiVES does not recognise the subtitle file type \"%s\".\n"
4343                   "Click Cancel to set another file name\nor OK to continue and save as type \"%s\"\n"),
4344                 ext, type_ext);
4345   ret = do_warning_dialogf(msg);
4346   lives_free(msg);
4347   return ret;
4348 }
4349 
4350 
do_move_workdir_dialog(void)4351 LIVES_GLOBAL_INLINE boolean do_move_workdir_dialog(void) {
4352   return do_yesno_dialog(_("\nDo you wish to move the current clip sets to the new directory ?\n("
4353                            "If unsure, click Yes)\n"));
4354 }
4355 
4356 
do_set_locked_warning(const char * setname)4357 LIVES_GLOBAL_INLINE boolean do_set_locked_warning(const char *setname) {
4358   return do_yesno_dialogf(
4359            _("\nWarning - the set %s\nis in use by another copy of LiVES.\n"
4360              "You are strongly advised to close the other copy before clicking Yes to continue\n.\n"
4361              "Click No to cancel loading the set.\n"),
4362            setname);
4363 }
4364 
4365 
do_no_sets_dialog(const char * dir)4366 LIVES_GLOBAL_INLINE void do_no_sets_dialog(const char *dir) {
4367   extra_cb_key = 1;
4368   do_info_dialogf(_("No Sets could be found in the directory\n%s\n\n"
4369                     "If you have Sets in another directory, you can either:\n"
4370                     " - change the working directory in Preferences, or\n"
4371                     " - restart lives with the -workdir switch to set it temporarily"),
4372                   dir);
4373 }
4374 
4375 
do_foundclips_query(void)4376 boolean do_foundclips_query(void) {
4377   char *text = (_("Possible lost clips were detected within the LiVES working directory.\n"
4378                   "What would you like me to do with them ?\n"));
4379   char *title = (_("Missing Clips Detected"));
4380   LiVESWidget *dlg = create_question_dialog(title, text);
4381   LiVESResponseType ret;
4382   lives_free(text); lives_free(title);
4383   widget_opts.expand = LIVES_EXPAND_EXTRA_WIDTH | LIVES_EXPAND_DEFAULT_HEIGHT;
4384   lives_dialog_add_button_from_stock(LIVES_DIALOG(dlg), LIVES_STOCK_CLEAR,
4385                                      _("Maybe later"), LIVES_RESPONSE_NO);
4386   lives_dialog_add_button_from_stock(LIVES_DIALOG(dlg), LIVES_STOCK_REMOVE,
4387                                      _("Try to recover them"), LIVES_RESPONSE_YES);
4388   widget_opts.expand = LIVES_EXPAND_DEFAULT;
4389 
4390   lives_dialog_set_button_layout(LIVES_DIALOG(dlg), LIVES_BUTTONBOX_SPREAD);
4391 
4392   ret = lives_dialog_run(LIVES_DIALOG(dlg));
4393   lives_widget_destroy(dlg);
4394   lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
4395   return (ret == LIVES_RESPONSE_YES);
4396 }
4397 
4398 
do_no_in_vdevs_error(void)4399 LIVES_GLOBAL_INLINE void do_no_in_vdevs_error(void) {
4400   do_error_dialog(_("\nNo video input devices could be found.\n"));
4401 }
4402 
4403 
do_locked_in_vdevs_error(void)4404 LIVES_GLOBAL_INLINE void do_locked_in_vdevs_error(void) {
4405   do_error_dialog(_("\nAll video input devices are already in use.\n"));
4406 }
4407 
4408 
do_do_not_close_d(void)4409 LIVES_GLOBAL_INLINE void do_do_not_close_d(void) {
4410   char *msg = (_("\n\nCLEANING AND COPYING FILES. THIS MAY TAKE SOME TIME.\nDO NOT SHUT DOWN OR "
4411                  "CLOSE LIVES !\n"));
4412   create_message_dialog(LIVES_DIALOG_WARN, msg, 0);
4413   lives_free(msg);
4414 }
4415 
4416 
do_resize_dlg(int cwidth,int cheight,int fwidth,int fheight)4417 LIVES_GLOBAL_INLINE LiVESResponseType do_resize_dlg(int cwidth, int cheight, int fwidth, int fheight) {
4418   LiVESWidget *butt;
4419   LiVESResponseType resp;
4420   char *text = lives_strdup_printf(_("Some frames in this clip may be wrongly sized.\n"
4421                                      "The clip size is %d X %d, however at least one frame has size %d X %d\n"
4422                                      "What would you like to do ?"), cwidth, cheight, fwidth, fheight);
4423 
4424   LiVESWidget *dialog = create_question_dialog(_("Problem Detected"), text);
4425 
4426   lives_free(text);
4427 
4428   widget_opts.expand = LIVES_EXPAND_EXTRA_WIDTH | LIVES_EXPAND_DEFAULT_HEIGHT;
4429   lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_CANCEL, _("Continue anyway"),
4430                                      LIVES_RESPONSE_CANCEL);
4431 
4432   lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_CLEAR, _("Use the image size"),
4433                                      LIVES_RESPONSE_ACCEPT);
4434 
4435   butt = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_REVERT_TO_SAVED,
4436          _("Resize images to clip size"),
4437          LIVES_RESPONSE_YES);
4438   widget_opts.expand = LIVES_EXPAND_DEFAULT;
4439   lives_button_grab_default_special(butt);
4440 
4441   lives_dialog_set_button_layout(LIVES_DIALOG(dialog), LIVES_BUTTONBOX_EXPAND);
4442   resp = lives_dialog_run(LIVES_DIALOG(dialog));
4443   lives_widget_destroy(dialog);
4444   lives_widget_context_update();
4445   return resp;
4446 }
4447 
4448 
do_imgfmts_error(lives_img_type_t imgtype)4449 LIVES_GLOBAL_INLINE LiVESResponseType do_imgfmts_error(lives_img_type_t imgtype) {
4450   LiVESResponseType resp;
4451   char *text = lives_strdup_printf(_("Some frames in this clip have the wrong image format.\n"
4452                                      "The image format should be %s\n"
4453                                      "What would you like to do ?"),
4454                                    image_ext_to_lives_image_type(get_image_ext_for_type(imgtype)));
4455 
4456   LiVESWidget *dialog = create_question_dialog(_("Problem Detected"), text);
4457 
4458   lives_free(text);
4459 
4460   widget_opts.expand = LIVES_EXPAND_EXTRA_WIDTH | LIVES_EXPAND_DEFAULT_HEIGHT;
4461   lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_CANCEL, _("Continue anyway"),
4462                                      LIVES_RESPONSE_CANCEL);
4463 
4464   lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_REVERT_TO_SAVED, _("Correct the images"),
4465                                      LIVES_RESPONSE_OK);
4466   widget_opts.expand = LIVES_EXPAND_DEFAULT;
4467 
4468   resp = lives_dialog_run(LIVES_DIALOG(dialog));
4469   lives_widget_destroy(dialog);
4470   return resp;
4471 }
4472 
4473 
do_bad_theme_error(const char * themefile)4474 LIVES_GLOBAL_INLINE void do_bad_theme_error(const char *themefile) {
4475   do_error_dialogf(_("\nThe theme file %s has missing elements.\n"
4476                      "The theme could not be loaded correctly.\n"), themefile);
4477 }
4478 
4479 
do_set_noclips_error(const char * setname)4480 LIVES_GLOBAL_INLINE void do_set_noclips_error(const char *setname) {
4481   char *msg = lives_strdup_printf(
4482                 _("No clips were recovered for set (%s).\n"
4483                   "Please check the spelling of the set name and try again.\n"),
4484                 setname);
4485   d_print(msg);
4486   lives_free(msg);
4487 }
4488 
4489 
get_upd_msg(void)4490 LIVES_GLOBAL_INLINE char *get_upd_msg(void) {
4491   LIVES_DEBUG("upd msg !");
4492   // TRANSLATORS: make sure the menu text matches what is in gui.c
4493   char *msg = lives_strdup_printf(_("\nWelcome to LiVES version %s\n\n"
4494                                     "After upgrading, you are advised to run:"
4495                                     "\n\nFiles -> Clean up Diskspace\n"), LiVES_VERSION);
4496   return msg;
4497 }
4498 
4499 
do_no_autolives_error(void)4500 LIVES_GLOBAL_INLINE void do_no_autolives_error(void) {
4501   do_error_dialogf(_("\nYou must have %s installed and in your path to use this toy.\n"
4502                      "Consult your package distributor.\n"),
4503                    EXEC_AUTOLIVES_PL);
4504 }
4505 
4506 
do_autolives_needs_clips_error(void)4507 LIVES_GLOBAL_INLINE void do_autolives_needs_clips_error(void) {
4508   do_error_dialog(_("\nYou must have a minimum of one clip loaded to use this toy.\n"));
4509 }
4510 
4511 
do_jack_lost_conn_error(void)4512 LIVES_GLOBAL_INLINE void do_jack_lost_conn_error(void) {
4513   do_error_dialog(_("\nLiVES lost its connection to jack and was unable to reconnect.\n"
4514                     "Restarting LiVES is recommended.\n"));
4515 }
4516 
4517 
do_pulse_lost_conn_error(void)4518 LIVES_GLOBAL_INLINE void do_pulse_lost_conn_error(void) {
4519   do_error_dialog(
4520     _("\nLiVES lost its connection to pulseaudio and was unable to reconnect.\n"
4521       "Restarting LiVES is recommended.\n"));
4522 }
4523 
4524 
do_cd_error_dialog(void)4525 LIVES_GLOBAL_INLINE void do_cd_error_dialog(void) {
4526   do_error_dialog(_("Please set your CD play device in Tools | Preferences | Misc\n"));
4527 }
4528 
4529 
do_bad_theme_import_error(const char * theme_file)4530 LIVES_GLOBAL_INLINE void do_bad_theme_import_error(const char *theme_file) {
4531   do_error_dialogf(_("\nLiVES was unable to import the theme file\n%s\n(Theme name not found).\n"),
4532                    theme_file);
4533 }
4534 
4535 
do_close_changed_warn(void)4536 boolean do_close_changed_warn(void) {
4537   extra_cb_key = 2;
4538   del_cb_key = 2;
4539   return do_warning_dialog(_("Changes made to this clip have not been saved or backed up.\n\n"
4540                              "Really close it ?"));
4541 }
4542 
4543 
workdir_ch_warning(void)4544 LIVES_GLOBAL_INLINE char *workdir_ch_warning(void) {
4545   return lives_strdup(
4546            _("You have chosen to change the working directory.\n"
4547              "Please make sure you have no other copies of LiVES open.\n\n"
4548              "If you do have other copies of LiVES open, please close them now, "
4549              "*before* pressing OK.\n\n"
4550              "Alternatively, press Cancel to restore the working directory to its original setting."));
4551 }
4552 
4553 
do_shutdown_msg(void)4554 LIVES_GLOBAL_INLINE void do_shutdown_msg(void) {
4555   do_info_dialog(
4556     _("\nLiVES will now shut down. You need to restart it for the new "
4557       "preferences to take effect.\nClick OK to continue.\n"));
4558 }
4559 
4560 
do_theme_exists_warn(const char * themename)4561 LIVES_GLOBAL_INLINE boolean do_theme_exists_warn(const char *themename) {
4562   return do_yesno_dialogf(_("\nA custom theme with the name\n%s\nalready exists. "
4563                             "Would you like to overwrite it ?\n"), themename);
4564 }
4565 
4566 
add_resnn_label(LiVESDialog * dialog)4567 void add_resnn_label(LiVESDialog * dialog) {
4568   LiVESWidget *dialog_vbox = lives_dialog_get_content_area(dialog);
4569   LiVESWidget *label;
4570   LiVESWidget *hsep = lives_standard_hseparator_new();
4571   lives_box_pack_first(LIVES_BOX(dialog_vbox), hsep, FALSE, TRUE, 0);
4572   lives_widget_show(hsep);
4573   widget_opts.justify = LIVES_JUSTIFY_CENTER;
4574   label = lives_standard_label_new(_(
4575                                      "\n\nResizing of clips is no longer necessary, "
4576                                      "as LiVES will internally adjust frame sizes as "
4577                                      "needed at the appropriate moments.\n\n"
4578                                      "However, physically reducing the frame size may in some cases "
4579                                      "lead to improved playback \n"
4580                                      "and processing rates.\n\n"));
4581   widget_opts.justify = LIVES_JUSTIFY_DEFAULT;
4582   lives_box_pack_first(LIVES_BOX(dialog_vbox), label, FALSE, TRUE, 0);
4583   lives_widget_show(label);
4584 }
4585 
4586 
ask_permission_dialog(int what)4587 boolean ask_permission_dialog(int what) {
4588   if (!prefs->show_gui) return FALSE;
4589 
4590   switch (what) {
4591   case LIVES_PERM_OSC_PORTS:
4592     return do_yesno_dialogf(
4593              _("\nLiVES would like to open a local network connection (UDP port %d),\n"
4594                "to let other applications connect to it.\n"
4595                "Do you wish to allow this (for this session only) ?\n"),
4596              prefs->osc_udp_port);
4597   default:
4598     break;
4599   }
4600 
4601   return FALSE;
4602 }
4603 
4604 
ask_permission_dialog_complex(int what,char ** argv,int argc,int offs,const char * sudocom)4605 boolean ask_permission_dialog_complex(int what, char **argv, int argc, int offs, const char *sudocom) {
4606   if (prefs->show_gui) {
4607     LiVESWidget *dlg;
4608     LiVESResponseType ret;
4609     char *text, *title, *prname, *errtxt, *errtxt2, *xsudt;
4610     char *tmp, *action, *verb;
4611     int nrem = argc - offs;
4612     //boolean retry;
4613 
4614     //try_again:
4615     //retry = FALSE;
4616     switch (what) {
4617     case LIVES_PERM_DOWNLOAD_LOCAL:
4618     case LIVES_PERM_COPY_LOCAL:
4619       // argv (starting at offs) should have: name_of_package_bin, grant_idx, grant_key,
4620       // cmds to run, future consequences.
4621       if (nrem < 4) return FALSE; /// badly formed request, ignore it
4622 
4623       mainw->permmgr = (lives_permmgr_t *)lives_calloc(1, sizeof(lives_permmgr_t));
4624       mainw->permmgr->idx = atoi(argv[offs + 1]);
4625       mainw->permmgr->key = lives_strdup(argv[offs + 2]);
4626       if (nrem >= 4) mainw->permmgr->cmdlist = argv[offs + 3];
4627       if (nrem >= 5) mainw->permmgr->futures = argv[offs + 4];
4628 
4629       if (sudocom) {
4630         char *sudotext = lives_strdup_printf(_("Alternately, you can try running\n"
4631                                                "  %s %s\n from a commandline terminal\n"),
4632                                              EXEC_SUDO, sudocom);
4633         xsudt = lives_markup_escape_text(sudotext, -1);
4634         lives_free(sudotext);
4635       } else xsudt = lives_strdup("");
4636 
4637       prname = lives_markup_escape_text(argv[offs], -1);
4638       errtxt = lives_markup_escape_text(argv[3], -1);
4639 
4640       if (errtxt && *errtxt)
4641         errtxt2 = lives_strdup_printf(_("The following error occurred when running %s:"
4642                                         "\n\n'%s'\n\n"), prname, errtxt);
4643       else
4644         errtxt2 = lives_strdup_printf(_("LiVES was not able to run the programe %s\n"), prname);
4645 
4646       lives_free(errtxt);
4647 
4648       if (what == LIVES_PERM_DOWNLOAD_LOCAL) {
4649         verb = _("download");
4650         action = _("by downloading");
4651       } else {
4652         verb = _("copying");
4653         action = lives_strdup(_("by creating"));
4654       }
4655 
4656       text = lives_strdup_printf(_("%sYou may need to reinstall or update %s\n\n"
4657                                    "<b>Alternately, it may be possible to fix this "
4658                                    "%s an individual copy of the program\n%s to your "
4659                                    "home directory</b>\n"
4660                                    "Please consider the options "
4661                                    "and then decide how to proceed.\n"),
4662                                  errtxt2, prname, action, prname);
4663 
4664       lives_free(prname); lives_free(xsudt); lives_free(errtxt2); lives_free(action);
4665       title = (_("Problem Detected"));
4666       widget_opts.use_markup = TRUE;
4667       dlg = create_question_dialog(title, text);
4668       widget_opts.use_markup = FALSE;
4669       lives_free(title);
4670       lives_free(text);
4671       widget_opts.expand = LIVES_EXPAND_EXTRA_WIDTH | LIVES_EXPAND_DEFAULT_HEIGHT;
4672 
4673       lives_dialog_add_button_from_stock(LIVES_DIALOG(dlg), LIVES_STOCK_CANCEL, NULL,
4674                                          LIVES_RESPONSE_CANCEL);
4675 
4676       lives_dialog_add_button_from_stock(LIVES_DIALOG(dlg), LIVES_STOCK_ADD,
4677                                          (tmp = lives_strdup_printf(_("Proceed with %s"), verb)),
4678                                          LIVES_RESPONSE_YES);
4679       lives_free(tmp); lives_free(verb);
4680       widget_opts.expand = LIVES_EXPAND_DEFAULT;
4681       ret = lives_dialog_run(LIVES_DIALOG(dlg));
4682       lives_widget_destroy(dlg);
4683       lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
4684 
4685       if (ret == LIVES_RESPONSE_CANCEL) {
4686         lives_freep((void **)&mainw->permmgr->key);
4687         return FALSE;
4688       }
4689       return TRUE;
4690     default:
4691       break;
4692     }
4693   }
4694   return FALSE;
4695 }
4696 
4697 
do_layout_recover_dialog(void)4698 LIVES_GLOBAL_INLINE boolean do_layout_recover_dialog(void) {
4699   if (!do_yesno_dialog(
4700         _("\nLiVES has detected a multitrack layout from a previous session.\n"
4701           "Would you like to try and recover it ?\n"))) {
4702     recover_layout_cancelled(TRUE);
4703     return FALSE;
4704   } else {
4705     boolean ret;
4706     lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
4707     //lives_widget_context_update();
4708     ret = recover_layout();
4709     lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
4710     return ret;
4711   }
4712 }
4713 
4714