1 // liblives.cpp
2 // LiVES (lives-exe)
3 // (c) G. Finch <salsaman@gmail.com> 2015 - 2017
4 // Released under the GPL 3 or later
5 // see file ../COPYING for licensing details
6 
7 /** \file liblives.cpp
8     liblives interface
9 */
10 
11 #ifndef DOXYGEN_SKIP
12 
13 #include "liblives.hpp"
14 
15 #include <stdlib.h>
16 #include <pthread.h>
17 #include <unistd.h>
18 #include <iostream>
19 
20 extern "C" {
21   typedef int Boolean;
22 #include <libOSC/libosc.h>
23 #include <libOSC/OSC-client.h>
24 
25 #include "main.h"
26 #include "lbindings.h"
27 #include "effects-weed.h"
28 
29   int real_main(int argc, char *argv[], pthread_t *gtk_thread, ulong id);
30 
31   bool lives_osc_cb_quit(void *context, int arglen, const void *vargs, OSCTimeTag when, void *ra);
32 
33   track_rect *find_block_by_uid(lives_mt *mt, ulong uid);
34 }
35 
36 static volatile bool spinning;
37 static ulong msg_id;
38 static char *private_response;
39 static pthread_mutex_t spin_mutex = PTHREAD_MUTEX_INITIALIZER;
40 static pthread_mutex_t cond_mutex = PTHREAD_MUTEX_INITIALIZER;
41 static pthread_cond_t cond_done = PTHREAD_COND_INITIALIZER;
42 
private_cb(lives::_privateInfo * info,void * data)43 static bool private_cb(lives::_privateInfo *info, void *data) {
44   if (info->id == msg_id) {
45     private_response = strdup(info->response);
46     spinning = false;
47     pthread_cond_signal(&cond_done);
48     return false;
49   }
50   return true;
51 }
52 
53 #endif // doxygen_skip
54 
55 //////////////////////////////////////////////////
56 
57 namespace lives {
58 #ifndef DOXYGEN_SKIP
59 typedef struct {
60   ulong id;
61   livesApp *app;
62 } livesAppCtx;
63 
64 static list<livesAppCtx> appMgr;
65 
find_instance_for_id(ulong id)66 static livesApp *find_instance_for_id(ulong id) {
67   list<livesAppCtx>::iterator it;
68   for (it = appMgr.begin(); it != appMgr.end(); it++) {
69     if ((*it).id == id) return (*it).app;
70   }
71   return NULL;
72 }
73 
74 #endif
75 
setEncoding(lives_char_encoding_t enc)76 void livesString::setEncoding(lives_char_encoding_t enc) {
77   m_encoding = enc;
78 }
79 
encoding()80 lives_char_encoding_t livesString::encoding() {
81   return m_encoding;
82 }
83 
toEncoding(lives_char_encoding_t enc)84 livesString livesString::toEncoding(lives_char_encoding_t enc) {
85   if (enc == LIVES_CHAR_ENCODING_UTF8) {
86     if (m_encoding == LIVES_CHAR_ENCODING_LOCAL8BIT) {
87       livesString str(L2U8(this->c_str()));
88       str.setEncoding(LIVES_CHAR_ENCODING_UTF8);
89       return str;
90     }
91 #ifndef IS_MINGW
92     else if (m_encoding == LIVES_CHAR_ENCODING_FILESYSTEM) {
93       livesString str(F2U8(this->c_str()));
94       str.setEncoding(LIVES_CHAR_ENCODING_UTF8);
95       return str;
96     }
97 #endif
98   } else if (enc == LIVES_CHAR_ENCODING_FILESYSTEM) {
99 #ifndef IS_MINGW
100     if (m_encoding == LIVES_CHAR_ENCODING_UTF8) {
101       livesString str(U82F(this->c_str()));
102       str.setEncoding(LIVES_CHAR_ENCODING_FILESYSTEM);
103       return str;
104     }
105 #else
106     if (m_encoding == LIVES_CHAR_ENCODING_LOCAL8BIT) {
107       livesString str(U82L(this->c_str()));
108       str.setEncoding(LIVES_CHAR_ENCODING_FILESYSTEM);
109       return str;
110     }
111 #endif
112   } else if (enc == LIVES_CHAR_ENCODING_LOCAL8BIT) {
113     if (m_encoding == LIVES_CHAR_ENCODING_UTF8) {
114       livesString str(U82L(this->c_str()));
115       str.setEncoding(LIVES_CHAR_ENCODING_LOCAL8BIT);
116       return str;
117     }
118 #ifndef IS_MINGW
119     if (m_encoding == LIVES_CHAR_ENCODING_FILESYSTEM) {
120       livesString str(F2U8(this->c_str()));
121       str.assign(U82L(str.c_str()));
122       str.setEncoding(LIVES_CHAR_ENCODING_LOCAL8BIT);
123       return str;
124     }
125 #endif
126   }
127   return *this;
128 }
129 
130 
init(int argc,char * oargv[])131 void livesApp::init(int argc, char *oargv[]) {
132   char **argv;
133   char progname[] = "lives-exe";
134   if (argc < 0) argc = 0;
135   argc++;
136 
137   argv = (char **)malloc(argc * sizeof(char *));
138   argv[0] = strdup(progname);
139 
140   for (int i = 1; i < argc; i++) {
141     argv[i] = strdup(oargv[i - 1]);
142   }
143 
144   ulong id = lives_random();
145   livesAppCtx *ctx = new livesAppCtx;
146 
147   pthread_t *gtk_thread = new pthread_t;
148 
149   ctx->id = id;
150   ctx->app = this;
151   appMgr.push_back(*ctx);
152 
153   m_set = new set(this);
154   m_player = new player(this);
155   m_effectKeyMap = new effectKeyMap(this);
156   m_multitrack = new multitrack(this);
157 
158   m_deinterlace = false;
159 
160   m_thread = gtk_thread;
161 
162   m_id = id;
163 
164   real_main(argc, argv, m_thread, m_id);
165   free(argv);
166 }
167 
168 
livesApp()169 livesApp::livesApp() : m_id(0l) {
170   if (appMgr.empty())
171     init(0, NULL);
172 }
173 
174 
livesApp(int argc,char * argv[])175 livesApp::livesApp(int argc, char *argv[]) : m_id(0l) {
176   if (appMgr.empty())
177     init(argc, argv);
178 }
179 
180 
~livesApp()181 livesApp::~livesApp() {
182   if (!isValid()) return;
183   idle_quit(m_thread);
184 }
185 
186 
isValid() const187 bool livesApp::isValid() const {
188   return this != NULL && m_id != 0l;
189 }
190 
191 
isPlaying() const192 bool livesApp::isPlaying() const {
193   return status() == LIVES_STATUS_PLAYING;
194 }
195 
196 
isReady() const197 bool livesApp::isReady() const {
198   return status() == LIVES_STATUS_READY;
199 }
200 
201 
getSet()202 const set &livesApp::getSet() {
203   return *m_set;
204 }
205 
206 
getPlayer()207 const player &livesApp::getPlayer() {
208   return *m_player;
209 }
210 
211 
getMultitrack()212 const multitrack &livesApp::getMultitrack() {
213   return *m_multitrack;
214 }
215 
216 
appendClosure(lives_callback_t cb_type,callback_f func,void * data) const217 ulong livesApp::appendClosure(lives_callback_t cb_type, callback_f func, void *data) const {
218   closure *cl = new closure;
219   cl->id = lives_random();
220   cl->object = (livesApp *)this;
221   cl->cb_type = cb_type;
222   cl->func = (callback_f)func;
223   cl->data = data;
224   while (pthread_mutex_trylock(&spin_mutex)) {
225     // lock mutex so that new callbacks cannot be added yet
226     lives_usleep(::prefs->sleep_time);
227   }
228   ((livesApp *)this)->m_closures.push_back(cl);
229   pthread_mutex_unlock(&spin_mutex);
230   return cl->id;
231 }
232 
233 #ifndef DOXYGEN_SKIP
234 
setClosures(closureList cl)235 void livesApp::setClosures(closureList cl) {
236   m_closures = cl;
237 }
238 
239 #endif
240 
addCallback(lives_callback_t cb_type,modeChanged_callback_f func,void * data) const241 ulong livesApp::addCallback(lives_callback_t cb_type, modeChanged_callback_f func, void *data) const {
242   if (cb_type != LIVES_CALLBACK_MODE_CHANGED) return 0l;
243   return appendClosure(cb_type, (callback_f)func, data);
244 }
245 
addCallback(lives_callback_t cb_type,private_callback_f func,void * data) const246 ulong livesApp::addCallback(lives_callback_t cb_type, private_callback_f func, void *data) const {
247   if (cb_type != LIVES_CALLBACK_PRIVATE) return 0l;
248   return appendClosure(cb_type, (callback_f)func, data);
249 }
250 
addCallback(lives_callback_t cb_type,objectDestroyed_callback_f func,void * data) const251 ulong livesApp::addCallback(lives_callback_t cb_type, objectDestroyed_callback_f func, void *data) const {
252   if (cb_type != LIVES_CALLBACK_OBJECT_DESTROYED) return 0l;
253   return appendClosure(cb_type, (callback_f)func, data);
254 }
255 
addCallback(lives_callback_t cb_type,appQuit_callback_f func,void * data) const256 ulong livesApp::addCallback(lives_callback_t cb_type, appQuit_callback_f func, void *data) const {
257   if (cb_type != LIVES_CALLBACK_APP_QUIT) return 0l;
258   return appendClosure(cb_type, (callback_f)func, data);
259 }
260 
removeCallback(ulong id) const261 bool livesApp::removeCallback(ulong id) const {
262   while (pthread_mutex_trylock(&spin_mutex)) {
263     // lock mutex so that new callbacks cannot be added yet
264     lives_usleep(::prefs->sleep_time);
265   }
266   closureListIterator it = ((livesApp *)this)->m_closures.begin();
267   while (it != ((livesApp *)this)->m_closures.end()) {
268     if ((*it)->id == id) {
269       delete *it;
270       ((livesApp *)this)->m_closures.erase(it);
271       pthread_mutex_unlock(&spin_mutex);
272       return true;
273     }
274     ++it;
275   }
276   pthread_mutex_unlock(&spin_mutex);
277   return false;
278 }
279 
280 
showInfo(livesString text,bool blocking)281 lives_dialog_response_t livesApp::showInfo(livesString text, bool blocking) {
282   lives_dialog_response_t ret = LIVES_DIALOG_RESPONSE_INVALID;
283   if (!isValid() || status() == LIVES_STATUS_NOTREADY) return ret;
284   // if blocking wait for response
285   if (blocking) {
286     spinning = true;
287     msg_id = lives_random();
288     ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
289     pthread_mutex_lock(&cond_mutex);
290     if (!idle_show_info(text.toEncoding(LIVES_CHAR_ENCODING_UTF8).c_str(), blocking, msg_id)) {
291       pthread_mutex_unlock(&cond_mutex);
292       spinning = false;
293       removeCallback(cbid);
294     } else {
295       while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
296       pthread_mutex_unlock(&cond_mutex);
297       if (isValid()) {
298         ret = (lives_dialog_response_t)atoi(private_response);
299         lives_free(private_response);
300       }
301     }
302     return ret;
303   }
304   if (idle_show_info(text.toEncoding(LIVES_CHAR_ENCODING_UTF8).c_str(), blocking, 0))
305     return LIVES_DIALOG_RESPONSE_NONE;
306   return ret;
307 }
308 
309 
chooseFileWithPreview(livesString dirname,lives_filechooser_t preview_type,livesString title)310 livesString livesApp::chooseFileWithPreview(livesString dirname, lives_filechooser_t preview_type, livesString title) {
311   livesString emptystr;
312   if (!isValid() || status() == LIVES_STATUS_NOTREADY) return emptystr;
313   if (preview_type != LIVES_FILE_CHOOSER_VIDEO_AUDIO && preview_type != LIVES_FILE_CHOOSER_AUDIO_ONLY) return emptystr;
314   spinning = true;
315   msg_id = lives_random();
316   ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
317   pthread_mutex_lock(&cond_mutex);
318   if (!idle_choose_file_with_preview(dirname.toEncoding(LIVES_CHAR_ENCODING_FILESYSTEM).c_str(),
319                                      title.toEncoding(LIVES_CHAR_ENCODING_UTF8).c_str(),
320                                      preview_type, msg_id)) {
321     pthread_mutex_unlock(&cond_mutex);
322     spinning = false;
323     removeCallback(cbid);
324   } else {
325     while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
326     pthread_mutex_unlock(&cond_mutex);
327     if (isValid()) {
328       // last 2 chars are " " and %d (deinterlace choice)
329       livesString str(private_response, strlen(private_response) - 2, LIVES_CHAR_ENCODING_FILESYSTEM);
330       m_deinterlace = (bool)atoi(private_response + strlen(private_response) - 2);
331       lives_free(private_response);
332       return str;
333     }
334   }
335   return emptystr;
336 }
337 
338 
chooseSet()339 livesString livesApp::chooseSet() {
340   livesString emptystr;
341   if (!isValid() || status() == LIVES_STATUS_NOTREADY) return emptystr;
342   spinning = true;
343   msg_id = lives_random();
344   ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
345   pthread_mutex_lock(&cond_mutex);
346   if (!idle_choose_set(msg_id)) {
347     pthread_mutex_unlock(&cond_mutex);
348     spinning = false;
349     removeCallback(cbid);
350   } else {
351     while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
352     pthread_mutex_unlock(&cond_mutex);
353     if (isValid()) {
354       livesString str(private_response, LIVES_CHAR_ENCODING_FILESYSTEM);
355       lives_free(private_response);
356       return str;
357     }
358   }
359   return emptystr;
360 }
361 
362 
availableSets()363 livesStringList livesApp::availableSets() {
364   livesStringList list;
365   if (!isValid() || status() == LIVES_STATUS_NOTREADY) return list;
366   LiVESList *setlist = get_set_list(::prefs->workdir, true), *slist = setlist;
367   while (slist != NULL) {
368     list.push_back(livesString((const char *)slist->data, LIVES_CHAR_ENCODING_UTF8));
369     lives_free(slist->data);
370     slist = slist->next;
371   }
372   lives_list_free(setlist);
373   return list;
374 }
375 
376 
openFile(livesString fname,bool with_audio,double stime,int frames,bool deinterlace)377 clip livesApp::openFile(livesString fname, bool with_audio, double stime, int frames, bool deinterlace) {
378   if (!isValid() || status() == LIVES_STATUS_NOTREADY) return clip();
379   if (fname.empty()) return clip();
380   spinning = true;
381   msg_id = lives_random();
382   ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
383   ulong cid = 0l;
384 
385   pthread_mutex_lock(&cond_mutex);
386   if (!idle_open_file(fname.toEncoding(LIVES_CHAR_ENCODING_FILESYSTEM).c_str(), stime, frames, msg_id)) {
387     pthread_mutex_unlock(&cond_mutex);
388     spinning = false;
389     removeCallback(cbid);
390   } else {
391     while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
392     pthread_mutex_unlock(&cond_mutex);
393     if (isValid()) {
394       cid = strtoul(private_response, NULL, 10);
395       lives_free(private_response);
396     }
397   }
398   return clip(cid, this);
399 }
400 
401 
reloadSet(livesString setname)402 bool livesApp::reloadSet(livesString setname) {
403   if (!isValid() || status() == LIVES_STATUS_NOTREADY) return false;
404   spinning = true;
405   msg_id = lives_random();
406   ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
407 
408   pthread_mutex_lock(&cond_mutex);
409   if (!idle_reload_set(setname.toEncoding(LIVES_CHAR_ENCODING_FILESYSTEM).c_str(), msg_id)) {
410     pthread_mutex_unlock(&cond_mutex);
411     spinning = false;
412     removeCallback(cbid);
413     return false;
414   }
415   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
416   pthread_mutex_unlock(&cond_mutex);
417   if (isValid()) {
418     bool ret = (bool)atoi(private_response);
419     lives_free(private_response);
420     return ret;
421   }
422   return false;
423 }
424 
425 
deinterlaceOption()426 bool livesApp::deinterlaceOption() {
427   return m_deinterlace;
428 }
429 
430 
mode()431 lives_interface_mode_t livesApp::mode() {
432   if (!isValid() || status() == LIVES_STATUS_NOTREADY) return LIVES_INTERFACE_MODE_INVALID;
433   if (m_multitrack->isActive()) return LIVES_INTERFACE_MODE_MULTITRACK;
434   return LIVES_INTERFACE_MODE_CLIPEDIT;
435 }
436 
437 
setMode(lives_interface_mode_t newmode)438 lives_interface_mode_t livesApp::setMode(lives_interface_mode_t newmode) {
439   if (!isValid() || status() == LIVES_STATUS_NOTREADY) return LIVES_INTERFACE_MODE_INVALID;
440   spinning = true;
441   msg_id = lives_random();
442   ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
443 
444   pthread_mutex_lock(&cond_mutex);
445   if (!idle_set_if_mode(newmode, msg_id)) {
446     pthread_mutex_unlock(&cond_mutex);
447     spinning = false;
448     removeCallback(cbid);
449     return mode();
450   }
451   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
452   pthread_mutex_unlock(&cond_mutex);
453   if (isValid()) {
454     lives_free(private_response);
455   }
456   return mode();
457 }
458 
459 
status() const460 lives_status_t livesApp::status() const {
461   if (!isValid()) return LIVES_STATUS_INVALID;
462   if (mainw->go_away) return LIVES_STATUS_NOTREADY;
463   if (mainw->is_processing) return LIVES_STATUS_PROCESSING;
464   if ((mainw->preview || mainw->event_list != NULL) && mainw->multitrack == NULL) return LIVES_STATUS_PREVIEW;
465   if (mainw->playing_file > -1 || mainw->preview) return LIVES_STATUS_PLAYING;
466   return LIVES_STATUS_READY;
467 }
468 
469 
cancel()470 bool livesApp::cancel() {
471   if (!isValid()) return false;
472   if (status() != LIVES_STATUS_PROCESSING) return false;
473   bool ret = false;
474   spinning = true;
475   msg_id = lives_random();
476   ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
477 
478   pthread_mutex_lock(&cond_mutex);
479   if (!idle_cancel_proc(msg_id)) {
480     pthread_mutex_unlock(&cond_mutex);
481     spinning = false;
482     removeCallback(cbid);
483     return false;
484   }
485   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
486   pthread_mutex_unlock(&cond_mutex);
487   if (isValid()) {
488     ret = (bool)atoi(private_response);
489     lives_free(private_response);
490   }
491   return ret;
492 }
493 
494 #ifndef DOXYGEN_SKIP
495 
closures()496 closureList &livesApp::closures() {
497   return m_closures;
498 }
499 
500 #endif
501 
invalidate()502 void livesApp::invalidate() {
503   m_id = 0l;
504 }
505 
506 
interactive()507 bool livesApp::interactive() {
508   return mainw->interactive;
509 }
510 
511 
setInteractive(bool setting)512 bool livesApp::setInteractive(bool setting) {
513   if (!isValid() || status() == LIVES_STATUS_NOTREADY) return mainw->interactive;
514   spinning = true;
515   msg_id = lives_random();
516   ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
517   pthread_mutex_lock(&cond_mutex);
518   if (!idle_set_interactive(setting, msg_id)) {
519     pthread_mutex_unlock(&cond_mutex);
520     spinning = false;
521     removeCallback(cbid);
522   }
523   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
524   pthread_mutex_unlock(&cond_mutex);
525   if (isValid()) {
526     lives_free(private_response);
527   }
528   return setting;
529 }
530 
531 
getEffectKeyMap()532 const effectKeyMap &livesApp::getEffectKeyMap() {
533   return *m_effectKeyMap;
534 }
535 
536 
537 #ifndef DOXYGEN_SKIP
setPref(const char * prefidx,bool val) const538 bool livesApp::setPref(const char *prefidx, bool val) const {
539   if (!isValid() || status() == LIVES_STATUS_NOTREADY) return false;
540   spinning = true;
541   msg_id = lives_random();
542   ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
543   pthread_mutex_lock(&cond_mutex);
544   if (!idle_set_pref_bool(prefidx, val, msg_id)) {
545     pthread_mutex_unlock(&cond_mutex);
546     spinning = false;
547     removeCallback(cbid);
548     return false;
549   }
550   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
551   pthread_mutex_unlock(&cond_mutex);
552   if (isValid()) {
553     lives_free(private_response);
554   }
555   return true;
556 }
557 
setPref(const char * prefidx,int val) const558 bool livesApp::setPref(const char *prefidx, int val) const {
559   if (!isValid() || status() == LIVES_STATUS_NOTREADY) return false;
560   spinning = true;
561   msg_id = lives_random();
562   ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
563   pthread_mutex_lock(&cond_mutex);
564   if (!idle_set_pref_int(prefidx, val, msg_id)) {
565     pthread_mutex_unlock(&cond_mutex);
566     spinning = false;
567     removeCallback(cbid);
568     return false;
569   }
570   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
571   pthread_mutex_unlock(&cond_mutex);
572   if (isValid()) {
573     lives_free(private_response);
574   }
575   return true;
576 }
577 
setPref(const char * prefidx,int bitfield,bool val) const578 bool livesApp::setPref(const char *prefidx, int bitfield, bool val) const {
579   if (!isValid() || status() == LIVES_STATUS_NOTREADY) return false;
580   spinning = true;
581   msg_id = lives_random();
582   ulong cbid = addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
583   pthread_mutex_lock(&cond_mutex);
584   if (!idle_set_pref_bitmapped(prefidx, bitfield, val, msg_id)) {
585     pthread_mutex_unlock(&cond_mutex);
586     spinning = false;
587     removeCallback(cbid);
588     return false;
589   }
590   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
591   pthread_mutex_unlock(&cond_mutex);
592   if (isValid()) {
593     lives_free(private_response);
594   }
595   return true;
596 }
597 #endif
598 
599 //////////////// player ////////////////////
600 
player(livesApp * lives)601 player::player(livesApp *lives) {
602   // make shared ptr
603   m_lives = lives;
604 }
605 
606 
isValid() const607 bool player::isValid() const {
608   return m_lives != NULL && m_lives->isValid() && m_lives->status() != LIVES_STATUS_NOTREADY;
609 }
610 
611 
isPlaying() const612 bool player::isPlaying() const {
613   return isValid() && m_lives->isPlaying();
614 }
615 
616 
isRecording() const617 bool player::isRecording() const {
618   return isValid() && mainw->record;
619 }
620 
621 
play() const622 bool player::play() const {
623   if (!isValid() || !m_lives->isReady()) return false;
624   return start_player();
625 }
626 
627 
stop() const628 bool player::stop() const {
629   if (!isPlaying()) return false;
630   spinning = true;
631   msg_id = lives_random();
632   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
633   pthread_mutex_lock(&cond_mutex);
634   if (!idle_stop_playback(msg_id)) {
635     pthread_mutex_unlock(&cond_mutex);
636     spinning = false;
637     m_lives->removeCallback(cbid);
638     return false;
639   }
640   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
641   pthread_mutex_unlock(&cond_mutex);
642   if (isValid()) {
643     bool ret = atoi(private_response);
644     lives_free(private_response);
645     return ret;
646   }
647   return false;
648 }
649 
650 
setSepWin(bool setting) const651 void player::setSepWin(bool setting) const {
652   if (!isValid()) return;
653   spinning = true;
654   msg_id = lives_random();
655   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
656   pthread_mutex_lock(&cond_mutex);
657   if (!idle_set_sepwin(setting, msg_id)) {
658     pthread_mutex_unlock(&cond_mutex);
659     spinning = false;
660     m_lives->removeCallback(cbid);
661     return;
662   }
663   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
664   pthread_mutex_unlock(&cond_mutex);
665   if (isValid()) {
666     lives_free(private_response);
667   }
668   return;
669 }
670 
671 
setFullScreen(bool setting) const672 void player::setFullScreen(bool setting) const {
673   if (!isValid()) return;
674   spinning = true;
675   msg_id = lives_random();
676   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
677   pthread_mutex_lock(&cond_mutex);
678   if (!idle_set_fullscreen(setting, msg_id)) {
679     pthread_mutex_unlock(&cond_mutex);
680     spinning = false;
681     m_lives->removeCallback(cbid);
682     return;
683   }
684   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
685   pthread_mutex_unlock(&cond_mutex);
686   if (isValid()) {
687     lives_free(private_response);
688   }
689   return;
690 }
691 
692 
sepWin() const693 bool player::sepWin() const {
694   if (!isValid()) return false;
695   return mainw->sep_win;
696 }
697 
698 
fullScreen() const699 bool player::fullScreen() const {
700   if (!isValid()) return false;
701   return mainw->fs;
702 }
703 
704 
setForegroundClip(clip c) const705 bool player::setForegroundClip(clip c) const {
706   if (!isPlaying()) return false;
707   return c.switchTo();
708 }
709 
710 
setBackgroundClip(clip c) const711 bool player::setBackgroundClip(clip c) const {
712   if (!isPlaying()) return false;
713   return c.setIsBackground();
714 }
715 
716 
foregroundClip() const717 clip player::foregroundClip() const {
718   if (!isPlaying()) return clip();
719   if (m_lives->m_multitrack->isActive()) return clip();
720   if (mainw->files[mainw->playing_file] != NULL) return clip(mainw->files[mainw->playing_file]->unique_id, m_lives);
721   return clip();
722 }
723 
backgroundClip() const724 clip player::backgroundClip() const {
725   if (!isPlaying()) return clip();
726   if (m_lives->m_multitrack->isActive()) return clip();
727   if (mainw->files[mainw->blend_file] != NULL) return clip(mainw->files[mainw->blend_file]->unique_id, m_lives);
728   return clip();
729 }
730 
setFS(bool setting) const731 void player::setFS(bool setting) const {
732   if (!isValid()) return;
733   spinning = true;
734   msg_id = lives_random();
735   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
736   pthread_mutex_lock(&cond_mutex);
737   if (!idle_set_fullscreen_sepwin(setting, msg_id)) {
738     pthread_mutex_unlock(&cond_mutex);
739     spinning = false;
740     m_lives->removeCallback(cbid);
741     return;
742   }
743   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
744   pthread_mutex_unlock(&cond_mutex);
745   if (isValid()) {
746     lives_free(private_response);
747   }
748   return;
749 }
750 
751 
videoPlaybackTime(bool background) const752 double player::videoPlaybackTime(bool background) const {
753   if (!isValid()) return 0.;
754 
755   if (m_lives->status() == LIVES_STATUS_NOTREADY || m_lives->status() == LIVES_STATUS_PROCESSING) return 0.;
756 
757   if (!m_lives->m_multitrack->isActive()) {
758     if (mainw->current_file == -1) return 0.;
759     if (mainw->playing_file > -1) {
760       if (!background) return (cfile->frameno - 1.) / cfile->fps;
761       else if (mainw->blend_file != -1 && mainw->blend_file != mainw->current_file && mainw->files[mainw->blend_file] != NULL) {
762         return mainw->files[mainw->blend_file]->frameno;
763       } else return 0.;
764     } else return cfile->pointer_time;
765   } else {
766     return lives_ruler_get_value(LIVES_RULER(mainw->multitrack->timeline));
767   }
768 }
769 
770 
audioPlaybackTime() const771 double player::audioPlaybackTime() const {
772   if (!isValid()) return 0.;
773 
774   if (m_lives->status() != LIVES_STATUS_NOTREADY || m_lives->status() == LIVES_STATUS_PROCESSING) return 0.;
775 
776   if (!m_lives->m_multitrack->isActive()) {
777     if (mainw->current_file == -1) return 0.;
778     if (mainw->playing_file > -1) return (mainw->aframeno - 1.) / cfile->fps;
779     else return cfile->pointer_time;
780   } else {
781     return lives_ruler_get_value(LIVES_RULER(mainw->multitrack->timeline));
782   }
783 }
784 
785 
setAudioPlaybackTime(double time) const786 double player::setAudioPlaybackTime(double time) const {
787   if (!isValid()) return 0.;
788   if (!m_lives->isPlaying()) return 0.;
789   if (!is_realtime_aplayer(::prefs->audio_player)) return 0.;
790   if (mainw->record && ::prefs->audio_src == AUDIO_SRC_EXT) return 0.;
791   if (mainw->multitrack != NULL) return 0.;
792   if (time < 0. || time > cfile->laudio_time) return 0.;
793 
794   spinning = true;
795   msg_id = lives_random();
796   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
797   pthread_mutex_lock(&cond_mutex);
798   if (!idle_set_current_audio_time(time, msg_id)) {
799     pthread_mutex_unlock(&cond_mutex);
800     spinning = false;
801     m_lives->removeCallback(cbid);
802   } else {
803     while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
804     pthread_mutex_unlock(&cond_mutex);
805     if (isValid()) {
806       lives_free(private_response);
807     }
808   }
809   return audioPlaybackTime();
810 }
811 
812 
setPlaybackStartTime(double time) const813 double player::setPlaybackStartTime(double time) const {
814   if (!isValid()) return 0.;
815   if (!m_lives->isReady()) return 0.;
816 
817   spinning = true;
818   msg_id = lives_random();
819   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
820   pthread_mutex_lock(&cond_mutex);
821   if (!idle_set_current_time(time, msg_id)) {
822     pthread_mutex_unlock(&cond_mutex);
823     spinning = false;
824     m_lives->removeCallback(cbid);
825   } else {
826     while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
827     pthread_mutex_unlock(&cond_mutex);
828     if (isValid()) {
829       lives_free(private_response);
830     }
831   }
832   return videoPlaybackTime();
833 }
834 
835 
setVideoPlaybackFrame(int frame,bool bg) const836 int player::setVideoPlaybackFrame(int frame, bool bg) const {
837   if (!isValid()) return 0;
838 
839   spinning = true;
840   msg_id = lives_random();
841   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
842   pthread_mutex_lock(&cond_mutex);
843   if (!idle_set_current_frame(frame, bg, msg_id)) {
844     pthread_mutex_unlock(&cond_mutex);
845     spinning = false;
846     m_lives->removeCallback(cbid);
847   } else {
848     while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
849     pthread_mutex_unlock(&cond_mutex);
850     if (isValid()) {
851       lives_free(private_response);
852     }
853   }
854   return videoPlaybackTime();
855 }
856 
857 
elapsedTime() const858 double player::elapsedTime() const {
859   if (!isPlaying()) return 0.;
860   return mainw->currticks / TICKS_PER_SECOND_DBL;
861 }
862 
863 
currentFPS() const864 double player::currentFPS() const {
865   if (!isValid()) return 0.;
866   if (mainw->current_file == -1 || cfile == NULL) return 0.;
867   if (m_lives->status() != LIVES_STATUS_PLAYING && m_lives->status() != LIVES_STATUS_READY) return 0.;
868   return cfile->pb_fps;
869 }
870 
871 
setCurrentFPS(double fps) const872 double player::setCurrentFPS(double fps) const {
873   if (!isPlaying()) return 0.;
874 
875   spinning = true;
876   msg_id = lives_random();
877   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
878   pthread_mutex_lock(&cond_mutex);
879   if (!idle_set_current_fps(fps, msg_id)) {
880     pthread_mutex_unlock(&cond_mutex);
881     spinning = false;
882     m_lives->removeCallback(cbid);
883   } else {
884     while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
885     pthread_mutex_unlock(&cond_mutex);
886     if (isValid()) {
887       lives_free(private_response);
888     }
889   }
890   return currentFPS();
891 }
892 
893 
currentAudioRate() const894 int player::currentAudioRate() const {
895   if (!isValid()) return 0.;
896   if (m_lives->status() != LIVES_STATUS_PLAYING && m_lives->status() != LIVES_STATUS_READY) return 0.;
897   if (mainw->current_file == -1 || cfile == NULL) return 0.;
898   return cfile->arps;
899 }
900 
901 
setLoopMode(lives_loop_mode_t mode) const902 lives_loop_mode_t player::setLoopMode(lives_loop_mode_t mode) const {
903   if (!isValid()) return loopMode();
904   spinning = true;
905   msg_id = lives_random();
906   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
907   pthread_mutex_lock(&cond_mutex);
908   if (!idle_set_loop_mode(mode, msg_id)) {
909     pthread_mutex_unlock(&cond_mutex);
910     spinning = false;
911     m_lives->removeCallback(cbid);
912   } else {
913     while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
914     pthread_mutex_unlock(&cond_mutex);
915     if (isValid()) {
916       lives_free(private_response);
917     }
918   }
919   return loopMode();
920 }
921 
922 
loopMode() const923 lives_loop_mode_t player::loopMode() const {
924   unsigned int lmode = LIVES_LOOP_MODE_NONE;
925   if (!isValid()) return (lives_loop_mode_t)lmode;
926 
927   if (mainw->loop) lmode |= LIVES_LOOP_MODE_FIT_AUDIO;
928   if (mainw->loop_cont) lmode |= LIVES_LOOP_MODE_CONTINUOUS;
929 
930   return (lives_loop_mode_t)lmode;
931 }
932 
933 
setPingPong(bool setting) const934 bool player::setPingPong(bool setting) const {
935   if (!isValid()) return pingPong();
936   spinning = true;
937   msg_id = lives_random();
938   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
939   pthread_mutex_lock(&cond_mutex);
940   if (!idle_set_ping_pong(setting, msg_id)) {
941     pthread_mutex_unlock(&cond_mutex);
942     spinning = false;
943     m_lives->removeCallback(cbid);
944   } else {
945     while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
946     pthread_mutex_unlock(&cond_mutex);
947     if (isValid()) {
948       lives_free(private_response);
949     }
950   }
951   return pingPong();
952 }
953 
954 
pingPong() const955 bool player::pingPong() const {
956   if (!isValid()) return false;
957   return mainw->ping_pong;
958 }
959 
960 
resyncFPS() const961 bool player::resyncFPS() const {
962   if (!isPlaying()) return false;
963   spinning = true;
964   msg_id = lives_random();
965   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
966   pthread_mutex_lock(&cond_mutex);
967   if (!idle_resync_fps(msg_id)) {
968     pthread_mutex_unlock(&cond_mutex);
969     spinning = false;
970     m_lives->removeCallback(cbid);
971   } else {
972     while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
973     pthread_mutex_unlock(&cond_mutex);
974     if (isValid()) {
975       lives_free(private_response);
976     }
977   }
978   return true;
979 }
980 
981 
982 //////////////// set ////////////////////
983 
set(livesApp * lives)984 set::set(livesApp *lives) {
985   m_lives = lives;
986 }
987 
988 
isValid() const989 bool set::isValid() const {
990   return m_lives != NULL && m_lives->isValid() && m_lives->status() != LIVES_STATUS_NOTREADY;
991 }
992 
993 
name() const994 livesString set::name() const {
995   if (!isValid()) return livesString();
996   return livesString(mainw->set_name, LIVES_CHAR_ENCODING_UTF8);
997 }
998 
999 
setName(livesString name) const1000 bool set::setName(livesString name) const {
1001   if (!isValid()) return false;
1002   if (strlen(mainw->set_name) > 0) return false;
1003   if (numClips() == 0) return false;
1004 
1005   if (!name.empty()) {
1006     const char *new_set_name = name.toEncoding(LIVES_CHAR_ENCODING_FILESYSTEM).c_str();
1007     if (is_legal_set_name(new_set_name, TRUE)) {
1008       lives_snprintf(mainw->set_name, 128, "%s", new_set_name);
1009       return true;
1010     }
1011     return false;
1012   }
1013 
1014   spinning = true;
1015   msg_id = lives_random();
1016   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1017 
1018   pthread_mutex_lock(&cond_mutex);
1019   if (!idle_set_set_name(msg_id)) {
1020     pthread_mutex_unlock(&cond_mutex);
1021     spinning = false;
1022     m_lives->removeCallback(cbid);
1023     return false;
1024   }
1025   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1026   pthread_mutex_unlock(&cond_mutex);
1027   if (isValid()) {
1028     bool ret = (bool)atoi(private_response);
1029     lives_free(private_response);
1030     return ret;
1031   }
1032   return false;
1033 }
1034 
1035 
numClips() const1036 unsigned int set::numClips() const {
1037   if (!isValid()) return 0;
1038   (const_cast<set *>(this))->update_clip_list();
1039   return m_clips.size();
1040 }
1041 
1042 
nthClip(unsigned int n) const1043 clip set::nthClip(unsigned int n) const {
1044   if (!isValid()) return clip();
1045   (const_cast<set *>(this))->update_clip_list();
1046   if (n >= m_clips.size()) return clip();
1047   return clip(m_clips[n], m_lives);
1048 }
1049 
1050 
indexOf(clip c) const1051 int set::indexOf(clip c) const {
1052   if (!isValid()) return -1;
1053   if (!c.isValid()) return -1;
1054   if (m_clips.empty()) return -1;
1055   (const_cast<set *>(this))->update_clip_list();
1056   int i;
1057   for (i = 0; i < (int)m_clips.size(); i++) {
1058     if (m_clips[i] == c.m_uid) return i;
1059   }
1060   return -1;
1061 }
1062 
1063 
save(livesString name,bool force_append) const1064 bool set::save(livesString name, bool force_append) const {
1065   if (!isValid()) return FALSE;
1066   const char *cname = name.toEncoding(LIVES_CHAR_ENCODING_FILESYSTEM).c_str();
1067 
1068   spinning = true;
1069   msg_id = lives_random();
1070 
1071   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1072 
1073   bool ret = false;
1074 
1075   pthread_mutex_lock(&cond_mutex);
1076   if (!idle_save_set(cname, force_append, msg_id)) {
1077     pthread_mutex_unlock(&cond_mutex);
1078     spinning = false;
1079     m_lives->removeCallback(cbid);
1080   } else {
1081     while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1082     pthread_mutex_unlock(&cond_mutex);
1083     if (isValid()) {
1084       ret = (bool)(atoi(private_response));
1085       lives_free(private_response);
1086     }
1087   }
1088   return ret;
1089 }
1090 
1091 
save() const1092 bool set::save() const {
1093   return save(name());
1094 }
1095 
1096 
update_clip_list()1097 void set::update_clip_list() {
1098   clipListIterator it = m_clips.begin();
1099   while (it != m_clips.end()) {
1100     it = m_clips.erase(it);
1101   }
1102   if (isValid()) {
1103     ulong *ids = get_unique_ids();
1104 
1105     for (int i = 0; ids[i] != 0l; i++) {
1106       m_clips.push_back(ids[i]);
1107     }
1108     lives_free(ids);
1109   }
1110 }
1111 
1112 
1113 /////////////// clip ////////////////
1114 
clip()1115 clip::clip() : m_uid(0l), m_lives(NULL) {};
1116 
clip(ulong uid,livesApp * lives)1117 clip::clip(ulong uid, livesApp *lives) {
1118   m_uid = uid;
1119   m_lives = lives;
1120 }
1121 
isValid() const1122 bool clip::isValid() const {
1123   return (m_lives != NULL && m_lives->isValid() && m_lives->status() != LIVES_STATUS_NOTREADY && cnum_for_uid(m_uid) != -1);
1124 }
1125 
frames()1126 int clip::frames() {
1127   if (isValid()) {
1128     int cnum = cnum_for_uid(m_uid);
1129     if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->frames;
1130   }
1131   return 0;
1132 }
1133 
width()1134 int clip::width() {
1135   if (isValid()) {
1136     int cnum = cnum_for_uid(m_uid);
1137     if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->hsize;
1138   }
1139   return 0;
1140 }
1141 
height()1142 int clip::height() {
1143   if (isValid()) {
1144     int cnum = cnum_for_uid(m_uid);
1145     if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->vsize;
1146   }
1147   return 0;
1148 }
1149 
FPS()1150 double clip::FPS() {
1151   if (isValid()) {
1152     int cnum = cnum_for_uid(m_uid);
1153     if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->fps;
1154   }
1155   return 0.;
1156 }
1157 
1158 
playbackFPS()1159 double clip::playbackFPS() {
1160   if (isValid()) {
1161     if (!m_lives->m_multitrack->isActive()) {
1162       int cnum = cnum_for_uid(m_uid);
1163       if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->pb_fps;
1164     } else return m_lives->m_multitrack->FPS();
1165   }
1166   return 0.;
1167 }
1168 
1169 
audioRate()1170 int clip::audioRate() {
1171   if (isValid()) {
1172     int cnum = cnum_for_uid(m_uid);
1173     if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->arate;
1174   }
1175   return 0;
1176 }
1177 
1178 
playbackAudioRate()1179 int clip::playbackAudioRate() {
1180   int arps = -1;
1181   if (isValid()) {
1182     if (!m_lives->m_multitrack->isActive()) {
1183       int cnum = cnum_for_uid(m_uid);
1184       if (cnum > -1 && mainw->files[cnum] != NULL) {
1185         arps = mainw->files[cnum]->arps;
1186         arps *= mainw->files[cnum]->pb_fps / mainw->files[cnum]->fps;
1187       }
1188     } else arps = mainw->files[mainw->multitrack->render_file]->arate;
1189   }
1190   return arps;
1191 }
1192 
1193 
audioLength()1194 double clip::audioLength() {
1195   if (isValid()) {
1196     int cnum = cnum_for_uid(m_uid);
1197     if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->laudio_time;
1198   }
1199   return 0.;
1200 }
1201 
1202 
audioChannels()1203 int clip::audioChannels() {
1204   if (isValid()) {
1205     int cnum = cnum_for_uid(m_uid);
1206     if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->achans;
1207   }
1208   return 0;
1209 }
1210 
1211 
audioSampleSize()1212 int clip::audioSampleSize() {
1213   if (isValid()) {
1214     int cnum = cnum_for_uid(m_uid);
1215     if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->asampsize;
1216   }
1217   return 0;
1218 }
1219 
1220 
audioSigned()1221 bool clip::audioSigned() {
1222   if (isValid()) {
1223     int cnum = cnum_for_uid(m_uid);
1224     if (cnum > -1 && mainw->files[cnum] != NULL) return !(mainw->files[cnum]->signed_endian & AFORM_UNSIGNED);
1225   }
1226   return true;
1227 }
1228 
1229 
audioEndian()1230 lives_endian_t clip::audioEndian() {
1231   if (isValid()) {
1232     int cnum = cnum_for_uid(m_uid);
1233     if (cnum > -1 && mainw->files[cnum] != NULL) {
1234       if (mainw->files[cnum]->signed_endian & AFORM_BIG_ENDIAN) return LIVES_BIGENDIAN;
1235     }
1236   }
1237   return LIVES_LITTLEENDIAN;
1238 }
1239 
1240 
name()1241 livesString clip::name() {
1242   if (isValid()) {
1243     int cnum = cnum_for_uid(m_uid);
1244     if (cnum > -1 &&
1245         mainw->files[cnum] != NULL) return livesString(get_menu_name(mainw->files[cnum], FALSE), LIVES_CHAR_ENCODING_UTF8);
1246   }
1247   return livesString();
1248 }
1249 
1250 
selectionStart()1251 int clip::selectionStart() {
1252   if (isValid()) {
1253     int cnum = cnum_for_uid(m_uid);
1254     if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->start;
1255   }
1256   return 0;
1257 }
1258 
1259 
selectionEnd()1260 int clip::selectionEnd() {
1261   if (isValid()) {
1262     int cnum = cnum_for_uid(m_uid);
1263     if (cnum > -1 && mainw->files[cnum] != NULL) return mainw->files[cnum]->end;
1264   }
1265   return 0;
1266 }
1267 
1268 
selectAll()1269 bool clip::selectAll() {
1270   if (!isValid() || m_lives->status() == LIVES_STATUS_NOTREADY) return false;
1271   spinning = true;
1272   msg_id = lives_random();
1273   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1274 
1275   int cnum = cnum_for_uid(m_uid);
1276   pthread_mutex_lock(&cond_mutex);
1277   if (!idle_select_all(cnum, msg_id)) {
1278     pthread_mutex_unlock(&cond_mutex);
1279     spinning = false;
1280     m_lives->removeCallback(cbid);
1281     return false;
1282   }
1283   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1284   pthread_mutex_unlock(&cond_mutex);
1285   if (isValid()) {
1286     bool ret = (bool)atoi(private_response);
1287     lives_free(private_response);
1288     return ret;
1289   }
1290   return false;
1291 }
1292 
1293 
setSelectionStart(unsigned int frame)1294 bool clip::setSelectionStart(unsigned int frame) {
1295   if (!isValid()) return false;
1296   spinning = true;
1297   msg_id = lives_random();
1298   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1299 
1300   int cnum = cnum_for_uid(m_uid);
1301   pthread_mutex_lock(&cond_mutex);
1302   if (!idle_select_start(cnum, frame, msg_id)) {
1303     pthread_mutex_unlock(&cond_mutex);
1304     spinning = false;
1305     m_lives->removeCallback(cbid);
1306     return false;
1307   }
1308   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1309   pthread_mutex_unlock(&cond_mutex);
1310   if (isValid()) {
1311     bool ret = (bool)atoi(private_response);
1312     lives_free(private_response);
1313     return ret;
1314   }
1315   return false;
1316 }
1317 
1318 
setSelectionEnd(unsigned int frame)1319 bool clip::setSelectionEnd(unsigned int frame) {
1320   if (!isValid()) return false;
1321   spinning = true;
1322   msg_id = lives_random();
1323   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1324 
1325   int cnum = cnum_for_uid(m_uid);
1326   pthread_mutex_lock(&cond_mutex);
1327   if (!idle_select_end(cnum, frame, msg_id)) {
1328     pthread_mutex_unlock(&cond_mutex);
1329     spinning = false;
1330     m_lives->removeCallback(cbid);
1331     return false;
1332   }
1333   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1334   pthread_mutex_unlock(&cond_mutex);
1335   if (isValid()) {
1336     bool ret = (bool)atoi(private_response);
1337     lives_free(private_response);
1338     return ret;
1339   }
1340   return false;
1341 }
1342 
1343 
switchTo()1344 bool clip::switchTo() {
1345   if (!isValid()) return false;
1346   if (m_lives->m_multitrack->isActive()) return false;
1347   spinning = true;
1348   msg_id = lives_random();
1349   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1350 
1351   int cnum = cnum_for_uid(m_uid);
1352   pthread_mutex_lock(&cond_mutex);
1353   if (!idle_switch_clip(1, cnum, msg_id)) {
1354     pthread_mutex_unlock(&cond_mutex);
1355     spinning = false;
1356     m_lives->removeCallback(cbid);
1357     return false;
1358   }
1359   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1360   pthread_mutex_unlock(&cond_mutex);
1361   if (isValid()) {
1362     bool ret = (bool)atoi(private_response);
1363     lives_free(private_response);
1364     return ret;
1365   }
1366   return false;
1367 }
1368 
1369 
setIsBackground()1370 bool clip::setIsBackground() {
1371   if (!isValid()) return false;
1372   if (m_lives->m_multitrack->isActive()) return false;
1373   spinning = true;
1374   msg_id = lives_random();
1375   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1376 
1377   int cnum = cnum_for_uid(m_uid);
1378   pthread_mutex_lock(&cond_mutex);
1379   if (!idle_switch_clip(2, cnum, msg_id)) {
1380     pthread_mutex_unlock(&cond_mutex);
1381     spinning = false;
1382     m_lives->removeCallback(cbid);
1383     return false;
1384   }
1385   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1386   pthread_mutex_unlock(&cond_mutex);
1387   if (isValid()) {
1388     bool ret = (bool)atoi(private_response);
1389     lives_free(private_response);
1390     return ret;
1391   }
1392   return false;
1393 }
1394 
1395 //////////////////////////////////////////////
1396 
1397 //// effectKeyMap
effectKeyMap(livesApp * lives)1398 effectKeyMap::effectKeyMap(livesApp *lives) {
1399   m_lives = lives;
1400 }
1401 
1402 
isValid() const1403 bool effectKeyMap::isValid() const {
1404   return m_lives != NULL && m_lives->isValid() && m_lives->status() != LIVES_STATUS_NOTREADY;
1405 }
1406 
1407 
at(int i) const1408 effectKey effectKeyMap::at(int i) const {
1409   return (*this)[i];
1410 }
1411 
1412 
size() const1413 size_t effectKeyMap::size() const {
1414   if (!isValid()) return 0;
1415   return (size_t) prefs::rteKeysVirtual(*m_lives);
1416 }
1417 
1418 
clear() const1419 bool effectKeyMap::clear() const {
1420   if (!isValid()) return false;
1421   spinning = true;
1422   msg_id = lives_random();
1423   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1424 
1425   pthread_mutex_lock(&cond_mutex);
1426   if (!idle_unmap_effects(msg_id)) {
1427     pthread_mutex_unlock(&cond_mutex);
1428     spinning = false;
1429     m_lives->removeCallback(cbid);
1430     return false;
1431   }
1432   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1433   pthread_mutex_unlock(&cond_mutex);
1434   if (isValid()) {
1435     bool ret = (bool)atoi(private_response);
1436     lives_free(private_response);
1437     return ret;
1438   }
1439   return false;
1440 }
1441 
1442 
1443 /////////////////////////////////////////////////
1444 
1445 /// effectKey
effectKey()1446 effectKey::effectKey() {
1447   m_key = 0;
1448 }
1449 
1450 
effectKey(livesApp * lives,int key)1451 effectKey::effectKey(livesApp *lives, int key) {
1452   m_lives = lives;
1453   m_key = key;
1454 }
1455 
1456 
isValid() const1457 bool effectKey::isValid() const {
1458   return m_lives != NULL && m_lives->isValid() && m_lives->status() != LIVES_STATUS_NOTREADY &&
1459          m_key >= 1 && m_key <= prefs::rteKeysVirtual(*(m_lives));
1460 }
1461 
1462 
key()1463 int effectKey::key() {
1464   return m_key;
1465 }
1466 
1467 
numModes()1468 int effectKey::numModes() {
1469   if (!isValid()) return 0;
1470   return ::prefs->max_modes_per_key;
1471 }
1472 
1473 
numMappedModes()1474 int effectKey::numMappedModes() {
1475   if (!isValid()) return 0;
1476   return get_num_mapped_modes_for_key(m_key);
1477 }
1478 
1479 
currentMode()1480 int effectKey::currentMode() {
1481   if (!isValid()) return -1;
1482   return get_current_mode_for_key(m_key);
1483 }
1484 
1485 
enabled()1486 bool effectKey::enabled() {
1487   if (!isValid()) return false;
1488   return get_rte_key_is_enabled(m_key);
1489 }
1490 
1491 
setCurrentMode(int new_mode)1492 int effectKey::setCurrentMode(int new_mode) {
1493   if (!isValid()) return -1;
1494   if (new_mode < 0 || new_mode >= numMappedModes()) return currentMode();
1495 
1496   if (new_mode == currentMode()) return currentMode();
1497 
1498   spinning = true;
1499   msg_id = lives_random();
1500   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1501 
1502   pthread_mutex_lock(&cond_mutex);
1503   if (!idle_fx_setmode(m_key, new_mode, msg_id)) {
1504     pthread_mutex_unlock(&cond_mutex);
1505     spinning = false;
1506     m_lives->removeCallback(cbid);
1507     return currentMode();
1508   }
1509   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1510   pthread_mutex_unlock(&cond_mutex);
1511   if (isValid()) {
1512     lives_free(private_response);
1513   }
1514   return currentMode();
1515 }
1516 
1517 
setEnabled(bool setting)1518 bool effectKey::setEnabled(bool setting) {
1519   if (!isValid()) return false;
1520 
1521   spinning = true;
1522   msg_id = lives_random();
1523   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1524 
1525   pthread_mutex_lock(&cond_mutex);
1526   if (!idle_fx_enable(m_key, setting,  msg_id)) {
1527     spinning = false;
1528     m_lives->removeCallback(cbid);
1529     return enabled();
1530   }
1531   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1532   pthread_mutex_unlock(&cond_mutex);
1533   if (isValid()) {
1534     lives_free(private_response);
1535 
1536     // TODO: if it was a generator, wait for playing or error
1537 
1538   }
1539   return enabled();
1540 }
1541 
1542 
appendMapping(effect fx)1543 int effectKey::appendMapping(effect fx) {
1544   if (!isValid()) return -1;
1545 
1546   if (!fx.isValid()) return -1;
1547 
1548   if (fx.m_lives != m_lives) return -1;
1549 
1550   if (!m_lives->isReady() && !m_lives->isPlaying()) return -1;
1551 
1552   int mode = numMappedModes();
1553   if (mode == numModes()) return -1;
1554 
1555   spinning = true;
1556   msg_id = lives_random();
1557   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1558 
1559   pthread_mutex_lock(&cond_mutex);
1560   if (!idle_map_fx(m_key, mode, fx.m_idx, msg_id)) {
1561     pthread_mutex_unlock(&cond_mutex);
1562     spinning = false;
1563     m_lives->removeCallback(cbid);
1564     return -1;
1565   }
1566   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1567   pthread_mutex_unlock(&cond_mutex);
1568   if (isValid()) {
1569     bool ret = (bool)atoi(private_response);
1570     lives_free(private_response);
1571     if (ret) return mode;
1572   }
1573   return -1;
1574 }
1575 
1576 
removeMapping(int mode)1577 bool effectKey::removeMapping(int mode) {
1578   if (!isValid()) return false;
1579 
1580   if (!m_lives->isReady() && !m_lives->isPlaying()) return false;
1581 
1582   if (mode >= numMappedModes()) return false;
1583 
1584   spinning = true;
1585   msg_id = lives_random();
1586   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1587 
1588   pthread_mutex_lock(&cond_mutex);
1589   if (!idle_unmap_fx(m_key, mode, msg_id)) {
1590     pthread_mutex_unlock(&cond_mutex);
1591     spinning = false;
1592     m_lives->removeCallback(cbid);
1593     return false;
1594   }
1595   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1596   pthread_mutex_unlock(&cond_mutex);
1597   if (isValid()) {
1598     bool ret = (bool)atoi(private_response);
1599     lives_free(private_response);
1600     return ret;
1601   }
1602   return false;
1603 }
1604 
1605 
at(int mode)1606 effect effectKey::at(int mode) {
1607   effect e;
1608   if (!isValid()) return e;
1609   int idx = rte_keymode_get_filter_idx(m_key, mode);
1610   if (idx == -1) return e;
1611   e = effect(m_lives, idx);
1612   return e;
1613 }
1614 
1615 
1616 ////////////////////////////////////////////////////////
1617 
effect(const livesApp & lives,livesString hashname,bool match_full)1618 effect::effect(const livesApp &lives, livesString hashname, bool match_full) {
1619   m_idx = -1;
1620   m_lives = (livesApp *)&lives;
1621   if (m_lives != NULL && m_lives->isValid() && m_lives->status() != LIVES_STATUS_NOTREADY) {
1622     m_idx = weed_get_idx_for_hashname(hashname.toEncoding(LIVES_CHAR_ENCODING_UTF8).c_str(), match_full);
1623   }
1624 }
1625 
effect(const livesApp & lives,livesString package,livesString fxname,livesString author,int version)1626 effect::effect(const livesApp &lives, livesString package, livesString fxname, livesString author, int version) {
1627   m_idx = -1;
1628   m_lives = (livesApp *)&lives;
1629   if (m_lives != NULL && m_lives->isValid() && m_lives->status() != LIVES_STATUS_NOTREADY) {
1630     m_idx = get_first_fx_matched(package.toEncoding(LIVES_CHAR_ENCODING_UTF8).c_str(),
1631                                  fxname.toEncoding(LIVES_CHAR_ENCODING_UTF8).c_str(),
1632                                  author.toEncoding(LIVES_CHAR_ENCODING_UTF8).c_str(),
1633                                  version);
1634   }
1635 }
1636 
isValid() const1637 bool effect::isValid() const {
1638   return (m_idx != -1 && m_lives != NULL && m_lives->isValid() && m_lives->status() != LIVES_STATUS_NOTREADY);
1639 }
1640 
1641 
effect()1642 effect::effect() : m_lives(NULL), m_idx(-1) {}
1643 
effect(livesApp * lives,int idx)1644 effect::effect(livesApp *lives, int idx) : m_lives(lives), m_idx(idx) {}
1645 
1646 
1647 ///////////////////////////////
1648 //// block
1649 
block(multitrack * m,ulong uid)1650 block::block(multitrack *m, ulong uid) : m_uid(uid) {
1651   if (m == NULL) m_lives = NULL;
1652   else m_lives = m->m_lives;
1653 }
1654 
1655 
block(multitrack m,int track,double time)1656 block::block(multitrack m, int track, double time) {
1657   m_lives = m.m_lives;
1658   if (!m.isActive()) m_uid = 0l;
1659   else {
1660     track_rect *tr = get_block_from_track_and_time(mainw->multitrack, track, time);
1661     if (tr == NULL) m_uid = 0l;
1662     else m_uid = tr->uid;
1663   }
1664 }
1665 
1666 
isValid() const1667 bool block::isValid() const {
1668   if (m_lives == NULL || !m_lives->isValid() || !m_lives->m_multitrack->isActive() || m_uid == 0l ||
1669       find_block_by_uid(mainw->multitrack, m_uid) == NULL) return false;
1670   return true;
1671 }
1672 
1673 
invalidate()1674 void block::invalidate() {
1675   m_uid = 0l;
1676 }
1677 
1678 
startTime()1679 double block::startTime() {
1680   track_rect *tr = find_block_by_uid(mainw->multitrack, m_uid);
1681   if (tr == NULL) return -1.;
1682   return (double)get_event_timecode(tr->start_event) / TICKS_PER_SECOND_DBL;
1683 }
1684 
1685 
length()1686 double block::length() {
1687   track_rect *tr = find_block_by_uid(mainw->multitrack, m_uid);
1688   if (tr == NULL) return -1.;
1689   return (double)get_event_timecode(tr->end_event) / TICKS_PER_SECOND_DBL + 1. / mainw->multitrack->fps -
1690          (double)get_event_timecode(tr->start_event) / TICKS_PER_SECOND_DBL;
1691 }
1692 
1693 
clipSource()1694 clip block::clipSource() {
1695   track_rect *tr = find_block_by_uid(mainw->multitrack, m_uid);
1696   if (tr == NULL) return clip();
1697   int cnum = get_clip_for_block(tr);
1698   if (cnum == -1) return clip();
1699   return clip(mainw->files[cnum]->unique_id, m_lives);
1700 }
1701 
1702 
track()1703 int block::track() {
1704   track_rect *tr = find_block_by_uid(mainw->multitrack, m_uid);
1705   if (tr == NULL) return 0;
1706   return get_track_for_block(tr);
1707 }
1708 
1709 
remove()1710 bool block::remove() {
1711   if (!isValid()) return false;
1712   if (!m_lives->isReady()) return false;
1713 
1714   track_rect *tr = find_block_by_uid(mainw->multitrack, m_uid);
1715   if (tr == NULL) return false;
1716 
1717   spinning = true;
1718   msg_id = lives_random();
1719   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1720   pthread_mutex_lock(&cond_mutex);
1721   if (!idle_remove_block(m_uid, msg_id)) {
1722     pthread_mutex_unlock(&cond_mutex);
1723     spinning = false;
1724     m_lives->removeCallback(cbid);
1725     return false;
1726   }
1727   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1728   pthread_mutex_unlock(&cond_mutex);
1729   if (isValid()) {
1730     bool ret = (bool)atoi(private_response);
1731     lives_free(private_response);
1732     if (ret) invalidate();
1733     return ret;
1734   }
1735   return false;
1736 }
1737 
1738 
moveTo(int track,double time)1739 bool block::moveTo(int track, double time) {
1740   if (!isValid()) return false;
1741   if (!m_lives->isReady()) return false;
1742 
1743   track_rect *tr = find_block_by_uid(mainw->multitrack, m_uid);
1744   if (tr == NULL) return false;
1745 
1746   spinning = true;
1747   msg_id = lives_random();
1748   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1749   pthread_mutex_lock(&cond_mutex);
1750   if (!idle_move_block(m_uid, track, time, msg_id)) {
1751     pthread_mutex_unlock(&cond_mutex);
1752     spinning = false;
1753     m_lives->removeCallback(cbid);
1754     return false;
1755   }
1756   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1757   pthread_mutex_unlock(&cond_mutex);
1758   if (isValid()) {
1759     bool ret = (bool)atoi(private_response);
1760     lives_free(private_response);
1761     if (ret) invalidate();
1762     return ret;
1763   }
1764   return false;
1765 }
1766 
1767 
1768 ///////////////////////////////////////////////////////////////////
1769 /// multitrack
1770 
multitrack(livesApp * lives)1771 multitrack::multitrack(livesApp *lives) {
1772   m_lives = lives;
1773 }
1774 
1775 
isValid() const1776 bool multitrack::isValid() const {
1777   return m_lives != NULL && m_lives->m_id != 0l && m_lives->status() != LIVES_STATUS_NOTREADY;
1778 }
1779 
1780 
isActive() const1781 bool multitrack::isActive() const {
1782   return (isValid() && mainw->multitrack != NULL);
1783 }
1784 
1785 
currentTime() const1786 double multitrack::currentTime() const {
1787   if (!isActive()) return 0.;
1788   return m_lives->m_player->videoPlaybackTime();
1789 }
1790 
1791 
setCurrentTime(double time) const1792 double multitrack::setCurrentTime(double time) const {
1793   if (!isActive() || !m_lives->isReady()) return currentTime();
1794   return m_lives->m_player->setPlaybackStartTime(time);
1795 }
1796 
1797 
insertBlock(clip c,bool ign_sel,bool without_audio) const1798 block multitrack::insertBlock(clip c, bool ign_sel, bool without_audio) const {
1799   if (!isActive()) return block();
1800   if (!c.isValid()) return block();
1801   if (!m_lives->isReady()) return block();
1802 
1803   int clipno = cnum_for_uid(c.m_uid);
1804 
1805   spinning = true;
1806   msg_id = lives_random();
1807   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1808   pthread_mutex_lock(&cond_mutex);
1809   if (!idle_insert_block(clipno, ign_sel, !without_audio, msg_id)) {
1810     pthread_mutex_unlock(&cond_mutex);
1811     spinning = false;
1812     m_lives->removeCallback(cbid);
1813     return block();
1814   }
1815   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1816   pthread_mutex_unlock(&cond_mutex);
1817   if (isValid()) {
1818     ulong uid = strtoul(private_response, NULL, 10);
1819     lives_free(private_response);
1820     return block(const_cast<multitrack *>(this), uid);
1821   }
1822   return block();
1823 }
1824 
1825 
wipeLayout(bool force) const1826 livesString multitrack::wipeLayout(bool force) const {
1827   livesString emptystr;
1828   if (!isActive()) return emptystr;
1829   if (!m_lives->isReady()) return emptystr;
1830 
1831   spinning = true;
1832   msg_id = lives_random();
1833   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1834 
1835   pthread_mutex_lock(&cond_mutex);
1836   if (!idle_wipe_layout(force,  msg_id)) {
1837     pthread_mutex_unlock(&cond_mutex);
1838     spinning = false;
1839     m_lives->removeCallback(cbid);
1840     return emptystr;
1841   }
1842   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1843   pthread_mutex_unlock(&cond_mutex);
1844   if (isValid()) {
1845     livesString str(private_response, LIVES_CHAR_ENCODING_UTF8);
1846     lives_free(private_response);
1847     return str;
1848   }
1849   return emptystr;
1850 }
1851 
1852 
chooseLayout() const1853 livesString multitrack::chooseLayout() const {
1854   livesString emptystr;
1855   if (!isActive()) return emptystr;
1856   if (!m_lives->isReady()) return emptystr;
1857 
1858   spinning = true;
1859   msg_id = lives_random();
1860   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1861 
1862   pthread_mutex_lock(&cond_mutex);
1863   if (!idle_choose_layout(msg_id)) {
1864     pthread_mutex_unlock(&cond_mutex);
1865     spinning = false;
1866     m_lives->removeCallback(cbid);
1867     return emptystr;
1868   }
1869   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1870   pthread_mutex_unlock(&cond_mutex);
1871   if (isValid()) {
1872     livesString str(private_response, LIVES_CHAR_ENCODING_UTF8);
1873     lives_free(private_response);
1874     return str;
1875   }
1876   return emptystr;
1877 }
1878 
1879 
availableLayouts() const1880 livesStringList multitrack::availableLayouts() const {
1881   livesStringList list;
1882   if (!isValid()) return list;
1883   LiVESList *layoutlist = mainw->current_layouts_map;
1884   while (layoutlist != NULL) {
1885     char *data = repl_workdir((const char *)layoutlist->data, FALSE);
1886     list.push_back(livesString(data, LIVES_CHAR_ENCODING_FILESYSTEM).toEncoding(LIVES_CHAR_ENCODING_UTF8));
1887     lives_free(data);
1888     layoutlist = layoutlist->next;
1889   }
1890   return list;
1891 }
1892 
1893 
reloadLayout(livesString layoutname) const1894 bool multitrack::reloadLayout(livesString layoutname) const {
1895   if (!isActive()) return false;
1896   if (!m_lives->isReady()) return false;
1897 
1898   spinning = true;
1899   msg_id = lives_random();
1900   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1901 
1902   pthread_mutex_lock(&cond_mutex);
1903   if (!idle_reload_layout(layoutname.toEncoding(LIVES_CHAR_ENCODING_FILESYSTEM).c_str(), msg_id)) {
1904     pthread_mutex_unlock(&cond_mutex);
1905     spinning = false;
1906     m_lives->removeCallback(cbid);
1907     return false;
1908   }
1909   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1910   pthread_mutex_unlock(&cond_mutex);
1911   if (isValid()) {
1912     bool ret = (bool)atoi(private_response);
1913     lives_free(private_response);
1914     return ret;
1915   }
1916   return false;
1917 }
1918 
1919 
saveLayout(livesString name) const1920 livesString multitrack::saveLayout(livesString name) const {
1921   livesString emptystr;
1922   if (!isActive()) return emptystr;
1923   if (!m_lives->isReady()) return emptystr;
1924 
1925   spinning = true;
1926   msg_id = lives_random();
1927   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1928 
1929   pthread_mutex_lock(&cond_mutex);
1930   if (!idle_save_layout(name.toEncoding(LIVES_CHAR_ENCODING_FILESYSTEM).c_str(), msg_id)) {
1931     pthread_mutex_unlock(&cond_mutex);
1932     spinning = false;
1933     m_lives->removeCallback(cbid);
1934     return emptystr;
1935   }
1936   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1937   pthread_mutex_unlock(&cond_mutex);
1938   if (isValid()) {
1939     char *lname = strdup(private_response);
1940     lives_free(private_response);
1941     return livesString(lname).toEncoding(LIVES_CHAR_ENCODING_UTF8);
1942   }
1943   return emptystr;
1944 }
1945 
1946 
saveLayout() const1947 livesString multitrack::saveLayout() const {
1948   livesString emptystr;
1949   if (!isActive()) return emptystr;
1950   if (!m_lives->isReady()) return emptystr;
1951 
1952   spinning = true;
1953   msg_id = lives_random();
1954   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1955 
1956   pthread_mutex_lock(&cond_mutex);
1957   if (!idle_save_layout(NULL, msg_id)) {
1958     pthread_mutex_unlock(&cond_mutex);
1959     spinning = false;
1960     m_lives->removeCallback(cbid);
1961     return emptystr;
1962   }
1963   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1964   pthread_mutex_unlock(&cond_mutex);
1965   if (isValid()) {
1966     char *lname = strdup(private_response);
1967     lives_free(private_response);
1968     return livesString(lname).toEncoding(LIVES_CHAR_ENCODING_UTF8);
1969   }
1970   return emptystr;
1971 }
1972 
1973 
render(bool with_audio,bool normalise_audio) const1974 clip multitrack::render(bool with_audio, bool normalise_audio) const {
1975   clip c;
1976   if (!isActive()) return c;
1977   if (!m_lives->isReady()) return c;
1978 
1979   spinning = true;
1980   msg_id = lives_random();
1981   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
1982 
1983   pthread_mutex_lock(&cond_mutex);
1984   if (!idle_render_layout(with_audio, normalise_audio, msg_id)) {
1985     pthread_mutex_unlock(&cond_mutex);
1986     spinning = false;
1987     m_lives->removeCallback(cbid);
1988     return c;
1989   }
1990   while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
1991   pthread_mutex_unlock(&cond_mutex);
1992   if (isValid()) {
1993     ulong uid = strtoul(private_response, NULL, 10);
1994     c = clip(uid, m_lives);
1995     lives_free(private_response);
1996   }
1997   return c;
1998 }
1999 
2000 
autoTransition() const2001 effect multitrack::autoTransition() const {
2002   effect e;
2003   if (!m_lives->isValid() || m_lives->status() == LIVES_STATUS_NOTREADY) return e;
2004   if (::prefs->atrans_fx == -1) return e;
2005   e = effect(m_lives, ::prefs->atrans_fx);
2006   return e;
2007 }
2008 
2009 
setAutoTransition(effect autotrans) const2010 bool multitrack::setAutoTransition(effect autotrans) const {
2011   if (!m_lives->isValid()) return false;
2012   if (!autotrans.isValid()) return disableAutoTransition();
2013 
2014   // check if is transition
2015   if (get_transition_param(get_weed_filter(autotrans.m_idx), FALSE) == -1) return false;
2016 
2017   if (m_lives->status() != LIVES_STATUS_READY && m_lives->status() != LIVES_STATUS_PLAYING) return false;
2018   mt_set_autotrans(autotrans.m_idx);
2019   return true;
2020 }
2021 
2022 
disableAutoTransition() const2023 bool multitrack::disableAutoTransition() const {
2024   if (!m_lives->isValid()) return false;
2025   if (m_lives->status() != LIVES_STATUS_READY && m_lives->status() != LIVES_STATUS_PLAYING) return false;
2026   mt_set_autotrans(-1);
2027   return true;
2028 }
2029 
2030 
currentTrack() const2031 int multitrack::currentTrack() const {
2032   if (!isActive()) return 0;
2033   return mainw->multitrack->current_track;
2034 }
2035 
2036 
setCurrentTrack(int track) const2037 bool multitrack::setCurrentTrack(int track) const {
2038   if (m_lives->status() == LIVES_STATUS_PROCESSING) return false;
2039   if (!isActive()) return false;
2040 
2041   spinning = true;
2042   msg_id = lives_random();
2043   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
2044   pthread_mutex_lock(&cond_mutex);
2045   if (!idle_mt_set_track(track, msg_id)) {
2046     pthread_mutex_unlock(&cond_mutex);
2047     spinning = false;
2048     m_lives->removeCallback(cbid);
2049   } else {
2050     while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
2051     pthread_mutex_unlock(&cond_mutex);
2052     if (isValid()) {
2053       bool ret = (bool)(atoi(private_response));
2054       lives_free(private_response);
2055       return ret;
2056     }
2057   }
2058   return false;
2059 }
2060 
2061 
trackLabel(int track) const2062 livesString multitrack::trackLabel(int track) const {
2063   livesString emptystr;
2064   if (!isActive()) return emptystr;
2065 
2066   if (mt_track_is_video(mainw->multitrack, track))
2067     return livesString(get_track_name(mainw->multitrack, track, FALSE), LIVES_CHAR_ENCODING_UTF8);
2068   if (mt_track_is_audio(mainw->multitrack, track))
2069     return livesString(get_track_name(mainw->multitrack, track, TRUE), LIVES_CHAR_ENCODING_UTF8);
2070 
2071   return emptystr;
2072 }
2073 
2074 
FPS() const2075 double multitrack::FPS() const {
2076   if (!isActive()) return 0.;
2077   return mainw->multitrack->fps;
2078 }
2079 
2080 
setTrackLabel(int track,livesString label) const2081 bool multitrack::setTrackLabel(int track, livesString label) const {
2082   if (m_lives->status() == LIVES_STATUS_PROCESSING) return false;
2083   if (!isActive()) return false;
2084 
2085   spinning = true;
2086   msg_id = lives_random();
2087   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
2088   pthread_mutex_lock(&cond_mutex);
2089   if (!idle_set_track_label(track, label.toEncoding(LIVES_CHAR_ENCODING_UTF8).c_str(), msg_id)) {
2090     pthread_mutex_unlock(&cond_mutex);
2091     spinning = false;
2092     m_lives->removeCallback(cbid);
2093   } else {
2094     while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
2095     pthread_mutex_unlock(&cond_mutex);
2096     if (isValid()) {
2097       bool ret = (bool)(atoi(private_response));
2098       lives_free(private_response);
2099       return ret;
2100     }
2101   }
2102   return false;
2103 }
2104 
2105 
gravity() const2106 lives_gravity_t multitrack::gravity() const {
2107   if (!isActive()) return LIVES_GRAVITY_NORMAL;
2108   switch (mainw->multitrack->opts.grav_mode) {
2109   case GRAV_MODE_LEFT:
2110     return LIVES_GRAVITY_LEFT;
2111   case GRAV_MODE_RIGHT:
2112     return LIVES_GRAVITY_RIGHT;
2113   default:
2114     return LIVES_GRAVITY_NORMAL;
2115   }
2116 }
2117 
2118 
setGravity(lives_gravity_t grav) const2119 lives_gravity_t multitrack::setGravity(lives_gravity_t grav) const {
2120   if (m_lives->status() == LIVES_STATUS_PROCESSING) return gravity();
2121   if (!isActive()) return gravity();
2122 
2123   spinning = true;
2124   msg_id = lives_random();
2125   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
2126   pthread_mutex_lock(&cond_mutex);
2127   if (!idle_set_gravity((int)grav, msg_id)) {
2128     pthread_mutex_unlock(&cond_mutex);
2129     spinning = false;
2130     m_lives->removeCallback(cbid);
2131   } else {
2132     while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
2133     pthread_mutex_unlock(&cond_mutex);
2134     if (isValid()) {
2135       lives_free(private_response);
2136     }
2137   }
2138   return gravity();
2139 }
2140 
2141 
insertMode() const2142 lives_insert_mode_t multitrack::insertMode() const {
2143   if (!isActive()) return LIVES_INSERT_MODE_NORMAL;
2144   switch (mainw->multitrack->opts.insert_mode) {
2145   default:
2146     return LIVES_INSERT_MODE_NORMAL;
2147   }
2148 }
2149 
2150 
setInsertMode(lives_insert_mode_t mode) const2151 lives_insert_mode_t multitrack::setInsertMode(lives_insert_mode_t mode) const {
2152   if (m_lives->status() == LIVES_STATUS_PROCESSING) return insertMode();
2153   if (!isActive()) return insertMode();
2154 
2155   spinning = true;
2156   msg_id = lives_random();
2157   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
2158   pthread_mutex_lock(&cond_mutex);
2159   if (!idle_set_insert_mode((int)mode, msg_id)) {
2160     pthread_mutex_unlock(&cond_mutex);
2161     spinning = false;
2162     m_lives->removeCallback(cbid);
2163   } else {
2164     while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
2165     pthread_mutex_unlock(&cond_mutex);
2166     if (isValid()) {
2167       lives_free(private_response);
2168     }
2169   }
2170   return insertMode();
2171 }
2172 
2173 
numAudioTracks() const2174 int multitrack::numAudioTracks() const {
2175   if (!isActive()) return 0;
2176   return mainw->multitrack->opts.back_audio_tracks;
2177 }
2178 
2179 
numVideoTracks() const2180 int multitrack::numVideoTracks() const {
2181   if (!isActive()) return 0;
2182   return mainw->multitrack->num_video_tracks;
2183 }
2184 
2185 
addVideoTrack(bool in_front) const2186 int multitrack::addVideoTrack(bool in_front) const {
2187   if (!isActive()) return -1;
2188   if (m_lives->isReady()) return -1.;
2189 
2190   spinning = true;
2191   msg_id = lives_random();
2192   ulong cbid = m_lives->addCallback(LIVES_CALLBACK_PRIVATE, private_cb, NULL);
2193   pthread_mutex_lock(&cond_mutex);
2194   if (!idle_insert_vtrack(in_front, msg_id)) {
2195     pthread_mutex_unlock(&cond_mutex);
2196     spinning = false;
2197     m_lives->removeCallback(cbid);
2198   } else {
2199     while (spinning) pthread_cond_wait(&cond_done, &cond_mutex);
2200     pthread_mutex_unlock(&cond_mutex);
2201     if (isValid()) {
2202       int tnum = atoi(private_response);
2203       lives_free(private_response);
2204       return tnum;
2205     }
2206   }
2207   return -1;
2208 }
2209 
2210 
2211 //////////////////////////////////////////////
2212 
2213 ////// prefs
2214 
2215 namespace prefs {
currentVideoLoadDir(const livesApp & lives)2216 livesString currentVideoLoadDir(const livesApp &lives) {
2217   if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return livesString();
2218   return livesString(mainw->vid_load_dir, LIVES_CHAR_ENCODING_UTF8);
2219 }
2220 
currentAudioDir(const livesApp & lives)2221 livesString currentAudioDir(const livesApp &lives) {
2222   if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return livesString();
2223   return livesString(mainw->audio_dir, LIVES_CHAR_ENCODING_UTF8);
2224 }
2225 
tmpDir(const livesApp & lives)2226 livesString tmpDir(const livesApp &lives) {
2227   if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return livesString();
2228   return livesString(::prefs->workdir, LIVES_CHAR_ENCODING_FILESYSTEM);
2229 }
2230 
audioSource(const livesApp & lives)2231 lives_audio_source_t audioSource(const livesApp &lives) {
2232   if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return LIVES_AUDIO_SOURCE_UNKNOWN;
2233   if (::prefs->audio_src == AUDIO_SRC_EXT) return LIVES_AUDIO_SOURCE_EXTERNAL;
2234   return LIVES_AUDIO_SOURCE_INTERNAL;
2235 }
2236 
setAudioSource(const livesApp & lives,lives_audio_source_t asrc)2237 bool setAudioSource(const livesApp &lives, lives_audio_source_t asrc) {
2238   if (!lives.isReady()) return false;
2239   return lives.setPref(PREF_REC_EXT_AUDIO, (bool)(asrc == LIVES_AUDIO_SOURCE_EXTERNAL));
2240 }
2241 
audioPlayer(const livesApp & lives)2242 lives_audio_player_t audioPlayer(const livesApp &lives) {
2243   if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return LIVES_AUDIO_PLAYER_UNKNOWN;
2244   if (::prefs->audio_player == AUD_PLAYER_SOX) return LIVES_AUDIO_PLAYER_SOX;
2245   if (::prefs->audio_player == AUD_PLAYER_JACK) return LIVES_AUDIO_PLAYER_JACK;
2246   if (::prefs->audio_player == AUD_PLAYER_PULSE) return LIVES_AUDIO_PLAYER_PULSE;
2247   return LIVES_AUDIO_PLAYER_UNKNOWN;
2248 }
2249 
audioPlayerRate(const livesApp & lives)2250 int audioPlayerRate(const livesApp &lives) {
2251   if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return 0;
2252 #ifdef ENABLE_JACK
2253   if (::prefs->audio_player == AUD_PLAYER_JACK && mainw->jackd != NULL) return mainw->jackd->sample_out_rate;
2254 #endif
2255 #ifdef HAVE_PULSE_AUDIO
2256   if (::prefs->audio_player == AUD_PLAYER_PULSE && mainw->pulsed != NULL) return mainw->pulsed->out_arate;
2257 #endif
2258   return 0;
2259 }
2260 
isRealtimeAudioPlayer(lives_audio_player_t player_type)2261 bool isRealtimeAudioPlayer(lives_audio_player_t player_type) {
2262   int ptype = AUD_PLAYER_NONE;
2263   if (player_type == LIVES_AUDIO_PLAYER_SOX) ptype = AUD_PLAYER_SOX;
2264   else if (player_type == LIVES_AUDIO_PLAYER_JACK) ptype = AUD_PLAYER_JACK;
2265   else if (player_type == LIVES_AUDIO_PLAYER_PULSE) ptype = AUD_PLAYER_PULSE;
2266   return is_realtime_aplayer(ptype);
2267 }
2268 
rteKeysVirtual(const livesApp & lives)2269 int rteKeysVirtual(const livesApp &lives) {
2270   if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return 0;
2271   return ::prefs->rte_keys_virtual;
2272 }
2273 
maxFPS(const livesApp & lives)2274 double maxFPS(const livesApp &lives) {
2275   if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return 0.;
2276   return FPS_MAX;
2277 }
2278 
audioFollowsVideoChanges(const livesApp & lives)2279 bool audioFollowsVideoChanges(const livesApp &lives) {
2280   if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return false;
2281   return ::prefs->audio_opts & AUDIO_OPTS_FOLLOW_CLIPS;
2282 }
2283 
audioFollowsFPSChanges(const livesApp & lives)2284 bool audioFollowsFPSChanges(const livesApp &lives) {
2285   if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return false;
2286   return ::prefs->audio_opts & AUDIO_OPTS_FOLLOW_FPS;
2287 }
2288 
setAudioFollowsVideoChanges(const livesApp & lives,bool setting)2289 bool setAudioFollowsVideoChanges(const livesApp &lives, bool setting) {
2290   if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return false;
2291   return lives.setPref(PREF_AUDIO_OPTS, AUDIO_OPTS_FOLLOW_CLIPS, setting);
2292 }
2293 
setAudioFollowsFPSChanges(const livesApp & lives,bool setting)2294 bool setAudioFollowsFPSChanges(const livesApp &lives, bool setting) {
2295   if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return false;
2296   return lives.setPref(PREF_AUDIO_OPTS, AUDIO_OPTS_FOLLOW_FPS, setting);
2297 }
2298 
sepWinSticky(const livesApp & lives)2299 bool sepWinSticky(const livesApp &lives) {
2300   if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return false;
2301   return ::prefs->sepwin_type == SEPWIN_TYPE_STICKY;
2302 }
2303 
setSepWinSticky(const livesApp & lives,bool setting)2304 bool setSepWinSticky(const livesApp &lives, bool setting) {
2305   if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return false;
2306   return lives.setPref(PREF_SEPWIN_TYPE, setting ? SEPWIN_TYPE_STICKY : SEPWIN_TYPE_NON_STICKY);
2307 }
2308 
mtExitRender(const livesApp & lives)2309 bool mtExitRender(const livesApp &lives) {
2310   if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return false;
2311   return ::prefs->mt_exit_render;
2312 }
2313 
setMtExitRender(const livesApp & lives,bool setting)2314 bool setMtExitRender(const livesApp &lives, bool setting) {
2315   if (!lives.isValid() || lives.status() == LIVES_STATUS_NOTREADY) return false;
2316   return lives.setPref(PREF_MT_EXIT_RENDER, setting);
2317 }
2318 }
2319 }
2320 
2321 
2322 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2323 
2324 #ifndef DOXYGEN_SKIP
2325 
binding_cb(lives_callback_t cb_type,const char * msgstring,ulong id)2326 void binding_cb(lives_callback_t cb_type, const char *msgstring, ulong id) {
2327   bool ret;
2328   lives::livesApp *lapp;
2329 
2330   if (cb_type == LIVES_CALLBACK_OBJECT_DESTROYED) lapp = (lives::livesApp *)id;
2331   else lapp = lives::find_instance_for_id(id);
2332 
2333   if (lapp == NULL) return;
2334 
2335   pthread_mutex_lock(&spin_mutex); // lock mutex so that new callbacks cannot be added yet
2336 
2337   lives::closureList cl = lapp->closures();
2338 
2339   lives::closureListIterator it = cl.begin();
2340   while (it != cl.end()) {
2341     if ((*it)->cb_type == cb_type) {
2342       switch (cb_type) {
2343       case LIVES_CALLBACK_MODE_CHANGED: {
2344         lives::modeChangedInfo info;
2345         info.mode = (lives_interface_mode_t)atoi(msgstring);
2346         lives::modeChanged_callback_f fn = (lives::modeChanged_callback_f)((*it)->func);
2347         ret = (fn)((*it)->object, &info, (*it)->data);
2348       }
2349       break;
2350       case LIVES_CALLBACK_APP_QUIT: {
2351         // TODO !! test
2352         lives::appQuitInfo info;
2353         info.signum = atoi(msgstring);
2354         lives::appQuit_callback_f fn = (lives::appQuit_callback_f)((*it)->func);
2355         lapp->invalidate();
2356         ret = (fn)((*it)->object, &info, (*it)->data);
2357         spinning = false;
2358       }
2359       break;
2360       case LIVES_CALLBACK_OBJECT_DESTROYED: {
2361         lives::objectDestroyed_callback_f fn = (lives::objectDestroyed_callback_f)((*it)->func);
2362         ret = (fn)((*it)->object, (*it)->data);
2363       }
2364       break;
2365       case LIVES_CALLBACK_PRIVATE: {
2366         // private event type
2367         lives::_privateInfo info;
2368         char *endptr;
2369         info.id = strtoul(msgstring, &endptr, 10);
2370         info.response = endptr + 1;
2371         lives::private_callback_f fn = (lives::private_callback_f)((*it)->func);
2372         ret = (fn)(&info, (*it)->data);
2373       }
2374       break;
2375       default:
2376         ++it;
2377         continue;
2378       }
2379       if (!ret) {
2380         delete *it;
2381         it = cl.erase(it);
2382         lapp->setClosures(cl);
2383         continue;
2384       }
2385     }
2386     ++it;
2387   }
2388 
2389   pthread_mutex_unlock(&spin_mutex);
2390 }
2391 
2392 #endif // doxygen_skip
2393