delete convert_render; convert_render = 0;
delete render; render = 0;
delete mixers_align; mixers_align = 0;
+#ifdef HAVE_COMMERCIALS
commit_commercial();
if( commercials && !commercials->remove_user() ) commercials = 0;
+#endif
close_mixers();
if( speed_edl ) { speed_edl->remove_user(); speed_edl = 0; }
// Save defaults for open plugins
plugin_gui_lock->unlock();
hide_keyframe_guis();
clean_indexes();
+ clean_backups();
save_defaults();
// Give up and go to a movie
// cant run valgrind if this is used
delete [] base_path;
}
-int MWindow::load_plugin_index(MWindow *mwindow, FILE *fp, const char *plugin_dir)
+/**
+* @brief Load plugins according to an index file.
+*
+* @details Builds an ArrayList of plugin servers only if there is no
+* mismatch for file layout version, index identifier, or timestamp of
+* the built-in plugins. If OK, add the plugin servers to the global list.
+*
+* @note If an error is returned the index file needs to be rebuilt, and
+* then this function must be called again.
+* There are two types of index files, with the same layout internally.
+* One called "Cinelerra_plugins" for built-ins, ffmpeg and lv2 .
+* The other type "ladspa_plugins.index_id" where index_id is either the
+* path or the $CIN_BUILD identifier if the path is from the running
+* AppImage itself. If there are multiple ladspa directories in the
+* path, there will be multiple index files.
+*
+* @return -1 if file no open, 0 if OK, 1 if error.
+*/
+int MWindow::load_plugin_index(MWindow *mwindow, FILE *fp, const char *plugin_dir, const char *index_id)
{
if( !fp ) return -1;
+ struct stat st;
+ fstat (fileno(fp), &st); // don't bother if the file has just been created.
+ if( st.st_size < 4 ) return 1;
+
// load index
fseek(fp, 0, SEEK_SET);
int ret = 0;
char index_line[BCTEXTLEN];
- int index_version = -1, len = strlen(plugin_dir);
+ int index_version = -1, len = strlen(index_id);
if( !fgets(index_line, BCTEXTLEN, fp) ||
sscanf(index_line, "%d", &index_version) != 1 ||
index_version != PLUGIN_FILE_VERSION ||
!fgets(index_line, BCTEXTLEN, fp) ||
(int)strlen(index_line)-1 != len || index_line[len] != '\n' ||
- strncmp(index_line, plugin_dir, len) != 0 ) ret = 1;
-
+ strncmp(index_line, index_id, len) != 0 ) {
+// printf("index file mismatch, version %d, index id length %d, expected id %s, file id %s\n", index_version, len, index_id, index_line);
+ ret = 1;
+ }
+
ArrayList<PluginServer*> plugins;
while( !ret && !feof(fp) && fgets(index_line, BCTEXTLEN, fp) ) {
if( index_line[0] == ';' ) continue;
char path[BCTEXTLEN], title[BCTEXTLEN];
int64_t mtime = 0;
if( PluginServer::scan_table(index_line, type, path, title, mtime) ) {
+// printf("PluginServer::scan_table failed for %s\n", index_line);
ret = 1; continue;
}
PluginServer *server = 0;
char plugin_path[BCTEXTLEN]; struct stat st;
sprintf(plugin_path, "%s/%s", plugin_dir, path);
if( stat(plugin_path, &st) || st.st_mtime != mtime ) {
+// printf("Plugin %s index time %ld, file time %ld\n", plugin_path, mtime, st.st_mtime);
ret = 1; continue;
}
server = new PluginServer(mwindow, plugin_path, type);
// Create plugin server from index entry
server->set_title(title);
if( server->read_table(index_line) ) {
+// printf("server->read_table failed for title %s, %s\n", title, index_line);
ret = 1; continue;
}
}
return 0;
}
-
+/**
+* @brief Load built-in and LV2 plugins as specified in index file,
+* rebuild the index file if needed.
+* @param[in] mwindow: GUI class pointer, will be NULL for batch
+* rendering or renderfarm client.
+* @param[in] preferences: Information from cinelerra_rc file.
+*/
int MWindow::init_plugins(MWindow *mwindow, Preferences *preferences)
{
if( !plugindb )
plugindb = new ArrayList<PluginServer*>;
init_ffmpeg();
- char index_path[BCTEXTLEN], plugin_path[BCTEXTLEN];
+ char index_path[BCTEXTLEN], plugin_path[BCTEXTLEN], index_id[BCTEXTLEN];
create_defaults_path(index_path, PLUGIN_FILE);
char *plugin_dir = FileSystem::basepath(preferences->plugin_dir);
strcpy(plugin_path, plugin_dir); delete [] plugin_dir;
+ // index_id is 2nd line of the index file, normally full plugin path,
+ // but fixed value if AppImage because the path changes on each run.
+ // And if the second line does not match on the next run the index is rebuilt.
+ if( getenv("APPDIR") && getenv("CINGG_BUILD")) strcpy(index_id, getenv("CINGG_BUILD"));
+ else strcpy(index_id, plugin_path);
FILE *fp = fopen(index_path,"a+");
if( !fp ) {
fprintf(stderr,_("MWindow::init_plugins: "
int fd = fileno(fp), ret = -1;
if( !flock(fd, LOCK_EX) ) {
fseek(fp, 0, SEEK_SET);
- ret = load_plugin_index(mwindow, fp, plugin_path);
+ ret = load_plugin_index(mwindow, fp, plugin_path, index_id);
}
if( ret > 0 ) {
ftruncate(fd, 0);
fseek(fp, 0, SEEK_SET);
- printf("init plugin index: %s\n", plugin_path);
+ printf("build plugin index for: %s\n", plugin_path);
fprintf(fp, "%d\n", PLUGIN_FILE_VERSION);
- fprintf(fp, "%s\n", plugin_path);
+ fprintf(fp, "%s\n", index_id);
init_plugin_index(mwindow, preferences, fp, plugin_path);
init_ffmpeg_index(mwindow, preferences, fp);
init_lv2_index(mwindow, preferences, fp);
fseek(fp, 0, SEEK_SET);
- ret = load_plugin_index(mwindow, fp, plugin_path);
+ ret = load_plugin_index(mwindow, fp, plugin_path, index_id);
}
if( ret ) {
fprintf(stderr,_("MWindow::init_plugins: "
return ret;
}
+/**
+* @brief Load ladspa plugins as specified in index files, for each ladspa
+* directory keep a separate index file. Rebuild index file(s) if needed.
+* @param[in] mwindow: GUI class pointer, will be NULL for batch
+* rendering or renderfarm client.
+* @param[in] preferences: Information from cinelerra_rc file.
+*/
int MWindow::init_ladspa_plugins(MWindow *mwindow, Preferences *preferences)
{
#ifdef HAVE_LADSPA
char *path = getenv("LADSPA_PATH");
+ char *appdir = getenv("APPDIR");
char ladspa_path[BCTEXTLEN];
- if( !path ) {
+ if( !path ) { // if no env var, use CinGG's own ladspa dir
strncpy(ladspa_path, File::get_ladspa_path(), sizeof(ladspa_path));
path = ladspa_path;
}
for( int len=0; *path; path+=len ) {
char *cp = strchr(path,':');
len = !cp ? strlen(path) : cp-path;
- char index_path[BCTEXTLEN], plugin_path[BCTEXTLEN];
+ char index_path[BCTEXTLEN], plugin_path[BCTEXTLEN], index_id[BCTEXTLEN];
memcpy(plugin_path, path, len); plugin_path[len] = 0;
if( cp ) ++len;
char *plugin_dir = FileSystem::basepath(plugin_path);
strcpy(plugin_path, plugin_dir); delete [] plugin_dir;
create_defaults_path(index_path, LADSPA_FILE);
cp = index_path + strlen(index_path);
- for( char *bp=plugin_path; *bp!=0; ++bp )
+ // If the first part of the plugin_path matches the APPDIR, we are
+ // referring to CinGG's ladspa, replace the path by a fixed ID. APPDIR
+ // only exists if we are running as AppImage (with variable mount points).
+ if( appdir && strncmp(plugin_path, appdir, strlen(appdir)) == 0 )
+ strcpy(index_id, getenv("CINGG_BUILD"));
+ else strcpy(index_id, plugin_path);
+
+ // Concatenate the path, replacing '/' with '_'.
+ for( char *bp=index_id; *bp!=0; ++bp )
*cp++ = *bp=='/' ? '_' : *bp;
*cp = 0;
FILE *fp = fopen(index_path,"a+");
int fd = fileno(fp), ret = -1;
if( !flock(fd, LOCK_EX) ) {
fseek(fp, 0, SEEK_SET);
- ret = load_plugin_index(mwindow, fp, plugin_path);
+ ret = load_plugin_index(mwindow, fp, plugin_path, index_id);
}
if( ret > 0 ) {
ftruncate(fd, 0);
fseek(fp, 0, SEEK_SET);
- init_ladspa_index(mwindow, preferences, fp, plugin_path);
+ printf("build ladspa plugin index for: %s\n", plugin_path);
+ fprintf(fp, "%d\n", PLUGIN_FILE_VERSION);
+ fprintf(fp, "%s\n", index_id);
+ init_plugin_index(mwindow, preferences, fp, plugin_path);
fseek(fp, 0, SEEK_SET);
- ret = load_plugin_index(mwindow, fp, plugin_path);
+ ret = load_plugin_index(mwindow, fp, plugin_path, index_id);
}
if( ret ) {
fprintf(stderr,_("MWindow::init_ladspa_plugins: "
char fs_path[BCTEXTLEN], path[BCTEXTLEN];
get_plugin_path(fs_path, 0, fs.dir_list[i]->path);
get_plugin_path(path, plug_dir, fs_path);
- if( fs.is_dir(fs_path) ) {
+ if( fs.is_dir(fs_path) ) { // recursively scan child directory
scan_plugin_index(mwindow, preferences, fp, plug_dir, path, idx);
continue;
}
YUV::yuv.yuv_set_colors(preferences->yuv_color_space, preferences->yuv_color_range);
}
+void MWindow::clean_backups()
+{
+ FileSystem fs;
+ int total_excess;
+ long oldest = 0;
+ int oldest_item = -1;
+ char string[BCTEXTLEN];
+
+// Delete extra backups
+ fs.set_filter("backup*.prev_*");
+ fs.complete_path(preferences->index_directory);
+ fs.update(preferences->index_directory);
+
+ // set to 50 for now
+ // total_excess = fs.dir_list.total - preferences->index_count;
+ total_excess = fs.dir_list.total - 50;
+ printf("Total excess of backups: %i \n", total_excess);
+
+//printf("MWindow::clean_backups 1 %d\n", fs.dir_list.total);
+
+ while(total_excess > 0)
+ {
+// Get oldest
+ for(int i = 0; i < fs.dir_list.total; i++)
+ {
+ fs.join_names(string, preferences->index_directory, fs.dir_list[i]->name);
+
+ if(i == 0 || fs.get_date(string) <= oldest)
+ {
+ oldest = fs.get_date(string);
+ oldest_item = i;
+ }
+ }
+
+ if(oldest_item >= 0)
+ {
+// Remove backup file
+ fs.join_names(string,
+ preferences->index_directory,
+ fs.dir_list[oldest_item]->name);
+//printf("MWindow::clean_backups 1 %s\n", string);
+ if(remove(string))
+ perror("delete_backups");
+ delete fs.dir_list[oldest_item];
+ fs.dir_list.remove_number(oldest_item);
+
+ }
+
+ total_excess--;
+ }
+}
+
void MWindow::clean_indexes()
{
FileSystem fs;
}
result = 0;
break; }
+// File is a list and size of listed files don't match
+ case FILE_SIZE_DONT_MATCH: {
+ eprintf(_("File sizes don't match"));
+ sprintf(string, _("File sizes don't match"));
+ gui->show_message(string, theme->message_error);
+ gui->update_default_message();
+ break; }
case FILE_NOT_FOUND: {
eprintf(_("Failed to open %s"), new_asset->path);
gui->mainmenu->show_vwindow->set_checked(1);
}
+
+void MWindow::hide_vwindow(int raise)
+{
+ session->show_vwindow = 0;
+ int total_running = 0;
+
+ for(int j = 0; j < vwindows.size(); j++) {
+ VWindow *vwindow = vwindows[j];
+ if( !vwindow->is_running() ) continue;
+ total_running++;
+ if( !raise && !vwindow->gui->is_hidden() ) continue;
+ vwindow->gui->lock_window("MWindow::show_vwindow");
+ vwindow->gui->hide_window(0);
+ vwindow->gui->unlock_window();
+ }
+ gui->mainmenu->show_vwindow->set_checked(0);
+}
+
+
void MWindow::show_awindow()
{
session->show_awindow = 1;
gui->mainmenu->show_awindow->set_checked(1);
}
+void MWindow::hide_awindow()
+{
+ session->show_awindow = 0;
+
+ awindow->gui->lock_window("MWindow::show_awindow");
+ awindow->gui->hide_window();
+ awindow->gui->unlock_window();
+ gui->mainmenu->show_awindow->set_checked(0);
+}
+
+
char *MWindow::get_cwindow_display()
{
char *x11_host = screens < 2 || session->window_config == 0 ?
gui->mainmenu->show_cwindow->set_checked(1);
}
+
+void MWindow::hide_cwindow()
+{
+ session->show_cwindow = 0;
+
+ cwindow->gui->lock_window("MWindow::show_cwindow");
+ cwindow->gui->hide_window();
+ cwindow->gui->unlock_window();
+ gui->mainmenu->show_cwindow->set_checked(0);
+}
+
+
void MWindow::show_gwindow()
{
session->show_gwindow = 1;
gui->mainmenu->show_gwindow->set_checked(1);
}
+
void MWindow::hide_gwindow()
{
session->show_gwindow = 0;
gwindow->gui->lock_window("MWindow::show_gwindow");
gwindow->gui->hide_window();
gwindow->gui->unlock_window();
+ gui->mainmenu->show_gwindow->set_checked(0);
}
void MWindow::show_lwindow()
gui->mainmenu->show_lwindow->set_checked(1);
}
+void MWindow::hide_lwindow()
+{
+ session->show_lwindow = 0;
+
+ lwindow->gui->lock_window("MWindow::show_lwindow");
+ lwindow->gui->hide_window();
+ lwindow->gui->unlock_window();
+ gui->mainmenu->show_lwindow->set_checked(0);
+}
+
+
void MWindow::restore_windows()
{
gui->unlock_window();
cwindow->gui->unlock_window();
}
else if( session->show_cwindow && cwindow->gui->is_hidden() )
- cwindow->show_window();
+ show_cwindow();
if( !session->show_gwindow && !gwindow->gui->is_hidden() ) {
gwindow->gui->lock_window("MWindow::restore_windows");
cp += snprintf(cp, ep-cp, idx ? BACKUPn_FILE : BACKUP_FILE, idx);
}
+void MWindow::create_timestamped_copy_from_previous_backup(char *previouspath)
+{
+ if (previouspath == NULL) return;
+ char backup_path[BCTEXTLEN];
+ backup_path[0] = 0;
+ time_t now = time(NULL);
+ struct tm* currenttime = localtime(&now);
+ snprintf(backup_path, sizeof(backup_path),
+ "%s/%s_%d%.2d%.2d_%.2d%.2d%.2d",
+ File::get_config_path(), BACKUP_FILE1,
+ currenttime->tm_year + 1900,
+ currenttime->tm_mon + 1,
+ currenttime->tm_mday,
+ currenttime->tm_hour,
+ currenttime->tm_min,
+ currenttime->tm_sec);
+ rename(previouspath, backup_path);
+}
+
void MWindow::save_backup()
{
FileXML file;
snprintf(backup_path1, sizeof(backup_path1), "%s/%s",
File::get_config_path(), BACKUP_FILE1);
get_backup_path(backup_path, sizeof(backup_path));
+ if( preferences->ongoing_backups )
+ create_timestamped_copy_from_previous_backup(backup_path1);
rename(backup_path, backup_path1);
edl->save_xml(&file, backup_path);
file.terminate_string();
{
this->mwindow = mwindow;
this->path = path;
+// *** CONTEXT_HELP ***
+ context_help_set_keyword("File by Reference");
}
ConfirmRefWindow::~ConfirmRefWindow()