1 // saveplay.c
2 // LiVES (lives-exe)
3 // (c) G. Finch 2003 - 2018 (salsaman+lives@gmail.com)
4 // released under the GNU GPL 3 or later
5 // see file ../COPYING or www.gnu.org for licensing details
6 
7 #include <fcntl.h>
8 #include <glib.h>
9 
10 #include "main.h"
11 #include "callbacks.h"
12 #include "resample.h"
13 #include "effects.h"
14 #include "audio.h"
15 #include "htmsocket.h"
16 #include "cvirtual.h"
17 #include "interface.h"
18 
_start_playback(livespointer data)19 boolean _start_playback(livespointer data) {
20   int new_file, old_file;
21   int play_type = LIVES_POINTER_TO_INT(data);
22   if (play_type != 8 && mainw->noswitch) return TRUE;
23   switch (play_type) {
24   case 8: case 6: case 0:
25     /// normal play
26     play_all(play_type == 8);
27     if (play_type == 6) {
28       /// triggered by generator
29       // need to set this after playback ends; this stops the key from being activated (again) in effects.c
30       // also stops the (now defunct instance being unreffed)
31       mainw->gen_started_play = TRUE;
32     }
33     break;
34   case 1:
35     /// play selection
36     if (!mainw->multitrack) play_sel();
37     else multitrack_play_sel(NULL, mainw->multitrack);
38     break;
39   case 2:
40     /// play stream
41     mainw->play_start = 1;
42     mainw->play_end = INT_MAX;
43     play_file();
44     break;
45   case 3:
46     /// osc playall
47     mainw->osc_auto = 1; ///< request notifiction of success
48     play_all(FALSE);
49     mainw->osc_auto = 0;
50     break;
51   case 4:
52     /// osc playsel
53     mainw->osc_auto = 1; ///< request notifiction of success
54     if (!mainw->multitrack) play_sel();
55     else multitrack_play_sel(NULL, mainw->multitrack);
56     mainw->osc_auto = 0;
57     break;
58   case 5:
59     /// clipboard
60     play_file();
61     mainw->loop = mainw->oloop;
62     mainw->loop_cont = mainw->oloop_cont;
63 
64     if (mainw->pre_play_file > 0) {
65       switch_to_file(0, mainw->pre_play_file);
66     } else {
67       mainw->current_file = -1;
68       close_current_file(0);
69     }
70     if (mainw->cancelled == CANCEL_AUDIO_ERROR) {
71       handle_audio_timeout();
72       mainw->cancelled = CANCEL_ERROR;
73     }
74     break;
75   case 7:
76     /// yuv4mpeg
77     new_file = mainw->current_file;
78     old_file = mainw->pre_play_file;
79     play_file();
80     if (mainw->current_file != old_file && mainw->current_file != new_file)
81       old_file = mainw->current_file; // we could have rendered to a new file
82     mainw->current_file = new_file;
83     // close this temporary clip
84     close_current_file(old_file);
85     mainw->pre_play_file = -1;
86     break;
87   default:
88     /// do nothing
89     break;
90   }
91   return FALSE;
92 }
93 
start_playback(int type)94 LIVES_GLOBAL_INLINE boolean start_playback(int type) {return  _start_playback(LIVES_INT_TO_POINTER(type));}
95 
start_playback_async(int type)96 LIVES_GLOBAL_INLINE void start_playback_async(int type) {
97   lives_idle_add(_start_playback, LIVES_INT_TO_POINTER(type));
98   //lives_proc_thread_create(0, (lives_funcptr_t)_start_playback, 0, "i", type);
99   //_start_playback(LIVES_INT_TO_POINTER(type));
100 }
101 
102 
save_clip_values(int which)103 boolean save_clip_values(int which) {
104   lives_clip_t *sfile = mainw->files[which];
105   char *lives_header_new;
106   boolean all_ok = FALSE;
107   int asigned, endian;
108   int retval;
109 
110   if (which == 0 || which == mainw->scrap_file || which == mainw->ascrap_file) return TRUE;
111 
112   set_signal_handlers((SignalHandlerPointer)defer_sigint); // ignore ctrl-c
113 
114   asigned = !(sfile->signed_endian & AFORM_UNSIGNED);
115   endian = sfile->signed_endian & AFORM_BIG_ENDIAN;
116   if (which == mainw->ascrap_file)
117     lives_header_new = lives_build_filename(prefs->workdir, sfile->handle, LIVES_ACLIP_HEADER_NEW, NULL);
118   else
119     lives_header_new = lives_build_filename(prefs->workdir, sfile->handle, LIVES_CLIP_HEADER_NEW, NULL);
120 
121   do {
122     THREADVAR(com_failed) = THREADVAR(write_failed) = FALSE;
123     mainw->clip_header = fopen(lives_header_new, "w");
124     if (!mainw->clip_header) {
125       retval = do_write_failed_error_s_with_retry(lives_header_new, lives_strerror(errno));
126       if (retval == LIVES_RESPONSE_CANCEL) {
127         set_signal_handlers((SignalHandlerPointer)catch_sigint);
128         if (mainw->signal_caught) catch_sigint(mainw->signal_caught);
129         lives_free(lives_header_new);
130         return FALSE;
131       }
132     } else {
133       sfile->header_version = LIVES_CLIP_HEADER_VERSION;
134       do {
135         retval = 0;
136         if (!save_clip_value(which, CLIP_DETAILS_HEADER_VERSION, &sfile->header_version)) break;
137         if (!save_clip_value(which, CLIP_DETAILS_BPP, &sfile->bpp)) break;
138         if (sfile->clip_type == CLIP_TYPE_FILE && sfile->ext_src) {
139           lives_clip_data_t *cdata = ((lives_decoder_t *)sfile->ext_src)->cdata;
140           double dfps = (double)cdata->fps;
141           if (!save_clip_value(which, CLIP_DETAILS_FPS, &dfps)) break;
142           if (!save_clip_value(which, CLIP_DETAILS_PB_FPS, &sfile->fps)) break;
143         } else {
144           if (!save_clip_value(which, CLIP_DETAILS_FPS, &sfile->fps)) break;
145           if (!save_clip_value(which, CLIP_DETAILS_PB_FPS, &sfile->pb_fps)) break;
146         }
147         if (!save_clip_value(which, CLIP_DETAILS_WIDTH, &sfile->hsize)) break;
148         if (!save_clip_value(which, CLIP_DETAILS_HEIGHT, &sfile->vsize)) break;
149         if (!save_clip_value(which, CLIP_DETAILS_INTERLACE, &sfile->interlace)) break;
150         if (!save_clip_value(which, CLIP_DETAILS_UNIQUE_ID, &sfile->unique_id)) break;
151         if (!save_clip_value(which, CLIP_DETAILS_ARATE, &sfile->arps)) break;
152         if (!save_clip_value(which, CLIP_DETAILS_PB_ARATE, &sfile->arate)) break;
153         if (!save_clip_value(which, CLIP_DETAILS_ACHANS, &sfile->achans)) break;
154         if (sfile->achans > 0) {
155           if (!save_clip_value(which, CLIP_DETAILS_ASIGNED, &asigned)) break;
156           if (!save_clip_value(which, CLIP_DETAILS_AENDIAN, &endian)) break;
157         }
158         if (!save_clip_value(which, CLIP_DETAILS_ASAMPS, &sfile->asampsize)) break;
159         if (!save_clip_value(which, CLIP_DETAILS_FRAMES, &sfile->frames)) break;
160         if (!save_clip_value(which, CLIP_DETAILS_GAMMA_TYPE, &sfile->gamma_type)) break;
161         if (!save_clip_value(which, CLIP_DETAILS_TITLE, sfile->title)) break;
162         if (!save_clip_value(which, CLIP_DETAILS_AUTHOR, sfile->author)) break;
163         if (!save_clip_value(which, CLIP_DETAILS_COMMENT, sfile->comment)) break;
164         if (!save_clip_value(which, CLIP_DETAILS_PB_FRAMENO, &sfile->frameno)) break;
165         if (!save_clip_value(which, CLIP_DETAILS_CLIPNAME, sfile->name)) break;
166         if (!save_clip_value(which, CLIP_DETAILS_FILENAME, sfile->file_name)) break;
167         if (!save_clip_value(which, CLIP_DETAILS_KEYWORDS, sfile->keywords)) break;
168         if (sfile->clip_type == CLIP_TYPE_FILE && sfile->ext_src) {
169           lives_decoder_t *dplug = (lives_decoder_t *)sfile->ext_src;
170           if (!save_clip_value(which, CLIP_DETAILS_DECODER_NAME, (void *)dplug->decoder->name)) break;
171         }
172         all_ok = TRUE;
173       } while (FALSE);
174 
175       fclose(mainw->clip_header);
176 
177       if (!all_ok) {
178         retval = do_write_failed_error_s_with_retry(lives_header_new, NULL);
179       } else {
180         char *lives_header;
181         if (which == mainw->ascrap_file)
182           lives_header = lives_build_filename(prefs->workdir, sfile->handle, LIVES_ACLIP_HEADER, NULL);
183         else
184           lives_header = lives_build_filename(prefs->workdir, sfile->handle, LIVES_CLIP_HEADER, NULL);
185         // TODO - check the sizes before and after
186         lives_cp(lives_header_new, lives_header);
187         lives_free(lives_header);
188         if (THREADVAR(com_failed) || THREADVAR(write_failed)) {
189           retval = do_write_failed_error_s_with_retry(lives_header_new, NULL);
190         } else lives_rm(lives_header_new);
191       }
192     }
193   } while (retval == LIVES_RESPONSE_RETRY);
194 
195   if (mainw->signal_caught) catch_sigint(mainw->signal_caught);
196   set_signal_handlers((SignalHandlerPointer)catch_sigint);
197 
198   lives_free(lives_header_new);
199   mainw->clip_header = NULL;
200 
201   if (retval == LIVES_RESPONSE_CANCEL) return FALSE;
202 
203   return TRUE;
204 }
205 
206 
read_file_details(const char * file_name,boolean is_audio,boolean is_img)207 boolean read_file_details(const char *file_name, boolean is_audio, boolean is_img) {
208   // get preliminary details
209 
210   // is_audio set to TRUE prevents us from checking for images, and deleting the (existing) first frame
211   // therefore it is IMPORTANT to set it when loading new audio for an existing clip !
212 
213   // is_img will force unpacking of img into frames and return the count
214 
215   char *tmp, *com = lives_strdup_printf("%s get_details \"%s\" \"%s\" \"%s\" %d", prefs->backend_sync, cfile->handle,
216                                         (tmp = lives_filename_from_utf8(file_name, -1, NULL, NULL, NULL)),
217                                         get_image_ext_for_type(IMG_TYPE_BEST), mainw->opening_loc ? 3 :
218                                         is_audio ? 2 : is_img ? 4 : 0);
219   lives_free(tmp);
220   lives_popen(com, FALSE, mainw->msg, MAINW_MSG_SIZE);
221   lives_free(com);
222   if (THREADVAR(com_failed)) {
223     THREADVAR(com_failed) = FALSE;
224     return FALSE;
225   }
226   return TRUE;
227 }
228 
229 
get_deinterlace_string(void)230 const char *get_deinterlace_string(void) {
231   if (mainw->open_deint) {
232     if (USE_MPV) return "--deinterlace=yes";
233     return "-vf pp=ci";
234   } else return "";
235 }
236 
237 
deduce_file(const char * file_name,double start,int end)238 ulong deduce_file(const char *file_name, double start, int end) {
239   // this is a utility function to deduce whether we are dealing with a file,
240   // a selection, a backup, or a location
241   char short_file_name[PATH_MAX];
242   ulong uid;
243   mainw->img_concat_clip = -1;
244 
245   if (lives_strrstr(file_name, "://") && strncmp(file_name, "dvd://", 6)) {
246     mainw->opening_loc = TRUE;
247     uid = open_file(file_name);
248     mainw->opening_loc = FALSE;
249   } else {
250     lives_snprintf(short_file_name, PATH_MAX, "%s", file_name);
251     if (!(strcmp(file_name + strlen(file_name) - 4, "."LIVES_FILE_EXT_BACKUP))) {
252       uid = restore_file(file_name);
253     } else {
254       uid = open_file_sel(file_name, start, end);
255     }
256   }
257   return uid;
258 }
259 
260 
open_file(const char * file_name)261 ulong open_file(const char *file_name) {
262   // this function should be called to open a whole file
263   return open_file_sel(file_name, 0., 0);
264 }
265 
266 
rip_audio_cancelled(int old_file,weed_plant_t * mt_pb_start_event,boolean mt_has_audio_file)267 static boolean rip_audio_cancelled(int old_file, weed_plant_t *mt_pb_start_event,
268                                    boolean mt_has_audio_file) {
269 
270   if (mainw->cancelled == CANCEL_KEEP) {
271     // user clicked "enough"
272     mainw->cancelled = CANCEL_NONE;
273     return TRUE;
274   }
275 
276   end_threaded_dialog();
277 
278   d_print("\n");
279   d_print_cancelled();
280   close_current_file(old_file);
281 
282   if (mainw->multitrack) {
283     mainw->multitrack->pb_start_event = mt_pb_start_event;
284     mainw->multitrack->has_audio_file = mt_has_audio_file;
285   }
286 
287   lives_freep((void **)&mainw->file_open_params);
288   lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
289   return FALSE;
290 }
291 
292 
pad_init_silence(void)293 void pad_init_silence(void) {
294   cfile->undo1_dbl = 0.;
295   cfile->undo2_dbl = CLIP_TOTAL_TIME(mainw->current_file) - cfile->laudio_time;
296   cfile->undo_arate = cfile->arate;
297   cfile->undo_signed_endian = cfile->signed_endian;
298   cfile->undo_achans = cfile->achans;
299   cfile->undo_asampsize = cfile->asampsize;
300   cfile->undo_arps = cfile->arps;
301   d_print(_("Auto padding with %.4f seconds of silence at start..."), cfile->undo2_dbl);
302   if (on_ins_silence_activate(NULL, NULL)) d_print_done();
303   else d_print("\n");
304 }
305 
306 
307 #define AUDIO_FRAMES_TO_READ 100
308 
open_file_sel(const char * file_name,double start,int frames)309 ulong open_file_sel(const char *file_name, double start, int frames) {
310   LiVESResponseType response;
311   char msg[256], loc[PATH_MAX];
312   char *tmp = NULL;
313   char *isubfname = NULL;
314   char *fname = lives_strdup(file_name), *msgstr;
315   char *com, *what;
316 
317   int withsound = 1;
318   int old_file = mainw->current_file;
319   int new_file = old_file;
320 
321   int achans, arate, arps, asampsize;
322   int current_file;
323   int extra_frames = 0;
324   int probed_achans = 0;
325 
326   boolean mt_has_audio_file = TRUE;
327 
328   const lives_clip_data_t *cdata;
329 
330   weed_plant_t *mt_pb_start_event = NULL;
331 
332   if (!lives_file_test(fname, LIVES_FILE_TEST_EXISTS)) {
333     do_no_loadfile_error(fname);
334     lives_free(fname);
335     return 0;
336   }
337 
338   if (old_file == -1 || !CURRENT_CLIP_IS_VALID || !cfile->opening) {
339     new_file = mainw->first_free_file;
340 
341     if (!get_new_handle(new_file, fname)) {
342       lives_free(fname);
343       return 0;
344     }
345     lives_free(fname);
346 
347     lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
348     lives_widget_context_update();
349 
350     if (frames == 0) {
351       com = lives_strdup_printf(_("Opening %s"), file_name);
352     } else {
353       com = lives_strdup_printf(_("Opening %s start time %.2f sec. frames %d"), file_name, start, frames);
354     }
355     d_print(""); // exhaust "switch" message
356 
357     d_print(com);
358     lives_free(com);
359 
360     if (!mainw->save_with_sound) {
361       d_print(_(" without sound"));
362       withsound = 0;
363     }
364 
365     mainw->current_file = new_file;
366 
367     /// probe the file to see what it might be...
368     read_file_details(file_name, FALSE, FALSE);
369     lives_rm(cfile->info_file);
370     if (THREADVAR(com_failed)) return 0;
371 
372     if (*mainw->msg) add_file_info(cfile->handle, FALSE);
373 
374     if (mainw->multitrack) {
375       // set up for opening preview
376       mt_pb_start_event = mainw->multitrack->pb_start_event;
377       mt_has_audio_file = mainw->multitrack->has_audio_file;
378       mainw->multitrack->pb_start_event = NULL;
379       mainw->multitrack->has_audio_file = TRUE;
380     }
381 
382     cfile->img_type = lives_image_ext_to_img_type(prefs->image_ext);
383     if ((!strcmp(cfile->type, LIVES_IMAGE_TYPE_JPEG) || !strcmp(cfile->type, LIVES_IMAGE_TYPE_PNG))) {
384       read_file_details(file_name, FALSE, TRUE);
385       add_file_info(cfile->handle, FALSE);
386       if (cfile->frames == 0) {
387         d_print_failed();
388         close_current_file(old_file);
389         if (mainw->multitrack) {
390           mainw->multitrack->pb_start_event = mt_pb_start_event;
391           mainw->multitrack->has_audio_file = mt_has_audio_file;
392         }
393         lives_freep((void **)&mainw->file_open_params);
394         lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
395         return 0;
396       }
397       goto img_load;
398     }
399 
400     if (prefs->instant_open && !mainw->opening_loc) {
401       // cd to clip directory - so decoder plugins can write temp files
402       char *ppath = lives_build_filename(prefs->workdir, cfile->handle, NULL);
403       char *cwd = lives_get_current_dir();
404 
405       if (!mainw->decoders_loaded) {
406         mainw->decoder_list = load_decoders();
407         mainw->decoders_loaded = TRUE;
408       }
409 
410       lives_chdir(ppath, FALSE);
411       lives_free(ppath);
412 
413       cdata = get_decoder_cdata(mainw->current_file, prefs->disabled_decoders, NULL);
414 
415       lives_chdir(cwd, FALSE);
416       lives_free(cwd);
417 
418       if (cfile->ext_src) {
419         lives_decoder_t *dplug = (lives_decoder_t *)cfile->ext_src;
420         cfile->opening = TRUE;
421         cfile->clip_type = CLIP_TYPE_FILE;
422         cfile->img_type = IMG_TYPE_BEST; // override the pref
423 
424         if (cdata->frame_width > 0) {
425           cfile->hsize = cdata->frame_width;
426           cfile->vsize = cdata->frame_height;
427         } else {
428           cfile->hsize = cdata->width;
429           cfile->vsize = cdata->height;
430         }
431 
432         if (cfile->frames > cdata->nframes && cfile->frames != 123456789) {
433           extra_frames = cfile->frames - cdata->nframes;
434         }
435         cfile->frames = cdata->nframes;
436         if (!*cfile->author)
437           lives_snprintf(cfile->author, 1024, "%s", cdata->author);
438         if (!*cfile->title)
439           lives_snprintf(cfile->title, 1024, "%s", cdata->title);
440         if (!*cfile->comment)
441           lives_snprintf(cfile->comment, 1024, "%s", cdata->comment);
442 
443         if (frames > 0 && cfile->frames > frames) {
444           cfile->frames = frames;
445           extra_frames = 0;
446         }
447 
448         cfile->start = 1;
449         cfile->end = cfile->frames;
450 
451         what = (_("creating the frame index for the clip"));
452 
453         do {
454           response = LIVES_RESPONSE_OK;
455           create_frame_index(mainw->current_file, TRUE, cfile->fps * (start == 0 ? 0 : start - 1),
456                              frames == 0 ? cfile->frames : frames);
457           if (!cfile->frame_index) {
458             response = do_memory_error_dialog(what, (frames == 0 ? cfile->frames : frames) * 4);
459           }
460         } while (response == LIVES_RESPONSE_RETRY);
461         lives_free(what);
462         if (response == LIVES_RESPONSE_CANCEL) {
463           return 0;
464         }
465         probed_achans = cfile->achans;
466         cfile->arate = cfile->arps = cdata->arate;
467         cfile->achans = cdata->achans;
468         cfile->asampsize = cdata->asamps;
469 
470         cfile->signed_endian = get_signed_endian(cdata->asigned, capable->byte_order == LIVES_LITTLE_ENDIAN);
471 
472         if (cfile->achans > 0 && (dplug->decoder->rip_audio) && withsound == 1) {
473           // call rip_audio() in the decoder plugin
474           // the plugin gets a chance to do any internal cleanup in rip_audio_cleanup()
475 
476           int64_t stframe = cfile->fps * start + .5;
477           int64_t maxframe = (stframe + (frames == 0)) ? cfile->frames : frames;
478           int64_t nframes = AUDIO_FRAMES_TO_READ;
479           char *afile = get_audio_file_name(mainw->current_file, TRUE);
480 
481           msgstr = lives_strdup_printf(_("Opening audio for %s"), file_name);
482 
483           if (!LIVES_IS_PLAYING) resize(1);
484 
485           mainw->cancelled = CANCEL_NONE;
486 
487           if (!LIVES_IS_PLAYING) {
488             mainw->cancel_type = CANCEL_SOFT;
489             do_threaded_dialog(msgstr, TRUE);
490             mainw->cancel_type = CANCEL_KILL;
491           }
492 
493           do {
494             if (stframe + nframes > maxframe) nframes = maxframe - stframe;
495             if (nframes <= 0) break;
496             (dplug->decoder->rip_audio)(cdata, afile, stframe, nframes, NULL);
497             threaded_dialog_spin(0.);
498             stframe += nframes;
499           } while (mainw->cancelled == CANCEL_NONE);
500 
501           if (dplug->decoder->rip_audio_cleanup) {
502             (dplug->decoder->rip_audio_cleanup)(cdata);
503           }
504 
505           if (mainw->cancelled != CANCEL_NONE) {
506             if (!rip_audio_cancelled(old_file, mt_pb_start_event, mt_has_audio_file)) {
507               lives_free(afile);
508               return 0;
509             }
510           }
511           end_threaded_dialog();
512           lives_free(msgstr);
513           lives_free(afile);
514         } else {
515           cfile->arate = 0.;
516           cfile->achans = cfile->asampsize = 0;
517         }
518 
519         cfile->fps = cfile->pb_fps = cdata->fps;
520         d_print("\n");
521 
522         if (cfile->achans == 0 && probed_achans > 0 && withsound == 1) {
523           // plugin returned no audio, try with mplayer / mpv
524           if (probed_achans > MAX_ACHANS) {
525             probed_achans = MAX_ACHANS;
526             d_print(_("Forcing audio channels to %d\n"), MAX_ACHANS);
527           }
528 
529           if (!mainw->file_open_params) {
530 #if 1
531             mainw->file_open_params = lives_strdup("alang eng");
532 #else
533             mainw->file_open_params = lives_strdup("");
534 #endif
535           }
536           com = lives_strdup_printf("%s open \"%s\" \"%s\" %d %s:%s %.2f %d %d \"%s\"", prefs->backend, cfile->handle,
537                                     (tmp = lives_filename_from_utf8(file_name, -1, NULL, NULL, NULL)), -1,
538                                     prefs->image_ext, get_image_ext_for_type(IMG_TYPE_BEST), start, frames, probed_achans,
539                                     mainw->file_open_params);
540 
541           lives_free(tmp);
542 
543           lives_rm(cfile->info_file);
544           lives_system(com, FALSE);
545           lives_free(com);
546 
547           // if we have a quick-opening file, display the first and last frames now
548           // for some codecs this can be helpful since we can locate the last frame while audio is loading
549           if (cfile->clip_type == CLIP_TYPE_FILE && !LIVES_IS_PLAYING) resize(1);
550 
551           mainw->effects_paused = FALSE; // set to TRUE if user clicks "Enough"
552 
553           msgstr = lives_strdup_printf(_("Opening audio"), file_name);
554           if (!do_progress_dialog(TRUE, TRUE, msgstr)) {
555             // error or user cancelled or switched to another clip
556             lives_free(msgstr);
557 
558             cfile->opening_frames = -1;
559 
560             if (mainw->multitrack) {
561               mainw->multitrack->pb_start_event = mt_pb_start_event;
562               mainw->multitrack->has_audio_file = mt_has_audio_file;
563             }
564 
565             if (mainw->cancelled == CANCEL_NO_PROPOGATE) {
566               lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
567               mainw->cancelled = CANCEL_NONE;
568               return 0;
569             }
570 
571             // cancelled
572             if (mainw->cancelled != CANCEL_ERROR) {
573               lives_kill_subprocesses(cfile->handle, TRUE);
574             }
575 
576             lives_freep((void **)&mainw->file_open_params);
577             close_current_file(old_file);
578             lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
579             if (mainw->error) {
580               do_error_dialog(mainw->msg);
581               mainw->error = 0;
582               clear_mainw_msg();
583             }
584             sensitize();
585             return 0;
586           }
587           lives_free(msgstr);
588 
589           cfile->opening = FALSE;
590 
591           wait_for_bg_audio_sync(mainw->current_file);
592           if (mainw->error == 0) add_file_info(cfile->handle, TRUE);
593           mainw->error = 0;
594           get_total_time(cfile);
595 
596           if (prefs->auto_trim_audio) {
597             if ((cdata->sync_hint & SYNC_HINT_VIDEO_PAD_START) && cdata->video_start_time <= 1.) {
598               // pad with blank frames at start
599               int st_extra_frames = cdata->video_start_time * cfile->fps;
600               insert_blank_frames(mainw->current_file, st_extra_frames, 0, WEED_PALETTE_RGB24);
601               cfile->video_time += st_extra_frames / cfile->fps;
602               extra_frames -= st_extra_frames;
603               showclipimgs();
604               if (!mainw->multitrack)
605                 redraw_timeline(mainw->current_file);
606             }
607 
608             if ((cfile->frames + extra_frames) / cfile->fps > cfile->laudio_time) {
609               extra_frames = (cfile->laudio_time - (double)cfile->frames / cfile->fps) * cfile->fps;
610             }
611 
612             if (extra_frames > 0 || ((cdata->sync_hint & SYNC_HINT_VIDEO_PAD_END)
613                                      && (double)cfile->frames / cfile->fps < cfile->laudio_time)) {
614               // pad with blank frames at end
615               if (cdata->sync_hint & SYNC_HINT_VIDEO_PAD_END) {
616                 int xextra_frames = (cfile->laudio_time - (double)cfile->frames / cfile->fps) * cfile->fps;
617                 if (xextra_frames > extra_frames) extra_frames = xextra_frames;
618               }
619               insert_blank_frames(mainw->current_file, extra_frames, cfile->frames, WEED_PALETTE_RGB24);
620               cfile->video_time += extra_frames / cfile->fps;
621               load_end_image(cfile->end);
622             }
623             if (cfile->laudio_time > cfile->video_time + AV_TRACK_MIN_DIFF && cfile->frames > 0) {
624               if (cdata->sync_hint & SYNC_HINT_AUDIO_TRIM_START) {
625                 cfile->undo1_dbl = 0.;
626                 cfile->undo2_dbl = cfile->laudio_time - cfile->video_time;
627                 d_print(_("Auto trimming %.4f seconds of audio at start..."), cfile->undo2_dbl);
628                 if (on_del_audio_activate(NULL, NULL)) d_print_done();
629                 else d_print("\n");
630                 cfile->changed = FALSE;
631               }
632             }
633             if (cfile->laudio_time > cfile->video_time + AV_TRACK_MIN_DIFF && cfile->frames > 0) {
634               if (cdata->sync_hint & SYNC_HINT_AUDIO_TRIM_END) {
635                 cfile->end = cfile->frames;
636                 d_print(_("Auto trimming %.4f seconds of audio at end..."), cfile->laudio_time - cfile->video_time);
637                 if (on_trim_audio_activate(NULL, LIVES_INT_TO_POINTER(0))) d_print_done();
638                 else d_print("\n");
639                 cfile->changed = FALSE;
640               }
641             }
642             if (!mainw->effects_paused && cfile->afilesize > 0 && cfile->achans > 0
643                 && CLIP_TOTAL_TIME(mainw->current_file) > cfile->laudio_time + AV_TRACK_MIN_DIFF) {
644               if (cdata->sync_hint & SYNC_HINT_AUDIO_PAD_START) {
645                 pad_init_silence();
646                 cfile->changed = FALSE;
647               }
648               if (cdata->sync_hint & SYNC_HINT_AUDIO_PAD_END) {
649                 cfile->undo1_dbl = cfile->laudio_time;
650                 cfile->undo2_dbl = CLIP_TOTAL_TIME(mainw->current_file) - cfile->laudio_time;
651                 cfile->undo_arate = cfile->arate;
652                 cfile->undo_signed_endian = cfile->signed_endian;
653                 cfile->undo_achans = cfile->achans;
654                 cfile->undo_asampsize = cfile->asampsize;
655                 cfile->undo_arps = cfile->arps;
656                 d_print(_("Auto padding with %.4f seconds of silence at end..."), cfile->undo2_dbl);
657                 if (on_ins_silence_activate(NULL, NULL)) d_print_done();
658                 else d_print("\n");
659                 cfile->changed = FALSE;
660 		// *INDENT-OFF*
661               }}}}
662 	// *INDENT-ON*
663 
664         get_mime_type(cfile->type, 40, cdata);
665         save_frame_index(mainw->current_file);
666       }
667     }
668 
669     if (cfile->ext_src) {
670       if (mainw->open_deint) {
671         // override what the plugin says
672         cfile->deinterlace = TRUE;
673         cfile->interlace = LIVES_INTERLACE_TOP_FIRST; // guessing
674         save_clip_value(mainw->current_file, CLIP_DETAILS_INTERLACE, &cfile->interlace);
675         if (THREADVAR(com_failed) || THREADVAR(write_failed)) do_header_write_error(mainw->current_file);
676       }
677     } else {
678       // be careful, here we switch from mainw->opening_loc to cfile->opening_loc
679       if (mainw->opening_loc) {
680         cfile->opening_loc = TRUE;
681         mainw->opening_loc = FALSE;
682       } else {
683         if (cfile->f_size > prefs->warn_file_size * 1000000. && mainw->is_ready && frames == 0) {
684           char *fsize_ds = lives_format_storage_space_string((uint64_t)cfile->f_size);
685           char *warn = lives_strdup_printf(
686                          _("\nLiVES cannot Instant Open this file, it may take some time to load.\n"
687                            "Are you sure you wish to continue ?"),
688                          fsize_ds);
689           lives_free(fsize_ds);
690           if (!do_warning_dialog_with_check(warn, WARN_MASK_FSIZE)) {
691             lives_free(warn);
692             close_current_file(old_file);
693             if (mainw->multitrack) {
694               mainw->multitrack->pb_start_event = mt_pb_start_event;
695               mainw->multitrack->has_audio_file = mt_has_audio_file;
696             }
697             lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
698             return 0;
699           }
700           lives_free(warn);
701           d_print(_(" - please be patient."));
702 
703         }
704         d_print("\n");
705 #if defined DEBUG
706         g_print("open_file: dpd in\n");
707 #endif
708       }
709     }
710 
711     // set undo_start and undo_end for preview
712     cfile->undo_start = 1;
713     cfile->undo_end = cfile->frames;
714 
715     if (cfile->achans > 0) {
716       cfile->opening_audio = TRUE;
717     }
718 
719     // these will get reset as we have no audio file yet, so preserve them
720     achans = cfile->achans;
721     arate = cfile->arate;
722     arps = cfile->arps;
723     asampsize = cfile->asampsize;
724     cfile->old_frames = cfile->frames;
725     cfile->frames = 0;
726 
727     // we need this FALSE here, otherwise we will switch straight back here...
728     cfile->opening = FALSE;
729 
730     // force a resize
731     current_file = mainw->current_file;
732 
733     cfile->opening = TRUE;
734     cfile->achans = achans;
735     cfile->arate = arate;
736     cfile->arps = arps;
737     cfile->asampsize = asampsize;
738     cfile->frames = cfile->old_frames;
739 
740     if (cfile->frames <= 0) {
741       cfile->undo_end = cfile->frames = 123456789;
742     }
743     if (cfile->hsize * cfile->vsize == 0) {
744       cfile->frames = 0;
745     }
746 
747     if (!mainw->multitrack) get_play_times();
748 
749     add_to_clipmenu();
750     set_main_title(cfile->file_name, 0);
751 
752     mainw->effects_paused = FALSE;
753 
754     if (!cfile->ext_src) {
755       if (!mainw->file_open_params) mainw->file_open_params = lives_strdup("");
756 
757       tmp = lives_strconcat(mainw->file_open_params, get_deinterlace_string(), NULL);
758       lives_free(mainw->file_open_params);
759       mainw->file_open_params = tmp;
760 
761       if (cfile->achans > MAX_ACHANS) {
762         cfile->achans = MAX_ACHANS;
763         d_print(_("Forcing audio channels to %d\n"), MAX_ACHANS);
764       }
765 
766       com = lives_strdup_printf("%s open \"%s\" \"%s\" %d %s:%s %.2f %d %d \"%s\"", prefs->backend, cfile->handle,
767                                 (tmp = lives_filename_from_utf8(file_name, -1, NULL, NULL, NULL)), withsound,
768                                 prefs->image_ext, get_image_ext_for_type(IMG_TYPE_BEST), start, frames, cfile->achans,
769                                 mainw->file_open_params);
770 
771       lives_rm(cfile->info_file);
772       lives_system(com, FALSE);
773       lives_free(com);
774       lives_free(tmp);
775 
776       if (mainw->toy_type == LIVES_TOY_TV) {
777         // for LiVES TV we do an auto-preview
778         mainw->play_start = cfile->start = cfile->undo_start;
779         mainw->play_end = cfile->end = cfile->undo_end;
780         mainw->preview = TRUE;
781         do {
782           desensitize();
783           procw_desensitize();
784           on_playsel_activate(NULL, NULL);
785         } while (mainw->cancelled == CANCEL_KEEP_LOOPING);
786         mainw->preview = FALSE;
787         on_toy_activate(NULL, LIVES_INT_TO_POINTER(LIVES_TOY_NONE));
788         lives_freep((void **)&mainw->file_open_params);
789         mainw->cancelled = CANCEL_NONE;
790         lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
791         return 0;
792       }
793     }
794 
795     //  loading:
796 
797     // 'entry point' when we switch back
798 
799     // spin until loading is complete
800     // afterwards, mainw->msg will contain file details
801     cfile->progress_start = cfile->progress_end = 0;
802 
803     // (also check for cancel)
804     msgstr = lives_strdup_printf(_("Opening %s"), file_name);
805 
806     if (!cfile->ext_src && mainw->toy_type != LIVES_TOY_TV) {
807       mainw->cs_permitted = TRUE;
808       mainw->disk_mon = MONITOR_QUOTA;
809       if (!do_progress_dialog(TRUE, TRUE, msgstr)) {
810         // user cancelled or switched to another clip
811         mainw->cs_permitted = FALSE;
812         mainw->disk_mon = 0;
813 
814         lives_free(msgstr);
815         mainw->effects_paused = FALSE;
816 
817         if (mainw->cancelled == CANCEL_NO_PROPOGATE) {
818           mainw->cancelled = CANCEL_NONE;
819           lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
820           return 0;
821         }
822 
823         // cancelled
824         // clean up our temp files
825         if (IS_VALID_CLIP(current_file)) mainw->current_file = current_file;
826         lives_kill_subprocesses(cfile->handle, TRUE);
827         lives_freep((void **)&mainw->file_open_params);
828         close_current_file(old_file);
829         if (mainw->multitrack) {
830           mainw->multitrack->pb_start_event = mt_pb_start_event;
831           mainw->multitrack->has_audio_file = mt_has_audio_file;
832         }
833         lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
834 
835         // mainw->error is TRUE if we could not open the file
836         if (mainw->error) {
837           d_print_failed();
838           do_error_dialog(mainw->msg);
839         }
840         if (!mainw->multitrack)
841           redraw_timeline(mainw->current_file);
842         showclipimgs();
843         return 0;
844       }
845       mainw->cs_permitted = FALSE;
846       mainw->disk_mon = 0;
847     }
848     lives_free(msgstr);
849   }
850 
851   if (cfile->ext_src && cfile->achans > 0) {
852     char *afile = get_audio_file_name(mainw->current_file, TRUE);
853     char *ofile = get_audio_file_name(mainw->current_file, FALSE);
854     rename(afile, ofile);
855     lives_free(afile);
856     lives_free(ofile);
857   }
858 
859   cfile->opening = cfile->opening_audio = cfile->opening_only_audio = FALSE;
860   cfile->opening_frames = -1;
861   mainw->effects_paused = FALSE;
862 
863 #if defined DEBUG
864   g_print("Out of dpd\n");
865 #endif
866 
867   if (mainw->multitrack) {
868     mainw->multitrack->pb_start_event = mt_pb_start_event;
869     mainw->multitrack->has_audio_file = mt_has_audio_file;
870   }
871 
872   // mainw->error is TRUE if we could not open the file
873   if (mainw->error) {
874     do_error_dialog(mainw->msg);
875     d_print_failed();
876     close_current_file(old_file);
877     lives_freep((void **)&mainw->file_open_params);
878     lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
879     return 0;
880   }
881 
882   if (cfile->opening_loc) {
883     cfile->changed = TRUE;
884     cfile->opening_loc = FALSE;
885   } else {
886     if (prefs->autoload_subs) {
887       char filename[512];
888       char *subfname;
889       lives_subtitle_type_t subtype = SUBTITLE_TYPE_NONE;
890 
891       lives_snprintf(filename, 512, "%s", file_name);
892       get_filename(filename, FALSE); // strip extension
893       isubfname = lives_strdup_printf("%s.%s", filename, LIVES_FILE_EXT_SRT);
894       if (lives_file_test(isubfname, LIVES_FILE_TEST_EXISTS)) {
895         subfname = lives_build_filename(prefs->workdir, cfile->handle, SUBS_FILENAME "." LIVES_FILE_EXT_SRT, NULL);
896         subtype = SUBTITLE_TYPE_SRT;
897       } else {
898         lives_free(isubfname);
899         isubfname = lives_strdup_printf("%s.%s", filename, LIVES_FILE_EXT_SUB);
900         if (lives_file_test(isubfname, LIVES_FILE_TEST_EXISTS)) {
901           subfname = lives_build_filename(prefs->workdir, cfile->handle, SUBS_FILENAME "." LIVES_FILE_EXT_SUB, NULL);
902           subtype = SUBTITLE_TYPE_SUB;
903         }
904       }
905       if (subtype != SUBTITLE_TYPE_NONE) {
906         lives_cp(isubfname, subfname);
907         if (!THREADVAR(com_failed))
908           subtitles_init(cfile, subfname, subtype);
909         lives_free(subfname);
910       } else {
911         lives_freep((void **)&isubfname);
912       }
913     }
914   }
915 
916   // now file should be loaded...get full details
917   if (!cfile->ext_src) add_file_info(cfile->handle, FALSE);
918   cfile->is_loaded = TRUE;
919 
920   if (cfile->frames <= 0) {
921     if (cfile->afilesize == 0l) {
922       // we got neither video nor audio...
923       lives_snprintf(msg, 256, "%s", _
924                      ("\n\nLiVES was unable to extract either video or audio.\n"
925                       "Please check the terminal window for more details.\n"));
926 
927       if (!capable->has_mplayer && !capable->has_mplayer2 && !capable->has_mpv) {
928         lives_strappend(msg, 256, _("\n\nYou may need to install mplayer, mplayer2 or mpv to open this file.\n"));
929       } else {
930         if (capable->has_mplayer) {
931           get_location(EXEC_MPLAYER, loc, PATH_MAX);
932         } else if (capable->has_mplayer2) {
933           get_location(EXEC_MPLAYER2, loc, PATH_MAX);
934         } else if (capable->has_mpv) {
935           get_location(EXEC_MPV, loc, PATH_MAX);
936         }
937 
938         if (strcmp(prefs->video_open_command, loc) && strncmp(prefs->video_open_command + 1, loc, strlen(loc))) {
939           lives_strappend(msg, 256, _("\n\nPlease check the setting of Video Open Command in\nTools|Preferences|Decoding\n"));
940         }
941       }
942       widget_opts.non_modal = TRUE;
943       do_error_dialog(msg);
944       widget_opts.non_modal = FALSE;
945       d_print_failed();
946       close_current_file(old_file);
947       if (mainw->multitrack) {
948         mainw->multitrack->pb_start_event = mt_pb_start_event;
949         mainw->multitrack->has_audio_file = mt_has_audio_file;
950       }
951       lives_freep((void **)&mainw->file_open_params);
952       lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
953       return 0;
954     }
955     cfile->frames = 0;
956   }
957 
958   if (!cfile->ext_src) {
959     extra_frames = cfile->frames;
960     add_file_info(cfile->handle, FALSE);
961     extra_frames -= cfile->frames;
962     cfile->end = cfile->frames;
963     cfile->video_time = cfile->frames / cfile->fps;
964   } else {
965     add_file_info(NULL, FALSE);
966     if (cfile->f_size == 0) {
967       off_t fsize = sget_file_size((char *)file_name);
968       if (fsize < 0) fsize = 0;
969       cfile->f_size = (size_t)fsize;
970     }
971   }
972 
973   if (!cfile->ext_src) {
974     reget_afilesize(mainw->current_file);
975     if (prefs->auto_trim_audio || prefs->keep_all_audio) {
976       if (cfile->laudio_time > cfile->video_time && cfile->frames > 0) {
977         if (!prefs->keep_all_audio || start != 0. || extra_frames <= 0) {
978           d_print(_("Auto trimming %.2f seconds of audio at end..."), cfile->laudio_time - cfile->video_time);
979           if (on_trim_audio_activate(NULL, LIVES_INT_TO_POINTER(0))) d_print_done();
980           else d_print("\n");
981           cfile->changed = FALSE;
982         } else {
983           /// insert blank frames
984           if (prefs->keep_all_audio && (cfile->laudio_time - cfile->video_time) * cfile->fps > extra_frames)
985             extra_frames = (cfile->laudio_time - cfile->video_time) * cfile->fps;
986           insert_blank_frames(mainw->current_file, extra_frames, cfile->frames, WEED_PALETTE_RGB24);
987           cfile->video_time += extra_frames / cfile->fps;
988           cfile->end = cfile->frames;
989           showclipimgs();
990           if (!mainw->multitrack)
991             redraw_timeline(mainw->current_file);
992         }
993       }
994       if (cfile->laudio_time < cfile->video_time && cfile->achans > 0) {
995         cfile->undo1_dbl = cfile->laudio_time;
996         cfile->undo2_dbl = CLIP_TOTAL_TIME(mainw->current_file) - cfile->laudio_time;
997         cfile->undo_arate = cfile->arate;
998         cfile->undo_signed_endian = cfile->signed_endian;
999         cfile->undo_achans = cfile->achans;
1000         cfile->undo_asampsize = cfile->asampsize;
1001         cfile->undo_arps = cfile->arps;
1002         d_print(_("Auto padding with %.2f seconds of silence at end..."), cfile->undo2_dbl);
1003         if (on_ins_silence_activate(NULL, NULL)) d_print_done();
1004         else d_print("\n");
1005         cfile->changed = FALSE;
1006       }
1007     }
1008   }
1009 
1010   if (isubfname) {
1011     d_print(_("Loaded subtitle file: %s\n"), isubfname);
1012     lives_free(isubfname);
1013   }
1014 
1015 img_load:
1016   current_file = mainw->current_file;
1017 
1018 #ifdef GET_MD5
1019   g_print("md5sum is %s\n", get_md5sum(file_name));
1020 #endif
1021 
1022   // TODO - prompt for copy to origs (unless it is already there)
1023 
1024   lives_notify(LIVES_OSC_NOTIFY_CLIP_OPENED, "");
1025 
1026   if (prefs->show_recent && !mainw->is_generating) {
1027     add_to_recent(file_name, start, frames, mainw->file_open_params);
1028   }
1029   lives_freep((void **)&mainw->file_open_params);
1030 
1031   if (!strcmp(cfile->type, "Frames") || !strcmp(cfile->type, LIVES_IMAGE_TYPE_JPEG) ||
1032       !strcmp(cfile->type, LIVES_IMAGE_TYPE_PNG) ||
1033       !strcmp(cfile->type, "Audio")) {
1034     cfile->is_untitled = TRUE;
1035   }
1036 
1037   if ((!strcmp(cfile->type, LIVES_IMAGE_TYPE_JPEG) || !strcmp(cfile->type, LIVES_IMAGE_TYPE_PNG))) {
1038     if (mainw->img_concat_clip == -1) {
1039       cfile->img_type = lives_image_type_to_img_type(cfile->type);
1040       mainw->img_concat_clip = mainw->current_file;
1041       add_to_clipmenu();
1042       set_main_title(cfile->file_name, 0);
1043       cfile->opening = cfile->opening_audio = cfile->opening_only_audio = FALSE;
1044       cfile->opening_frames = -1;
1045       mainw->effects_paused = FALSE;
1046       cfile->is_loaded = TRUE;
1047     } else if (prefs->concat_images) {
1048       // insert this image into our image clip, close this file
1049 
1050       com = lives_strdup_printf("%s insert \"%s\" \"%s\" %d 1 1 \"%s\" 0 %d %d %d", prefs->backend,
1051                                 mainw->files[mainw->img_concat_clip]->handle,
1052                                 get_image_ext_for_type(mainw->files[mainw->img_concat_clip]->img_type),
1053                                 mainw->files[mainw->img_concat_clip]->frames,
1054                                 cfile->handle, mainw->files[mainw->img_concat_clip]->frames,
1055                                 mainw->files[mainw->img_concat_clip]->hsize, mainw->files[mainw->img_concat_clip]->vsize);
1056 
1057       mainw->current_file = mainw->img_concat_clip;
1058 
1059       lives_rm(cfile->info_file);
1060 
1061       mainw->cancelled = CANCEL_NONE;
1062       mainw->error = FALSE;
1063       lives_system(com, FALSE);
1064       lives_free(com);
1065 
1066       do_auto_dialog(_("Adding image..."), 2);
1067 
1068       if (current_file != mainw->img_concat_clip) {
1069         mainw->current_file = current_file;
1070         close_current_file(mainw->img_concat_clip);
1071       }
1072 
1073       if (mainw->cancelled || mainw->error) {
1074         goto load_done;
1075       }
1076 
1077       cfile->frames++;
1078       cfile->end++;
1079 
1080       lives_signal_handler_block(mainw->spinbutton_end, mainw->spin_end_func);
1081       lives_spin_button_set_range(LIVES_SPIN_BUTTON(mainw->spinbutton_end), cfile->frames == 0 ? 0 : 1, cfile->frames);
1082       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), cfile->end);
1083       lives_signal_handler_unblock(mainw->spinbutton_end, mainw->spin_end_func);
1084 
1085       lives_signal_handler_block(mainw->spinbutton_start, mainw->spin_start_func);
1086       lives_spin_button_set_range(LIVES_SPIN_BUTTON(mainw->spinbutton_start), cfile->frames == 0 ? 0 : 1, cfile->frames);
1087       lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), cfile->start);
1088       lives_signal_handler_unblock(mainw->spinbutton_start, mainw->spin_start_func);
1089       lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
1090       return 0;
1091     }
1092   }
1093 
1094   // set new style file details
1095   if (!save_clip_values(current_file)) {
1096     close_current_file(old_file);
1097     return 0;
1098   }
1099 
1100   if (prefs->crash_recovery) add_to_recovery_file(cfile->handle);
1101 
1102 load_done:
1103   if (!mainw->multitrack) {
1104     // update widgets
1105     switch_to_file((mainw->current_file = 0), current_file);
1106     lives_widget_queue_draw(LIVES_MAIN_WINDOW_WIDGET);
1107   } else {
1108     lives_mt *multi = mainw->multitrack;
1109     mainw->multitrack = NULL; // allow getting of afilesize
1110     current_file = mainw->current_file;
1111     mainw->current_file = -1; // stop framebars from being drawn
1112     reget_afilesize(current_file);
1113     mainw->current_file = current_file;
1114     mainw->multitrack = multi;
1115     get_total_time(cfile);
1116     if (!mainw->is_generating) mainw->current_file = mainw->multitrack->render_file;
1117     mt_init_clips(mainw->multitrack, current_file, TRUE);
1118     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
1119     mt_clip_select(mainw->multitrack, TRUE);
1120   }
1121   lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
1122   check_storage_space(-1, FALSE);
1123   return cfile->unique_id;
1124 }
1125 
1126 
save_subs_to_file(lives_clip_t * sfile,char * fname)1127 static void save_subs_to_file(lives_clip_t *sfile, char *fname) {
1128   char *ext;
1129   lives_subtitle_type_t otype, itype;
1130 
1131   if (!sfile->subt) return;
1132 
1133   itype = sfile->subt->type;
1134 
1135   ext = get_extension(fname);
1136 
1137   if (!strcmp(ext, LIVES_FILE_EXT_SUB)) otype = SUBTITLE_TYPE_SUB;
1138   else if (!strcmp(ext, LIVES_FILE_EXT_SRT)) otype = SUBTITLE_TYPE_SRT;
1139   else otype = itype;
1140 
1141   lives_free(ext);
1142 
1143   // TODO - use sfile->subt->save_fn
1144   switch (otype) {
1145   case SUBTITLE_TYPE_SUB:
1146     save_sub_subtitles(sfile, (double)(sfile->start - 1) / sfile->fps, (double)sfile->end / sfile->fps,
1147                        (double)(sfile->start - 1) / sfile->fps, fname);
1148     break;
1149 
1150   case SUBTITLE_TYPE_SRT:
1151     save_srt_subtitles(sfile, (double)(sfile->start - 1) / sfile->fps, (double)sfile->end / sfile->fps,
1152                        (double)(sfile->start - 1) / sfile->fps, fname);
1153     break;
1154 
1155   default:
1156     return;
1157   }
1158 
1159   d_print(_("Subtitles were saved as %s\n"), mainw->subt_save_file);
1160 }
1161 
1162 
get_handle_from_info_file(int index)1163 boolean get_handle_from_info_file(int index) {
1164   // called from get_new_handle to get the 'real' file handle
1165   // because until we know the handle we can't use the normal info file yet
1166   char *com = lives_strdup_printf("%s new", prefs->backend_sync);
1167 
1168   lives_popen(com, FALSE, mainw->msg, MAINW_MSG_SIZE);
1169   lives_free(com);
1170 
1171   if (!strncmp(mainw->msg, "error|", 6)) {
1172     handle_backend_errors(FALSE);
1173     return FALSE;
1174   }
1175 
1176   if (!mainw->files[index]) {
1177     mainw->files[index] = (lives_clip_t *)(lives_calloc(1, sizeof(lives_clip_t)));
1178     mainw->files[index]->clip_type = CLIP_TYPE_DISK; // the default
1179   }
1180   lives_snprintf(mainw->files[index]->handle, 256, "%s", mainw->msg);
1181 
1182   return TRUE;
1183 }
1184 
1185 
save_frame(LiVESMenuItem * menuitem,livespointer user_data)1186 void save_frame(LiVESMenuItem * menuitem, livespointer user_data) {
1187   int frame;
1188   // save a single frame from a clip
1189   char *filt[2];
1190   char *ttl;
1191   char *filename, *defname;
1192 
1193   filt[0] = lives_strdup_printf("*.%s", get_image_ext_for_type(cfile->img_type));
1194   filt[1] = NULL;
1195 
1196   frame = LIVES_POINTER_TO_INT(user_data);
1197 
1198   if (frame > 0)
1199     ttl = lives_strdup_printf(_("Save Frame %d"), frame);
1200 
1201   else
1202     ttl = (_("Save Frame"));
1203 
1204   defname = lives_strdup_printf("frame%08d.%s", frame, get_image_ext_for_type(cfile->img_type));
1205 
1206   filename = choose_file(*mainw->image_dir ? mainw->image_dir : NULL, defname,
1207                          filt, LIVES_FILE_CHOOSER_ACTION_SAVE, ttl, NULL);
1208 
1209   lives_free(defname); lives_free(filt[0]); lives_free(ttl);
1210 
1211   if (!filename) return;
1212   if (!*filename) {
1213     lives_free(filename);
1214     return;
1215   }
1216 
1217   if (!save_frame_inner(mainw->current_file, frame, filename, -1, -1, FALSE)) {
1218     lives_free(filename);
1219     return;
1220   }
1221 
1222   lives_snprintf(mainw->image_dir, PATH_MAX, "%s", filename);
1223   lives_free(filename);
1224   get_dirname(mainw->image_dir);
1225   if (prefs->save_directories) {
1226     set_utf8_pref(PREF_IMAGE_DIR, mainw->image_dir);
1227   }
1228 }
1229 
1230 
save_log_file(const char * prefix)1231 static void save_log_file(const char *prefix) {
1232   int logfd;
1233 
1234   // save the logfile in workdir
1235 #ifndef IS_MINGW
1236   char *logfile = lives_strdup_printf("%s/%s_%d_%d.txt", prefs->workdir, prefix, lives_getuid(), lives_getgid());
1237   if ((logfd = creat(logfile, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) != -1) {
1238 #else
1239   char *logfile = lives_strdup_printf("%s\\%s_%d_%d.txt", prefs->workdir, prefix, lives_getuid(), lives_getgid());
1240   if ((logfd = creat(logfile, S_IRUSR | S_IWUSR)) != -1) {
1241 #endif
1242     char *btext = lives_text_view_get_text(mainw->optextview);
1243     lives_write(logfd, btext, strlen(btext), TRUE); // not really important if it fails
1244     lives_free(btext);
1245     close(logfd);
1246   }
1247   lives_free(logfile);
1248 }
1249 
1250 
1251 LIVES_GLOBAL_INLINE void set_default_comment(lives_clip_t *sfile, const char *extrat) {
1252   if (!*sfile->comment)
1253     lives_snprintf(sfile->comment, 1024, "Created with LiVES version %s.\nSee: %s\n%s",
1254                    LiVES_VERSION, LIVES_WEBSITE, extrat);
1255   if (!*sfile->author && *prefs->def_author)
1256     lives_snprintf(sfile->author, 1024, "%s", prefs->def_author);
1257 }
1258 
1259 
1260 void save_file(int clip, int start, int end, const char *filename) {
1261   // save clip from frame start to frame end
1262   lives_clip_t *sfile = mainw->files[clip], *nfile = NULL;
1263   double aud_start = 0., aud_end = 0.;
1264 
1265   char *n_file_name = NULL;
1266   char *fps_string;
1267   char *extra_params = NULL;
1268   char *redir = lives_strdup("1>&2 2>"LIVES_DEVNULL);
1269   char *new_stderr_name = NULL;
1270   char *mesg, *bit, *tmp;
1271   char *com, *msg;
1272   char *full_file_name = NULL;
1273   char *enc_exec_name = NULL;
1274   char *clipdir;
1275   char *cwd;
1276 
1277   boolean recheck_name = FALSE;
1278 
1279   int new_stderr = -1;
1280   int retval;
1281   int startframe = 1;
1282   int current_file = mainw->current_file;
1283   int asigned = !(sfile->signed_endian & AFORM_UNSIGNED); // 1 is signed (in backend)
1284   int aendian = (sfile->signed_endian & AFORM_BIG_ENDIAN); // 2 is bigend
1285   int arate;
1286   int new_file = -1;
1287 
1288 #ifdef GUI_GTK
1289   GError *gerr = NULL;
1290 #endif
1291 
1292   struct stat filestat;
1293 
1294   off_t fsize;
1295 
1296   LiVESWidget *hbox;
1297   frames_t res;
1298 
1299   boolean safe_symlinks = prefs->safe_symlinks;
1300   boolean not_cancelled = FALSE;
1301   boolean output_exists = FALSE;
1302   boolean save_all = FALSE;
1303   boolean debug_mode = FALSE;
1304 
1305   if (!check_storage_space(mainw->current_file, FALSE)) return;
1306 
1307   lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
1308   lives_widget_context_update();
1309 
1310   if (start == 1 && end == sfile->frames) save_all = TRUE;
1311 
1312   // new handling for save selection:
1313   // symlink images 1 - n to the encoded frames
1314   // symlinks are now created in /tmp (for dynebolic)
1315   // then encode the symlinked frames
1316 
1317   if (!filename) {
1318     // prompt for encoder type/output format
1319     if (prefs->show_rdet) {
1320       int response;
1321       rdet = create_render_details(1); // WARNING !! - rdet is global in events.h
1322 
1323       while (1) {
1324         response = lives_dialog_run(LIVES_DIALOG(rdet->dialog));
1325         lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
1326         lives_widget_hide(rdet->dialog);
1327 
1328         if (response == LIVES_RESPONSE_CANCEL) {
1329           lives_widget_destroy(rdet->dialog);
1330           lives_free(rdet->encoder_name);
1331           lives_freep((void **)&rdet);
1332           lives_freep((void **)&resaudw);
1333           return;
1334         }
1335 
1336         clear_mainw_msg();
1337         // initialise new plugin
1338 
1339         if (enc_exec_name) lives_free(enc_exec_name);
1340         enc_exec_name = lives_build_filename(prefs->lib_dir, PLUGIN_EXEC_DIR, PLUGIN_ENCODERS, prefs->encoder.name, NULL);
1341 
1342         com = lives_strdup_printf("\"%s\" init", enc_exec_name);
1343         lives_popen(com, TRUE, mainw->msg, MAINW_MSG_SIZE);
1344         lives_free(com);
1345 
1346         if (strcmp(mainw->msg, "initialised\n")) {
1347           if (*mainw->msg) {
1348             msg = lives_strdup_printf(_("\n\nThe '%s' plugin reports:\n%s\n"), prefs->encoder.name, mainw->msg);
1349           } else {
1350             msg = lives_strdup_printf
1351                   (_("\n\nUnable to find the 'init' method in the %s plugin.\n"
1352                      "The plugin may be broken or not installed correctly."), prefs->encoder.name);
1353           }
1354           do_error_dialog(msg);
1355           lives_free(msg);
1356         } else break;
1357       }
1358       if (rdet->debug && lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(rdet->debug)))
1359         debug_mode = TRUE;
1360     }
1361   }
1362 
1363   if (!enc_exec_name)
1364     enc_exec_name = lives_build_filename(prefs->lib_dir, PLUGIN_EXEC_DIR, PLUGIN_ENCODERS, prefs->encoder.name, NULL);
1365 
1366   // get file extension
1367   check_encoder_restrictions(TRUE, FALSE, save_all);
1368 
1369   hbox = lives_hbox_new(FALSE, 0);
1370   mainw->fx1_bool = TRUE;
1371   add_suffix_check(LIVES_BOX(hbox), prefs->encoder.of_def_ext);
1372   lives_widget_show_all(hbox);
1373 
1374   if (palette->style & STYLE_1) {
1375     lives_widget_set_fg_color(hbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
1376     lives_widget_set_bg_color(hbox, LIVES_WIDGET_STATE_NORMAL, &palette->normal_back);
1377   }
1378 
1379   if (!filename) {
1380     char *ttl = (_("Save Clip"));
1381     do {
1382       lives_freep((void **)&n_file_name);
1383       n_file_name = choose_file_bg(mainw->vid_save_dir, NULL, NULL, LIVES_FILE_CHOOSER_ACTION_SAVE, ttl, hbox);
1384       if (mainw->fc_buttonresponse == LIVES_RESPONSE_CANCEL) return;
1385     } while (!*n_file_name);
1386     lives_snprintf(mainw->vid_save_dir, PATH_MAX, "%s", n_file_name);
1387     get_dirname(mainw->vid_save_dir);
1388     if (prefs->save_directories) {
1389       set_utf8_pref(PREF_VID_SAVE_DIR, mainw->vid_save_dir);
1390     }
1391     lives_free(ttl);
1392   } else n_file_name = lives_strdup(filename);
1393 
1394   //append default extension (if necessary)
1395   if (!*prefs->encoder.of_def_ext) {
1396     // encoder adds its own extension
1397     get_filename(n_file_name, FALSE);
1398   } else {
1399     if (mainw->fx1_bool && (strlen(n_file_name) <= strlen(prefs->encoder.of_def_ext) ||
1400                             strncmp(n_file_name + strlen(n_file_name) - strlen(prefs->encoder.of_def_ext) - 1, ".", 1) ||
1401                             strcmp(n_file_name + strlen(n_file_name) - strlen(prefs->encoder.of_def_ext),
1402                                    prefs->encoder.of_def_ext))) {
1403       full_file_name = lives_strconcat(n_file_name, ".", prefs->encoder.of_def_ext, NULL);
1404       recheck_name = TRUE;
1405     }
1406   }
1407 
1408   if (!full_file_name) {
1409     full_file_name = lives_strdup(n_file_name);
1410   }
1411 
1412   if (!filename && recheck_name) {
1413     if (!check_file(full_file_name, strcmp(full_file_name, n_file_name))) {
1414       lives_free(full_file_name);
1415       lives_free(n_file_name);
1416       if (rdet) {
1417         lives_widget_destroy(rdet->dialog);
1418         lives_free(rdet->encoder_name);
1419         lives_freep((void **)&rdet);
1420         lives_freep((void **)&resaudw);
1421       }
1422       return;
1423     }
1424     sfile->orig_file_name = FALSE;
1425   }
1426 
1427   if (!*sfile->comment) set_default_comment(sfile, NULL);
1428 
1429   if (!do_comments_dialog(clip, full_file_name)) {
1430     lives_free(full_file_name);
1431     if (rdet) {
1432       lives_widget_destroy(rdet->dialog);
1433       lives_free(rdet->encoder_name);
1434       lives_freep((void **)&rdet);
1435       lives_freep((void **)&resaudw);
1436     }
1437     lives_freep((void **)&mainw->subt_save_file);
1438     return;
1439   }
1440 
1441   if (rdet) {
1442     lives_widget_destroy(rdet->dialog);
1443     lives_freep((void **)&rdet->encoder_name);
1444     lives_freep((void **)&rdet);
1445     lives_freep((void **)&resaudw);
1446   }
1447 
1448   if (sfile->arate * sfile->achans != 0) {
1449     aud_start = calc_time_from_frame(clip, start) * sfile->arps / sfile->arate;
1450     aud_end = calc_time_from_frame(clip, end + 1) * sfile->arps / sfile->arate;
1451   }
1452 
1453   // get extra params for encoder
1454 
1455   if (!sfile->ratio_fps) {
1456     fps_string = lives_strdup_printf("%.3f", sfile->fps);
1457   } else {
1458     fps_string = lives_strdup_printf("%.8f", sfile->fps);
1459   }
1460 
1461   arate = sfile->arate;
1462 
1463   if (!mainw->save_with_sound || prefs->encoder.of_allowed_acodecs == 0) {
1464     arate = 0;
1465   }
1466 
1467   /// get extra parameters for saving
1468   if (prefs->encoder.capabilities & HAS_RFX) {
1469     char buff[65536];
1470 
1471     com = lives_strdup_printf("\"%s\" get_rfx %s %d %d %d", enc_exec_name, prefs->encoder.of_name,
1472                               prefs->encoder.audio_codec, cfile->hsize, cfile->vsize);
1473     if (debug_mode) {
1474       fprintf(stderr, "Running command: %s\n", com);
1475     }
1476     lives_popen(com, TRUE, buff, 65536);
1477     lives_free(com);
1478 
1479     if (!THREADVAR(com_failed)) {
1480       extra_params = plugin_run_param_window(buff, NULL, NULL);
1481     }
1482     if (!extra_params) {
1483       lives_free(fps_string);
1484       if (!mainw->multitrack) {
1485         switch_to_file(mainw->current_file, current_file);
1486       }
1487       lives_freep((void **)&mainw->subt_save_file);
1488       return;
1489     }
1490   }
1491 
1492   if (!save_all && !safe_symlinks) {
1493     // we are saving a selection - make symlinks from a temporary clip
1494 
1495     if ((new_file = mainw->first_free_file) == ALL_USED) {
1496       too_many_files();
1497       lives_freep((void **)&mainw->subt_save_file);
1498       return;
1499     }
1500 
1501     // create new clip
1502     if (!get_new_handle(new_file, (_("selection")))) {
1503       lives_freep((void **)&mainw->subt_save_file);
1504       return;
1505     }
1506 
1507     if (sfile->clip_type == CLIP_TYPE_FILE) {
1508       mainw->cancelled = CANCEL_NONE;
1509       cfile->progress_start = 1;
1510       cfile->progress_end = count_virtual_frames(sfile->frame_index, start, end);
1511       do_threaded_dialog(_("Pulling frames from clip..."), TRUE);
1512       res = virtual_to_images(clip, start, end, TRUE, NULL);
1513       end_threaded_dialog();
1514 
1515       if (mainw->cancelled != CANCEL_NONE || res < 0) {
1516         mainw->cancelled = CANCEL_USER;
1517         lives_freep((void **)&mainw->subt_save_file);
1518         if (res <= 0) d_print_file_error_failed();
1519         return;
1520       }
1521     }
1522 
1523     mainw->effects_paused = FALSE;
1524 
1525     nfile = mainw->files[new_file];
1526     nfile->hsize = sfile->hsize;
1527     nfile->vsize = sfile->vsize;
1528     cfile->progress_start = nfile->start = 1;
1529     cfile->progress_end = nfile->frames = nfile->end = end - start + 1;
1530     nfile->fps = sfile->fps;
1531     nfile->arps = sfile->arps;
1532     nfile->arate = sfile->arate;
1533     nfile->achans = sfile->achans;
1534     nfile->asampsize = sfile->asampsize;
1535     nfile->signed_endian = sfile->signed_endian;
1536     nfile->img_type = sfile->img_type;
1537 
1538     com = lives_strdup_printf("%s link_frames \"%s\" %d %d %.8f %.8f %d %d %d %d %d \"%s\"", prefs->backend, nfile->handle,
1539                               start, end, aud_start, aud_end, nfile->arate, nfile->achans, nfile->asampsize,
1540                               !(nfile->signed_endian & AFORM_UNSIGNED), !(nfile->signed_endian & AFORM_BIG_ENDIAN),
1541                               sfile->handle);
1542 
1543     lives_rm(nfile->info_file);
1544     lives_system(com, FALSE);
1545     lives_free(com);
1546 
1547     // TODO - eliminate this
1548     mainw->current_file = new_file;
1549 
1550     if (THREADVAR(com_failed)) {
1551       char *permitname = lives_build_filename(prefs->workdir, cfile->handle, TEMPFILE_MARKER "." LIVES_FILE_EXT_TMP, NULL);
1552 #ifdef IS_MINGW
1553       // kill any active processes: for other OSes the backend does this
1554       lives_kill_subprocesses(cfile->handle, TRUE);
1555 #endif
1556       lives_touch(permitname);
1557       lives_free(permitname);
1558       lives_system(lives_strdup_printf("%s close \"%s\"", prefs->backend, cfile->handle), TRUE);
1559       lives_freep((void **)&cfile);
1560       if (mainw->first_free_file == ALL_USED || mainw->first_free_file > new_file)
1561         mainw->first_free_file = new_file;
1562       if (!mainw->multitrack) {
1563         switch_to_file(mainw->current_file, current_file);
1564       }
1565       d_print_cancelled();
1566       lives_freep((void **)&mainw->subt_save_file);
1567       return;
1568     }
1569 
1570     cfile->nopreview = TRUE;
1571     if (!(do_progress_dialog(TRUE, TRUE, _("Linking selection")))) {
1572       char *permitname = lives_build_filename(prefs->workdir, cfile->handle, TEMPFILE_MARKER "." LIVES_FILE_EXT_TMP, NULL);
1573 #ifdef IS_MINGW
1574       // kill any active processes: for other OSes the backend does this
1575       lives_kill_subprocesses(cfile->handle, TRUE);
1576 #endif
1577       lives_touch(permitname);
1578       lives_free(permitname);
1579       lives_system((tmp = lives_strdup_printf("%s close \"%s\"", prefs->backend, cfile->handle)), TRUE);
1580       lives_free(tmp);
1581       lives_freep((void **)&cfile);
1582       if (mainw->first_free_file == ALL_USED || mainw->first_free_file > new_file)
1583         mainw->first_free_file = new_file;
1584 
1585       if (!mainw->multitrack) {
1586         switch_to_file(mainw->current_file, current_file);
1587       }
1588       if (mainw->error) d_print_failed();
1589       else d_print_cancelled();
1590       lives_freep((void **)&mainw->subt_save_file);
1591       return;
1592     }
1593 
1594     // cfile->arate, etc., would have been reset by calls to do_progress_dialog() which calls get_total_time() [since cfile->afilesize==0]
1595     // so we need to set these again now that link_frames has provided an actual audio clip
1596 
1597     nfile->arps = sfile->arps;
1598     nfile->arate = sfile->arate;
1599     nfile->achans = sfile->achans;
1600     nfile->asampsize = sfile->asampsize;
1601     nfile->signed_endian = sfile->signed_endian;
1602 
1603     reget_afilesize(new_file);
1604 
1605     aud_start = calc_time_from_frame(new_file, 1) * nfile->arps / nfile->arate;
1606     aud_end = calc_time_from_frame(new_file, nfile->frames + 1) * nfile->arps / nfile->arate;
1607     cfile->nopreview = FALSE;
1608   } else mainw->current_file = clip; // for encoder restns
1609 
1610   if (rdet) rdet->is_encoding = TRUE;
1611 
1612   if (!check_encoder_restrictions(FALSE, FALSE, save_all)) {
1613     if (!save_all && !safe_symlinks) {
1614       char *permitname = lives_build_filename(prefs->workdir, nfile->handle, TEMPFILE_MARKER "." LIVES_FILE_EXT_TMP, NULL);
1615 #ifdef IS_MINGW
1616       lives_kill_subprocesses(nfile->handle, TRUE);
1617 #endif
1618       lives_touch(permitname);
1619       lives_free(permitname);
1620       lives_system((com = lives_strdup_printf("%s close \"%s\"", prefs->backend, nfile->handle)), TRUE);
1621       lives_free(com);
1622       lives_free(nfile);
1623       mainw->files[new_file] = NULL;
1624       if (mainw->first_free_file == ALL_USED || new_file) mainw->first_free_file = new_file;
1625     }
1626     if (!mainw->multitrack) {
1627       switch_to_file(mainw->current_file, current_file);
1628     }
1629     d_print_cancelled();
1630     lives_freep((void **)&mainw->subt_save_file);
1631     return;
1632   }
1633 
1634   if (!save_all && safe_symlinks) {
1635     int xarps, xarate, xachans, xasamps, xasigned_endian;
1636     // we are saving a selection - make symlinks in /tmp
1637 
1638     startframe = -1;
1639 
1640     if (sfile->clip_type == CLIP_TYPE_FILE) {
1641       mainw->cancelled = CANCEL_NONE;
1642       cfile->progress_start = 1;
1643       cfile->progress_end = count_virtual_frames(sfile->frame_index, start, end);
1644       do_threaded_dialog(_("Pulling frames from clip..."), TRUE);
1645       res = virtual_to_images(clip, start, end, TRUE, NULL);
1646       end_threaded_dialog();
1647 
1648       if (mainw->cancelled != CANCEL_NONE || res <= 0) {
1649         if (mainw->cancelled != CANCEL_NONE) mainw->cancelled = CANCEL_USER;
1650         lives_freep((void **)&mainw->subt_save_file);
1651         if (res <= 0) d_print_file_error_failed();
1652         return;
1653       }
1654     }
1655 
1656     com = lives_strdup_printf("%s link_frames \"%s\" %d %d %.8f %.8f %d %d %d %d %d", prefs->backend, sfile->handle,
1657                               start, end, aud_start, aud_end, sfile->arate, sfile->achans, sfile->asampsize,
1658                               !(sfile->signed_endian & AFORM_UNSIGNED), !(sfile->signed_endian & AFORM_BIG_ENDIAN));
1659 
1660     lives_rm(sfile->info_file);
1661     lives_system(com, FALSE);
1662     lives_free(com);
1663 
1664     mainw->current_file = clip;
1665 
1666     xarps = sfile->arps;
1667     xarate = sfile->arate;
1668     xachans = sfile->achans;
1669     xasamps = sfile->asampsize;
1670     xasigned_endian = sfile->signed_endian;
1671 
1672     if (THREADVAR(com_failed)) {
1673       com = lives_strdup_printf("%s clear_symlinks \"%s\"", prefs->backend_sync, cfile->handle);
1674       lives_system(com, TRUE);
1675       lives_free(com);
1676       cfile->nopreview = FALSE;
1677       if (!mainw->multitrack) {
1678         switch_to_file(mainw->current_file, current_file);
1679       }
1680       d_print_cancelled();
1681       lives_freep((void **)&mainw->subt_save_file);
1682       return;
1683     }
1684 
1685     cfile->nopreview = TRUE;
1686     if (!(do_progress_dialog(TRUE, TRUE, _("Linking selection")))) {
1687       com = lives_strdup_printf("%s clear_symlinks \"%s\"", prefs->backend_sync, cfile->handle);
1688       lives_system(com, TRUE);
1689       lives_free(com);
1690       cfile->nopreview = FALSE;
1691       if (!mainw->multitrack) {
1692         switch_to_file(mainw->current_file, current_file);
1693       }
1694       if (mainw->error) d_print_failed();
1695       else d_print_cancelled();
1696       lives_freep((void **)&mainw->subt_save_file);
1697       return;
1698     }
1699 
1700     // cfile->arate, etc., would have been reset by calls to do_progress_dialog() which calls get_total_time() [since cfile->afilesize==0]
1701     // so we need to set these again now that link_frames has provided an actual audio clip
1702 
1703     sfile->arps = xarps;
1704     sfile->arate = xarate;
1705     sfile->achans = xachans;
1706     sfile->asampsize = xasamps;
1707     sfile->signed_endian = xasigned_endian;
1708 
1709     reget_afilesize(clip);
1710 
1711     aud_start = calc_time_from_frame(clip, 1) * sfile->arps / sfile->arate;
1712     aud_end = calc_time_from_frame(clip, end - start + 1) * sfile->arps / sfile->arate;
1713     cfile->nopreview = FALSE;
1714   }
1715 
1716   if (save_all) {
1717     if (sfile->clip_type == CLIP_TYPE_FILE) {
1718       frames_t ret;
1719       char *msg = (_("Pulling frames from clip..."));
1720       if ((ret = realize_all_frames(clip, msg, FALSE)) < sfile->frames) {
1721         lives_free(msg);
1722         lives_freep((void **)&mainw->subt_save_file);
1723         if (ret > 0) d_print_cancelled();
1724         if (!mainw->multitrack) {
1725           switch_to_file(mainw->current_file, current_file);
1726         }
1727         return;
1728       }
1729       lives_free(msg);
1730     }
1731   }
1732 
1733   if (!mainw->save_with_sound || prefs->encoder.of_allowed_acodecs == 0) {
1734     bit = (_(" (with no sound)\n"));
1735   } else {
1736     bit = lives_strdup("\n");
1737   }
1738 
1739   if (!save_all) {
1740     mesg = lives_strdup_printf(_("Saving frames %d to %d%s as \"%s\" : encoder = %s : format = %s..."),
1741                                start, end, bit, full_file_name, prefs->encoder.name, prefs->encoder.of_desc);
1742   } // end selection
1743   else {
1744     mesg = lives_strdup_printf(_("Saving frames 1 to %d%s as \"%s\" : encoder %s : format = %s..."),
1745                                sfile->frames, bit, full_file_name, prefs->encoder.name, prefs->encoder.of_desc);
1746   }
1747   lives_free(bit);
1748 
1749   mainw->no_switch_dprint = TRUE;
1750   d_print(mesg);
1751   mainw->no_switch_dprint = FALSE;
1752   lives_free(mesg);
1753 
1754   if (prefs->show_gui && !debug_mode) {
1755     // open a file for stderr
1756     new_stderr_name = lives_build_filename(prefs->workdir, cfile->handle, LIVES_ENC_DEBUG_FILE_NAME, NULL);
1757     lives_free(redir);
1758 
1759     do {
1760       retval = 0;
1761       new_stderr = lives_open3(new_stderr_name, O_CREAT | O_RDONLY | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR);
1762       if (new_stderr < 0) {
1763         retval = do_write_failed_error_s_with_retry(new_stderr_name, lives_strerror(errno));
1764         if (retval == LIVES_RESPONSE_CANCEL) redir = lives_strdup("1>&2");
1765       } else {
1766 
1767 #ifdef IS_MINGW
1768 
1769 #ifdef GUI_GTK
1770         mainw->iochan = g_io_channel_win32_new_fd(new_stderr);
1771 #endif
1772         redir = lives_strdup_printf("2>&1 >\"%s\"", new_stderr_name);
1773 #else
1774 #ifdef GUI_GTK
1775         mainw->iochan = g_io_channel_unix_new(new_stderr);
1776 #endif
1777         redir = lives_strdup_printf("2>\"%s\"", new_stderr_name);
1778 #endif
1779 
1780 #ifdef GUI_QT
1781         mainw->iochan = new QFile;
1782         mainw->iochan->open(new_stderr, QIODevice::ReadOnly);
1783 #endif
1784 
1785 #ifdef GUI_GTK
1786         g_io_channel_set_encoding(mainw->iochan, NULL, NULL);
1787         g_io_channel_set_buffer_size(mainw->iochan, 0);
1788         g_io_channel_set_flags(mainw->iochan, G_IO_FLAG_NONBLOCK, &gerr);
1789         if (gerr) lives_error_free(gerr);
1790         gerr = NULL;
1791 #endif
1792         mainw->optextview = create_output_textview();
1793       }
1794     } while (retval == LIVES_RESPONSE_RETRY);
1795   } else {
1796     lives_free(redir);
1797     redir = lives_strdup("1>&2");
1798   }
1799 
1800   if (lives_file_test((tmp = lives_filename_from_utf8(full_file_name, -1, NULL, NULL, NULL)), LIVES_FILE_TEST_EXISTS)) {
1801     lives_rm(tmp);
1802   }
1803   lives_free(tmp);
1804 
1805   /// re-read values in case they were resampled
1806 
1807   if (arate != 0) arate = cfile->arate;
1808 
1809   if (!cfile->ratio_fps) {
1810     fps_string = lives_strdup_printf("%.3f", cfile->fps);
1811   } else {
1812     fps_string = lives_strdup_printf("%.8f", cfile->fps);
1813   }
1814 
1815   // if startframe is -ve, we will use the links created for safe_symlinks - in /tmp
1816   // for non-safe symlinks, cfile will be our new links file
1817   // for save_all, cfile will be sfile
1818 
1819   if (prefs->encoder.capabilities & ENCODER_NON_NATIVE) {
1820     com = lives_strdup_printf("%s save \"%s\" \"%s\" \"%s\" \"%s\" %d %d %d %d %d %d %.4f %.4f %s %s", prefs->backend,
1821                               cfile->handle,
1822                               enc_exec_name, fps_string, (tmp = lives_filename_from_utf8(full_file_name, -1, NULL, NULL, NULL)),
1823                               startframe, cfile->frames, arate, cfile->achans, cfile->asampsize,
1824                               asigned | aendian, aud_start, aud_end, (extra_params == NULL) ? "" : extra_params, redir);
1825   } else {
1826     com = lives_strdup_printf("%s save \"%s\" \"native:%s\" \"%s\" \"%s\" %d %d %d %d %d %d %.4f %.4f %s %s", prefs->backend,
1827                               cfile->handle,
1828                               enc_exec_name, fps_string, (tmp = lives_filename_from_utf8(full_file_name, -1, NULL, NULL, NULL)),
1829                               startframe, cfile->frames, arate, cfile->achans, cfile->asampsize,
1830                               asigned | aendian, aud_start, aud_end, (extra_params == NULL) ? "" : extra_params, redir);
1831 
1832   }
1833   lives_free(tmp);
1834   lives_free(fps_string);
1835 
1836   lives_freep((void **)&extra_params);
1837 
1838   mainw->effects_paused = FALSE;
1839   cfile->nokeep = TRUE;
1840 
1841   lives_rm(cfile->info_file);
1842   THREADVAR(write_failed) = FALSE;
1843   save_file_comments(current_file);
1844 
1845   if (debug_mode) {
1846     fprintf(stderr, "Running command: %s\n", com);
1847   }
1848 
1849   lives_system(com, FALSE);
1850   lives_free(com);
1851   mainw->error = FALSE;
1852 
1853   if (THREADVAR(com_failed) || THREADVAR(write_failed)) {
1854     mainw->error = TRUE;
1855   }
1856 
1857   if (!mainw->error) {
1858     //char *pluginstr;
1859 
1860     cfile->progress_start = 1;
1861     cfile->progress_end = cfile->frames;
1862 
1863     not_cancelled = do_progress_dialog(TRUE, TRUE, _("Saving [can take a long time]"));
1864 
1865     if (mainw->iochan) {
1866       /// flush last of stdout/stderr from plugin
1867 
1868       lives_fsync(new_stderr);
1869       pump_io_chan(mainw->iochan);
1870 
1871 #ifdef GUI_GTK
1872       g_io_channel_shutdown(mainw->iochan, FALSE, &gerr);
1873       g_io_channel_unref(mainw->iochan);
1874       if (gerr) lives_error_free(gerr);
1875 #endif
1876 #ifdef GUI_QT
1877       delete mainw->iochan;
1878 #endif
1879       mainw->iochan = NULL;
1880 
1881       close(new_stderr);
1882       lives_rm(new_stderr_name);
1883       lives_free(new_stderr_name);
1884       lives_free(redir);
1885     }
1886 
1887     mainw->effects_paused = FALSE;
1888     cfile->nokeep = FALSE;
1889   } else {
1890     if (mainw->iochan) {
1891       /// flush last of stdout/stderr from plugin
1892 
1893       lives_fsync(new_stderr);
1894       pump_io_chan(mainw->iochan);
1895 
1896 #ifdef GUI_GTK
1897       g_io_channel_shutdown(mainw->iochan, FALSE, &gerr);
1898       g_io_channel_unref(mainw->iochan);
1899       if (gerr) lives_error_free(gerr);
1900 #endif
1901 #ifdef GUI_QT
1902       delete mainw->iochan;
1903 #endif
1904       mainw->iochan = NULL;
1905       close(new_stderr);
1906       lives_rm(new_stderr_name);
1907       lives_free(new_stderr_name);
1908       lives_free(redir);
1909     }
1910   }
1911 
1912   cwd = lives_get_current_dir();
1913 
1914   clipdir = lives_build_path(prefs->workdir, cfile->handle, NULL);
1915   lives_chdir(clipdir, FALSE);
1916   lives_free(clipdir);
1917 
1918   com = lives_strdup_printf("\"%s\" clear", enc_exec_name);
1919 
1920   if (debug_mode) {
1921     fprintf(stderr, "Running command: %s\n", com);
1922   }
1923   lives_system(com, FALSE);
1924   lives_free(com);
1925 
1926   lives_chdir(cwd, FALSE);
1927   lives_free(cwd);
1928 
1929   lives_free(enc_exec_name);
1930 
1931   if (not_cancelled || mainw->error) {
1932     if (mainw->error) {
1933       mainw->no_switch_dprint = TRUE;
1934       d_print_failed();
1935       mainw->no_switch_dprint = FALSE;
1936       lives_free(full_file_name);
1937       if (!save_all && !safe_symlinks) {
1938         char *permitname = lives_build_filename(prefs->workdir, cfile->handle, TEMPFILE_MARKER "." LIVES_FILE_EXT_TMP, NULL);
1939         lives_kill_subprocesses(cfile->handle, TRUE);
1940         lives_touch(permitname);
1941         lives_free(permitname);
1942         lives_system((com = lives_strdup_printf("%s close \"%s\"", prefs->backend, cfile->handle)), TRUE);
1943         lives_free(com);
1944         lives_freep((void **)&cfile);
1945         if (mainw->first_free_file == ALL_USED || mainw->first_free_file > mainw->current_file)
1946           mainw->first_free_file = mainw->current_file;
1947       } else if (!save_all && safe_symlinks) {
1948         com = lives_strdup_printf("%s clear_symlinks \"%s\"", prefs->backend_sync, cfile->handle);
1949         lives_system(com, TRUE);
1950         lives_free(com);
1951       }
1952 
1953       switch_to_file(mainw->current_file, current_file);
1954 
1955       lives_freep((void **)&mainw->subt_save_file);
1956       sensitize();
1957       return;
1958     }
1959 
1960     if (lives_file_test((tmp = lives_filename_from_utf8(full_file_name, -1, NULL, NULL, NULL)), LIVES_FILE_TEST_EXISTS)) {
1961       lives_free(tmp);
1962       stat((tmp = lives_filename_from_utf8(full_file_name, -1, NULL, NULL, NULL)), &filestat);
1963       if (filestat.st_size > 0) output_exists = TRUE;
1964     }
1965     if (!output_exists) {
1966       lives_free(tmp);
1967 
1968       mainw->no_switch_dprint = TRUE;
1969       d_print_failed();
1970       mainw->no_switch_dprint = FALSE;
1971       lives_free(full_file_name);
1972       if (!save_all && !safe_symlinks) {
1973         char *permitname = lives_build_filename(prefs->workdir, cfile->handle, TEMPFILE_MARKER "." LIVES_FILE_EXT_TMP, NULL);
1974         lives_kill_subprocesses(cfile->handle, TRUE);
1975         lives_touch(permitname);
1976         lives_free(permitname);
1977         lives_system((com = lives_strdup_printf("%s close \"%s\"", prefs->backend, cfile->handle)), TRUE);
1978         lives_free(com);
1979         lives_freep((void **)&cfile);
1980         if (mainw->first_free_file == ALL_USED || mainw->first_free_file > mainw->current_file)
1981           mainw->first_free_file = mainw->current_file;
1982       } else if (!save_all && safe_symlinks) {
1983         com = lives_strdup_printf("%s clear_symlinks \"%s\"", prefs->backend_sync, cfile->handle);
1984         lives_system(com, TRUE);
1985         lives_free(com);
1986       }
1987 
1988       if (!mainw->multitrack) {
1989         switch_to_file(mainw->current_file, current_file);
1990       }
1991       retval = do_error_dialog(_("\n\nEncoder error - output file was not created !\n"));
1992 
1993       if (retval == LIVES_RESPONSE_SHOW_DETAILS) {
1994         /// show iochan (encoder) details
1995         on_details_button_clicked();
1996       }
1997 
1998       if (mainw->iochan) {
1999         save_log_file("failed_encoder_log");
2000         mainw->iochan = NULL;
2001         lives_widget_object_unref(mainw->optextview);
2002       }
2003 
2004       lives_freep((void **)&mainw->subt_save_file);
2005       sensitize();
2006       if (mainw->error) d_print_failed();
2007 
2008       return;
2009     }
2010     lives_free(tmp);
2011 
2012     if (save_all) {
2013       if (prefs->enc_letterbox) {
2014         /// replace letterboxed frames with maxspect frames
2015         int iwidth = sfile->ohsize;
2016         int iheight = sfile->ovsize;
2017         boolean bad_header = FALSE;
2018 
2019         com = lives_strdup_printf("%s mv_mgk \"%s\" %d %d \"%s\" 1", prefs->backend, sfile->handle, 1, sfile->frames,
2020                                   get_image_ext_for_type(sfile->img_type));
2021 
2022         lives_rm(sfile->info_file);
2023         lives_system(com, FALSE);
2024 
2025         do_progress_dialog(TRUE, FALSE, _("Clearing letterbox"));
2026 
2027         if (mainw->error) {
2028           //	  cfile->may_be_damaged=TRUE;
2029           d_print_failed();
2030           return;
2031         }
2032 
2033         calc_maxspect(sfile->hsize, sfile->vsize, &iwidth, &iheight);
2034 
2035         sfile->hsize = iwidth;
2036         sfile->vsize = iheight;
2037 
2038         save_clip_value(clip, CLIP_DETAILS_WIDTH, &sfile->hsize);
2039         if (THREADVAR(com_failed) || THREADVAR(write_failed)) bad_header = TRUE;
2040         save_clip_value(clip, CLIP_DETAILS_HEIGHT, &sfile->vsize);
2041         if (THREADVAR(com_failed) || THREADVAR(write_failed)) bad_header = TRUE;
2042         if (bad_header) do_header_write_error(mainw->current_file);
2043       }
2044 
2045       lives_snprintf(sfile->save_file_name, PATH_MAX, "%s", full_file_name);
2046       sfile->changed = FALSE;
2047 
2048       /// save was successful
2049       /// TODO - check for size < 0 !!!
2050       fsize = sget_file_size(full_file_name);
2051       if (fsize < 0) fsize = 0;
2052       cfile->f_size = (size_t)fsize;
2053 
2054       if (sfile->is_untitled) {
2055         sfile->is_untitled = FALSE;
2056       }
2057       if (!sfile->was_renamed) {
2058         lives_menu_item_set_text(sfile->menuentry, full_file_name, FALSE);
2059         lives_snprintf(sfile->name, CLIP_NAME_MAXLEN, "%s", full_file_name);
2060       }
2061       set_main_title(cfile->name, 0);
2062       if (prefs->show_recent) {
2063         add_to_recent(full_file_name, 0., 0, NULL);
2064         global_recent_manager_add(full_file_name);
2065       }
2066     } else {
2067       if (!safe_symlinks) {
2068         char *permitname = lives_build_filename(prefs->workdir, nfile->handle, TEMPFILE_MARKER "." LIVES_FILE_EXT_TMP, NULL);
2069 #ifdef IS_MINGW
2070         lives_kill_subprocesses(nfile->handle, TRUE);
2071 #endif
2072         lives_touch(permitname);
2073         lives_free(permitname);
2074         lives_system((com = lives_strdup_printf("%s close \"%s\"", prefs->backend, nfile->handle)), TRUE);
2075         lives_free(com);
2076         lives_free(nfile);
2077         mainw->files[new_file] = NULL;
2078         if (mainw->first_free_file == ALL_USED || mainw->first_free_file > mainw->current_file)
2079           mainw->first_free_file = new_file;
2080       } else {
2081         com = lives_strdup_printf("%s clear_symlinks \"%s\"", prefs->backend_sync, cfile->handle);
2082         lives_system(com, TRUE);
2083         lives_free(com);
2084       }
2085     }
2086   }
2087 
2088   if (!mainw->multitrack) {
2089     switch_to_file(mainw->current_file, current_file);
2090   }
2091   if (mainw->iochan) {
2092     save_log_file("encoder_log");
2093     lives_widget_object_unref(mainw->optextview);
2094     mainw->iochan = NULL;
2095   }
2096 
2097   if (not_cancelled) {
2098     char *fsize_ds;
2099     mainw->no_switch_dprint = TRUE;
2100     d_print_done();
2101 
2102     /// get size of file and show it
2103 
2104     fsize = sget_file_size(full_file_name);
2105     if (fsize >= 0) {
2106       /// TODO - handle file errors !!!!!
2107 
2108       fsize_ds = lives_format_storage_space_string(fsize);
2109       d_print(_("File size was %s\n"), fsize_ds);
2110       lives_free(fsize_ds);
2111 
2112       if (mainw->subt_save_file) {
2113         save_subs_to_file(sfile, mainw->subt_save_file);
2114         lives_freep((void **)&mainw->subt_save_file);
2115       }
2116     }
2117     mainw->no_switch_dprint = FALSE;
2118 
2119     lives_notify(LIVES_OSC_NOTIFY_SUCCESS,
2120                  (mesg = lives_strdup_printf("encode %d \"%s\"", clip,
2121                          (tmp = lives_filename_from_utf8(full_file_name, -1, NULL, NULL, NULL)))));
2122     lives_free(tmp);
2123     lives_free(mesg);
2124   } else {
2125     lives_rm((tmp = lives_filename_from_utf8(full_file_name, -1, NULL, NULL, NULL)));
2126     lives_free(tmp);
2127   }
2128 
2129   lives_free(full_file_name);
2130 }
2131 
2132 
2133 char *prep_audio_player(char *com2, char *com3, frames_t audio_end, int arate, int asigned, int aendian) {
2134   char *stfile = NULL;
2135   char *stopcom = NULL, *com;
2136   short audio_player = prefs->audio_player;
2137   int loop = 0;
2138 
2139   if (cfile->achans > 0) {
2140     cfile->aseek_pos = (off64_t)(cfile->real_pointer_time * (double)cfile->arate) * cfile->achans * (cfile->asampsize / 8);
2141     if (mainw->playing_sel) {
2142       off64_t apos = (off64_t)((double)(mainw->play_start - 1.) / cfile->fps * (double)cfile->arate) * cfile->achans *
2143                      (cfile->asampsize / 8);
2144       if (apos > cfile->aseek_pos) cfile->aseek_pos = apos;
2145     }
2146     if (cfile->aseek_pos > cfile->afilesize) cfile->aseek_pos = 0.;
2147     if (mainw->current_file == 0 && cfile->arate < 0) cfile->aseek_pos = cfile->afilesize;
2148   }
2149   // start up our audio player (jack or pulse)
2150   if (audio_player == AUD_PLAYER_JACK) {
2151 #ifdef ENABLE_JACK
2152     if (mainw->jackd) jack_aud_pb_ready(mainw->current_file);
2153     return NULL;
2154 #endif
2155   } else if (audio_player == AUD_PLAYER_PULSE) {
2156 #ifdef HAVE_PULSE_AUDIO
2157     if (mainw->pulsed) pulse_aud_pb_ready(mainw->current_file);
2158     return NULL;
2159 #endif
2160   } else if (audio_player != AUD_PLAYER_NONE && cfile->achans > 0) {
2161     // sox or mplayer audio - run as background process
2162     if (com3) {
2163       if (mainw->loop_cont) {
2164         // tell audio to loop forever
2165         loop = -1;
2166       }
2167 
2168       stfile = lives_build_filename(prefs->workdir, cfile->handle, ".stoploop", NULL);
2169       lives_rm(stfile);
2170 
2171       if (cfile->achans > 0 || (!cfile->is_loaded && !mainw->is_generating)) {
2172         if (loop) {
2173           lives_free(com3);
2174           com3 = lives_strdup_printf("%s \"%s\" 2>\"%s\" 1>&2", capable->touch_cmd, stfile, prefs->cmd_log);
2175         }
2176 
2177         if (com2) {
2178           if (cfile->achans > 0) {
2179             com2 = lives_strdup_printf("%s stop_audio %s", prefs->backend_sync, cfile->handle);
2180           }
2181           stopcom = lives_strconcat(com3, com2, NULL);
2182         }
2183       }
2184     }
2185 
2186     lives_freep((void **)&stfile);
2187 
2188     stfile = lives_build_filename(prefs->workdir, cfile->handle, LIVES_STATUS_FILE_NAME".play", NULL);
2189 
2190     lives_snprintf(cfile->info_file, PATH_MAX, "%s", stfile);
2191     lives_free(stfile);
2192     if (cfile->clip_type == CLIP_TYPE_DISK) lives_rm(cfile->info_file);
2193 
2194     // PLAY
2195 
2196     if (cfile->clip_type == CLIP_TYPE_DISK && cfile->opening) {
2197       com = lives_strdup_printf("%s play_opening_preview \"%s\" %.3f %d %d %d %d %d %d %d %d", prefs->backend,
2198                                 cfile->handle, cfile->fps, mainw->audio_start, audio_end, 0,
2199                                 arate, cfile->achans, cfile->asampsize, asigned, aendian);
2200     } else {
2201       // this is only used now for sox or mplayer audio player
2202       com = lives_strdup_printf("%s play %s %.3f %d %d %d %d %d %d %d %d", prefs->backend, cfile->handle,
2203                                 cfile->fps, mainw->audio_start, audio_end, loop,
2204                                 arate, cfile->achans, cfile->asampsize, asigned, aendian);
2205     }
2206     if (!mainw->multitrack && com) lives_system(com, FALSE);
2207   }
2208   return stopcom;
2209 }
2210 
2211 
2212 /// play the current clip from 'mainw->play_start' to 'mainw->play_end'
2213 void play_file(void) {
2214   LiVESWidgetClosure *freeze_closure, *bg_freeze_closure;
2215   LiVESList *cliplist;
2216   weed_plant_t *pb_start_event = NULL;
2217 
2218 #ifdef GDK_WINDOWING_X11
2219   uint64_t awinid = -1;
2220 #endif
2221 
2222   char *com, *com2 = lives_strdup(" "), *com3 = lives_strdup(" ");
2223   char *stopcom = NULL;
2224   char *stfile;
2225 #ifdef GDK_WINDOWING_X11
2226   char *tmp;
2227 #endif
2228 
2229   double fps_med = 0.;
2230   double pointer_time = cfile->pointer_time;
2231   double real_pointer_time = cfile->real_pointer_time;
2232 
2233   short audio_player = prefs->audio_player;
2234 
2235   boolean mute;
2236   boolean needsadone = FALSE;
2237 
2238 #ifdef RT_AUDIO
2239   boolean exact_preview = FALSE;
2240 #endif
2241   boolean has_audio_buffers = FALSE;
2242 
2243   int arate;
2244 
2245   int asigned = !(cfile->signed_endian & AFORM_UNSIGNED);
2246   int aendian = !(cfile->signed_endian & AFORM_BIG_ENDIAN);
2247   int current_file = mainw->current_file;
2248   int audio_end = 0;
2249 
2250   /// from now on we can only switch at the designated SWITCH POINT
2251   mainw->noswitch = TRUE;
2252   mainw->cancelled = CANCEL_NONE;
2253 
2254   asigned = !(cfile->signed_endian & AFORM_UNSIGNED);
2255   aendian = !(cfile->signed_endian & AFORM_BIG_ENDIAN);
2256   current_file = mainw->current_file;
2257   if (mainw->pre_play_file == -1) mainw->pre_play_file = current_file;
2258 
2259   if (!is_realtime_aplayer(audio_player)) mainw->aud_file_to_kill = mainw->current_file;
2260   else mainw->aud_file_to_kill = -1;
2261 
2262 #ifdef ENABLE_JACK_TRANSPORT
2263   if (!mainw->preview && !mainw->foreign) {
2264     if (!mainw->multitrack)
2265       jack_pb_start(cfile->achans > 0 ? cfile->real_pointer_time : cfile->pointer_time);
2266     else
2267       jack_pb_start(mainw->multitrack->pb_start_time);
2268   }
2269 #endif
2270 
2271   mainw->ext_playback = FALSE;
2272 
2273   mainw->rec_aclip = -1;
2274 
2275   init_conversions(LIVES_INTENTION_PLAY);
2276 
2277   if (mainw->pre_src_file == -2) mainw->pre_src_file = mainw->current_file;
2278   mainw->pre_src_audio_file = mainw->current_file;
2279 
2280   /// enable the freeze button
2281   lives_accel_group_connect(LIVES_ACCEL_GROUP(mainw->accel_group), LIVES_KEY_BackSpace,
2282                             (LiVESXModifierType)LIVES_CONTROL_MASK,
2283                             (LiVESAccelFlags)0, (freeze_closure = lives_cclosure_new(LIVES_GUI_CALLBACK(freeze_callback),
2284                                 LIVES_INT_TO_POINTER(SCREEN_AREA_FOREGROUND), NULL)));
2285   lives_accel_group_connect(LIVES_ACCEL_GROUP(mainw->accel_group), LIVES_KEY_BackSpace,
2286                             (LiVESXModifierType)(LIVES_CONTROL_MASK | LIVES_ALT_MASK),
2287                             (LiVESAccelFlags)0, (bg_freeze_closure = lives_cclosure_new(LIVES_GUI_CALLBACK(freeze_callback),
2288                                 LIVES_INT_TO_POINTER(SCREEN_AREA_BACKGROUND), NULL)));
2289 
2290   /// disable ctrl-q since it can be activated by user error
2291   lives_accel_path_disconnect(mainw->accel_group, LIVES_ACCEL_PATH_QUIT);
2292 
2293   if (mainw->multitrack) {
2294     mainw->event_list = mainw->multitrack->event_list;
2295     pb_start_event = mainw->multitrack->pb_start_event;
2296 #ifdef RT_AUDIO
2297     exact_preview = mainw->multitrack->exact_preview;
2298 #endif
2299   }
2300 
2301   // reinit all active effects
2302   if (!mainw->preview && !mainw->is_rendering && !mainw->foreign) weed_reinit_all();
2303 
2304   if (mainw->record) {
2305     if (mainw->preview) {
2306       mainw->record = FALSE;
2307       d_print(_("recording aborted by preview.\n"));
2308     } else if (mainw->current_file == 0) {
2309       mainw->record = FALSE;
2310       d_print(_("recording aborted by clipboard playback.\n"));
2311     } else {
2312       d_print(_("Recording performance..."));
2313       needsadone = TRUE;
2314       mainw->clip_switched = FALSE;
2315       // TODO
2316       if (mainw->current_file > 0 && (cfile->undo_action == UNDO_RESAMPLE || cfile->undo_action == UNDO_RENDER)) {
2317         lives_widget_set_sensitive(mainw->undo, FALSE);
2318         lives_widget_set_sensitive(mainw->redo, FALSE);
2319         cfile->undoable = cfile->redoable = FALSE;
2320       }
2321     }
2322   }
2323   /// set performance at right place
2324   else if (mainw->event_list) cfile->next_event = get_first_event(mainw->event_list);
2325 
2326   if (!mainw->multitrack && CURRENT_CLIP_HAS_VIDEO) {
2327     lives_widget_set_frozen(mainw->spinbutton_start, TRUE);
2328     lives_widget_set_frozen(mainw->spinbutton_end, TRUE);
2329     //lives_signal_handler_block(mainw->spinbutton_start, mainw->spin_start_func);
2330     //lives_signal_handler_block(mainw->spinbutton_end, mainw->spin_end_func);
2331   }
2332 
2333 #ifdef ENABLE_JACK_TRANSPORT
2334   if (mainw->jack_can_stop && !mainw->event_list && !mainw->preview
2335       && (prefs->jack_opts & (JACK_OPTS_TIMEBASE_START | JACK_OPTS_TIMEBASE_CLIENT))) {
2336     // calculate the start position from jack transport
2337     double sttime = (double)jack_transport_get_current_ticks() / TICKS_PER_SECOND_DBL;
2338     cfile->pointer_time = cfile->real_pointer_time = sttime;
2339     if (cfile->real_pointer_time > CLIP_TOTAL_TIME(mainw->current_file))
2340       cfile->real_pointer_time = CLIP_TOTAL_TIME(mainw->current_file);
2341     if (cfile->pointer_time > cfile->video_time) cfile->pointer_time = 0.;
2342     mainw->play_start = calc_frame_from_time(mainw->current_file, cfile->pointer_time);
2343   }
2344 #endif
2345 
2346   /// these values are only relevant for non-realtime audio players (e.g. sox)
2347   mainw->audio_start = mainw->audio_end = 0;
2348 
2349   if (cfile->achans > 0) {
2350     if (mainw->event_list &&
2351         !(mainw->preview && mainw->is_rendering) &&
2352         !(mainw->multitrack && mainw->preview && mainw->multitrack->is_rendering)) {
2353       /// play performance data
2354       if (event_list_get_end_secs(mainw->event_list) > cfile->frames / cfile->fps && !mainw->playing_sel) {
2355         mainw->audio_end = (event_list_get_end_secs(mainw->event_list) * cfile->fps + 1.) * cfile->arate / cfile->arps;
2356       }
2357     }
2358 
2359     if (mainw->audio_end == 0) {
2360       mainw->audio_start = calc_time_from_frame(mainw->current_file,
2361                            mainw->play_start) * cfile->fps + 1. * cfile->arate / cfile->arps;
2362       mainw->audio_end = calc_time_from_frame(mainw->current_file, mainw->play_end) * cfile->fps
2363                          + 1. * cfile->arate / cfile->arps;
2364       if (!mainw->playing_sel) {
2365         mainw->audio_end = 0;
2366       }
2367     }
2368   }
2369 
2370   if (!cfile->opening_audio && !mainw->loop) {
2371     /** if we are opening audio or looping we just play to the end of audio,
2372       otherwise...*/
2373     audio_end = mainw->audio_end;
2374   }
2375 
2376   if (!mainw->multitrack) {
2377     if (!mainw->preview) {
2378       lives_frame_set_label(LIVES_FRAME(mainw->playframe), _("Play"));
2379     } else {
2380       lives_frame_set_label(LIVES_FRAME(mainw->playframe), _("Preview"));
2381     }
2382 
2383     if (palette->style & STYLE_1) {
2384       lives_widget_set_fg_color(lives_frame_get_label_widget(LIVES_FRAME(mainw->playframe)),
2385                                 LIVES_WIDGET_STATE_NORMAL, &palette->normal_fore);
2386     }
2387 
2388     if (mainw->foreign) {
2389       lives_widget_show_all(mainw->top_vbox);
2390       lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
2391     }
2392 
2393     /// blank the background if asked to
2394     if ((mainw->faded || (prefs->show_playwin && !prefs->show_gui)
2395          || (mainw->fs && (!mainw->sep_win))) && (cfile->frames > 0 ||
2396              mainw->foreign)) {
2397       fade_background();
2398     }
2399 
2400     if ((!mainw->sep_win || (!mainw->faded && (prefs->sepwin_type != SEPWIN_TYPE_STICKY)))
2401         && (cfile->frames > 0 ||
2402             mainw->foreign)) {
2403       /// show the frame in the main window
2404       lives_widget_set_opacity(mainw->playframe, 1.);
2405       lives_widget_show_all(mainw->playframe);
2406     }
2407 
2408     /// plug the plug into the playframe socket if we need to
2409     add_to_playframe();
2410   }
2411 
2412   arate = cfile->arate;
2413 
2414   mute = mainw->mute;
2415 
2416   if (!is_realtime_aplayer(audio_player)) {
2417     if (cfile->achans == 0 || mainw->is_rendering) mainw->mute = TRUE;
2418     if (mainw->mute && !cfile->opening_only_audio) arate = arate ? -arate : -1;
2419   }
2420 
2421   cfile->frameno = mainw->play_start;
2422   cfile->pb_fps = cfile->fps;
2423   if (mainw->reverse_pb) {
2424     cfile->pb_fps = -cfile->pb_fps;
2425     cfile->frameno = mainw->play_end;
2426   }
2427   cfile->last_frameno = cfile->frameno;
2428   mainw->reverse_pb = FALSE;
2429 
2430   mainw->swapped_clip = -1;
2431   mainw->blend_palette = WEED_PALETTE_END;
2432 
2433   cfile->play_paused = FALSE;
2434   mainw->period = TICKS_PER_SECOND_DBL / cfile->pb_fps;
2435 
2436   if (audio_player == AUD_PLAYER_JACK
2437       || (mainw->event_list && (!mainw->is_rendering || !mainw->preview || mainw->preview_rendering)))
2438     audio_cache_init();
2439 
2440   if (mainw->blend_file != -1 && !IS_VALID_CLIP(mainw->blend_file)) mainw->blend_file = -1;
2441 
2442   lives_widget_set_sensitive(mainw->m_stopbutton, TRUE);
2443   mainw->playing_file = mainw->current_file;
2444 
2445   if (!mainw->preview || !cfile->opening) {
2446     enable_record();
2447     desensitize();
2448     lives_widget_set_sensitive(mainw->spinbutton_pb_fps, TRUE);
2449   }
2450 
2451   if (mainw->record) {
2452     if (mainw->event_list) event_list_free(mainw->event_list);
2453     mainw->record_starting = TRUE;
2454   }
2455 
2456   if (prefs->show_msg_area && mainw->double_size && !mainw->multitrack) {
2457     lives_widget_hide(mainw->message_box);
2458   }
2459 
2460   lives_widget_set_sensitive(mainw->stop, TRUE);
2461 
2462   if (!mainw->multitrack) lives_widget_set_sensitive(mainw->m_playbutton, FALSE);
2463   else if (!cfile->opening) {
2464     if (!mainw->is_processing) mt_swap_play_pause(mainw->multitrack, TRUE);
2465     else {
2466       lives_widget_set_sensitive(mainw->multitrack->playall, FALSE);
2467       lives_widget_set_sensitive(mainw->m_playbutton, FALSE);
2468     }
2469   }
2470 
2471   lives_table_set_column_homogeneous(LIVES_TABLE(mainw->pf_grid), !mainw->double_size);
2472 
2473   lives_widget_set_sensitive(mainw->m_playselbutton, FALSE);
2474   lives_widget_set_sensitive(mainw->m_rewindbutton, FALSE);
2475   lives_widget_set_sensitive(mainw->m_mutebutton, is_realtime_aplayer(audio_player) || mainw->multitrack);
2476 
2477   lives_widget_set_sensitive(mainw->m_loopbutton, (!cfile->achans || mainw->mute || mainw->multitrack ||
2478                              mainw->loop_cont || is_realtime_aplayer(audio_player))
2479                              && mainw->current_file > 0);
2480   lives_widget_set_sensitive(mainw->loop_continue, (!cfile->achans || mainw->mute || mainw->loop_cont ||
2481                              is_realtime_aplayer(audio_player))
2482                              && mainw->current_file > 0);
2483 
2484   if (cfile->frames == 0 && !mainw->multitrack) {
2485     if (mainw->preview_box && lives_widget_get_parent(mainw->preview_box)) {
2486 
2487       lives_container_remove(LIVES_CONTAINER(mainw->play_window), mainw->preview_box);
2488 
2489       mainw->pw_scroll_func = lives_signal_connect(LIVES_GUI_OBJECT(mainw->play_window), LIVES_WIDGET_SCROLL_EVENT,
2490                               LIVES_GUI_CALLBACK(on_mouse_scroll), NULL);
2491     }
2492   } else {
2493     if (mainw->sep_win) {
2494       /// create a separate window for the internal player if requested
2495       if (prefs->sepwin_type == SEPWIN_TYPE_NON_STICKY) {
2496         make_play_window();
2497       } else {
2498         if (!mainw->multitrack) {
2499           if (mainw->preview_controls) {
2500             lives_widget_hide(mainw->preview_controls);
2501             /* mainw->pw_scroll_func = lives_signal_connect(LIVES_GUI_OBJECT(mainw->play_window), LIVES_WIDGET_SCROLL_EVENT, */
2502             /*                         LIVES_GUI_CALLBACK(on_mouse_scroll), */
2503             /*                         NULL); */
2504           }
2505         }
2506 
2507         if (!mainw->multitrack || mainw->fs) {
2508           resize_play_window();
2509         }
2510 
2511         /// needed
2512         if (!mainw->multitrack) {
2513           lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
2514         } else {
2515           /// this doesn't get called if we don't call resize_play_window()
2516           if (mainw->play_window) {
2517             if (prefs->show_playwin) {
2518               lives_window_present(LIVES_WINDOW(mainw->play_window));
2519               lives_xwindow_raise(lives_widget_get_xwindow(mainw->play_window));
2520 	      // *INDENT-OFF*
2521 	    }}}}}
2522     // *INDENT-ON*
2523 
2524     if (mainw->play_window) {
2525       hide_cursor(lives_widget_get_xwindow(mainw->play_window));
2526       lives_widget_set_app_paintable(mainw->play_window, TRUE);
2527       play_window_set_title();
2528     }
2529 
2530     if (!mainw->foreign && !mainw->sep_win) {
2531       hide_cursor(lives_widget_get_xwindow(mainw->playarea));
2532     }
2533 
2534     if (!mainw->sep_win && !mainw->foreign) {
2535       if (mainw->double_size) resize(2.);
2536       else resize(1);
2537     }
2538 
2539     /* if (mainw->vpp && mainw->vpp->fheight > -1 && mainw->vpp->fwidth > -1) { */
2540     /*   // fixed o/p size for stream */
2541     /*   if (mainw->vpp->fwidth * mainw->vpp->fheight == 0) { */
2542     /*     mainw->vpp->fwidth = DEF_VPP_HSIZE; */
2543     /*     mainw->vpp->fheight = DEF_VPP_VSIZE; */
2544     /*   } */
2545     /*   if (!(mainw->vpp->capabilities & VPP_CAN_RESIZE)) { */
2546     /*     mainw->pwidth = mainw->vpp->fwidth; */
2547     /*     mainw->pheight = mainw->vpp->fheight; */
2548     /*   } */
2549     /* } */
2550 
2551     if (mainw->fs && !mainw->sep_win && cfile->frames > 0) {
2552       fullscreen_internal();
2553     }
2554   }
2555 
2556   // moved down because xdg-screensaver requires a mapped windowID
2557   if (prefs->stop_screensaver) {
2558     lives_freep((void **)&com2);
2559 #ifdef GDK_WINDOWING_X11
2560     if (!prefs->show_gui && prefs->show_playwin && mainw->play_window) {
2561       awinid = lives_widget_get_xwinid(mainw->play_window, NULL);
2562     } else if (prefs->show_gui) {
2563       awinid = lives_widget_get_xwinid(LIVES_MAIN_WINDOW_WIDGET, NULL);
2564     }
2565 
2566     com2 = lives_strdup("xset s off 2>/dev/null; xset -dpms 2>/dev/null ;");
2567 
2568     if (capable->has_gconftool_2) {
2569       char *xnew = lives_strdup(" gconftool-2 --set --type bool /apps/gnome-screensaver/idle_activation_enabled "
2570                                 "false 2>/dev/null ;");
2571       tmp = lives_concat(com2, xnew);
2572       com2 = tmp;
2573     }
2574     if (capable->has_xdg_screensaver && awinid != -1) {
2575       char *xnew = lives_strdup_printf(" xdg-screensaver suspend %"PRIu64" 2>/dev/null ;", awinid);
2576       tmp = lives_concat(com2, xnew);
2577       com2 = tmp;
2578     }
2579 #else
2580     if (capable->has_gconftool_2) {
2581       com2 = lives_strdup("gconftool-2 --set --type bool /apps/gnome-screensaver/idle_activation_enabled false 2>/dev/null ;");
2582     } else com2 = lives_strdup("");
2583 #endif
2584     if (!com2) com2 = lives_strdup("");
2585   }
2586 
2587   if (!mainw->foreign && prefs->midisynch && !mainw->preview) {
2588     lives_free(com3);
2589     com3 = lives_strdup(EXEC_MIDISTART);
2590   }
2591   com = lives_strconcat(com2, com3, NULL);
2592   if (*com) {
2593     // allow this to fail - not all sub-commands may be present
2594     lives_system(com, TRUE);
2595   }
2596   lives_freep((void **)&com);
2597   lives_freep((void **)&com2);
2598   lives_free(com3);
2599   com3 = lives_strdup(" ");
2600 
2601   if (!mainw->multitrack) {
2602     lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_pb_fps), cfile->pb_fps);
2603 
2604     mainw->last_blend_file = -1;
2605 
2606     // show the framebar
2607     if (!mainw->multitrack && !mainw->faded
2608         && (!prefs->hide_framebar &&
2609             (!mainw->fs || (widget_opts.monitor + 1 != prefs->play_monitor && prefs->play_monitor != 0
2610                             && capable->nmonitors > 1 &&
2611                             mainw->sep_win) ||
2612              (mainw->vpp && mainw->sep_win && !(mainw->vpp->capabilities & VPP_LOCAL_DISPLAY))) &&
2613             ((!mainw->preview && (cfile->frames > 0 || mainw->foreign)) || cfile->opening))) {
2614       lives_widget_show(mainw->framebar);
2615     }
2616   }
2617 
2618   cfile->play_paused = FALSE;
2619   mainw->actual_frame = 0;
2620 
2621   mainw->currticks = 0;
2622   mainw->effort = -EFFORT_RANGE_MAX;
2623 
2624   find_when_to_stop();
2625 
2626   // reinit all active effects
2627   if (!mainw->preview && !mainw->is_rendering && !mainw->foreign) weed_reinit_all();
2628 
2629   if (!mainw->foreign && (!(prefs->audio_src == AUDIO_SRC_EXT &&
2630                             (audio_player == AUD_PLAYER_JACK ||
2631                              audio_player == AUD_PLAYER_PULSE || audio_player == AUD_PLAYER_NONE)))) {
2632     stopcom = prep_audio_player(com2, com3, audio_end, arate, asigned, aendian);
2633   }
2634 
2635   lives_free(com3);
2636 
2637   // if recording, refrain from writing audio until we are ready
2638   if (mainw->record) mainw->record_paused = TRUE;
2639 
2640   // if recording, set up recorder (jack or pulse)
2641   if (!mainw->preview && (prefs->audio_src == AUDIO_SRC_EXT || (mainw->record && mainw->agen_key != 0))
2642       && (audio_player == AUD_PLAYER_JACK || audio_player == AUD_PLAYER_PULSE)) {
2643     mainw->rec_samples = -1; // record unlimited
2644     if (mainw->record) {
2645       // create temp clip
2646       open_ascrap_file();
2647       if (mainw->ascrap_file != -1) {
2648         mainw->rec_aclip = mainw->ascrap_file;
2649         mainw->rec_avel = 1.;
2650         mainw->rec_aseek = 0;
2651       }
2652     }
2653     if (audio_player == AUD_PLAYER_JACK) {
2654 #ifdef ENABLE_JACK
2655       if ((prefs->audio_src == AUDIO_SRC_EXT || mainw->agen_key != 0 || mainw->agen_needs_reinit) && mainw->jackd) {
2656         if (mainw->agen_key != 0 || mainw->agen_needs_reinit) {
2657           mainw->jackd->playing_file = mainw->current_file;
2658           if (mainw->ascrap_file != -1 || !prefs->perm_audio_reader)
2659             jack_rec_audio_to_clip(mainw->ascrap_file, -1, RECA_GENERATED);
2660         } else {
2661           if (mainw->ascrap_file != -1 || !prefs->perm_audio_reader)
2662             jack_rec_audio_to_clip(mainw->ascrap_file, -1, RECA_EXTERNAL);
2663         }
2664         //mainw->jackd->in_use = TRUE;
2665       }
2666       if (prefs->audio_src == AUDIO_SRC_EXT && mainw->jackd_read) {
2667         mainw->jackd_read->num_input_channels = mainw->jackd_read->num_output_channels = 2;
2668         mainw->jackd_read->sample_in_rate = mainw->jackd_read->sample_out_rate;
2669         mainw->jackd_read->is_paused = TRUE;
2670         mainw->jackd_read->in_use = TRUE;
2671       }
2672 #endif
2673     }
2674     if (audio_player == AUD_PLAYER_PULSE) {
2675 #ifdef HAVE_PULSE_AUDIO
2676       if ((prefs->audio_src == AUDIO_SRC_EXT || mainw->agen_key != 0  || mainw->agen_needs_reinit) && mainw->pulsed) {
2677         if (mainw->agen_key != 0 || mainw->agen_needs_reinit) {
2678           mainw->pulsed->playing_file = mainw->current_file;
2679           if (mainw->ascrap_file != -1 || !prefs->perm_audio_reader)
2680             pulse_rec_audio_to_clip(mainw->ascrap_file, -1, RECA_GENERATED);
2681         } else {
2682           if (mainw->ascrap_file != -1 || !prefs->perm_audio_reader)
2683             pulse_rec_audio_to_clip(mainw->ascrap_file, -1, RECA_EXTERNAL);
2684         }
2685         //mainw->pulsed->in_use = TRUE;
2686       }
2687       if (prefs->audio_src == AUDIO_SRC_EXT && mainw->pulsed_read) {
2688         mainw->pulsed_read->in_achans = mainw->pulsed_read->out_achans = PA_ACHANS;
2689         mainw->pulsed_read->in_asamps = mainw->pulsed_read->out_asamps = PA_SAMPSIZE;
2690         mainw->pulsed_read->in_arate = mainw->pulsed_read->out_arate;
2691         mainw->pulsed_read->is_paused = TRUE;
2692         mainw->pulsed_read->in_use = TRUE;
2693       }
2694 #endif
2695     }
2696   }
2697 
2698   // set in case audio lock gets actioned
2699   future_prefs->audio_opts = prefs->audio_opts;
2700 
2701   if (mainw->foreign || weed_playback_gen_start()) {
2702     if (mainw->osc_auto)
2703       lives_notify(LIVES_OSC_NOTIFY_SUCCESS, "");
2704     lives_notify(LIVES_OSC_NOTIFY_PLAYBACK_STARTED, "");
2705 
2706 #ifdef ENABLE_JACK
2707     if (mainw->event_list && !mainw->record && audio_player == AUD_PLAYER_JACK && mainw->jackd &&
2708         !(mainw->preview && mainw->is_processing &&
2709           !(mainw->multitrack && mainw->preview && mainw->multitrack->is_rendering))) {
2710       // if playing an event list, we switch to audio memory buffer mode
2711       if (mainw->multitrack) init_jack_audio_buffers(cfile->achans, cfile->arate, exact_preview);
2712       else init_jack_audio_buffers(DEFAULT_AUDIO_CHANS, DEFAULT_AUDIO_RATE, FALSE);
2713       has_audio_buffers = TRUE;
2714     }
2715 #endif
2716 #ifdef HAVE_PULSE_AUDIO
2717     if (mainw->event_list && !mainw->record && audio_player == AUD_PLAYER_PULSE && mainw->pulsed &&
2718         !(mainw->preview && mainw->is_processing &&
2719           !(mainw->multitrack && mainw->preview && mainw->multitrack->is_rendering))) {
2720       // if playing an event list, we switch to audio memory buffer mode
2721       if (mainw->multitrack) init_pulse_audio_buffers(cfile->achans, cfile->arate, exact_preview);
2722       else init_pulse_audio_buffers(DEFAULT_AUDIO_CHANS, DEFAULT_AUDIO_RATE, FALSE);
2723       has_audio_buffers = TRUE;
2724     }
2725 #endif
2726 
2727     mainw->abufs_to_fill = 0;
2728     //lives_widget_context_update();
2729     //play until stopped or a stream finishes
2730     do {
2731       mainw->cancelled = CANCEL_NONE;
2732       mainw->play_sequence++;
2733       mainw->fps_measure = 0;
2734 
2735       if (mainw->event_list && !mainw->record) {
2736         if (!pb_start_event) pb_start_event = get_first_event(mainw->event_list);
2737 
2738         if (!(mainw->preview && mainw->multitrack && mainw->multitrack->is_rendering))
2739           init_track_decoders();
2740 
2741         if (has_audio_buffers) {
2742 #ifdef ENABLE_JACK
2743           if (audio_player == AUD_PLAYER_JACK) {
2744             int i;
2745             mainw->write_abuf = 0;
2746 
2747             // fill our audio buffers now
2748             // this will also get our effects state
2749 
2750             // reset because audio sync may have set it
2751             if (mainw->multitrack) mainw->jackd->abufs[0]->arate = cfile->arate;
2752             else mainw->jackd->abufs[0]->arate = mainw->jackd->sample_out_rate;
2753             fill_abuffer_from(mainw->jackd->abufs[0], mainw->event_list, pb_start_event, exact_preview);
2754             for (i = 1; i < prefs->num_rtaudiobufs; i++) {
2755               // reset because audio sync may have set it
2756               if (mainw->multitrack) mainw->jackd->abufs[i]->arate = cfile->arate;
2757               else mainw->jackd->abufs[i]->arate = mainw->jackd->sample_out_rate;
2758               fill_abuffer_from(mainw->jackd->abufs[i], mainw->event_list, NULL, FALSE);
2759             }
2760 
2761             pthread_mutex_lock(&mainw->abuf_mutex);
2762             mainw->jackd->read_abuf = 0;
2763             mainw->abufs_to_fill = 0;
2764             pthread_mutex_unlock(&mainw->abuf_mutex);
2765             if (mainw->event_list)
2766               mainw->jackd->in_use = TRUE;
2767           }
2768 #endif
2769 #ifdef HAVE_PULSE_AUDIO
2770           if (audio_player == AUD_PLAYER_PULSE) {
2771             int i;
2772             mainw->write_abuf = 0;
2773 
2774             /// fill our audio buffers now
2775             /// this will also get our effects state
2776 
2777             /// this is the IN rate, everything is resampled to this rate and then to output rate
2778             if (mainw->multitrack) mainw->pulsed->abufs[0]->arate = cfile->arate;
2779             else mainw->pulsed->abufs[0]->arate = mainw->pulsed->out_arate;
2780 
2781             /// need to set asamps, in case padding with silence is needed
2782             mainw->pulsed->abufs[0]->out_asamps = mainw->pulsed->out_asamps;
2783 
2784             fill_abuffer_from(mainw->pulsed->abufs[0], mainw->event_list, pb_start_event, exact_preview);
2785             for (i = 1; i < prefs->num_rtaudiobufs; i++) {
2786               if (mainw->multitrack) mainw->pulsed->abufs[i]->arate = cfile->arate;
2787               else mainw->pulsed->abufs[i]->arate = mainw->pulsed->out_arate;
2788               mainw->pulsed->abufs[i]->out_asamps = mainw->pulsed->out_asamps;
2789               fill_abuffer_from(mainw->pulsed->abufs[i], mainw->event_list, NULL, FALSE);
2790             }
2791 
2792             pthread_mutex_lock(&mainw->abuf_mutex);
2793             mainw->pulsed->read_abuf = 0;
2794             mainw->abufs_to_fill = 0;
2795             pthread_mutex_unlock(&mainw->abuf_mutex);
2796             if (mainw->event_list) {
2797               mainw->pulsed->in_use = TRUE;
2798             }
2799           }
2800 #endif
2801         }
2802       }
2803 
2804       if (!mainw->foreign && !mainw->multitrack)
2805         mainw->video_seek_ready = mainw->audio_seek_ready = FALSE;
2806       else
2807         mainw->video_seek_ready = mainw->audio_seek_ready = TRUE;
2808 
2809       if (!mainw->multitrack || !mainw->multitrack->pb_start_event) {
2810         do_progress_dialog(FALSE, FALSE, NULL);
2811 
2812         // reset audio buffers
2813 #ifdef ENABLE_JACK
2814         if (audio_player == AUD_PLAYER_JACK && mainw->jackd) {
2815           // must do this before deinit fx
2816           pthread_mutex_lock(&mainw->abuf_mutex);
2817           mainw->jackd->read_abuf = -1;
2818           mainw->jackd->in_use = FALSE;
2819           pthread_mutex_unlock(&mainw->abuf_mutex);
2820         }
2821 #endif
2822 #ifdef HAVE_PULSE_AUDIO
2823         if (audio_player == AUD_PLAYER_PULSE && mainw->pulsed) {
2824           // must do this before deinit fx
2825           pthread_mutex_lock(&mainw->abuf_mutex);
2826           mainw->pulsed->read_abuf = -1;
2827           mainw->pulsed->in_use = FALSE;
2828           pthread_mutex_unlock(&mainw->abuf_mutex);
2829         }
2830 #endif
2831       } else {
2832         // play from middle of mt timeline
2833         cfile->next_event = mainw->multitrack->pb_start_event;
2834 
2835         if (!has_audio_buffers) {
2836           // no audio buffering
2837           // get just effects state
2838           get_audio_and_effects_state_at(mainw->multitrack->event_list, mainw->multitrack->pb_start_event, 0,
2839                                          LIVES_PREVIEW_TYPE_VIDEO_ONLY, mainw->multitrack->exact_preview);
2840         }
2841 
2842         do_progress_dialog(FALSE, FALSE, NULL);
2843 
2844         // reset audio read buffers
2845 #ifdef ENABLE_JACK
2846         if (audio_player == AUD_PLAYER_JACK && mainw->jackd) {
2847           // must do this before deinit fx
2848           pthread_mutex_lock(&mainw->abuf_mutex);
2849           mainw->jackd->read_abuf = -1;
2850           mainw->jackd->in_use = FALSE;
2851           pthread_mutex_unlock(&mainw->abuf_mutex);
2852         }
2853 #endif
2854 #ifdef HAVE_PULSE_AUDIO
2855         if (audio_player == AUD_PLAYER_PULSE && mainw->pulsed) {
2856           // must do this before deinit fx
2857           pthread_mutex_lock(&mainw->abuf_mutex);
2858           mainw->pulsed->read_abuf = -1;
2859           mainw->pulsed->in_use = FALSE;
2860           pthread_mutex_unlock(&mainw->abuf_mutex);
2861         }
2862 #endif
2863         // realtime effects off (for multitrack and event_list preview)
2864         deinit_render_effects();
2865 
2866         cfile->next_event = NULL;
2867 
2868         if (!(mainw->preview && mainw->multitrack && mainw->multitrack->is_rendering))
2869           free_track_decoders();
2870 
2871         // multitrack loop - go back to loop start position unless external transport moved us
2872         if (mainw->scratch == SCRATCH_NONE) {
2873           mainw->multitrack->pb_start_event = mainw->multitrack->pb_loop_event;
2874         }
2875       }
2876       mainw->effort = 0;
2877       if (mainw->multitrack) pb_start_event = mainw->multitrack->pb_start_event;
2878     } while (mainw->multitrack && (mainw->loop_cont || mainw->scratch != SCRATCH_NONE) &&
2879              (mainw->cancelled == CANCEL_NONE || mainw->cancelled == CANCEL_EVENT_LIST_END));
2880   }
2881 
2882   mainw->osc_block = TRUE;
2883   mainw->rte_textparm = NULL;
2884   mainw->playing_file = -1;
2885   mainw->abufs_to_fill = 0;
2886 
2887   if (!mainw->foreign) {
2888     /// deinit any active real time effects
2889     if (prefs->allow_easing && !mainw->multitrack) {
2890       // any effects which were "easing out" should be deinited now
2891       deinit_easing_effects();
2892     }
2893   }
2894 
2895   if (mainw->ext_playback) {
2896 #ifndef IS_MINGW
2897     vid_playback_plugin_exit();
2898     if (mainw->play_window) lives_window_unfullscreen(LIVES_WINDOW(mainw->play_window));
2899 #else
2900     if (mainw->play_window) lives_window_unfullscreen(LIVES_WINDOW(mainw->play_window));
2901     vid_playback_plugin_exit();
2902 #endif
2903   }
2904 
2905   // play completed
2906   if (prefs->show_player_stats) {
2907     if (mainw->fps_measure > 0) {
2908       fps_med = (double)mainw->fps_measure / ((double)lives_get_relative_ticks(mainw->origsecs, mainw->orignsecs)
2909                                               / TICKS_PER_SECOND_DBL);
2910     }
2911   }
2912   mainw->video_seek_ready = mainw->audio_seek_ready = FALSE;
2913   mainw->osc_auto = 0;
2914 
2915   if (mainw->loop_locked) unlock_loop_lock();
2916   if (prefs->show_msg_area) {
2917     lives_widget_set_size_request(mainw->message_box, -1, MIN_MSGBAR_HEIGHT);
2918   }
2919 
2920   mainw->jack_can_stop = FALSE;
2921   if ((mainw->current_file == current_file) && CURRENT_CLIP_IS_VALID) {
2922     cfile->pointer_time = pointer_time;
2923     cfile->real_pointer_time = real_pointer_time;
2924   }
2925 
2926 #ifdef ENABLE_JACK
2927   if (audio_player == AUD_PLAYER_JACK && (mainw->jackd || mainw->jackd_read)) {
2928     if (mainw->jackd_read || mainw->aud_rec_fd != -1)
2929       jack_rec_audio_end(!prefs->perm_audio_reader, TRUE);
2930 
2931     if (mainw->jackd_read) {
2932       mainw->jackd_read->in_use = FALSE;
2933     }
2934 
2935     // send jack transport stop
2936     if (!mainw->preview && !mainw->foreign) jack_pb_stop();
2937 
2938     // tell jack client to close audio file
2939     if (mainw->jackd && mainw->jackd->playing_file > 0) {
2940       ticks_t timeout = 0;
2941       if (mainw->cancelled != CANCEL_AUDIO_ERROR) {
2942         lives_alarm_t alarm_handle = lives_alarm_set(LIVES_DEFAULT_TIMEOUT);
2943         while ((timeout = lives_alarm_check(alarm_handle)) > 0 && jack_get_msgq(mainw->jackd)) {
2944           sched_yield(); // wait for seek
2945           lives_usleep(prefs->sleep_time);
2946         }
2947         lives_alarm_clear(alarm_handle);
2948       }
2949       if (mainw->cancelled == CANCEL_AUDIO_ERROR) mainw->cancelled = CANCEL_ERROR;
2950       jack_message.command = ASERVER_CMD_FILE_CLOSE;
2951       jack_message.data = NULL;
2952       jack_message.next = NULL;
2953       mainw->jackd->msgq = &jack_message;
2954       if (timeout == 0) handle_audio_timeout();
2955       else {
2956         while (mainw->jackd->playing_file > -1) {
2957           sched_yield();
2958           lives_usleep(prefs->sleep_time);
2959         }
2960       }
2961     }
2962   } else {
2963 #endif
2964 #ifdef HAVE_PULSE_AUDIO
2965     if (audio_player == AUD_PLAYER_PULSE && (mainw->pulsed || mainw->pulsed_read)) {
2966       if (mainw->pulsed_read || mainw->aud_rec_fd != -1)
2967         pulse_rec_audio_end(!prefs->perm_audio_reader, TRUE);
2968 
2969       if (mainw->pulsed_read) {
2970         mainw->pulsed_read->in_use = FALSE;
2971         pulse_driver_cork(mainw->pulsed_read);
2972       }
2973 
2974       // tell pulse client to close audio file
2975       if (mainw->pulsed) {
2976         if (mainw->pulsed->playing_file > 0 || mainw->pulsed->fd > 0) {
2977           ticks_t timeout = 0;
2978           if (mainw->cancelled != CANCEL_AUDIO_ERROR) {
2979             lives_alarm_t alarm_handle = lives_alarm_set(LIVES_DEFAULT_TIMEOUT);
2980             while ((timeout = lives_alarm_check(alarm_handle)) > 0 && pulse_get_msgq(mainw->pulsed)) {
2981               sched_yield(); // wait for seek
2982               lives_usleep(prefs->sleep_time);
2983             }
2984             lives_alarm_clear(alarm_handle);
2985           }
2986           if (mainw->cancelled == CANCEL_AUDIO_ERROR) mainw->cancelled = CANCEL_ERROR;
2987           pulse_message.command = ASERVER_CMD_FILE_CLOSE;
2988           pulse_message.data = NULL;
2989           pulse_message.next = NULL;
2990           mainw->pulsed->msgq = &pulse_message;
2991           if (timeout == 0)  {
2992             handle_audio_timeout();
2993             mainw->pulsed->playing_file = -1;
2994             mainw->pulsed->fd = -1;
2995           } else {
2996             while (mainw->pulsed->playing_file > -1 || mainw->pulsed->fd > 0) {
2997               sched_yield();
2998               lives_usleep(prefs->sleep_time);
2999             }
3000             pulse_driver_cork(mainw->pulsed);
3001           }
3002         } else {
3003           pulse_driver_cork(mainw->pulsed);
3004         }
3005       }
3006     } else {
3007 #endif
3008       if (!is_realtime_aplayer(audio_player) && stopcom) {
3009         // kill sound (if still playing)
3010         lives_system(stopcom, TRUE);
3011         mainw->aud_file_to_kill = -1;
3012         lives_free(stopcom);
3013       }
3014 #ifdef ENABLE_JACK
3015     }
3016 #endif
3017 #ifdef HAVE_PULSE_AUDIO
3018   }
3019 #endif
3020 
3021   lives_freep((void **)&com);
3022   lives_freep((void **)&mainw->urgency_msg);
3023   mainw->actual_frame = 0;
3024 
3025   lives_notify(LIVES_OSC_NOTIFY_PLAYBACK_STOPPED, "");
3026 
3027   if (mainw->new_clip != -1) {
3028     mainw->current_file = mainw->new_clip;
3029     mainw->new_clip = -1;
3030   }
3031 
3032   // stop the audio players
3033 #ifdef ENABLE_JACK
3034   if (audio_player == AUD_PLAYER_JACK && mainw->jackd) {
3035     mainw->jackd->in_use = FALSE;
3036   }
3037 #endif
3038 #ifdef HAVE_PULSE_AUDIO
3039   if (audio_player == AUD_PLAYER_PULSE && mainw->pulsed) {
3040     mainw->pulsed->in_use = FALSE;
3041   }
3042 #endif
3043 
3044   /// stop the players before the cache thread, else the players may try to play from a non-existent file
3045   if (audio_player == AUD_PLAYER_JACK
3046       || (mainw->event_list && !mainw->record && (!mainw->is_rendering
3047           || !mainw->preview || mainw->preview_rendering)))
3048     audio_cache_end();
3049 
3050   // terminate autolives if running
3051   lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->autolives), FALSE);
3052 
3053   // PLAY FINISHED...
3054 
3055   // allow this to fail - not all sub-commands may be present
3056   if (prefs->stop_screensaver) {
3057 #ifdef GDK_WINDOWING_X11
3058     com = lives_strdup("xset s on 2>/dev/null; xset +dpms 2>/dev/null ;");
3059 
3060     if (capable->has_gconftool_2) {
3061       char *xnew = lives_strdup(" gconftool-2 --set --type bool /apps/gnome-screensaver/idle_activation_enabled "
3062                                 "true 2>/dev/null ;");
3063       tmp = lives_strconcat(com, xnew, NULL);
3064       lives_free(com);
3065       lives_free(xnew);
3066       com = tmp;
3067     }
3068     if (capable->has_xdg_screensaver && awinid != -1) {
3069       char *xnew = lives_strdup_printf(" xdg-screensaver resume %"PRIu64" 2>/dev/null ;", awinid);
3070       tmp = lives_strconcat(com, xnew, NULL);
3071       lives_free(com);
3072       lives_free(xnew);
3073       com = tmp;
3074     }
3075 #else
3076     if (capable->has_gconftool_2) {
3077       com = lives_strdup("gconftool-2 --set --type bool /apps/gnome-screensaver/idle_activation_enabled true 2>/dev/null ;");
3078     } else com = lives_strdup("");
3079 #endif
3080 
3081     if (com) {
3082       lives_system(com, TRUE);
3083       lives_free(com);
3084     }
3085   }
3086 
3087   if (!mainw->multitrack && mainw->ext_audio_mon)
3088     lives_toggle_tool_button_set_active(LIVES_TOGGLE_TOOL_BUTTON(mainw->ext_audio_mon), FALSE);
3089 
3090   // reset in case audio lock was actioned
3091   prefs->audio_opts = future_prefs->audio_opts;
3092 
3093   // TODO ***: use MIDI output port for this
3094   if (!mainw->foreign && prefs->midisynch) lives_system(EXEC_MIDISTOP, TRUE);
3095 
3096   // we could have started by playing a generator, which could've been closed
3097   if (!mainw->files[current_file]) current_file = mainw->current_file;
3098 
3099   if (!is_realtime_aplayer(audio_player)) {
3100     // wait for audio_ended...
3101     if (cfile->achans > 0 && com2) {
3102       wait_for_stop(com2);
3103       mainw->aud_file_to_kill = -1;
3104     }
3105     lives_freep((void **)&com2);
3106   }
3107 
3108   if (CURRENT_CLIP_IS_NORMAL) {
3109     cfile->last_play_sequence = mainw->play_sequence;
3110     stfile = lives_build_filename(prefs->workdir, cfile->handle, LIVES_STATUS_FILE_NAME, NULL);
3111     lives_snprintf(cfile->info_file, PATH_MAX, "%s", stfile);
3112     lives_free(stfile);
3113   }
3114 
3115   if (IS_VALID_CLIP(mainw->scrap_file) && mainw->files[mainw->scrap_file]->ext_src) {
3116     lives_close_buffered(LIVES_POINTER_TO_INT(mainw->files[mainw->scrap_file]->ext_src));
3117     mainw->files[mainw->scrap_file]->ext_src = NULL;
3118     mainw->files[mainw->scrap_file]->ext_src_type = LIVES_EXT_SRC_NONE;
3119   }
3120 
3121   if (mainw->foreign) {
3122     // recording from external window capture
3123     mainw->pwidth = lives_widget_get_allocation_width(mainw->playframe) - H_RESIZE_ADJUST;
3124     mainw->pheight = lives_widget_get_allocation_height(mainw->playframe) - V_RESIZE_ADJUST;
3125 
3126     cfile->hsize = mainw->pwidth;
3127     cfile->vsize = mainw->pheight;
3128 
3129     lives_xwindow_set_keep_above(mainw->foreign_window, FALSE);
3130 
3131     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
3132 
3133     return;
3134   }
3135 
3136   disable_record();
3137   prefs->pb_quality = future_prefs->pb_quality;
3138   mainw->lockstats = FALSE;
3139   mainw->blend_palette = WEED_PALETTE_END;
3140   mainw->audio_stretch = 1.;
3141 
3142   if (!mainw->multitrack) {
3143     if (mainw->faded || mainw->fs) {
3144       unfade_background();
3145     }
3146 
3147     if (mainw->sep_win) add_to_playframe();
3148 
3149     if (CURRENT_CLIP_HAS_VIDEO) {
3150       resize(1.);
3151       lives_widget_show_all(mainw->playframe);
3152       lives_frame_set_label(LIVES_FRAME(mainw->playframe), NULL);
3153     }
3154 
3155     if (palette->style & STYLE_1) {
3156       lives_widget_show(mainw->sep_image);
3157     }
3158 
3159     if (prefs->show_msg_area && !mainw->multitrack) {
3160       lives_widget_show(mainw->message_box);
3161       reset_message_area(); ///< necessary
3162     }
3163 
3164     lives_widget_show(mainw->frame1);
3165     lives_widget_show(mainw->frame2);
3166     lives_widget_show(mainw->eventbox3);
3167     lives_widget_show(mainw->eventbox4);
3168     lives_widget_show(mainw->sep_image);
3169 
3170     if (!prefs->hide_framebar && !prefs->hfbwnp) {
3171       lives_widget_show(mainw->framebar);
3172     }
3173   }
3174 
3175   if (!is_realtime_aplayer(audio_player)) mainw->mute = mute;
3176 
3177   /// kill the separate play window
3178   if (mainw->play_window) {
3179     if (mainw->fs) {
3180       mainw->ignore_screen_size = TRUE;
3181       if (prefs->show_desktop_panel && (capable->wm_caps.pan_annoy & ANNOY_DISPLAY)
3182           && (capable->wm_caps.pan_annoy & ANNOY_FS) && (capable->wm_caps.pan_res & RES_HIDE) &&
3183           capable->wm_caps.pan_res & RESTYPE_ACTION) {
3184         show_desktop_panel();
3185       }
3186       lives_window_unfullscreen(LIVES_WINDOW(mainw->play_window));
3187     }
3188     if (prefs->sepwin_type == SEPWIN_TYPE_NON_STICKY) {
3189       kill_play_window();
3190     } else {
3191       /// or resize it back to single size
3192       if (CURRENT_CLIP_IS_VALID && cfile->is_loaded && cfile->frames > 0 && !mainw->is_rendering &&
3193           (cfile->clip_type != CLIP_TYPE_GENERATOR)) {
3194         if (mainw->preview_controls) {
3195           /// create the preview in the sepwin
3196           if (prefs->show_gui) {
3197             lives_widget_set_no_show_all(mainw->preview_controls, FALSE);
3198             lives_widget_show_all(mainw->preview_box);
3199             lives_widget_show_now(mainw->preview_box);
3200             lives_widget_set_no_show_all(mainw->preview_controls, TRUE);
3201             //lives_widget_grab_focus(mainw->preview_spinbutton);
3202           }
3203         }
3204         if (mainw->current_file != current_file) {
3205           // now we have to guess how to center the play window
3206           mainw->opwx = mainw->opwy = -1;
3207           mainw->preview_frame = 0;
3208         }
3209       }
3210 
3211       if (!mainw->multitrack) {
3212         mainw->playing_file = -2;
3213         if (mainw->fs) mainw->ignore_screen_size = TRUE;
3214         resize_play_window();
3215         mainw->playing_file = -1;
3216         lives_widget_queue_draw(LIVES_MAIN_WINDOW_WIDGET);
3217 
3218         if (!mainw->preview_box) {
3219           // create the preview box that shows frames
3220           make_preview_box();
3221         }
3222         // and add it to the play window
3223         if (!lives_widget_get_parent(mainw->preview_box) && CURRENT_CLIP_IS_NORMAL && !mainw->is_rendering) {
3224           lives_widget_queue_draw(mainw->play_window);
3225           lives_container_add(LIVES_CONTAINER(mainw->play_window), mainw->preview_box);
3226           play_window_set_title();
3227         }
3228 
3229         if (mainw->play_window) {
3230           if (prefs->show_playwin) {
3231             lives_window_present(LIVES_WINDOW(mainw->play_window));
3232             lives_xwindow_raise(lives_widget_get_xwindow(mainw->play_window));
3233             unhide_cursor(lives_widget_get_xwindow(mainw->play_window));
3234             lives_widget_set_no_show_all(mainw->preview_controls, FALSE);
3235             lives_widget_show_all(mainw->preview_box);
3236             lives_widget_show_now(mainw->preview_box);
3237             lives_widget_grab_focus(mainw->preview_spinbutton);
3238             lives_widget_set_no_show_all(mainw->preview_controls, TRUE);
3239             lives_widget_process_updates(mainw->play_window);
3240             lives_window_center(LIVES_WINDOW(mainw->play_window));
3241             clear_widget_bg(mainw->play_image, mainw->play_surface);
3242             load_preview_image(FALSE);
3243 	  // *INDENT-OFF*
3244 	  }}}}}
3245   // *INDENT-ON*
3246 
3247   /// free the last frame image
3248   if (mainw->frame_layer) {
3249     weed_layer_free(mainw->frame_layer);
3250     mainw->frame_layer = NULL;
3251   }
3252 
3253   if (mainw->lazy) mainw->lazy = lives_idle_add_simple(lazy_startup_checks, NULL);
3254 
3255   cliplist = mainw->cliplist;
3256   while (cliplist) {
3257     int i = LIVES_POINTER_TO_INT(cliplist->data);
3258     if (IS_NORMAL_CLIP(i) && mainw->files[i]->clip_type == CLIP_TYPE_FILE)
3259       chill_decoder_plugin(i);
3260     mainw->files[i]->adirection = LIVES_DIRECTION_FORWARD;
3261     cliplist = cliplist->next;
3262   }
3263 
3264   if (!mainw->foreign) {
3265     unhide_cursor(lives_widget_get_xwindow(mainw->playarea));
3266   }
3267 
3268   if (CURRENT_CLIP_IS_VALID) cfile->play_paused = FALSE;
3269 
3270   if (mainw->blend_file != -1 && mainw->blend_file != mainw->current_file && mainw->files[mainw->blend_file] &&
3271       mainw->files[mainw->blend_file]->clip_type == CLIP_TYPE_GENERATOR) {
3272     int xcurrent_file = mainw->current_file;
3273     weed_bg_generator_end((weed_plant_t *)mainw->files[mainw->blend_file]->ext_src);
3274     mainw->current_file = xcurrent_file;
3275   }
3276 
3277   mainw->filter_map = mainw->afilter_map = mainw->audio_event = NULL;
3278 
3279   /// disable the freeze key
3280   lives_accel_group_disconnect(LIVES_ACCEL_GROUP(mainw->accel_group), freeze_closure);
3281   lives_accel_group_disconnect(LIVES_ACCEL_GROUP(mainw->accel_group), bg_freeze_closure);
3282 
3283   if (needsadone) d_print_done();
3284 
3285   /// free any pre-cached frame
3286   if (mainw->frame_layer_preload && mainw->pred_clip != -1) {
3287     check_layer_ready(mainw->frame_layer_preload);
3288     weed_layer_free(mainw->frame_layer_preload);
3289   }
3290   mainw->frame_layer_preload = NULL;
3291 
3292   if (!prefs->vj_mode) {
3293     /// pop up error dialog if badly sized frames were detected
3294     if (mainw->size_warn) {
3295       if (mainw->size_warn > 0 && mainw->files[mainw->size_warn]) {
3296         char *smsg = lives_strdup_printf(
3297                        _("\n\nSome frames in the clip\n%s\nare wrongly sized.\nYou should "
3298                          "click on Tools--->Resize All\n"
3299                          "and resize all frames to the current size.\n"),
3300                        mainw->files[mainw->size_warn]->name);
3301         widget_opts.non_modal = TRUE;
3302         do_error_dialog(smsg);
3303         widget_opts.non_modal = FALSE;
3304         lives_free(smsg);
3305       }
3306     }
3307   }
3308   mainw->size_warn = 0;
3309 
3310   // set processing state again if a previewe finished
3311   // CAUTION !!
3312   mainw->is_processing = mainw->preview;
3313   /////////////////
3314 
3315   if (prefs->volume != (double)future_prefs->volume) pref_factory_float(PREF_MASTER_VOLUME, future_prefs->volume, TRUE);
3316 
3317   // TODO - ????
3318   if (CURRENT_CLIP_IS_VALID && cfile->clip_type == CLIP_TYPE_DISK && cfile->frames == 0 && mainw->record_perf) {
3319     lives_signal_handler_block(mainw->record_perf, mainw->record_perf_func);
3320     lives_check_menu_item_set_active(LIVES_CHECK_MENU_ITEM(mainw->record_perf), FALSE);
3321     lives_signal_handler_unblock(mainw->record_perf, mainw->record_perf_func);
3322   }
3323 
3324   // TODO - can this be done earlier ?
3325   if (mainw->cancelled == CANCEL_APP_QUIT) on_quit_activate(NULL, NULL);
3326 
3327   /// end record performance
3328 
3329 #ifdef ENABLE_JACK
3330   if (audio_player == AUD_PLAYER_JACK && mainw->jackd) {
3331     ticks_t timeout;
3332     lives_alarm_t alarm_handle = lives_alarm_set(LIVES_DEFAULT_TIMEOUT);
3333     while ((timeout = lives_alarm_check(alarm_handle)) > 0 && jack_get_msgq(mainw->jackd)) {
3334       sched_yield(); ///< wait for seek
3335       lives_usleep(prefs->sleep_time);
3336     }
3337     lives_alarm_clear(alarm_handle);
3338     if (timeout == 0)  {
3339       handle_audio_timeout();
3340     }
3341     if (has_audio_buffers) {
3342       free_jack_audio_buffers();
3343       audio_free_fnames();
3344     }
3345   }
3346 #endif
3347 #ifdef HAVE_PULSE_AUDIO
3348   if (audio_player == AUD_PLAYER_PULSE && mainw->pulsed) {
3349     ticks_t timeout;
3350     lives_alarm_t alarm_handle = lives_alarm_set(LIVES_DEFAULT_TIMEOUT);
3351     while ((timeout = lives_alarm_check(alarm_handle)) > 0 && pulse_get_msgq(mainw->pulsed)) {
3352       sched_yield(); ///< wait for seek
3353       lives_usleep(prefs->sleep_time);
3354     }
3355     lives_alarm_clear(alarm_handle);
3356     if (timeout == 0)  {
3357       handle_audio_timeout();
3358     }
3359 
3360     if (has_audio_buffers) {
3361       free_pulse_audio_buffers();
3362       audio_free_fnames();
3363     }
3364   }
3365 #endif
3366 
3367   if (prefs->show_player_stats) {
3368     if (mainw->fps_measure > 0) {
3369       d_print(_("Average FPS was %.4f (%d frames in clock time of %f)\n"), fps_med, mainw->fps_measure,
3370               (double)lives_get_relative_ticks(mainw->origsecs, mainw->orignsecs) / TICKS_PER_SECOND_DBL);
3371     }
3372   }
3373 
3374   if (THREADVAR(bad_aud_file)) {
3375     /// we got an error recording audio
3376     do_write_failed_error_s(THREADVAR(bad_aud_file), NULL);
3377     lives_freep((void **)&THREADVAR(bad_aud_file));
3378   }
3379 
3380   if (mainw->new_vpp) {
3381     mainw->noswitch = FALSE;
3382     mainw->vpp = open_vid_playback_plugin(mainw->new_vpp, TRUE);
3383     mainw->new_vpp = NULL;
3384     mainw->noswitch = TRUE;
3385   }
3386 
3387   if (!mainw->multitrack && CURRENT_CLIP_HAS_VIDEO) {
3388     lives_widget_set_sensitive(mainw->spinbutton_start, TRUE);
3389     lives_widget_set_sensitive(mainw->spinbutton_end, TRUE);
3390   }
3391 
3392   if (!mainw->preview && CURRENT_CLIP_IS_VALID && cfile->clip_type == CLIP_TYPE_GENERATOR) {
3393     mainw->osc_block = TRUE;
3394     weed_generator_end((weed_plant_t *)cfile->ext_src);
3395     mainw->osc_block = FALSE;
3396   } else {
3397     if (mainw->current_file > -1) {
3398       if (mainw->toy_type == LIVES_TOY_MAD_FRAMES && !cfile->opening) {
3399         showclipimgs();
3400         if (!mainw->multitrack)
3401           redraw_timeline(mainw->current_file);
3402       }
3403     }
3404   }
3405 
3406   if (CURRENT_CLIP_IS_VALID) {
3407     if (!mainw->multitrack) {
3408       lives_ce_update_timeline(0, cfile->real_pointer_time);
3409       mainw->ptrtime = cfile->real_pointer_time;
3410       lives_widget_queue_draw(mainw->eventbox2);
3411       lives_widget_queue_draw_if_visible(mainw->framecounter);
3412     }
3413   }
3414 
3415   if (!mainw->multitrack) {
3416     //lives_table_set_column_homogeneous(LIVES_TABLE(mainw->pf_grid), FALSE);
3417   }
3418 
3419   if (prefs->show_gui && ((mainw->multitrack  && mainw->double_size) ||
3420                           (lives_widget_get_allocation_height(LIVES_MAIN_WINDOW_WIDGET) > GUI_SCREEN_HEIGHT ||
3421                            lives_widget_get_allocation_width(LIVES_MAIN_WINDOW_WIDGET) > GUI_SCREEN_WIDTH))) {
3422     //if (prefs->gui_monitor == 0) lives_window_move(LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET), 0, 0);
3423     if (prefs->open_maximised)
3424       lives_window_maximize(LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET));
3425     lives_widget_queue_draw(LIVES_MAIN_WINDOW_WIDGET);
3426   }
3427 
3428   if (!mainw->preview && (mainw->current_file == -1 || (CURRENT_CLIP_IS_VALID && !cfile->opening))) {
3429     sensitize();
3430   }
3431 
3432   if (CURRENT_CLIP_IS_VALID && cfile->opening) {
3433     lives_widget_set_sensitive(mainw->mute_audio, cfile->achans > 0);
3434     lives_widget_set_sensitive(mainw->loop_continue, TRUE);
3435     lives_widget_set_sensitive(mainw->loop_video, cfile->achans > 0 && cfile->frames > 0);
3436   }
3437 
3438   if (mainw->cancelled != CANCEL_USER_PAUSED) {
3439     lives_widget_set_sensitive(mainw->stop, FALSE);
3440     lives_widget_set_sensitive(mainw->m_stopbutton, FALSE);
3441   }
3442 
3443   lives_widget_set_sensitive(mainw->spinbutton_pb_fps, FALSE);
3444 
3445   if (!mainw->multitrack) {
3446     /// update screen for internal players
3447     if (prefs->hfbwnp) {
3448       lives_widget_hide(mainw->framebar);
3449     }
3450     set_drawing_area_from_pixbuf(mainw->play_image, NULL, mainw->play_surface);
3451     lives_widget_set_opacity(mainw->play_image, 0.);
3452   }
3453 
3454   if (!mainw->multitrack) mainw->osc_block = FALSE;
3455 
3456   reset_clipmenu();
3457 
3458   lives_menu_item_set_accel_path(LIVES_MENU_ITEM(mainw->quit), LIVES_ACCEL_PATH_QUIT);
3459 
3460   if (!mainw->multitrack && CURRENT_CLIP_IS_VALID)
3461     set_main_title(cfile->name, 0);
3462 
3463   if (!mainw->multitrack && !mainw->foreign && CURRENT_CLIP_IS_VALID && (!cfile->opening ||
3464       cfile->clip_type == CLIP_TYPE_FILE)) {
3465     showclipimgs();
3466     redraw_timeline(mainw->current_file);
3467   }
3468 
3469   if (prefs->show_msg_area) {
3470     if (mainw->idlemax == 0) {
3471       lives_idle_add_simple(resize_message_area, NULL);
3472     }
3473     mainw->idlemax = DEF_IDLE_MAX;
3474   }
3475 
3476   lives_widget_queue_draw(LIVES_MAIN_WINDOW_WIDGET);
3477 
3478   /// need to do this here, in case we want to preview with only a generator and no other clips (which will close to -1)
3479   if (mainw->record) {
3480     lives_idle_add_simple(render_choice_idle, LIVES_INT_TO_POINTER(FALSE));
3481   }
3482 
3483   mainw->record_paused = mainw->record_starting = mainw->record = FALSE;
3484 
3485   mainw->ignore_screen_size = FALSE;
3486 
3487   /// re-enable generic clip switching
3488   mainw->noswitch = FALSE;
3489 }
3490 
3491 
3492 /**
3493    @brief close cfile and switch to new clip (may be -1)
3494 
3495    note this only closes the disk and basic resources, it does not affect the interface
3496    (c.f. close_current_file())
3497    returns new_clip */
3498 int close_temp_handle(int new_clip) {
3499   char *com, *permitname;
3500   int clipno = mainw->current_file;
3501 
3502   if (!IS_VALID_CLIP(new_clip)) new_clip = -1;
3503   if (!IS_VALID_CLIP(clipno)) {
3504     mainw->current_file = new_clip;
3505     return new_clip;
3506   }
3507   if (cfile->clip_type != CLIP_TYPE_TEMP
3508       && mainw->current_file != mainw->scrap_file && mainw->current_file != mainw->ascrap_file) {
3509     close_current_file(new_clip);
3510   }
3511 
3512   // as a safety feature we create a special file which allows the back end to delete the directory
3513   permitname = lives_build_filename(prefs->workdir, cfile->handle, TEMPFILE_MARKER "." LIVES_FILE_EXT_TMP, NULL);
3514   lives_touch(permitname);
3515   lives_free(permitname);
3516 
3517   com = lives_strdup_printf("%s close \"%s\"", prefs->backend, cfile->handle);
3518   lives_system(com, TRUE);
3519   lives_free(com);
3520   lives_freep((void **)&mainw->files[clipno]);
3521 
3522   mainw->current_file = new_clip;
3523 
3524   if (mainw->first_free_file == ALL_USED || mainw->first_free_file > clipno)
3525     mainw->first_free_file = clipno;
3526   return new_clip;
3527 }
3528 
3529 
3530 /**
3531    @brief get next free file slot, or -1 if we are full
3532 
3533   can support MAX_FILES files (default 65536) */
3534 static int get_next_free_file(void) {
3535   int idx = mainw->first_free_file++;
3536   while ((mainw->first_free_file != ALL_USED) && mainw->files[mainw->first_free_file]) {
3537     mainw->first_free_file++;
3538     if (mainw->first_free_file >= MAX_FILES) mainw->first_free_file = ALL_USED;
3539   }
3540   return idx;
3541 }
3542 
3543 
3544 /**
3545     @brief get a temp "handle" from disk.
3546 
3547     Call this to get a temp handle for returning info from the backend
3548     (this is deprecated for simple data, use lives_popen() instead whenever possible)
3549 
3550     This function is also called from get_new_handle() to create a permanent handle
3551     for an opened file.
3552 
3553     there are two special instances when this is called with an index != -1:
3554     - when saving a set and a clip is moved from outside the set to inside it.
3555     we need a new handle which is guaranteed unique for the set, but we retain all the other details
3556     - when called from get_new_handle() to create the disk part of a clip
3557 
3558     otherwise, index should be passed in as -1 (the normal case)
3559     -- handle will be fetched and a directory created in workdir.
3560     -- clip_type is set to CLIP_TYPE_TEMP.
3561     call close_temp_handle() on it after use, then restore mainw->current_file
3562 
3563     function returns FALSE if write to workdir fails.
3564 
3565     WARNING:
3566     this function changes mainw->current_file, unless it returns FALSE (could not create cfile)
3567 
3568     get_new_handle() calls this with the index value passed to it, which should not be -1,
3569     sets defaults for the clip,
3570     and also sets the clip name and filename. That function should be used instead to create permanent clips. */
3571 boolean get_temp_handle(int index) {
3572   boolean is_unique, create = FALSE;
3573 
3574   if (CURRENT_CLIP_IS_TEMP) {
3575     break_me("temp clip in temp clip !!");
3576     return TRUE;
3577   }
3578 
3579   if (index < -1 || index > MAX_FILES) {
3580     char *msg = lives_strdup_printf("Attempt to create invalid new temp clip %d\n", index);
3581     LIVES_WARN(msg);
3582     lives_free(msg);
3583     return FALSE;
3584   }
3585 
3586   if (index == -1) {
3587     if (mainw->first_free_file == ALL_USED) {
3588       too_many_files();
3589       return FALSE;
3590     }
3591     create = TRUE;
3592     index = mainw->first_free_file;
3593     get_next_free_file();
3594   }
3595 
3596   do {
3597     is_unique = TRUE;
3598 
3599     // get handle from info file, the first time we will also malloc a
3600     // new "file" struct here and create a directory in prefs->workdir
3601     if (!get_handle_from_info_file(index)) {
3602       lives_freep((void **)&mainw->files[index]);
3603       if (mainw->first_free_file == ALL_USED || index < mainw->first_free_file)
3604         mainw->first_free_file = index;
3605       return FALSE;
3606     }
3607 
3608     if (*mainw->set_name) {
3609       char *setclipdir = CLIPDIR(cfile->handle);
3610       if (lives_file_test(setclipdir, LIVES_FILE_TEST_IS_DIR)) is_unique = FALSE;
3611       lives_free(setclipdir);
3612     }
3613   } while (!is_unique);
3614 
3615   mainw->current_file = index;
3616 
3617   if (create) {
3618     // create a marker file in directory, else we will be barred from
3619     // removing it
3620     char *canremove = lives_build_filename(prefs->workdir, cfile->handle,
3621                                            TEMPFILE_MARKER "." LIVES_FILE_EXT_TMP, NULL);
3622     lives_touch(canremove);
3623     lives_free(canremove);
3624     // fill with default values
3625     create_cfile(index, cfile->handle, FALSE);
3626     cfile->clip_type = CLIP_TYPE_TEMP;
3627   }
3628   return TRUE;
3629 }
3630 
3631 
3632 /**
3633    @brief set default values for a clip (in memory)
3634 
3635    if new_file == -1 we create (malloc) a new clip and switch to it
3636    - setting its handle to "handle" (reload / crash recovery)
3637 
3638    if new_file != -1 the parameter "handle" is ignored, and we switch to new_file, without mallocing anything
3639    - "handle" in the clip must have been set already (called from get_new_handle() and get_temp_handle())
3640    -- get_new_handle() will set name and fliename and switch back to original clip.
3641 
3642    default values are then set for the clip
3643    - a "unique_id" is assigned via uuidgen or lives_random()
3644    - type is set to CLIP_TYPE_DISK
3645    - img_type is set depending on prefs->image_type
3646    - frames is set to 0
3647    etc.
3648 
3649    - loaded is set = to is_loaded
3650 
3651    WARNING: on success, returns the clip, and changes the value of
3652    mainw->current_file !!  returns NULL if: new_file is out of range
3653    or points to a NULL clip; new_file is -1 and all free clips are
3654    in use (unlikely), or malloc fails.
3655 */
3656 lives_clip_t *create_cfile(int new_file, const char *handle, boolean is_loaded) {
3657   lives_clip_t *sfile;
3658   char *stfile;
3659 
3660   if (new_file == -1) {
3661     // if new_file == -1, we are going to create a new clip
3662     new_file = mainw->first_free_file;
3663     if (new_file == -1) {
3664       too_many_files();
3665       return NULL;
3666     }
3667 
3668     mainw->current_file = new_file;
3669     get_next_free_file();
3670 
3671     if (new_file < 0 || new_file > MAX_FILES || IS_VALID_CLIP(new_file)) {
3672       char *msg = lives_strdup_printf("Attempt to create invalid new clip %d\n", new_file);
3673       LIVES_WARN(msg);
3674       lives_free(msg);
3675       return NULL;
3676     }
3677 
3678     if (!handle) {
3679       // if handle is NULL, we create a new clip on disk, switch to it
3680       // (unused)
3681       if (!get_handle_from_info_file(new_file)) return NULL;
3682       sfile = mainw->files[new_file];
3683     } else {
3684       // else just create the in-memory part and set the handle
3685       sfile = mainw->files[new_file] = (lives_clip_t *)(lives_calloc(1, sizeof(lives_clip_t)));
3686       if (!sfile) return NULL;
3687       lives_snprintf(sfile->handle, 256, "%s", handle);
3688     }
3689   }
3690 
3691   mainw->current_file = new_file;
3692 
3693   cfile->is_loaded = is_loaded;
3694 
3695   // any cfile (clip) initialisation goes in here
3696   lives_memcpy((void *)&cfile->binfmt_check.chars, "LiVESXXX", 8);
3697   cfile->binfmt_version.num = make_version_hash(LiVES_VERSION);
3698   cfile->binfmt_bytes.size = (size_t)((void *)&cfile->binfmt_end - (void *)cfile);
3699   cfile->menuentry = NULL;
3700   cfile->start = cfile->end = 0;
3701   cfile->old_frames = cfile->opening_frames = cfile->frames = 0;
3702   lives_snprintf(cfile->type, 40, "%s", _("Unknown"));
3703   cfile->f_size = 0l;
3704   cfile->achans = 0;
3705   cfile->arate = 0;
3706   cfile->arps = 0;
3707   cfile->afilesize = 0l;
3708   cfile->asampsize = 0;
3709   cfile->adirection = LIVES_DIRECTION_FORWARD;
3710   cfile->undoable = FALSE;
3711   cfile->redoable = FALSE;
3712   cfile->changed = FALSE;
3713   cfile->was_in_set = FALSE;
3714   cfile->hsize = cfile->vsize = cfile->ohsize = cfile->ovsize = 0;
3715   cfile->fps = cfile->pb_fps = prefs->default_fps;
3716   cfile->resample_events = NULL;
3717   cfile->insert_start = cfile->insert_end = 0;
3718   cfile->is_untitled = TRUE;
3719   cfile->was_renamed = FALSE;
3720   cfile->undo_action = UNDO_NONE;
3721   cfile->opening_audio = cfile->opening = cfile->opening_only_audio = FALSE;
3722   cfile->pointer_time = 0.;
3723   cfile->real_pointer_time = 0.;
3724   cfile->restoring = cfile->opening_loc = cfile->nopreview = FALSE;
3725   cfile->video_time = cfile->laudio_time = cfile->raudio_time = 0.;
3726   cfile->freeze_fps = 0.;
3727   cfile->last_vframe_played = 0;
3728   cfile->frameno = cfile->last_frameno = cfile->saved_frameno = 1;
3729   cfile->progress_start = cfile->progress_end = 0;
3730   cfile->play_paused = cfile->nokeep = FALSE;
3731   cfile->undo_start = cfile->undo_end = 0;
3732   cfile->ext_src = NULL;
3733   cfile->ext_src_type = LIVES_EXT_SRC_NONE;
3734   cfile->clip_type = CLIP_TYPE_DISK;
3735   cfile->ratio_fps = FALSE;
3736   cfile->aseek_pos = 0;
3737   cfile->unique_id = gen_unique_id();
3738   cfile->layout_map = NULL;
3739   cfile->frame_index = cfile->frame_index_back = NULL;
3740   cfile->fx_frame_pump = 0;
3741   cfile->pumper = NULL;
3742   cfile->stored_layout_frame = 0;
3743   cfile->stored_layout_audio = 0.;
3744   cfile->stored_layout_fps = 0.;
3745   cfile->stored_layout_idx = -1;
3746   cfile->interlace = LIVES_INTERLACE_NONE;
3747   cfile->subt = NULL;
3748   cfile->no_proc_sys_errors = cfile->no_proc_read_errors = cfile->no_proc_write_errors = FALSE;
3749   cfile->keep_without_preview = FALSE;
3750   cfile->cb_src = -1;
3751   cfile->needs_update = cfile->needs_silent_update = FALSE;
3752   cfile->audio_waveform = NULL;
3753   cfile->md5sum[0] = 0;
3754   cfile->gamma_type = WEED_GAMMA_SRGB;
3755   cfile->last_play_sequence = 0;
3756   cfile->tcache_dubious_from = 0;
3757   cfile->tcache_height = 0;
3758   cfile->tcache = NULL;
3759   cfile->checked = FALSE;
3760   cfile->has_binfmt = TRUE;
3761 
3762   if (!strcmp(prefs->image_ext, LIVES_FILE_EXT_JPG)) cfile->img_type = IMG_TYPE_JPEG;
3763   else cfile->img_type = IMG_TYPE_PNG;
3764 
3765   cfile->bpp = (cfile->img_type == IMG_TYPE_JPEG) ? 24 : 32;
3766   cfile->deinterlace = FALSE;
3767 
3768   cfile->play_paused = FALSE;
3769   cfile->header_version = LIVES_CLIP_HEADER_VERSION;
3770 
3771   cfile->event_list = cfile->event_list_back = NULL;
3772   cfile->next_event = NULL;
3773   cfile->vol = 1.;
3774 
3775   lives_memset(cfile->name, 0, 1);
3776   lives_memset(cfile->mime_type, 0, 1);
3777   lives_memset(cfile->file_name, 0, 1);
3778   lives_memset(cfile->save_file_name, 0, 1);
3779 
3780   lives_memset(cfile->comment, 0, 1);
3781   lives_memset(cfile->author, 0, 1);
3782   lives_memset(cfile->title, 0, 1);
3783   lives_memset(cfile->keywords, 0, 1);
3784 
3785   cfile->signed_endian = AFORM_UNKNOWN;
3786   lives_snprintf(cfile->undo_text, 32, "%s", _("_Undo"));
3787   lives_snprintf(cfile->redo_text, 32, "%s", _("_Redo"));
3788 
3789   stfile = lives_build_filename(prefs->workdir, cfile->handle, LIVES_STATUS_FILE_NAME, NULL);
3790 
3791   lives_snprintf(cfile->info_file, PATH_MAX, "%s", stfile);
3792   lives_free(stfile);
3793 
3794   // backwards compat.
3795   cfile->checked_for_old_header = FALSE;
3796   cfile->has_old_header = FALSE;
3797 
3798   return cfile;
3799 }
3800 
3801 
3802 LIVES_GLOBAL_INLINE char *get_untitled_name(int number) {
3803   // utility function to get clip name
3804   return lives_strdup_printf(_("Untitled%d"), number);
3805 }
3806 
3807 
3808 int create_nullvideo_clip(const char *handle) {
3809   // create a file with no video, just produces blank frames
3810   // may be used to playback with audio, for testign etc.
3811   int new_file;
3812   int current_file = mainw->current_file;
3813   create_cfile(-1, handle, TRUE);
3814   new_file = mainw->current_file;
3815   mainw->current_file = current_file;
3816   mainw->files[new_file]->clip_type = CLIP_TYPE_NULL_VIDEO;
3817   return new_file;
3818 }
3819 
3820 
3821 boolean get_new_handle(int index, const char *name) {
3822   // here is where we first initialize for the clipboard
3823   // and for paste_as_new, and restore, etc.
3824   // pass in name as NULL or "" and it will be set with an untitled number
3825 
3826   // this function *does not* change mainw->current_file (except briefly), or add to the menu
3827   // or update mainw->clips_available
3828 
3829   // differences from get_temp_handle:
3830   // - here we dont't switch clips;
3831   // - index is normally passed in rather than generated (pulled from next_free_file) - this allows
3832   //     the caller to know the index number and do preconfig before calling
3833   // - we set name and file_name from the name parameter, or if name is NULL, we set an untitled name
3834   //        and increment mainw->untitled_number
3835   // - the clip should be closed using close_current_file() instead of close_temp_handle()
3836 
3837   char *xname;
3838 
3839   int current_file = mainw->current_file;
3840 
3841   // if TRUE, changes mainw->current_file (and hence cfile)
3842   if (!get_temp_handle(index)) return FALSE;
3843 
3844   // setup would have been done already in get_temp_handle()
3845   if (index == -1) index = mainw->current_file;
3846 
3847   else create_cfile(index, cfile->handle, FALSE);
3848 
3849   // note : don't need to update first_free_file for the clipboard
3850   // because we used index 0 instead of a free index number
3851   if (index != 0) {
3852     get_next_free_file();
3853   }
3854 
3855   if (!name || !*name) {
3856     cfile->is_untitled = TRUE;
3857     xname = get_untitled_name(mainw->untitled_number++);
3858   } else xname = lives_strdup(name);
3859 
3860   lives_snprintf(cfile->file_name, PATH_MAX, "%s", xname);
3861   lives_snprintf(cfile->name, CLIP_NAME_MAXLEN, "%s", xname);
3862 
3863   mainw->current_file = current_file;
3864 
3865   lives_free(xname);
3866   return TRUE;
3867 }
3868 
3869 
3870 boolean add_file_info(const char *check_handle, boolean aud_only) {
3871   // file information has been retrieved, set struct cfile with details
3872   // contained in mainw->msg. We do this twice, once before opening the file, once again after.
3873   // The first time, frames and afilesize may not be correct.
3874   char *mesg, *mesg1;
3875   char **array;
3876   char *test_fps_string1;
3877   char *test_fps_string2;
3878 
3879   if (aud_only && !mainw->save_with_sound) {
3880     cfile->arps = cfile->arate = cfile->achans = cfile->asampsize = 0;
3881     cfile->afilesize = 0l;
3882     return TRUE;
3883   }
3884 
3885   if (!strcmp(mainw->msg, "killed")) {
3886     char *com;
3887     // user pressed "enough"
3888     // just in case last frame is damaged, we delete it (physically, otherwise it will get dragged in when the file is opened)
3889     if (!cfile->ext_src) {
3890       cfile->frames = get_frame_count(mainw->current_file, cfile->opening_frames);
3891       if (cfile->frames > 1) {
3892         com = lives_strdup_printf("%s cut \"%s\" %d %d %d %d \"%s\" %.3f %d %d %d",
3893                                   prefs->backend, cfile->handle, cfile->frames, cfile->frames,
3894                                   FALSE, cfile->frames, get_image_ext_for_type(cfile->img_type),
3895                                   0., 0, 0, 0);
3896         lives_system(com, FALSE);
3897         lives_free(com);
3898         cfile->frames--;
3899       }
3900     }
3901 
3902     // commit audio
3903     mainw->cancelled = CANCEL_NONE;
3904     lives_rm(cfile->info_file);
3905 
3906     com = lives_strdup_printf("%s commit_audio \"%s\" 1", prefs->backend, cfile->handle);
3907     lives_system(com, TRUE);
3908     lives_free(com);
3909 
3910     wait_for_bg_audio_sync(mainw->current_file);
3911 
3912     reget_afilesize(mainw->current_file);
3913     d_print_enough(cfile->frames);
3914 
3915     if (prefs->auto_trim_audio) {
3916       if (cfile->laudio_time > cfile->video_time) {
3917         d_print(_("Auto trimming %.2f seconds of audio at end..."), cfile->laudio_time - cfile->video_time);
3918         if (on_trim_audio_activate(NULL, LIVES_INT_TO_POINTER(0))) d_print_done();
3919         else d_print("\n");
3920         cfile->changed = FALSE;
3921       }
3922     }
3923   } else {
3924     if (check_handle) {
3925       int npieces = get_token_count(mainw->msg, '|');
3926       if (npieces < 2) return FALSE;
3927 
3928       array = lives_strsplit(mainw->msg, "|", npieces);
3929 
3930       if (!strcmp(array[0], "error")) {
3931         if (npieces >= 3) {
3932           mesg = lives_strdup_printf(_("\nAn error occurred doing\n%s\n"), array[2]);
3933           LIVES_ERROR(array[2]);
3934         } else mesg = (_("\nAn error occurred opening the file\n"));
3935         widget_opts.non_modal = TRUE;
3936         do_error_dialog(mesg);
3937         widget_opts.non_modal = FALSE;
3938         lives_free(mesg);
3939         lives_strfreev(array);
3940         return FALSE;
3941       }
3942 
3943       // sanity check handle against status file
3944       // (this should never happen...)
3945       if (strcmp(check_handle, array[1])) {
3946         LIVES_ERROR("Handle!=statusfile !");
3947         mesg = lives_strdup_printf(_("\nError getting file info for clip %s.\nBad things may happen with this clip.\n"),
3948                                    check_handle);
3949         widget_opts.non_modal = TRUE;
3950         do_error_dialog(mesg);
3951         widget_opts.non_modal = FALSE;
3952         lives_free(mesg);
3953         lives_strfreev(array);
3954         return FALSE;
3955       }
3956 
3957       cfile->arps = cfile->arate = atoi(array[9]);
3958       cfile->achans = atoi(array[10]);
3959       cfile->asampsize = atoi(array[11]);
3960       cfile->signed_endian = get_signed_endian(atoi(array[12]), atoi(array[13]));
3961       cfile->afilesize = strtol(array[14], NULL, 10);
3962       if (aud_only) {
3963         lives_strfreev(array);
3964         return TRUE;
3965       }
3966 
3967       cfile->frames = atoi(array[2]);
3968       if (aud_only) {
3969         lives_strfreev(array);
3970         return TRUE;
3971       }
3972       lives_snprintf(cfile->type, 40, "%s", array[3]);
3973       cfile->hsize = atoi(array[4]);
3974       cfile->vsize = atoi(array[5]);
3975       cfile->bpp = atoi(array[6]);
3976       cfile->pb_fps = cfile->fps = lives_strtod(array[7], NULL);
3977       cfile->f_size = strtol(array[8], NULL, 10);
3978 
3979       if (npieces > 15 && array[15]) {
3980         if (prefs->btgamma) {
3981           if (!strcmp(array[15], "bt709")) cfile->gamma_type = WEED_GAMMA_BT709;
3982         }
3983       }
3984 
3985       if (!*cfile->title && npieces > 16 && array[16]) {
3986         lives_snprintf(cfile->title, 1024, "%s", lives_strstrip(array[16]));
3987       }
3988       if (!*cfile->author && npieces > 17 && array[17]) {
3989         lives_snprintf(cfile->author, 1024, "%s", lives_strstrip(array[17]));
3990       }
3991       if (!*cfile->comment && npieces > 18 && array[18]) {
3992         lives_snprintf(cfile->comment, 1024, "%s", lives_strstrip(array[18]));
3993       }
3994 
3995       lives_strfreev(array);
3996     }
3997   }
3998 
3999   cfile->video_time = 0;
4000 
4001   test_fps_string1 = lives_strdup_printf("%.3f00000", cfile->fps);
4002   test_fps_string2 = lives_strdup_printf("%.8f", cfile->fps);
4003 
4004   if (strcmp(test_fps_string1, test_fps_string2)) {
4005     cfile->ratio_fps = TRUE;
4006   } else {
4007     cfile->ratio_fps = FALSE;
4008   }
4009   lives_free(test_fps_string1);
4010   lives_free(test_fps_string2);
4011 
4012   if (!mainw->save_with_sound) {
4013     cfile->arps = cfile->arate = cfile->achans = cfile->asampsize = 0;
4014     cfile->afilesize = 0l;
4015   }
4016 
4017   if (cfile->frames <= 0) {
4018     if (cfile->afilesize == 0l && cfile->is_loaded) {
4019       // we got no video or audio...
4020       return FALSE;
4021     }
4022     cfile->start = cfile->end = cfile->undo_start = cfile->undo_end = 0;
4023   } else {
4024     // start with all selected
4025     cfile->start = 1;
4026     cfile->end = cfile->frames;
4027     cfile->undo_start = cfile->start;
4028     cfile->undo_end = cfile->end;
4029   }
4030 
4031   cfile->orig_file_name = TRUE;
4032   cfile->is_untitled = FALSE;
4033 
4034   // some files give us silly frame rates, even single frames...
4035   // fps of 1000. is used for some streams (i.e. play each frame as it is received)
4036   if (cfile->fps == 0. || cfile->fps == 1000. || (cfile->frames < 2 && cfile->is_loaded)) {
4037 
4038     if ((cfile->afilesize * cfile->asampsize * cfile->arate * cfile->achans == 0) || cfile->frames < 2) {
4039       if (cfile->frames != 1) {
4040         d_print(_("\nPlayback speed not found or invalid ! Using default fps of %.3f fps. \n"
4041                   "Default can be set in Tools | Preferences | Misc.\n"),
4042                 prefs->default_fps);
4043       }
4044       cfile->pb_fps = cfile->fps = prefs->default_fps;
4045     } else {
4046       cfile->laudio_time = cfile->raudio_time = cfile->afilesize / cfile->asampsize * 8. / cfile->arate / cfile->achans;
4047       cfile->pb_fps = cfile->fps = 1.*(int)(cfile->frames / cfile->laudio_time);
4048       if (cfile->fps > FPS_MAX || cfile->fps < 1.) {
4049         cfile->pb_fps = cfile->fps = prefs->default_fps;
4050       }
4051       d_print(_("Playback speed was adjusted to %.3f frames per second to fit audio.\n"), cfile->fps);
4052     }
4053   }
4054 
4055   cfile->video_time = (double)cfile->frames / cfile->fps;
4056 
4057   if (cfile->opening) return TRUE;
4058 
4059   if ((!strcmp(cfile->type, LIVES_IMAGE_TYPE_JPEG) || !strcmp(cfile->type, LIVES_IMAGE_TYPE_PNG))) {
4060     mesg = (_("Image format detected"));
4061     d_print(mesg);
4062     lives_free(mesg);
4063     return TRUE;
4064   }
4065 
4066   if (cfile->bpp == 256) {
4067     mesg1 = lives_strdup_printf(_("Frames=%d type=%s size=%dx%d *bpp=Greyscale* fps=%.3f\nAudio:"), cfile->frames,
4068                                 cfile->type, cfile->hsize, cfile->vsize, cfile->fps);
4069   } else {
4070     if (cfile->bpp != 32) cfile->bpp = 24; // assume RGB24  *** TODO - check
4071     mesg1 = lives_strdup_printf(_("Frames=%d type=%s size=%dx%d bpp=%d fps=%.3f\nAudio:"), cfile->frames,
4072                                 cfile->type, cfile->hsize, cfile->vsize, cfile->bpp, cfile->fps);
4073   }
4074 
4075   if (cfile->achans == 0) {
4076     mesg = lives_strdup_printf(_("%s none\n"), mesg1);
4077   } else {
4078     mesg = lives_strdup_printf(P_("%s %d Hz %d channel %d bps\n", "%s %d Hz %d channels %d bps\n", cfile->achans),
4079                                mesg1, cfile->arate, cfile->achans, cfile->asampsize);
4080   }
4081   d_print(mesg);
4082   lives_free(mesg1);
4083   lives_free(mesg);
4084 
4085   // get the author,title,comments
4086   if (*cfile->author) {
4087     d_print(_(" - Author: %s\n"), cfile->author);
4088   }
4089   if (*cfile->title) {
4090     d_print(_(" - Title: %s\n"), cfile->title);
4091   }
4092   if (*cfile->comment) {
4093     d_print(_(" - Comment: %s\n"), cfile->comment);
4094   }
4095 
4096   return TRUE;
4097 }
4098 
4099 
4100 boolean save_file_comments(int fileno) {
4101   // save the comments etc for smogrify
4102   int retval;
4103   int comment_fd;
4104   char *comment_file = lives_strdup_printf("%s/%s/.comment", prefs->workdir, cfile->handle);
4105   lives_clip_t *sfile = mainw->files[fileno];
4106 
4107   lives_rm(comment_file);
4108 
4109   do {
4110     retval = 0;
4111     comment_fd = creat(comment_file, S_IRUSR | S_IWUSR);
4112     if (comment_fd < 0) {
4113       THREADVAR(write_failed) = TRUE;
4114       retval = do_write_failed_error_s_with_retry(comment_file, lives_strerror(errno));
4115     } else {
4116       THREADVAR(write_failed) = FALSE;
4117       lives_write(comment_fd, sfile->title, strlen(sfile->title), TRUE);
4118       lives_write(comment_fd, "||%", 3, TRUE);
4119       lives_write(comment_fd, sfile->author, strlen(sfile->author), TRUE);
4120       lives_write(comment_fd, "||%", 3, TRUE);
4121       lives_write(comment_fd, sfile->comment, strlen(sfile->comment), TRUE);
4122 
4123       close(comment_fd);
4124 
4125       if (THREADVAR(write_failed)) {
4126         retval = do_write_failed_error_s_with_retry(comment_file, NULL);
4127       }
4128     }
4129   } while (retval == LIVES_RESPONSE_RETRY);
4130 
4131   lives_free(comment_file);
4132 
4133   if (THREADVAR(write_failed)) return FALSE;
4134 
4135   return TRUE;
4136 }
4137 
4138 
4139 void wait_for_stop(const char *stop_command) {
4140   FILE *infofile;
4141 
4142   // only used for audio player mplayer or audio player sox
4143 
4144 # define SECOND_STOP_TIME 0.1
4145 # define STOP_GIVE_UP_TIME 1.0
4146 
4147   double time_waited = 0.;
4148   boolean sent_second_stop = FALSE;
4149 
4150   // send another stop if necessary
4151   while (!(infofile = fopen(cfile->info_file, "r"))) {
4152     lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
4153     lives_usleep(prefs->sleep_time);
4154     time_waited += 1000000. / prefs->sleep_time;
4155     if (time_waited > SECOND_STOP_TIME && !sent_second_stop) {
4156       lives_system(stop_command, TRUE);
4157       sent_second_stop = TRUE;
4158     }
4159 
4160     if (time_waited > STOP_GIVE_UP_TIME) {
4161       // give up waiting, but send a last try...
4162       lives_system(stop_command, TRUE);
4163       break;
4164     }
4165   }
4166   if (infofile) fclose(infofile);
4167 }
4168 
4169 
4170 boolean save_frame_inner(int clip, int frame, const char *file_name, int width, int height, boolean from_osc) {
4171   // save 1 frame as an image
4172   // width==-1, height==-1 to use "natural" values
4173   LiVESResponseType resp;
4174   lives_clip_t *sfile = mainw->files[clip];
4175   char full_file_name[PATH_MAX];
4176   char *com, *tmp;
4177 
4178   boolean allow_over = FALSE;
4179 
4180   if (!from_osc && strrchr(file_name, '.') == NULL) {
4181     lives_snprintf(full_file_name, PATH_MAX, "%s.%s", file_name,
4182                    get_image_ext_for_type(sfile->img_type));
4183   } else {
4184     lives_snprintf(full_file_name, PATH_MAX, "%s", file_name);
4185     if (!allow_over) allow_over = TRUE;
4186   }
4187 
4188   // TODO - allow overwriting in sandbox
4189   if (from_osc && lives_file_test(full_file_name, LIVES_FILE_TEST_EXISTS)) return FALSE;
4190 
4191   tmp = lives_filename_from_utf8(full_file_name, -1, NULL, NULL, NULL);
4192 
4193   if (!mainw->multitrack) {
4194     d_print(_("Saving frame %d as %s..."), frame, full_file_name);
4195 
4196     if (sfile->clip_type == CLIP_TYPE_FILE) {
4197       frames_t res = virtual_to_images(clip, frame, frame, FALSE, NULL);
4198       if (res <= 0) {
4199         d_print_file_error_failed();
4200         return FALSE;
4201       }
4202     }
4203 
4204     do {
4205       resp = LIVES_RESPONSE_NONE;
4206 
4207       com = lives_strdup_printf("%s save_frame %s %d \"%s\" %d %d", prefs->backend_sync, sfile->handle,
4208                                 frame, tmp, width, height);
4209       lives_system(com, FALSE);
4210       lives_free(com);
4211 
4212       if (THREADVAR(write_failed)) {
4213         THREADVAR(write_failed) = 0;
4214         d_print_file_error_failed();
4215         resp = do_file_perm_error(tmp, TRUE);
4216         if (resp == LIVES_RESPONSE_CANCEL) {
4217           lives_free(tmp);
4218           return FALSE;
4219         }
4220       }
4221       if (!THREADVAR(com_failed)) {
4222         lives_free(tmp);
4223         d_print_done();
4224         return TRUE;
4225       }
4226     } while (resp == LIVES_RESPONSE_RETRY);
4227   } else {
4228     // multitrack mode
4229     LiVESError *gerr = NULL;
4230     LiVESPixbuf *pixbuf;
4231     int retval;
4232 
4233     mt_show_current_frame(mainw->multitrack, TRUE);
4234     resize_layer(mainw->frame_layer, sfile->hsize, sfile->vsize, LIVES_INTERP_BEST, WEED_PALETTE_RGB24, 0);
4235     convert_layer_palette(mainw->frame_layer, WEED_PALETTE_RGB24, 0);
4236     weed_set_int_value(mainw->frame_layer, WEED_LEAF_GAMMA_TYPE, WEED_GAMMA_SRGB);
4237     pixbuf = layer_to_pixbuf(mainw->frame_layer, TRUE, FALSE);
4238     weed_plant_free(mainw->frame_layer);
4239     mainw->frame_layer = NULL;
4240 
4241     do {
4242       retval = 0;
4243       if (sfile->img_type == IMG_TYPE_JPEG) lives_pixbuf_save(pixbuf, tmp, IMG_TYPE_JPEG, 100,
4244             sfile->hsize, sfile->vsize, &gerr);
4245       else if (sfile->img_type == IMG_TYPE_PNG) lives_pixbuf_save(pixbuf, tmp, IMG_TYPE_PNG, 100,
4246             sfile->hsize, sfile->vsize, &gerr);
4247 
4248       if (gerr) {
4249         retval = do_write_failed_error_s_with_retry(full_file_name, gerr->message);
4250         lives_error_free(gerr);
4251         gerr = NULL;
4252       }
4253     } while (retval == LIVES_RESPONSE_RETRY);
4254 
4255     free(tmp);
4256     lives_widget_object_unref(pixbuf);
4257   }
4258 
4259   // some other error condition
4260   return FALSE;
4261 }
4262 
4263 
4264 void backup_file(int clip, int start, int end, const char *file_name) {
4265   lives_clip_t *sfile = mainw->files[clip];
4266   char **array;
4267 
4268   char *title;
4269   char full_file_name[PATH_MAX];
4270 
4271   char *com, *tmp;
4272 
4273   boolean with_perf = FALSE;
4274   boolean retval, allow_over;
4275 
4276   int withsound = 1;
4277   int current_file = mainw->current_file;
4278 
4279   if (strrchr(file_name, '.') == NULL) {
4280     lives_snprintf(full_file_name, PATH_MAX, "%s.%s", file_name, LIVES_FILE_EXT_BACKUP);
4281     allow_over = FALSE;
4282   } else {
4283     lives_snprintf(full_file_name, PATH_MAX, "%s", file_name);
4284     allow_over = TRUE;
4285   }
4286 
4287   // check if file exists
4288   if (!check_file(full_file_name, allow_over)) return;
4289 
4290   // create header files
4291   retval = write_headers(sfile); // for pre LiVES 0.9.6
4292   retval = save_clip_values(clip); // new style (0.9.6+)
4293 
4294   if (!retval) return;
4295 
4296   //...and backup
4297   title = get_menu_name(sfile, FALSE);
4298   d_print(_("Backing up %s to %s"), title, full_file_name);
4299   lives_free(title);
4300 
4301   if (!mainw->save_with_sound) {
4302     d_print(_(" without sound"));
4303     withsound = 0;
4304   }
4305 
4306   d_print("...");
4307   cfile->progress_start = 1;
4308   cfile->progress_end = sfile->frames;
4309 
4310   if (sfile->clip_type == CLIP_TYPE_FILE) {
4311     frames_t ret;
4312     char *msg = (_("Pulling frames from clip..."));
4313     if ((ret = realize_all_frames(clip, msg, FALSE)) < cfile->frames) {
4314       lives_free(msg);
4315       cfile->nopreview = FALSE;
4316       if (ret > 0) d_print_cancelled();
4317       return;
4318     }
4319     lives_free(msg);
4320   }
4321 
4322   com = lives_strdup_printf("%s backup %s %d %d %d %s", prefs->backend, sfile->handle, withsound,
4323                             start, end, (tmp = lives_filename_from_utf8(full_file_name, -1, NULL, NULL, NULL)));
4324   lives_free(tmp);
4325 
4326   // TODO
4327   mainw->current_file = clip;
4328 
4329   lives_rm(cfile->info_file);
4330   cfile->nopreview = TRUE;
4331   lives_system(com, FALSE);
4332   lives_free(com);
4333 
4334   if (THREADVAR(com_failed)) {
4335     THREADVAR(com_failed) = FALSE;
4336     mainw->current_file = current_file;
4337     return;
4338   }
4339 
4340   if (!(do_progress_dialog(TRUE, TRUE, _("Backing up"))) || mainw->error) {
4341     if (mainw->error) {
4342       d_print_failed();
4343     }
4344 
4345     // cancelled - clear up files
4346     cfile->nopreview = FALSE;
4347 
4348     // using restore details in the 'wrong' way here...it will also clear files
4349     com = lives_strdup_printf("%s restore_details %s", prefs->backend, cfile->handle);
4350     lives_popen(com, FALSE, mainw->msg, MAINW_MSG_SIZE);
4351     lives_free(com);
4352 
4353     //save_clip_values(mainw->current_file);
4354     mainw->current_file = current_file;
4355     return;
4356   }
4357 
4358   cfile->nopreview = FALSE;
4359 
4360   mainw->current_file = current_file;
4361 
4362   if (mainw->error) {
4363     widget_opts.non_modal = TRUE;
4364     do_error_dialog(mainw->msg);
4365     widget_opts.non_modal = FALSE;
4366     d_print_failed();
4367     return;
4368   }
4369 
4370   if (with_perf) {
4371     d_print(_("performance data was backed up..."));
4372   }
4373 
4374   array = lives_strsplit(mainw->msg, "|", 3);
4375   sfile->f_size = strtol(array[1], NULL, 10);
4376   lives_strfreev(array);
4377 
4378   lives_snprintf(sfile->file_name, PATH_MAX, "%s", full_file_name);
4379   if (!sfile->was_renamed) {
4380     lives_snprintf(sfile->name, CLIP_NAME_MAXLEN, "%s", full_file_name);
4381     set_main_title(cfile->name, 0);
4382     lives_menu_item_set_text(sfile->menuentry, full_file_name, FALSE);
4383   }
4384   if (prefs->show_recent)
4385     add_to_recent(full_file_name, 0., 0, NULL);
4386 
4387   sfile->changed = FALSE;
4388   // set is_untitled to stop users from saving with a .lv1 extension
4389   sfile->is_untitled = TRUE;
4390   d_print_done();
4391 }
4392 
4393 
4394 boolean write_headers(lives_clip_t *file) {
4395   // this function is included only for backwards compatibility with ancient builds of LiVES
4396   //
4397 
4398   int retval;
4399   int header_fd;
4400   char *hdrfile;
4401 
4402   // save the file details
4403   hdrfile = lives_build_filename(prefs->workdir, file->handle, LIVES_CLIP_HEADER_OLD, NULL);
4404 
4405   do {
4406     retval = 0;
4407     header_fd = creat(hdrfile, S_IRUSR | S_IWUSR);
4408     if (header_fd < 0) {
4409       retval = do_write_failed_error_s_with_retry(hdrfile, lives_strerror(errno));
4410     } else {
4411       THREADVAR(write_failed) = FALSE;
4412 
4413       lives_write_le(header_fd, &cfile->bpp, 4, TRUE);
4414       lives_write_le(header_fd, &cfile->fps, 8, TRUE);
4415       lives_write_le(header_fd, &cfile->hsize, 4, TRUE);
4416       lives_write_le(header_fd, &cfile->vsize, 4, TRUE);
4417       lives_write_le(header_fd, &cfile->arps, 4, TRUE);
4418       lives_write_le(header_fd, &cfile->signed_endian, 4, TRUE);
4419       lives_write_le(header_fd, &cfile->arate, 4, TRUE);
4420       lives_write_le(header_fd, &cfile->unique_id, 8, TRUE);
4421       lives_write_le(header_fd, &cfile->achans, 4, TRUE);
4422       lives_write_le(header_fd, &cfile->asampsize, 4, TRUE);
4423 
4424       lives_write(header_fd, LiVES_VERSION, strlen(LiVES_VERSION), TRUE);
4425       close(header_fd);
4426 
4427       if (THREADVAR(write_failed)) retval = do_write_failed_error_s_with_retry(hdrfile, NULL);
4428     }
4429   } while (retval == LIVES_RESPONSE_RETRY);
4430 
4431   lives_free(hdrfile);
4432 
4433   if (retval != LIVES_RESPONSE_CANCEL) {
4434     // more file details (since version 0.7.5)
4435     hdrfile = lives_build_filename(prefs->workdir, file->handle, LIVES_CLIP_HEADER_OLD2, NULL);
4436 
4437     do {
4438       retval = 0;
4439       header_fd = creat(hdrfile, S_IRUSR | S_IWUSR);
4440 
4441       if (header_fd < 0) {
4442         retval = do_write_failed_error_s_with_retry(hdrfile, lives_strerror(errno));
4443       } else {
4444         THREADVAR(write_failed) = FALSE;
4445         lives_write_le(header_fd, &file->frames, 4, TRUE);
4446         lives_write(header_fd, &file->title, 1024, TRUE);
4447         lives_write(header_fd, &file->author, 1024, TRUE);
4448         lives_write(header_fd, &file->comment, 1024, TRUE);
4449         close(header_fd);
4450       }
4451       if (THREADVAR(write_failed)) retval = do_write_failed_error_s_with_retry(hdrfile, NULL);
4452     } while (retval == LIVES_RESPONSE_RETRY);
4453 
4454     lives_free(hdrfile);
4455   }
4456 
4457   if (retval == LIVES_RESPONSE_CANCEL) {
4458     THREADVAR(write_failed) = FALSE;
4459     return FALSE;
4460   }
4461   return TRUE;
4462 }
4463 
4464 
4465 boolean read_headers(int fileno, const char *dir, const char *file_name) {
4466   // file_name is only used to get the file size on the disk
4467   lives_clip_t *sfile;
4468   char **array;
4469   char buff[1024];
4470   char version[32];
4471   char *com, *tmp;
4472   char *old_hdrfile, *lives_header = NULL;
4473 
4474   off_t header_size;
4475   int version_hash;
4476   int pieces;
4477   int header_fd;
4478   int retval2;
4479   int asigned = 0, aendian = LIVES_LITTLE_ENDIAN;
4480 
4481   lives_clip_details_t detail;
4482 
4483   boolean retval, retvala;
4484   boolean is_ascrap = FALSE;
4485 
4486   off_t sizhead = 28; //8 * 4 + 8 + 8;
4487 
4488   time_t old_time = 0, new_time = 1;
4489   struct stat mystat;
4490 
4491   if (!IS_VALID_CLIP(fileno)) return FALSE;
4492 
4493   if (mainw->hdrs_cache) cached_list_free(&mainw->hdrs_cache);
4494 
4495   sfile = mainw->files[fileno];
4496 
4497   old_hdrfile = lives_build_filename(dir, LIVES_CLIP_HEADER_OLD, NULL);
4498 
4499   if (fileno == mainw->ascrap_file) {
4500     is_ascrap = TRUE;
4501     /// ascrap_file now uses a different header name; this is to facilitate diskspace cleanup
4502     /// otherwise it may be wrongly classified as a recoverable clip
4503     lives_header = lives_build_filename(dir, LIVES_ACLIP_HEADER, NULL);
4504     if (!lives_file_test(lives_header, LIVES_FILE_TEST_EXISTS)) {
4505       lives_free(lives_header);
4506       lives_header = NULL;
4507     }
4508   }
4509   if (!lives_header) lives_header = lives_build_filename(dir, LIVES_CLIP_HEADER, NULL);
4510 
4511   sfile->checked_for_old_header = TRUE;
4512   sfile->img_type = IMG_TYPE_UNKNOWN;
4513 
4514   if (lives_file_test(lives_header, LIVES_FILE_TEST_EXISTS)) {
4515     do {
4516       retval2 = LIVES_RESPONSE_OK;
4517       if (!(mainw->hdrs_cache = cache_file_contents(lives_header))) {
4518         if (fileno != mainw->current_file) goto rhd_failed;
4519         retval2 = do_read_failed_error_s_with_retry(lives_header, NULL);
4520       }
4521     } while (retval2 == LIVES_RESPONSE_RETRY);
4522 
4523     if (retval2 == LIVES_RESPONSE_CANCEL) {
4524       goto rhd_failed;
4525     }
4526 
4527     if (fileno == mainw->current_file) {
4528       threaded_dialog_spin(0.);
4529     }
4530 
4531     if (!is_ascrap) restore_clip_binfmt(fileno);
4532 
4533     do {
4534       do {
4535         detail = CLIP_DETAILS_HEADER_VERSION;
4536         retval = get_clip_value(fileno, detail, &sfile->header_version, 16);
4537         if (retval) {
4538           if (sfile->header_version < 100) goto old_check;
4539         } else {
4540           if (lives_file_test(old_hdrfile, LIVES_FILE_TEST_EXISTS)) {
4541             goto old_check;
4542           }
4543           if (fileno != mainw->current_file) {
4544             goto rhd_failed;
4545           }
4546           if (mainw->hdrs_cache) {
4547             retval2 = do_header_missing_detail_error(fileno, CLIP_DETAILS_HEADER_VERSION);
4548           } else {
4549             retval2 = do_header_read_error_with_retry(fileno);
4550           }
4551         }
4552       } while (retval2 == LIVES_RESPONSE_RETRY);
4553 
4554       if (retval2 == LIVES_RESPONSE_CANCEL) goto rhd_failed;
4555 
4556       if (is_ascrap) goto get_avals;
4557 
4558       detail = CLIP_DETAILS_FRAMES;
4559       retval = get_clip_value(fileno, detail, &sfile->frames, 0);
4560 
4561       if (retval) {
4562         detail = CLIP_DETAILS_BPP;
4563         retval = get_clip_value(fileno, detail, &sfile->bpp, 0);
4564       }
4565       if (retval) {
4566         detail = CLIP_DETAILS_FPS;
4567         retval = get_clip_value(fileno, detail, &sfile->fps, 0);
4568       }
4569       if (retval) {
4570         detail = CLIP_DETAILS_PB_FPS;
4571         retval = get_clip_value(fileno, detail, &sfile->pb_fps, 0);
4572         if (!retval) {
4573           retval = TRUE;
4574           sfile->pb_fps = sfile->fps;
4575         }
4576       }
4577       if (retval) {
4578         retval = get_clip_value(fileno, CLIP_DETAILS_PB_FRAMENO, &sfile->frameno, 0);
4579         if (!retval) {
4580           retval = TRUE;
4581           sfile->frameno = 1;
4582         }
4583         if (sfile->frameno <= 0) sfile->frameno = 1;
4584       }
4585       if (retval) {
4586         detail = CLIP_DETAILS_WIDTH;
4587         retval = get_clip_value(fileno, detail, &sfile->hsize, 0);
4588       }
4589       if (retval) {
4590         detail = CLIP_DETAILS_HEIGHT;
4591         retval = get_clip_value(fileno, detail, &sfile->vsize, 0);
4592       }
4593       if (retval) {
4594         if (sfile->header_version > 100) {
4595           detail = CLIP_DETAILS_GAMMA_TYPE;
4596           get_clip_value(fileno, detail, &sfile->gamma_type, 0);
4597           if (sfile->gamma_type == 0) sfile->gamma_type = WEED_GAMMA_SRGB;
4598           if (sfile->gamma_type != WEED_GAMMA_SRGB) {
4599             if (!do_gamma_import_warn(sfile->has_binfmt ?
4600                                       sfile->binfmt_version.num : 0, sfile->gamma_type)) goto rhd_failed;
4601           }
4602         }
4603       }
4604       if (retval) {
4605         detail = CLIP_DETAILS_CLIPNAME;
4606         get_clip_value(fileno, detail, sfile->name, CLIP_NAME_MAXLEN);
4607       }
4608       if (retval) {
4609         detail = CLIP_DETAILS_FILENAME;
4610         get_clip_value(fileno, detail, sfile->file_name, PATH_MAX);
4611       }
4612 
4613 get_avals:
4614       if (retval) {
4615         detail = CLIP_DETAILS_ACHANS;
4616         retvala = get_clip_value(fileno, detail, &sfile->achans, 0);
4617         if (!retvala) sfile->achans = 0;
4618       }
4619 
4620       if (sfile->achans == 0) retvala = FALSE;
4621       else retvala = TRUE;
4622 
4623       if (retval && retvala) {
4624         detail = CLIP_DETAILS_ARATE;
4625         retvala = get_clip_value(fileno, detail, &sfile->arps, 0);
4626       }
4627 
4628       if (!retvala) sfile->arps = sfile->achans = sfile->arate = sfile->asampsize = 0;
4629       if (sfile->arps == 0) retvala = FALSE;
4630 
4631       if (retvala && retval) {
4632         detail = CLIP_DETAILS_PB_ARATE;
4633         retvala = get_clip_value(fileno, detail, &sfile->arate, 0);
4634         if (!retvala) {
4635           retvala = TRUE;
4636           sfile->arate = sfile->arps;
4637         }
4638       }
4639       if (retvala && retval) {
4640         detail = CLIP_DETAILS_ASIGNED;
4641         retval = get_clip_value(fileno, detail, &asigned, 0);
4642       }
4643       if (retvala && retval) {
4644         detail = CLIP_DETAILS_AENDIAN;
4645         retval = get_clip_value(fileno, detail, &aendian, 0);
4646       }
4647 
4648       sfile->signed_endian = asigned + aendian;
4649 
4650       if (retvala && retval) {
4651         detail = CLIP_DETAILS_ASAMPS;
4652         retval = get_clip_value(fileno, detail, &sfile->asampsize, 0);
4653       }
4654       if (!retval) {
4655         if (fileno != mainw->current_file) goto rhd_failed;
4656         if (mainw->hdrs_cache) {
4657           retval2 = do_header_missing_detail_error(fileno, detail);
4658         } else {
4659           retval2 = do_header_read_error_with_retry(fileno);
4660         }
4661       } else {
4662         if (!is_ascrap) {
4663           get_clip_value(fileno, CLIP_DETAILS_TITLE, sfile->title, 1024);
4664           get_clip_value(fileno, CLIP_DETAILS_AUTHOR, sfile->author, 1024);
4665           get_clip_value(fileno, CLIP_DETAILS_COMMENT, sfile->comment, 1024);
4666           get_clip_value(fileno, CLIP_DETAILS_KEYWORDS, sfile->keywords, 1024);
4667           get_clip_value(fileno, CLIP_DETAILS_INTERLACE, &sfile->interlace, 0);
4668           // user must have selected this:
4669           if (sfile->interlace != LIVES_INTERLACE_NONE) sfile->deinterlace = TRUE;
4670         }
4671         lives_free(old_hdrfile);
4672         lives_free(lives_header);
4673         if (!prefs->vj_mode) {
4674           sfile->afilesize = reget_afilesize_inner(fileno);
4675         }
4676         /// need to maintain mainw->hdrs_cache in this case, as it may be
4677         // passed to further functions, but it needs to be freed and set to NULL
4678         // at some point
4679         return TRUE;
4680       }
4681     } while (retval2 == LIVES_RESPONSE_RETRY);
4682     goto rhd_failed;
4683   }
4684 
4685 old_check:
4686 
4687   if (lives_file_test(old_hdrfile, LIVES_FILE_TEST_EXISTS)) {
4688     sfile->has_old_header = TRUE;
4689     if (!stat(old_hdrfile, &mystat)) old_time = mystat.st_mtime;
4690     if (!stat(lives_header, &mystat)) new_time = mystat.st_mtime;
4691   }
4692 
4693   lives_free(lives_header);
4694   lives_header = NULL;
4695   ///////////////
4696 
4697   if (sfile->has_old_header && old_time <= new_time) {
4698     retval2 = LIVES_RESPONSE_OK;
4699     detail = CLIP_DETAILS_FRAMES;
4700 
4701     if (get_clip_value(fileno, detail, &sfile->frames, 0)) {
4702       char *tmp;
4703 
4704       // use new style header (LiVES 0.9.6+)
4705       // clean up and get file sizes
4706       if (file_name) {
4707         com = lives_strdup_printf("%s restore_details \"%s\" \"%s\" 0",
4708                                   prefs->backend_sync, sfile->handle,
4709                                   (tmp = lives_filename_from_utf8(file_name, -1, NULL, NULL, NULL)));
4710         lives_free(tmp);
4711       } else
4712         com = lives_strdup_printf("%s restore_details \"%s\" . 1", prefs->backend_sync, sfile->handle);
4713 
4714       lives_popen(com, fileno != mainw->current_file, buff, 1024);
4715       lives_free(com);
4716 
4717       if (THREADVAR(com_failed)) {
4718         THREADVAR(com_failed) = FALSE;
4719         goto rhd_failed;
4720       }
4721 
4722       pieces = get_token_count(buff, '|');
4723 
4724       if (pieces > 3) {
4725         array = lives_strsplit(buff, "|", pieces);
4726         sfile->f_size = strtol(array[1], NULL, 10);
4727         sfile->afilesize = strtol(array[2], NULL, 10);
4728         if (sfile->clip_type == CLIP_TYPE_DISK) {
4729           if (!strcmp(array[3], LIVES_FILE_EXT_JPG)) sfile->img_type = IMG_TYPE_JPEG;
4730           else sfile->img_type = IMG_TYPE_PNG;
4731         }
4732         lives_strfreev(array);
4733       }
4734       if (fileno == mainw->current_file) threaded_dialog_spin(0.);
4735     } else goto rhd_failed;
4736     lives_free(old_hdrfile);
4737     /// mainw->hdrs_cache never set
4738     return TRUE;
4739   }
4740 
4741   do {
4742     // old style headers (pre 0.9.6)
4743     retval = LIVES_RESPONSE_OK;
4744     THREADVAR(read_failed) = FALSE;
4745     lives_memset(version, 0, 32);
4746     lives_memset(buff, 0, 1024);
4747 
4748     header_fd = lives_open2(old_hdrfile, O_RDONLY);
4749 
4750     if (header_fd < 0) {
4751       if (fileno != mainw->current_file) {
4752         goto rhd_failed;
4753       }
4754       retval = do_read_failed_error_s_with_retry(old_hdrfile, lives_strerror(errno));
4755     } else {
4756       THREADVAR(read_failed) = FALSE;
4757       header_size = get_file_size(header_fd);
4758 
4759       if (header_size < sizhead) {
4760         close(header_fd);
4761         goto rhd_failed;
4762       } else {
4763         THREADVAR(read_failed) = FALSE;
4764         lives_read_le(header_fd, &sfile->fps, 4, FALSE);
4765         if (!THREADVAR(read_failed))
4766           lives_read_le(header_fd, &sfile->bpp, 8, FALSE);
4767         if (!THREADVAR(read_failed))
4768           lives_read_le(header_fd, &sfile->hsize, 4, FALSE);
4769         if (!THREADVAR(read_failed))
4770           lives_read_le(header_fd, &sfile->vsize, 4, FALSE);
4771         if (!THREADVAR(read_failed))
4772           lives_read_le(header_fd, &sfile->arps, 4, FALSE);
4773         if (!THREADVAR(read_failed))
4774           lives_read_le(header_fd, &sfile->signed_endian, 4, FALSE);
4775         if (!THREADVAR(read_failed))
4776           lives_read_le(header_fd, &sfile->arate, 4, FALSE);
4777         if (!THREADVAR(read_failed))
4778           lives_read_le(header_fd, &sfile->unique_id, 8, FALSE);
4779         if (!THREADVAR(read_failed))
4780           lives_read_le(header_fd, &sfile->achans, 4, FALSE);
4781         if (!THREADVAR(read_failed))
4782           lives_read_le(header_fd, &sfile->asampsize, 4, FALSE);
4783 
4784         if (header_size > sizhead) {
4785           if (header_size - sizhead > 31) {
4786             if (!THREADVAR(read_failed))
4787               lives_read(header_fd, &version, 31, FALSE);
4788             version[31] = '\0';
4789           } else {
4790             if (!THREADVAR(read_failed))
4791               lives_read(header_fd, &version, header_size - sizhead, FALSE);
4792             version[header_size - sizhead] = '\0';
4793           }
4794         }
4795       }
4796       close(header_fd);
4797     }
4798 
4799     if (THREADVAR(read_failed)) {
4800       if (fileno != mainw->current_file) goto rhd_failed;
4801       retval = do_read_failed_error_s_with_retry(old_hdrfile, NULL);
4802       if (retval == LIVES_RESPONSE_CANCEL) goto rhd_failed;
4803     }
4804   } while (retval == LIVES_RESPONSE_RETRY);
4805 
4806   lives_freep((void **)&old_hdrfile);
4807 
4808   if (retval == LIVES_RESPONSE_CANCEL) goto rhd_failed;
4809 
4810   // handle version changes
4811   version_hash = verhash(version);
4812   if (version_hash < 7001) {
4813     sfile->arps = sfile->arate;
4814     sfile->signed_endian = mainw->endian;
4815   }
4816 
4817   com = lives_strdup_printf("%s restore_details %s %s %d", prefs->backend_sync, sfile->handle,
4818                             (tmp = lives_filename_from_utf8(file_name, -1, NULL, NULL, NULL)),
4819                             !strcmp(file_name, "."));
4820 
4821   lives_popen(com, FALSE, buff, 1024);
4822   lives_free(com);
4823   lives_free(tmp);
4824 
4825   if (THREADVAR(com_failed)) {
4826     THREADVAR(com_failed) = FALSE;
4827     goto rhd_failed;
4828   }
4829 
4830   pieces = get_token_count(buff, '|');
4831   array = lives_strsplit(buff, "|", pieces);
4832   sfile->f_size = strtol(array[1], NULL, 10);
4833   sfile->afilesize = strtol(array[2], NULL, 10);
4834 
4835   if (sfile->clip_type == CLIP_TYPE_DISK) {
4836     if (!strcmp(array[3], LIVES_FILE_EXT_JPG)) sfile->img_type = IMG_TYPE_JPEG;
4837     else sfile->img_type = IMG_TYPE_PNG;
4838   }
4839 
4840   sfile->frames = atoi(array[4]);
4841 
4842   sfile->bpp = (sfile->img_type == IMG_TYPE_JPEG) ? 24 : 32;
4843 
4844   if (pieces > 4 && array[5]) {
4845     lives_snprintf(sfile->title, 1024, "%s", lives_strstrip(array[4]));
4846   }
4847   if (pieces > 5 && array[6]) {
4848     lives_snprintf(sfile->author, 1024, "%s", lives_strstrip(array[5]));
4849   }
4850   if (pieces > 6 && array[7]) {
4851     lives_snprintf(sfile->comment, 1024, "%s", lives_strstrip(array[6]));
4852   }
4853 
4854   lives_strfreev(array);
4855   return TRUE;
4856 
4857 rhd_failed:
4858   lives_freep((void **)&lives_header);
4859   lives_freep((void **)&old_hdrfile);
4860   return FALSE;
4861 }
4862 
4863 
4864 void open_set_file(int clipnum) {
4865   char name[CLIP_NAME_MAXLEN];
4866 
4867   if (mainw->current_file < 1) return;
4868 
4869   lives_memset(name, 0, CLIP_NAME_MAXLEN);
4870 
4871   if (mainw->hdrs_cache) {
4872     boolean retval;
4873     // LiVES 0.9.6+
4874 
4875     retval = get_clip_value(mainw->current_file, CLIP_DETAILS_PB_FPS, &cfile->pb_fps, 0);
4876     if (!retval) {
4877       cfile->pb_fps = cfile->fps;
4878     }
4879     retval = get_clip_value(mainw->current_file, CLIP_DETAILS_PB_FRAMENO, &cfile->frameno, 0);
4880     if (!retval) {
4881       cfile->frameno = 1;
4882     }
4883 
4884     retval = get_clip_value(mainw->current_file, CLIP_DETAILS_CLIPNAME, name, CLIP_NAME_MAXLEN);
4885     if (!retval) {
4886       char *tmp;
4887       lives_snprintf(name, CLIP_NAME_MAXLEN, "%s", (tmp = get_untitled_name(mainw->untitled_number++)));
4888       lives_free(tmp);
4889       cfile->needs_update = TRUE;
4890     }
4891     retval = get_clip_value(mainw->current_file, CLIP_DETAILS_UNIQUE_ID, &cfile->unique_id, 0);
4892     if (!retval) {
4893       cfile->unique_id = gen_unique_id();
4894       cfile->needs_silent_update = TRUE;
4895     }
4896     retval = get_clip_value(mainw->current_file, CLIP_DETAILS_INTERLACE, &cfile->interlace, 0);
4897     if (!retval) {
4898       cfile->interlace = LIVES_INTERLACE_NONE;
4899       cfile->needs_silent_update = TRUE;
4900     }
4901     if (cfile->interlace != LIVES_INTERLACE_NONE) cfile->deinterlace = TRUE;
4902   } else {
4903     // pre 0.9.6 <- ancient code
4904     ssize_t nlen;
4905     int set_fd;
4906     int pb_fps;
4907     int retval;
4908     char *setfile = lives_strdup_printf("%s/%s/set.%s", prefs->workdir, cfile->handle, mainw->set_name);
4909 
4910     do {
4911       retval = 0;
4912       if ((set_fd = lives_open2(setfile, O_RDONLY)) > -1) {
4913         // get perf_start
4914         if ((nlen = lives_read_le(set_fd, &pb_fps, 4, TRUE)) > 0) {
4915           cfile->pb_fps = pb_fps / 1000.;
4916           lives_read_le(set_fd, &cfile->frameno, 4, TRUE);
4917           lives_read(set_fd, name, CLIP_NAME_MAXLEN, TRUE);
4918         }
4919         close(set_fd);
4920       } else retval = do_read_failed_error_s_with_retry(setfile, lives_strerror(errno));
4921     } while (retval == LIVES_RESPONSE_RETRY);
4922 
4923     lives_free(setfile);
4924     cfile->needs_silent_update = TRUE;
4925   }
4926 
4927   if (!*name) {
4928     lives_snprintf(name, CLIP_NAME_MAXLEN, "set_clip %.3d", clipnum);
4929   } else {
4930     // pre 3.x, files erroneously had the set name appended permanently, so here we undo that
4931     if (lives_string_ends_with(name, " (%s)", mainw->set_name)) {
4932       char *remove = lives_strdup_printf(" (%s)", mainw->set_name);
4933       if (strlen(name) > strlen(remove)) name[strlen(name) - strlen(remove)] = 0;
4934       lives_free(remove);
4935       cfile->needs_silent_update = TRUE;
4936     }
4937     lives_snprintf(cfile->name, CLIP_NAME_MAXLEN, "%s", name);
4938   }
4939 }
4940 
4941 
4942 void reload_subs(int fileno) {
4943   lives_clip_t *sfile;
4944   char *subfname;
4945   if (!IS_VALID_CLIP(fileno)) return;
4946 
4947   sfile = mainw->files[fileno];
4948   subfname = lives_build_filename(prefs->workdir, sfile->handle, SUBS_FILENAME "."
4949                                   LIVES_FILE_EXT_SRT, NULL);
4950   if (lives_file_test(subfname, LIVES_FILE_TEST_EXISTS)) {
4951     subtitles_init(sfile, subfname, SUBTITLE_TYPE_SRT);
4952   } else {
4953     lives_free(subfname);
4954     subfname = lives_build_filename(prefs->workdir, sfile->handle, SUBS_FILENAME "."
4955                                     LIVES_FILE_EXT_SUB, NULL);
4956     if (lives_file_test(subfname, LIVES_FILE_TEST_EXISTS)) {
4957       subtitles_init(sfile, subfname, SUBTITLE_TYPE_SUB);
4958     }
4959   }
4960   lives_free(subfname);
4961 }
4962 
4963 
4964 ulong restore_file(const char *file_name) {
4965   char *com = lives_strdup("dummy");
4966   char *mesg, *mesg1, *tmp;
4967   boolean is_OK = TRUE;
4968   char *fname = lives_strdup(file_name);
4969   char *clipdir;
4970 
4971   int old_file = mainw->current_file, current_file;
4972   int new_file = mainw->first_free_file;
4973   boolean not_cancelled;
4974 
4975   // create a new file
4976   if (!get_new_handle(new_file, fname)) {
4977     return 0;
4978   }
4979 
4980   d_print(_("Restoring %s..."), file_name);
4981 
4982   mainw->current_file = new_file;
4983 
4984   cfile->hsize = mainw->def_width;
4985   cfile->vsize = mainw->def_height;
4986 
4987   if (!mainw->multitrack) {
4988     switch_to_file((mainw->current_file = old_file), new_file);
4989     set_main_title(cfile->file_name, 0);
4990   }
4991 
4992   com = lives_strdup_printf("%s restore %s %s", prefs->backend, cfile->handle,
4993                             (tmp = lives_filename_from_utf8(file_name, -1, NULL, NULL, NULL)));
4994 
4995   lives_rm(cfile->info_file);
4996   lives_system(com, FALSE);
4997   lives_free(tmp);
4998   lives_free(com);
4999 
5000   if (THREADVAR(com_failed)) {
5001     THREADVAR(com_failed) = FALSE;
5002     close_current_file(old_file);
5003     return 0;
5004   }
5005 
5006   cfile->restoring = TRUE;
5007   not_cancelled = do_progress_dialog(TRUE, TRUE, _("Restoring"));
5008   cfile->restoring = FALSE;
5009 
5010   if (mainw->error || !not_cancelled) {
5011     if (mainw->error && mainw->cancelled != CANCEL_ERROR) {
5012       do_error_dialog(mainw->msg);
5013     }
5014     close_current_file(old_file);
5015     return 0;
5016   }
5017 
5018   // call function to return rest of file details
5019   // fsize, afilesize and frames
5020   clipdir = lives_build_path(prefs->workdir, cfile->handle, NULL);
5021   is_OK = read_headers(mainw->current_file, clipdir, file_name);
5022   lives_free(clipdir);
5023   if (mainw->hdrs_cache) cached_list_free(&mainw->hdrs_cache);
5024 
5025   if (!is_OK) {
5026     mesg = lives_strdup_printf(_("\n\nThe file %s is corrupt.\nLiVES was unable to restore it.\n"),
5027                                file_name);
5028     do_error_dialog(mesg);
5029     lives_free(mesg);
5030 
5031     d_print_failed();
5032     close_current_file(old_file);
5033     return 0;
5034   }
5035 
5036   // get img_type, check frame count and size
5037   if (!cfile->checked && !check_clip_integrity(mainw->current_file, NULL, cfile->frames)) {
5038     if (cfile->afilesize == 0) {
5039       reget_afilesize_inner(mainw->current_file);
5040     }
5041     if (!check_frame_count(mainw->current_file, FALSE)) {
5042       cfile->frames = get_frame_count(mainw->current_file, 1);
5043     }
5044   }
5045   cfile->checked = TRUE;
5046 
5047   // add entry to window menu
5048   // TODO - do this earlier and allow switching during restore
5049   add_to_clipmenu();
5050 
5051   if (prefs->show_recent) {
5052     add_to_recent(file_name, 0., 0, NULL);
5053   }
5054 
5055   if (cfile->frames > 0) {
5056     cfile->start = 1;
5057   } else {
5058     cfile->start = 0;
5059   }
5060   cfile->end = cfile->frames;
5061   cfile->arps = cfile->arate;
5062   cfile->pb_fps = cfile->fps;
5063   cfile->opening = FALSE;
5064   cfile->changed = FALSE;
5065 
5066   if (prefs->autoload_subs) {
5067     reload_subs(mainw->current_file);
5068   }
5069 
5070   lives_snprintf(cfile->type, 40, "Frames");
5071   mesg1 = lives_strdup_printf(_("Frames=%d type=%s size=%dx%d bpp=%d fps=%.3f\nAudio:"), cfile->frames, cfile->type,
5072                               cfile->hsize, cfile->vsize, cfile->bpp, cfile->fps);
5073 
5074   if (cfile->afilesize == 0l) {
5075     cfile->achans = 0;
5076     mesg = lives_strdup_printf(_("%s none\n"), mesg1);
5077   } else {
5078     mesg = lives_strdup_printf(P_("%s %d Hz %d channel %d bps\n", "%s %d Hz %d channels %d bps\n", cfile->achans),
5079                                mesg1, cfile->arate, cfile->achans, cfile->asampsize);
5080   }
5081   d_print(mesg);
5082   lives_free(mesg);
5083   lives_free(mesg1);
5084 
5085   cfile->is_loaded = TRUE;
5086   current_file = mainw->current_file;
5087 
5088   // set new bpp
5089   cfile->bpp = (cfile->img_type == IMG_TYPE_JPEG) ? 24 : 32;
5090 
5091   cfile->saved_frameno = cfile->frameno;
5092   if (cfile->frameno > cfile->frames && cfile->frameno > 1) cfile->frameno = cfile->frames;
5093   cfile->last_frameno = cfile->frameno;
5094   cfile->pointer_time = cfile->real_pointer_time = calc_time_from_frame(mainw->current_file, cfile->frameno);
5095   if (cfile->real_pointer_time > CLIP_TOTAL_TIME(mainw->current_file))
5096     cfile->real_pointer_time = CLIP_TOTAL_TIME(mainw->current_file);
5097   if (cfile->pointer_time > cfile->video_time) cfile->pointer_time = 0.;
5098 
5099   if (cfile->achans) {
5100     cfile->aseek_pos = (off64_t)((double)(cfile->real_pointer_time * cfile->arate) * cfile->achans *
5101                                  (cfile->asampsize / 8));
5102     if (cfile->aseek_pos > cfile->afilesize) cfile->aseek_pos = 0.;
5103   }
5104 
5105   if (!save_clip_values(current_file)) {
5106     close_current_file(old_file);
5107     return 0;
5108   }
5109 
5110   if (prefs->crash_recovery) add_to_recovery_file(cfile->handle);
5111 
5112   if (!mainw->multitrack) {
5113     switch_to_file((mainw->current_file = old_file), current_file);
5114   }
5115   lives_notify(LIVES_OSC_NOTIFY_CLIP_OPENED, "");
5116 
5117   return cfile->unique_id;
5118 }
5119 
5120 
5121 int save_event_frames(void) {
5122   // when doing a resample, we save a list of frames for the back end to do
5123   // a reorder
5124 
5125   // here we also update the frame_index for clips of type CLIP_TYPE_FILE
5126 
5127   char *hdrfile = lives_strdup_printf("%s/%s/event.frames", prefs->workdir, cfile->handle);
5128 
5129   int header_fd, i = 0;
5130   int retval;
5131   int perf_start, perf_end;
5132   int nevents;
5133 
5134   if (!cfile->event_list) {
5135     lives_rm(hdrfile);
5136     return -1;
5137   }
5138 
5139   perf_start = (int)(cfile->fps * event_list_get_start_secs(cfile->event_list)) + 1;
5140   perf_end = perf_start + (nevents = count_events(cfile->event_list, FALSE, 0, 0)) - 1;
5141 
5142   if (!event_list_to_block(cfile->event_list, nevents)) return -1;
5143 
5144   if (cfile->frame_index) {
5145     LiVESResponseType response;
5146     int xframes = cfile->frames;
5147     char *what = (_("creating the frame index for resampling "));
5148 
5149     if (cfile->frame_index_back) lives_free(cfile->frame_index_back);
5150     cfile->frame_index_back = cfile->frame_index;
5151     cfile->frame_index = NULL;
5152 
5153     do {
5154       response = LIVES_RESPONSE_OK;
5155       create_frame_index(mainw->current_file, FALSE, 0, nevents);
5156       if (!cfile->frame_index) {
5157         response = do_memory_error_dialog(what, nevents * 4);
5158       }
5159     } while (response == LIVES_RESPONSE_RETRY);
5160     lives_free(what);
5161     if (response == LIVES_RESPONSE_CANCEL) {
5162       cfile->frame_index = cfile->frame_index_back;
5163       cfile->frame_index_back = NULL;
5164       return -1;
5165     }
5166 
5167     for (i = 0; i < nevents; i++) {
5168       cfile->frame_index[i] = cfile->frame_index_back[(cfile->resample_events + i)->value - 1];
5169     }
5170 
5171     cfile->frames = nevents;
5172     if (!check_if_non_virtual(mainw->current_file, 1, cfile->frames)) save_frame_index(mainw->current_file);
5173     cfile->frames = xframes;
5174   }
5175 
5176   do {
5177     retval = 0;
5178     header_fd = creat(hdrfile, S_IRUSR | S_IWUSR);
5179     if (header_fd < 0) {
5180       retval = do_write_failed_error_s_with_retry(hdrfile, lives_strerror(errno));
5181     } else {
5182       // use machine endian.
5183       // When we call "smogrify reorder", we will pass the endianness as 3rd parameter
5184 
5185       THREADVAR(write_failed) = FALSE;
5186       lives_write(header_fd, &perf_start, 4, FALSE);
5187 
5188       if (cfile->resample_events) {
5189         for (i = 0; i <= perf_end - perf_start; i++) {
5190           if (THREADVAR(write_failed)) break;
5191           lives_write(header_fd, &((cfile->resample_events + i)->value), 4, TRUE);
5192         }
5193         lives_freep((void **)&cfile->resample_events);
5194       }
5195 
5196       if (THREADVAR(write_failed)) {
5197         retval = do_write_failed_error_s_with_retry(hdrfile, NULL);
5198       }
5199 
5200       close(header_fd);
5201     }
5202   } while (retval == LIVES_RESPONSE_RETRY);
5203 
5204   if (retval == LIVES_RESPONSE_CANCEL) {
5205     i = -1;
5206   }
5207 
5208   lives_free(hdrfile);
5209   return i;
5210 }
5211 
5212 
5213 /////////////////////////////////////////////////
5214 /// scrap file
5215 ///  the scrap file is used during recording to dump any streamed (non-disk) clips to
5216 /// during render/preview we load frames from the scrap file, but only as necessary
5217 
5218 /// ascrap file
5219 /// this is used to record external audio during playback with record on (if the user requests this)
5220 /// afterwards the audio from it can be rendered/played back
5221 
5222 static double ascrap_mb;  // MB written to audio file
5223 static uint64_t free_mb; // MB free to write
5224 
5225 void add_to_ascrap_mb(uint64_t bytes) {
5226   ascrap_mb += bytes / 1000000.;
5227 }
5228 
5229 
5230 boolean open_scrap_file(void) {
5231   // create a scrap file for recording generated video frames
5232   int current_file = mainw->current_file;
5233   char *dir;
5234   char *handle, *scrap_handle;
5235 
5236   if (!check_for_executable(&capable->has_mktemp, EXEC_MKTEMP)) {
5237     do_program_not_found_error(EXEC_MKTEMP);
5238     return FALSE;
5239   }
5240 
5241   handle = get_worktmp("_scrap");
5242   if (!handle) {
5243     workdir_warning();
5244     return FALSE;
5245   }
5246   if (!create_cfile(-1, handle, FALSE)) {
5247     dir = lives_build_path(prefs->workdir, cfile->handle, NULL);
5248     lives_rmdir(dir, FALSE);
5249     lives_free(dir); lives_free(handle);
5250     return FALSE;
5251   }
5252   lives_free(handle);
5253 
5254   mainw->scrap_file = mainw->current_file;
5255 
5256   lives_snprintf(cfile->type, 40, "scrap");
5257 
5258   scrap_handle = lives_strdup_printf("scrap|%s", cfile->handle);
5259   if (prefs->crash_recovery) add_to_recovery_file(scrap_handle);
5260   lives_free(scrap_handle);
5261 
5262   pthread_mutex_lock(&mainw->clip_list_mutex);
5263   mainw->cliplist = lives_list_append(mainw->cliplist, LIVES_INT_TO_POINTER(mainw->current_file));
5264   pthread_mutex_unlock(&mainw->clip_list_mutex);
5265 
5266   dir = lives_build_path(prefs->workdir, cfile->handle, NULL);
5267   free_mb = (double)get_ds_free(dir) / (double)ONE_MILLION;
5268   lives_free(dir);
5269 
5270   mainw->current_file = current_file;
5271 
5272   if (mainw->ascrap_file == -1) ascrap_mb = 0.;
5273 
5274   return TRUE;
5275 }
5276 
5277 
5278 boolean open_ascrap_file(void) {
5279   // create a scrap file for recording audio
5280   int current_file = mainw->current_file;
5281   char *dir;
5282   char *handle, *ascrap_handle;
5283 
5284   if (!check_for_executable(&capable->has_mktemp, EXEC_MKTEMP)) {
5285     do_program_not_found_error(EXEC_MKTEMP);
5286     return FALSE;
5287   }
5288 
5289   handle = get_worktmp("_ascrap");
5290   if (!handle) {
5291     workdir_warning();
5292     return FALSE;
5293   }
5294   if (!create_cfile(-1, handle, FALSE)) {
5295     dir = lives_build_path(prefs->workdir, cfile->handle, NULL);
5296     lives_rmdir(dir, FALSE);
5297     lives_free(dir); lives_free(handle);
5298     return FALSE;
5299   }
5300   lives_free(handle);
5301 
5302   mainw->ascrap_file = mainw->current_file;
5303   lives_snprintf(cfile->type, 40, "ascrap");
5304 
5305   cfile->opening = FALSE;
5306 
5307   cfile->achans = 2;
5308   cfile->arate = cfile->arps = DEFAULT_AUDIO_RATE;
5309   cfile->asampsize = 16;
5310   cfile->signed_endian = 0; // ???
5311 
5312 #ifdef HAVE_PULSE_AUDIO
5313   if (prefs->audio_player == AUD_PLAYER_PULSE) {
5314     if (prefs->audio_src == AUDIO_SRC_EXT) {
5315       if (mainw->pulsed_read) {
5316         cfile->arate = cfile->arps = mainw->pulsed_read->in_arate;
5317       }
5318     } else {
5319       if (mainw->pulsed) {
5320         cfile->arate = cfile->arps = mainw->pulsed->out_arate;
5321       }
5322     }
5323   }
5324 #endif
5325 
5326 #ifdef ENABLE_JACK
5327   if (prefs->audio_player == AUD_PLAYER_JACK) {
5328     if (prefs->audio_src == AUDIO_SRC_EXT) {
5329       if (mainw->jackd_read) {
5330         cfile->arate = cfile->arps = mainw->jackd_read->sample_in_rate;
5331       }
5332     } else {
5333       if (mainw->jackd) {
5334         cfile->arate = cfile->arps = mainw->jackd->sample_out_rate;
5335       }
5336     }
5337   }
5338 #endif
5339 
5340   ascrap_handle = lives_strdup_printf("ascrap|%s", cfile->handle);
5341   if (prefs->crash_recovery) add_to_recovery_file(ascrap_handle);
5342   lives_free(ascrap_handle);
5343 
5344   pthread_mutex_lock(&mainw->clip_list_mutex);
5345   mainw->cliplist = lives_list_append(mainw->cliplist, LIVES_INT_TO_POINTER(mainw->current_file));
5346   pthread_mutex_unlock(&mainw->clip_list_mutex);
5347 
5348   dir = lives_build_path(prefs->workdir, cfile->handle, NULL);
5349   free_mb = (double)get_ds_free(dir) / (double)ONE_MILLION;
5350   lives_free(dir);
5351 
5352   mainw->current_file = current_file;
5353 
5354   ascrap_mb = 0.;
5355 
5356   return TRUE;
5357 }
5358 
5359 
5360 boolean load_from_scrap_file(weed_layer_t *layer, int frame) {
5361   // load raw frame data from scrap file
5362 
5363   // this will also set cfile width and height - for letterboxing etc.
5364 
5365   // return FALSE if the frame does not exist/we are unable to read it
5366 
5367   char *oname;
5368 
5369   lives_clip_t *scrapfile = mainw->files[mainw->scrap_file];
5370 
5371   int fd;
5372   if (!IS_VALID_CLIP(mainw->scrap_file)) return FALSE;
5373 
5374   if (!scrapfile->ext_src) {
5375     oname = make_image_file_name(scrapfile, 1, LIVES_FILE_EXT_SCRAP);
5376     fd = lives_open_buffered_rdonly(oname);
5377     lives_free(oname);
5378     if (fd < 0) return FALSE;
5379 #ifdef HAVE_POSIX_FADVISE
5380     posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
5381 #endif
5382     scrapfile->ext_src = LIVES_INT_TO_POINTER(fd);
5383     scrapfile->ext_src_type = LIVES_EXT_SRC_FILE_BUFF;
5384   } else fd = LIVES_POINTER_TO_INT(scrapfile->ext_src);
5385 
5386   if (frame < 0 || !layer) return TRUE; /// just open fd
5387 
5388   if (!weed_plant_deserialise(fd, NULL, layer)) {
5389     //g_print("bad scrapfile frame\n");
5390     return FALSE;
5391   }
5392   return TRUE;
5393 }
5394 
5395 
5396 static void ds_warn(boolean freelow, uint64_t bytes) {
5397   char *reason, *aorb;
5398   char *amount = lives_format_storage_space_string(bytes);
5399   if (freelow) {
5400     reason = (_("FREE DISK SPACE"));
5401     aorb = (_("BELOW"));
5402   } else {
5403     reason = (_("DISK SPACE USED"));
5404     aorb = (_("ABOVE"));
5405   }
5406   d_print(_("\nRECORDING was PAUSED because %s in %s IS %s %s !\n"
5407             "Diskspace limits can be set in Preferences / Misc.\n"),
5408           reason, prefs->workdir, aorb, amount);
5409   on_record_perf_activate(NULL, NULL);
5410   d_print_urgency(URGENCY_MSG_TIMEOUT, _("RECORDING WAS PAUSED DUE TO DISKSPACE LIMITS\n"));
5411   lives_free(reason);
5412   lives_free(aorb);
5413 }
5414 
5415 
5416 boolean check_for_disk_space(boolean fullcheck) {
5417   /// fullcheck == FALSE, we MAY check ds used, and we WILL check free ds using cached value
5418   /// fullcheck == TRUE, we WILL update free ds
5419   static int64_t free_ds = -1;
5420   static double xscrap_mb = -1., xascrap_mb = -1.;
5421   static double xxscrap_mb = -1., xxascrap_mb = -1.;
5422   static int64_t ds_used = -1;
5423   static boolean wrtable = FALSE;
5424 
5425   double scrap_mb = 0.;
5426 
5427   if (prefs->disk_quota == 0 && prefs->rec_stop_gb < 0.) return TRUE;
5428 
5429   if (fullcheck) ds_used = -1;
5430 
5431   if (IS_VALID_CLIP(mainw->scrap_file)) {
5432     scrap_mb = (double)mainw->files[mainw->scrap_file]->f_size / (double)ONE_MILLION;
5433   }
5434 
5435   if (prefs->disk_quota > 0) {
5436     int64_t xds_used = -1;
5437 
5438     if ((ds_used = disk_monitor_check_result(prefs->workdir)) > -1) {
5439       xds_used = ds_used;
5440       xxscrap_mb = scrap_mb;
5441       xxascrap_mb = ascrap_mb;
5442     } else {
5443       if (xxscrap_mb == -1. || xxscrap_mb > scrap_mb) xxscrap_mb = scrap_mb;
5444       if (xxascrap_mb == -1. || xxascrap_mb > ascrap_mb) xxascrap_mb = ascrap_mb;
5445       if (ds_used > -1) xds_used = ds_used
5446                                      + (int64_t)(scrap_mb + ascrap_mb - xxscrap_mb - xxascrap_mb)
5447                                      * ONE_MILLION;
5448     }
5449     if (xds_used > -1) {
5450       /// value is in BYTES
5451       if ((uint64_t)xds_used >= prefs->disk_quota * ONE_BILLION) {
5452         if (mainw->record && !mainw->record_paused) {
5453           ds_warn(FALSE, (uint64_t)ds_used);
5454         }
5455         return FALSE;
5456       }
5457     }
5458   }
5459 
5460   // check if we have enough free space left on the volume (return FALSE if not)
5461   if (prefs->rec_stop_gb > -1.) {
5462     // check free space again
5463     if (fullcheck || free_ds == -1 || xscrap_mb == -1 || xascrap_mb == -1
5464         || scrap_mb < xscrap_mb || ascrap_mb < xascrap_mb) {
5465       free_ds = (int64_t)get_ds_free(prefs->workdir);
5466       xscrap_mb = scrap_mb;
5467       xascrap_mb = ascrap_mb;
5468       if (free_ds == 0) wrtable = is_writeable_dir(prefs->workdir);
5469       else wrtable = TRUE;
5470     }
5471     if (wrtable) {
5472       double free_mb = (double)free_ds / (double)ONE_MILLION;
5473       double freesp = free_mb - (scrap_mb + ascrap_mb - xscrap_mb - xascrap_mb);
5474       if ((double)freesp / 1000. < prefs->rec_stop_gb) {
5475         if (mainw->record && !mainw->record_paused) {
5476           ds_warn(TRUE, freesp * ONE_MILLION);
5477         }
5478         return FALSE;
5479       }
5480     }
5481   }
5482   return TRUE;
5483 }
5484 
5485 
5486 static void _save_to_scrap_file(weed_layer_t *layer) {
5487   // returns frame number
5488   // dump the raw layer (frame) data to disk
5489 
5490   // TODO: run as bg thread
5491 
5492   size_t pdata_size;
5493 
5494   lives_clip_t *scrapfile = mainw->files[mainw->scrap_file];
5495 
5496   boolean writeable = TRUE;
5497 
5498   char *framecount;
5499 
5500   //int flags = O_WRONLY | O_CREAT | O_TRUNC;
5501   int fd;
5502 
5503   if (!scrapfile->ext_src) {
5504     char *oname = make_image_file_name(scrapfile, 1, LIVES_FILE_EXT_SCRAP), *dirname;
5505 
5506 #ifdef O_NOATIME
5507     //flags |= O_NOATIME;
5508 #endif
5509 
5510     dirname = lives_build_filename(prefs->workdir, scrapfile->handle, NULL);
5511     lives_mkdir_with_parents(dirname, capable->umask);
5512     lives_free(dirname);
5513 
5514     fd = lives_create_buffered_nosync(oname, DEF_FILE_PERMS);
5515     lives_free(oname);
5516 
5517     if (fd < 0) {
5518       weed_layer_free(layer);
5519       return;
5520     }
5521     scrapfile->ext_src = LIVES_INT_TO_POINTER(fd);
5522     scrapfile->ext_src_type = LIVES_EXT_SRC_FILE_BUFF;
5523 
5524 #ifdef HAVE_POSIX_FADVISE
5525     posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
5526 #endif
5527   } else fd = LIVES_POINTER_TO_INT(scrapfile->ext_src);
5528 
5529 
5530   // serialise entire frame to scrap file
5531   pdata_size = weed_plant_serialise(fd, layer, NULL);
5532   weed_layer_free(layer);
5533 
5534   scrapfile->f_size += pdata_size;
5535 
5536   // check free space every 256 frames or every 10 MB of audio (TODO ****)
5537   if ((scrapfile->frames & 0xFF) == 0) {
5538     char *dir = lives_build_filename(prefs->workdir, scrapfile->handle, NULL);
5539     free_mb = (double)get_ds_free(dir) / 1000000.;
5540     if (free_mb == 0) writeable = is_writeable_dir(dir);
5541     lives_free(dir);
5542   }
5543 
5544   if ((!mainw->fs || (prefs->play_monitor != widget_opts.monitor + 1 && capable->nmonitors > 1)) && !prefs->hide_framebar &&
5545       !mainw->faded) {
5546     double scrap_mb = (double)scrapfile->f_size / 1000000.;
5547     if ((scrap_mb + ascrap_mb) < (double)free_mb * .75) {
5548       // TRANSLATORS: rec(ord) %.2f M(ega)B(ytes)
5549       framecount = lives_strdup_printf(_("rec %.2f MB"), scrap_mb + ascrap_mb);
5550     } else {
5551       // warn if scrap_file > 3/4 of free space
5552       // TRANSLATORS: !rec(ord) %.2f M(ega)B(ytes)
5553       if (writeable)
5554         framecount = lives_strdup_printf(_("!rec %.2f MB"), scrap_mb + ascrap_mb);
5555       else
5556         // TRANSLATORS: rec(ord) ?? M(ega)B(ytes)
5557         framecount = (_("rec ?? MB"));
5558     }
5559     lives_entry_set_text(LIVES_ENTRY(mainw->framecounter), framecount);
5560     lives_free(framecount);
5561   }
5562 
5563   /// check every 64 frames for quota overrun, because its a background task
5564   check_for_disk_space((scrapfile->frames & 0x3F) ? TRUE : FALSE);
5565 }
5566 
5567 static lives_proc_thread_t scrap_file_procthrd = NULL;
5568 
5569 int save_to_scrap_file(weed_layer_t *layer) {
5570   weed_layer_t *orig_layer;
5571   lives_clip_t *scrapfile = mainw->files[mainw->scrap_file];
5572   if (!IS_VALID_CLIP(mainw->scrap_file)) return -1;
5573   if (!layer) return scrapfile->frames;
5574   orig_layer = weed_layer_copy(NULL, layer);
5575   if (scrap_file_procthrd) {
5576     lives_proc_thread_join(scrap_file_procthrd);
5577   }
5578   scrap_file_procthrd = lives_proc_thread_create(LIVES_THRDATTR_NONE,
5579                         (lives_funcptr_t)_save_to_scrap_file, -1, "V", orig_layer);
5580   return scrapfile->frames;
5581 }
5582 
5583 void close_scrap_file(boolean remove) {
5584   int current_file = mainw->current_file;
5585 
5586   if (!IS_VALID_CLIP(mainw->scrap_file)) return;
5587 
5588   if (scrap_file_procthrd) {
5589     lives_proc_thread_join(scrap_file_procthrd);
5590     scrap_file_procthrd = NULL;
5591   }
5592 
5593   mainw->current_file = mainw->scrap_file;
5594   if (cfile->ext_src && cfile->ext_src_type == LIVES_EXT_SRC_FILE_BUFF)
5595     lives_close_buffered(LIVES_POINTER_TO_INT(cfile->ext_src));
5596   cfile->ext_src = NULL;
5597   cfile->ext_src_type = LIVES_EXT_SRC_NONE;
5598 
5599   if (remove) close_temp_handle(current_file);
5600   else mainw->current_file = current_file;
5601 
5602   pthread_mutex_lock(&mainw->clip_list_mutex);
5603   mainw->cliplist = lives_list_remove(mainw->cliplist, LIVES_INT_TO_POINTER(mainw->scrap_file));
5604   pthread_mutex_unlock(&mainw->clip_list_mutex);
5605 
5606   if (prefs->crash_recovery) rewrite_recovery_file();
5607 
5608   mainw->scrap_file = -1;
5609 }
5610 
5611 
5612 void close_ascrap_file(boolean remove) {
5613   int current_file = mainw->current_file;
5614 
5615   if (mainw->ascrap_file == -1) return;
5616 
5617   if (remove) {
5618     mainw->current_file = mainw->ascrap_file;
5619     close_temp_handle(current_file);
5620   }
5621 
5622   pthread_mutex_lock(&mainw->clip_list_mutex);
5623   mainw->cliplist = lives_list_remove(mainw->cliplist, LIVES_INT_TO_POINTER(mainw->ascrap_file));
5624   pthread_mutex_unlock(&mainw->clip_list_mutex);
5625 
5626   if (prefs->crash_recovery) rewrite_recovery_file();
5627 
5628   mainw->ascrap_file = -1;
5629 }
5630 
5631 
5632 void recover_layout_map(int numclips) {
5633   // load global layout map for a set and assign entries to clips [mainw->files[i]->layout_map]
5634   LiVESList *omlist, *mlist, *lmap_node, *lmap_node_next, *lmap_entry_list, *lmap_entry_list_next;
5635 
5636   layout_map *lmap_entry;
5637   uint32_t mask;
5638 
5639   char **array;
5640   char *check_handle;
5641 
5642   if (numclips > MAX_FILES) numclips = MAX_FILES;
5643 
5644   if ((omlist = load_layout_map())) {
5645     int i;
5646 
5647     mlist = omlist;
5648 
5649     // assign layout map to clips
5650     for (i = 1; i <= numclips; i++) {
5651       lives_clip_t *sfile = mainw->files[i];
5652       if (!sfile) continue;
5653       lmap_node = mlist;
5654       while (lmap_node) {
5655         lmap_node_next = lmap_node->next;
5656         lmap_entry = (layout_map *)lmap_node->data;
5657         check_handle = lives_strdup(sfile->handle);
5658 
5659         if (strstr(lmap_entry->handle, "/") == NULL) {
5660           lives_free(check_handle);
5661           check_handle = lives_path_get_basename(sfile->handle);
5662         }
5663 
5664         if ((!strcmp(check_handle, lmap_entry->handle) && (sfile->unique_id == lmap_entry->unique_id)) ||
5665             (prefs->mt_load_fuzzy && (!strcmp(check_handle, lmap_entry->handle) || (sfile->unique_id == lmap_entry->unique_id)))
5666            ) {
5667           // check handle and unique id match
5668           // got a match, assign list to layout_map and delete this node
5669           lmap_entry_list = lmap_entry->list;
5670           while (lmap_entry_list) {
5671             lmap_entry_list_next = lmap_entry_list->next;
5672             array = lives_strsplit((char *)lmap_entry_list->data, "|", -1);
5673             if (!lives_file_test(array[0], LIVES_FILE_TEST_EXISTS)) {
5674               //g_print("removing layout because no file %s\n", array[0]);
5675               // layout file has been deleted, remove this entry
5676               if (lmap_entry_list->prev) lmap_entry_list->prev->next = lmap_entry_list_next;
5677               else lmap_entry->list = lmap_entry_list_next;
5678               if (lmap_entry_list_next) lmap_entry_list_next->prev = lmap_entry_list->prev;
5679               lives_free((livespointer)lmap_entry_list->data);
5680               lives_list_free(lmap_entry_list);
5681             }
5682             lives_strfreev(array);
5683             lmap_entry_list = lmap_entry_list_next;
5684           }
5685           sfile->layout_map = lmap_entry->list;
5686           lives_free(lmap_entry->handle);
5687           lives_free(lmap_entry->name);
5688           lives_free(lmap_entry);
5689           if (lmap_node->prev) lmap_node->prev->next = lmap_node_next;
5690           else omlist = mlist = lmap_node_next;
5691           if (lmap_node_next) lmap_node_next->prev = lmap_node->prev;
5692           lmap_node->prev = lmap_node->next = NULL;
5693           lives_list_free(lmap_node);
5694           /// check for missing frames and audio in layouts
5695           // TODO: -- needs checking ----
5696           mask = 0;
5697           mainw->xlays = layout_frame_is_affected(i, sfile->frames + 1, 0, mainw->xlays);
5698           if (mainw->xlays) {
5699             add_lmap_error(LMAP_ERROR_DELETE_FRAMES, sfile->name, (livespointer)sfile->layout_map, i,
5700                            sfile->frames, 0., FALSE);
5701             lives_list_free_all(&mainw->xlays);
5702             mask |= WARN_MASK_LAYOUT_DELETE_FRAMES;
5703             //g_print("FRMS %d\n", cfile->frames);
5704           }
5705 
5706           mainw->xlays = layout_audio_is_affected(i, sfile->laudio_time, 0., mainw->xlays);
5707           if (mainw->xlays) {
5708             add_lmap_error(LMAP_ERROR_DELETE_AUDIO, sfile->name, (livespointer)sfile->layout_map, i,
5709                            sfile->frames, sfile->laudio_time, FALSE);
5710             lives_list_free_all(&mainw->xlays);
5711             mask |= WARN_MASK_LAYOUT_DELETE_AUDIO;
5712             //g_print("AUD %f\n", cfile->laudio_time);
5713           }
5714           if (mask != 0) popup_lmap_errors(NULL, LIVES_INT_TO_POINTER(mask));
5715         }
5716 
5717         lives_free(check_handle);
5718         lmap_node = lmap_node_next;
5719       }
5720     }
5721 
5722     lmap_node = mlist;
5723     while (lmap_node) {
5724       lmap_entry = (layout_map *)lmap_node->data;
5725       if (lmap_entry->name) lives_free(lmap_entry->name);
5726       if (lmap_entry->handle) lives_free(lmap_entry->handle);
5727       lives_list_free_all(&lmap_entry->list);
5728       lmap_node = lmap_node->next;
5729     }
5730     if (omlist) lives_list_free(omlist);
5731   }
5732 }
5733 
5734 
5735 boolean reload_clip(int fileno, int maxframe) {
5736   // reload clip -- for CLIP_TYPE_FILE
5737   // cd to clip directory - so decoder plugins can write temp files
5738   LiVESList *odeclist;
5739   lives_clip_t *sfile = mainw->files[fileno];
5740 
5741   const lives_clip_data_t *cdata = NULL;
5742   lives_clip_data_t *fake_cdata = (lives_clip_data_t *)lives_calloc(sizeof(lives_clip_data_t), 1);
5743 
5744   double orig_fps = sfile->fps;
5745 
5746   char decoder_name[PATH_MAX];
5747   char *orig_filename = lives_strdup(sfile->file_name);
5748   char *cwd = lives_get_current_dir();
5749   char *ppath = lives_build_filename(prefs->workdir, sfile->handle, NULL);
5750 
5751   LiVESResponseType response;
5752   boolean was_renamed = FALSE, retb = FALSE;
5753   int current_file;
5754 
5755   fake_cdata = (lives_clip_data_t *)struct_from_template(LIVES_STRUCT_CLIP_DATA_T);
5756 
5757   if (!mainw->decoders_loaded) {
5758     mainw->decoder_list = load_decoders();
5759     mainw->decoders_loaded = TRUE;
5760   }
5761 
5762   odeclist = lives_list_copy(mainw->decoder_list);  ///< retain original order to restore for freshly opened clips
5763   retb = get_clip_value(fileno, CLIP_DETAILS_DECODER_NAME, decoder_name, PATH_MAX);
5764   if (retb && *decoder_name) {
5765     decoder_plugin_move_to_first(decoder_name);
5766   }
5767   retb = FALSE;
5768   lives_chdir(ppath, FALSE);
5769   lives_free(ppath);
5770 
5771   while (1) {
5772     threaded_dialog_spin(0.);
5773 
5774     fake_cdata->URI = lives_strdup(sfile->file_name);
5775     fake_cdata->fps = sfile->fps;
5776     fake_cdata->nframes = maxframe;
5777 
5778     response = LIVES_RESPONSE_NONE;
5779 
5780     if ((cdata = get_decoder_cdata(fileno, prefs->disabled_decoders, fake_cdata->fps != 0. ? fake_cdata : NULL)) == NULL) {
5781       if (mainw->error) {
5782 manual_locate:
5783         response = do_file_notfound_dialog(_("The original file"), orig_filename);
5784         if (response == LIVES_RESPONSE_RETRY) {
5785           lives_freep((void **)&fake_cdata->URI);
5786           continue;
5787         }
5788         if (response == LIVES_RESPONSE_BROWSE) {
5789           int resp;
5790           char fname[PATH_MAX], dirname[PATH_MAX], *newname;
5791           LiVESWidget *chooser;
5792 
5793           lives_snprintf(dirname, PATH_MAX, "%s", orig_filename);
5794           lives_snprintf(fname, PATH_MAX, "%s", orig_filename);
5795 
5796           get_dirname(dirname);
5797           get_basename(fname);
5798 
5799           chooser = choose_file_with_preview(dirname, fname, NULL, LIVES_FILE_SELECTION_VIDEO_AUDIO);
5800 
5801           resp = lives_dialog_run(LIVES_DIALOG(chooser));
5802 
5803           end_fs_preview();
5804 
5805           if (resp == LIVES_RESPONSE_ACCEPT) {
5806             newname = lives_file_chooser_get_filename(LIVES_FILE_CHOOSER(chooser));
5807             lives_widget_destroy(LIVES_WIDGET(chooser));
5808 
5809             if (newname && *newname) {
5810               char *tmp;
5811               lives_snprintf(sfile->file_name, PATH_MAX, "%s", (tmp = lives_filename_to_utf8(newname, -1, NULL, NULL, NULL)));
5812               lives_free(tmp);
5813               lives_free(newname);
5814             }
5815 
5816             lives_freep((void **)&fake_cdata->URI);
5817 
5818             //re-scan for these
5819             sfile->fps = 0.;
5820             maxframe = 0;
5821 
5822             was_renamed = TRUE;
5823             // try again with the new file
5824             continue;
5825           }
5826           // cancelled from filechooser
5827           lives_widget_destroy(LIVES_WIDGET(chooser));
5828           goto manual_locate;
5829         }
5830         // cancelled
5831       } else {
5832         // unopenable
5833         if (was_renamed) goto manual_locate;
5834         do_no_decoder_error(sfile->file_name);
5835       }
5836 
5837       lives_chdir(cwd, FALSE);
5838       lives_free(cwd);
5839 
5840       // NOT openable, or not found and user cancelled, switch back to original clip
5841       if (!sfile->checked && cdata) {
5842         check_clip_integrity(fileno, cdata, maxframe);
5843         if (sfile->frames > 0 || sfile->afilesize > 0) {
5844           // recover whatever we can
5845           sfile->clip_type = CLIP_TYPE_FILE;
5846           retb = check_if_non_virtual(fileno, 1, sfile->frames);
5847         }
5848         sfile->checked = TRUE;
5849       }
5850       if (!retb) {
5851         current_file = mainw->current_file;
5852         mainw->current_file = fileno;
5853         close_current_file(current_file);
5854       }
5855       unref_struct(&fake_cdata->lsd);
5856 
5857       lives_free(orig_filename);
5858       lives_list_free(mainw->decoder_list);
5859       mainw->decoder_list = odeclist;
5860       return retb;
5861     }
5862 
5863     // got cdata
5864     if (was_renamed) {
5865       // manual relocation
5866       sfile->fps = orig_fps;
5867       if (!sfile->checked && !check_clip_integrity(fileno, cdata, maxframe)) {
5868         // get correct img_type, fps, etc.
5869         if (THREADVAR(com_failed) || THREADVAR(write_failed)) do_header_write_error(fileno);
5870         goto manual_locate;
5871       }
5872       sfile->checked = TRUE;
5873       sfile->needs_silent_update = TRUE; // force filename update in header
5874       if (prefs->show_recent) {
5875         // replace in recent menu
5876         char file[PATH_MAX];
5877         int i;
5878         for (i = 0; i < 4; i++) {
5879           char *tmp;
5880           char *pref = lives_strdup_printf("%s%d", PREF_RECENT, i + 1);
5881           get_utf8_pref(pref, file, PATH_MAX);
5882           tmp = subst(file, orig_filename, sfile->file_name);
5883           if (lives_utf8_strcmp(tmp, file)) {
5884             lives_snprintf(file, PATH_MAX, "%s", tmp);
5885             set_utf8_pref(pref, file);
5886             lives_menu_item_set_text(mainw->recent[i], file, FALSE);
5887             if (mainw->multitrack) lives_menu_item_set_text(mainw->multitrack->recent[i], file, FALSE);
5888           }
5889           lives_free(tmp);
5890           lives_free(pref);
5891         }
5892         if (mainw->prefs_cache) {
5893           // update recent files -> force reload of prefs
5894           cached_list_free(&mainw->prefs_cache);
5895           mainw->prefs_cache = cache_file_contents(prefs->configfile);
5896         }
5897       }
5898     }
5899 
5900     threaded_dialog_spin(0.);
5901     unref_struct(&fake_cdata->lsd);
5902     break;
5903   }
5904 
5905   lives_free(orig_filename);
5906   lives_chdir(cwd, FALSE);
5907   lives_free(cwd);
5908 
5909   sfile->clip_type = CLIP_TYPE_FILE;
5910   get_mime_type(sfile->type, 40, cdata);
5911   sfile->img_type = IMG_TYPE_BEST; // read_headers() will have set this to "jpeg" (default)
5912   // we will set correct value in check_clip_integrity() if there are any real images
5913 
5914   if (sfile->ext_src) {
5915     boolean bad_header = FALSE;
5916     boolean correct = TRUE;
5917     if (!was_renamed) {
5918       if (!sfile->checked)
5919         correct = check_clip_integrity(fileno, cdata, maxframe); // get correct img_type, fps, etc.
5920       sfile->checked = TRUE;
5921     }
5922     if (!correct) {
5923       if (THREADVAR(com_failed) || THREADVAR(write_failed)) bad_header = TRUE;
5924     } else {
5925       lives_decoder_t *dplug = (lives_decoder_t *)sfile->ext_src;
5926       if (dplug) {
5927         lives_decoder_sys_t *dpsys = (lives_decoder_sys_t *)dplug->decoder;
5928         if (dpsys && *dpsys->name && strcmp(dpsys->name, decoder_name)) {
5929           save_clip_value(fileno, CLIP_DETAILS_DECODER_NAME, (void *)dpsys->name);
5930           if (THREADVAR(com_failed) || THREADVAR(write_failed)) bad_header = TRUE;
5931         }
5932       }
5933     }
5934 
5935     if (bad_header) do_header_write_error(fileno);
5936   }
5937   lives_list_free(mainw->decoder_list);
5938   mainw->decoder_list = odeclist;
5939   if (prefs->autoload_subs) {
5940     reload_subs(fileno);
5941   }
5942   return TRUE;
5943 }
5944 
5945 #define _RELOAD(field) sfile->field = loaded->field
5946 #define _RELOAD_STRING(field, len) lives_snprintf(sfile->field, len, "%s", loaded->field)
5947 
5948 #define DSIZE_MAX 100000
5949 
5950 static lives_clip_t *_restore_binfmt(int clipno, boolean forensic) {
5951   if (IS_NORMAL_CLIP(clipno)) {
5952     lives_clip_t *sfile = mainw->files[clipno];
5953     char *fname = lives_build_filename(prefs->workdir, sfile->handle, TOTALSAVE_NAME, NULL);
5954     if (lives_file_test(fname, LIVES_FILE_TEST_EXISTS)) {
5955       ssize_t bytes;
5956       size_t cursize = (size_t)((char *)&sfile->binfmt_end - (char *)sfile), dsize;
5957       char *xloaded = (char *)lives_calloc(1, cursize);
5958       boolean badsize = FALSE;
5959       int fd = lives_open_buffered_rdonly(fname);
5960       size_t fsize = lives_buffered_orig_size(fd);
5961       lives_clip_t *loaded = (lives_clip_t *)xloaded;
5962       if (fsize < cursize) badsize = TRUE;
5963       else {
5964         bytes = lives_read_buffered(fd, xloaded, 8, TRUE);
5965         if (bytes < 8 || lives_memcmp(loaded->binfmt_check.chars, CLIP_BINFMT_CHECK, 8)) badsize = TRUE;
5966         else {
5967           bytes += lives_read_buffered(fd, xloaded + 8, 16, TRUE);
5968           if (bytes < 16) badsize = TRUE;
5969           else {
5970             dsize = loaded->binfmt_bytes.num;
5971             if (dsize < cursize) badsize = TRUE;
5972             else {
5973               if (dsize > cursize && dsize < DSIZE_MAX) {
5974                 xloaded = lives_realloc(xloaded, dsize);
5975                 loaded = (lives_clip_t *)xloaded;
5976               } else dsize = cursize;
5977               bytes += lives_read_buffered(fd, xloaded + 24, dsize - 24, TRUE);
5978               if (bytes < dsize) badsize = TRUE;
5979             }
5980           }
5981         }
5982       }
5983       lives_close_buffered(fd);
5984       //if (!forensic) lives_rm(fname);
5985       lives_free(fname);
5986 
5987       if (badsize) {
5988         lives_free(xloaded);
5989         return FALSE;
5990       }
5991 
5992       THREADVAR(com_failed) = FALSE;
5993       if (THREADVAR(read_failed) == fd + 1) {
5994         THREADVAR(read_failed) = 0;
5995         lives_free(xloaded);
5996         return NULL;
5997       }
5998 
5999       sfile->has_binfmt = TRUE;
6000 
6001       if (forensic) return loaded;
6002 
6003       _RELOAD_STRING(save_file_name, PATH_MAX);  _RELOAD(start); _RELOAD(end); _RELOAD(is_untitled); _RELOAD(was_in_set);
6004       _RELOAD(ratio_fps); _RELOAD_STRING(mime_type, 256);
6005       _RELOAD(changed); _RELOAD(deinterlace); _RELOAD(vol);
6006       if (sfile->start < 1) sfile->start = 1;
6007       if (sfile->end > sfile->frames) sfile->end = sfile->frames;
6008       if (sfile->start > sfile->end) sfile->start = sfile->end;
6009       if (lives_strlen(sfile->save_file_name) > PATH_MAX) lives_memset(sfile->save_file_name, 0, PATH_MAX);
6010       if (sfile->pointer_time > sfile->video_time) sfile->pointer_time = 0.;
6011       if (sfile->real_pointer_time > CLIP_TOTAL_TIME(clipno)) sfile->real_pointer_time = sfile->pointer_time;
6012       return loaded;
6013     }
6014     lives_free(fname);
6015   }
6016   return NULL;
6017 }
6018 
6019 #undef _RELOAD
6020 #undef _RELOAD_STRING
6021 
6022 boolean restore_clip_binfmt(int clipno) {
6023   lives_clip_t *recov = _restore_binfmt(clipno, FALSE);
6024   if (!recov) return FALSE;
6025   lives_free(recov);
6026   return TRUE;
6027 }
6028 
6029 lives_clip_t *clip_forensic(int clipno) {
6030   return  _restore_binfmt(clipno, TRUE);
6031 }
6032 
6033 boolean recover_files(char *recovery_file, boolean auto_recover) {
6034   FILE *rfile = NULL;
6035 
6036   char buff[256], *buffptr;
6037   char *clipdir;
6038 
6039   LiVESResponseType resp;
6040 
6041   int clipnum = 0;
6042   int maxframe;
6043   int last_good_file = -1, ngoodclips;
6044 
6045   boolean is_scrap, is_ascrap;
6046   boolean did_set_check = FALSE;
6047   boolean is_ready = mainw->is_ready, mt_is_ready = FALSE;
6048   boolean mt_needs_idlefunc = FALSE;
6049   boolean retb = TRUE, retval;
6050   boolean load_from_set = TRUE;
6051   boolean rec_cleanup = FALSE;
6052 
6053   // setting is_ready allows us to get the correct transient window for dialogs
6054   // otherwise the dialogs will appear behind the main interface
6055   // we do this for mainwindow and multitrack
6056 
6057   // we will reset these before returning
6058   mainw->is_ready = TRUE;
6059 
6060   if (mainw->multitrack) {
6061     if (mainw->multitrack->idlefunc > 0) {
6062       lives_source_remove(mainw->multitrack->idlefunc);
6063       mainw->multitrack->idlefunc = 0;
6064       mt_needs_idlefunc = TRUE;
6065     }
6066     mt_desensitise(mainw->multitrack);
6067     mt_is_ready = mainw->multitrack->is_ready;
6068     mainw->multitrack->is_ready = TRUE;
6069   }
6070 
6071   if (!auto_recover) {
6072     if (1 || mainw->multitrack) {
6073       lives_widget_show_all(LIVES_MAIN_WINDOW_WIDGET);
6074       lives_widget_context_update();
6075     }
6076     if (!do_yesno_dialog
6077         (_("\nFiles from a previous run of LiVES were found.\nDo you want to attempt to recover them ?\n"))) {
6078       retb = FALSE;
6079       goto recovery_done;
6080     }
6081   }
6082 
6083   if (recovery_file) {
6084     do {
6085       resp = LIVES_RESPONSE_NONE;
6086       rfile = fopen(recovery_file, "r");
6087       if (!rfile) {
6088         resp = do_read_failed_error_s_with_retry(recovery_file, lives_strerror(errno));
6089         if (resp == LIVES_RESPONSE_CANCEL) {
6090           retb = FALSE;
6091           goto recovery_done;
6092         }
6093       }
6094     } while (resp == LIVES_RESPONSE_RETRY);
6095   }
6096 
6097   do_threaded_dialog(_("Recovering files"), FALSE);
6098   d_print(_("\nRecovering files..."));
6099 
6100   threaded_dialog_spin(0.);
6101 
6102   mainw->suppress_dprint = TRUE;
6103   mainw->recovering_files = TRUE;
6104 
6105   while (1) {
6106     if (mainw->hdrs_cache) cached_list_free(&mainw->hdrs_cache);
6107 
6108     threaded_dialog_spin(0.);
6109     is_scrap = FALSE;
6110     is_ascrap = FALSE;
6111 
6112     THREADVAR(read_failed) = FALSE;
6113 
6114     if (recovery_file) {
6115       if (!lives_fgets(buff, 256, rfile)) {
6116         reset_clipmenu();
6117         threaded_dialog_spin(0.);
6118         mainw->suppress_dprint = FALSE;
6119         if (THREADVAR(read_failed)) {
6120           d_print_failed();
6121           do_read_failed_error_s(recovery_file, NULL);
6122         } else d_print_done();
6123         break;
6124       }
6125     } else {
6126       if (!mainw->recovery_list) {
6127         reset_clipmenu();
6128         mainw->suppress_dprint = FALSE;
6129         d_print_done();
6130         break;
6131       }
6132       lives_snprintf(buff, 256, "%s", (char *)mainw->recovery_list->data);
6133       mainw->recovery_list = mainw->recovery_list->next;
6134     }
6135 
6136     lives_chomp(buff);
6137 
6138     if (buff[strlen(buff) - 1] == '*') {
6139       boolean crash_recovery = prefs->crash_recovery;
6140       LiVESResponseType resp;
6141       // set to be opened
6142       buff[strlen(buff) - 1 - strlen(LIVES_DIR_SEP)] = 0;
6143       do {
6144         resp = LIVES_RESPONSE_OK;
6145         if (!is_legal_set_name(buff, TRUE, TRUE)) {
6146           resp = do_abort_cancel_retry_dialog(_("Click Abort to exit LiVES immediately, Retry to try again,"
6147                                                 " or Cancel to continue without reloading the set.\n"));
6148         }
6149       } while (resp == LIVES_RESPONSE_RETRY);
6150       if (resp == LIVES_RESPONSE_CANCEL) continue;
6151 
6152       /** dont write an entry yet, in case of the unklikely chance we were assigned the same pid as the recovery file,
6153         otherwise we will end up in am endless loop of reloading the same set and appending it to the recovery file
6154         in any case, the old file is still there and we will create a fresh recovery file after a succesful reload */
6155       prefs->crash_recovery = FALSE;
6156 
6157       if (!reload_set(buff)) {
6158         prefs->crash_recovery = crash_recovery; /// reset to original value
6159         mainw->suppress_dprint = FALSE;
6160         d_print_failed();
6161         mainw->suppress_dprint = FALSE;
6162         continue;
6163       }
6164       mainw->was_set = TRUE;
6165       prefs->crash_recovery = crash_recovery; /// reset to original value
6166     } else {
6167       /// load single file
6168       if (!strncmp(buff, "scrap|", 6)) {
6169         is_scrap = TRUE;
6170         buffptr = buff + 6;
6171       } else if (!strncmp(buff, "ascrap|", 7)) {
6172         is_ascrap = TRUE;
6173         buffptr = buff + 7;
6174       } else {
6175         if (!strncmp(buff, "ascrap", 6) || !strncmp(buff, "scrap", 5)) {
6176           rec_cleanup = TRUE;
6177           continue;
6178         }
6179         buffptr = buff;
6180       }
6181 
6182       clipdir = lives_build_filename(prefs->workdir, buffptr, NULL);
6183 
6184       if (!lives_file_test(clipdir, LIVES_FILE_TEST_IS_DIR)) {
6185         lives_free(clipdir);
6186         continue;
6187       }
6188       lives_free(clipdir);
6189 
6190       if (strstr(buffptr, "/" CLIPS_DIRNAME "/")) {
6191         char **array;
6192         threaded_dialog_spin(0.);
6193         if (!load_from_set) continue;
6194         array = lives_strsplit(buffptr, "/" CLIPS_DIRNAME "/", -1);
6195         mainw->was_set = TRUE;
6196         lives_snprintf(mainw->set_name, 128, "%s", array[0]);
6197         lives_strfreev(array);
6198 
6199         if (!did_set_check && !check_for_lock_file(mainw->set_name, 0)) {
6200           if (!do_set_locked_warning(mainw->set_name)) {
6201             load_from_set = FALSE;
6202             mainw->was_set = FALSE;
6203             mainw->set_name[0] = 0;
6204           }
6205           did_set_check = TRUE;
6206         }
6207       }
6208 
6209       /// create a new cfile and fill in the details
6210       if (!create_cfile(-1, buffptr, FALSE)) {
6211         threaded_dialog_spin(0.);
6212         end_threaded_dialog();
6213         mainw->suppress_dprint = FALSE;
6214         d_print_failed();
6215         break;
6216       }
6217 
6218       if (is_scrap || is_ascrap) {
6219         pthread_mutex_lock(&mainw->clip_list_mutex);
6220         mainw->cliplist = lives_list_append(mainw->cliplist, LIVES_INT_TO_POINTER(mainw->current_file));
6221         pthread_mutex_unlock(&mainw->clip_list_mutex);
6222       }
6223 
6224       if (is_scrap) {
6225         mainw->scrap_file = mainw->current_file;
6226         cfile->opening = FALSE;
6227         lives_snprintf(cfile->type, 40, "scrap");
6228         cfile->frames = 1;
6229         cfile->hsize = 640;
6230         cfile->vsize = 480;
6231         continue;
6232       }
6233 
6234       if (is_ascrap) {
6235         mainw->ascrap_file = mainw->current_file;
6236         cfile->opening = FALSE;
6237         lives_snprintf(cfile->type, 40, "ascrap");
6238       }
6239 
6240       /// get file details; this will cache the header in mainw->hdrs_cache
6241       // we need to keep this around for open_set_file(), below.
6242       clipdir = lives_build_path(prefs->workdir, cfile->handle, NULL);
6243       retval = read_headers(mainw->current_file, clipdir, NULL);
6244       lives_free(clipdir);
6245 
6246       if (is_ascrap) {
6247         if (!retval) {
6248           mainw->first_free_file = mainw->current_file;
6249           mainw->ascrap_file = -1;
6250         }
6251         continue;
6252       }
6253 
6254       if (mainw->current_file < 1) continue;
6255 
6256       /// see function reload_set() for detailed comments
6257       if ((maxframe = load_frame_index(mainw->current_file)) > 0) {
6258         /// CLIP_TYPE_FILE
6259         if (!*cfile->file_name) continue;
6260         if (!reload_clip(mainw->current_file, maxframe)) continue;
6261         if (cfile->img_type == IMG_TYPE_UNKNOWN) {
6262           lives_clip_data_t *cdata = ((lives_decoder_t *)cfile->ext_src)->cdata;
6263           int fvirt = count_virtual_frames(cfile->frame_index, 1, cfile->frames);
6264           if (fvirt < cfile->frames) {
6265             if (!cfile->checked && !check_clip_integrity(mainw->current_file, cdata, cfile->frames)) {
6266               cfile->needs_update = TRUE;
6267             }
6268             cfile->checked = TRUE;
6269           }
6270           if (cfile->header_version >= 102) cfile->fps = cfile->pb_fps;
6271         }
6272       } else {
6273         /// CLIP_TYPE_DISK
6274         boolean is_ok = TRUE;
6275         if (!cfile->checked) {
6276           if (!(is_ok = check_clip_integrity(mainw->current_file, NULL, cfile->frames))) {
6277             cfile->needs_update = TRUE;
6278           }
6279           cfile->checked = TRUE;
6280         }
6281         if (!prefs->vj_mode && !is_ok) {
6282           if (cfile->afilesize == 0) {
6283             reget_afilesize_inner(mainw->current_file);
6284           }
6285           if (!check_frame_count(mainw->current_file, is_ok)) {
6286             cfile->frames = get_frame_count(mainw->current_file, 1);
6287             cfile->needs_update = TRUE;
6288           }
6289         }
6290       }
6291       if (!recovery_file && !cfile->checked) {
6292         lives_clip_data_t *cdata = ((lives_decoder_t *)cfile->ext_src)->cdata;
6293         if (!check_clip_integrity(mainw->current_file, cdata, cfile->frames)) {
6294           cfile->needs_update = TRUE;
6295           if (cfile->header_version >= 102) cfile->fps = cfile->pb_fps;
6296         }
6297       }
6298 
6299       threaded_dialog_spin(0.);
6300 
6301       /** not really from a set, but let's pretend to get the details
6302         read the playback fps, play frame, and name */
6303 
6304       /// NEED TO maintain mainw->hdrs_cache when entering the function,
6305       /// else it will be considered a legacy file load
6306       open_set_file(++clipnum);
6307 
6308       threaded_dialog_spin(0.);
6309 
6310       if (mainw->hdrs_cache) cached_list_free(&mainw->hdrs_cache);
6311 
6312       if (mainw->current_file < 1) continue;
6313 
6314       if (cfile->clip_type == CLIP_TYPE_FILE && cfile->header_version >= 102) cfile->fps = cfile->pb_fps;
6315       get_total_time(cfile);
6316 
6317       if (CLIP_TOTAL_TIME(mainw->current_file) == 0.) {
6318         close_current_file(last_good_file);
6319         continue;
6320       }
6321 
6322       last_good_file = mainw->current_file;
6323 
6324       if (cfile->needs_update || cfile->needs_silent_update) {
6325         if (cfile->needs_update) do_clip_divergence_error(mainw->current_file);
6326         save_clip_values(mainw->current_file);
6327         cfile->needs_silent_update = cfile->needs_update = FALSE;
6328       }
6329 
6330       // add to clip menu
6331       threaded_dialog_spin(0.);
6332       add_to_clipmenu();
6333       cfile->start = cfile->frames > 0 ? 1 : 0;
6334       cfile->end = cfile->frames;
6335       cfile->is_loaded = TRUE;
6336       cfile->changed = TRUE;
6337       lives_rm(cfile->info_file);
6338       if (!mainw->multitrack) set_main_title(cfile->name, 0);
6339 
6340       if (cfile->frameno > cfile->frames) cfile->frameno = cfile->last_frameno = 1;
6341 
6342       if (!mainw->multitrack) {
6343         lives_signal_handler_block(mainw->spinbutton_start, mainw->spin_start_func);
6344         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_start), cfile->start);
6345         lives_signal_handler_unblock(mainw->spinbutton_start, mainw->spin_start_func);
6346         lives_signal_handler_block(mainw->spinbutton_end, mainw->spin_end_func);
6347         lives_spin_button_set_value(LIVES_SPIN_BUTTON(mainw->spinbutton_end), cfile->end);
6348         lives_signal_handler_unblock(mainw->spinbutton_end, mainw->spin_end_func);
6349         showclipimgs();
6350       } else {
6351         int current_file = mainw->current_file;
6352         lives_mt *multi = mainw->multitrack;
6353         mainw->multitrack = NULL;
6354         mainw->current_file = -1;
6355         reget_afilesize(current_file);
6356         mainw->current_file = current_file;
6357         mainw->multitrack = multi;
6358         get_total_time(cfile);
6359         mainw->current_file = mainw->multitrack->render_file;
6360         mt_init_clips(mainw->multitrack, current_file, TRUE);
6361         set_poly_tab(mainw->multitrack, POLY_CLIPS);
6362         lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
6363         mt_clip_select(mainw->multitrack, TRUE);
6364         lives_widget_process_updates(LIVES_MAIN_WINDOW_WIDGET);
6365         mainw->current_file = current_file;
6366       }
6367 
6368       threaded_dialog_spin(0.);
6369 
6370       if (cfile->frameno > cfile->frames && cfile->frameno > 1) cfile->frameno = cfile->frames;
6371       cfile->last_frameno = cfile->frameno;
6372       cfile->pointer_time = cfile->real_pointer_time = calc_time_from_frame(mainw->current_file, cfile->frameno);
6373       if (cfile->real_pointer_time > CLIP_TOTAL_TIME(mainw->current_file))
6374         cfile->real_pointer_time = CLIP_TOTAL_TIME(mainw->current_file);
6375       if (cfile->pointer_time > cfile->video_time) cfile->pointer_time = 0.;
6376 
6377       if (cfile->achans) {
6378         cfile->aseek_pos = (off64_t)((double)(cfile->real_pointer_time * cfile->arate) * cfile->achans *
6379                                      (cfile->asampsize / 8));
6380         if (cfile->aseek_pos > cfile->afilesize) cfile->aseek_pos = 0.;
6381       }
6382 
6383       if (mainw->current_file != -1)
6384         if (*mainw->set_name) recover_layout_map(mainw->current_file);
6385 
6386       if (!mainw->multitrack) resize(1);
6387 
6388       lives_notify(LIVES_OSC_NOTIFY_CLIP_OPENED, "");
6389     }
6390   }
6391 
6392   if (mainw->hdrs_cache) cached_list_free(&mainw->hdrs_cache);
6393 
6394   ngoodclips = lives_list_length(mainw->cliplist);
6395   if (!ngoodclips) {
6396     d_print(_("No clips were recovered.\n"));
6397   }
6398   d_print(P_("%d clip was recovered ", "%d clips were recovered ", ngoodclips), ngoodclips);
6399   if (recovery_file)
6400     d_print(_("from the previous session.\n"));
6401   else
6402     d_print(_("from previous sessions.\n"));
6403 
6404   if (!mainw->multitrack) { // TODO check if we can do this in mt too
6405     int start_file = mainw->current_file;
6406     if (start_file > 1 && start_file == mainw->ascrap_file && mainw->files[start_file - 1]) {
6407       start_file--;
6408     }
6409     if (start_file > 1 && start_file == mainw->scrap_file && mainw->files[start_file - 1]) {
6410       start_file--;
6411     }
6412     if (start_file > 1 && start_file == mainw->ascrap_file && mainw->files[start_file - 1]) {
6413       start_file--;
6414     }
6415     if ((!IS_VALID_CLIP(start_file) || (mainw->files[start_file]->frames == 0 && mainw->files[start_file]->afilesize == 0))
6416         && mainw->files[1] && start_file != 1) {
6417       for (start_file = MAX_FILES; start_file > 0; start_file--) {
6418         if (mainw->files[start_file]
6419             && (mainw->files[start_file]->frames > 0 || mainw->files[start_file]->afilesize > 0))
6420           if (start_file != mainw->scrap_file && start_file != mainw->ascrap_file) break;
6421       }
6422     }
6423 
6424     if (start_file != mainw->current_file) {
6425       rec_cleanup = TRUE;
6426       switch_to_file(mainw->current_file, start_file);
6427       showclipimgs();
6428       redraw_timeline(mainw->current_file);
6429     }
6430   } else {
6431     mt_clip_select(mainw->multitrack, TRUE); // scroll clip on screen
6432   }
6433 
6434   if (recovery_file) fclose(rfile);
6435 
6436 recovery_done:
6437 
6438   end_threaded_dialog();
6439 
6440   mainw->suppress_dprint = FALSE;
6441   lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
6442   mainw->recovering_files = FALSE;
6443   mainw->is_ready = is_ready;
6444   if (mainw->multitrack) {
6445     mainw->multitrack->is_ready = mt_is_ready;
6446     mainw->current_file = mainw->multitrack->render_file;
6447     polymorph(mainw->multitrack, POLY_NONE);
6448     polymorph(mainw->multitrack, POLY_CLIPS);
6449     mt_sensitise(mainw->multitrack);
6450     if (mt_needs_idlefunc) mainw->multitrack->idlefunc = mt_idle_add(mainw->multitrack);
6451   } else update_play_times();
6452   mainw->last_dprint_file = -1;
6453   mainw->no_switch_dprint = FALSE;
6454   d_print("");
6455   mainw->invalid_clips = rec_cleanup;
6456   return retb;
6457 }
6458 
6459 
6460 void add_to_recovery_file(const char *handle) {
6461   lives_echo(handle, mainw->recovery_file, TRUE);
6462 
6463   if (THREADVAR(com_failed)) {
6464     THREADVAR(com_failed) = FALSE;
6465     return;
6466   }
6467 
6468   if ((mainw->multitrack && mainw->multitrack->event_list) || mainw->stored_event_list)
6469     write_backup_layout_numbering(mainw->multitrack);
6470 }
6471 
6472 
6473 boolean rewrite_recovery_file(void) {
6474   // part of the crash recovery system
6475   // returns TRUE if successful
6476   LiVESList *clist = mainw->cliplist;
6477   char *recovery_entry;
6478   char *temp_recovery_file;
6479 
6480   boolean opened = FALSE;
6481   boolean wrote_set_entry = FALSE;
6482 
6483   int recovery_fd = -1;
6484   LiVESResponseType retval;
6485 
6486   if (!clist || !prefs->crash_recovery) {
6487     lives_rm(mainw->recovery_file);
6488     return FALSE;
6489   }
6490 
6491   temp_recovery_file = lives_strdup_printf("%s.%s", mainw->recovery_file, LIVES_FILE_EXT_TMP);
6492 
6493   do {
6494     retval = LIVES_RESPONSE_NONE;
6495     THREADVAR(write_failed) = FALSE;
6496     opened = FALSE;
6497     recovery_fd = -1;
6498 
6499     for (; clist; clist = clist->next) {
6500       int i = LIVES_POINTER_TO_INT(clist->data);
6501       if (IS_NORMAL_CLIP(i)) {
6502         lives_clip_t *sfile = mainw->files[i];
6503         if (i == mainw->scrap_file) {
6504           recovery_entry = lives_strdup_printf("scrap|%s\n", sfile->handle);
6505         } else if (i == mainw->ascrap_file) {
6506           recovery_entry = lives_strdup_printf("ascrap|%s\n", sfile->handle);
6507         } else {
6508           if (sfile->was_in_set && *mainw->set_name) {
6509             if (!wrote_set_entry) {
6510               recovery_entry = lives_build_filename(mainw->set_name, "*\n", NULL);
6511               wrote_set_entry = TRUE;
6512             } else continue;
6513           } else recovery_entry = lives_strdup_printf("%s\n", sfile->handle);
6514         }
6515 
6516         if (!opened) recovery_fd = creat(temp_recovery_file, S_IRUSR | S_IWUSR);
6517         if (recovery_fd < 0) retval = do_write_failed_error_s_with_retry(temp_recovery_file, lives_strerror(errno));
6518         else {
6519           opened = TRUE;
6520           lives_write(recovery_fd, recovery_entry, strlen(recovery_entry), TRUE);
6521           if (THREADVAR(write_failed)) retval = do_write_failed_error_s_with_retry(temp_recovery_file, NULL);
6522         }
6523         lives_free(recovery_entry);
6524       }
6525       if (THREADVAR(write_failed)) break;
6526     }
6527   } while (retval == LIVES_RESPONSE_RETRY);
6528 
6529   if (!opened) lives_rm(mainw->recovery_file);
6530   else if (recovery_fd >= 0) {
6531     close(recovery_fd);
6532     retval = LIVES_RESPONSE_INVALID;
6533     do {
6534       lives_mv(temp_recovery_file, mainw->recovery_file);
6535       if (THREADVAR(com_failed)) {
6536         retval = do_write_failed_error_s_with_retry(temp_recovery_file, NULL);
6537       }
6538     } while (retval == LIVES_RESPONSE_RETRY);
6539   }
6540 
6541   lives_free(temp_recovery_file);
6542 
6543   if ((mainw->multitrack && mainw->multitrack->event_list) || mainw->stored_event_list)
6544     write_backup_layout_numbering(mainw->multitrack);
6545 
6546   return TRUE;
6547 }
6548 
6549 
6550 boolean check_for_recovery_files(boolean auto_recover) {
6551   uint32_t recpid = 0;
6552 
6553   char *recovery_file, *recovery_numbering_file, *recording_file, *recording_numbering_file, *xfile;
6554   char *com;
6555 
6556   boolean retval = FALSE;
6557   boolean found = FALSE, found_recording = FALSE;
6558 
6559   int lgid = lives_getgid();
6560   int luid = lives_getuid();
6561 
6562   lives_pgid_t lpid = capable->mainpid;
6563 
6564   // ask backend to find the latest recovery file which is not owned by a running version of LiVES
6565   com = lives_strdup_printf("%s get_recovery_file %d %d %s recovery %d", prefs->backend_sync, luid, lgid,
6566                             capable->myname, capable->mainpid);
6567 
6568   lives_popen(com, FALSE, mainw->msg, MAINW_MSG_SIZE);
6569   lives_free(com);
6570 
6571   if (THREADVAR(com_failed)) {
6572     THREADVAR(com_failed) = FALSE;
6573     return FALSE;
6574   }
6575 
6576   recpid = atoi(mainw->msg);
6577   if (recpid == 0) return FALSE;
6578 
6579   retval = recover_files((recovery_file = lives_strdup_printf("%s/recovery.%d.%d.%d", prefs->workdir, luid,
6580                                           lgid, recpid)), auto_recover);
6581   lives_free(recovery_file);
6582 
6583   if (!retval || prefs->vj_mode) {
6584     com = lives_strdup_printf("%s clean_recovery_files %d %d \"%s\" %d %d", prefs->backend_sync, luid, lgid, capable->myname,
6585                               capable->mainpid, prefs->vj_mode);
6586     lives_system(com, FALSE);
6587     lives_free(com);
6588     if (prefs->vj_mode) {
6589       rewrite_recovery_file();
6590       return TRUE;
6591     }
6592     return FALSE;
6593   }
6594 
6595 #if !GTK_CHECK_VERSION(3, 0, 0)
6596   if (CURRENT_CLIP_IS_VALID) {
6597     showclipimgs();
6598     lives_widget_queue_resize(mainw->video_draw);
6599     lives_widget_queue_resize(mainw->laudio_draw);
6600     lives_widget_queue_resize(mainw->raudio_draw);
6601   }
6602 #endif
6603 
6604   THREADVAR(com_failed) = FALSE;
6605 
6606   /// CRITICAL: make sure this gets called even on system failure and abort
6607   if (prefs->crash_recovery) mainw->abort_hook_func = (lives_funcptr_t)rewrite_recovery_file;
6608 
6609   // check for layout recovery file
6610   recovery_file = lives_strdup_printf("%s/%s.%d.%d.%d.%s", prefs->workdir, LAYOUT_FILENAME, luid, lgid, recpid,
6611                                       LIVES_FILE_EXT_LAYOUT);
6612   recovery_numbering_file = lives_strdup_printf("%s/%s.%d.%d.%d", prefs->workdir, LAYOUT_NUMBERING_FILENAME, luid, lgid, recpid);
6613 
6614   recording_file = lives_strdup_printf("%s/recorded-%s.%d.%d.%d.%s", prefs->workdir, LAYOUT_FILENAME, luid, lgid, recpid,
6615                                        LIVES_FILE_EXT_LAYOUT);
6616 
6617   recording_numbering_file = lives_strdup_printf("%s/recorded-%s.%d.%d.%d", prefs->workdir, LAYOUT_NUMBERING_FILENAME, luid, lgid,
6618                              recpid);
6619 
6620   if (!lives_file_test(recovery_file, LIVES_FILE_TEST_EXISTS)) {
6621     lives_free(recovery_file);
6622     recovery_file = lives_strdup_printf("%s/%s.%d.%d.%d", prefs->workdir, LAYOUT_FILENAME, luid, lgid, recpid);
6623     if (lives_file_test(recovery_file, LIVES_FILE_TEST_EXISTS)) {
6624       found = TRUE;
6625     }
6626   } else {
6627     found = TRUE;
6628   }
6629   if (found) {
6630     if (!lives_file_test(recovery_numbering_file, LIVES_FILE_TEST_EXISTS)) {
6631       found = FALSE;
6632     }
6633   }
6634 
6635   if (prefs->rr_crash && lives_file_test(recording_file, LIVES_FILE_TEST_EXISTS)) {
6636     if (lives_file_test(recording_numbering_file, LIVES_FILE_TEST_EXISTS)) {
6637       found_recording = TRUE;
6638       xfile = lives_strdup_printf("%s/keep_recorded-layout.%d.%d.%d", prefs->workdir, luid, lgid, lpid);
6639       lives_mv(recording_file, xfile);
6640       lives_free(xfile);
6641       xfile = lives_strdup_printf("%s/keep_recorded-layout_numbering.%d.%d.%d", prefs->workdir, luid, lgid, lpid);
6642       lives_mv(recording_numbering_file, xfile);
6643       lives_free(xfile);
6644       mainw->recording_recovered = TRUE;
6645     }
6646   }
6647 
6648   if (found) {
6649     // move files temporarily to stop them being cleansed
6650     xfile = lives_strdup_printf("%s/keep_layout.%d.%d.%d", prefs->workdir, luid, lgid, lpid);
6651     lives_mv(recovery_file, xfile);
6652     lives_free(xfile);
6653     xfile = lives_strdup_printf("%s/keep_layout_numbering.%d.%d.%d", prefs->workdir, luid, lgid, lpid);
6654     lives_mv(recovery_numbering_file, xfile);
6655     lives_free(xfile);
6656     mainw->recoverable_layout = TRUE;
6657   }
6658 
6659   if (!found && !found_recording) {
6660     if (mainw->scrap_file != -1) close_scrap_file(TRUE);
6661     if (mainw->ascrap_file != -1) close_ascrap_file(TRUE);
6662   }
6663 
6664   lives_free(recovery_file);
6665   lives_free(recovery_numbering_file);
6666   lives_free(recording_file);
6667   lives_free(recording_numbering_file);
6668 
6669   if (THREADVAR(com_failed) && prefs->crash_recovery) {
6670     rewrite_recovery_file();
6671     return FALSE;
6672   }
6673 
6674   com = lives_strdup_printf("%s clean_recovery_files %d %d \"%s\" %d 0", prefs->backend_sync, luid, lgid, capable->myname,
6675                             capable->mainpid);
6676   lives_system(com, FALSE);
6677   lives_free(com);
6678 
6679   recovery_file = lives_strdup_printf("%s/%s.%d.%d.%d.%s", prefs->workdir, LAYOUT_FILENAME, luid, lgid, lpid,
6680                                       LIVES_FILE_EXT_LAYOUT);
6681   recovery_numbering_file = lives_strdup_printf("%s/%s.%d.%d.%d", prefs->workdir, LAYOUT_NUMBERING_FILENAME, luid, lgid, lpid);
6682 
6683   if (mainw->recoverable_layout) {
6684     // move files back
6685     xfile = lives_strdup_printf("%s/keep_layout.%d.%d.%d", prefs->workdir, luid, lgid, lpid);
6686     lives_mv(xfile, recovery_file);
6687     lives_free(xfile);
6688     xfile = lives_strdup_printf("%s/keep_layout_numbering.%d.%d.%d", prefs->workdir, luid, lgid, lpid);
6689     lives_mv(xfile, recovery_numbering_file);
6690     lives_free(xfile);
6691   }
6692 
6693   recording_file = lives_strdup_printf("%s/recorded-%s.%d.%d.%d.%s", prefs->workdir, LAYOUT_FILENAME, luid, lgid, lpid,
6694                                        LIVES_FILE_EXT_LAYOUT);
6695   recording_numbering_file = lives_strdup_printf("%s/recorded-%s.%d.%d.%d", prefs->workdir, LAYOUT_NUMBERING_FILENAME, luid, lgid,
6696                              lpid);
6697 
6698   if (mainw->recording_recovered) {
6699     xfile = lives_strdup_printf("%s/keep_recorded-layout.%d.%d.%d", prefs->workdir, luid, lgid, lpid);
6700     /// may fail -> abort
6701     lives_mv(xfile, recording_file);
6702     lives_free(xfile);
6703     xfile = lives_strdup_printf("%s/keep_recorded-layout_numbering.%d.%d.%d", prefs->workdir, luid, lgid, lpid);
6704     lives_mv(xfile, recording_numbering_file);
6705     lives_free(xfile);
6706   }
6707 
6708   lives_free(recovery_file);
6709   lives_free(recovery_numbering_file);
6710   lives_free(recording_file);
6711   lives_free(recording_numbering_file);
6712 
6713   if (prefs->crash_recovery) rewrite_recovery_file();
6714   mainw->abort_hook_func = NULL;
6715 
6716   if (!mainw->recoverable_layout && !mainw->recording_recovered) {
6717     if (mainw->invalid_clips && (prefs->warning_mask ^ (WARN_MASK_CLEAN_AFTER_CRASH | WARN_MASK_CLEAN_INVALID))
6718         == WARN_MASK_CLEAN_INVALID) do_after_invalid_warning();
6719     else do_after_crash_warning();
6720     mainw->invalid_clips = FALSE;
6721   }
6722   return retval;
6723 }
6724 
6725