MatN prevents continuous reload of plugins for AppImage + Andrew libaom compile mod
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / mwindow.C
1 /*
2  * CINELERRA
3  * Copyright (C) 1997-2014 Adam Williams <broadcast at earthling dot net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20
21 #include "aboutprefs.h"
22 #include "asset.h"
23 #include "assets.h"
24 #include "atrack.h"
25 #include "audioalsa.h"
26 #include "autos.h"
27 #include "awindowgui.h"
28 #include "awindow.h"
29 #include "batchrender.h"
30 #include "bccmodels.h"
31 #include "bcdisplayinfo.h"
32 #include "bcprogressbox.h"
33 #include "bcsignals.h"
34 #include "bctimer.h"
35 #include "bctrace.h"
36 #include "bdcreate.h"
37 #include "brender.h"
38 #include "cache.h"
39 #include "channel.h"
40 #include "channeldb.h"
41 #include "channelinfo.h"
42 #include "clip.h"
43 #include "clipedls.h"
44 #include "commercials.h"
45 #include "confirmsave.h"
46 #include "convert.h"
47 #include "cplayback.h"
48 #include "ctimebar.h"
49 #include "cwindowgui.h"
50 #include "cwindow.h"
51 #include "cwindowtool.h"
52 #include "bchash.h"
53 #include "devicedvbinput.inc"
54 #include "dvdcreate.h"
55 #include "editpanel.h"
56 #include "edl.h"
57 #include "edlsession.h"
58 #include "errorbox.h"
59 #include "fileformat.h"
60 #include "file.h"
61 #include "filesystem.h"
62 #include "filexml.h"
63 #include "floatautos.h"
64 #include "framecache.h"
65 #include "gwindow.h"
66 #include "gwindowgui.h"
67 #include "keyframegui.h"
68 #include "indexfile.h"
69 #include "intautos.h"
70 #include "interlacemodes.h"
71 #include "language.h"
72 #include "levelwindowgui.h"
73 #include "levelwindow.h"
74 #include "loadfile.inc"
75 #include "localsession.h"
76 #include "maincursor.h"
77 #include "mainerror.h"
78 #include "mainindexes.h"
79 #include "mainmenu.h"
80 #include "mainprogress.h"
81 #include "mainsession.h"
82 #include "mainundo.h"
83 #include "mbuttons.h"
84 #include "mixersalign.h"
85 #include "mutex.h"
86 #include "mwindowgui.h"
87 #include "mwindow.h"
88 #include "new.h"
89 #include "panautos.h"
90 #include "patchbay.h"
91 #include "playback3d.h"
92 #include "playbackengine.h"
93 #include "plugin.h"
94 #include "pluginserver.h"
95 #include "pluginset.h"
96 #include "preferences.h"
97 #include "proxy.h"
98 #include "record.h"
99 #include "recordmonitor.h"
100 #include "recordlabel.h"
101 #include "removefile.h"
102 #include "render.h"
103 #include "resourcethread.h"
104 #include "savefile.inc"
105 #include "samplescroll.h"
106 #include "sha1.h"
107 #include "shuttle.h"
108 #include "sighandler.h"
109 #include "splashgui.h"
110 #include "statusbar.h"
111 #include "theme.h"
112 #include "threadloader.h"
113 #include "timebar.h"
114 #include "timelinepane.h"
115 #include "tipwindow.h"
116 #include "trackcanvas.h"
117 #include "track.h"
118 #include "tracking.h"
119 #include "trackscroll.h"
120 #include "tracks.h"
121 #include "transition.h"
122 #include "transportque.h"
123 #include "vframe.h"
124 #include "vtrack.h"
125 #include "versioninfo.h"
126 #include "vicon.h"
127 #include "videodevice.inc"
128 #include "videowindow.h"
129 #include "vplayback.h"
130 #include "vwindowgui.h"
131 #include "vwindow.h"
132 #include "wavecache.h"
133 #include "wintv.h"
134 #include "wwindow.h"
135 #include "x10tv.h"
136 #include "zoombar.h"
137 #include "zwindow.h"
138 #include "zwindowgui.h"
139 #include "exportedl.h"
140
141 #include "defaultformats.h"
142 #include "ntsczones.h"
143
144 #include <stdint.h>
145 #include <stdlib.h>
146 #include <unistd.h>
147 #include <fcntl.h>
148 #include <string.h>
149 #include <sys/types.h>
150 #include <sys/stat.h>
151 #include <sys/time.h>
152 #include <sys/mman.h>
153 #include <sys/file.h>
154 #include <limits.h>
155 #include <errno.h>
156
157
158 extern "C"
159 {
160
161
162
163
164 // Hack for libdv to remove glib dependancy
165
166 // void
167 // g_log (const char    *log_domain,
168 //        int  log_level,
169 //        const char    *format,
170 //        ...)
171 // {
172 // }
173 //
174 // void
175 // g_logv (const char    *log_domain,
176 //        int  log_level,
177 //        const char    *format,
178 //        ...)
179 // {
180 // }
181 //
182
183 }
184
185 extern long cin_timezone;
186
187 ArrayList<PluginServer*>* MWindow::plugindb = 0;
188 Commercials* MWindow::commercials = 0;
189
190
191 MWindow::MWindow()
192  : Thread(1, 0, 0)
193 {
194         run_lock = new Mutex("MWindow::run_lock");
195         plugin_gui_lock = new Mutex("MWindow::plugin_gui_lock");
196         dead_plugin_lock = new Mutex("MWindow::dead_plugin_lock");
197         vwindows_lock = new Mutex("MWindow::vwindows_lock");
198         zwindows_lock = new Mutex("MWindow::zwindows_lock");
199         brender_lock = new Mutex("MWindow::brender_lock");
200         keyframe_gui_lock = new Mutex("MWindow::keyframe_gui_lock");
201
202         playback_3d = 0;
203         splash_window = 0;
204         undo = 0;
205         undo_command = COMMAND_NONE;
206         defaults = 0;
207         assets = 0;
208         //commercials = 0;
209         commercial_active = 0;
210         audio_cache = 0;
211         video_cache = 0;
212         frame_cache = 0;
213         wave_cache = 0;
214         preferences = 0;
215         session = 0;
216         theme = 0;
217         mainindexes = 0;
218         mainprogress = 0;
219         brender = 0;
220         brender_active = 0;
221         strcpy(cin_lang,"en");
222         channeldb_buz =  new ChannelDB;
223         channeldb_v4l2jpeg =  new ChannelDB;
224         plugin_guis = 0;
225         dead_plugins = 0;
226         keyframe_threads = 0;
227         create_bd = 0;
228         create_dvd = 0;
229         batch_render = 0;
230         convert_render = 0;
231         render = 0;
232         edl = 0;
233         gui = 0;
234         cwindow = 0;
235         awindow = 0;
236         gwindow = 0;
237         twindow = 0;
238         wwindow = 0;
239         lwindow = 0;
240         sighandler = 0;
241         restart_status = 0;
242         screens = 1;
243         appimageDir = getenv("APPDIR"); //NULL if not running as appimage
244         in_destructor = 0;
245         speed_edl = 0;
246         beeper = 0;
247         redraw_tracks = 0;
248         shuttle = 0;
249         wintv = 0;
250         x10tv = 0;
251         mixers_align = 0;
252 }
253
254
255 // Need to delete brender temporary here.
256 MWindow::~MWindow()
257 {
258         run_lock->lock("MWindow::~MWindow");
259         in_destructor = 1;
260 //printf("MWindow::~MWindow %d\n", __LINE__);
261         if( wwindow && wwindow->is_running() )
262                 wwindow->close_window();
263         if( twindow && twindow->is_running() )
264                 twindow->close_window();
265         gui->remote_control->deactivate();
266         gui->record->stop();
267 #ifdef HAVE_DVB
268         gui->channel_info->stop();
269 #endif
270         delete beeper;          beeper = 0;
271         delete redraw_tracks;   redraw_tracks = 0;
272         delete create_bd;       create_bd = 0;
273         delete create_dvd;      create_dvd = 0;
274 #ifdef HAVE_SHUTTLE
275         delete shuttle;         shuttle = 0;
276 #endif
277 #ifdef HAVE_WINTV
278         delete wintv;           wintv = 0;
279 #endif
280 #ifdef HAVE_X10TV
281         delete x10tv;           x10tv = 0;
282 #endif
283         delete batch_render;    batch_render = 0;
284         delete convert_render;  convert_render = 0;
285         delete render;          render = 0;
286         delete mixers_align;    mixers_align = 0;
287         commit_commercial();
288         if( commercials && !commercials->remove_user() ) commercials = 0;
289         close_mixers();
290         if( speed_edl ) { speed_edl->remove_user();  speed_edl = 0; }
291 // Save defaults for open plugins
292         plugin_gui_lock->lock("MWindow::~MWindow");
293         for(int i = 0; i < plugin_guis->size(); i++) {
294                 plugin_guis->get(i)->hide_gui();
295                 delete_plugin(plugin_guis->get(i));
296         }
297         plugin_gui_lock->unlock();
298         hide_keyframe_guis();
299         clean_indexes();
300         clean_backups();
301         save_defaults();
302 // Give up and go to a movie
303 //  cant run valgrind if this is used
304
305         gui->del_keyboard_listener(
306                 (int (BC_WindowBase::*)(BC_WindowBase *))
307                 &MWindowGUI::keyboard_listener);
308         reset_caches(0);
309 #if 0
310 // release the hounds
311         if( awindow && awindow->gui ) awindow->gui->close(0);
312         if( cwindow && cwindow->gui ) cwindow->gui->close(0);
313         if( lwindow && lwindow->gui ) lwindow->gui->close(0);
314         if( gwindow && gwindow->gui ) gwindow->gui->close(0);
315         vwindows.remove_all_objects();
316         zwindows.remove_all_objects();
317         gui->close(0);
318         if( awindow ) awindow->join();
319         if( cwindow ) cwindow->join();
320         if( lwindow ) lwindow->join();
321         if( gwindow ) gwindow->join();
322         join();
323 #else
324 // one at a time, or nouveau chokes
325 #define close_gui(win) if( win ) { \
326   if( win->gui ) win->gui->close(0); \
327   win->join(); }
328         close_gui(awindow);
329         close_gui(cwindow);
330         close_gui(lwindow);
331         close_gui(gwindow);
332         vwindows.remove_all_objects();
333         zwindows.remove_all_objects();
334         gui->close(0);
335         join();
336 #endif
337         dead_plugins->remove_all_objects();
338 // must delete theme before destroying plugindb
339 //  theme destructor will be deleted by delete_plugins
340         delete theme;           theme = 0;
341         delete_plugins();
342         finit_error();
343         keyframe_threads->remove_all_objects();
344         colormodels.remove_all_objects();
345         delete awindow;         awindow = 0;
346         delete lwindow;         lwindow = 0;
347         delete twindow;         twindow = 0;
348         delete wwindow;         wwindow = 0;
349         delete gwindow;         gwindow = 0;
350         delete cwindow;         cwindow = 0;
351         delete gui;             gui = 0;
352         delete mainindexes;     mainindexes = 0;
353         delete mainprogress;    mainprogress = 0;
354  // delete the caches after the assets
355         if( audio_cache ) { audio_cache->remove_user();  audio_cache = 0; }
356         if( video_cache ) { video_cache->remove_user();  video_cache = 0; }
357         delete frame_cache;     frame_cache = 0;
358         delete wave_cache;      wave_cache = 0;
359         delete plugin_guis;     plugin_guis = 0;
360         delete dead_plugins;    dead_plugins = 0;
361         delete keyframe_threads;  keyframe_threads = 0;
362         delete undo;            undo = 0;
363         delete preferences;     preferences = 0;
364         delete exportedl;       exportedl = 0;
365         delete session;         session = 0;
366         delete defaults;        defaults = 0;
367         delete assets;          assets = 0;
368         delete splash_window;   splash_window = 0;
369         if( !edl->Garbage::remove_user() ) edl = 0;
370         delete channeldb_buz;
371         delete channeldb_v4l2jpeg;
372 // This must be last thread to exit
373         delete playback_3d;     playback_3d = 0;
374         delete dead_plugin_lock;
375         delete plugin_gui_lock;
376         delete vwindows_lock;
377         delete zwindows_lock;
378         delete brender_lock;
379         delete keyframe_gui_lock;
380         colormodels.remove_all_objects();
381         interlace_project_modes.remove_all_objects();
382         interlace_asset_modes.remove_all_objects();
383         sighandler->terminate();
384         delete sighandler;
385         delete run_lock;
386 }
387
388
389 void MWindow::quit()
390 {
391         gui->set_done(0);
392 }
393
394 void MWindow::init_error()
395 {
396         MainError::init_error(this);
397 }
398
399 void MWindow::finit_error()
400 {
401         MainError::finit_error();
402 }
403
404 void MWindow::create_defaults_path(char *string, const char *config_file)
405 {
406 // set the .bcast path
407         FileSystem fs;
408
409         sprintf(string, "%s/", File::get_config_path());
410         fs.complete_path(string);
411         if(!fs.is_dir(string))
412                 fs.create_dir(string);
413
414 // load the defaults
415         strcat(string, config_file);
416 }
417 const char *MWindow::default_std()
418 {
419         char buf[BCTEXTLEN], *p = 0;
420
421         int fd = open(TIMEZONE_NAME, O_RDONLY);
422         if( fd >= 0 ) {
423                 int l = read(fd, buf, sizeof(buf)-1);
424                 close(fd);
425                 if( l > 0 ) {
426                         if( buf[l-1] == '\n' ) --l;
427                         buf[l] = 0;
428                         p = buf;
429                 }
430         }
431         if( !p ) {
432                 int l = readlink(LOCALTIME_LINK, buf, sizeof(buf)-1);
433                 if( l > 0 ) {
434                         buf[l] = 0;
435                         if( !(p=strstr(buf, ZONEINFO_STR)) != 0 ) return "PAL";
436                         p += strlen(ZONEINFO_STR);
437                 }
438         }
439
440         if( p ) {
441                 for( int i=0; ntsc_zones[i]; ++i ) {
442                         if( !strcmp(ntsc_zones[i], p) )
443                                 return "NTSC";
444                 }
445         }
446
447         p = getenv("TZ");
448         if( p ) {
449                 for( int i=0; ntsc_zones[i]; ++i ) {
450                         if( !strcmp(ntsc_zones[i], p) )
451                                 return "NTSC";
452                 }
453         }
454
455 // cin_timezone: Seconds west of UTC.  240sec/deg
456         double tz_deg =  -cin_timezone / 240.;
457 // from Honolulu = -10, to New York = -5, 15deg/hr   lat -150..-75
458         return tz_deg >= -10*15 && tz_deg <= -5*15 ? "NTSC" : "PAL";
459 }
460
461 void MWindow::fill_preset_defaults(const char *preset, EDLSession *session)
462 {
463         struct formatpresets *fpr;
464
465         for(fpr = &format_presets[0]; fpr->name; fpr++)
466         {
467                 if(strcmp(_(fpr->name), preset) == 0)
468                 {
469                         session->audio_channels = fpr->audio_channels;
470                         session->audio_tracks = fpr->audio_tracks;
471                         session->sample_rate = fpr->sample_rate;
472                         session->video_channels = fpr->video_channels;
473                         session->video_tracks = fpr->video_tracks;
474                         session->frame_rate = fpr->frame_rate;
475                         session->output_w = fpr->output_w;
476                         session->output_h = fpr->output_h;
477                         session->aspect_w = fpr->aspect_w;
478                         session->aspect_h = fpr->aspect_h;
479                         session->interlace_mode = fpr->interlace_mode;
480                         session->color_model = fpr->color_model;
481                         return;
482                 }
483         }
484 }
485
486 const char *MWindow::get_preset_name(int index)
487 {
488         if(index < 0 || index >= (int)MAX_NUM_PRESETS)
489                 return "Error";
490         return _(format_presets[index].name);
491 }
492
493
494 void MWindow::init_defaults(BC_Hash* &defaults, char *config_path)
495 {
496         char path[BCTEXTLEN];
497 // Use user supplied path
498         if(config_path[0])
499                 strcpy(path, config_path);
500         else
501                 create_defaults_path(path, CONFIG_FILE);
502
503         delete defaults;
504         defaults = new BC_Hash(path);
505         defaults->load();
506 }
507
508
509 void MWindow::check_language()
510 {
511         char pref_locale[BCSTRLEN];
512         strcpy(pref_locale, DEFAULT_LOCALE);
513         defaults->get("LOCALE",pref_locale);
514 // set LANGUAGE if pref locale != sys
515         if( strcmp(pref_locale, DEFAULT_LOCALE) )
516                 setenv("LANGUAGE", pref_locale, 1);
517
518         char curr_lang[BCTEXTLEN]; curr_lang[0] = 0;
519         const char *env_lang = getenv("LANGUAGE");
520         if( !env_lang ) env_lang = getenv("LC_ALL");
521         if( !env_lang ) env_lang = getenv("LANG");
522         if( !env_lang ) {
523                 snprintf(curr_lang, sizeof(curr_lang), "%s_%s-%s",
524                         BC_Resources::language, BC_Resources::region, BC_Resources::encoding);
525                 env_lang = curr_lang;
526         }
527         char last_lang[BCTEXTLEN]; last_lang[0] = 0;
528         defaults->get("LAST_LANG",last_lang);
529         if( strcmp(env_lang,last_lang)) {
530                 printf("lang changed from '%s' to '%s'\n", last_lang, env_lang);
531                 defaults->update("LAST_LANG",env_lang);
532                 char plugin_path[BCTEXTLEN];
533                 create_defaults_path(plugin_path, PLUGIN_FILE);
534                 ::remove(plugin_path);
535                 char ladspa_path[BCTEXTLEN];
536                 create_defaults_path(ladspa_path, LADSPA_FILE);
537                 ::remove(ladspa_path);
538                 defaults->save();
539         }
540         if( strlen(env_lang) > 1 &&
541             ( env_lang[2] == 0 || env_lang[2] == '_'  || env_lang[2] == '.' ) ) {
542                 cin_lang[0] = env_lang[0];  cin_lang[1] = env_lang[1];  cin_lang[2] = 0;
543         }
544         else
545                 strcpy(cin_lang, "en");
546 }
547
548 void MWindow::get_plugin_path(char *path, const char *plug_dir, const char *fs_path)
549 {
550         char *base_path = FileSystem::basepath(fs_path), *bp = base_path;
551         if( plug_dir ) {
552                 const char *dp = plug_dir;
553                 while( *bp && *dp && *bp == *dp ) { ++bp; ++dp; }
554                 bp = !*dp && *bp == '/' ? bp+1 : base_path;
555         }
556         strcpy(path, bp);
557         delete [] base_path;
558 }
559
560 /**
561 * @brief Load plugins according to an index file.
562
563 * @details Builds an ArrayList of plugin servers only if there is no
564 * mismatch for file layout version, index identifier, or executable
565 * timestamp mismatch for the built-in plugins. If OK, add the plugin
566 * servers to the global list.
567
568 * @note If an error is returned the index file needs to be rebuilt, and
569 * then this function must be called again.
570 * There are two types of index files, with the same layout internally.
571 * One called "Cinelerra_plugins" for built-ins, ffmpeg and lv2 .
572 * The other type "ladspa_plugins.index_id" where index_id is either the
573 * path or the $CIN_BUILD identifier if the path is from the running
574 * AppImage itself. If there are multiple ladspa directories in the
575 * path, there will be multiple index files.
576
577 * @return  -1 if file no open, 0 if OK, 1 if error. 
578 */
579 int MWindow::load_plugin_index(MWindow *mwindow, FILE *fp, const char *plugin_dir, const char *index_id)
580 {
581         if( !fp ) return -1;
582         struct stat st;
583         fstat (fileno(fp), &st);        // don't bother if the file has just been created.
584         if( st.st_size < 4 ) return 1;
585         
586 // load index
587         fseek(fp, 0, SEEK_SET);
588         int ret = 0;
589         char index_line[BCTEXTLEN];
590         int index_version = -1, len = strlen(index_id);
591         if( !fgets(index_line, BCTEXTLEN, fp) ||
592             sscanf(index_line, "%d", &index_version) != 1 ||
593             index_version != PLUGIN_FILE_VERSION ||
594             !fgets(index_line, BCTEXTLEN, fp) ||
595             (int)strlen(index_line)-1 != len || index_line[len] != '\n' ||
596             strncmp(index_line, index_id, len) != 0 ) {
597 //              printf("index file mismatch, version %d, index id length %d, expected id %s, file id %s\n", index_version, len, index_id, index_line);
598                 ret = 1;
599         }
600         
601         ArrayList<PluginServer*> plugins;
602         while( !ret && !feof(fp) && fgets(index_line, BCTEXTLEN, fp) ) {
603                 if( index_line[0] == ';' ) continue;
604                 if( index_line[0] == '#' ) continue;
605                 int type = PLUGIN_TYPE_UNKNOWN;
606                 char path[BCTEXTLEN], title[BCTEXTLEN];
607                 int64_t mtime = 0;
608                 if( PluginServer::scan_table(index_line, type, path, title, mtime) ) {
609 //                      printf("PluginServer::scan_table failed for %s\n", index_line);
610                         ret = 1; continue;
611                 }
612                 PluginServer *server = 0;
613                 switch( type ) {
614                 case PLUGIN_TYPE_BUILTIN:
615                 case PLUGIN_TYPE_EXECUTABLE:
616                 case PLUGIN_TYPE_LADSPA: {
617                         char plugin_path[BCTEXTLEN];  struct stat st;
618                         sprintf(plugin_path, "%s/%s", plugin_dir, path);
619                         if( stat(plugin_path, &st) || st.st_mtime != mtime ) {
620 //                              printf("Plugin %s index time %ld, file time %ld\n", plugin_path, mtime, st.st_mtime); 
621                                 ret = 1; continue;
622                         }
623                         server = new PluginServer(mwindow, plugin_path, type);
624                         break; }
625                 case PLUGIN_TYPE_FFMPEG: {
626                         server = new_ffmpeg_server(mwindow, path);
627                         break; }
628                 case PLUGIN_TYPE_LV2: {
629                         server = new_lv2_server(mwindow, path);
630                         break; }
631                 }
632                 if( !server ) continue;
633                 plugins.append(server);
634 // Create plugin server from index entry
635                 server->set_title(title);
636                 if( server->read_table(index_line) ) {
637 //                      printf("server->read_table failed for title %s, %s\n", title, index_line);
638                         ret = 1;  continue;
639                 }
640         }
641
642         if( !ret )
643                 ret = check_plugin_index(plugins, plugin_dir, ".");
644
645         if( !ret )
646                 add_plugins(plugins);
647         else
648                 plugins.remove_all_objects();
649
650         return ret;
651 }
652
653 int MWindow::check_plugin_index(ArrayList<PluginServer*> &plugins,
654         const char *plug_dir, const char *plug_path)
655 {
656         char plugin_path[BCTEXTLEN];
657         sprintf(plugin_path, "%s/%s", plug_dir, plug_path);
658         FileSystem fs;
659         fs.set_filter( "[*.plugin][*.so]" );
660         if( fs.update(plugin_path) )
661                 return 1;
662
663         for( int i=0; i<fs.dir_list.total; ++i ) {
664                 char fs_path[BCTEXTLEN];
665                 get_plugin_path(fs_path, 0, fs.dir_list[i]->path);
666                 if( fs.is_dir(fs_path) ) {
667                         get_plugin_path(plugin_path, plug_dir, fs_path);
668                         if( check_plugin_index(plugins, plug_dir, plugin_path) )
669                                 return 1;
670                 }
671                 else if( !plugin_exists(fs_path, plugins) )
672                         return 1;
673         }
674         return 0;
675 }
676 /*
677 * @brief Load built-in and LV2 plugins as specified in index file,
678 *        rebuild the index file if needed.
679 */
680 int MWindow::init_plugins(MWindow *mwindow, Preferences *preferences)
681 {
682         if( !plugindb )
683                 plugindb = new ArrayList<PluginServer*>;
684         init_ffmpeg();
685         char index_path[BCTEXTLEN], plugin_path[BCTEXTLEN], index_id[BCTEXTLEN];
686         create_defaults_path(index_path, PLUGIN_FILE);
687         char *plugin_dir = FileSystem::basepath(preferences->plugin_dir);
688         strcpy(plugin_path, plugin_dir);  delete [] plugin_dir;
689         // index_id is 2nd line of the index file, normally full plugin path,
690         // but fixed value if AppImage because the path changes on each run.
691         // And if the second line does not match on the next run the index is rebuilt.
692         if( mwindow->appimageDir ) strcpy(index_id, getenv("CINGG_BUILD"));
693         else strcpy(index_id, plugin_path);
694         FILE *fp = fopen(index_path,"a+");
695         if( !fp ) {
696                 fprintf(stderr,_("MWindow::init_plugins: "
697                         "can't open plugin index: %s\n"), index_path);
698                 return -1;
699         }
700         int fd = fileno(fp), ret = -1;
701         if( !flock(fd, LOCK_EX) ) {
702                 fseek(fp, 0, SEEK_SET);
703                 ret = load_plugin_index(mwindow, fp, plugin_path, index_id);
704         }
705         if( ret > 0 ) {
706                 ftruncate(fd, 0);
707                 fseek(fp, 0, SEEK_SET);
708                 printf("build plugin index for: %s\n", plugin_path);
709                 fprintf(fp, "%d\n", PLUGIN_FILE_VERSION);
710                 fprintf(fp, "%s\n", index_id);
711                 init_plugin_index(mwindow, preferences, fp, plugin_path);
712                 init_ffmpeg_index(mwindow, preferences, fp);
713                 init_lv2_index(mwindow, preferences, fp);
714                 fseek(fp, 0, SEEK_SET);
715                 ret = load_plugin_index(mwindow, fp, plugin_path, index_id);
716         }
717         if( ret ) {
718                 fprintf(stderr,_("MWindow::init_plugins: "
719                         "can't %s plugin index: %s\n"),
720                         ret>0 ? _("create") : _("lock"), index_path);
721         }
722         fclose(fp);
723         return ret;
724 }
725
726 /*
727 * @brief Load ladspa plugins as specified in index files, for each ladspa
728 *        directory keep a separate index file. Rebuild index file(s) if needed.
729 **/
730 int MWindow::init_ladspa_plugins(MWindow *mwindow, Preferences *preferences)
731 {
732 #ifdef HAVE_LADSPA
733         char *path = getenv("LADSPA_PATH");
734         char ladspa_path[BCTEXTLEN];
735         if( !path ) {                   // if no env var, use CinGG's own ladspa dir
736                 strncpy(ladspa_path, File::get_ladspa_path(), sizeof(ladspa_path));
737                 path = ladspa_path;
738         }
739         for( int len=0; *path; path+=len ) {
740                 char *cp = strchr(path,':');
741                 len = !cp ? strlen(path) : cp-path;
742                 char index_path[BCTEXTLEN], plugin_path[BCTEXTLEN], index_id[BCTEXTLEN];
743                 memcpy(plugin_path, path, len);  plugin_path[len] = 0;
744                 if( cp ) ++len;
745                 char *plugin_dir = FileSystem::basepath(plugin_path);
746                 strcpy(plugin_path, plugin_dir);  delete [] plugin_dir;
747                 create_defaults_path(index_path, LADSPA_FILE);
748                 cp = index_path + strlen(index_path);
749                 // If the first part of the plugin_path matches the APPDIR, we are
750                 // referring to CinGG's ladspa, replace the path by a fixed ID. APPDIR
751                 // only exists if we are running as AppImage (with variable mount points).
752                 if( mwindow->appimageDir && strncmp(plugin_path, mwindow->appimageDir, strlen(mwindow->appimageDir)) == 0 )
753                         strcpy(index_id, getenv("CINGG_BUILD"));
754                 else strcpy(index_id, plugin_path);
755
756                 // Concatenate the path, replacing '/' with '_'. 
757                 for( char *bp=index_id; *bp!=0; ++bp )
758                         *cp++ = *bp=='/' ? '_' : *bp;
759                 *cp = 0;
760                 FILE *fp = fopen(index_path,"a+");
761                 if( !fp ) {
762                         fprintf(stderr,_("MWindow::init_ladspa_plugins: "
763                                 "can't open ladspa plugin index: %s\n"), index_path);
764                         continue;
765                 }
766                 int fd = fileno(fp), ret = -1;
767                 if( !flock(fd, LOCK_EX) ) {
768                         fseek(fp, 0, SEEK_SET);
769                         ret = load_plugin_index(mwindow, fp, plugin_path, index_id);
770                 }
771                 if( ret > 0 ) {
772                         ftruncate(fd, 0);
773                         fseek(fp, 0, SEEK_SET);
774                         printf("build ladspa plugin index for: %s\n", plugin_path);
775                         fprintf(fp, "%d\n", PLUGIN_FILE_VERSION);
776                         fprintf(fp, "%s\n", index_id);
777                         init_plugin_index(mwindow, preferences, fp, plugin_path);
778                         fseek(fp, 0, SEEK_SET);
779                         ret = load_plugin_index(mwindow, fp, plugin_path, index_id);
780                 }
781                 if( ret ) {
782                         fprintf(stderr,_("MWindow::init_ladspa_plugins: "
783                                 "can't %s ladspa plugin index: %s\n"),
784                                 ret>0 ? _("create") : _("lock"), index_path);
785                 }
786                 fclose(fp);
787         }
788 #endif
789         return 1;
790 }
791
792 void MWindow::init_plugin_index(MWindow *mwindow, Preferences *preferences,
793         FILE *fp, const char *plugin_dir)
794 {
795         int idx = PLUGIN_IDS;
796         if( plugindb ) {
797                 for( int i=0; i<plugindb->size(); ++i ) {
798                         PluginServer *server = plugindb->get(i);
799                         if( server->dir_idx >= idx )
800                                 idx = server->dir_idx+1;
801                 }
802         }
803         scan_plugin_index(mwindow, preferences, fp, plugin_dir, ".", idx);
804 }
805
806 void MWindow::scan_plugin_index(MWindow *mwindow, Preferences *preferences, FILE *fp,
807         const char *plug_dir, const char *plug_path, int &idx)
808 {
809         char plugin_path[BCTEXTLEN];
810         sprintf(plugin_path, "%s/%s", plug_dir, plug_path);
811         FileSystem fs;
812         fs.set_filter( "[*.plugin][*.so][*.dll]" );
813         int result = fs.update(plugin_path);
814         if( result || !fs.dir_list.total ) return;
815         int vis_id = idx++;
816
817         for( int i=0; i<fs.dir_list.total; ++i ) {
818                 char fs_path[BCTEXTLEN], path[BCTEXTLEN];
819                 get_plugin_path(fs_path, 0, fs.dir_list[i]->path);
820                 get_plugin_path(path, plug_dir, fs_path);
821                 if( fs.is_dir(fs_path) ) {
822                         scan_plugin_index(mwindow, preferences, fp, plug_dir, path, idx);
823                         continue;
824                 }
825                 if( plugin_exists(path) ) continue;
826                 struct stat st;
827                 if( stat(fs_path, &st) ) continue;
828                 int64_t mtime = st.st_mtime;
829                 PluginServer server(mwindow, fs_path, PLUGIN_TYPE_UNKNOWN);
830                 result = server.open_plugin(1, preferences, 0, 0);
831                 if( !result ) {
832                         server.write_table(fp, path, vis_id, mtime);
833                         server.close_plugin();
834                 }
835                 else if( result == PLUGINSERVER_IS_LAD ) {
836                         int lad_index = 0;
837                         for(;;) {
838                                 PluginServer ladspa(mwindow, fs_path, PLUGIN_TYPE_LADSPA);
839                                 ladspa.set_lad_index(lad_index++);
840                                 result = ladspa.open_plugin(1, preferences, 0, 0);
841                                 if( result ) break;
842                                 ladspa.write_table(fp, path, PLUGIN_LADSPA_ID, mtime);
843                                 ladspa.close_plugin();
844                         }
845                 }
846         }
847 }
848
849 void MWindow::add_plugins(ArrayList<PluginServer*> &plugins)
850 {
851         for( int i=0; i<plugins.size(); ++i )
852                 plugindb->append(plugins[i]);
853         plugins.remove_all();
854 }
855
856 void MWindow::init_plugin_tips(ArrayList<PluginServer*> &plugins, const char *lang)
857 {
858         const char *dat_path = File::get_cindat_path();
859         char msg_path[BCTEXTLEN];
860         FILE *fp = 0;
861         if( BC_Resources::language[0] ) {
862                 snprintf(msg_path, sizeof(msg_path), "%s/info/plugins.%s",
863                         dat_path, lang);
864                 fp = fopen(msg_path, "r");
865         }
866         if( !fp ) {
867                 snprintf(msg_path, sizeof(msg_path), "%s/info/plugins.txt",
868                         dat_path);
869                 fp = fopen(msg_path, "r");
870         }
871         if( !fp ) return;
872         char text[BCTEXTLEN];
873         char *tp = text, *ep = tp + sizeof(text)-1;
874         char title[BCTEXTLEN];
875         title[0] = 0;
876         int no = 0;
877         for(;;) {
878                 ++no;  int done = 1;
879                 char line[BCTEXTLEN], *cp = line;
880                 if( fgets(line,sizeof(line)-1,fp) ) {
881                         if( *cp == '#' ) continue;
882                         done = *cp == ' ' || *cp == '\t' ? 0 : -1;
883                 }
884                 if( done ) {
885                         if( tp > text && *--tp == '\n' ) *tp = 0;
886                         if( title[0] ) {
887                                 int idx = plugins.size();
888                                 while( --idx>=0 && strcmp(plugins[idx]->title, title) );
889                                 if( idx >= 0 ) {
890                                         delete [] plugins[idx]->tip;
891                                         plugins[idx]->tip = cstrdup(text);
892                                 }
893                                 title[0] = 0;
894                         }
895                         if( done > 0 ) break;
896                         tp = text;  *tp = 0;
897                         char *dp = strchr(cp, ':');
898                         if( !dp ) {
899                                 printf("plugin tips: error on line %d\n", no);
900                                 continue;
901                         }
902                         char *bp = title;
903                         while( cp < dp ) *bp++ = *cp++;
904                         *bp = 0;
905                         ++cp;
906                 }
907
908                 while( *cp == ' ' || *cp == '\t' ) ++cp;
909                 for( ; tp<ep && (*tp=*cp)!=0; ++tp,++cp );
910         }
911         fclose(fp);
912 }
913
914 void MWindow::delete_plugins()
915 {
916         plugindb->remove_all_objects();
917         delete plugindb;
918         plugindb = 0;
919 }
920
921 void MWindow::search_plugindb(int do_audio,
922                 int do_video,
923                 int is_realtime,
924                 int is_transition,
925                 int is_theme,
926                 ArrayList<PluginServer*> &results)
927 {
928 // Get plugins
929         for(int i = 0; i < MWindow::plugindb->total; i++)
930         {
931                 PluginServer *current = MWindow::plugindb->get(i);
932
933                 if(current->audio == do_audio &&
934                         current->video == do_video &&
935                         (current->realtime == is_realtime || is_realtime < 0) &&
936                         current->transition == is_transition &&
937                         current->theme == is_theme)
938                         results.append(current);
939         }
940
941 // Alphabetize list by title
942         int done = 0;
943         while(!done)
944         {
945                 done = 1;
946
947                 for(int i = 0; i < results.total - 1; i++)
948                 {
949                         PluginServer *value1 = results[i];
950                         PluginServer *value2 = results[i + 1];
951                         if(strcmp(_(value1->title), _(value2->title)) > 0)
952                         {
953                                 done = 0;
954                                 results[i] = value2;
955                                 results[i + 1] = value1;
956                         }
957                 }
958         }
959 }
960
961 PluginServer* MWindow::scan_plugindb(char *title,
962                 int data_type)
963 {
964 //      if(data_type < 0)
965 //      {
966 //              printf("MWindow::scan_plugindb data_type < 0\n");
967 //              return 0;
968 //      }
969
970         for(int i = 0; i < plugindb->total; i++)
971         {
972                 PluginServer *server = plugindb->get(i);
973                 if(server->title &&
974                         !strcasecmp(server->title, title) &&
975                         (data_type < 0 ||
976                                 (data_type == TRACK_AUDIO && server->audio) ||
977                                 (data_type == TRACK_VIDEO && server->video)))
978                         return plugindb->get(i);
979         }
980         return 0;
981 }
982
983 // repair session files with xlated plugin titles
984 void MWindow::fix_plugin_title(char *title)
985 {
986         for(int i = 0; i < plugindb->total; i++) {
987                 PluginServer *server = plugindb->get(i);
988                 if( !server->title ) continue;
989                 const char *server_title = server->title;
990                 if( !bstrcasecmp(title, _(server_title)) ) {
991                         strcpy(title, server_title);
992                         return;
993                 }
994         }
995 }
996
997 int MWindow::plugin_exists(const char *plugin_path, ArrayList<PluginServer*> &plugins)
998 {
999         for( int i=0; i<plugins.size(); ++i )
1000                 if( !strcmp(plugin_path, plugins[i]->get_path()) ) return 1;
1001         return 0;
1002 }
1003
1004 int MWindow::plugin_exists(char *plugin_path)
1005 {
1006         return !plugindb ? 0 : plugin_exists(plugin_path, *plugindb);
1007 }
1008
1009 void MWindow::remove_plugin_index()
1010 {
1011         char index_path[BCTEXTLEN];
1012         MWindow::create_defaults_path(index_path, PLUGIN_FILE);
1013         ::remove(index_path);
1014 }
1015
1016 void MWindow::init_preferences()
1017 {
1018         preferences = new Preferences;
1019         preferences->load_defaults(defaults);
1020         File::setenv_path("LV2_PATH",preferences->lv2_path, 1);
1021         session = new MainSession(this);
1022         session->load_defaults(defaults);
1023         // set x11_host, screens, window_config
1024         screens = session->set_default_x11_host();
1025         BC_Signals::set_trap_hook(trap_hook, this);
1026         BC_Signals::set_catch_segv(preferences->trap_sigsegv);
1027         BC_Signals::set_catch_intr(preferences->trap_sigintr);
1028         if( preferences->trap_sigsegv || preferences->trap_sigintr ) {
1029                 BC_Trace::enable_locks();
1030         }
1031         else {
1032                 BC_Trace::disable_locks();
1033         }
1034         BC_WindowBase::get_resources()->popupmenu_btnup = preferences->popupmenu_btnup;
1035         BC_WindowBase::get_resources()->textbox_focus_policy = preferences->textbox_focus_policy;
1036         BC_WindowBase::get_resources()->grab_input_focus = preferences->grab_input_focus;
1037         YUV::yuv.yuv_set_colors(preferences->yuv_color_space, preferences->yuv_color_range);
1038 }
1039
1040 void MWindow::clean_backups()
1041 {
1042     FileSystem fs;
1043     int total_excess;
1044     long oldest = 0;
1045     int oldest_item = -1;
1046     char string[BCTEXTLEN];
1047
1048 // Delete extra backups
1049     fs.set_filter("backup*.prev_*");
1050     fs.complete_path(preferences->index_directory);
1051     fs.update(preferences->index_directory);
1052
1053     // set to 50 for now
1054     // total_excess = fs.dir_list.total - preferences->index_count;
1055     total_excess = fs.dir_list.total - 50;
1056     printf("Total excess of backups: %i \n", total_excess);
1057
1058 //printf("MWindow::clean_backups 1 %d\n", fs.dir_list.total);
1059
1060     while(total_excess > 0)
1061     {
1062 // Get oldest
1063        for(int i = 0; i < fs.dir_list.total; i++)
1064        {
1065            fs.join_names(string, preferences->index_directory, fs.dir_list[i]->name);
1066
1067            if(i == 0 || fs.get_date(string) <= oldest)
1068            {
1069                oldest = fs.get_date(string);
1070                oldest_item = i;
1071            }
1072        }
1073
1074        if(oldest_item >= 0)
1075        {
1076 // Remove backup file
1077            fs.join_names(string,
1078                preferences->index_directory,
1079                fs.dir_list[oldest_item]->name);
1080 //printf("MWindow::clean_backups 1 %s\n", string);
1081            if(remove(string))
1082                perror("delete_backups");
1083            delete fs.dir_list[oldest_item];
1084            fs.dir_list.remove_number(oldest_item);
1085
1086        }
1087
1088        total_excess--;
1089     }
1090 }
1091
1092 void MWindow::clean_indexes()
1093 {
1094         FileSystem fs;
1095         int total_excess;
1096         long oldest = 0;
1097         int oldest_item = -1;
1098         int result;
1099         char string[BCTEXTLEN];
1100         char string2[BCTEXTLEN];
1101
1102 // Delete extra indexes
1103         fs.set_filter("*.idx");
1104         fs.complete_path(preferences->index_directory);
1105         fs.update(preferences->index_directory);
1106 //printf("MWindow::clean_indexes 1 %d\n", fs.dir_list.total);
1107
1108 // Eliminate directories
1109         result = 1;
1110         while(result)
1111         {
1112                 result = 0;
1113                 for(int i = 0; i < fs.dir_list.total && !result; i++)
1114                 {
1115                         fs.join_names(string, preferences->index_directory, fs.dir_list[i]->name);
1116                         if(fs.is_dir(string))
1117                         {
1118                                 delete fs.dir_list[i];
1119                                 fs.dir_list.remove_number(i);
1120                                 result = 1;
1121                         }
1122                 }
1123         }
1124         total_excess = fs.dir_list.total - preferences->index_count;
1125
1126 //printf("MWindow::clean_indexes 2 %d\n", fs.dir_list.total);
1127         while(total_excess > 0)
1128         {
1129 // Get oldest
1130                 for(int i = 0; i < fs.dir_list.total; i++)
1131                 {
1132                         fs.join_names(string, preferences->index_directory, fs.dir_list[i]->name);
1133
1134                         if(i == 0 || fs.get_date(string) <= oldest)
1135                         {
1136                                 oldest = fs.get_date(string);
1137                                 oldest_item = i;
1138                         }
1139                 }
1140
1141                 if(oldest_item >= 0)
1142                 {
1143 // Remove index file
1144                         fs.join_names(string,
1145                                 preferences->index_directory,
1146                                 fs.dir_list[oldest_item]->name);
1147 //printf("MWindow::clean_indexes 1 %s\n", string);
1148                         if(remove(string))
1149                                 perror("delete_indexes");
1150                         delete fs.dir_list[oldest_item];
1151                         fs.dir_list.remove_number(oldest_item);
1152
1153 // Remove table of contents if it exists
1154                         strcpy(string2, string);
1155                         char *ptr = strrchr(string2, '.');
1156                         if(ptr)
1157                         {
1158 //printf("MWindow::clean_indexes 2 %s\n", string2);
1159                                 sprintf(ptr, ".toc");
1160                                 remove(string2);
1161                                 sprintf(ptr, ".mkr");
1162                                 remove(string2);
1163                         }
1164                 }
1165
1166                 total_excess--;
1167         }
1168 }
1169
1170 void MWindow::init_awindow()
1171 {
1172         awindow = new AWindow(this);
1173         awindow->create_objects();
1174 }
1175
1176 void MWindow::init_gwindow()
1177 {
1178         gwindow = new GWindow(this);
1179         gwindow->create_objects();
1180 }
1181
1182 void MWindow::init_tipwindow()
1183 {
1184         TipWindow::load_tips(cin_lang);
1185         if( !twindow )
1186                 twindow = new TipWindow(this);
1187         twindow->start();
1188 }
1189
1190 void MWindow::show_warning(int *do_warning, const char *text)
1191 {
1192         if( do_warning && !*do_warning ) return;
1193         if( !wwindow )
1194                 wwindow = new WWindow(this);
1195         wwindow->show_warning(do_warning, text);
1196 }
1197
1198 int MWindow::wait_warning()
1199 {
1200         return wwindow->wait_result();
1201 }
1202
1203 void MWindow::init_theme()
1204 {
1205         Timer timer;
1206         theme = 0;
1207
1208         PluginServer *theme_plugin = 0;
1209         for(int i = 0; i < plugindb->total && !theme_plugin; i++) {
1210                 if( plugindb->get(i)->theme &&
1211                     !strcasecmp(preferences->theme, plugindb->get(i)->title) )
1212                         theme_plugin = plugindb->get(i);
1213         }
1214
1215         if( !theme_plugin )
1216                 fprintf(stderr, _("MWindow::init_theme: prefered theme %s not found.\n"),
1217                          preferences->theme);
1218
1219         const char *default_theme = DEFAULT_THEME;
1220         if( !theme_plugin && strcasecmp(preferences->theme, default_theme) ) {
1221                 fprintf(stderr, _("MWindow::init_theme: trying default theme %s\n"),
1222                         default_theme);
1223                 for(int i = 0; i < plugindb->total && !theme_plugin; i++) {
1224                         if( plugindb->get(i)->theme &&
1225                             !strcasecmp(default_theme, plugindb->get(i)->title) )
1226                                 theme_plugin = plugindb->get(i);
1227                 }
1228         }
1229
1230         if(!theme_plugin) {
1231                 fprintf(stderr, _("MWindow::init_theme: theme_plugin not found.\n"));
1232                 exit(1);
1233         }
1234
1235         PluginServer *plugin = new PluginServer(*theme_plugin);
1236         if( plugin->open_plugin(0, preferences, 0, 0) ) {
1237                 fprintf(stderr, _("MWindow::init_theme: unable to load theme %s\n"),
1238                         theme_plugin->title);
1239                 exit(1);
1240         }
1241
1242         theme = plugin->new_theme();
1243         theme->mwindow = this;
1244         strcpy(theme->path, plugin->path);
1245         delete plugin;
1246
1247 // Load default images & settings
1248         theme->Theme::initialize();
1249 // Load user images & settings
1250         theme->initialize();
1251 // Create menus with user colors
1252         theme->build_menus();
1253         init_menus();
1254
1255         theme->sort_image_sets();
1256         theme->check_used();
1257 //printf("MWindow::init_theme %d total_time=%d\n", __LINE__, (int)timer.get_difference());
1258 }
1259
1260 void MWindow::init_3d()
1261 {
1262         playback_3d = new Playback3D(this);
1263         playback_3d->create_objects();
1264 }
1265
1266 void MWindow::init_edl()
1267 {
1268         edl = new EDL;
1269         edl->create_objects();
1270         fill_preset_defaults(default_standard, edl->session);
1271         edl->load_defaults(defaults);
1272         edl->session->brender_start = edl->session->brender_end = 0;
1273         edl->create_default_tracks();
1274         edl->tracks->update_y_pixels(theme);
1275 }
1276
1277 void MWindow::init_compositor()
1278 {
1279         cwindow = new CWindow(this);
1280         cwindow->create_objects();
1281 }
1282
1283 void MWindow::init_levelwindow()
1284 {
1285         lwindow = new LevelWindow(this);
1286         lwindow->create_objects();
1287 }
1288
1289 VWindow *MWindow::get_viewer(int start_it, int idx)
1290 {
1291         vwindows_lock->lock("MWindow::get_viewer");
1292         VWindow *vwindow = idx >= 0 && idx < vwindows.size() ? vwindows[idx] : 0;
1293         if( !vwindow ) idx = vwindows.size();
1294         while( !vwindow && --idx >= 0 ) {
1295                 VWindow *vwin = vwindows[idx];
1296                 if( !vwin->is_running() || !vwin->get_edl() )
1297                         vwindow = vwin;
1298         }
1299         if( !vwindow ) {
1300                 vwindow = new VWindow(this);
1301                 vwindow->load_defaults();
1302                 vwindow->create_objects();
1303                 vwindows.append(vwindow);
1304         }
1305         vwindows_lock->unlock();
1306         if( start_it ) vwindow->start();
1307         return vwindow;
1308 }
1309
1310 ZWindow *MWindow::get_mixer(Mixer *&mixer)
1311 {
1312         zwindows_lock->lock("MWindow::get_mixer");
1313         if( !mixer ) mixer = edl->mixers.new_mixer();
1314         ZWindow *zwindow = 0;
1315         for( int i=0; !zwindow && i<zwindows.size(); ++i ) {
1316                 ZWindow *zwdw = zwindows[i];
1317                 if( zwdw->running() ) continue;
1318                 if( zwdw->idx >= 0 ) continue;
1319                 zwindow = zwindows[i];
1320         }
1321         if( !zwindow )
1322                 zwindows.append(zwindow = new ZWindow(this));
1323         zwindow->idx = mixer->idx;
1324         zwindows_lock->unlock();
1325         return zwindow;
1326 }
1327
1328 ZWindow *MWindow::get_mixer(int idx)
1329 {
1330         ZWindow *zwindow = 0;
1331         zwindows_lock->lock("MWindow::get_mixer");
1332         for( int i=0; !zwindow && i<zwindows.size(); ++i ) {
1333                 ZWindow *zwdw = zwindows[i];
1334                 if( !zwdw->running() ) continue;
1335                 if( zwdw->idx != idx ) continue;
1336                 zwindow = zwindows[i];
1337         }
1338         zwindows_lock->unlock();
1339         return zwindow;
1340 }
1341
1342 void MWindow::close_mixer(ZWindow *zwindow)
1343 {
1344         zwindows_lock->lock("MWindow::close_mixer 0");
1345         if( session->selected_zwindow >= 0 ) {
1346                 int i = zwindows.number_of(zwindow);
1347                 if( i >= 0 && i < session->selected_zwindow )
1348                         --session->selected_zwindow;
1349                 else if( i == session->selected_zwindow )
1350                         session->selected_zwindow = -1;
1351         }
1352         zwindows_lock->unlock();
1353         gui->lock_window("MWindow::close_mixer 1");
1354         gui->update_mixers(0, -1);
1355         gui->unlock_window();
1356 }
1357
1358 void MWindow::start_mixer()
1359 {
1360         Mixer *mixer = 0;
1361         ZWindow *zwindow = get_mixer(mixer);
1362         const char *title = 0;
1363
1364         for( Track *track=edl->tracks->first; track!=0; track=track->next ) {
1365                 PatchGUI *patchgui = get_patchgui(track);
1366                 if( !patchgui || !patchgui->mixer ) continue;
1367                 mixer->mixer_ids.append(track->get_mixer_id());
1368                 if( !title ) title = track->title;
1369         }
1370
1371         session->selected_zwindow = -1;
1372         gui->lock_window("MWindow::start_mixer");
1373         gui->update_mixers(0, 0);
1374         gui->unlock_window();
1375
1376         zwindow->set_title(title);
1377         zwindow->start();
1378         refresh_mixers();
1379 }
1380
1381 int MWindow::mixer_track_active(Track *track)
1382 {
1383         int i = session->selected_zwindow;
1384         if( i < 0 || i >= zwindows.size() ) return 0;
1385         ZWindow *zwindow = zwindows[i];
1386         Mixer *mixer = edl->mixers.get_mixer(zwindow->idx);
1387         if( !mixer ) return 0;
1388         int n = mixer->mixer_ids.number_of(track->get_mixer_id());
1389         return n >= 0 ? 1 : 0;
1390 }
1391
1392 void MWindow::update_mixer_tracks()
1393 {
1394         zwindows_lock->lock("MixPatch::handle_event");
1395         int i = session->selected_zwindow;
1396         if( i >= 0 && i < zwindows.size() ) {
1397                 ZWindow *zwindow = zwindows[i];
1398                 zwindow->update_mixer_ids();
1399         }
1400         zwindows_lock->unlock();
1401 }
1402
1403 void MWindow::handle_mixers(EDL *edl, int command, int wait_tracking,
1404                 int use_inout, int toggle_audio, int loop_play, float speed)
1405 {
1406         zwindows_lock->lock("MWindow::handle_mixers");
1407         for( int vidx=0; vidx<zwindows.size(); ++vidx ) {
1408                 ZWindow *zwindow = zwindows[vidx];
1409                 if( zwindow->idx < 0 ) continue;
1410                 Mixer *mixer = edl->mixers.get_mixer(zwindow->idx);
1411                 if( !mixer || !mixer->mixer_ids.size() ) continue;
1412                 int k = -1;
1413                 for( Track *track = edl->tracks->first; k<0 && track!=0; track=track->next ) {
1414                         if( track->data_type != TRACK_VIDEO ) continue;
1415                         int mixer_id = track->get_mixer_id();
1416                         k = mixer->mixer_ids.size();
1417                         while( --k >= 0 && mixer_id != mixer->mixer_ids[k] );
1418                 }
1419                 if( k < 0 ) continue;
1420                 EDL *mixer_edl = new EDL(this->edl);
1421                 mixer_edl->create_objects();
1422                 mixer_edl->copy_all(edl);
1423                 mixer_edl->remove_vwindow_edls();
1424                 for( Track *track = mixer_edl->tracks->first; track!=0; track=track->next ) {
1425                         k = mixer->mixer_ids.size();
1426                         while( --k >= 0 && track->get_mixer_id() != mixer->mixer_ids[k] );
1427                         if( k >= 0 ) {
1428                                 track->armed = 1;
1429                                 track->play = track->data_type == TRACK_VIDEO ? 1 : 0;
1430                         }
1431                         else
1432                                 track->armed = track->play = 0;
1433                 }
1434                 zwindow->change_source(mixer_edl);
1435                 zwindow->handle_mixer(command, 0,
1436                                 use_inout, toggle_audio, loop_play, speed);
1437         }
1438         zwindows_lock->unlock();
1439 }
1440
1441 void MWindow::refresh_mixers(int dir)
1442 {
1443         int command = dir >= 0 ? CURRENT_FRAME : LAST_FRAME;
1444         handle_mixers(edl, command, 0, 0, 0, 0, 0);
1445 }
1446
1447 void MWindow::stop_mixers()
1448 {
1449         for( int vidx=0; vidx<zwindows.size(); ++vidx ) {
1450                 ZWindow *zwindow = zwindows[vidx];
1451                 if( zwindow->idx < 0 ) continue;
1452                 zwindow->handle_mixer(STOP, 0, 0, 0, 0, 0);
1453         }
1454 }
1455
1456 void MWindow::close_mixers(int result)
1457 {
1458         ArrayList<ZWindow*> closed;
1459         zwindows_lock->lock("MWindow::close_mixers");
1460         for( int i=zwindows.size(); --i>=0; ) {
1461                 ZWindow *zwindow = zwindows[i];
1462                 if( zwindow->idx < 0 ) continue;
1463                 zwindow->idx = -1;
1464                 ZWindowGUI *zgui = zwindow->zgui;
1465                 zgui->lock_window("MWindow::select_zwindow 0");
1466                 zgui->set_done(result);
1467                 zgui->unlock_window();
1468                 closed.append(zwindow);
1469         }
1470         zwindows_lock->unlock();
1471         for( int i=0; i<closed.size(); ++i ) {
1472                 ZWindow *zwindow = closed[i];
1473                 zwindow->join();
1474         }
1475 }
1476
1477 ZWindow *MWindow::create_mixer(Indexable *indexable, double position)
1478 {
1479         ArrayList<Indexable*> new_assets;
1480         new_assets.append(indexable);
1481         Track *track = edl->tracks->last;
1482         load_assets(&new_assets, position, LOADMODE_NEW_TRACKS, 0, 0, 0, 0, 0, 1);
1483         track = !track ? edl->tracks->first : track->next;
1484         Mixer *mixer = 0;
1485         ZWindow *zwindow = get_mixer(mixer);
1486         while( track ) {
1487                 track->play = track->armed = 0;
1488                 if( track->data_type == TRACK_VIDEO ) {
1489                         sprintf(track->title, _("Mixer %d"), zwindow->idx);
1490                 }
1491                 mixer->mixer_ids.append(track->get_mixer_id());
1492                 track = track->next;
1493         }
1494         if(  indexable->is_asset ) {
1495                 char *path = indexable->path;
1496                 char *tp = strrchr(path, '/');
1497                 if( !tp ) tp = path; else ++tp;
1498                 zwindow->set_title(tp);
1499         }
1500         else {
1501                 char *title = ((EDL*)indexable)->local_session->clip_title;
1502                 zwindow->set_title(title);
1503         }
1504         return zwindow;
1505 }
1506
1507 void MWindow::create_mixers(double position)
1508 {
1509         if( !session->drag_assets->size() &&
1510             !session->drag_clips->size() ) return;
1511         undo_before();
1512
1513         select_zwindow(0);
1514         ArrayList<ZWindow *>new_mixers;
1515
1516         for( int i=0; i<session->drag_assets->size(); ++i ) {
1517                 Indexable *indexable = session->drag_assets->get(i);
1518                 if( !indexable->have_video() ) continue;
1519                 ZWindow *zwindow = create_mixer(indexable, position);
1520                 new_mixers.append(zwindow);
1521         }
1522         for( int i=0; i<session->drag_clips->size(); ++i ) {
1523                 Indexable *indexable = (Indexable*)session->drag_clips->get(i);
1524                 if( !indexable->have_video() ) continue;
1525                 ZWindow *zwindow = create_mixer(indexable, position);
1526                 new_mixers.append(zwindow);
1527         }
1528
1529         tile_mixers();
1530         for( int i=0; i<new_mixers.size(); ++i )
1531                 new_mixers[i]->start();
1532
1533         refresh_mixers();
1534         save_backup();
1535         undo_after(_("create mixers"), LOAD_ALL);
1536         restart_brender();
1537         gui->update(1, FORCE_REDRAW, 1, 1, 1, 1, 0);
1538         sync_parameters(CHANGE_ALL);
1539 }
1540
1541 void MWindow::open_mixers()
1542 {
1543         for( int i=0; i<edl->mixers.size(); ++i ) {
1544                 Mixer *mixer = edl->mixers[i];
1545                 ZWindow *zwindow = get_mixer(mixer);
1546                 zwindow->set_title(mixer->title);
1547                 zwindow->start();
1548         }
1549         refresh_mixers();
1550 }
1551
1552 int MWindow::select_zwindow(ZWindow *zwindow)
1553 {
1554         int ret = 0, n = zwindows.number_of(zwindow);
1555         if( session->selected_zwindow != n ) {
1556                 session->selected_zwindow = n;
1557                 for( int i=0; i<zwindows.size(); ++i ) {
1558                         ZWindow *zwindow = zwindows[i];
1559                         if( zwindow->idx < 0 ) continue;
1560                         ZWindowGUI *zgui = zwindow->zgui;
1561                         zgui->lock_window("MWindow::select_zwindow 0");
1562                         zwindow->highlighted = i == n ? 1 : 0;
1563                         if( zgui->draw_overlays() )
1564                                 zgui->canvas->get_canvas()->flash(1);
1565                         zgui->unlock_window();
1566                 }
1567                 ret = 1;
1568                 gui->lock_window("MWindow::select_window 1");
1569                 gui->update_mixers(0, -1);
1570                 gui->unlock_window();
1571         }
1572         return ret;
1573 }
1574
1575 void MWindow::tile_mixers()
1576 {
1577         int x1 = session->tile_mixers_x;
1578         int y1 = session->tile_mixers_y;
1579         int x2 = x1 + session->tile_mixers_w;
1580         int y2 = y1 + session->tile_mixers_h;
1581         tile_mixers(x1, y1, x2, y2);
1582 }
1583
1584 void MWindow::tile_mixers(int x1, int y1, int x2, int y2)
1585 {
1586         if( x1 == x2 || y1 == y2 ) {
1587 // use top left quadrent
1588                 int sw = gui->get_screen_w(1, -1);
1589                 int sh = gui->get_screen_w(1, -1);
1590                 x1 = 1;     y1 = 1;
1591                 x2 = sw/2;  y2 = sh/2;
1592         }
1593         else {
1594                 if( x1 > x2 ) { int t = x1;  x1 = x2;  x2 = t; }
1595                 if( y1 > y2 ) { int t = y1;  y1 = y2;  y2 = t; }
1596         }
1597         int ow = edl->session->output_w, oh = edl->session->output_h;
1598         int nz = 0;
1599         for( int i=0; i<zwindows.size(); ++i ) {
1600                 ZWindow *zwindow = zwindows[i];
1601                 if( zwindow->idx < 0 ) continue;
1602                 ++nz;
1603         }
1604         if( !nz ) return;
1605         int lt = BC_DisplayInfo::get_left_border();
1606         int top = BC_DisplayInfo::get_top_border();
1607         int bw = lt + BC_DisplayInfo::get_right_border();  // borders
1608         int bh = top + BC_DisplayInfo::get_bottom_border();
1609         int mw = xS(10+10), mh = yS(10+10); // canvas margins
1610         int dx = x2 - x1, dy = y2 - y1;
1611         int64_t sz = dx * dy, best_r = sz;
1612         int bx = 1, by = nz;
1613         for( int nx=1; nx<=nz; ++nx ) {
1614                 int ny = ceil((double)nz / nx);
1615                 int zw = dx / nx;
1616                 int ww = zw - bw;
1617                 int hh = (ww - mw) * oh / ow + mh;
1618                 int zh = hh + bh;
1619                 int64_t za = zw*nx * zh*ny;
1620                 int64_t r = sz - za;
1621                 if( r < 0 ) continue;
1622                 if( r >= best_r ) continue;
1623                 best_r = r;
1624                 bx = nx;  by = ny;
1625         }
1626         for( int ny=1; ny<=nz; ++ny ) {
1627                 int nx = ceil((double)nz / ny);
1628                 int zh = dy / ny;
1629                 int hh = zh - bh;
1630                 int ww = (hh - mh) * ow / oh + mw;
1631                 int zw = ww + bw;
1632                 int64_t za = zw*nx * zh*ny;
1633                 int64_t r = sz - za;
1634                 if( r < 0 ) continue;
1635                 if( r >= best_r ) continue;
1636                 best_r = r;
1637                 bx = nx;  by = ny;
1638         }
1639         int zw, zh, ww, hh;
1640         if( bx < by ) {
1641                 zh = dy / by;
1642                 hh = zh - bh;
1643                 ww = (hh - mh) * ow / oh + mw;
1644                 zw = ww + bw;
1645         }
1646         else {
1647                 zw = dx / bx;
1648                 ww = zw - bw;
1649                 hh = (ww - mw) * oh / ow + mh;
1650                 zh = hh + bh;
1651         }
1652
1653         int zx = 0, zy = 0;  // window origins
1654         int n = 0;
1655         for( int i=0; i<zwindows.size(); ++i ) {
1656                 ZWindow *zwindow = zwindows[i];
1657                 if( zwindow->idx < 0 ) continue;
1658                 int xx = zx + x1, yy = zy + y1;
1659                 int mx = x2 - zw, my = y2 - zh;
1660                 if( xx > mx ) xx = mx;
1661                 if( yy > my ) yy = my;
1662                 zwindow->reposition(xx,yy, ww,hh);
1663                 if( zwindow->running() ) {
1664                         ZWindowGUI *gui = (ZWindowGUI *)zwindow->get_gui();
1665                         gui->lock_window("MWindow::tile_mixers");
1666                         gui->BC_WindowBase::reposition_window(xx,yy, ww,hh);
1667                         gui->unlock_window();
1668                 }
1669                 if( ++n >= bx ) {
1670                         zx = 0;  zy += zh;
1671                         n = 0;
1672                 }
1673                 else
1674                         zx += zw;
1675         }
1676 }
1677
1678 void MWindow::set_gang_tracks(int v)
1679 {
1680         edl->local_session->gang_tracks = v;
1681         sync_parameters(CHANGE_PARAMS);
1682         gui->update(1, 1, 0, 0, 1, 0, 0);
1683         gui->flush();
1684 }
1685
1686
1687 void MWindow::init_cache()
1688 {
1689         audio_cache = new CICache(preferences);
1690         video_cache = new CICache(preferences);
1691         frame_cache = new FrameCache;
1692         wave_cache = new WaveCache;
1693 }
1694
1695 void MWindow::init_channeldb()
1696 {
1697         channeldb_buz->load("channeldb_buz");
1698         channeldb_v4l2jpeg->load("channeldb_v4l2jpeg");
1699 }
1700
1701 void MWindow::init_menus()
1702 {
1703         char string[BCTEXTLEN];
1704
1705         // Color Models
1706         BC_CModels::to_text(string, BC_RGB888);
1707         colormodels.append(new ColormodelItem(string, BC_RGB888));
1708         BC_CModels::to_text(string, BC_RGBA8888);
1709         colormodels.append(new ColormodelItem(string, BC_RGBA8888));
1710 //      BC_CModels::to_text(string, BC_RGB161616);
1711 //      colormodels.append(new ColormodelItem(string, BC_RGB161616));
1712 //      BC_CModels::to_text(string, BC_RGBA16161616);
1713 //      colormodels.append(new ColormodelItem(string, BC_RGBA16161616));
1714         BC_CModels::to_text(string, BC_RGB_FLOAT);
1715         colormodels.append(new ColormodelItem(string, BC_RGB_FLOAT));
1716         BC_CModels::to_text(string, BC_RGBA_FLOAT);
1717         colormodels.append(new ColormodelItem(string, BC_RGBA_FLOAT));
1718         BC_CModels::to_text(string, BC_YUV888);
1719         colormodels.append(new ColormodelItem(string, BC_YUV888));
1720         BC_CModels::to_text(string, BC_YUVA8888);
1721         colormodels.append(new ColormodelItem(string, BC_YUVA8888));
1722 //      BC_CModels::to_text(string, BC_YUV161616);
1723 //      colormodels.append(new ColormodelItem(string, BC_YUV161616));
1724 //      BC_CModels::to_text(string, BC_YUVA16161616);
1725 //      colormodels.append(new ColormodelItem(string, BC_YUVA16161616));
1726
1727 #define ILACEPROJECTMODELISTADD(x) ilacemode_to_text(string, x); \
1728                            interlace_project_modes.append(new InterlacemodeItem(string, x));
1729
1730 #define ILACEASSETMODELISTADD(x) ilacemode_to_text(string, x); \
1731                            interlace_asset_modes.append(new InterlacemodeItem(string, x));
1732
1733         // Interlacing Modes
1734         ILACEASSETMODELISTADD(ILACE_MODE_UNDETECTED); // Not included in the list for the project options.
1735
1736         ILACEASSETMODELISTADD(ILACE_MODE_TOP_FIRST);
1737         ILACEPROJECTMODELISTADD(ILACE_MODE_TOP_FIRST);
1738
1739         ILACEASSETMODELISTADD(ILACE_MODE_BOTTOM_FIRST);
1740         ILACEPROJECTMODELISTADD(ILACE_MODE_BOTTOM_FIRST);
1741
1742         ILACEASSETMODELISTADD(ILACE_MODE_NOTINTERLACED);
1743         ILACEPROJECTMODELISTADD(ILACE_MODE_NOTINTERLACED);
1744
1745         mixers_align = new MixersAlign(this);
1746 }
1747
1748 void MWindow::init_indexes()
1749 {
1750         mainindexes = new MainIndexes(this);
1751         mainindexes->start_loop();
1752 }
1753
1754 void MWindow::init_gui()
1755 {
1756         gui = new MWindowGUI(this);
1757         gui->create_objects();
1758         gui->load_defaults(defaults);
1759 }
1760
1761 void MWindow::init_signals()
1762 {
1763         sighandler = new SigHandler;
1764         sighandler->initialize("/tmp/cinelerra_%d.dmp");
1765 ENABLE_BUFFER
1766 }
1767
1768 void MWindow::init_render()
1769 {
1770         render = new Render(this);
1771         create_bd = new CreateBD_Thread(this);
1772         create_dvd = new CreateDVD_Thread(this);
1773         batch_render = new BatchRenderThread(this);
1774 }
1775
1776 void MWindow::init_exportedl()
1777 {
1778         exportedl = new ExportEDL(this);
1779 }
1780
1781
1782 void MWindow::init_shuttle()
1783 {
1784 #ifdef HAVE_SHUTTLE
1785         int ret = Shuttle::probe();
1786         if( ret >= 0 ) {
1787                 shuttle = new Shuttle(this);
1788                 if( shuttle->read_config_file() > 0 ) {
1789                         printf("shuttle: bad config file\n");
1790                         delete shuttle;  shuttle = 0;
1791                         return;
1792                 }
1793                 shuttle->start(ret);
1794         }
1795 #endif
1796 }
1797 void MWindow::init_wintv()
1798 {
1799 #ifdef HAVE_WINTV
1800         wintv = WinTV::probe(this);
1801         if( wintv )
1802                 wintv->start();
1803 #endif
1804 }
1805 void MWindow::init_x10tv()
1806 {
1807 #ifdef HAVE_X10TV
1808         x10tv = X10TV::probe(this);
1809         if( x10tv )
1810                 x10tv->start();
1811 #endif
1812 }
1813
1814
1815 void MWindow::init_brender()
1816 {
1817         if(preferences->use_brender && !brender)
1818         {
1819                 brender_lock->lock("MWindow::init_brender 1");
1820                 brender = new BRender(this);
1821                 brender->initialize();
1822                 session->brender_end = 0;
1823                 brender_lock->unlock();
1824         }
1825         else
1826         if(!preferences->use_brender && brender)
1827         {
1828                 brender_lock->lock("MWindow::init_brender 2");
1829                 delete brender;
1830                 brender = 0;
1831                 session->brender_end = 0;
1832                 brender_lock->unlock();
1833         }
1834         brender_active = 0;
1835         stop_brender();
1836 }
1837
1838 void MWindow::restart_brender()
1839 {
1840 //printf("MWindow::restart_brender 1\n");
1841         if( !brender_active || !preferences->use_brender ) return;
1842         if( !brender ) return;
1843         brender->restart(edl);
1844 }
1845
1846 void MWindow::stop_brender()
1847 {
1848         if( !brender ) return;
1849 // cannot be holding mwindow->gui display lock
1850         brender->stop();
1851 }
1852
1853 int MWindow::brender_available(int position)
1854 {
1855         int result = 0;
1856         brender_lock->lock("MWindow::brender_available 1");
1857         if(brender && brender_active)
1858         {
1859                 if(brender->map_valid)
1860                 {
1861                         brender->map_lock->lock("MWindow::brender_available 2");
1862                         if(position < brender->map_size &&
1863                                 position >= 0)
1864                         {
1865 //printf("MWindow::brender_available 1 %d %d\n", position, brender->map[position]);
1866                                 if(brender->map[position] == BRender::RENDERED)
1867                                         result = 1;
1868                         }
1869                         brender->map_lock->unlock();
1870                 }
1871         }
1872         brender_lock->unlock();
1873         return result;
1874 }
1875
1876 void MWindow::set_brender_active(int v, int update)
1877 {
1878         if( !preferences->use_brender ) v = 0;
1879         brender_active = v;
1880         gui->mainmenu->brender_active->set_checked(v);
1881         if( v != 0 ) {
1882                 edl->session->brender_start = edl->local_session->get_selectionstart(1);
1883                 edl->session->brender_end = edl->local_session->get_selectionend(1);
1884
1885                 if(EQUIV(edl->session->brender_end, edl->session->brender_start)) {
1886                         edl->session->brender_end = edl->tracks->total_video_length();
1887                 }
1888                 restart_brender();
1889         }
1890         else {
1891                 edl->session->brender_start = edl->session->brender_end = 0;
1892                 gui->unlock_window();
1893                 stop_brender();
1894                 gui->lock_window("MWindow::set_brender_active");
1895         }
1896         if( update ) {
1897                 gui->update_timebar(0);
1898                 gui->draw_overlays(1);
1899         }
1900 }
1901
1902 int MWindow::has_commercials()
1903 {
1904 #ifdef HAVE_COMMERCIAL
1905         return theme->use_commercials;
1906 #else
1907         return 0;
1908 #endif
1909 }
1910
1911 void MWindow::init_commercials()
1912 {
1913 #ifdef HAVE_COMMERCIAL
1914         if( !commercials ) {
1915                 commercials = new Commercials(this);
1916                 commercial_active = 0;
1917         }
1918         else
1919                 commercials->add_user();
1920 #endif
1921 }
1922
1923 void MWindow::commit_commercial()
1924 {
1925 #ifdef HAVE_COMMERCIAL
1926         if( !commercial_active ) return;
1927         commercial_active = 0;
1928         if( !commercials ) return;
1929         commercials->commitDb();
1930 #endif
1931 }
1932
1933 void MWindow::undo_commercial()
1934 {
1935 #ifdef HAVE_COMMERCIAL
1936         if( !commercial_active ) return;
1937         commercial_active = 0;
1938         if( !commercials ) return;
1939         commercials->undoDb();
1940 #endif
1941 }
1942
1943 int MWindow::put_commercial()
1944 {
1945         int result = 0;
1946 #ifdef HAVE_COMMERCIAL
1947         double start = edl->local_session->get_selectionstart();
1948         double end = edl->local_session->get_selectionend();
1949         if( start >= end ) return 0;
1950
1951         const char *errmsg = 0;
1952         int count = 0;
1953         Tracks *tracks = edl->tracks;
1954         //check it
1955         for(Track *track=tracks->first; track && !errmsg; track=track->next) {
1956                 if( track->data_type != TRACK_VIDEO ) continue;
1957                 if( !track->armed ) continue;
1958                 if( count > 0 ) { errmsg = _("multiple video tracks"); break; }
1959                 ++count;
1960                 int64_t units_start = track->to_units(start,0);
1961                 int64_t units_end = track->to_units(end,0);
1962                 Edits *edits = track->edits;
1963                 Edit *edit1 = edits->editof(units_start, PLAY_FORWARD, 0);
1964                 Edit *edit2 = edits->editof(units_end, PLAY_FORWARD, 0);
1965                 if(!edit1 && !edit2) continue;  // nothing selected
1966                 if(!edit2) {                    // edit2 beyond end of track
1967                         edit2 = edits->last;
1968                         units_end = edits->length();
1969                 }
1970                 if(edit1 != edit2) { errmsg = _("crosses edits"); break; }
1971                 Indexable *indexable = edit1->get_source();
1972                 if( !indexable->is_asset ) { errmsg = _("not asset"); break; }
1973         }
1974         //run it
1975         for(Track *track=tracks->first; track && !errmsg; track=track->next) {
1976                 if( track->data_type != TRACK_VIDEO ) continue;
1977                 if( !track->armed ) continue;
1978                 int64_t units_start = track->to_units(start,0);
1979                 int64_t units_end = track->to_units(end,0);
1980                 Edits *edits = track->edits;
1981                 Edit *edit1 = edits->editof(units_start, PLAY_FORWARD, 0);
1982                 Edit *edit2 = edits->editof(units_end, PLAY_FORWARD, 0);
1983                 if(!edit1 && !edit2) continue;  // nothing selected
1984                 if(!edit2) {                    // edit2 beyond end of track
1985                         edit2 = edits->last;
1986                         units_end = edits->length();
1987                 }
1988                 Indexable *indexable = edit1->get_source();
1989                 Asset *asset = (Asset *)indexable;
1990                 File *file = video_cache->check_out(asset, edl);
1991                 if( !file ) { errmsg = _("no file"); break; }
1992                 int64_t edit_length = units_end - units_start;
1993                 int64_t edit_start = units_start - edit1->startproject + edit1->startsource;
1994                 result = commercials->put_clip(file, edit1->channel,
1995                         track->from_units(edit_start), track->from_units(edit_length));
1996                 video_cache->check_in(asset);
1997                 if( result ) { errmsg = _("db failed"); break; }
1998         }
1999         if( errmsg ) {
2000                 char string[BCTEXTLEN];
2001                 sprintf(string, _("put_commercial: %s"), errmsg);
2002                 MainError::show_error(string);
2003                 undo_commercial();
2004                 result = 1;
2005         }
2006 #endif
2007         return result;
2008 }
2009
2010 void MWindow::stop_playback(int wait)
2011 {
2012         gui->stop_drawing();
2013
2014         cwindow->stop_playback(wait);
2015
2016         for(int i = 0; i < vwindows.size(); i++) {
2017                 VWindow *vwindow = vwindows[i];
2018                 if( !vwindow->is_running() ) continue;
2019                 vwindow->stop_playback(wait);
2020         }
2021         for(int i = 0; i < zwindows.size(); i++) {
2022                 ZWindow *zwindow = zwindows[i];
2023                 if( zwindow->idx < 0 ) continue;
2024                 zwindow->stop_playback(wait);
2025         }
2026 }
2027
2028 void MWindow::stop_transport()
2029 {
2030         gui->stop_transport(gui->get_window_lock() ? "MWindow::stop_transport" : 0);
2031 }
2032
2033 void MWindow::undo_before(const char *description, void *creator)
2034 {
2035         undo->update_undo_before(description, creator);
2036 }
2037
2038 void MWindow::undo_after(const char *description, uint32_t load_flags, int changes_made)
2039 {
2040         undo->update_undo_after(description, load_flags, changes_made);
2041 }
2042
2043 void MWindow::beep(double freq, double secs, double gain)
2044 {
2045         if( !beeper ) beeper = new Beeper(this);
2046         beeper->tone(freq, secs, gain);
2047 }
2048
2049 Beeper::Beeper(MWindow *mwindow)
2050  : Thread(1, 0, 0)
2051 {
2052         this->mwindow = mwindow;
2053         audio = new AudioDevice(mwindow);
2054         playing_audio = 0;
2055         interrupted = -1;
2056 }
2057
2058 Beeper::~Beeper()
2059 {
2060         stop(0);
2061         delete audio;
2062 }
2063
2064 void Beeper::run()
2065 {
2066         int channels = 2;
2067         int64_t bfrsz = BEEP_SAMPLE_RATE;
2068         EDL *edl = mwindow->edl;
2069         EDLSession *session = edl->session;
2070         AudioOutConfig *aconfig = session->playback_config->aconfig;
2071         audio->open_output(aconfig, BEEP_SAMPLE_RATE, bfrsz, channels, 0);
2072         audio->start_playback();
2073
2074         double out0[bfrsz], out1[bfrsz], *out[2] = { out0, out1 };
2075         const double two_pi = 2*M_PI;
2076         int64_t audio_len = BEEP_SAMPLE_RATE * secs;
2077         const double dt = two_pi * freq/BEEP_SAMPLE_RATE;
2078         double th = 0;
2079
2080         audio_pos = 0;
2081         playing_audio = 1;
2082         while( !interrupted ) {
2083                 int len = audio_len - audio_pos;
2084                 if( len <= 0 ) break;
2085                 if( len > bfrsz ) len = bfrsz;
2086                 int k = audio_pos;
2087                 for( int i=0; i<len; ++i,++k,th+=dt ) {
2088                         double t = th - two_pi;
2089                         if( t >= 0 ) th = t;
2090                         out0[i] = out1[i] = sin(th) * gain;
2091                 }
2092                 audio->write_buffer(out, channels, len);
2093                 audio_pos = k;
2094         }
2095
2096         if( !interrupted )
2097                 audio->set_last_buffer();
2098         audio->stop_audio(interrupted ? 0 : 1);
2099         playing_audio = 0;
2100
2101         audio->close_all();
2102 }
2103
2104 void Beeper::start()
2105 {
2106         if( running() ) return;
2107         audio_pos = -1;
2108         interrupted = 0;
2109         Thread::start();
2110 }
2111
2112 void Beeper::stop(int wait)
2113 {
2114         if( running() && !interrupted ) {
2115                 interrupted = 1;
2116                 audio->stop_audio(wait);
2117         }
2118         Thread::join();
2119 }
2120
2121 void Beeper::tone(double freq, double secs, double gain)
2122 {
2123         stop(0);
2124         this->freq = freq;
2125         this->secs = secs;
2126         this->gain = gain;
2127         start();
2128 }
2129
2130
2131 int MWindow::load_filenames(ArrayList<char*> *filenames,
2132                 int load_mode, int edl_mode, int update_filename)
2133 {
2134         ArrayList<EDL*> new_edls;
2135         ArrayList<Asset*> new_assets;
2136         ArrayList<File*> new_files;
2137
2138 //      save_defaults();
2139         gui->start_hourglass();
2140
2141 // Need to stop playback since tracking depends on the EDL not getting
2142 // deleted.
2143         gui->unlock_window();
2144         stop_playback(1);
2145         gui->lock_window("MWindow::load_filenames 0");
2146         undo_before();
2147
2148 const int debug = 0;
2149 if(debug) printf("MWindow::load_filenames %d\n", __LINE__);
2150
2151 // Define new_edls and new_assets to load
2152         int result = 0, ftype = -1;
2153
2154         for( int i=0; i<filenames->size(); ++i ) {
2155 // Get type of file
2156                 File *new_file = new File;
2157                 Asset *new_asset = new Asset(filenames->get(i));
2158                 EDL *new_edl = new EDL;
2159                 new_edl->create_objects();
2160                 new_edl->copy_session(edl, -1);
2161                 new_file->set_program(edl->session->program_no);
2162
2163                 char string[BCTEXTLEN];
2164                 sprintf(string, _("Loading %s"), new_asset->path);
2165                 gui->show_message(string);
2166
2167                 ftype = new_file->open_file(preferences, new_asset, 1, 0);
2168
2169                 result = 1;
2170                 switch( ftype ) {
2171 // Convert media file to EDL
2172                 case FILE_OK: {
2173 // Warn about odd image dimensions
2174                         if( new_asset->video_data &&
2175                             ((new_asset->width % 2) || (new_asset->height % 2)) ) {
2176                                 eprintf(_("%s's resolution is %dx%d.\n"
2177                                         "Images with odd dimensions may not decode properly."),
2178                                         new_asset->path, new_asset->width, new_asset->height);
2179                         }
2180
2181                         if( new_asset->program >= 0 &&
2182                             edl->session->program_no != new_asset->program ) {
2183                                 eprintf(_("%s's index was built for program number %d\n"
2184                                         "Playback preference is %d.\n  Using program %d."),
2185                                         new_asset->path, new_asset->program,
2186                                         edl->session->program_no, new_asset->program);
2187                         }
2188
2189                         if( load_mode == LOADMODE_RESOURCESONLY ) {
2190                                 new_assets.append(new_asset);
2191                                 new_asset->add_user();
2192                                 result = 0;
2193                                 break;
2194                         }
2195
2196                         RecordLabels *labels = edl->session->label_cells ?
2197                                 new RecordLabels(new_file) : 0;
2198                         asset_to_edl(new_edl, new_asset, labels);
2199                         new_edls.append(new_edl);
2200                         new_edl->add_user();
2201                         delete labels;
2202
2203                         if( load_mode == LOADMODE_REPLACE ||
2204                             load_mode == LOADMODE_REPLACE_CONCATENATE ) {
2205 // Set filename to nothing for assets since save EDL would overwrite them.
2206                                 set_filename("");
2207 // Reset timeline position
2208                                 for( int i=0; i<TOTAL_PANES; ++i ) {
2209                                         new_edl->local_session->view_start[i] = 0;
2210                                         new_edl->local_session->track_start[i] = 0;
2211                                 }
2212                         }
2213                         result = 0;
2214                         break; }
2215
2216                 case FILE_NOT_FOUND: {
2217                         eprintf(_("Failed to open %s"), new_asset->path);
2218                         sprintf(string, _("Failed to open %s"), new_asset->path);
2219                         gui->show_message(string, theme->message_error);
2220                         gui->update_default_message();
2221                         break; }
2222
2223 // Unknown format
2224                 case FILE_UNRECOGNIZED_CODEC: {
2225 // Test index file
2226                         { IndexFile indexfile(this, new_asset);
2227                         if( !(result = indexfile.open_index()) )
2228                                 indexfile.close_index(); }
2229
2230 // Test existing EDLs
2231                         for( int j=0; result && j<new_edls.total; ++j ) {
2232                                 Asset *old_asset = new_edls[j]->assets->get_asset(new_asset->path);
2233                                 if( old_asset ) {
2234                                         new_asset->copy_from(old_asset,1);
2235                                         result = 0;
2236                                 }
2237                         }
2238                         if( result ) {
2239                                 Asset *old_asset = edl->assets->get_asset(new_asset->path);
2240                                 if( old_asset ) {
2241                                         new_asset->copy_from(old_asset,1);
2242                                         result = 0;
2243                                 }
2244                         }
2245
2246 // Prompt user
2247                         if( result ) {
2248                                 new_asset->audio_data = 1;
2249                                 new_asset->format = FILE_PCM;
2250                                 new_asset->channels = defaults->get("AUDIO_CHANNELS", 2);
2251                                 new_asset->sample_rate = defaults->get("SAMPLE_RATE", 44100);
2252                                 new_asset->bits = defaults->get("AUDIO_BITS", 16);
2253                                 new_asset->byte_order = defaults->get("BYTE_ORDER", 1);
2254                                 new_asset->signed_ = defaults->get("SIGNED_", 1);
2255                                 new_asset->header = defaults->get("HEADER", 0);
2256
2257                                 FileSystem fs;
2258                                 fs.extract_name(string, new_asset->path);
2259                                 strcat(string, _("'s format couldn't be determined."));
2260                                 FileFormat fwindow(this);
2261                                 fwindow.create_objects(new_asset, string);
2262                                 result = fwindow.run_window();
2263
2264                                 defaults->update("AUDIO_CHANNELS", new_asset->channels);
2265                                 defaults->update("SAMPLE_RATE", new_asset->sample_rate);
2266                                 defaults->update("AUDIO_BITS", new_asset->bits);
2267                                 defaults->update("BYTE_ORDER", new_asset->byte_order);
2268                                 defaults->update("SIGNED_", new_asset->signed_);
2269                                 defaults->update("HEADER", new_asset->header);
2270                                 save_defaults();
2271                         }
2272 // Append to list
2273                         if( !result ) {
2274 // Recalculate length
2275                                 delete new_file;
2276                                 new_file = new File;
2277                                 result = new_file->open_file(preferences, new_asset, 1, 0);
2278
2279                                 if( load_mode != LOADMODE_RESOURCESONLY ) {
2280                                         RecordLabels *labels = edl->session->label_cells ?
2281                                                 new RecordLabels(new_file) : 0;
2282                                         asset_to_edl(new_edl, new_asset, labels);
2283                                         new_edls.append(new_edl);
2284                                         new_edl->add_user();
2285                                         delete labels;
2286                                 }
2287                                 else {
2288                                         new_assets.append(new_asset);
2289                                         new_asset->add_user();
2290                                 }
2291                         }
2292                         break; }
2293
2294                 case FILE_IS_XML: {
2295                         FileXML xml_file;
2296                         const char *filename = filenames->get(i);
2297                         if( xml_file.read_from_file(filename, 1) ) {
2298                                 eprintf(_("Error: unable to open:\n  %s"), filename);
2299                                 break;
2300                         }
2301                         const char *cin_version = 0;
2302                         while( !xml_file.read_tag() ) {
2303                                 if( xml_file.tag.title_is("EDL") ) {
2304                                         cin_version = xml_file.tag.get_property("VERSION");
2305                                         break;
2306                                 }
2307                         }
2308                         xml_file.rewind();
2309                         if( !cin_version ) {
2310                                 eprintf(_("XML file %s\n not from cinelerra."),filename);
2311                                 char string[BCTEXTLEN];
2312                                 sprintf(string,_("Unknown %s"), filename);
2313                                 gui->show_message(string);
2314                                 break;
2315                         }
2316                         if( strcmp(cin_version, CINELERRA_VERSION) &&
2317                             strcmp(cin_version, "Unify") &&
2318                             strcmp(cin_version, "5.1") ) {
2319                                 eprintf(_("Warning: XML from cinelerra version %s\n"
2320                                         "Session data may be incompatible."), cin_version);
2321                         }
2322                         if( new_edl->load_xml(&xml_file, LOAD_ALL) ) {
2323                                 eprintf(_("Error: unable to load:\n  %s"), filename);
2324                                 break;
2325                         }
2326                         test_plugins(new_edl, filename);
2327                         int groups = new_edl->regroup(session->group_number);
2328                         session->group_number += groups;
2329                         switch( edl_mode ) {
2330                         case LOADMODE_EDL_CLIP: {
2331                                 strcpy(new_edl->local_session->clip_title,
2332                                         filenames->get(i));
2333                                 struct stat st;
2334                                 time_t t = !stat(filenames->get(i),&st) ?
2335                                         st.st_mtime : time(&t);
2336                                 ctime_r(&t, new_edl->local_session->clip_notes);
2337                                 switch( load_mode ) {
2338                                 case LOADMODE_REPLACE:
2339                                 case LOADMODE_REPLACE_CONCATENATE:
2340                                         strcpy(session->filename, filename);
2341                                         if( update_filename ) set_filename(filename);
2342                                         break;
2343                                 }
2344                                 result = 0;
2345                                 break; }
2346                         case LOADMODE_EDL_NESTED: {
2347                                 EDL *nested_edl = new EDL;
2348                                 nested_edl->create_objects();
2349                                 nested_edl->copy_session(edl, -1);
2350                                 nested_edl->create_nested(new_edl);
2351                                 nested_edl->set_path(filename);
2352                                 new_edl->remove_user();
2353                                 new_edl = nested_edl;
2354                                 result = 0;
2355                                 break; }
2356                         case LOADMODE_EDL_FILEREF: {
2357                                 result = create_ref(new_asset, new_edl);
2358                                 if( result ) break;
2359                                 new_assets.append(new_asset);
2360                                 new_asset->add_user();
2361                                 new_edl->remove_user();
2362                                 new_edl = new EDL;
2363                                 new_edl->create_objects();
2364                                 new_edl->copy_session(edl, -1);
2365                                 asset_to_edl(new_edl, new_asset);
2366                                 delete new_file;
2367                                 new_file = new File;
2368                                 result = new_file->open_file(preferences, new_asset, 1, 0);
2369                                 break; }
2370                         }
2371                         if( !result ) {
2372                                 new_edls.append(new_edl);
2373                                 new_edl->add_user();
2374                         }
2375                         else
2376                                 eprintf(_("Error: Unable to load xml:\n  %s"), new_asset->path);
2377                         break; }
2378                 }
2379
2380                 new_edl->Garbage::remove_user();
2381                 new_asset->Garbage::remove_user();
2382
2383 // Store for testing index
2384                 new_files.append(new_file);
2385         }
2386
2387 if(debug) printf("MWindow::load_filenames %d\n", __LINE__);
2388
2389         if(!result) {
2390                 gui->reset_default_message();
2391                 gui->default_message();
2392         }
2393
2394 if(debug) printf("MWindow::load_filenames %d\n", __LINE__);
2395
2396 // Paste them.
2397 // Don't back up here.
2398         if( new_edls.size() ) {
2399 // For pasting, clear the active region
2400                 if( load_mode == LOADMODE_PASTE ) {
2401                         double start = edl->local_session->get_selectionstart();
2402                         double end = edl->local_session->get_selectionend();
2403                         if(!EQUIV(start, end))
2404                                 edl->clear(start, end,
2405                                         edl->session->labels_follow_edits,
2406                                         edl->session->plugins_follow_edits,
2407                                         edl->session->autos_follow_edits);
2408
2409                         paste_edls(&new_edls, load_mode, 0, -1,
2410                                 edl->session->labels_follow_edits,
2411                                 edl->session->plugins_follow_edits,
2412                                 edl->session->autos_follow_edits,
2413                                 0); // overwrite
2414                 }
2415                 else if( load_mode == LOADMODE_NEW_TRACKS &&
2416                          edl_mode != LOADMODE_EDL_CLIP )
2417                         paste_edls(&new_edls, load_mode, 0, -1, 0, 0, 0, 0);
2418                 else if( load_mode != LOADMODE_RESOURCESONLY ||
2419                          edl_mode == LOADMODE_EDL_CLIP )
2420                         paste_edls(&new_edls, load_mode, 0, -1, 1, 1, 1, 0);
2421                 else
2422                         paste_edls(&new_edls, LOADMODE_NOTHING, 0, -1, 0, 0, 0, 0);
2423
2424         }
2425
2426 // Add new assets to EDL and schedule assets for index building.
2427         int got_indexes = 0;
2428         for( int i=0; i<new_edls.size(); ++i ) {
2429                 EDL *new_edl = new_edls[i];
2430                 for( int j=0; j<new_edl->nested_edls.size(); ++j ) {
2431                         mainindexes->add_indexable(new_edl->nested_edls[j]);
2432                         edl->nested_edls.update_index(new_edl->nested_edls[j]);
2433                         got_indexes = 1;
2434                 }
2435
2436         }
2437
2438         for( int i=0; i<new_assets.size(); ++i ) {
2439                 Asset *new_asset = new_assets[i];
2440                 mainindexes->add_indexable(new_asset);
2441                 edl->assets->update(new_asset);
2442                 got_indexes = 1;
2443         }
2444
2445 // Start examining next batch of index files
2446         if(got_indexes) mainindexes->start_build();
2447
2448 // reload renderengine edl or the plugin guis will flip out
2449         sync_parameters(CHANGE_ALL);
2450 // Open plugin GUIs
2451         show_plugins();
2452
2453         // opening new session
2454         if( ( load_mode == LOADMODE_REPLACE ||
2455               load_mode == LOADMODE_REPLACE_CONCATENATE ) &&
2456             (ftype != FILE_IS_XML || edl_mode != LOADMODE_EDL_CLIP) ) {
2457                 select_asset(0, 0);
2458                 edl->session->proxy_state = PROXY_INACTIVE;
2459                 edl->session->proxy_scale = 1;
2460                 edl->session->proxy_disabled_scale = 1;
2461                 edl->session->proxy_use_scaler = 0;
2462                 edl->session->proxy_auto_scale = 0;
2463                 edl->session->proxy_beep = 0;
2464                 edl->local_session->preview_start = 0;
2465                 edl->local_session->preview_end = -1;
2466                 edl->local_session->loop_playback = 0;
2467                 edl->local_session->set_selectionstart(0);
2468                 edl->local_session->set_selectionend(0);
2469                 edl->local_session->unset_inpoint();
2470                 edl->local_session-> unset_outpoint();
2471                 set_brender_active(0, 0);
2472                 fit_selection();
2473                 goto_start();
2474         }
2475
2476         if( edl->session->proxy_state != PROXY_INACTIVE && edl->session->proxy_auto_scale &&
2477             load_mode != LOADMODE_REPLACE && load_mode != LOADMODE_REPLACE_CONCATENATE ) {
2478                 ArrayList<Indexable *> orig_idxbls;
2479                 for( int i=0; i<new_assets.size(); ++i )
2480                         orig_idxbls.append(new_assets.get(i));
2481                 for( int i=0; i<new_edls.size(); ++i ) {
2482                         EDL *new_edl = new_edls[i];
2483                         for( Track *track=new_edl->tracks->first; track; track=track->next ) {
2484                                 if( track->data_type != TRACK_VIDEO ) continue;
2485                                 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
2486                                         Indexable *idxbl = (Indexable *)edit->asset;
2487                                         if( !idxbl ) continue;
2488                                         if( !idxbl->have_video() ) continue;
2489                                         if( edit->channel != 0 ) continue; // first layer only
2490                                         orig_idxbls.append(edit->asset);
2491                                 }
2492                         }
2493                 }
2494                 gui->unlock_window(); // to update progress bar
2495                 int ret = render_proxy(orig_idxbls);
2496                 gui->lock_window("MWindow::load_filenames");
2497                 float gain = edl->session->proxy_beep;
2498                 if( ret >= 0 && gain > 0 ) {
2499                         if( ret > 0 )
2500                                 beep(2000., 1.5, gain);
2501                         else
2502                                 beep(4000., 0.25, gain);
2503                 }
2504         }
2505
2506 // need to update undo before project, since mwindow is unlocked & a new load
2507 // can begin here.  Should really prevent loading until we're done.
2508 if(debug) printf("MWindow::load_filenames %d\n", __LINE__);
2509         undo_after(_("load"), LOAD_ALL);
2510
2511         for(int i = 0; i < new_edls.size(); i++)
2512         {
2513                 new_edls[i]->remove_user();
2514         }
2515 if(debug) printf("MWindow::load_filenames %d\n", __LINE__);
2516
2517         new_edls.remove_all();
2518
2519         for(int i = 0; i < new_assets.size(); i++)
2520         {
2521                 new_assets[i]->Garbage::remove_user();
2522         }
2523
2524         new_assets.remove_all();
2525         new_files.remove_all_objects();
2526
2527
2528 if(debug) printf("MWindow::load_filenames %d\n", __LINE__);
2529         if(load_mode == LOADMODE_REPLACE ||
2530                 load_mode == LOADMODE_REPLACE_CONCATENATE)
2531         {
2532                 session->changes_made = 0;
2533         }
2534         else
2535         {
2536                 session->changes_made = 1;
2537         }
2538
2539         gui->stop_hourglass();
2540
2541
2542 if(debug) printf("MWindow::load_filenames %d\n", __LINE__);
2543         update_project(load_mode);
2544
2545 if(debug) printf("MWindow::load_filenames %d\n", __LINE__);
2546
2547         return 0;
2548 }
2549
2550 int MWindow::render_proxy(ArrayList<Indexable *> &new_idxbls)
2551 {
2552         Asset *format_asset = new Asset;
2553         format_asset->format = FILE_FFMPEG;
2554         format_asset->load_defaults(defaults, "PROXY_", 1, 1, 0, 0, 0);
2555         int new_scale = edl->session->proxy_scale;
2556         ProxyRender proxy_render(this, format_asset, new_scale);
2557
2558         for( int i=0; i<new_idxbls.size(); ++i ) {
2559                 Indexable *orig = new_idxbls.get(i);
2560                 Asset *proxy = proxy_render.add_original(orig, new_scale);
2561                 if( !proxy ) continue;
2562                 FileSystem fs;
2563                 int exists = fs.get_size(proxy->path) > 0 ? 1 : 0;
2564                 int got_it = exists && // if proxy exists, and is newer than orig
2565                     fs.get_date(proxy->path) > fs.get_date(orig->path) ? 1 : 0;
2566                 if( got_it ) continue;
2567                 proxy_render.add_needed(orig, proxy);
2568         }
2569
2570 // render needed proxies
2571         int result = proxy_render.create_needed_proxies(new_scale);
2572         if( !result ) {
2573                 add_proxy(&proxy_render.orig_idxbls, &proxy_render.orig_proxies);
2574         }
2575         format_asset->remove_user();
2576         return !result ? proxy_render.needed_proxies.size() : -1;
2577 }
2578
2579 int MWindow::enable_proxy()
2580 {
2581         int ret = 0;
2582         if( edl->session->proxy_state == PROXY_DISABLED ) {
2583                 int new_scale = edl->session->proxy_disabled_scale;
2584                 int new_use_scaler = edl->session->proxy_use_scaler;
2585                 Asset *asset = new Asset;
2586                 asset->format = FILE_FFMPEG;
2587                 asset->load_defaults(defaults, "PROXY_", 1, 1, 0, 0, 0);
2588                 ret = to_proxy(asset, new_scale, new_use_scaler);
2589                 asset->remove_user();
2590                 if( ret > 0 ) {
2591                         float gain = edl->session->proxy_beep;
2592                         beep(2000., 1.5, gain);
2593                 }
2594                 edl->session->proxy_disabled_scale = 1;
2595                 edl->session->proxy_state = PROXY_ACTIVE;
2596                 gui->lock_window("MWindow::to_proxy");
2597                 update_project(LOADMODE_REPLACE);
2598                 gui->unlock_window();
2599         }
2600         return 1;
2601 }
2602
2603 int MWindow::disable_proxy()
2604 {
2605         if( edl->session->proxy_state == PROXY_ACTIVE ) {
2606                 int old_scale = edl->session->proxy_scale, new_scale = 0;
2607                 int new_use_scaler = edl->session->proxy_use_scaler;
2608                 Asset *asset = new Asset;
2609                 asset->format = FILE_FFMPEG;
2610                 asset->load_defaults(defaults, "PROXY_", 1, 1, 0, 0, 0);
2611                 to_proxy(asset, new_scale, new_use_scaler);
2612                 asset->remove_user();
2613                 edl->session->proxy_state = PROXY_DISABLED;
2614                 edl->session->proxy_disabled_scale = old_scale;
2615                 gui->lock_window("MWindow::to_proxy");
2616                 update_project(LOADMODE_REPLACE);
2617                 gui->unlock_window();
2618         }
2619         return 1;
2620 }
2621
2622 int MWindow::to_proxy(Asset *asset, int new_scale, int new_use_scaler)
2623 {
2624         ArrayList<Indexable*> orig_idxbls;
2625         ArrayList<Indexable*> proxy_assets;
2626
2627         edl->Garbage::add_user();
2628         save_backup();
2629         undo_before(_("proxy"), this);
2630         int asset_scale = !new_scale ? 0 :
2631                         !new_use_scaler ? 1 : new_scale;
2632         ProxyRender proxy_render(this, asset, asset_scale);
2633
2634 // revert project to original size from current size
2635 // remove all session proxy assets at the at the current proxy_scale
2636         int proxy_scale = edl->session->proxy_scale;
2637
2638         if( edl->session->proxy_state == PROXY_ACTIVE ) {
2639                 Asset *orig_asset = edl->assets->first;
2640                 for( ; orig_asset; orig_asset=orig_asset->next ) {
2641                         char new_path[BCTEXTLEN];
2642                         proxy_render.to_proxy_path(new_path, orig_asset, proxy_scale);
2643 // test if proxy asset was already added to proxy_assets
2644                         int got_it = 0;
2645                         for( int i = 0; !got_it && i<proxy_assets.size(); ++i )
2646                                 got_it = !strcmp(proxy_assets[i]->path, new_path);
2647                         if( got_it ) continue;
2648                         Asset *proxy_asset = edl->assets->get_asset(new_path);
2649                         if( !proxy_asset ) continue;
2650 // add pointer to existing EDL asset if it exists
2651 // EDL won't delete it unless it's the same pointer.
2652                         proxy_assets.append(proxy_asset);
2653                         proxy_asset->add_user();
2654                         orig_idxbls.append(orig_asset);
2655                         orig_asset->add_user();
2656                 }
2657                 for( int i=0,n=edl->nested_edls.size(); i<n; ++i ) {
2658                         EDL *orig_nested = edl->nested_edls[i];
2659                         char new_path[BCTEXTLEN];
2660                         proxy_render.to_proxy_path(new_path, orig_nested, proxy_scale);
2661 // test if proxy asset was already added to proxy_assets
2662                         int got_it = 0;
2663                         for( int i = 0; !got_it && i<proxy_assets.size(); ++i )
2664                                 got_it = !strcmp(proxy_assets[i]->path, new_path);
2665                         if( got_it ) continue;
2666                         Asset *proxy_nested = edl->assets->get_asset(new_path);
2667                         if( !proxy_nested ) continue;
2668 // add pointer to existing EDL asset if it exists
2669 // EDL won't delete it unless it's the same pointer.
2670                         proxy_assets.append(proxy_nested);
2671                         proxy_nested->add_user();
2672                         orig_idxbls.append(orig_nested);
2673                         orig_nested->add_user();
2674                 }
2675
2676 // convert from the proxy assets to the original assets
2677                 edl->set_proxy(0, 0, &proxy_assets, &orig_idxbls);
2678
2679 // remove the references
2680                 for( int i=0; i<proxy_assets.size(); ++i ) {
2681                         Asset *proxy = (Asset *) proxy_assets[i];
2682                         proxy->width = proxy->actual_width;
2683                         proxy->height = proxy->actual_height;
2684                         proxy->remove_user();
2685                         edl->assets->remove_pointer(proxy);
2686                         proxy->remove_user();
2687                 }
2688                 proxy_assets.remove_all();
2689                 for( int i = 0; i < orig_idxbls.size(); i++ )
2690                         orig_idxbls[i]->remove_user();
2691                 orig_idxbls.remove_all();
2692         }
2693
2694         ArrayList<char *> confirm_paths;    // test for new files
2695         confirm_paths.set_array_delete();
2696
2697 // convert to new size if not original size
2698         if( new_scale ) {
2699                 FileSystem fs;
2700                 Asset *orig = edl->assets->first;
2701                 for( ; orig; orig=orig->next ) {
2702                         Asset *proxy = proxy_render.add_original(orig, new_scale);
2703                         if( !proxy ) continue;
2704                         int exists = fs.get_size(proxy->path) > 0 ? 1 : 0;
2705                         int got_it = exists && // if proxy exists, and is newer than orig
2706                             fs.get_date(proxy->path) > fs.get_date(orig->path) ? 1 : 0;
2707                         if( !got_it ) {
2708                                 if( exists ) // prompt user to overwrite
2709                                         confirm_paths.append(cstrdup(proxy->path));
2710                                 proxy_render.add_needed(orig, proxy);
2711                         }
2712                 }
2713                 for( int i=0,n=edl->nested_edls.size(); i<n; ++i ) {
2714                         EDL *orig_nested = edl->nested_edls[i];
2715                         Asset *proxy = proxy_render.add_original(orig_nested, new_scale);
2716                         if( !proxy ) continue;
2717                         int exists = fs.get_size(proxy->path) > 0 ? 1 : 0;
2718                         int got_it = exists && // if proxy exists, and is newer than orig_nested
2719                             fs.get_date(proxy->path) > fs.get_date(orig_nested->path) ? 1 : 0;
2720                         if( !got_it ) {
2721                                 if( exists ) // prompt user to overwrite
2722                                         confirm_paths.append(cstrdup(proxy->path));
2723                                 proxy_render.add_needed(orig_nested, proxy);
2724                         }
2725                 }
2726                 edl->session->proxy_state = PROXY_ACTIVE;
2727         }
2728         else
2729                 edl->session->proxy_state = PROXY_INACTIVE;
2730
2731         int result = 0;
2732 // test for existing files
2733         if( confirm_paths.size() ) {
2734                 result = ConfirmSave::test_files(this, &confirm_paths);
2735                 confirm_paths.remove_all_objects();
2736         }
2737
2738         if( !result && new_scale )
2739                 result = proxy_render.create_needed_proxies(new_scale);
2740
2741         if( !result ) { // resize project
2742                 edl->set_proxy(new_scale, new_use_scaler,
2743                         &proxy_render.orig_idxbls, &proxy_render.orig_proxies);
2744         }
2745         undo_after(_("proxy"), LOAD_ALL);
2746         edl->Garbage::remove_user();
2747         restart_brender();
2748
2749         return !result ? proxy_render.needed_proxies.size() : -1;
2750 }
2751
2752 void MWindow::test_plugins(EDL *new_edl, const char *path)
2753 {
2754         char string[BCTEXTLEN];
2755
2756 // Do a check whether plugins exist
2757         for( Track *track=new_edl->tracks->first; track; track=track->next ) {
2758                 for( int k=0; k<track->plugin_set.total; ++k ) {
2759                         PluginSet *plugin_set = track->plugin_set[k];
2760                         for( Plugin *plugin = (Plugin*)plugin_set->first;
2761                                         plugin; plugin = (Plugin*)plugin->next ) {
2762                                 if( plugin->plugin_type != PLUGIN_STANDALONE ) continue;
2763 // ok we need to find it in plugindb
2764                                 PluginServer *server =
2765                                         scan_plugindb(plugin->title, track->data_type);
2766                                 if( !server || server->transition ) {
2767                                         sprintf(string,
2768         _("The %s '%s' in file '%s' is not part of your installation of Cinelerra.\n"
2769           "The project won't be rendered as it was meant and Cinelerra might crash.\n"),
2770                                                 "effect", _(plugin->title), path);
2771                                         MainError::show_error(string);
2772                                 }
2773                         }
2774                 }
2775
2776                 for( Edit *edit=track->edits->first; edit; edit=edit->next ) {
2777                         if( !edit->transition ) continue;
2778 // ok we need to find transition in plugindb
2779                         PluginServer *server =
2780                                 scan_plugindb(edit->transition->title, track->data_type);
2781                         if( !server || !server->transition ) {
2782                                 sprintf(string,
2783         _("The %s '%s' in file '%s' is not part of your installation of Cinelerra.\n"
2784           "The project won't be rendered as it was meant and Cinelerra might crash.\n"),
2785                                         "transition", _(edit->transition->title), path);
2786                                 MainError::show_error(string);
2787                         }
2788                 }
2789         }
2790 }
2791
2792
2793 void MWindow::init_shm(const char *pfn, int64_t min)
2794 {
2795         int64_t result = 0;
2796 // Fix shared memory
2797         FILE *fd = fopen(pfn, "r");
2798         if( fd ) {
2799                 fscanf(fd, "%jd", &result);
2800                 fclose(fd);
2801                 if( result >= min ) return;
2802         }
2803
2804         fd = fopen(pfn, "w");
2805         if( !fd ) return;
2806         fprintf(fd, "0x%jx", min);
2807         fclose(fd);
2808
2809         fd = fopen(pfn, "r");
2810         if( !fd ) {
2811                 eprintf(_("MWindow::init_shm: couldn't open %s for reading.\n"), pfn);
2812                 return;
2813         }
2814
2815         fscanf(fd, "%jd", &result);
2816         fclose(fd);
2817         if( result < min ) {
2818                 eprintf(_("MWindow::init_shm: %s is %p.\n"
2819                         "you probably need to be root, or:\n"
2820                         "as root, run: echo 0x%jx > %s\n"
2821                         "before trying to start cinelerra.\n"
2822                         "It should be at least 0x%jx for Cinelerra.\n"),
2823                         pfn, (void *)result, min, pfn, min);
2824         }
2825 }
2826
2827 void MWindow::create_objects(int want_gui,
2828         int want_new,
2829         char *config_path)
2830 {
2831         const int debug = 0;
2832         if(debug) PRINT_TRACE
2833
2834 // For some reason, init_signals must come after show_splash or the signals won't
2835 // get trapped.
2836         init_signals();
2837         if(debug) PRINT_TRACE
2838         init_3d();
2839
2840         if(debug) PRINT_TRACE
2841
2842         if(debug) PRINT_TRACE
2843         default_standard = default_std();
2844         init_defaults(defaults, config_path);
2845         check_language();
2846         show_splash();
2847         init_preferences();
2848         if(splash_window)
2849                 splash_window->update_status(_("Initializing Plugins"));
2850         init_plugins(this, preferences);
2851         if(debug) PRINT_TRACE
2852         init_ladspa_plugins(this, preferences);
2853         if(debug) PRINT_TRACE
2854         init_plugin_tips(*plugindb, cin_lang);
2855         if(splash_window)
2856                 splash_window->update_status(_("Initializing GUI"));
2857         if(debug) PRINT_TRACE
2858         init_theme();
2859
2860         if(debug) PRINT_TRACE
2861         init_error();
2862
2863         if(splash_window)
2864                 splash_window->update_status(_("Initializing Fonts"));
2865         char string[BCTEXTLEN];
2866         strcpy(string, preferences->plugin_dir);
2867         strcat(string, "/" FONT_SEARCHPATH);
2868         BC_Resources::init_fontconfig(string);
2869         if(debug) PRINT_TRACE
2870 // use if plugged
2871         init_x10tv();
2872         if( !x10tv )
2873                 init_wintv();
2874
2875 // Default project created here
2876         init_edl();
2877         if(debug) PRINT_TRACE
2878
2879         init_cache();
2880         if(debug) PRINT_TRACE
2881
2882         Timer timer;
2883
2884         init_awindow();
2885         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2886
2887         init_compositor();
2888         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2889
2890 //printf("MWindow::create_objects %d session->show_vwindow=%d\n", __LINE__, session->show_vwindow);
2891         if(session->show_vwindow)
2892                 get_viewer(1, DEFAULT_VWINDOW);
2893         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2894
2895         init_gui();
2896         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2897
2898         init_levelwindow();
2899         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2900
2901         init_indexes();
2902         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2903
2904         init_channeldb();
2905         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2906
2907         init_gwindow();
2908         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2909
2910         init_render();
2911         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2912
2913         init_shuttle();
2914         init_brender();
2915         init_exportedl();
2916         init_commercials();
2917         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2918         mainprogress = new MainProgress(this, gui);
2919         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2920         undo = new MainUndo(this);
2921
2922         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2923
2924         plugin_guis = new PluginGUIs(this);
2925         dead_plugins = new ArrayList<PluginServer*>;
2926         keyframe_threads = new ArrayList<KeyFrameThread*>;
2927
2928         if(debug) printf("MWindow::create_objects %d vwindows=%d show_vwindow=%d\n",
2929                 __LINE__,
2930                 vwindows.size(),
2931                 session->show_vwindow);
2932
2933 // Show all vwindows
2934 //      if(session->show_vwindow) {
2935 //              for(int j = 0; j < vwindows.size(); j++) {
2936 //                      VWindow *vwindow = vwindows[j];
2937 //                      if( !vwindow->is_running() ) continue;
2938 //                      if(debug) printf("MWindow::create_objects %d vwindow=%p\n",
2939 //                              __LINE__,
2940 //                              vwindow);
2941 //                      if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2942 //                      vwindow->gui->lock_window("MWindow::create_objects 1");
2943 //                      if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2944 //                      vwindow->gui->show_window();
2945 //                      if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2946 //                      vwindow->gui->unlock_window();
2947 //              }
2948 //      }
2949
2950
2951         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2952
2953         if(session->show_cwindow)
2954         {
2955                 cwindow->gui->lock_window("MWindow::create_objects 1");
2956                 cwindow->gui->show_window();
2957                 cwindow->gui->unlock_window();
2958         }
2959
2960         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2961         if(session->show_awindow)
2962         {
2963                 awindow->gui->lock_window("MWindow::create_objects 1");
2964                 awindow->gui->show_window();
2965                 awindow->gui->unlock_window();
2966         }
2967
2968         if(debug) printf("MWindow::create_objects %d total_time=%d\n", __LINE__, (int)timer.get_difference());
2969         if(session->show_lwindow)
2970         {
2971                 lwindow->gui->lock_window("MWindow::create_objects 1");
2972                 lwindow->gui->show_window();
2973                 lwindow->gui->unlock_window();
2974         }
2975
2976         if(debug) printf("MWindow::create_objects %d total_time=%d gwindow=%p\n",
2977                 __LINE__,
2978                 (int)timer.get_difference(),
2979                 gwindow->gui);
2980         if(session->show_gwindow)
2981         {
2982                 gwindow->gui->lock_window("MWindow::create_objects 1");
2983                 gwindow->gui->show_window();
2984                 gwindow->gui->unlock_window();
2985         }
2986
2987         if(debug) PRINT_TRACE
2988
2989         gui->lock_window("MWindow::create_objects 1");
2990         gui->mainmenu->load_defaults(defaults);
2991         gui->mainmenu->update_toggles(0);
2992         gui->update_patchbay();
2993         gui->draw_canvas(0, 0);
2994         gui->draw_cursor(1);
2995         gui->show_window();
2996         gui->raise_window();
2997         gui->unlock_window();
2998         cwindow->gui->lock_window("MWindow::create_objects 1");
2999         cwindow->gui->tool_panel->raise_tool();
3000         cwindow->gui->unlock_window();
3001         if(debug) PRINT_TRACE
3002
3003         if(preferences->use_tipwindow)
3004                 init_tipwindow();
3005         if(debug) PRINT_TRACE
3006
3007         gui->add_keyboard_listener(
3008                 (int (BC_WindowBase::*)(BC_WindowBase *))
3009                 &MWindowGUI::keyboard_listener);
3010
3011         hide_splash();
3012         init_shm("/proc/sys/kernel/shmmax", 0x7fffffff);
3013         init_shm("/proc/sys/kernel/shmmni", 0x4000);
3014         if(debug) PRINT_TRACE
3015
3016         BC_WindowBase::get_resources()->vframe_shm = 1;
3017 }
3018
3019 int MWindow::uses_opengl()
3020 {
3021         if( !playback_3d || !playback_3d->running() ) return 0;
3022         PlaybackConfig *playback_config = edl->session->playback_config;
3023         return playback_config->vconfig->driver == PLAYBACK_X11_GL ? 1 : 0;
3024 }
3025
3026 void MWindow::show_splash()
3027 {
3028 #include "data/heroine_logo9_png.h"
3029         VFrame *frame = new VFramePng(heroine_logo9_png);
3030         BC_DisplayInfo dpyi;
3031         int rw = dpyi.get_root_w(), rh = dpyi.get_root_h();
3032         int rr = (rw < rh ? rw : rh) / 4;
3033         splash_window = new SplashGUI(frame, rr, rr);
3034         splash_window->create_objects();
3035 }
3036
3037 void MWindow::hide_splash()
3038 {
3039         if(splash_window)
3040                 delete splash_window;
3041         splash_window = 0;
3042 }
3043
3044
3045 void MWindow::start()
3046 {
3047 ENABLE_BUFFER
3048 //PRINT_TRACE
3049 //      vwindows[DEFAULT_VWINDOW]->start();
3050         awindow->start();
3051 //PRINT_TRACE
3052         cwindow->start();
3053 //PRINT_TRACE
3054         lwindow->start();
3055 //PRINT_TRACE
3056         gwindow->start();
3057 //PRINT_TRACE
3058 //      Thread::start();
3059 //PRINT_TRACE
3060         playback_3d->start();
3061 //PRINT_TRACE
3062 }
3063
3064 void MWindow::run()
3065 {
3066         run_lock->lock("MWindow::run");
3067         gui->run_window();
3068         stop_playback(1);
3069
3070         brender_lock->lock("MWindow::run 1");
3071         delete brender;         brender = 0;
3072         brender_lock->unlock();
3073
3074         interrupt_indexes();
3075         clean_indexes();
3076         save_defaults();
3077         run_lock->unlock();
3078 }
3079
3080 void MWindow::show_vwindow(int raise)
3081 {
3082         int total_running = 0;
3083         session->show_vwindow = 1;
3084
3085 //printf("MWindow::show_vwindow %d %d\n", __LINE__, vwindows.size());
3086 // Raise all windows which are visible
3087         for(int j = 0; j < vwindows.size(); j++) {
3088                 VWindow *vwindow = vwindows[j];
3089                 if( !vwindow->is_running() ) continue;
3090                 total_running++;
3091                 if( !raise && !vwindow->gui->is_hidden() ) continue;
3092                 vwindow->gui->lock_window("MWindow::show_vwindow");
3093                 vwindow->gui->show_window(0);
3094                 vwindow->gui->raise_window();
3095                 vwindow->gui->flush();
3096                 vwindow->gui->unlock_window();
3097         }
3098 // If no windows visible
3099         if( !total_running )
3100                 get_viewer(1, DEFAULT_VWINDOW);
3101
3102         gui->mainmenu->show_vwindow->set_checked(1);
3103 }
3104
3105
3106 void MWindow::hide_vwindow(int raise)
3107 {
3108         session->show_vwindow = 0;
3109         int total_running = 0;
3110
3111         for(int j = 0; j < vwindows.size(); j++) {
3112                 VWindow *vwindow = vwindows[j];