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