1 // plugins.c
2 // LiVES
3 // (c) G. Finch 2003 - 2020 <salsaman+lives@gmail.com>
4 // released under the GNU GPL 3 or later
5 // see file ../COPYING or www.gnu.org for licensing details
6 
7 #include <dlfcn.h>
8 #include <errno.h>
9 
10 #include "main.h"
11 #include "resample.h"
12 #include "effects.h"
13 #include "interface.h"
14 
15 #include "rfx-builder.h"
16 #include "paramwindow.h"
17 
18 #include "lsd-tab.h"
19 
20 // *INDENT-OFF*
21 const char *const anames[AUDIO_CODEC_MAX] = {"mp3", "pcm", "mp2", "vorbis", "AC3", "AAC", "AMR_NB",
22                                              "raw", "wma2", "opus", ""};
23 // *INDENT-ON*
24 
25 static boolean list_plugins;
26 
27 ///////////////////////
28 // command-line plugins
29 
get_plugin_result(const char * command,const char * delim,boolean allow_blanks,boolean strip)30 LiVESList *get_plugin_result(const char *command, const char *delim, boolean allow_blanks, boolean strip) {
31   LiVESList *list = NULL;
32   char buffer[65536];
33 
34   //threaded_dialog_spin(0.);
35 
36   lives_popen(command, !mainw->is_ready && !list_plugins, buffer, 65535);
37 
38   if (THREADVAR(com_failed)) return NULL;
39 
40   //threaded_dialog_spin(0.);
41   list = buff_to_list(buffer, delim, allow_blanks, strip);
42   //threaded_dialog_spin(0.);
43   return list;
44 }
45 
46 
plugin_request_with_blanks(const char * plugin_type,const char * plugin_name,const char * request)47 LIVES_GLOBAL_INLINE LiVESList *plugin_request_with_blanks(const char *plugin_type, const char *plugin_name,
48     const char *request) {
49   // allow blanks in a list
50   return plugin_request_common(plugin_type, plugin_name, request, "|", TRUE);
51 }
52 
53 
plugin_request(const char * plugin_type,const char * plugin_name,const char * request)54 LIVES_GLOBAL_INLINE LiVESList *plugin_request(const char *plugin_type, const char *plugin_name, const char *request) {
55   return plugin_request_common(plugin_type, plugin_name, request, "|", FALSE);
56 }
57 
58 
plugin_request_by_line(const char * plugin_type,const char * plugin_name,const char * request)59 LIVES_GLOBAL_INLINE LiVESList *plugin_request_by_line(const char *plugin_type, const char *plugin_name, const char *request) {
60   return plugin_request_common(plugin_type, plugin_name, request, "\n", FALSE);
61 }
62 
63 
plugin_request_by_space(const char * plugin_type,const char * plugin_name,const char * request)64 LIVES_GLOBAL_INLINE LiVESList *plugin_request_by_space(const char *plugin_type, const char *plugin_name, const char *request) {
65   return plugin_request_common(plugin_type, plugin_name, request, " ", FALSE);
66 }
67 
68 
plugin_request_common(const char * plugin_type,const char * plugin_name,const char * request,const char * delim,boolean allow_blanks)69 LiVESList *plugin_request_common(const char *plugin_type, const char *plugin_name, const char *request,
70                                  const char *delim, boolean allow_blanks) {
71   // returns a LiVESList of responses to -request, or NULL on error
72 
73   // NOTE: request must not be quoted here, since it contains a list of parameters
74   // instead, caller should ensure that any strings in *request are suitably escaped and quoted
75   // e.g. by calling param_marshall()
76 
77   LiVESList *reslist = NULL;
78   char *com, *comfile;
79 
80   if (plugin_type) {
81     if (!plugin_name || !strlen(plugin_name)) {
82       return reslist;
83     }
84 
85     // some types live in the config directory...
86     if (!strcmp(plugin_type, PLUGIN_RENDERED_EFFECTS_CUSTOM) || !strcmp(plugin_type, PLUGIN_RENDERED_EFFECTS_TEST)) {
87       comfile = lives_build_filename(prefs->config_datadir, plugin_type, plugin_name, NULL);
88     } else if (!strcmp(plugin_type, PLUGIN_RFX_SCRAP)) {
89       // scraps are in the workdir
90       comfile = lives_build_filename(prefs->workdir, plugin_name, NULL);
91     } else {
92       comfile = lives_build_filename(prefs->lib_dir, PLUGIN_EXEC_DIR, plugin_type, plugin_name, NULL);
93     }
94 
95     //#define DEBUG_PLUGINS
96 #ifdef DEBUG_PLUGINS
97     com = lives_strdup_printf("\"%s\" %s", comfile, request);
98     lives_printerr("will run: %s\n", com);
99 #else
100     com = lives_strdup_printf("\"%s\" %s 2>/dev/null", comfile, request);
101 #endif
102     lives_free(comfile);
103   } else com = lives_strdup(request);
104   list_plugins = FALSE;
105   reslist = get_plugin_result(com, delim, allow_blanks, TRUE);
106   lives_free(com);
107   //threaded_dialog_spin(0.);
108   return reslist;
109 }
110 
111 
112 //////////////////
113 // get list of plugins of various types
114 
get_plugin_list(const char * plugin_type,boolean allow_nonex,const char * plugdir,const char * filter_ext)115 LiVESList *get_plugin_list(const char *plugin_type, boolean allow_nonex, const char *plugdir, const char *filter_ext) {
116   // returns a LiVESList * of plugins of type plugin_type
117   // returns empty list if there are no plugins of that type
118 
119   // allow_nonex to allow non-executable files (e.g. libs)
120   // filter_ext can be non-NULL to filter for files ending .filter_ext
121 
122   // TODO - use enum for plugin_type
123 
124   // format is: allow_nonex (0 or 1) allow_subdirs (0 or 1)  plugindir   ext
125 
126   char *com, *tmp;
127   LiVESList *pluglist;
128 
129   const char *ext = (filter_ext == NULL) ? "" : filter_ext;
130 
131   if (!strcmp(plugin_type, PLUGIN_THEMES)) {
132     // must not allow_nonex, otherwise we get splash image etc (just want dirs)
133     com = lives_strdup_printf("%s list_plugins 0 1 \"%s%s\" \"\"", prefs->backend_sync, prefs->prefix_dir, THEME_DIR);
134   } else if (!strcmp(plugin_type, PLUGIN_RENDERED_EFFECTS_CUSTOM_SCRIPTS) ||
135              !strcmp(plugin_type, PLUGIN_RENDERED_EFFECTS_TEST_SCRIPTS) ||
136              !strcmp(plugin_type, PLUGIN_RENDERED_EFFECTS_CUSTOM) ||
137              !strcmp(plugin_type, PLUGIN_RENDERED_EFFECTS_TEST) ||
138              !strcmp(plugin_type, PLUGIN_COMPOUND_EFFECTS_CUSTOM)
139             ) {
140     // look in home
141     tmp = lives_build_path(prefs->config_datadir, plugin_type, NULL);
142     com = lives_strdup_printf("%s list_plugins %d 0 \"%s\" \"%s\"", prefs->backend_sync, allow_nonex, tmp, ext);
143     lives_free(tmp);
144   } else if (!strcmp(plugin_type, PLUGIN_THEMES_CUSTOM)) {
145     tmp = lives_build_path(prefs->config_datadir, PLUGIN_THEMES, NULL);
146     com = lives_strdup_printf("%s list_plugins 0 1 \"%s\"", prefs->backend_sync, tmp);
147     lives_free(tmp);
148   } else if (!strcmp(plugin_type, PLUGIN_EFFECTS_WEED)) {
149     com = lives_strdup_printf("%s list_plugins 1 1 \"%s\" \"%s\"", prefs->backend_sync,
150                               (tmp = lives_filename_from_utf8((char *)plugdir, -1, NULL, NULL, NULL)), ext);
151     lives_free(tmp);
152   } else if (!strcmp(plugin_type, PLUGIN_DECODERS)) {
153     com = lives_strdup_printf("%s list_plugins 1 0 \"%s\" \"%s\"", prefs->backend_sync,
154                               (tmp = lives_filename_from_utf8((char *)plugdir, -1, NULL, NULL, NULL)), ext);
155     lives_free(tmp);
156   } else if (!strcmp(plugin_type, PLUGIN_RENDERED_EFFECTS_BUILTIN_SCRIPTS)) {
157     com = lives_strdup_printf("%s list_plugins %d 0 \"%s%s%s\" \"%s\"", prefs->backend_sync, allow_nonex, prefs->prefix_dir,
158                               PLUGIN_SCRIPTS_DIR, plugin_type, ext);
159   } else if (!strcmp(plugin_type, PLUGIN_COMPOUND_EFFECTS_BUILTIN)) {
160     com = lives_strdup_printf("%s list_plugins %d 0 \"%s%s%s\" \"%s\"", prefs->backend_sync, allow_nonex, prefs->prefix_dir,
161                               PLUGIN_COMPOUND_DIR, plugin_type, ext);
162   } else {
163     com = lives_strdup_printf("%s list_plugins %d 0 \"%s%s%s\" \"%s\"", prefs->backend_sync, allow_nonex, prefs->lib_dir,
164                               PLUGIN_EXEC_DIR, plugin_type, ext);
165   }
166   list_plugins = TRUE;
167 
168   //g_print("\n\n\nLIST CMD: %s\n",com);
169 
170   pluglist = get_plugin_result(com, "|", FALSE, TRUE);
171   lives_free(com);
172   //threaded_dialog_spin(0.);
173   pluglist = lives_list_sort_alpha(pluglist, TRUE);
174   return pluglist;
175 }
176 
177 
178 ///////////////////
179 // video plugins
180 
save_vpp_defaults(_vid_playback_plugin * vpp,char * vpp_file)181 void save_vpp_defaults(_vid_playback_plugin *vpp, char *vpp_file) {
182   // format is:
183   // nbytes (string) : LiVES vpp defaults file version 2\n
184   // for each video playback plugin:
185   // 4 bytes (int) name length
186   // n bytes name
187   // 4 bytes (int) version length
188   // n bytes version
189   // 4 bytes (int) palette
190   // 4 bytes (int) YUV sampling
191   // 4 bytes (int) YUV clamping
192   // 4 bytes (int) YUV subspace
193   // 4 bytes (int) width
194   // 4 bytes (int) height
195   // 8 bytes (double) fps
196   // 4 bytes (int) fps_numerator [0 indicates use fps double, >0 use fps_numer/fps_denom ]
197   // 4 bytes (int) fps_denominator
198   // 4 bytes argc (count of argv, may be 0)
199   //
200   // for each argv (extra params):
201   // 4 bytes (int) length
202   // n bytes string param value
203 
204   int fd;
205   int32_t len;
206   const char *version;
207   int i;
208   char *msg;
209   int intzero = 0;
210   double dblzero = 0.;
211 
212   if (!vpp) {
213     lives_rm(vpp_file);
214     return;
215   }
216 
217   if ((fd = lives_open3(vpp_file, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) == -1) {
218     msg = lives_strdup_printf(_("\n\nUnable to write video playback plugin defaults file\n%s\nError code %d\n"), vpp_file, errno);
219     LIVES_ERROR(msg);
220     lives_free(msg);
221     return;
222   }
223 
224   msg = lives_strdup_printf(_("Updating video playback plugin defaults in %s\n"), vpp_file);
225   LIVES_INFO(msg);
226   lives_free(msg);
227 
228   msg = lives_strdup("LiVES vpp defaults file version 2\n");
229   if (!lives_write(fd, msg, strlen(msg), FALSE)) return;
230   lives_free(msg);
231 
232   len = strlen(vpp->name);
233   if (lives_write_le(fd, &len, 4, FALSE) < 4) return;
234   if (lives_write(fd, vpp->name, len, FALSE) < len) return;
235 
236   version = (*vpp->version)();
237   len = strlen(version);
238   if (lives_write_le(fd, &len, 4, FALSE) < 4) return;
239   if (lives_write(fd, version, len, FALSE) < len) return;
240 
241   if (lives_write_le(fd, &(vpp->palette), 4, FALSE) < 4) return;
242   if (lives_write_le(fd, &(vpp->YUV_sampling), 4, FALSE) < 4) return;
243   if (lives_write_le(fd, &(vpp->YUV_clamping), 4, FALSE) < 4) return;
244   if (lives_write_le(fd, &(vpp->YUV_subspace), 4, FALSE) < 4) return;
245 
246   if (lives_write_le(fd, vpp->fwidth <= 0 ? &intzero : & (vpp->fwidth), 4, FALSE) < 4) return;
247   if (lives_write_le(fd, vpp->fheight <= 0 ? &intzero : & (vpp->fheight), 4, FALSE) < 4) return;
248 
249   if (lives_write_le(fd, vpp->fixed_fpsd <= 0. ? &dblzero : & (vpp->fixed_fpsd), 8, FALSE) < 8) return;
250   if (lives_write_le(fd, vpp->fixed_fps_numer <= 0 ? &intzero : & (vpp->fixed_fps_numer), 4, FALSE) < 4) return;
251   if (lives_write_le(fd, vpp->fixed_fps_denom <= 0 ? &intzero : & (vpp->fixed_fps_denom), 4, FALSE) < 4) return;
252 
253   if (lives_write_le(fd, &(vpp->extra_argc), 4, FALSE) < 4) return;
254 
255   for (i = 0; i < vpp->extra_argc; i++) {
256     len = strlen(vpp->extra_argv[i]);
257     if (lives_write_le(fd, &len, 4, FALSE) < 4) return;
258     if (lives_write(fd, vpp->extra_argv[i], len, FALSE) < len) return;
259   }
260 
261   close(fd);
262 }
263 
264 
load_vpp_defaults(_vid_playback_plugin * vpp,char * vpp_file)265 void load_vpp_defaults(_vid_playback_plugin *vpp, char *vpp_file) {
266   ssize_t len;
267   const char *version;
268 
269   char buf[512];
270 
271   char *msg;
272 
273   int retval;
274   int fd;
275 
276   register int i;
277 
278   if (!lives_file_test(vpp_file, LIVES_FILE_TEST_EXISTS)) {
279     return;
280   }
281 
282   d_print(_("Loading video playback plugin defaults from %s..."), vpp_file);
283 
284   do {
285     retval = 0;
286     if ((fd = lives_open2(vpp_file, O_RDONLY)) == -1) {
287       retval = do_read_failed_error_s_with_retry(vpp_file, lives_strerror(errno));
288       if (retval == LIVES_RESPONSE_CANCEL) {
289         vpp = NULL;
290         return;
291       }
292     } else {
293       do {
294         // only do this loop once, so we can use break to escape it
295 
296         THREADVAR(read_failed) = FALSE;
297         msg = lives_strdup("LiVES vpp defaults file version 2\n");
298         len = lives_read(fd, buf, strlen(msg), FALSE);
299         if (len < 0) len = 0;
300         lives_memset(buf + len, 0, 1);
301 
302         if (THREADVAR(read_failed)) break;
303 
304         // identifier string
305         if (strcmp(msg, buf)) {
306           lives_free(msg);
307           d_print_file_error_failed();
308           close(fd);
309           return;
310         }
311         lives_free(msg);
312 
313         // plugin name
314         lives_read_le(fd, &len, 4, FALSE);
315         if (THREADVAR(read_failed)) break;
316         lives_read(fd, buf, len, FALSE);
317         lives_memset(buf + len, 0, 1);
318 
319         if (THREADVAR(read_failed)) break;
320 
321         if (strcmp(buf, vpp->name)) {
322           d_print_file_error_failed();
323           close(fd);
324           return;
325         }
326 
327         // version string
328         version = (*vpp->version)();
329         lives_read_le(fd, &len, 4, FALSE);
330         if (THREADVAR(read_failed)) break;
331         lives_read(fd, buf, len, FALSE);
332 
333         if (THREADVAR(read_failed)) break;
334 
335         lives_memset(buf + len, 0, 1);
336 
337         if (strcmp(buf, version)) {
338           msg = lives_strdup_printf(
339                   _("\nThe %s video playback plugin has been updated.\nPlease check your settings in\n"
340                     "Tools|Preferences|Playback|Playback Plugins Advanced\n\n"),
341                   vpp->name);
342           widget_opts.non_modal = TRUE;
343           do_error_dialog(msg);
344           widget_opts.non_modal = FALSE;
345           lives_free(msg);
346           lives_rm(vpp_file);
347           d_print_failed();
348           close(fd);
349           return;
350         }
351 
352         lives_read_le(fd, &(vpp->palette), 4, FALSE);
353         lives_read_le(fd, &(vpp->YUV_sampling), 4, FALSE);
354         lives_read_le(fd, &(vpp->YUV_clamping), 4, FALSE);
355         lives_read_le(fd, &(vpp->YUV_subspace), 4, FALSE);
356         lives_read_le(fd, &(vpp->fwidth), 4, FALSE);
357         lives_read_le(fd, &(vpp->fheight), 4, FALSE);
358         lives_read_le(fd, &(vpp->fixed_fpsd), 8, FALSE);
359         lives_read_le(fd, &(vpp->fixed_fps_numer), 4, FALSE);
360         lives_read_le(fd, &(vpp->fixed_fps_denom), 4, FALSE);
361 
362         if (THREADVAR(read_failed)) break;
363 
364         lives_read_le(fd, &(vpp->extra_argc), 4, FALSE);
365 
366         if (THREADVAR(read_failed)) break;
367 
368         if (vpp->extra_argv) {
369           for (i = 0; vpp->extra_argv[i]; i++) {
370             lives_free(vpp->extra_argv[i]);
371           }
372           lives_free(vpp->extra_argv);
373         }
374 
375         vpp->extra_argv = (char **)lives_calloc((vpp->extra_argc + 1), (sizeof(char *)));
376 
377         for (i = 0; i < vpp->extra_argc; i++) {
378           lives_read_le(fd, &len, 4, FALSE);
379           if (THREADVAR(read_failed)) break;
380           vpp->extra_argv[i] = (char *)lives_malloc(len + 1);
381           lives_read(fd, vpp->extra_argv[i], len, FALSE);
382           if (THREADVAR(read_failed)) break;
383           lives_memset((vpp->extra_argv[i]) + len, 0, 1);
384         }
385 
386         vpp->extra_argv[i] = NULL;
387 
388         close(fd);
389       } while (FALSE);
390 
391       if (THREADVAR(read_failed)) {
392         close(fd);
393         retval = do_read_failed_error_s_with_retry(vpp_file, NULL);
394         if (retval == LIVES_RESPONSE_CANCEL) {
395           THREADVAR(read_failed) = FALSE;
396           vpp = NULL;
397           d_print_file_error_failed();
398           return;
399         }
400       }
401     }
402   } while (retval == LIVES_RESPONSE_RETRY);
403 
404   d_print_done();
405 }
406 
407 
on_vppa_cancel_clicked(LiVESButton * button,livespointer user_data)408 void on_vppa_cancel_clicked(LiVESButton *button, livespointer user_data) {
409   _vppaw *vppw = (_vppaw *)user_data;
410   _vid_playback_plugin *vpp = vppw->plugin;
411 
412   lives_widget_destroy(vppw->dialog);
413   lives_widget_context_update();
414   if (vpp && vpp != mainw->vpp) {
415     // close the temp current vpp
416     close_vid_playback_plugin(vpp);
417   }
418 
419   if (vppw->rfx) {
420     if (!vppw->keep_rfx) {
421       rfx_free(vppw->rfx);
422       lives_free(vppw->rfx);
423     }
424   }
425 
426   lives_free(vppw);
427 
428   if (prefsw) {
429     lives_window_present(LIVES_WINDOW(prefsw->prefs_dialog));
430     lives_xwindow_raise(lives_widget_get_xwindow(prefsw->prefs_dialog));
431   }
432 }
433 
434 
on_vppa_ok_clicked(LiVESButton * button,livespointer user_data)435 void on_vppa_ok_clicked(LiVESButton *button, livespointer user_data) {
436   _vppaw *vppw = (_vppaw *)user_data;
437   uint64_t xwinid = 0;
438 
439   const char *fixed_fps = NULL;
440   const char *tmp;
441 
442   char *cur_pal = NULL;
443 
444   int *pal_list, i = 0;
445 
446   boolean ext_audio = FALSE;
447 
448   _vid_playback_plugin *vpp = vppw->plugin;
449 
450   if (vppw->rfx && mainw->textwidget_focus) {
451     // make sure text widgets are updated if they activate the default
452     LiVESWidget *textwidget = (LiVESWidget *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(mainw->textwidget_focus),
453                               TEXTWIDGET_KEY);
454     after_param_text_changed(textwidget, vppw->rfx);
455   }
456 
457   if (!special_cleanup(TRUE)) {
458     // check for file overwrites with special type "filewrite"
459     // if user declines, will return with LIVES_RESPONSE_RETRY
460     if (LIVES_IS_DIALOG(lives_widget_get_toplevel(LIVES_WIDGET(button))))
461       lives_dialog_response(LIVES_DIALOG(lives_widget_get_toplevel(LIVES_WIDGET(button))), LIVES_RESPONSE_RETRY);
462     return;
463   }
464 
465   mainw->textwidget_focus = NULL;
466 
467   if (vpp == mainw->vpp) {
468     if (vppw->spinbuttonw) mainw->vpp->fwidth
469         = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(vppw->spinbuttonw));
470     if (vppw->spinbuttonh) mainw->vpp->fheight
471         = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(vppw->spinbuttonh));
472     if (vppw->apply_fx) mainw->fx1_bool = lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(vppw->apply_fx));
473     if (vppw->fps_entry) fixed_fps = lives_entry_get_text(LIVES_ENTRY(vppw->fps_entry));
474     if (vppw->pal_entry) {
475       cur_pal = lives_strdup(lives_entry_get_text(LIVES_ENTRY(vppw->pal_entry)));
476 
477       if (get_token_count(cur_pal, ' ') > 1) {
478         char **array = lives_strsplit(cur_pal, " ", 2);
479         char *clamping = lives_strdup(array[1] + 1);
480         lives_free(cur_pal);
481         cur_pal = lives_strdup(array[0]);
482         lives_memset(clamping + strlen(clamping) - 1, 0, 1);
483         do {
484           tmp = weed_yuv_clamping_get_name(i);
485           if (tmp && !strcmp(clamping, tmp)) {
486             vpp->YUV_clamping = i;
487             break;
488           }
489           i++;
490         } while (tmp);
491         lives_strfreev(array);
492         lives_free(clamping);
493       }
494     }
495 
496     if (vppw->fps_entry) {
497       if (!strlen(fixed_fps)) {
498         mainw->vpp->fixed_fpsd = -1.;
499         mainw->vpp->fixed_fps_numer = 0;
500       } else {
501         if (get_token_count((char *)fixed_fps, ':') > 1) {
502           char **array = lives_strsplit(fixed_fps, ":", 2);
503           mainw->vpp->fixed_fps_numer = atoi(array[0]);
504           mainw->vpp->fixed_fps_denom = atoi(array[1]);
505           lives_strfreev(array);
506           mainw->vpp->fixed_fpsd = get_ratio_fps((char *)fixed_fps);
507         } else {
508           mainw->vpp->fixed_fpsd = lives_strtod(fixed_fps, NULL);
509           mainw->vpp->fixed_fps_numer = 0;
510         }
511       }
512     } else {
513       mainw->vpp->fixed_fpsd = -1.;
514       mainw->vpp->fixed_fps_numer = 0;
515     }
516 
517     if (mainw->vpp->fixed_fpsd > 0. && (mainw->fixed_fpsd > 0. ||
518                                         (mainw->vpp->set_fps &&
519                                          !((*mainw->vpp->set_fps)(mainw->vpp->fixed_fpsd))))) {
520       do_vpp_fps_error();
521       mainw->error = TRUE;
522       mainw->vpp->fixed_fpsd = -1.;
523       mainw->vpp->fixed_fps_numer = 0;
524     }
525 
526     if (vppw->pal_entry) {
527       if (vpp->get_palette_list && (pal_list = (*vpp->get_palette_list)())) {
528         for (i = 0; pal_list[i] != WEED_PALETTE_END; i++) {
529           if (!strcmp(cur_pal, weed_palette_get_name(pal_list[i]))) {
530             vpp->palette = pal_list[i];
531             if (mainw->ext_playback) {
532               pthread_mutex_lock(&mainw->vpp_stream_mutex);
533               mainw->ext_audio = FALSE;
534               pthread_mutex_unlock(&mainw->vpp_stream_mutex);
535               lives_grab_remove(LIVES_MAIN_WINDOW_WIDGET);
536               if (mainw->vpp->exit_screen) {
537                 (*mainw->vpp->exit_screen)(mainw->ptr_x, mainw->ptr_y);
538               }
539 
540 #ifdef RT_AUDIO
541               stop_audio_stream();
542 #endif
543               mainw->stream_ticks = -1;
544               mainw->vpp->palette = pal_list[i];
545               if (!(*vpp->set_palette)(vpp->palette)) {
546                 do_vpp_palette_error();
547                 mainw->error = TRUE;
548               }
549 
550               if (prefs->play_monitor != 0) {
551                 if (mainw->play_window) {
552                   xwinid = lives_widget_get_xwinid(mainw->play_window, "Unsupported display type for playback plugin");
553                   if (xwinid == -1) {
554                     lives_dialog_response(LIVES_DIALOG(vppw->dialog), LIVES_RESPONSE_CANCEL);
555                     return;
556                   }
557                 }
558               }
559 
560 #ifdef RT_AUDIO
561               if (vpp->set_yuv_palette_clamping)(*vpp->set_yuv_palette_clamping)(vpp->YUV_clamping);
562 
563               if (mainw->vpp->audio_codec != AUDIO_CODEC_NONE && prefs->stream_audio_out) {
564                 start_audio_stream();
565               }
566 
567 #endif
568               if (vpp->init_audio && prefs->stream_audio_out) {
569 #ifdef HAVE_PULSE_AUDIO
570                 if (prefs->audio_player == AUD_PLAYER_PULSE && mainw->pulsed) {
571                   if ((*vpp->init_audio)(mainw->pulsed->out_arate, mainw->pulsed->out_achans, vpp->extra_argc, vpp->extra_argv))
572                     mainw->ext_audio = TRUE;
573                 }
574 #endif
575 #ifdef ENABLE_JACK
576                 if (prefs->audio_player == AUD_PLAYER_JACK && mainw->jackd) {
577                   if ((*vpp->init_audio)(mainw->jackd->sample_out_rate, mainw->jackd->num_output_channels,
578                                          vpp->extra_argc, vpp->extra_argv))
579                     ext_audio = TRUE;
580                 }
581 #endif
582               }
583 
584               if (vpp->init_screen) {
585                 (*vpp->init_screen)(mainw->vpp->fwidth > 0 ? mainw->vpp->fwidth : mainw->pwidth,
586                                     mainw->vpp->fheight > 0 ? mainw->vpp->fheight : mainw->pheight,
587                                     TRUE, xwinid, vpp->extra_argc, vpp->extra_argv);
588               }
589               mainw->ext_audio = ext_audio; // cannot set this until after init_screen()
590               if (mainw->vpp->capabilities & VPP_LOCAL_DISPLAY && prefs->play_monitor == 0) {
591                 lives_window_set_keep_below(LIVES_WINDOW(mainw->play_window), TRUE);
592                 lives_grab_add(LIVES_MAIN_WINDOW_WIDGET);
593               }
594             } else {
595               mainw->vpp->palette = pal_list[i];
596               if (!(*vpp->set_palette)(vpp->palette)) {
597                 do_vpp_palette_error();
598                 mainw->error = TRUE;
599               }
600               if (vpp->set_yuv_palette_clamping)(*vpp->set_yuv_palette_clamping)(vpp->YUV_clamping);
601             }
602             break;
603           }
604         }
605       }
606       lives_free(cur_pal);
607     }
608     if (vpp->extra_argv) {
609       for (i = 0; vpp->extra_argv[i]; i++) lives_free(vpp->extra_argv[i]);
610       lives_free(vpp->extra_argv);
611       vpp->extra_argv = NULL;
612     }
613     vpp->extra_argc = 0;
614     if (vppw->rfx) {
615       vpp->extra_argv = param_marshall_to_argv(vppw->rfx);
616       for (i = 0; vpp->extra_argv[i]; vpp->extra_argc = ++i);
617     }
618     mainw->write_vpp_file = TRUE;
619   } else {
620     if (vppw->spinbuttonw)
621       future_prefs->vpp_fwidth = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(vppw->spinbuttonw));
622     else future_prefs->vpp_fwidth = -1;
623     if (vppw->spinbuttonh)
624       future_prefs->vpp_fheight = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(vppw->spinbuttonh));
625     else future_prefs->vpp_fheight = -1;
626     if (vppw->fps_entry) fixed_fps = lives_entry_get_text(LIVES_ENTRY(vppw->fps_entry));
627     if (vppw->pal_entry) {
628       cur_pal = lives_strdup(lives_entry_get_text(LIVES_ENTRY(vppw->pal_entry)));
629 
630       if (get_token_count(cur_pal, ' ') > 1) {
631         char **array = lives_strsplit(cur_pal, " ", 2);
632         char *clamping = lives_strdup(array[1] + 1);
633         lives_free(cur_pal);
634         cur_pal = lives_strdup(array[0]);
635         lives_memset(clamping + strlen(clamping) - 1, 0, 1);
636         do {
637           tmp = weed_yuv_clamping_get_name(i);
638           if (tmp && !strcmp(clamping, tmp)) {
639             future_prefs->vpp_YUV_clamping = i;
640             break;
641           }
642           i++;
643         } while (tmp);
644         lives_strfreev(array);
645         lives_free(clamping);
646       }
647     }
648 
649     if (fixed_fps) {
650       if (get_token_count((char *)fixed_fps, ':') > 1) {
651         char **array = lives_strsplit(fixed_fps, ":", 2);
652         future_prefs->vpp_fixed_fps_numer = atoi(array[0]);
653         future_prefs->vpp_fixed_fps_denom = atoi(array[1]);
654         lives_strfreev(array);
655         future_prefs->vpp_fixed_fpsd = get_ratio_fps((char *)fixed_fps);
656       } else {
657         future_prefs->vpp_fixed_fpsd = lives_strtod(fixed_fps, NULL);
658         future_prefs->vpp_fixed_fps_numer = 0;
659       }
660     } else {
661       future_prefs->vpp_fixed_fpsd = -1.;
662       future_prefs->vpp_fixed_fps_numer = 0;
663     }
664 
665     if (cur_pal) {
666       if (vpp->get_palette_list && (pal_list = (*vpp->get_palette_list)())) {
667         for (i = 0; pal_list[i] != WEED_PALETTE_END; i++) {
668           if (!strcmp(cur_pal, weed_palette_get_name(pal_list[i]))) {
669             future_prefs->vpp_palette = pal_list[i];
670             break;
671           }
672         }
673       }
674       lives_free(cur_pal);
675     } else future_prefs->vpp_palette = WEED_PALETTE_END;
676 
677     if (future_prefs->vpp_argv) {
678       for (i = 0; future_prefs->vpp_argv[i]; i++) lives_free(future_prefs->vpp_argv[i]);
679       lives_free(future_prefs->vpp_argv);
680       future_prefs->vpp_argv = NULL;
681     }
682 
683     future_prefs->vpp_argc = 0;
684     if (vppw->rfx) {
685       future_prefs->vpp_argv = param_marshall_to_argv(vppw->rfx);
686       if (future_prefs->vpp_argv) {
687         for (i = 0; future_prefs->vpp_argv[i]; future_prefs->vpp_argc = ++i);
688       }
689     } else {
690       future_prefs->vpp_argv = vpp->extra_argv;
691       vpp->extra_argv = NULL;
692       vpp->extra_argc = 0;
693     }
694   }
695   if (button && !mainw->error) on_vppa_cancel_clicked(button, user_data);
696   else lives_dialog_response(LIVES_DIALOG(vppw->dialog), LIVES_RESPONSE_OK);
697   if (button) mainw->error = FALSE;
698 }
699 
700 
on_vppa_save_clicked(LiVESButton * button,livespointer user_data)701 void on_vppa_save_clicked(LiVESButton *button, livespointer user_data) {
702   _vppaw *vppw = (_vppaw *)user_data;
703   _vid_playback_plugin *vpp = vppw->plugin;
704   char *save_file;
705 
706   // apply
707   mainw->error = FALSE;
708   on_vppa_ok_clicked(NULL, user_data);
709   if (mainw->error) {
710     mainw->error = FALSE;
711     return;
712   }
713 
714   // get filename
715   save_file = choose_file(NULL, NULL, NULL, LIVES_FILE_CHOOSER_ACTION_SAVE, NULL, NULL);
716   if (!save_file) return;
717 
718   // save
719   d_print(_("Saving playback plugin defaults to %s..."), save_file);
720   save_vpp_defaults(vpp, save_file);
721   d_print_done();
722   lives_free(save_file);
723 
724 }
725 
726 
on_vpp_advanced_clicked(LiVESButton * button,livespointer user_data)727 _vppaw *on_vpp_advanced_clicked(LiVESButton *button, livespointer user_data) {
728   LiVESWidget *dialog_vbox;
729   LiVESWidget *hbox;
730   LiVESWidget *label;
731   LiVESWidget *combo;
732   LiVESWidget *cancelbutton;
733   LiVESWidget *okbutton;
734   LiVESWidget *savebutton;
735 
736   LiVESAccelGroup *accel_group;
737 
738   _vppaw *vppa;
739 
740   _vid_playback_plugin *tmpvpp;
741 
742   int *pal_list;
743 
744   double wscale = 1., hscale = 1.;
745 
746   int intention = LIVES_INTENTION_PLAY;
747   int hsize, vsize;
748 
749   LiVESList *fps_list_strings = NULL;
750   LiVESList *pal_list_strings = NULL;
751 
752   const char *string;
753   const char *pversion;
754   const char *desc;
755   const char *fps_list;
756 
757   char *title;
758   char *tmp, *tmp2;
759 
760   char *ctext = NULL;
761 
762   // TODO - set default values from tmpvpp
763 
764   if (user_data) intention = LIVES_POINTER_TO_INT(user_data);
765 
766   if (strlen(future_prefs->vpp_name)) {
767     if (!(tmpvpp = open_vid_playback_plugin(future_prefs->vpp_name, FALSE))) return NULL;
768   } else {
769     if (!mainw->vpp) return NULL;
770     tmpvpp = mainw->vpp;
771   }
772 
773   vppa = (_vppaw *)(lives_calloc(1, sizeof(_vppaw)));
774 
775   vppa->plugin = tmpvpp;
776   /* vppa->rfx = NULL; */
777   /* vppa->spinbuttonh = vppa->spinbuttonw = NULL; */
778   /* vppa->apply_fx = NULL; */
779   /* vppa->pal_entry = vppa->fps_entry = NULL; */
780   /* vppa->keep_rfx = FALSE; */
781 
782   vppa->intention = intention;
783 
784   pversion = (tmpvpp->version)();
785 
786   if (intention == LIVES_INTENTION_PLAY)
787     title = lives_strdup_printf("%s", pversion);
788   else {
789     // LIVES_INTENTION_TRANSCODE
790     title = (_("Quick Transcoding"));
791     wscale = 2. * widget_opts.scale;
792     hscale = 1.5;
793   }
794 
795   vppa->dialog = lives_standard_dialog_new(title, FALSE, DEF_DIALOG_WIDTH * wscale, DEF_DIALOG_HEIGHT * hscale);
796   lives_free(title);
797 
798   accel_group = LIVES_ACCEL_GROUP(lives_accel_group_new());
799   lives_window_add_accel_group(LIVES_WINDOW(vppa->dialog), accel_group);
800 
801   dialog_vbox = lives_dialog_get_content_area(LIVES_DIALOG(vppa->dialog));
802 
803   // the filling...
804   if (intention == LIVES_INTENTION_PLAY && tmpvpp->get_description) {
805     desc = (tmpvpp->get_description)();
806     if (desc) {
807       label = lives_standard_label_new(desc);
808       lives_box_pack_start(LIVES_BOX(dialog_vbox), label, FALSE, FALSE, widget_opts.packing_height);
809     }
810   }
811   if (intention == LIVES_INTENTION_TRANSCODE) {
812     tmp = lives_big_and_bold("%s", _("Quick transcode provides a rapid, high quality preview of the selected frames and audio."));
813     widget_opts.use_markup = TRUE;
814     label = lives_standard_label_new(tmp);
815     widget_opts.use_markup = FALSE;
816     lives_free(tmp);
817     lives_box_pack_start(LIVES_BOX(dialog_vbox), label, FALSE, FALSE, widget_opts.packing_height);
818   }
819 
820   if (tmpvpp->get_fps_list && (fps_list = (*tmpvpp->get_fps_list)(tmpvpp->palette))) {
821     int nfps, i;
822     char **array = lives_strsplit(fps_list, "|", -1);
823 
824     nfps = get_token_count((char *)fps_list, '|');
825     for (i = 0; i < nfps; i++) {
826       if (strlen(array[i]) && strcmp(array[i], "\n")) {
827         if (get_token_count(array[i], ':') == 0) {
828           fps_list_strings = lives_list_append(fps_list_strings, remove_trailing_zeroes(lives_strtod(array[i], NULL)));
829         } else fps_list_strings = lives_list_append(fps_list_strings, lives_strdup(array[i]));
830       }
831     }
832 
833     if (intention == LIVES_INTENTION_PLAY) {
834       // fps
835       combo = lives_standard_combo_new((tmp = (_("_FPS"))), fps_list_strings,
836                                        LIVES_BOX(dialog_vbox), (tmp2 = (_("Fixed framerate for plugin.\n"))));
837       lives_free(tmp);
838       lives_free(tmp2);
839       vppa->fps_entry = lives_combo_get_entry(LIVES_COMBO(combo));
840       lives_entry_set_width_chars(LIVES_ENTRY(lives_combo_get_entry(LIVES_COMBO(combo))), COMBOWIDTHCHARS);
841 
842       lives_list_free_all(&fps_list_strings);
843       lives_strfreev(array);
844 
845       if (tmpvpp->fixed_fps_numer > 0) {
846         char *tmp = lives_strdup_printf("%d:%d", tmpvpp->fixed_fps_numer, tmpvpp->fixed_fps_denom);
847         lives_entry_set_text(LIVES_ENTRY(vppa->fps_entry), tmp);
848         lives_free(tmp);
849       } else {
850         char *tmp = remove_trailing_zeroes(tmpvpp->fixed_fpsd);
851         lives_entry_set_text(LIVES_ENTRY(vppa->fps_entry), tmp);
852         lives_free(tmp);
853       }
854     }
855   }
856 
857   // frame size
858 
859   if (!(tmpvpp->capabilities & VPP_LOCAL_DISPLAY)) {
860     hbox = lives_hbox_new(FALSE, 0);
861     lives_box_pack_start(LIVES_BOX(dialog_vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
862 
863     add_fill_to_box(LIVES_BOX(hbox));
864 
865     hsize = tmpvpp->fwidth > 0 ? tmpvpp->fwidth :
866             intention == LIVES_INTENTION_TRANSCODE ? cfile->hsize : DEF_VPP_HSIZE;
867 
868     vppa->spinbuttonw = lives_standard_spin_button_new(_("_Width"),
869                         hsize,
870                         4., MAX_FRAME_WIDTH, 4., 16., 0, LIVES_BOX(hbox), NULL);
871 
872     add_fill_to_box(LIVES_BOX(hbox));
873 
874     vsize = tmpvpp->fheight > 0 ? tmpvpp->fheight :
875             intention == LIVES_INTENTION_TRANSCODE ? cfile->vsize : DEF_VPP_VSIZE;
876 
877     vppa->spinbuttonh = lives_standard_spin_button_new(_("_Height"),
878                         vsize,
879                         4., MAX_FRAME_HEIGHT, 4., 16., 0, LIVES_BOX(hbox), NULL);
880 
881     if (intention == LIVES_INTENTION_TRANSCODE) {
882       if (mainw->event_list) {
883         lives_widget_set_no_show_all(hbox, TRUE);
884       } else {
885         // add aspect ratio butto
886         lives_special_aspect_t *aspect;
887         /* hbox = lives_hbox_new(FALSE, 0); */
888         /* lives_box_pack_start(LIVES_BOX(dialog_vbox), hbox, FALSE, FALSE, widget_opts.packing_height); */
889         aspect = (lives_special_aspect_t *)add_aspect_ratio_button(LIVES_SPIN_BUTTON(vppa->spinbuttonw),
890                  LIVES_SPIN_BUTTON(vppa->spinbuttonh), LIVES_BOX(hbox));
891         // don't reset the aspect params when we make_param_box
892         aspect->no_reset = TRUE;
893       }
894     }
895     add_fill_to_box(LIVES_BOX(hbox));
896   }
897 
898   if (intention == LIVES_INTENTION_PLAY) {
899     if (tmpvpp->get_palette_list && (pal_list = (*tmpvpp->get_palette_list)())) {
900       int i;
901 
902       for (i = 0; pal_list[i] != WEED_PALETTE_END; i++) {
903         int j = 0;
904         string = weed_palette_get_name(pal_list[i]);
905         if (weed_palette_is_yuv(pal_list[i]) && tmpvpp->get_yuv_palette_clamping) {
906           int *clampings = (*tmpvpp->get_yuv_palette_clamping)(pal_list[i]);
907           while (clampings[j] != -1) {
908             char *string2 = lives_strdup_printf("%s (%s)", string, weed_yuv_clamping_get_name(clampings[j]));
909             pal_list_strings = lives_list_append(pal_list_strings, string2);
910             j++;
911           }
912         }
913         if (j == 0) {
914           pal_list_strings = lives_list_append(pal_list_strings, lives_strdup(string));
915         }
916       }
917 
918       combo = lives_standard_combo_new((tmp = (_("_Colourspace"))), pal_list_strings,
919                                        LIVES_BOX(dialog_vbox), tmp2 = (_("Colourspace input to the plugin.\n")));
920       lives_free(tmp);
921       lives_free(tmp2);
922       vppa->pal_entry = lives_combo_get_entry(LIVES_COMBO(combo));
923 
924       if (tmpvpp->get_yuv_palette_clamping && weed_palette_is_yuv(tmpvpp->palette)) {
925         int *clampings = tmpvpp->get_yuv_palette_clamping(tmpvpp->palette);
926         if (clampings[0] != -1)
927           ctext = lives_strdup_printf("%s (%s)", weed_palette_get_name(tmpvpp->palette),
928                                       weed_yuv_clamping_get_name(tmpvpp->YUV_clamping));
929       }
930       if (!ctext) ctext = lives_strdup(weed_palette_get_name(tmpvpp->palette));
931       lives_entry_set_text(LIVES_ENTRY(vppa->pal_entry), ctext);
932       lives_free(ctext);
933       lives_list_free_all(&pal_list_strings);
934     }
935   }
936   if (intention == LIVES_INTENTION_TRANSCODE) {
937     vppa->apply_fx = lives_standard_check_button_new(_("Apply current realtime effects"),
938                      FALSE, LIVES_BOX(dialog_vbox), NULL);
939     if (mainw->event_list) lives_widget_set_no_show_all(widget_opts.last_container, TRUE);
940   }
941 
942   // extra params
943   if (tmpvpp->get_init_rfx) {
944     LiVESWidget *vbox = lives_vbox_new(FALSE, 0);
945     /* LiVESWidget *scrolledwindow = lives_standard_scrolled_window_new(RFX_WINSIZE_H, RFX_WINSIZE_V / 2, vbox); */
946     lives_box_pack_start(LIVES_BOX(dialog_vbox), vbox, TRUE, TRUE, 0);
947     plugin_run_param_window((*tmpvpp->get_init_rfx)(intention), LIVES_VBOX(vbox), &(vppa->rfx));
948     if (intention != LIVES_INTENTION_TRANSCODE) {
949       char *fnamex = lives_build_filename(prefs->workdir, vppa->rfx->name, NULL);
950       if (lives_file_test(fnamex, LIVES_FILE_TEST_EXISTS))
951         lives_rm(fnamex);
952       lives_free(fnamex);
953     }
954     if (tmpvpp->extra_argv && tmpvpp->extra_argc > 0) {
955       // update with defaults
956       LiVESList *plist = argv_to_marshalled_list(vppa->rfx, tmpvpp->extra_argc, tmpvpp->extra_argv);
957       param_demarshall(vppa->rfx, plist, FALSE, FALSE); // set defaults
958       param_demarshall(vppa->rfx, plist, FALSE, TRUE); // update widgets
959       lives_list_free_all(&plist);
960     }
961   }
962 
963   cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(vppa->dialog), LIVES_STOCK_CANCEL, NULL,
964                  LIVES_RESPONSE_CANCEL);
965 
966   lives_widget_add_accelerator(cancelbutton, LIVES_WIDGET_CLICKED_SIGNAL, accel_group,
967                                LIVES_KEY_Escape, (LiVESXModifierType)0, (LiVESAccelFlags)0);
968 
969   if (intention == LIVES_INTENTION_PLAY) {
970     savebutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(vppa->dialog), LIVES_STOCK_SAVE_AS, NULL,
971                  LIVES_RESPONSE_BROWSE);
972 
973     lives_widget_set_tooltip_text(savebutton, _("Save settings to an alternate file.\n"));
974     lives_signal_connect(LIVES_GUI_OBJECT(savebutton), LIVES_WIDGET_CLICKED_SIGNAL,
975                          LIVES_GUI_CALLBACK(on_vppa_save_clicked),
976                          vppa);
977   }
978 
979   okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(vppa->dialog), LIVES_STOCK_OK, NULL,
980              LIVES_RESPONSE_OK);
981 
982   lives_button_grab_default_special(okbutton);
983 
984   lives_signal_sync_connect(LIVES_GUI_OBJECT(cancelbutton), LIVES_WIDGET_CLICKED_SIGNAL,
985                             LIVES_GUI_CALLBACK(on_vppa_cancel_clicked), vppa);
986 
987   lives_signal_sync_connect(LIVES_GUI_OBJECT(okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
988                             LIVES_GUI_CALLBACK(on_vppa_ok_clicked), vppa);
989 
990   lives_widget_show_all(vppa->dialog);
991   lives_window_present(LIVES_WINDOW(vppa->dialog));
992   lives_xwindow_raise(lives_widget_get_xwindow(vppa->dialog));
993 
994   return vppa;
995 }
996 
997 
close_vid_playback_plugin(_vid_playback_plugin * vpp)998 void close_vid_playback_plugin(_vid_playback_plugin *vpp) {
999   register int i;
1000 
1001   if (vpp) {
1002     if (vpp == mainw->vpp) {
1003       lives_grab_remove(LIVES_MAIN_WINDOW_WIDGET);
1004       if (mainw->ext_playback) {
1005         pthread_mutex_lock(&mainw->vpp_stream_mutex);
1006         mainw->ext_audio = FALSE;
1007         mainw->ext_playback = FALSE;
1008         pthread_mutex_unlock(&mainw->vpp_stream_mutex);
1009         if (mainw->vpp->exit_screen) {
1010           (*mainw->vpp->exit_screen)(mainw->ptr_x, mainw->ptr_y);
1011         }
1012 #ifdef RT_AUDIO
1013         stop_audio_stream();
1014 #endif
1015         if (mainw->vpp->capabilities & VPP_LOCAL_DISPLAY)
1016           if (mainw->play_window && prefs->play_monitor == 0)
1017             lives_window_set_keep_below(LIVES_WINDOW(mainw->play_window), FALSE);
1018       }
1019       mainw->stream_ticks = -1;
1020       mainw->vpp = NULL;
1021     }
1022     if (vpp->module_unload)(vpp->module_unload)();
1023     dlclose(vpp->handle);
1024 
1025     if (vpp->extra_argv) {
1026       for (i = 0; vpp->extra_argv[i]; i++) {
1027         lives_free(vpp->extra_argv[i]);
1028       }
1029       lives_free(vpp->extra_argv);
1030     }
1031 
1032     for (i = 0; i < vpp->num_play_params + vpp->num_alpha_chans; i++) {
1033       weed_plant_free(vpp->play_params[i]);
1034     }
1035 
1036     lives_freep((void **)&vpp->play_params);
1037     lives_free(vpp);
1038   }
1039 }
1040 
1041 
pp_get_param(weed_plant_t ** pparams,int idx)1042 const weed_plant_t *pp_get_param(weed_plant_t **pparams, int idx) {
1043   register int i = 0;
1044   while (pparams[i]) {
1045     if (WEED_PLANT_IS_PARAMETER(pparams[i])) {
1046       if (--idx < 0) return pparams[i];
1047     }
1048     i++;
1049   }
1050   return NULL;
1051 }
1052 
1053 
pp_get_chan(weed_plant_t ** pparams,int idx)1054 const weed_plant_t *pp_get_chan(weed_plant_t **pparams, int idx) {
1055   register int i = 0;
1056   while (pparams[i]) {
1057     if (WEED_PLANT_IS_CHANNEL(pparams[i])) {
1058       if (--idx < 0) return pparams[i];
1059     }
1060     i++;
1061   }
1062   return NULL;
1063 }
1064 
1065 
vpp_try_match_palette(_vid_playback_plugin * vpp,weed_layer_t * layer)1066 boolean vpp_try_match_palette(_vid_playback_plugin *vpp, weed_layer_t *layer) {
1067   int *pal_list, i = 0;
1068   if (vpp->get_palette_list && (pal_list = (*vpp->get_palette_list)())) {
1069     int palette = weed_layer_get_palette(layer);
1070     for (i = 0; pal_list[i] != WEED_PALETTE_END; i++) {
1071       if (pal_list[i] == palette) break;
1072     }
1073     if (pal_list[i] == WEED_PALETTE_END) {
1074       if (!i) return FALSE;
1075       palette = best_palette_match(pal_list, i, palette);
1076     }
1077     if (palette == WEED_PALETTE_END) return FALSE;
1078     if (palette != vpp->palette) {
1079       if (!(*vpp->set_palette)(palette)) {
1080         return FALSE;
1081       }
1082     }
1083     vpp->palette = palette;
1084     if (weed_palette_is_yuv(palette) && vpp->get_yuv_palette_clamping) {
1085       int *yuv_clamping_types = (*vpp->get_yuv_palette_clamping)(vpp->palette);
1086       int lclamping = weed_layer_get_yuv_clamping(layer);
1087       for (i = 0; yuv_clamping_types[i] != -1; i++) {
1088         if (yuv_clamping_types[i] == lclamping) {
1089           if ((*vpp->set_yuv_palette_clamping)(lclamping))
1090             vpp->YUV_clamping = lclamping;
1091           break;
1092 	  // *INDENT-OFF*
1093 	}}}}
1094   // *INDENT-ON*
1095   return FALSE;
1096 }
1097 
1098 
open_vid_playback_plugin(const char * name,boolean in_use)1099 _vid_playback_plugin *open_vid_playback_plugin(const char *name, boolean in_use) {
1100   // this is called on startup or when the user selects a new playback plugin
1101 
1102   // if in_use is TRUE, it is our active vpp
1103 
1104   // TODO - if in_use, get fixed_fps,fwidth,fheight,palette,argc and argv from a file
1105 
1106   _vid_playback_plugin *vpp;
1107   char **array;
1108   const char *fps_list;
1109   const char *pl_error;
1110   void *handle;
1111   int *palette_list;
1112   char *msg, *tmp;
1113   char *plugname;
1114   int dlflags = RTLD_LAZY;
1115   boolean OK = TRUE;
1116 
1117   if (in_use && LIVES_IS_PLAYING && mainw->noswitch) {
1118     mainw->new_vpp = name;
1119     return NULL;
1120   }
1121 
1122   tmp = lives_strdup_printf("%s.%s", name, DLL_NAME);
1123   plugname = lives_build_filename(prefs->lib_dir, PLUGIN_EXEC_DIR, PLUGIN_VID_PLAYBACK, tmp, NULL);
1124   lives_free(tmp);
1125 
1126   handle = dlopen(plugname, dlflags);
1127 
1128   if (!handle) {
1129     char *msg = lives_strdup_printf(_("\n\nFailed to open playback plugin %s\nError was %s\n"
1130                                       "Playback plugin will be disabled,\n"
1131                                       "it can be re-enabled in Preferences / Playback.\n"), plugname, dlerror());
1132     if (prefs->startup_phase != 1 && prefs->startup_phase != -1) {
1133       if (!prefsw) widget_opts.non_modal = TRUE;
1134       do_error_dialog(msg);
1135       widget_opts.non_modal = FALSE;
1136     }
1137     LIVES_ERROR(msg);
1138     lives_free(msg);
1139     lives_free(plugname);
1140     lives_snprintf(future_prefs->vpp_name, 64, "%s", mainw->string_constants[LIVES_STRING_CONSTANT_NONE]);
1141     set_vpp(TRUE);
1142     return NULL;
1143   }
1144 
1145   vpp = (_vid_playback_plugin *) lives_calloc(sizeof(_vid_playback_plugin), 1);
1146 
1147   vpp->play_paramtmpls = NULL;
1148   vpp->get_init_rfx = NULL;
1149   vpp->play_params = NULL;
1150   vpp->alpha_chans = NULL;
1151   vpp->num_play_params = vpp->num_alpha_chans = 0;
1152   vpp->extra_argv = NULL;
1153 
1154   if ((vpp->module_check_init = (const char *(*)())dlsym(handle, "module_check_init")) == NULL) {
1155     OK = FALSE;
1156   }
1157   if ((vpp->version = (const char *(*)())dlsym(handle, "version")) == NULL) {
1158     OK = FALSE;
1159   }
1160   if ((vpp->get_palette_list = (int *(*)())dlsym(handle, "get_palette_list")) == NULL) {
1161     OK = FALSE;
1162   }
1163   if ((vpp->set_palette = (boolean(*)(int))dlsym(handle, "set_palette")) == NULL) {
1164     OK = FALSE;
1165   }
1166   if ((vpp->get_capabilities = (uint64_t (*)(int))dlsym(handle, "get_capabilities")) == NULL) {
1167     OK = FALSE;
1168   }
1169   if ((vpp->render_frame = (boolean(*)(int, int, int64_t, void **, void **, weed_plant_t **))
1170                            dlsym(handle, "render_frame")) == NULL) {
1171     if ((vpp->play_frame = (boolean(*)(weed_layer_t *, ticks_t, weed_layer_t *))
1172                            dlsym(handle, "play_frame")) == NULL) {
1173       OK = FALSE;
1174     }
1175   }
1176   if ((vpp->get_fps_list = (const char *(*)(int))dlsym(handle, "get_fps_list"))) {
1177     if ((vpp->set_fps = (boolean(*)(double))dlsym(handle, "set_fps")) == NULL) {
1178       OK = FALSE;
1179     }
1180   }
1181 
1182   if (!OK) {
1183     char *msg = lives_strdup_printf
1184                 (_("\n\nPlayback module %s\nis missing a mandatory function.\nUnable to use it.\n"), plugname);
1185     set_string_pref(PREF_VID_PLAYBACK_PLUGIN, "none");
1186     do_error_dialog(msg);
1187     lives_free(msg);
1188     dlclose(handle);
1189     lives_free(vpp);
1190     vpp = NULL;
1191     lives_free(plugname);
1192     return NULL;
1193   }
1194 
1195   if ((pl_error = (*vpp->module_check_init)())) {
1196     msg = lives_strdup_printf(_("Video playback plugin failed to initialise.\nError was: %s\n"), pl_error);
1197     if (prefs->startup_phase != 1 && prefs->startup_phase != -1) {
1198       do_error_dialog(msg);
1199     } else {
1200       LIVES_ERROR(msg);
1201     }
1202     lives_free(msg);
1203     dlclose(handle);
1204     lives_free(vpp);
1205     vpp = NULL;
1206     lives_free(plugname);
1207     return NULL;
1208   }
1209 
1210   // now check for optional functions
1211   vpp->weed_setup = (weed_plant_t *(*)(weed_bootstrap_f))dlsym(handle, "weed_setup");
1212 
1213   if (vpp->weed_setup) {
1214     weed_set_host_info_callback(host_info_cb, LIVES_INT_TO_POINTER(100));
1215     (*vpp->weed_setup)(weed_bootstrap);
1216   }
1217 
1218   vpp->get_description = (const char *(*)())dlsym(handle, "get_description");
1219   vpp->get_init_rfx = (const char *(*)())dlsym(handle, "get_init_rfx");
1220 
1221   vpp->get_play_params = (const weed_plant_t **(*)(weed_bootstrap_f))dlsym(handle, "get_play_params");
1222 
1223   vpp->get_yuv_palette_clamping = (int *(*)(int))dlsym(handle, "get_yuv_palette_clamping");
1224   vpp->set_yuv_palette_clamping = (int (*)(int))dlsym(handle, "set_yuv_palette_clamping");
1225   vpp->get_audio_fmts = (int *(*)())dlsym(handle, "get_audio_fmts");
1226   vpp->init_screen = (boolean(*)(int, int, boolean, uint64_t, int, char **))dlsym(handle, "init_screen");
1227   vpp->init_audio = (boolean(*)(int, int, int, char **))dlsym(handle, "init_audio");
1228   vpp->render_audio_frame_float = (boolean(*)(float **, int))dlsym(handle, "render_audio_frame_float");
1229   vpp->exit_screen = (void (*)(uint16_t, uint16_t))dlsym(handle, "exit_screen");
1230   vpp->module_unload = (void (*)())dlsym(handle, "module_unload");
1231 
1232   vpp->YUV_sampling = 0;
1233   vpp->YUV_subspace = 0;
1234 
1235   palette_list = (*vpp->get_palette_list)();
1236 
1237   if (future_prefs->vpp_argv) {
1238     vpp->palette = future_prefs->vpp_palette;
1239     vpp->YUV_clamping = future_prefs->vpp_YUV_clamping;
1240   } else {
1241     if (!in_use && mainw->vpp && !(strcmp(name, mainw->vpp->name))) {
1242       vpp->palette = mainw->vpp->palette;
1243       vpp->YUV_clamping = mainw->vpp->YUV_clamping;
1244     } else {
1245       vpp->palette = palette_list[0];
1246       vpp->YUV_clamping = -1;
1247     }
1248   }
1249 
1250   vpp->audio_codec = AUDIO_CODEC_NONE;
1251   vpp->capabilities = (*vpp->get_capabilities)(vpp->palette);
1252 
1253   if (vpp->capabilities & VPP_CAN_RESIZE && vpp->capabilities & VPP_LOCAL_DISPLAY) {
1254     vpp->fwidth = vpp->fheight = -1;
1255   } else {
1256     vpp->fwidth = vpp->fheight = 0;
1257   }
1258   if (future_prefs->vpp_argv) {
1259     vpp->fwidth = future_prefs->vpp_fwidth;
1260     vpp->fheight = future_prefs->vpp_fheight;
1261   } else if (!in_use && mainw->vpp && !(strcmp(name, mainw->vpp->name))) {
1262     vpp->fwidth = mainw->vpp->fwidth;
1263     vpp->fheight = mainw->vpp->fheight;
1264   }
1265   if (vpp->fwidth == -1 && !(vpp->capabilities & VPP_CAN_RESIZE && vpp->capabilities & VPP_LOCAL_DISPLAY)) {
1266     vpp->fwidth = vpp->fheight = 0;
1267   }
1268 
1269   vpp->fixed_fpsd = -1.;
1270   vpp->fixed_fps_numer = 0;
1271 
1272   if (future_prefs->vpp_argv) {
1273     vpp->fixed_fpsd = future_prefs->vpp_fixed_fpsd;
1274     vpp->fixed_fps_numer = future_prefs->vpp_fixed_fps_numer;
1275     vpp->fixed_fps_denom = future_prefs->vpp_fixed_fps_denom;
1276   } else if (!in_use && mainw->vpp && !(strcmp(name, mainw->vpp->name))) {
1277     vpp->fixed_fpsd = mainw->vpp->fixed_fpsd;
1278     vpp->fixed_fps_numer = mainw->vpp->fixed_fps_numer;
1279     vpp->fixed_fps_denom = mainw->vpp->fixed_fps_denom;
1280   }
1281 
1282   vpp->handle = handle;
1283   lives_snprintf(vpp->name, 256, "%s", name);
1284 
1285   if (future_prefs->vpp_argv) {
1286     vpp->extra_argc = future_prefs->vpp_argc;
1287     vpp->extra_argv = (char **)lives_calloc((vpp->extra_argc + 1), (sizeof(char *)));
1288     for (register int i = 0; i <= vpp->extra_argc; i++) vpp->extra_argv[i] = lives_strdup(future_prefs->vpp_argv[i]);
1289   } else {
1290     if (!in_use && mainw->vpp && !(strcmp(name, mainw->vpp->name))) {
1291       vpp->extra_argc = mainw->vpp->extra_argc;
1292       vpp->extra_argv = (char **)lives_calloc((mainw->vpp->extra_argc + 1), (sizeof(char *)));
1293       for (register int i = 0; i <= vpp->extra_argc; i++) vpp->extra_argv[i] = lives_strdup(mainw->vpp->extra_argv[i]);
1294     } else {
1295       vpp->extra_argc = 0;
1296       vpp->extra_argv = (char **)lives_malloc(sizeof(char *));
1297       vpp->extra_argv[0] = NULL;
1298     }
1299   }
1300   // see if plugin is using fixed fps
1301 
1302   if (vpp->fixed_fpsd <= 0. && vpp->get_fps_list) {
1303     // fixed fps
1304 
1305     if ((fps_list = (*vpp->get_fps_list)(vpp->palette))) {
1306       array = lives_strsplit(fps_list, "|", -1);
1307       if (get_token_count(array[0], ':') > 1) {
1308         char **array2 = lives_strsplit(array[0], ":", 2);
1309         vpp->fixed_fps_numer = atoi(array2[0]);
1310         vpp->fixed_fps_denom = atoi(array2[1]);
1311         lives_strfreev(array2);
1312         vpp->fixed_fpsd = get_ratio_fps(array[0]);
1313       } else {
1314         vpp->fixed_fpsd = lives_strtod(array[0], NULL);
1315         vpp->fixed_fps_numer = 0;
1316       }
1317       lives_strfreev(array);
1318     }
1319   }
1320 
1321   if (vpp->YUV_clamping == -1) {
1322     vpp->YUV_clamping = WEED_YUV_CLAMPING_CLAMPED;
1323 
1324     if (vpp->get_yuv_palette_clamping && weed_palette_is_yuv(vpp->palette)) {
1325       int *yuv_clamping_types = (*vpp->get_yuv_palette_clamping)(vpp->palette);
1326       if (yuv_clamping_types[0] != -1) vpp->YUV_clamping = yuv_clamping_types[0];
1327     }
1328   }
1329 
1330   if (vpp->get_audio_fmts && mainw->is_ready) vpp->audio_codec = get_best_audio(vpp);
1331   if (prefsw) {
1332     prefsw_set_astream_settings(vpp, prefsw);
1333     prefsw_set_rec_after_settings(vpp, prefsw);
1334   }
1335 
1336   /// get the play parameters (and alpha channels) if any and convert to weed params
1337   if (vpp->get_play_params) {
1338     vpp->play_paramtmpls = (*vpp->get_play_params)(NULL);
1339   }
1340 
1341   // create vpp->play_params
1342   if (vpp->play_paramtmpls) {
1343     register int i;
1344     weed_plant_t *ptmpl;
1345     for (i = 0; (ptmpl = (weed_plant_t *)vpp->play_paramtmpls[i]); i++) {
1346       vpp->play_params = (weed_plant_t **)lives_realloc(vpp->play_params, (i + 2) * sizeof(weed_plant_t *));
1347       if (WEED_PLANT_IS_PARAMETER_TEMPLATE(ptmpl)) {
1348         // is param template, create a param
1349         vpp->play_params[i] = weed_plant_new(WEED_PLANT_PARAMETER);
1350         weed_leaf_copy(vpp->play_params[i], WEED_LEAF_VALUE, ptmpl, WEED_LEAF_DEFAULT);
1351         weed_set_plantptr_value(vpp->play_params[i], WEED_LEAF_TEMPLATE, ptmpl);
1352         vpp->num_play_params++;
1353       } else {
1354         // must be an alpha channel
1355         vpp->play_params[i] = weed_plant_new(WEED_PLANT_CHANNEL);
1356         weed_set_plantptr_value(vpp->play_params[i], WEED_LEAF_TEMPLATE, ptmpl);
1357         vpp->num_alpha_chans++;
1358       }
1359     }
1360     vpp->play_params[i] = NULL;
1361   }
1362 
1363   if (!in_use) return vpp;
1364 
1365   if (!mainw->is_ready) {
1366     double fixed_fpsd = vpp->fixed_fpsd;
1367     int fwidth = vpp->fwidth;
1368     int fheight = vpp->fheight;
1369 
1370     mainw->vpp = vpp;
1371     load_vpp_defaults(vpp, mainw->vpp_defs_file);
1372     if (fixed_fpsd < 0.) vpp->fixed_fpsd = fixed_fpsd;
1373     if (fwidth < 0) vpp->fwidth = fwidth;
1374     if (fheight < 0) vpp->fheight = fheight;
1375   }
1376 
1377   if (!(*vpp->set_palette)(vpp->palette)) {
1378     do_vpp_palette_error();
1379     close_vid_playback_plugin(vpp);
1380     lives_free(plugname);
1381     return NULL;
1382   }
1383 
1384   if (vpp->set_yuv_palette_clamping)(*vpp->set_yuv_palette_clamping)(vpp->YUV_clamping);
1385 
1386   if (vpp->get_fps_list) {
1387     if (mainw->fixed_fpsd > 0. || (vpp->fixed_fpsd > 0. && vpp->set_fps &&
1388                                    !((*vpp->set_fps)(vpp->fixed_fpsd)))) {
1389       do_vpp_fps_error();
1390       vpp->fixed_fpsd = -1.;
1391       vpp->fixed_fps_numer = 0;
1392     }
1393   }
1394 
1395   cached_key = cached_mod = 0;
1396 
1397   // TODO: - support other YUV subspaces
1398   d_print(_("*** Using %s plugin for fs playback, agreed to use palette type %d ( %s ). ***\n"), name,
1399           vpp->palette, (tmp = weed_palette_get_name_full(vpp->palette, vpp->YUV_clamping,
1400                                WEED_YUV_SUBSPACE_YCBCR)));
1401   lives_free(tmp);
1402 
1403   lives_free(plugname);
1404 
1405   if (mainw->is_ready && in_use && mainw->vpp) {
1406     close_vid_playback_plugin(mainw->vpp);
1407   }
1408 
1409   return vpp;
1410 }
1411 
1412 
vid_playback_plugin_exit(void)1413 void vid_playback_plugin_exit(void) {
1414   // external plugin
1415   if (mainw->ext_playback) {
1416     pthread_mutex_lock(&mainw->vpp_stream_mutex);
1417     mainw->ext_audio = FALSE;
1418     pthread_mutex_unlock(&mainw->vpp_stream_mutex);
1419     lives_grab_remove(LIVES_MAIN_WINDOW_WIDGET);
1420 
1421     if (mainw->vpp->exit_screen) {
1422       (*mainw->vpp->exit_screen)(mainw->ptr_x, mainw->ptr_y);
1423     }
1424 #ifdef RT_AUDIO
1425     stop_audio_stream();
1426 #endif
1427     mainw->ext_playback = FALSE;
1428     if (mainw->vpp->capabilities & VPP_LOCAL_DISPLAY)
1429       if (mainw->play_window && prefs->play_monitor == 0)
1430         lives_window_set_keep_below(LIVES_WINDOW(mainw->play_window), FALSE);
1431   }
1432   mainw->stream_ticks = -1;
1433 
1434   if (LIVES_IS_PLAYING && mainw->fs && mainw->sep_win) lives_window_fullscreen(LIVES_WINDOW(mainw->play_window));
1435   if (mainw->play_window) {
1436     play_window_set_title();
1437   }
1438 }
1439 
1440 
get_best_audio(_vid_playback_plugin * vpp)1441 int64_t get_best_audio(_vid_playback_plugin * vpp) {
1442   // find best audio from video plugin list, matching with audiostream plugins
1443 
1444   // i.e. cross-check video list with astreamer list
1445 
1446   // only for plugins which want to stream audiom but dont provide a render_audio_frame()
1447 
1448   int *fmts, *sfmts;
1449   int ret = AUDIO_CODEC_NONE;
1450   int i, j = 0, nfmts;
1451   char *astreamer, *com;
1452   char buf[1024];
1453   char **array;
1454 
1455   if (vpp && vpp->get_audio_fmts) {
1456     fmts = (*vpp->get_audio_fmts)(); // const, so do not free()
1457 
1458     // make audiostream plugin name
1459     astreamer = lives_build_filename(prefs->lib_dir, PLUGIN_EXEC_DIR, PLUGIN_AUDIO_STREAM, AUDIO_STREAMER_NAME, NULL);
1460 
1461     // create sfmts array and nfmts
1462 
1463     com = lives_strdup_printf("\"%s\" get_formats", astreamer);
1464     lives_popen(com, FALSE, buf, 1024);
1465     lives_free(com);
1466 
1467     nfmts = get_token_count(buf, '|');
1468     array = lives_strsplit(buf, "|", nfmts);
1469     sfmts = (int *)lives_calloc(nfmts, sizint);
1470 
1471     for (i = 0; i < nfmts; i++) {
1472       if (array[i] && *array[i]) sfmts[j++] = atoi(array[i]);
1473     }
1474 
1475     nfmts = j;
1476     lives_strfreev(array);
1477 
1478     for (i = 0; fmts[i] != -1; i++) {
1479       // traverse video list and see if audiostreamer supports each one
1480       if (int_array_contains_value(sfmts, nfmts, fmts[i])) {
1481 
1482         com = lives_strdup_printf("\"%s\" check %d", astreamer, fmts[i]);
1483         lives_popen(com, FALSE, buf, 1024);
1484         lives_free(com);
1485 
1486         if (THREADVAR(com_failed)) {
1487           lives_free(astreamer);
1488           lives_free(com);
1489           lives_free(sfmts);
1490           return ret;
1491         }
1492 
1493         if (*buf) {
1494           if (i == 0 && prefsw) {
1495             do_error_dialog(buf);
1496             d_print(_("Audio stream unable to use preferred format '%s'\n"), anames[fmts[i]]);
1497           }
1498           continue;
1499         }
1500 
1501         if (i > 0 && prefsw) {
1502           d_print(_("Using format '%s' instead.\n"), anames[fmts[i]]);
1503         }
1504         ret = fmts[i];
1505         break;
1506       }
1507     }
1508 
1509     if (fmts[i] == -1) {
1510       //none suitable, stick with first
1511       for (i = 0; fmts[i] != -1; i++) {
1512         // traverse video list and see if audiostreamer supports each one
1513         if (int_array_contains_value(sfmts, nfmts, fmts[i])) {
1514           ret = fmts[i];
1515           break;
1516         }
1517       }
1518     }
1519 
1520     lives_free(sfmts);
1521     lives_free(astreamer);
1522   }
1523 
1524   return ret;
1525 }
1526 
1527 
1528 ///////////////////////
1529 // encoder plugins
1530 
do_plugin_encoder_error(const char * plugin_name)1531 void do_plugin_encoder_error(const char *plugin_name) {
1532   char *msg, *tmp;
1533 
1534   if (!plugin_name) {
1535     msg = lives_strdup_printf(
1536             _("LiVES was unable to find its encoder plugins. Please make sure you have the plugins installed in\n"
1537               "%s%s%s\nor change the value of <lib_dir> in %s\n"),
1538             prefs->lib_dir, PLUGIN_EXEC_DIR, PLUGIN_ENCODERS,
1539             (tmp = lives_filename_to_utf8(prefs->configfile, -1, NULL, NULL, NULL)));
1540     lives_free(tmp);
1541     widget_opts.non_modal = TRUE;
1542     do_error_dialog(msg);
1543     lives_free(msg);
1544     return;
1545   }
1546 
1547   msg = lives_strdup_printf(
1548           _("LiVES did not receive a response from the encoder plugin called '%s'.\n"
1549             "Please make sure you have that plugin installed correctly in\n%s%s%s\n"
1550             "or switch to another plugin using Tools|Preferences|Encoding\n"),
1551           plugin_name, prefs->lib_dir, PLUGIN_EXEC_DIR, PLUGIN_ENCODERS);
1552   do_error_dialog(msg);
1553   lives_free(msg);
1554 }
1555 
1556 
check_encoder_restrictions(boolean get_extension,boolean user_audio,boolean save_all)1557 boolean check_encoder_restrictions(boolean get_extension, boolean user_audio, boolean save_all) {
1558   LiVESList *ofmt_all = NULL;
1559   char **checks;
1560   char **array = NULL;
1561   char **array2;
1562   char aspect_buffer[512];
1563 
1564   // for auto resizing/resampling
1565   double best_fps = 0.;
1566   double fps;
1567   double best_fps_delta = 0.;
1568 
1569   boolean sizer = FALSE;
1570   boolean allow_aspect_override = FALSE;
1571   boolean calc_aspect = FALSE;
1572   boolean swap_endian = FALSE;
1573 
1574   int best_arate = 0;
1575   int width, owidth;
1576   int height, oheight;
1577   int best_arate_delta = 0;
1578   int hblock = 2, vblock = 2;
1579   int i, r, val;
1580   int pieces, numtok;
1581   int best_fps_num = 0, best_fps_denom = 0;
1582   int arate, achans, asampsize, asigned = 0;
1583 
1584   if (!rdet) {
1585     width = owidth = cfile->hsize;
1586     height = oheight = cfile->vsize;
1587     fps = cfile->fps;
1588   } else {
1589     width = owidth = rdet->width;
1590     height = oheight = rdet->height;
1591     fps = rdet->fps;
1592     rdet->suggestion_followed = FALSE;
1593   }
1594 
1595   if (mainw->osc_auto) {
1596     if (mainw->osc_enc_width > 0) {
1597       width = mainw->osc_enc_width;
1598       height = mainw->osc_enc_height;
1599     }
1600     if (mainw->osc_enc_fps != 0.) fps = mainw->osc_enc_fps;
1601   }
1602 
1603   // TODO - allow lists for size
1604   lives_snprintf(prefs->encoder.of_restrict, 5, "none");
1605   if ((ofmt_all = plugin_request_by_line(PLUGIN_ENCODERS, prefs->encoder.name, "get_formats"))) {
1606     // get any restrictions for the current format
1607     for (i = 0; i < lives_list_length(ofmt_all); i++) {
1608       if ((numtok = get_token_count((char *)lives_list_nth_data(ofmt_all, i), '|')) > 2) {
1609         array = lives_strsplit((char *)lives_list_nth_data(ofmt_all, i), "|", -1);
1610         if (!strcmp(array[0], prefs->encoder.of_name)) {
1611           if (numtok > 5) {
1612             lives_snprintf(prefs->encoder.ptext, 512, "%s", array[5]);
1613           } else {
1614             lives_memset(prefs->encoder.ptext, 0, 1);
1615           }
1616           if (numtok > 4) {
1617             lives_snprintf(prefs->encoder.of_def_ext, 16, "%s", array[4]);
1618           } else {
1619             lives_memset(prefs->encoder.of_def_ext, 0, 1);
1620           }
1621           if (numtok > 3) {
1622             lives_snprintf(prefs->encoder.of_restrict, 128, "%s", array[3]);
1623           } else {
1624             lives_snprintf(prefs->encoder.of_restrict, 128, "none");
1625           }
1626           prefs->encoder.of_allowed_acodecs = atoi(array[2]);
1627           lives_list_free_all(&ofmt_all);
1628           lives_strfreev(array);
1629           break;
1630         }
1631         lives_strfreev(array);
1632       }
1633     }
1634   }
1635 
1636   if (get_extension) {
1637     return TRUE; // just wanted file extension
1638   }
1639 
1640   if (!rdet && mainw->save_with_sound && prefs->encoder.audio_codec != AUDIO_CODEC_NONE) {
1641     if (!(prefs->encoder.of_allowed_acodecs & (1 << prefs->encoder.audio_codec))) {
1642       do_encoder_acodec_error();
1643       return FALSE;
1644     }
1645   }
1646 
1647   if (user_audio && future_prefs->encoder.of_allowed_acodecs == 0) best_arate = -1;
1648 
1649   if ((!*prefs->encoder.of_restrict || !strcmp(prefs->encoder.of_restrict, "none")) && best_arate > -1) {
1650     return TRUE;
1651   }
1652 
1653   if (!rdet) {
1654     arate = cfile->arate;
1655     achans = cfile->achans;
1656     asampsize = cfile->asampsize;
1657   } else {
1658     arate = rdet->arate;
1659     achans = rdet->achans;
1660     asampsize = rdet->asamps;
1661   }
1662 
1663   // audio endianness check - what should we do for big-endian machines ?
1664   if (((mainw->save_with_sound || rdet) && (!resaudw || resaudw->aud_checkbutton ||
1665        lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->aud_checkbutton))))
1666       && prefs->encoder.audio_codec != AUDIO_CODEC_NONE && arate != 0 && achans != 0 && asampsize != 0) {
1667     if (rdet && !rdet->is_encoding) {
1668       if (mainw->endian != AFORM_BIG_ENDIAN && (lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_bigend))))
1669         swap_endian = TRUE;
1670     } else {
1671       if (mainw->endian != AFORM_BIG_ENDIAN && (cfile->signed_endian & AFORM_BIG_ENDIAN)) swap_endian = TRUE;
1672       //if (mainw->endian==AFORM_BIG_ENDIAN && (cfile->signed_endian&AFORM_BIG_ENDIAN)) swap_endian=TRUE; // needs test
1673     }
1674   }
1675 
1676   if (*prefs->encoder.of_restrict) {
1677     pieces = get_token_count(prefs->encoder.of_restrict, ',');
1678     checks = lives_strsplit(prefs->encoder.of_restrict, ",", pieces);
1679 
1680     for (r = 0; r < pieces; r++) {
1681       // check each restriction in turn
1682 
1683       if (!strncmp(checks[r], "fps=", 4)) {
1684         double allowed_fps;
1685         int mbest_num = 0, mbest_denom = 0;
1686         int numparts;
1687         char *fixer;
1688 
1689         best_fps_delta = 1000000000.;
1690         array = lives_strsplit(checks[r], "=", 2);
1691         numtok = get_token_count(array[1], ';');
1692         array2 = lives_strsplit(array[1], ";", numtok);
1693         for (i = 0; i < numtok; i++) {
1694           mbest_num = mbest_denom = 0;
1695           if ((numparts = get_token_count(array2[i], ':')) > 1) {
1696             char **array3 = lives_strsplit(array2[i], ":", 2);
1697             mbest_num = atoi(array3[0]);
1698             mbest_denom = atoi(array3[1]);
1699             lives_strfreev(array3);
1700             if (mbest_denom == 0) continue;
1701             allowed_fps = (mbest_num * 1.) / (mbest_denom * 1.);
1702           } else allowed_fps = lives_strtod(array2[i], NULL);
1703 
1704           // convert to 8dp
1705           fixer = lives_strdup_printf("%.8f %.8f", allowed_fps, fps);
1706           lives_free(fixer);
1707 
1708           if (allowed_fps >= fps) {
1709             if (allowed_fps - fps < best_fps_delta) {
1710               best_fps_delta = allowed_fps - fps;
1711               if (mbest_denom > 0) {
1712                 best_fps_num = mbest_num;
1713                 best_fps_denom = mbest_denom;
1714                 best_fps = 0.;
1715                 if (!rdet) cfile->ratio_fps = TRUE;
1716                 else rdet->ratio_fps = TRUE;
1717               } else {
1718                 best_fps_num = best_fps_denom = 0;
1719                 best_fps = allowed_fps;
1720                 if (!rdet) cfile->ratio_fps = FALSE;
1721                 else rdet->ratio_fps = FALSE;
1722               }
1723             }
1724           } else if ((best_fps_denom == 0 && allowed_fps > best_fps) || (best_fps_denom > 0
1725                      && allowed_fps > (best_fps_num * 1.) /
1726                      (best_fps_denom * 1.))) {
1727             best_fps_delta = fps - allowed_fps;
1728             if (mbest_denom > 0) {
1729               best_fps_num = mbest_num;
1730               best_fps_denom = mbest_denom;
1731               best_fps = 0.;
1732               if (!rdet) cfile->ratio_fps = TRUE;
1733               else rdet->ratio_fps = TRUE;
1734             } else {
1735               best_fps = allowed_fps;
1736               best_fps_num = best_fps_denom = 0;
1737               if (!rdet) cfile->ratio_fps = FALSE;
1738               else rdet->ratio_fps = FALSE;
1739             }
1740           }
1741           if (best_fps_delta <= prefs->fps_tolerance) {
1742             best_fps_delta = 0.;
1743             best_fps_denom = best_fps_num = 0;
1744           }
1745           if (best_fps_delta == 0.) break;
1746         }
1747         lives_strfreev(array);
1748         lives_strfreev(array2);
1749         continue;
1750       }
1751 
1752       if (!strncmp(checks[r], "size=", 5)) {
1753         // TODO - allow list for size
1754         array = lives_strsplit(checks[r], "=", 2);
1755         array2 = lives_strsplit(array[1], "x", 2);
1756         width = atoi(array2[0]);
1757         height = atoi(array2[1]);
1758         lives_strfreev(array2);
1759         lives_strfreev(array);
1760         sizer = TRUE;
1761         continue;
1762       }
1763 
1764       if (!strncmp(checks[r], "minw=", 5)) {
1765         array = lives_strsplit(checks[r], "=", 2);
1766         val = atoi(array[1]);
1767         if (width < val) width = val;
1768         lives_strfreev(array);
1769         continue;
1770       }
1771 
1772       if (!strncmp(checks[r], "minh=", 5)) {
1773         array = lives_strsplit(checks[r], "=", 2);
1774         val = atoi(array[1]);
1775         if (height < val) height = val;
1776         lives_strfreev(array);
1777         continue;
1778       }
1779 
1780       if (!strncmp(checks[r], "maxh=", 5)) {
1781         array = lives_strsplit(checks[r], "=", 2);
1782         val = atoi(array[1]);
1783         if (height > val) height = val;
1784         lives_strfreev(array);
1785         continue;
1786       }
1787 
1788       if (!strncmp(checks[r], "maxw=", 5)) {
1789         array = lives_strsplit(checks[r], "=", 2);
1790         val = atoi(array[1]);
1791         if (width > val) width = val;
1792         lives_strfreev(array);
1793         continue;
1794       }
1795 
1796       if (!strncmp(checks[r], "asigned=", 8) &&
1797           ((mainw->save_with_sound || rdet) && (!resaudw ||
1798               !resaudw->aud_checkbutton ||
1799               lives_toggle_button_get_active
1800               (LIVES_TOGGLE_BUTTON(resaudw->aud_checkbutton)))) &&
1801           prefs->encoder.audio_codec != AUDIO_CODEC_NONE
1802           && arate != 0 && achans != 0 && asampsize != 0) {
1803         array = lives_strsplit(checks[r], "=", 2);
1804         if (!strcmp(array[1], "signed")) {
1805           asigned = 1;
1806         }
1807 
1808         if (!strcmp(array[1], "unsigned")) {
1809           asigned = 2;
1810         }
1811 
1812         lives_strfreev(array);
1813 
1814         if (asigned != 0 && !capable->has_sox_sox) {
1815           do_encoder_sox_error();
1816           lives_strfreev(checks);
1817           return FALSE;
1818         }
1819         continue;
1820       }
1821 
1822       if (!strncmp(checks[r], "arate=", 6) && ((mainw->save_with_sound || rdet) && (!resaudw ||
1823           !resaudw->aud_checkbutton ||
1824           lives_toggle_button_get_active
1825           (LIVES_TOGGLE_BUTTON
1826            (resaudw->aud_checkbutton)))) &&
1827           prefs->encoder.audio_codec != AUDIO_CODEC_NONE && arate != 0 && achans != 0 && asampsize != 0) {
1828         // we only perform this test if we are encoding with audio
1829         // find next highest allowed rate from list,
1830         // if none are higher, use the highest
1831         int allowed_arate;
1832         best_arate_delta = 1000000000;
1833 
1834         array = lives_strsplit(checks[r], "=", 2);
1835         numtok = get_token_count(array[1], ';');
1836         array2 = lives_strsplit(array[1], ";", numtok);
1837         for (i = 0; i < numtok; i++) {
1838           allowed_arate = atoi(array2[i]);
1839           if (allowed_arate >= arate) {
1840             if (allowed_arate - arate < best_arate_delta) {
1841               best_arate_delta = allowed_arate - arate;
1842               best_arate = allowed_arate;
1843             }
1844           } else if (allowed_arate > best_arate) best_arate = allowed_arate;
1845         }
1846         lives_strfreev(array2);
1847         lives_strfreev(array);
1848 
1849         if (!capable->has_sox_sox) {
1850           do_encoder_sox_error();
1851           lives_strfreev(checks);
1852           return FALSE;
1853         }
1854         continue;
1855       }
1856 
1857       if (!strncmp(checks[r], "hblock=", 7)) {
1858         // width must be a multiple of this
1859         array = lives_strsplit(checks[r], "=", 2);
1860         hblock = atoi(array[1]);
1861         width = (int)(width / hblock + .5) * hblock;
1862         lives_strfreev(array);
1863         continue;
1864       }
1865 
1866       if (!strncmp(checks[r], "vblock=", 7)) {
1867         // height must be a multiple of this
1868         array = lives_strsplit(checks[r], "=", 2);
1869         vblock = atoi(array[1]);
1870         height = (int)(height / vblock + .5) * vblock;
1871         lives_strfreev(array);
1872         continue;
1873       }
1874 
1875       if (!strncmp(checks[r], "aspect=", 7)) {
1876         // we calculate the nearest smaller frame size using aspect,
1877         // hblock and vblock
1878         calc_aspect = TRUE;
1879         array = lives_strsplit(checks[r], "=", 2);
1880         lives_snprintf(aspect_buffer, 512, "%s", array[1]);
1881         lives_strfreev(array);
1882         continue;
1883       }
1884     }
1885 
1886     /// end restrictions
1887     lives_strfreev(checks);
1888 
1889     if (!mainw->osc_auto && calc_aspect && !sizer) {
1890       // we calculate this last, after getting hblock and vblock sizes
1891       char **array3;
1892       double allowed_aspect;
1893       int xwidth = width;
1894       int xheight = height;
1895 
1896       width = height = 1000000;
1897 
1898       numtok = get_token_count(aspect_buffer, ';');
1899       array2 = lives_strsplit(aspect_buffer, ";", numtok);
1900 
1901       // see if we can get a width:height which is nearer an aspect than
1902       // current width:height
1903 
1904       for (i = 0; i < numtok; i++) {
1905         array3 = lives_strsplit(array2[i], ":", 2);
1906         allowed_aspect = lives_strtod(array3[0], NULL) / lives_strtod(array3[1], NULL);
1907         lives_strfreev(array3);
1908         minimise_aspect_delta(allowed_aspect, hblock, vblock, xwidth, xheight, &width, &height);
1909       }
1910       lives_strfreev(array2);
1911 
1912       // allow override if current width and height are integer multiples of blocks
1913       if (owidth % hblock == 0 && oheight % vblock == 0) allow_aspect_override = TRUE;
1914 
1915       // end recheck
1916     }
1917 
1918     // fps can't be altered if we have a multitrack event_list
1919     if (mainw->multitrack && mainw->multitrack->event_list) best_fps_delta = 0.;
1920 
1921     if (sizer) allow_aspect_override = FALSE;
1922   }
1923 
1924   // if we have min or max size, make sure we fit within that
1925 
1926   if (((width != owidth || height != oheight) && width * height > 0) || (best_fps_delta > 0.) || (best_arate_delta > 0 &&
1927       best_arate > 0) ||
1928       best_arate < 0 || asigned != 0 || swap_endian) {
1929     boolean ofx1_bool = mainw->fx1_bool;
1930     mainw->fx1_bool = FALSE;
1931     if ((width != owidth || height != oheight) && width * height > 0) {
1932       if (!capable->has_convert && !rdet && mainw->fx_candidates[FX_CANDIDATE_RESIZER].delegate == -1) {
1933         if (allow_aspect_override) {
1934           width = owidth;
1935           height = oheight;
1936         }
1937       }
1938     }
1939     if (rdet && !rdet->is_encoding) {
1940       rdet->arate = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_arate)));
1941       rdet->achans = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_achans)));
1942       rdet->asamps = (int)atoi(lives_entry_get_text(LIVES_ENTRY(resaudw->entry_asamps)));
1943       rdet->aendian = get_signed_endian(lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_unsigned)),
1944                                         lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_littleend)));
1945 
1946       if (swap_endian || width != rdet->width || height != rdet->height || best_fps_delta != 0. || best_arate != rdet->arate ||
1947           ((asigned == 1 && (rdet->aendian & AFORM_UNSIGNED)) || (asigned == 2 && !(rdet->aendian & AFORM_SIGNED)))) {
1948 
1949         if (rdet_suggest_values(width, height, best_fps, best_fps_num, best_fps_denom, best_arate, asigned, swap_endian,
1950                                 allow_aspect_override, (best_fps_delta == 0.))) {
1951           char *arate_string;
1952           rdet->width = width;
1953           rdet->height = height;
1954           if (best_arate != -1) rdet->arate = best_arate;
1955           else lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->aud_checkbutton), FALSE);
1956 
1957           if (asigned == 1) lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->rb_signed), TRUE);
1958           else if (asigned == 2) lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->rb_unsigned), TRUE);
1959 
1960           if (swap_endian) {
1961             if (!lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(resaudw->rb_bigend)))
1962               lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->rb_bigend), TRUE);
1963             else lives_toggle_button_set_active(LIVES_TOGGLE_BUTTON(resaudw->rb_littleend), TRUE);
1964           }
1965 
1966           if (best_fps_delta > 0.) {
1967             if (best_fps_denom > 0) {
1968               rdet->fps = (best_fps_num * 1.) / (best_fps_denom * 1.);
1969             } else rdet->fps = best_fps;
1970             lives_spin_button_set_value(LIVES_SPIN_BUTTON(rdet->spinbutton_fps), rdet->fps);
1971           }
1972           lives_spin_button_set_value(LIVES_SPIN_BUTTON(rdet->spinbutton_width), rdet->width);
1973           lives_spin_button_set_value(LIVES_SPIN_BUTTON(rdet->spinbutton_height), rdet->height);
1974           if (best_arate != -1) {
1975             arate_string = lives_strdup_printf("%d", best_arate);
1976             lives_entry_set_text(LIVES_ENTRY(resaudw->entry_arate), arate_string);
1977             lives_free(arate_string);
1978           }
1979           rdet->suggestion_followed = TRUE;
1980           return TRUE;
1981         }
1982       }
1983       return FALSE;
1984     }
1985 
1986     if (mainw->osc_auto || do_encoder_restrict_dialog(width, height, best_fps, best_fps_num, best_fps_denom, best_arate,
1987         asigned, swap_endian, allow_aspect_override, save_all)) {
1988       if (!mainw->fx1_bool && mainw->osc_enc_width == 0) {
1989         width = owidth;
1990         height = oheight;
1991       }
1992 
1993       if (!auto_resample_resize(width, height, best_fps, best_fps_num, best_fps_denom, best_arate, asigned, swap_endian)) {
1994         mainw->fx1_bool = ofx1_bool;
1995         return FALSE;
1996       }
1997     } else {
1998       mainw->fx1_bool = ofx1_bool;
1999       return FALSE;
2000     }
2001   }
2002   return TRUE;
2003 }
2004 
2005 
filter_encoders_by_img_ext(LiVESList * encoders,const char * img_ext)2006 LiVESList *filter_encoders_by_img_ext(LiVESList * encoders, const char *img_ext) {
2007   LiVESList *encoder_capabilities = NULL;
2008   LiVESList *list = encoders, *listnext;
2009   int caps;
2010 
2011   register int i;
2012 
2013   char *blacklist[] = {
2014     NULL,
2015     NULL
2016   };
2017 
2018   // something broke as of python 2.7.2, and python 3 files now just hang
2019   if (capable->python_version < 3000000) blacklist[0] = lives_strdup("multi_encoder3");
2020 
2021   while (list) {
2022     boolean skip = FALSE;
2023     i = 0;
2024 
2025     listnext = list->next;
2026 
2027     while (blacklist[i]) {
2028       if (!strcmp((const char *)list->data, blacklist[i])) {
2029         // skip blacklisted encoders
2030         lives_free((livespointer)list->data);
2031         encoders = lives_list_delete_link(encoders, list);
2032         skip = TRUE;
2033         break;
2034       }
2035       i++;
2036     }
2037     if (skip) {
2038       list = listnext;
2039       continue;
2040     }
2041 
2042     if (!strcmp(img_ext, LIVES_FILE_EXT_JPG)) {
2043       list = listnext;
2044       continue;
2045     }
2046 
2047     if ((encoder_capabilities = plugin_request(PLUGIN_ENCODERS, (char *)list->data, "get_capabilities")) == NULL) {
2048       lives_free((livespointer)list->data);
2049       encoders = lives_list_delete_link(encoders, list);
2050     } else {
2051       caps = atoi((char *)lives_list_nth_data(encoder_capabilities, 0));
2052       if (!(caps & CAN_ENCODE_PNG) && !strcmp(img_ext, LIVES_FILE_EXT_PNG)) {
2053         lives_free((livespointer)list->data);
2054         encoders = lives_list_delete_link(encoders, list);
2055       }
2056 
2057       lives_list_free_all(&encoder_capabilities);
2058     }
2059 
2060     list = listnext;
2061   }
2062 
2063   for (i = 0; blacklist[i]; i++) lives_free(blacklist[i]);
2064 
2065   return encoders;
2066 }
2067 
2068 
2069 //////////////////////////////////////////////////////
2070 // decoder plugins
2071 
decoder_plugin_move_to_first(const char * name)2072 boolean decoder_plugin_move_to_first(const char *name) {
2073   LiVESList *decoder_plugin, *last_decoder_plugin = NULL;
2074   decoder_plugin = mainw->decoder_list;
2075   while (decoder_plugin) {
2076     if (!strcmp((const char *)(decoder_plugin->data), name)) {
2077       if (last_decoder_plugin) {
2078         last_decoder_plugin->next = decoder_plugin->next;
2079         decoder_plugin->next = mainw->decoder_list;
2080         mainw->decoder_list = decoder_plugin;
2081       }
2082       return TRUE;
2083     }
2084     last_decoder_plugin = decoder_plugin;
2085     decoder_plugin = decoder_plugin->next;
2086   }
2087   return FALSE;
2088 }
2089 
2090 
2091 
load_decoders(void)2092 LiVESList *load_decoders(void) {
2093   lives_decoder_sys_t *dplug;
2094   char *decplugdir = lives_strdup_printf("%s%s%s", prefs->lib_dir, PLUGIN_EXEC_DIR, PLUGIN_DECODERS);
2095   LiVESList *dlist = NULL;
2096   LiVESList *decoder_plugins_o = get_plugin_list(PLUGIN_DECODERS, TRUE, decplugdir, "-" DLL_NAME);
2097   LiVESList *decoder_plugins = decoder_plugins_o;
2098 
2099   char *blacklist[3] = {
2100     "zyavformat_decoder",
2101     "ogg_theora_decoder",
2102     NULL
2103   };
2104 
2105   const char *dplugname;
2106   boolean skip;
2107 
2108   register int i;
2109 
2110   while (decoder_plugins) {
2111     skip = FALSE;
2112     dplugname = (const char *)(decoder_plugins->data);
2113     for (i = 0; blacklist[i]; i++) {
2114       if (!strcmp(dplugname, blacklist[i])) {
2115         // skip blacklisted decoders
2116         skip = TRUE;
2117         break;
2118       }
2119     }
2120     if (!skip) {
2121       dplug = open_decoder_plugin((char *)decoder_plugins->data);
2122       if (dplug) dlist = lives_list_append(dlist, (livespointer)dplug);
2123     }
2124     lives_free((livespointer)decoder_plugins->data);
2125     decoder_plugins = decoder_plugins->next;
2126   }
2127 
2128   lives_list_free(decoder_plugins_o);
2129 
2130   if (!dlist) {
2131     char *msg = lives_strdup_printf(_("\n\nNo decoders found in %s !\n"), decplugdir);
2132     LIVES_WARN(msg);
2133     d_print(msg);
2134     lives_free(msg);
2135   }
2136 
2137   lives_free(decplugdir);
2138   return dlist;
2139 }
2140 
2141 
set_cdata_memfuncs(lives_clip_data_t * cdata)2142 static void set_cdata_memfuncs(lives_clip_data_t *cdata) {
2143   // set specific memory functions for decoder plugins to use
2144   static malloc_f  ext_malloc  = (malloc_f)  _ext_malloc;
2145   static free_f    ext_free    = (free_f)    _ext_free;
2146   static memcpy_f  ext_memcpy  = (memcpy_f)  _ext_memcpy;
2147   static memset_f  ext_memset = (memset_f)  _ext_memset;
2148   static memmove_f ext_memmove = (memmove_f) _ext_memmove;
2149   static realloc_f ext_realloc = (realloc_f) _ext_realloc;
2150   static calloc_f  ext_calloc  = (calloc_f)  _ext_calloc;
2151   if (!cdata) return;
2152   cdata->ext_malloc  = &ext_malloc;
2153   cdata->ext_free    = &ext_free;
2154   cdata->ext_memcpy  = &ext_memcpy;
2155   cdata->ext_memset  = &ext_memset;
2156   cdata->ext_memmove = &ext_memmove;
2157   cdata->ext_realloc = &ext_realloc;
2158   cdata->ext_calloc  = &ext_calloc;
2159 }
2160 
2161 
sanity_check_cdata(lives_clip_data_t * cdata)2162 static boolean sanity_check_cdata(lives_clip_data_t *cdata) {
2163   if (cdata->nframes <= 0 || cdata->nframes >= INT_MAX) {
2164     return FALSE;
2165   }
2166 
2167   // no usable palettes found
2168   if (cdata->palettes[0] == WEED_PALETTE_END) return FALSE;
2169 
2170   // all checks passed - OK
2171   return TRUE;
2172 }
2173 
2174 
2175 typedef struct {
2176   LiVESList *disabled;
2177   lives_decoder_t *dplug;
2178   lives_clip_t *sfile;
2179 } tdp_data;
2180 
clone_decoder(int fileno)2181 lives_decoder_t *clone_decoder(int fileno) {
2182   lives_decoder_t *dplug;
2183   const lives_decoder_sys_t *dpsys;
2184   lives_clip_data_t *cdata;
2185 
2186   if (!mainw->files[fileno] || !mainw->files[fileno]->ext_src) return NULL;
2187 
2188   cdata = ((lives_decoder_sys_t *)((lives_decoder_t *)mainw->files[fileno]->ext_src)->decoder)->get_clip_data
2189           (NULL, ((lives_decoder_t *)mainw->files[fileno]->ext_src)->cdata);
2190 
2191   if (!cdata) return NULL;
2192 
2193   dplug = (lives_decoder_t *)lives_calloc(1, sizeof(lives_decoder_t));
2194   dpsys = ((lives_decoder_t *)mainw->files[fileno]->ext_src)->decoder;
2195 
2196   dplug->decoder = dpsys;
2197   dplug->cdata = cdata;
2198   dplug->refs = 1;
2199   set_cdata_memfuncs((lives_clip_data_t *)cdata);
2200   cdata->rec_rowstrides = NULL;
2201   return dplug;
2202 }
2203 
2204 
try_decoder_plugins(char * file_name,LiVESList * disabled,const lives_clip_data_t * fake_cdata)2205 static lives_decoder_t *try_decoder_plugins(char *file_name, LiVESList * disabled, const lives_clip_data_t *fake_cdata) {
2206   // here we test each decoder in turn to see if it can open "file_name"
2207 
2208   // if we are reopening a clip, then fake cdata is a partially initialised cdata, but with only the frame count and fps set
2209   // this allows the decoder plugins to startup quicker as they don't have to seek to the last frame or calculate the fps.
2210 
2211   // we pass this to each decoder in turn and check what it returns. If the values look sane then we use that decoder,
2212   // otherwise we try the next one.
2213 
2214   // when reloading clips we try the decoder which last opened them first, othwerwise they could get picked up by another
2215   // decoder and the frames could come out different
2216 
2217   lives_decoder_t *dplug = (lives_decoder_t *)lives_calloc(1, sizeof(lives_decoder_t));
2218   LiVESList *decoder_plugin = mainw->decoder_list;
2219   //LiVESList *last_decoder_plugin = NULL;
2220 
2221   dplug->refs = 1;
2222 
2223   if (fake_cdata) {
2224     set_cdata_memfuncs((lives_clip_data_t *)fake_cdata);
2225     //if (prefs->vj_mode) {
2226     ((lives_clip_data_t *)fake_cdata)->seek_flag = LIVES_SEEK_FAST;
2227     //}
2228   }
2229 
2230   while (decoder_plugin) {
2231     lives_decoder_sys_t *dpsys = (lives_decoder_sys_t *)decoder_plugin->data;
2232     if (lives_list_strcmp_index(disabled, dpsys->name, FALSE) != -1) {
2233       // check if (user) disabled this decoder
2234       decoder_plugin = decoder_plugin->next;
2235       continue;
2236     }
2237 
2238     //#define DEBUG_DECPLUG
2239 #ifdef DEBUG_DECPLUG
2240     g_print("trying decoder %s\n", dpsys->name);
2241 #endif
2242 
2243     dplug->cdata = (dpsys->get_clip_data)(file_name, fake_cdata);
2244 
2245     if (dplug->cdata) {
2246       // check for sanity
2247       //g_print("Checking return data from %s\n", dpsys->name);
2248       if (lsd_check_match((lives_struct_def_t *)get_lsd(LIVES_STRUCT_CLIP_DATA_T),
2249                           &dplug->cdata->lsd)) {
2250         g_printerr("Error in cdata received from decoder plugin:\n%s\nAborting.", dpsys->name);
2251         abort();
2252       }
2253       if (!sanity_check_cdata(dplug->cdata)) {
2254         //last_decoder_plugin = decoder_plugin;
2255         decoder_plugin = decoder_plugin->next;
2256         continue;
2257       }
2258       set_cdata_memfuncs(dplug->cdata);
2259       //////////////////////
2260 
2261       dplug->decoder = dpsys;
2262 
2263       /* if (strncmp(dpsys->name, "zz", 2)) { */
2264       /* 	// if libav decoder opened us, move it to the first, since it can save time */
2265       /* 	if (last_decoder_plugin) { */
2266       /* 	  last_decoder_plugin->next = decoder_plugin->next; */
2267       /* 	  decoder_plugin->next = mainw->decoder_list; */
2268       /* 	  mainw->decoder_list = decoder_plugin; */
2269       /* } */
2270       break;
2271     }
2272     //last_decoder_plugin = decoder_plugin;
2273     decoder_plugin = decoder_plugin->next;
2274   }
2275 
2276   if (!decoder_plugin) {
2277     lives_freep((void **)&dplug);
2278   } else {
2279     dplug->cdata->rec_rowstrides = NULL;
2280   }
2281 
2282   return dplug;
2283 }
2284 
2285 
get_decoder_cdata(int fileno,LiVESList * disabled,const lives_clip_data_t * fake_cdata)2286 const lives_clip_data_t *get_decoder_cdata(int fileno, LiVESList * disabled, const lives_clip_data_t *fake_cdata) {
2287   // pass file to each decoder (demuxer) plugin in turn, until we find one that can parse
2288   // the file
2289   // NULL is returned if no decoder plugin recognises the file - then we
2290   // fall back to other methods
2291 
2292   // otherwise we return data for the clip as supplied by the decoder plugin
2293 
2294   // If the file does not exist, we set mainw->error=TRUE and return NULL
2295 
2296   // If we find a plugin we also set sfile->ext_src to point to a newly created decoder_plugin_t
2297 
2298   lives_decoder_t *dplug;
2299 
2300   LiVESList *xdisabled;
2301 
2302   lives_clip_t *sfile = mainw->files[fileno];
2303 
2304   char decplugname[PATH_MAX];
2305 
2306   mainw->error = FALSE;
2307 
2308   if (!lives_file_test(sfile->file_name, LIVES_FILE_TEST_EXISTS)) {
2309     mainw->error = TRUE;
2310     return NULL;
2311   }
2312 
2313   lives_memset(decplugname, 0, 1);
2314 
2315   // check sfile->file_name against each decoder plugin,
2316   // until we get non-NULL cdata
2317 
2318   sfile->ext_src = NULL;
2319   sfile->ext_src_type = LIVES_EXT_SRC_NONE;
2320 
2321   lives_set_cursor_style(LIVES_CURSOR_BUSY, NULL);
2322 
2323   xdisabled = lives_list_copy(disabled);
2324 
2325   if (fake_cdata) {
2326     get_clip_value(fileno, CLIP_DETAILS_DECODER_NAME, decplugname, PATH_MAX);
2327     if (*decplugname) {
2328       LiVESList *decoder_plugin = mainw->decoder_list;
2329       xdisabled = lives_list_remove(xdisabled, decplugname);
2330       while (decoder_plugin) {
2331         lives_decoder_sys_t *dpsys = (lives_decoder_sys_t *)decoder_plugin->data;
2332         if (!strcmp(dpsys->name, decplugname)) {
2333           mainw->decoder_list = lives_list_move_to_first(mainw->decoder_list, decoder_plugin);
2334           break;
2335         }
2336         decoder_plugin = decoder_plugin->next;
2337       }
2338     }
2339   }
2340 
2341   /// TODO: background thread so we can animate GUI
2342   dplug = try_decoder_plugins(fake_cdata == NULL ? sfile->file_name : NULL, xdisabled, fake_cdata);
2343 
2344   if (xdisabled) lives_list_free(xdisabled);
2345 
2346   lives_set_cursor_style(LIVES_CURSOR_NORMAL, NULL);
2347 
2348   if (dplug) {
2349     d_print(_(" using %s"), dplug->decoder->version());
2350     sfile->ext_src = dplug;
2351     sfile->ext_src_type = LIVES_EXT_SRC_DECODER;
2352     return dplug->cdata;
2353   }
2354 
2355   if (dplug) return dplug->cdata;
2356   return NULL;
2357 }
2358 
2359 
2360 // close one instance of dplug
close_decoder_plugin(lives_decoder_t * dplug)2361 void close_decoder_plugin(lives_decoder_t *dplug) {
2362   lives_clip_data_t *cdata;
2363 
2364   if (!dplug) return;
2365 
2366   dplug->refs--;
2367   if (dplug->refs) return;
2368 
2369   cdata = dplug->cdata;
2370 
2371   if (cdata) {
2372     if (cdata->rec_rowstrides) {
2373       lives_free(cdata->rec_rowstrides);
2374       cdata->rec_rowstrides = NULL;
2375     }
2376     (*dplug->decoder->clip_data_free)(cdata);
2377   }
2378   lives_free(dplug);
2379 }
2380 
2381 
close_clip_decoder(int clipno)2382 void close_clip_decoder(int clipno) {
2383   if (!IS_VALID_CLIP(clipno)) return;
2384   else {
2385     lives_clip_t *sfile = mainw->files[clipno];
2386     if (sfile->ext_src && sfile->ext_src_type == LIVES_EXT_SRC_DECODER) {
2387       char *cwd = lives_get_current_dir();
2388       char *ppath = lives_build_filename(prefs->workdir, sfile->handle, NULL);
2389       lives_chdir(ppath, FALSE);
2390       lives_free(ppath);
2391       close_decoder_plugin((lives_decoder_t *)sfile->ext_src);
2392       sfile->ext_src = NULL;
2393       sfile->ext_src_type = LIVES_EXT_SRC_NONE;
2394       lives_chdir(cwd, FALSE);
2395       lives_free(cwd);
2396     }
2397   }
2398 }
2399 
2400 
unload_decoder_plugin(lives_decoder_sys_t * dplug)2401 static void unload_decoder_plugin(lives_decoder_sys_t *dplug) {
2402   if (dplug->module_unload)(*dplug->module_unload)();
2403 
2404   lives_freep((void **)&dplug->name);
2405 
2406   dlclose(dplug->handle);
2407   lives_free(dplug);
2408 }
2409 
2410 
unload_decoder_plugins(void)2411 void unload_decoder_plugins(void) {
2412   LiVESList *dplugs = mainw->decoder_list;
2413 
2414   while (dplugs) {
2415     unload_decoder_plugin((lives_decoder_sys_t *)dplugs->data);
2416     dplugs = dplugs->next;
2417   }
2418 
2419   lives_list_free(mainw->decoder_list);
2420   mainw->decoder_list = NULL;
2421   mainw->decoders_loaded = FALSE;
2422 }
2423 
2424 
chill_decoder_plugin(int fileno)2425 boolean chill_decoder_plugin(int fileno) {
2426   lives_clip_t *sfile = mainw->files[fileno];
2427   if (IS_NORMAL_CLIP(fileno) && sfile->clip_type == CLIP_TYPE_FILE && sfile->ext_src) {
2428     lives_decoder_t *dplug = (lives_decoder_t *)sfile->ext_src;
2429     lives_decoder_sys_t *dpsys = (lives_decoder_sys_t *)dplug->decoder;
2430     lives_clip_data_t *cdata = dplug->cdata;
2431     if (cdata)
2432       if (dpsys->chill_out) return (*dpsys->chill_out)(cdata);
2433   }
2434   return FALSE;
2435 }
2436 
2437 
open_decoder_plugin(const char * plname)2438 lives_decoder_sys_t *open_decoder_plugin(const char *plname) {
2439   lives_decoder_sys_t *dplug;
2440   char *plugname, *tmp;
2441   boolean OK = TRUE;
2442   const char *err;
2443   int dlflags = RTLD_NOW | RTLD_LOCAL;
2444 
2445   dplug = (lives_decoder_sys_t *)lives_calloc(1, sizeof(lives_decoder_sys_t));
2446 
2447   dplug->name = NULL;
2448 
2449   tmp = lives_strdup_printf("%s.%s", plname, DLL_NAME);
2450   plugname = lives_build_filename(prefs->lib_dir, PLUGIN_EXEC_DIR, PLUGIN_DECODERS, tmp, NULL);
2451   lives_free(tmp);
2452 
2453 #ifdef RTLD_DEEPBIND
2454   dlflags |= RTLD_DEEPBIND;
2455 #endif
2456 
2457   dplug->handle = dlopen(plugname, dlflags);
2458   lives_free(plugname);
2459 
2460   if (!dplug->handle) {
2461     d_print(_("\n\nFailed to open decoder plugin %s\nError was %s\n"), plname, dlerror());
2462     lives_free(dplug);
2463     return NULL;
2464   }
2465 
2466   if ((dplug->version = (const char *(*)())dlsym(dplug->handle, "version")) == NULL) {
2467     OK = FALSE;
2468   }
2469   if ((dplug->get_clip_data = (lives_clip_data_t *(*)(char *, const lives_clip_data_t *))
2470                               dlsym(dplug->handle, "get_clip_data")) == NULL) {
2471     OK = FALSE;
2472   }
2473   if ((dplug->get_frame = (boolean(*)(const lives_clip_data_t *, int64_t, int *, int, void **))
2474                           dlsym(dplug->handle, "get_frame")) == NULL) {
2475     OK = FALSE;
2476   }
2477   if ((dplug->clip_data_free = (void (*)(lives_clip_data_t *))dlsym(dplug->handle, "clip_data_free")) == NULL) {
2478     OK = FALSE;
2479   }
2480 
2481   if (!OK) {
2482     d_print(_("\n\nDecoder plugin %s\nis missing a mandatory function.\nUnable to use it.\n"), plname);
2483     unload_decoder_plugin(dplug);
2484     lives_free(dplug);
2485     return NULL;
2486   }
2487 
2488   // optional
2489   dplug->module_check_init = (const char *(*)())dlsym(dplug->handle, "module_check_init");
2490   dplug->chill_out = (boolean(*)(const lives_clip_data_t *))dlsym(dplug->handle, "chill_out");
2491   dplug->set_palette = (boolean(*)(lives_clip_data_t *))dlsym(dplug->handle, "set_palette");
2492   dplug->module_unload = (void (*)())dlsym(dplug->handle, "module_unload");
2493   dplug->rip_audio = (int64_t (*)(const lives_clip_data_t *, const char *, int64_t, int64_t, unsigned char **))
2494                      dlsym(dplug->handle, "rip_audio");
2495   dplug->rip_audio_cleanup = (void (*)(const lives_clip_data_t *))dlsym(dplug->handle, "rip_audio_cleanup");
2496 
2497   if (dplug->module_check_init) {
2498     err = (*dplug->module_check_init)();
2499 
2500     if (err) {
2501       lives_snprintf(mainw->msg, MAINW_MSG_SIZE, "%s", err);
2502       unload_decoder_plugin(dplug);
2503       lives_free(dplug);
2504       return NULL;
2505     }
2506   }
2507 
2508   dplug->name = lives_strdup(plname);
2509   return dplug;
2510 }
2511 
2512 
get_mime_type(char * text,int maxlen,const lives_clip_data_t * cdata)2513 void get_mime_type(char *text, int maxlen, const lives_clip_data_t *cdata) {
2514   char *audname;
2515 
2516   if (!*cdata->container_name) lives_snprintf(text, maxlen, "%s", _("unknown"));
2517   else lives_snprintf(text, maxlen, "%s", cdata->container_name);
2518 
2519   if (!*cdata->video_name && !*cdata->audio_name) return;
2520 
2521   if (!*cdata->video_name) lives_strappend(text, maxlen, _("/unknown"));
2522   else {
2523     char *vidname = lives_strdup_printf("/%s", cdata->video_name);
2524     lives_strappend(text, maxlen, vidname);
2525     lives_free(vidname);
2526   }
2527 
2528   if (!*cdata->audio_name) {
2529     if (cfile->achans == 0) return;
2530     audname = lives_strdup_printf("/%s", _("unknown"));
2531   } else
2532     audname = lives_strdup_printf("/%s", cdata->audio_name);
2533   lives_strappend(text, maxlen, audname);
2534   lives_free(audname);
2535 }
2536 
2537 
dpa_ok_clicked(LiVESButton * button,livespointer user_data)2538 static void dpa_ok_clicked(LiVESButton * button, livespointer user_data) {
2539   lives_general_button_clicked(button, NULL);
2540 
2541   if (prefsw) {
2542     lives_window_present(LIVES_WINDOW(prefsw->prefs_dialog));
2543     lives_xwindow_raise(lives_widget_get_xwindow(prefsw->prefs_dialog));
2544     if (string_lists_differ(future_prefs->disabled_decoders, future_prefs->disabled_decoders_new))
2545       apply_button_set_enabled(NULL, NULL);
2546   }
2547 
2548   lives_list_free_all(&future_prefs->disabled_decoders);
2549 
2550   future_prefs->disabled_decoders = future_prefs->disabled_decoders_new;
2551 }
2552 
2553 
dpa_cancel_clicked(LiVESButton * button,livespointer user_data)2554 static void dpa_cancel_clicked(LiVESButton * button, livespointer user_data) {
2555   lives_general_button_clicked(button, NULL);
2556 
2557   if (prefsw) {
2558     lives_window_present(LIVES_WINDOW(prefsw->prefs_dialog));
2559     lives_xwindow_raise(lives_widget_get_xwindow(prefsw->prefs_dialog));
2560   }
2561 
2562   lives_list_free_all(&future_prefs->disabled_decoders_new);
2563 }
2564 
2565 
on_dpa_cb_toggled(LiVESToggleButton * button,const char * decname)2566 static void on_dpa_cb_toggled(LiVESToggleButton * button, const char *decname) {
2567   if (!lives_toggle_button_get_active(button))
2568     // unchecked is disabled
2569     future_prefs->disabled_decoders_new = lives_list_append(future_prefs->disabled_decoders_new, lives_strdup(decname));
2570   else
2571     future_prefs->disabled_decoders_new = lives_list_delete_string(future_prefs->disabled_decoders_new, decname);
2572 }
2573 
2574 
on_decplug_advanced_clicked(LiVESButton * button,livespointer user_data)2575 void on_decplug_advanced_clicked(LiVESButton * button, livespointer user_data) {
2576   LiVESList *decoder_plugin;
2577 
2578   LiVESWidget *hbox;
2579   LiVESWidget *vbox;
2580   LiVESWidget *checkbutton;
2581   LiVESWidget *scrolledwindow;
2582   LiVESWidget *label;
2583   LiVESWidget *dialog;
2584   LiVESWidget *dialog_vbox;
2585   LiVESWidget *cancelbutton;
2586   LiVESWidget *okbutton;
2587 
2588   char *ltext;
2589   char *decplugdir = lives_strdup_printf("%s%s%s", prefs->lib_dir, PLUGIN_EXEC_DIR, PLUGIN_DECODERS);
2590 
2591   if (!mainw->decoders_loaded) {
2592     mainw->decoder_list = load_decoders();
2593     mainw->decoders_loaded = TRUE;
2594   }
2595 
2596   decoder_plugin = mainw->decoder_list;
2597 
2598   dialog = lives_standard_dialog_new(_("Decoder Plugins"), FALSE, DEF_DIALOG_WIDTH, DEF_DIALOG_HEIGHT);
2599 
2600   dialog_vbox = lives_dialog_get_content_area(LIVES_DIALOG(dialog));
2601 
2602   vbox = lives_vbox_new(FALSE, 0);
2603 
2604   scrolledwindow = lives_standard_scrolled_window_new(RFX_WINSIZE_H, RFX_WINSIZE_V, vbox);
2605 
2606   lives_container_add(LIVES_CONTAINER(dialog_vbox), scrolledwindow);
2607 
2608   label = lives_standard_label_new(_("Enabled Video Decoders (uncheck to disable)"));
2609   lives_box_pack_start(LIVES_BOX(vbox), label, FALSE, FALSE, widget_opts.packing_height);
2610 
2611   while (decoder_plugin) {
2612     lives_decoder_sys_t *dpsys = (lives_decoder_sys_t *)decoder_plugin->data;
2613     hbox = lives_hbox_new(FALSE, 0);
2614     lives_box_pack_start(LIVES_BOX(vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
2615     ltext = lives_strdup_printf("%s   (%s)", dpsys->name, (*dpsys->version)());
2616 
2617     widget_opts.mnemonic_label = FALSE;
2618     checkbutton = lives_standard_check_button_new(ltext, lives_list_strcmp_index(future_prefs->disabled_decoders, dpsys->name,
2619                   FALSE) == -1,
2620                   LIVES_BOX(hbox), NULL);
2621     widget_opts.mnemonic_label = TRUE;
2622 
2623     lives_free(ltext);
2624 
2625     lives_signal_sync_connect_after(LIVES_GUI_OBJECT(checkbutton), LIVES_WIDGET_TOGGLED_SIGNAL,
2626                                     LIVES_GUI_CALLBACK(on_dpa_cb_toggled),
2627                                     (livespointer)dpsys->name);
2628 
2629     decoder_plugin = decoder_plugin->next;
2630   }
2631 
2632   cancelbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_CANCEL, NULL,
2633                  LIVES_RESPONSE_CANCEL);
2634 
2635   okbutton = lives_dialog_add_button_from_stock(LIVES_DIALOG(dialog), LIVES_STOCK_OK, NULL,
2636              LIVES_RESPONSE_OK);
2637 
2638   lives_button_grab_default_special(okbutton);
2639 
2640   lives_signal_connect(LIVES_GUI_OBJECT(cancelbutton), LIVES_WIDGET_CLICKED_SIGNAL,
2641                        LIVES_GUI_CALLBACK(dpa_cancel_clicked),
2642                        NULL);
2643 
2644   lives_signal_connect(LIVES_GUI_OBJECT(okbutton), LIVES_WIDGET_CLICKED_SIGNAL,
2645                        LIVES_GUI_CALLBACK(dpa_ok_clicked),
2646                        NULL);
2647 
2648   lives_widget_show_all(dialog);
2649   lives_window_present(LIVES_WINDOW(dialog));
2650   lives_xwindow_raise(lives_widget_get_xwindow(dialog));
2651 
2652   future_prefs->disabled_decoders_new = lives_list_copy_strings(future_prefs->disabled_decoders);
2653 
2654   lives_free(decplugdir);
2655 }
2656 
2657 ///////////////////////////////////////////////////////
2658 // rfx plugin functions
2659 
2660 
check_rfx_for_lives(lives_rfx_t * rfx)2661 boolean check_rfx_for_lives(lives_rfx_t *rfx) {
2662   // check that an RFX is suitable for loading (cf. check_for_lives in effects-weed.c)
2663   if (rfx->num_in_channels == 2 && rfx->props & RFX_PROPS_MAY_RESIZE) {
2664     d_print(_("Failed to load %s, transitions may not resize.\n"), rfx->name);
2665     return FALSE;
2666   }
2667   return TRUE;
2668 }
2669 
2670 
do_rfx_cleanup(lives_rfx_t * rfx)2671 void do_rfx_cleanup(lives_rfx_t *rfx) {
2672   char *com;
2673   char *dir = NULL;
2674 
2675   /// skip cleanup if menuentry is "apply current realtime effects"
2676   if (rfx == &mainw->rendered_fx[0]) return;
2677 
2678   switch (rfx->status) {
2679   case RFX_STATUS_BUILTIN:
2680     dir = lives_build_filename(prefs->lib_dir, PLUGIN_EXEC_DIR, NULL);
2681     com = lives_strdup_printf("%s plugin_clear \"%s\" %d %d \"%s\" \"%s\" \"%s\"", prefs->backend_sync,
2682                               cfile->handle, cfile->start, cfile->end, dir,
2683                               PLUGIN_RENDERED_EFFECTS_BUILTIN, rfx->name);
2684     break;
2685   case RFX_STATUS_CUSTOM:
2686     com = lives_strdup_printf("%s plugin_clear \"%s\" %d %d \"%s\" \"%s\" \"%s\"", prefs->backend_sync,
2687                               cfile->handle, cfile->start, cfile->end, prefs->config_datadir,
2688                               PLUGIN_RENDERED_EFFECTS_CUSTOM, rfx->name);
2689     break;
2690   case RFX_STATUS_TEST:
2691     com = lives_strdup_printf("%s plugin_clear \"%s\" %d %d \"%s\" \"%s\" \"%s\"", prefs->backend_sync,
2692                               cfile->handle, cfile->start, cfile->end, prefs->config_datadir,
2693                               PLUGIN_RENDERED_EFFECTS_TEST, rfx->name);
2694     break;
2695   default:
2696     return;
2697   }
2698 
2699   lives_freep((void **)&dir);
2700 
2701   // if the command fails we just give a warning
2702   lives_system(com, FALSE);
2703   lives_free(com);
2704 }
2705 
2706 
render_fx_get_params(lives_rfx_t * rfx,const char * plugin_name,short status)2707 void render_fx_get_params(lives_rfx_t *rfx, const char *plugin_name, short status) {
2708   // create lives_param_t array from plugin supplied values
2709   LiVESList *parameter_list, *list;
2710   int param_idx, i;
2711   lives_param_t *cparam;
2712   char **param_array;
2713   char *line;
2714   int len;
2715 
2716   switch (status) {
2717   case RFX_STATUS_BUILTIN:
2718     parameter_list = plugin_request_by_line(PLUGIN_RENDERED_EFFECTS_BUILTIN, plugin_name, "get_parameters");
2719     break;
2720   case RFX_STATUS_CUSTOM:
2721     parameter_list = plugin_request_by_line(PLUGIN_RENDERED_EFFECTS_CUSTOM, plugin_name, "get_parameters");
2722     break;
2723   case RFX_STATUS_SCRAP:
2724     parameter_list = plugin_request_by_line(PLUGIN_RFX_SCRAP, plugin_name, "get_parameters");
2725     break;
2726   case RFX_STATUS_INTERNAL:
2727     parameter_list = plugin_request_by_line(PLUGIN_RFX_SCRAP, plugin_name, "get_parameters");
2728     break;
2729   default:
2730     parameter_list = plugin_request_by_line(PLUGIN_RENDERED_EFFECTS_TEST, plugin_name, "get_parameters");
2731     break;
2732   }
2733 
2734   if (!parameter_list) {
2735     rfx->num_params = 0;
2736     rfx->params = NULL;
2737     return;
2738   }
2739 
2740   //threaded_dialog_spin(0.);
2741   rfx->num_params = lives_list_length(parameter_list);
2742   rfx->params = (lives_param_t *)lives_calloc(rfx->num_params, sizeof(lives_param_t));
2743   list = parameter_list;
2744 
2745   for (param_idx = 0; param_idx < rfx->num_params; param_idx++) {
2746     line = (char *)list->data;
2747     list = list->next;
2748     len = get_token_count(line, (unsigned int)rfx->delim[0]);
2749 
2750     if (len < 3) continue;
2751 
2752     param_array = lives_strsplit(line, rfx->delim, -1);
2753 
2754     cparam = &rfx->params[param_idx];
2755     cparam->name = lives_strdup(param_array[0]);
2756     cparam->label = lives_strdup(param_array[1]);
2757     cparam->desc = NULL;
2758     cparam->use_mnemonic = TRUE;
2759     cparam->hidden = 0;
2760     cparam->wrap = FALSE;
2761     cparam->transition = FALSE;
2762     cparam->step_size = 1.;
2763     cparam->group = 0;
2764     cparam->max = 0.;
2765     cparam->reinit = FALSE;
2766     cparam->changed = FALSE;
2767     cparam->edited = FALSE;
2768     cparam->change_blocked = FALSE;
2769     cparam->source = NULL;
2770     cparam->source_type = LIVES_RFX_SOURCE_RFX;
2771     cparam->special_type = LIVES_PARAM_SPECIAL_TYPE_NONE;
2772     cparam->special_type_index = 0;
2773     cparam->def = NULL;
2774     cparam->value = NULL;
2775 
2776 #ifdef DEBUG_RENDER_FX_P
2777     lives_printerr("Got parameter %s\n", cparam->name);
2778 #endif
2779     cparam->dp = 0;
2780     cparam->list = NULL;
2781 
2782     cparam->type = LIVES_PARAM_UNKNOWN;
2783 
2784     if (!strncmp(param_array[2], "num", 3)) {
2785       cparam->dp = atoi(param_array[2] + 3);
2786       cparam->type = LIVES_PARAM_NUM;
2787     } else if (!strncmp(param_array[2], "bool", 4)) {
2788       cparam->type = LIVES_PARAM_BOOL;
2789     } else if (!strncmp(param_array[2], "colRGB24", 8)) {
2790       cparam->type = LIVES_PARAM_COLRGB24;
2791     } else if (!strncmp(param_array[2], "string", 8)) {
2792       cparam->type = LIVES_PARAM_STRING;
2793     } else if (!strncmp(param_array[2], "string_list", 8)) {
2794       cparam->type = LIVES_PARAM_STRING_LIST;
2795     } else continue;
2796 
2797     if (cparam->dp) {
2798       double val;
2799       if (len < 6) continue;
2800       val = lives_strtod(param_array[3], NULL);
2801       cparam->value = lives_malloc(sizdbl);
2802       cparam->def = lives_malloc(sizdbl);
2803       set_double_param(cparam->def, val);
2804       set_double_param(cparam->value, val);
2805       cparam->min = lives_strtod(param_array[4], NULL);
2806       cparam->max = lives_strtod(param_array[5], NULL);
2807       if (len > 6) {
2808         cparam->step_size = lives_strtod(param_array[6], NULL);
2809         if (cparam->step_size == 0.) cparam->step_size = 1. / (double)lives_10pow(cparam->dp);
2810         else if (cparam->step_size < 0.) {
2811           cparam->step_size = -cparam->step_size;
2812           cparam->wrap = TRUE;
2813         }
2814       }
2815     } else if (cparam->type == LIVES_PARAM_COLRGB24) {
2816       short red;
2817       short green;
2818       short blue;
2819       if (len < 6) continue;
2820       red = (short)atoi(param_array[3]);
2821       green = (short)atoi(param_array[4]);
2822       blue = (short)atoi(param_array[5]);
2823       cparam->value = lives_malloc(sizeof(lives_colRGB48_t));
2824       cparam->def = lives_malloc(sizeof(lives_colRGB48_t));
2825       set_colRGB24_param(cparam->def, red, green, blue);
2826       set_colRGB24_param(cparam->value, red, green, blue);
2827     } else if (cparam->type == LIVES_PARAM_STRING) {
2828       if (len < 4) continue;
2829       cparam->value = (_(param_array[3]));
2830       cparam->def = (_(param_array[3]));
2831       if (len > 4) cparam->max = (double)atoi(param_array[4]);
2832       if (cparam->max == 0. || cparam->max > RFX_MAXSTRINGLEN) cparam->max = RFX_MAXSTRINGLEN;
2833     } else if (cparam->type == LIVES_PARAM_STRING_LIST) {
2834       if (len < 4) continue;
2835       cparam->value = lives_malloc(sizint);
2836       cparam->def = lives_malloc(sizint);
2837       *(int *)cparam->def = atoi(param_array[3]);
2838       if (len > 4) {
2839         cparam->list = array_to_string_list(param_array, 3, len);
2840       } else {
2841         set_int_param(cparam->def, 0);
2842       }
2843       set_int_param(cparam->value, get_int_param(cparam->def));
2844     } else {
2845       // int or bool
2846       int val;
2847       if (len < 4) continue;
2848       val = atoi(param_array[3]);
2849       cparam->value = lives_malloc(sizint);
2850       cparam->def = lives_malloc(sizint);
2851       set_int_param(cparam->def, val);
2852       set_int_param(cparam->value, val);
2853       if (cparam->type == LIVES_PARAM_BOOL) {
2854         cparam->min = 0;
2855         cparam->max = 1;
2856         if (len > 4) cparam->group = atoi(param_array[4]);
2857       } else {
2858         if (len < 6) continue;
2859         cparam->min = (double)atoi(param_array[4]);
2860         cparam->max = (double)atoi(param_array[5]);
2861         if (len > 6) {
2862           cparam->step_size = (double)atoi(param_array[6]);
2863           if (cparam->step_size == 0.) cparam->step_size = 1.;
2864           else if (cparam->step_size < 0.) {
2865             cparam->step_size = -cparam->step_size;
2866             cparam->wrap = TRUE;
2867           }
2868         }
2869       }
2870     }
2871 
2872     for (i = 0; i < MAX_PARAM_WIDGETS; i++) {
2873       cparam->widgets[i] = NULL;
2874     }
2875     cparam->onchange = FALSE;
2876     lives_strfreev(param_array);
2877   }
2878   lives_list_free_all(&parameter_list);
2879   //threaded_dialog_spin(0.);
2880 }
2881 
2882 
array_to_string_list(char ** array,int offset,int len)2883 LiVESList *array_to_string_list(char **array, int offset, int len) {
2884   // build a LiVESList from an array.
2885   int i;
2886 
2887   char *string, *tmp;
2888   LiVESList *slist = NULL;
2889 
2890   for (i = offset + 1; i < len; i++) {
2891     string = subst((tmp = L2U8(array[i])), "\\n", "\n");
2892     lives_free(tmp);
2893 
2894     // omit a last empty string
2895     if (i < len - 1 || *string) {
2896       slist = lives_list_append(slist, string);
2897     } else lives_free(string);
2898   }
2899 
2900   return slist;
2901 }
2902 
2903 
cmp_menu_entries(livesconstpointer a,livesconstpointer b)2904 static int cmp_menu_entries(livesconstpointer a, livesconstpointer b) {
2905   return lives_utf8_strcmpfunc(((lives_rfx_t *)a)->menu_text, ((lives_rfx_t *)b)->menu_text, LIVES_INT_TO_POINTER(TRUE));
2906 }
2907 
2908 
sort_rfx_array(lives_rfx_t * in,int num)2909 void sort_rfx_array(lives_rfx_t *in, int num) {
2910   // sort rfx array into UTF-8 order by menu entry
2911   lives_rfx_t *rfx;
2912   int sorted = 1;
2913   register int i;
2914 
2915   LiVESList *rfx_list = NULL, *xrfx_list;
2916   for (i = num; i > 0; i--) {
2917     rfx_list = lives_list_prepend(rfx_list, (livespointer)&in[i]);
2918   }
2919   rfx_list = lives_list_sort(rfx_list, cmp_menu_entries);
2920   rfx = mainw->rendered_fx = (lives_rfx_t *)lives_calloc((num + 1), sizeof(lives_rfx_t));
2921   rfx_copy(rfx, in, FALSE);
2922   xrfx_list = rfx_list;
2923   while (xrfx_list) {
2924     rfx_copy(&mainw->rendered_fx[sorted++], (lives_rfx_t *)(xrfx_list->data), FALSE);
2925     xrfx_list = xrfx_list->next;
2926   }
2927   lives_list_free(rfx_list);
2928 }
2929 
2930 
rfx_copy(lives_rfx_t * dest,lives_rfx_t * src,boolean full)2931 void rfx_copy(lives_rfx_t *dest, lives_rfx_t *src, boolean full) {
2932   // Warning, does not copy all fields (full will do that)
2933   lives_memcpy(dest->delim, src->delim, 2);
2934   dest->source = src->source;
2935   if (!full) {
2936     // ref. assigned memory
2937     src->source = NULL;
2938     dest->name = src->name;
2939     src->name = NULL;
2940     dest->menu_text = src->menu_text;
2941     src->menu_text = NULL;
2942     dest->action_desc = src->action_desc;
2943     src->action_desc = NULL;
2944     dest->params = src->params;
2945     src->params = NULL;
2946   } else {
2947     // deep copy
2948     if (dest->source_type == LIVES_RFX_SOURCE_WEED && dest->source) weed_instance_ref(dest->source);
2949     dest->name = lives_strdup(src->name);
2950     dest->menu_text = lives_strdup(src->menu_text);
2951     dest->action_desc = lives_strdup(src->action_desc);
2952     // TODO - copy params
2953   }
2954 
2955   lives_snprintf(dest->rfx_version, 64, "%s", src->rfx_version);
2956   dest->min_frames = src->min_frames;
2957   dest->num_in_channels = src->num_in_channels;
2958   dest->status = src->status;
2959   dest->props = src->props;
2960   dest->source_type = src->source_type;
2961   dest->num_params = src->num_params;
2962   dest->is_template = src->is_template;
2963   dest->menuitem = src->menuitem;
2964   dest->gui_strings = lives_list_copy(src->gui_strings);
2965   dest->onchange_strings = lives_list_copy(src->onchange_strings);
2966   if (!full) return;
2967 
2968   // TODO
2969 
2970 }
2971 
2972 
rfx_params_free(lives_rfx_t * rfx)2973 void rfx_params_free(lives_rfx_t *rfx) {
2974   register int i;
2975   for (i = 0; i < rfx->num_params; i++) {
2976     if (rfx->params[i].type == LIVES_PARAM_UNDISPLAYABLE || rfx->params[i].type == LIVES_PARAM_UNKNOWN) continue;
2977     lives_free(rfx->params[i].name);
2978     lives_freep((void **)&rfx->params[i].def);
2979     lives_freep((void **)&rfx->params[i].value);
2980     lives_freep((void **)&rfx->params[i].label);
2981     lives_freep((void **)&rfx->params[i].desc);
2982     lives_list_free_all(&rfx->params[i].list);
2983   }
2984 }
2985 
2986 
rfx_free(lives_rfx_t * rfx)2987 void rfx_free(lives_rfx_t *rfx) {
2988   if (!rfx) return;
2989 
2990   if (mainw->vrfx_update == rfx) mainw->vrfx_update = NULL;
2991 
2992   lives_freep((void **)&rfx->name);
2993   lives_freep((void **)&rfx->menu_text);
2994   lives_freep((void **)&rfx->action_desc);
2995 
2996   if (rfx->params) {
2997     rfx_params_free(rfx);
2998     lives_free(rfx->params);
2999   }
3000 
3001   if (rfx->gui_strings) lives_list_free_all(&rfx->gui_strings);
3002   if (rfx->onchange_strings) lives_list_free_all(&rfx->onchange_strings);
3003 
3004   if (rfx->source_type == LIVES_RFX_SOURCE_WEED && rfx->source) {
3005     weed_instance_unref((weed_plant_t *)rfx->source); // remove the ref we held
3006   }
3007 }
3008 
3009 
rfx_free_all(void)3010 void rfx_free_all(void) {
3011   register int i;
3012   for (i = 0; i <= mainw->num_rendered_effects_builtin + mainw->num_rendered_effects_custom
3013        + mainw->num_rendered_effects_test; i++) {
3014     rfx_free(&mainw->rendered_fx[i]);
3015   }
3016   lives_freep((void **)&mainw->rendered_fx);
3017 }
3018 
3019 
param_copy(lives_param_t * dest,lives_param_t * src,boolean full)3020 void param_copy(lives_param_t *dest, lives_param_t *src, boolean full) {
3021   // rfxbuilder.c uses this to copy params to a temporary copy and back again
3022 
3023   dest->name = lives_strdup(src->name);
3024   dest->label = lives_strdup(src->label);
3025   dest->group = src->group;
3026   dest->onchange = src->onchange;
3027   dest->type = src->type;
3028   dest->dp = src->dp;
3029   dest->min = src->min;
3030   dest->max = src->max;
3031   dest->step_size = src->step_size;
3032   dest->wrap = src->wrap;
3033   dest->source = src->source;
3034   dest->reinit = src->reinit;
3035   dest->source_type = src->source_type;
3036   dest->value = dest->def = NULL;
3037   dest->list = NULL;
3038 
3039   switch (dest->type) {
3040   case LIVES_PARAM_BOOL:
3041     dest->dp = 0;
3042   case LIVES_PARAM_NUM:
3043     if (!dest->dp) {
3044       dest->def = lives_malloc(sizint);
3045       lives_memcpy(dest->def, src->def, sizint);
3046     } else {
3047       dest->def = lives_malloc(sizdbl);
3048       lives_memcpy(dest->def, src->def, sizdbl);
3049     }
3050     break;
3051   case LIVES_PARAM_COLRGB24:
3052     dest->def = lives_malloc(sizeof(lives_colRGB48_t));
3053     lives_memcpy(dest->def, src->def, sizeof(lives_colRGB48_t));
3054     break;
3055   case LIVES_PARAM_STRING:
3056     dest->def = lives_strdup((char *)src->def);
3057     break;
3058   case LIVES_PARAM_STRING_LIST:
3059     dest->def = lives_malloc(sizint);
3060     set_int_param(dest->def, get_int_param(src->def));
3061     if (src->list) dest->list = lives_list_copy(src->list);
3062     break;
3063   default:
3064     break;
3065   }
3066   if (!full) return;
3067   // TODO - copy value, copy widgets
3068 
3069 }
3070 
3071 
get_bool_param(void * value)3072 boolean get_bool_param(void *value) {
3073   boolean ret;
3074   lives_memcpy(&ret, value, 4);
3075   return ret;
3076 }
3077 
3078 
get_int_param(void * value)3079 int get_int_param(void *value) {
3080   int ret;
3081   lives_memcpy(&ret, value, sizint);
3082   return ret;
3083 }
3084 
3085 
get_double_param(void * value)3086 double get_double_param(void *value) {
3087   double ret;
3088   lives_memcpy(&ret, value, sizdbl);
3089   return ret;
3090 }
3091 
3092 
get_colRGB24_param(void * value,lives_colRGB48_t * rgb)3093 void get_colRGB24_param(void *value, lives_colRGB48_t *rgb) {
3094   lives_memcpy(rgb, value, sizeof(lives_colRGB48_t));
3095 }
3096 
3097 
get_colRGBA32_param(void * value,lives_colRGBA64_t * rgba)3098 void get_colRGBA32_param(void *value, lives_colRGBA64_t *rgba) {
3099   lives_memcpy(rgba, value, sizeof(lives_colRGBA64_t));
3100 }
3101 
3102 
set_bool_param(void * value,boolean _const)3103 void set_bool_param(void *value, boolean _const) {
3104   set_int_param(value, !!_const);
3105 }
3106 
3107 
set_string_param(void ** value_ptr,const char * _const,size_t maxlen)3108 void set_string_param(void **value_ptr, const char *_const, size_t maxlen) {
3109   lives_freep(value_ptr);
3110   *value_ptr = lives_strndup(_const, maxlen);
3111 }
3112 
3113 
set_int_param(void * value,int _const)3114 void set_int_param(void *value, int _const) {
3115   lives_memcpy(value, &_const, sizint);
3116 }
3117 
3118 
set_double_param(void * value,double _const)3119 void set_double_param(void *value, double _const) {
3120   lives_memcpy(value, &_const, sizdbl);
3121 }
3122 
3123 
set_rfx_param_by_name_string(lives_rfx_t * rfx,const char * name,const char * value,boolean update_visual)3124 boolean set_rfx_param_by_name_string(lives_rfx_t *rfx, const char *name, const char *value, boolean update_visual) {
3125   size_t len = strlen(value);
3126   lives_param_t *param = find_rfx_param_by_name(rfx, name);
3127   if (!param) return FALSE;
3128   set_string_param((void **)&param->value, value, len > RFX_MAXSTRINGLEN ? RFX_MAXSTRINGLEN : len);
3129   if (update_visual) {
3130     LiVESList *list = NULL;
3131     char *tmp, *tmp2;
3132     list = lives_list_append(list, lives_strdup_printf("\"%s\"", (tmp = U82L(tmp2 = subst(value, "\"", "\\\"")))));
3133     lives_free(tmp); lives_free(tmp2);
3134     set_param_from_list(list, param, 0, FALSE, TRUE);
3135     lives_list_free_all(&list);
3136   }
3137   return TRUE;
3138 }
3139 
3140 
get_rfx_param_by_name_string(lives_rfx_t * rfx,const char * name,char ** return_value)3141 boolean get_rfx_param_by_name_string(lives_rfx_t *rfx, const char *name, char **return_value) {
3142   lives_param_t *param = find_rfx_param_by_name(rfx, name);
3143   if (!param) return FALSE;
3144   *return_value = lives_strndup(param->value, RFX_MAXSTRINGLEN);
3145   return TRUE;
3146 }
3147 
3148 
set_colRGB24_param(void * value,short red,short green,short blue)3149 void set_colRGB24_param(void *value, short red, short green, short blue) {
3150   lives_colRGB48_t *rgbp = (lives_colRGB48_t *)value;
3151 
3152   if (red < 0) red = 0;
3153   if (red > 255) red = 255;
3154   if (green < 0) green = 0;
3155   if (green > 255) green = 255;
3156   if (blue < 0) blue = 0;
3157   if (blue > 255) blue = 255;
3158 
3159   rgbp->red = red;
3160   rgbp->green = green;
3161   rgbp->blue = blue;
3162 
3163 }
3164 
3165 
set_colRGBA32_param(void * value,short red,short green,short blue,short alpha)3166 void set_colRGBA32_param(void *value, short red, short green, short blue, short alpha) {
3167   lives_colRGBA64_t *rgbap = (lives_colRGBA64_t *)value;
3168   rgbap->red = red;
3169   rgbap->green = green;
3170   rgbap->blue = blue;
3171   rgbap->alpha = alpha;
3172 }
3173 
3174 
3175 ///////////////////////////////////////////////////////////////
3176 
find_rfx_plugin_by_name(const char * name,short status)3177 int find_rfx_plugin_by_name(const char *name, short status) {
3178   int i;
3179   for (i = 1; i < mainw->num_rendered_effects_builtin + mainw->num_rendered_effects_custom +
3180        mainw->num_rendered_effects_test; i++) {
3181     if (mainw->rendered_fx[i].name && !strcmp(mainw->rendered_fx[i].name, name)
3182         && mainw->rendered_fx[i].status == status)
3183       return (int)i;
3184   }
3185   return -1;
3186 }
3187 
3188 
find_rfx_param_by_name(lives_rfx_t * rfx,const char * name)3189 lives_param_t *find_rfx_param_by_name(lives_rfx_t *rfx, const char *name) {
3190   int i;
3191   if (!rfx) return NULL;
3192   for (i = 0; i < rfx->num_params; i++) {
3193     if (!strcmp(name, rfx->params[i].name)) {
3194       return &rfx->params[i];
3195     }
3196   }
3197   return NULL;
3198 }
3199 
3200 
weed_params_to_rfx(int npar,weed_plant_t * inst,boolean show_reinits)3201 lives_param_t *weed_params_to_rfx(int npar, weed_plant_t *inst, boolean show_reinits) {
3202   int i, j;
3203   lives_param_t *rpar = (lives_param_t *)lives_calloc(npar, sizeof(lives_param_t));
3204   int param_type;
3205   char **list;
3206   LiVESList *gtk_list = NULL;
3207   char *string;
3208   int vali;
3209   double vald;
3210   weed_plant_t *gui = NULL;
3211   int listlen;
3212   int cspace, *cols = NULL, red_min = 0, red_max = 255, green_min = 0, green_max = 255, blue_min = 0, blue_max = 255,
3213                *maxi = NULL, *mini = NULL;
3214   double *colsd;
3215   double red_mind = 0., red_maxd = 1., green_mind = 0., green_maxd = 1., blue_mind = 0., blue_maxd = 1.,
3216          *maxd = NULL, *mind = NULL;
3217   int flags = 0;
3218   int nwpars = 0, poffset = 0;
3219   boolean col_int;
3220 
3221   weed_plant_t *wtmpl;
3222   weed_plant_t **wpars = NULL, *wpar = NULL;
3223 
3224   weed_plant_t *chann, *ctmpl;
3225   weed_plant_t *filter = weed_instance_get_filter(inst, TRUE);
3226 
3227   wpars = weed_instance_get_in_params(inst, &nwpars);
3228 
3229   for (i = 0; i < npar; i++) {
3230     if (i - poffset >= nwpars) {
3231       // handling for compound fx
3232       poffset += nwpars;
3233       if (wpars) lives_free(wpars);
3234       inst = weed_get_plantptr_value(inst, WEED_LEAF_HOST_NEXT_INSTANCE, NULL);
3235       wpars = weed_instance_get_in_params(inst, &nwpars);
3236       i--;
3237       continue;
3238     }
3239 
3240     if (!wpars) {
3241       lives_free(rpar);
3242       return NULL;
3243     }
3244 
3245     wpar = wpars[i - poffset];
3246     wtmpl = weed_param_get_template(wpar);
3247 
3248     flags = weed_paramtmpl_get_flags(wtmpl);
3249 
3250     rpar[i].flags = flags;
3251 
3252     gui = weed_paramtmpl_get_gui(wtmpl, FALSE);
3253 
3254     rpar[i].group = 0;
3255 
3256     rpar[i].use_mnemonic = FALSE;
3257     rpar[i].hidden = 0;
3258     rpar[i].step_size = 1.;
3259     if (enabled_in_channels(filter, FALSE) == 2 && get_transition_param(filter, FALSE) == i) rpar[i].transition = TRUE;
3260     else rpar[i].transition = FALSE;
3261     rpar[i].wrap = FALSE;
3262     rpar[i].reinit = FALSE;
3263     rpar[i].change_blocked = FALSE;
3264     rpar[i].source = wtmpl;
3265     rpar[i].source_type = LIVES_RFX_SOURCE_WEED;
3266     rpar[i].special_type = LIVES_PARAM_SPECIAL_TYPE_NONE;
3267     rpar[i].special_type_index = 0;
3268     rpar[i].value = NULL;
3269     rpar[i].def = NULL;
3270 
3271     if (flags & WEED_PARAMETER_VARIABLE_SIZE && !(flags & WEED_PARAMETER_VALUE_PER_CHANNEL)) {
3272       rpar[i].hidden |= HIDDEN_MULTI;
3273       rpar[i].multi = PVAL_MULTI_ANY;
3274     } else if (flags & WEED_PARAMETER_VALUE_PER_CHANNEL) {
3275       rpar[i].hidden |= HIDDEN_MULTI;
3276       rpar[i].multi = PVAL_MULTI_PER_CHANNEL;
3277     } else rpar[i].multi = PVAL_MULTI_NONE;
3278 
3279     chann = get_enabled_channel(inst, 0, TRUE);
3280     ctmpl = weed_get_plantptr_value(chann, WEED_LEAF_TEMPLATE, NULL);
3281 
3282     if (weed_get_boolean_value(ctmpl, WEED_LEAF_IS_AUDIO, NULL) == WEED_TRUE) {
3283       // dont hide multivalued params for audio effects
3284       rpar[i].hidden = 0;
3285     }
3286 
3287     rpar[i].dp = 0;
3288     rpar[i].min = 0.;
3289     rpar[i].max = 0.;
3290     rpar[i].list = NULL;
3291 
3292     rpar[i].reinit = 0;
3293     if (flags & WEED_PARAMETER_REINIT_ON_VALUE_CHANGE)
3294       rpar[i].reinit = REINIT_FUNCTIONAL;
3295     if (gui && (weed_gui_get_flags(gui) & WEED_GUI_REINIT_ON_VALUE_CHANGE))
3296       rpar[i].reinit |= REINIT_VISUAL;
3297 
3298     if (!show_reinits && rpar[i].reinit != 0) rpar[i].hidden |= HIDDEN_NEEDS_REINIT;
3299 
3300     ///////////////////////////////
3301     param_type = weed_paramtmpl_get_type(wtmpl);
3302 
3303     switch (param_type) {
3304     case WEED_PARAM_SWITCH:
3305       if (weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 1) {
3306         rpar[i].hidden |= HIDDEN_MULTI;
3307       }
3308       rpar[i].type = LIVES_PARAM_BOOL;
3309       rpar[i].value = lives_malloc(sizint);
3310       rpar[i].def = lives_malloc(sizint);
3311       if (!weed_paramtmpl_value_irrelevant(wtmpl) && weed_plant_has_leaf(wtmpl, WEED_LEAF_HOST_DEFAULT))
3312         vali = weed_get_boolean_value(wtmpl, WEED_LEAF_HOST_DEFAULT, NULL);
3313       else if (weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 0)
3314         vali = weed_get_boolean_value(wtmpl, WEED_LEAF_DEFAULT, NULL);
3315       else vali = weed_get_boolean_value(wtmpl, WEED_LEAF_NEW_DEFAULT, NULL);
3316       set_int_param(rpar[i].def, vali);
3317       vali = weed_get_boolean_value(wpar, WEED_LEAF_VALUE, NULL);
3318       set_int_param(rpar[i].value, vali);
3319       if (weed_plant_has_leaf(wtmpl, WEED_LEAF_GROUP)) rpar[i].group = weed_get_int_value(wtmpl, WEED_LEAF_GROUP, NULL);
3320       break;
3321     case WEED_PARAM_INTEGER:
3322       if (weed_plant_has_leaf(wtmpl, WEED_LEAF_DEFAULT) && weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 1) {
3323         rpar[i].hidden |= HIDDEN_MULTI;
3324       }
3325       rpar[i].type = LIVES_PARAM_NUM;
3326       rpar[i].value = lives_malloc(sizint);
3327       rpar[i].def = lives_malloc(sizint);
3328       if (!weed_paramtmpl_value_irrelevant(wtmpl) && weed_plant_has_leaf(wtmpl, WEED_LEAF_HOST_DEFAULT)) {
3329         vali = weed_get_int_value(wtmpl, WEED_LEAF_HOST_DEFAULT, NULL);
3330       } else if (weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 0)
3331         vali = weed_get_int_value(wtmpl, WEED_LEAF_DEFAULT, NULL);
3332       else vali = weed_get_int_value(wtmpl, WEED_LEAF_NEW_DEFAULT, NULL);
3333       set_int_param(rpar[i].def, vali);
3334       vali = weed_get_int_value(wpar, WEED_LEAF_VALUE, NULL);
3335       set_int_param(rpar[i].value, vali);
3336       rpar[i].min = (double)weed_get_int_value(wtmpl, WEED_LEAF_MIN, NULL);
3337       rpar[i].max = (double)weed_get_int_value(wtmpl, WEED_LEAF_MAX, NULL);
3338       if (weed_paramtmpl_does_wrap(wtmpl)) rpar[i].wrap = TRUE;
3339       if (gui) {
3340         if (weed_plant_has_leaf(gui, WEED_LEAF_CHOICES)) {
3341           listlen = weed_leaf_num_elements(gui, WEED_LEAF_CHOICES);
3342           list = weed_get_string_array(gui, WEED_LEAF_CHOICES, NULL);
3343           for (j = 0; j < listlen; j++) {
3344             gtk_list = lives_list_append(gtk_list, list[j]);
3345           }
3346           lives_free(list);
3347           rpar[i].list = lives_list_copy(gtk_list);
3348           lives_list_free(gtk_list);
3349           gtk_list = NULL;
3350           rpar[i].type = LIVES_PARAM_STRING_LIST;
3351           rpar[i].max = listlen;
3352         } else if (weed_plant_has_leaf(gui, WEED_LEAF_STEP_SIZE))
3353           rpar[i].step_size = (double)weed_get_int_value(gui, WEED_LEAF_STEP_SIZE, NULL);
3354         if (rpar[i].step_size == 0.) rpar[i].step_size = 1.;
3355       }
3356       break;
3357     case WEED_PARAM_FLOAT:
3358       if (weed_plant_has_leaf(wtmpl, WEED_LEAF_DEFAULT) && weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 1) {
3359         rpar[i].hidden |= HIDDEN_MULTI;
3360       }
3361       rpar[i].type = LIVES_PARAM_NUM;
3362       rpar[i].value = lives_malloc(sizdbl);
3363       rpar[i].def = lives_malloc(sizdbl);
3364       if (!weed_paramtmpl_value_irrelevant(wtmpl) && weed_plant_has_leaf(wtmpl, WEED_LEAF_HOST_DEFAULT))
3365         vald = weed_get_double_value(wtmpl, WEED_LEAF_HOST_DEFAULT, NULL);
3366       else if (weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 0)
3367         vald = weed_get_double_value(wtmpl, WEED_LEAF_DEFAULT, NULL);
3368       else vald = weed_get_double_value(wtmpl, WEED_LEAF_NEW_DEFAULT, NULL);
3369       set_double_param(rpar[i].def, vald);
3370       vald = weed_get_double_value(wpar, WEED_LEAF_VALUE, NULL);
3371       set_double_param(rpar[i].value, vald);
3372       rpar[i].min = weed_get_double_value(wtmpl, WEED_LEAF_MIN, NULL);
3373       rpar[i].max = weed_get_double_value(wtmpl, WEED_LEAF_MAX, NULL);
3374       if (weed_paramtmpl_does_wrap(wtmpl)) rpar[i].wrap = TRUE;
3375       rpar[i].step_size = 0.;
3376       rpar[i].dp = 2;
3377       if (gui) {
3378         if (weed_plant_has_leaf(gui, WEED_LEAF_STEP_SIZE))
3379           rpar[i].step_size = weed_get_double_value(gui, WEED_LEAF_STEP_SIZE, NULL);
3380         if (weed_plant_has_leaf(gui, WEED_LEAF_DECIMALS))
3381           rpar[i].dp = weed_get_int_value(gui, WEED_LEAF_DECIMALS, NULL);
3382       }
3383       if (rpar[i].step_size == 0.) {
3384         if (rpar[i].max - rpar[i].min > 10. && !(rpar[i].min >= -10. && rpar[i].max <= 10.))
3385           rpar[i].step_size = 1.;
3386         else if (rpar[i].max - rpar[i].min > 1. && !(rpar[i].min >= -1. && rpar[i].max <= 1.))
3387           rpar[i].step_size = .1;
3388         else rpar[i].step_size = 1. / (double)lives_10pow(rpar[i].dp);
3389       }
3390       break;
3391     case WEED_PARAM_TEXT:
3392       if (weed_plant_has_leaf(wtmpl, WEED_LEAF_DEFAULT) && weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 1) {
3393         rpar[i].hidden |= HIDDEN_MULTI;
3394       }
3395       rpar[i].type = LIVES_PARAM_STRING;
3396       if (!weed_paramtmpl_value_irrelevant(wtmpl) && weed_plant_has_leaf(wtmpl, WEED_LEAF_HOST_DEFAULT))
3397         string = weed_get_string_value(wtmpl, WEED_LEAF_HOST_DEFAULT, NULL);
3398       else if (weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 0)
3399         string = weed_get_string_value(wtmpl, WEED_LEAF_DEFAULT, NULL);
3400       else string = weed_get_string_value(wtmpl, WEED_LEAF_NEW_DEFAULT, NULL);
3401       rpar[i].def = string;
3402       string = weed_get_string_value(wpar, WEED_LEAF_VALUE, NULL);
3403       rpar[i].value = string;
3404       rpar[i].max = 0.;
3405       if (gui && weed_plant_has_leaf(gui, WEED_LEAF_MAXCHARS)) {
3406         rpar[i].max = (double)weed_get_int_value(gui, WEED_LEAF_MAXCHARS, NULL);
3407         if (rpar[i].max < 0.) rpar[i].max = 0.;
3408       }
3409       break;
3410     case WEED_PARAM_COLOR:
3411       cspace = weed_get_int_value(wtmpl, WEED_LEAF_COLORSPACE, NULL);
3412       switch (cspace) {
3413       case WEED_COLORSPACE_RGB:
3414         if (weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 3) {
3415           rpar[i].hidden |= HIDDEN_MULTI;
3416         }
3417         rpar[i].type = LIVES_PARAM_COLRGB24;
3418         rpar[i].value = lives_malloc(3 * sizint);
3419         rpar[i].def = lives_malloc(3 * sizint);
3420 
3421         if (weed_leaf_seed_type(wtmpl, WEED_LEAF_DEFAULT) == WEED_SEED_INT) {
3422           if (!weed_paramtmpl_value_irrelevant(wtmpl) && weed_plant_has_leaf(wtmpl, WEED_LEAF_HOST_DEFAULT)) {
3423             cols = weed_get_int_array(wtmpl, WEED_LEAF_HOST_DEFAULT, NULL);
3424           } else if (weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 0)
3425             cols = weed_get_int_array(wtmpl, WEED_LEAF_DEFAULT, NULL);
3426           else cols = weed_get_int_array(wtmpl, WEED_LEAF_NEW_DEFAULT, NULL);
3427           if (weed_leaf_num_elements(wtmpl, WEED_LEAF_MAX) == 1) {
3428             red_max = green_max = blue_max = weed_get_int_value(wtmpl, WEED_LEAF_MAX, NULL);
3429           } else {
3430             maxi = weed_get_int_array(wtmpl, WEED_LEAF_MAX, NULL);
3431             red_max = maxi[0];
3432             green_max = maxi[1];
3433             blue_max = maxi[2];
3434           }
3435           if (weed_leaf_num_elements(wtmpl, WEED_LEAF_MIN) == 1) {
3436             red_min = green_min = blue_min = weed_get_int_value(wtmpl, WEED_LEAF_MIN, NULL);
3437           } else {
3438             mini = weed_get_int_array(wtmpl, WEED_LEAF_MIN, NULL);
3439             red_min = mini[0];
3440             green_min = mini[1];
3441             blue_min = mini[2];
3442           }
3443           if (cols[0] < red_min) cols[0] = red_min;
3444           if (cols[1] < green_min) cols[1] = green_min;
3445           if (cols[2] < blue_min) cols[2] = blue_min;
3446           if (cols[0] > red_max) cols[0] = red_max;
3447           if (cols[1] > green_max) cols[1] = green_max;
3448           if (cols[2] > blue_max) cols[2] = blue_max;
3449           cols[0] = (double)(cols[0] - red_min) / (double)(red_max - red_min) * 255. + .49999;
3450           cols[1] = (double)(cols[1] - green_min) / (double)(green_max - green_min) * 255. + .49999;
3451           cols[2] = (double)(cols[2] - blue_min) / (double)(blue_max - blue_min) * 255. + .49999;
3452           col_int = TRUE;
3453         } else {
3454           if (!weed_paramtmpl_value_irrelevant(wtmpl) && weed_plant_has_leaf(wtmpl, WEED_LEAF_HOST_DEFAULT))
3455             colsd = weed_get_double_array(wtmpl, WEED_LEAF_HOST_DEFAULT, NULL);
3456           else if (weed_leaf_num_elements(wtmpl, WEED_LEAF_DEFAULT) > 0)
3457             colsd = weed_get_double_array(wtmpl, WEED_LEAF_DEFAULT, NULL);
3458           else colsd = weed_get_double_array(wtmpl, WEED_LEAF_DEFAULT, NULL);
3459           if (weed_leaf_num_elements(wtmpl, WEED_LEAF_MAX) == 1) {
3460             red_maxd = green_maxd = blue_maxd = weed_get_double_value(wtmpl, WEED_LEAF_MAX, NULL);
3461           } else {
3462             maxd = weed_get_double_array(wtmpl, WEED_LEAF_MAX, NULL);
3463             red_maxd = maxd[0];
3464             green_maxd = maxd[1];
3465             blue_maxd = maxd[2];
3466           }
3467           if (weed_leaf_num_elements(wtmpl, WEED_LEAF_MIN) == 1) {
3468             red_mind = green_mind = blue_mind = weed_get_double_value(wtmpl, WEED_LEAF_MIN, NULL);
3469           } else {
3470             mind = weed_get_double_array(wtmpl, WEED_LEAF_MIN, NULL);
3471             red_mind = mind[0];
3472             green_mind = mind[1];
3473             blue_mind = mind[2];
3474           }
3475           if (colsd[0] < red_mind) colsd[0] = red_mind;
3476           if (colsd[1] < green_mind) colsd[1] = green_mind;
3477           if (colsd[2] < blue_mind) colsd[2] = blue_mind;
3478           if (colsd[0] > red_maxd) colsd[0] = red_maxd;
3479           if (colsd[1] > green_maxd) colsd[1] = green_maxd;
3480           if (colsd[2] > blue_maxd) colsd[2] = blue_maxd;
3481           cols = (int *)lives_malloc(3 * sizint);
3482           cols[0] = (colsd[0] - red_mind) / (red_maxd - red_mind) * 255. + .49999;
3483           cols[1] = (colsd[1] - green_mind) / (green_maxd - green_mind) * 255. + .49999;
3484           cols[2] = (colsd[2] - blue_mind) / (blue_maxd - blue_mind) * 255. + .49999;
3485           col_int = FALSE;
3486         }
3487         set_colRGB24_param(rpar[i].def, cols[0], cols[1], cols[2]);
3488 
3489         if (col_int) {
3490           lives_free(cols);
3491           cols = weed_get_int_array(wpar, WEED_LEAF_VALUE, NULL);
3492           if (cols[0] < red_min) cols[0] = red_min;
3493           if (cols[1] < green_min) cols[1] = green_min;
3494           if (cols[2] < blue_min) cols[2] = blue_min;
3495           if (cols[0] > red_max) cols[0] = red_max;
3496           if (cols[1] > green_max) cols[1] = green_max;
3497           if (cols[2] > blue_max) cols[2] = blue_max;
3498           cols[0] = (double)(cols[0] - red_min) / (double)(red_max - red_min) * 255. + .49999;
3499           cols[1] = (double)(cols[1] - green_min) / (double)(green_max - green_min) * 255. + .49999;
3500           cols[2] = (double)(cols[2] - blue_min) / (double)(blue_max - blue_min) * 255. + .49999;
3501         } else {
3502           colsd = weed_get_double_array(wpar, WEED_LEAF_VALUE, NULL);
3503           if (colsd[0] < red_mind) colsd[0] = red_mind;
3504           if (colsd[1] < green_mind) colsd[1] = green_mind;
3505           if (colsd[2] < blue_mind) colsd[2] = blue_mind;
3506           if (colsd[0] > red_maxd) colsd[0] = red_maxd;
3507           if (colsd[1] > green_maxd) colsd[1] = green_maxd;
3508           if (colsd[2] > blue_maxd) colsd[2] = blue_maxd;
3509           cols[0] = (colsd[0] - red_mind) / (red_maxd - red_mind) * 255. + .49999;
3510           cols[1] = (colsd[1] - green_mind) / (green_maxd - green_mind) * 255. + .49999;
3511           cols[2] = (colsd[2] - blue_mind) / (blue_maxd - blue_mind) * 255. + .49999;
3512         }
3513         set_colRGB24_param(rpar[i].value, (short)cols[0], (short)cols[1], (short)cols[2]);
3514         lives_free(cols);
3515 
3516         lives_freep((void **)&maxi);
3517         lives_freep((void **)&mini);
3518         lives_freep((void **)&maxd);
3519         lives_freep((void **)&mind);
3520         break;
3521       }
3522       break;
3523 
3524     default:
3525       rpar[i].type = LIVES_PARAM_UNKNOWN; // TODO - try to get default
3526     }
3527 
3528     string = weed_get_string_value(wtmpl, WEED_LEAF_NAME, NULL);
3529     rpar[i].name = string;
3530     rpar[i].label = lives_strdup(string);
3531 
3532     if (weed_plant_has_leaf(wtmpl, WEED_LEAF_DESCRIPTION)) {
3533       string = weed_get_string_value(wtmpl, WEED_LEAF_DESCRIPTION, NULL);
3534       rpar[i].desc = string;
3535     } else rpar[i].desc = NULL;
3536 
3537     if (is_hidden_param(inst, i)) rpar[i].hidden |= HIDDEN_GUI_PERM;
3538 
3539     // gui part /////////////////////
3540 
3541     if (gui) {
3542       if (weed_plant_has_leaf(gui, WEED_LEAF_LABEL)) {
3543         string = weed_get_string_value(gui, WEED_LEAF_LABEL, NULL);
3544         lives_free(rpar[i].label);
3545         rpar[i].label = string;
3546       }
3547       if (weed_plant_has_leaf(gui, WEED_LEAF_USE_MNEMONIC)) {
3548         rpar[i].use_mnemonic = weed_get_boolean_value(gui, WEED_LEAF_USE_MNEMONIC, NULL);
3549       }
3550     }
3551 
3552     for (j = 0; j < MAX_PARAM_WIDGETS; j++) {
3553       rpar[i].widgets[j] = NULL;
3554     }
3555     rpar[i].onchange = FALSE;
3556   }
3557 
3558   lives_free(wpars);
3559 
3560   return rpar;
3561 }
3562 
3563 
weed_to_rfx(weed_plant_t * plant,boolean show_reinits)3564 lives_rfx_t *weed_to_rfx(weed_plant_t *plant, boolean show_reinits) {
3565   // return an RFX for a weed effect; set rfx->source to an INSTANCE of the filter (first instance for compound fx)
3566   // instance should be refcounted
3567   weed_plant_t *filter, *inst;
3568 
3569   char *string;
3570   lives_rfx_t *rfx = (lives_rfx_t *)lives_calloc(1, sizeof(lives_rfx_t));
3571   rfx->is_template = FALSE;
3572   if (weed_get_int_value(plant, WEED_LEAF_TYPE, NULL) == WEED_PLANT_FILTER_INSTANCE) {
3573     filter = weed_instance_get_filter(plant, TRUE);
3574     inst = plant;
3575   } else {
3576     filter = plant;
3577     inst = weed_instance_from_filter(filter);
3578     // init and deinit the effect to allow the plugin to hide parameters, etc.
3579     // rfx will inherit the refcount
3580     weed_reinit_effect(inst, TRUE);
3581     weed_instance_unref(inst);
3582     rfx->is_template = TRUE;
3583   }
3584 
3585   string = weed_get_string_value(filter, WEED_LEAF_NAME, NULL);
3586   rfx->name = string;
3587   rfx->menu_text = lives_strdup(string);
3588   rfx->action_desc = lives_strdup("no action");
3589   rfx->min_frames = -1;
3590   rfx->num_in_channels = enabled_in_channels(filter, FALSE);
3591   rfx->status = RFX_STATUS_WEED;
3592   rfx->props = 0;
3593   rfx->menuitem = NULL;
3594   rfx->num_params = weed_leaf_num_elements(filter, WEED_LEAF_IN_PARAMETER_TEMPLATES);
3595   if (rfx->num_params > 0) rfx->params = weed_params_to_rfx(rfx->num_params, inst, show_reinits);
3596   else rfx->params = NULL;
3597   rfx->source = (void *)inst;
3598   rfx->source_type = LIVES_RFX_SOURCE_WEED;
3599   rfx->gui_strings = NULL;
3600   rfx->onchange_strings = NULL;
3601   rfx->flags = 0;
3602   rfx->needs_reinit = FALSE;
3603   return rfx;
3604 }
3605 
3606 /**
3607    @brief get the interface hints set by a Weed filter in the filter_class.
3608 
3609     for a compound effect we get the gui elements from each internal filter in sequence,
3610     inserting internal|nextfilter after each filter
3611 
3612     - the filter MUST have set LAYOUT_SCHEME to RFX in the filter class.
3613     - it must have set the leaf RFX_DELIM with the string delimiter (and anything after the first character is ignored)
3614     - the layout must be set in the RFX_STRINGS array, using the delimiter
3615 
3616     returns a LiVESList of the results
3617 */
get_external_window_hints(lives_rfx_t * rfx)3618 LiVESList *get_external_window_hints(lives_rfx_t *rfx) {
3619   LiVESList *hints = NULL;
3620 
3621   if (rfx->status == RFX_STATUS_WEED) {
3622     weed_plant_t *gui;
3623     weed_plant_t *inst = (weed_plant_t *)rfx->source;
3624     weed_plant_t *filter = weed_instance_get_filter(inst, TRUE);
3625     int *filters = NULL;
3626     char *string, **rfx_strings, *delim;
3627     int nfilters;
3628     int num_hints;
3629     int i;
3630 
3631     if ((nfilters = num_compound_fx(filter)) > 1) {
3632       // handle compound fx
3633       filters = weed_get_int_array(filter, WEED_LEAF_HOST_FILTER_LIST, NULL);
3634     }
3635 
3636     for (i = 0; i < nfilters; i++) {
3637       if (filters) {
3638         filter = get_weed_filter(filters[i]);
3639       }
3640 
3641       if (!weed_plant_has_leaf(filter, WEED_LEAF_GUI)) continue;
3642       gui = weed_get_plantptr_value(filter, WEED_LEAF_GUI, NULL);
3643 
3644       if (!weed_plant_has_leaf(gui, WEED_LEAF_LAYOUT_SCHEME)) continue;
3645 
3646       string = weed_get_string_value(gui, WEED_LEAF_LAYOUT_SCHEME, NULL);
3647       if (strcmp(string, "RFX")) {
3648         lives_free(string);
3649         continue;
3650       }
3651       lives_free(string);
3652 
3653       if (!weed_plant_has_leaf(gui, WEED_LEAF_RFX_DELIM)) continue;
3654       delim = weed_get_string_value(gui, WEED_LEAF_RFX_DELIM, NULL);
3655       lives_snprintf(rfx->delim, 2, "%s", delim);
3656       lives_free(delim);
3657 
3658       rfx_strings = weed_get_string_array_counted(gui, WEED_LEAF_RFX_STRINGS, &num_hints);
3659       if (!num_hints) continue;
3660 
3661       for (i = 0; i < num_hints; i++) {
3662         hints = lives_list_append(hints, rfx_strings[i]);
3663       }
3664       lives_free(rfx_strings);
3665 
3666       if (filters) hints = lives_list_append(hints, lives_strdup("internal|nextfilter"));
3667     }
3668 
3669     lives_freep((void **)&filters);
3670   }
3671 
3672   return hints;
3673 }
3674 
3675 
rfx_clean_exe(lives_rfx_t * rfx)3676 void rfx_clean_exe(lives_rfx_t *rfx) {
3677   if (rfx) {
3678     char *fnamex = lives_build_filename(prefs->workdir, rfx->name, NULL);
3679     if (lives_file_test(fnamex, LIVES_FILE_TEST_EXISTS)) lives_rm(fnamex);
3680     lives_free(fnamex);
3681   }
3682 }
3683 
3684 
3685 /**
3686    @brief create an interface window for a plugin; possibly run it, and return the parameters
3687 
3688     N.B. this is NOT for rendered effects, those have their own functions.
3689 
3690     -- currently used for: encoder plugins and video playback plugins.
3691 
3692     Given an RFX script in scrap_text, (generally retrieved by some means from the plugin),
3693     will create an rfx effect, building the parameters from the <params> section of scrap_text,
3694     using the layout hints (optional) from <param_window>, and construct a parameter interface.
3695 
3696     The function has two modes of operation:
3697 
3698     If vbox is not NULL it should point to a LiVESVBox into which the parameter box will be added.
3699     The function will return NULL, and the rfx can be retrieved from ret_rfx.
3700 
3701     If vbox is NULL, the param window will be run, and if the user clicks "OK", the parameter values are returned in a marshalled list.
3702     If the user closes the window with Cancel, NULL is returned instead.
3703 
3704     If the plugin has no user adjustable parameters, the an empty string is returned.
3705 
3706     If <onchange> exists then the init | trigger will be run
3707     to let the plugin update default values (for vpps only currently)
3708 
3709     The onchange code is currently run by generating a perl scrap and runing that. In future the code could
3710     be run in different languages or internally by using a simple parser like the one in the data_processor plugin.
3711 
3712 
3713     NOTE: if vbox is not NULL, we create the window inside vbox, without running it
3714     in this case, vbox should be packed in its own dialog window, which should then be run
3715 
3716     called from plugins.c (vpp opts) and saveplay.c (encoder opts) */
plugin_run_param_window(const char * scrap_text,LiVESVBox * vbox,lives_rfx_t ** ret_rfx)3717 char *plugin_run_param_window(const char *scrap_text, LiVESVBox * vbox, lives_rfx_t **ret_rfx) {
3718   FILE *sfile;
3719 
3720   lives_rfx_t *rfx = (lives_rfx_t *)lives_calloc(1, sizeof(lives_rfx_t));
3721 
3722   char *string;
3723   char *rfx_scrapname, *rfx_scriptname;
3724   char *rfxfile;
3725   char *com;
3726   char *fnamex = NULL;
3727   char *res_string = NULL;
3728   char buff[32];
3729 
3730   int res;
3731   int retval;
3732 
3733   if (!check_for_executable(&capable->has_mktemp, EXEC_MKTEMP)) {
3734     do_program_not_found_error(EXEC_MKTEMP);
3735     return NULL;
3736   }
3737 
3738   rfx_scrapname = get_worktmpfile("rfx.");
3739   if (!rfx_scrapname) {
3740     workdir_warning();
3741     return NULL;
3742   }
3743 
3744   rfx_scriptname = lives_strdup_printf("%s.%s", rfx_scrapname, LIVES_FILE_EXT_RFX_SCRIPT);
3745 
3746   rfxfile = lives_build_path(prefs->workdir, rfx_scriptname, NULL);
3747   lives_free(rfx_scriptname);
3748 
3749   rfx->name = NULL;
3750 
3751   string = lives_strdup_printf("<name>\n%s\n</name>\n", rfx_scrapname);
3752 
3753   do {
3754     retval = 0;
3755     sfile = fopen(rfxfile, "w");
3756     if (!sfile) {
3757       retval = do_write_failed_error_s_with_retry(rfxfile, lives_strerror(errno));
3758       if (retval == LIVES_RESPONSE_CANCEL) {
3759         lives_free(string);
3760         return NULL;
3761       }
3762     } else {
3763       THREADVAR(write_failed) = FALSE;
3764       lives_fputs(string, sfile);
3765       if (scrap_text) {
3766         char *data = subst(scrap_text, "\\n", "\n");
3767         lives_fputs(data, sfile);
3768         lives_free(data);
3769       }
3770       fclose(sfile);
3771       lives_free(string);
3772       if (THREADVAR(write_failed)) {
3773         retval = do_write_failed_error_s_with_retry(rfxfile, NULL);
3774         if (retval == LIVES_RESPONSE_CANCEL) {
3775           return NULL;
3776         }
3777       }
3778     }
3779   } while (retval == LIVES_RESPONSE_RETRY);
3780 
3781   // OK, we should now have an RFX fragment in a file, we can compile it, then build a parameter window from it
3782 
3783   // call RFX_BUILDER program to compile the script, passing parameters input_filename and output_directory
3784   com = lives_strdup_printf("\"%s\" \"%s\" \"%s\" >%s", EXEC_RFX_BUILDER, rfxfile, prefs->workdir, LIVES_DEVNULL);
3785   res = lives_system(com, TRUE);
3786   lives_free(com);
3787 
3788   lives_rm(rfxfile);
3789   lives_free(rfxfile);
3790 
3791   if (res == 0) {
3792     // the script compiled correctly
3793 
3794     // now we pop up the parameter window, get the values of our parameters, and marshall them as extra_params
3795 
3796     // first create a lives_rfx_t from the scrap
3797     rfx->name = lives_strdup(rfx_scrapname);
3798     rfx->menu_text = NULL;
3799     rfx->action_desc = NULL;
3800     rfx->gui_strings = NULL;
3801     rfx->onchange_strings = NULL;
3802     lives_snprintf(rfx->rfx_version, 64, "%s", RFX_VERSION);
3803     rfx->flags = 0;
3804     rfx->status = RFX_STATUS_SCRAP;
3805 
3806     rfx->num_in_channels = 0;
3807     rfx->min_frames = -1;
3808 
3809     rfx->flags = RFX_FLAGS_NO_SLIDERS;
3810 
3811     fnamex = lives_build_filename(prefs->workdir, rfx_scrapname, NULL);
3812     com = lives_strdup_printf("\"%s\" get_define", fnamex);
3813     lives_free(fnamex);
3814 
3815     if (!lives_popen(com, TRUE, buff, 32)) {
3816       THREADVAR(com_failed) = TRUE;
3817     }
3818     lives_free(com);
3819 
3820     // command to get_define failed
3821     if (THREADVAR(com_failed)) {
3822       rfx_clean_exe(rfx);
3823       return NULL;
3824     }
3825 
3826     lives_snprintf(rfx->delim, 2, "%s", buff);
3827 
3828     // ok, this might need adjusting afterwards
3829     rfx->menu_text = (vbox == NULL ? lives_strdup_printf(_("%s advanced settings"), prefs->encoder.of_desc) : lives_strdup(""));
3830     rfx->is_template = FALSE;
3831 
3832     rfx->source = NULL;
3833     rfx->source_type = LIVES_RFX_SOURCE_RFX;
3834 
3835 #if 0
3836     render_fx_get_params(rfx, scrap_text, RFX_STATUS_INTERNAL);
3837 #else
3838     render_fx_get_params(rfx, rfx_scrapname, RFX_STATUS_SCRAP);
3839 #endif
3840 
3841     /// check if we actually have params to display
3842     if (!make_param_box(NULL, rfx)) {
3843       res_string = lives_strdup("");
3844       goto prpw_done;
3845     }
3846 
3847     // now we build our window and get param values
3848     if (!vbox) {
3849       _fx_dialog *fxdialog = on_fx_pre_activate(rfx, TRUE, NULL);
3850       LiVESWidget *dialog = fxdialog->dialog;
3851       if (prefs->show_gui) {
3852         lives_window_set_transient_for(LIVES_WINDOW(dialog), LIVES_WINDOW(LIVES_MAIN_WINDOW_WIDGET));
3853       }
3854       lives_window_set_modal(LIVES_WINDOW(dialog), TRUE);
3855 
3856       do {
3857         res = lives_dialog_run(LIVES_DIALOG(dialog));
3858       } while (res == LIVES_RESPONSE_RETRY);
3859 
3860       if (res == LIVES_RESPONSE_OK) {
3861         // marshall our params for passing to the plugin
3862         res_string = param_marshall(rfx, FALSE);
3863       }
3864 
3865       lives_widget_destroy(dialog);
3866 
3867       if (fx_dialog[1]) {
3868         lives_freep((void **)&fx_dialog[1]);
3869       }
3870     } else {
3871       make_param_box(vbox, rfx);
3872     }
3873 
3874 prpw_done:
3875     if (ret_rfx) {
3876       *ret_rfx = rfx;
3877     } else {
3878       rfx_clean_exe(rfx);
3879       rfx_free(rfx);
3880       lives_free(rfx);
3881     }
3882   } else {
3883     if (ret_rfx) {
3884       *ret_rfx = NULL;
3885     } else {
3886       res_string = lives_strdup("");
3887     }
3888     if (rfx) {
3889       lives_free(rfx);
3890     }
3891   }
3892   lives_free(rfx_scrapname);
3893   return res_string;
3894 }
3895 
3896