1 #include <sstream>
2 #include <string>
3 #include <utility>
4 #include <stdexcept>
5 #include <stdint.h>
6 #if defined(_WIN32) || defined(_CYGWIN_)
7 #define UUID UUID_
8 #include <windows.h>
9 #undef UUID
10 #else
11 #include <dlfcn.h>
12 #endif
13 #include <map>
14 #include <type_traits>
15 #include <functional>
16 
17 // #include "tfxparam.h"
18 #include <toonzqt/addfxcontextmenu.h>  // as receiver
19 #include "tenv.h"
20 #include "../include/toonzqt/fxsettings.h"
21 #include "toonz/tcolumnfx.h"
22 
23 #include "pluginhost.h"
24 #include "toonz_plugin.h"
25 #include "toonz_hostif.h"
26 #include "toonz_params.h"
27 #include "plugin_tile_interface.h"
28 #include "plugin_port_interface.h"
29 #include "plugin_fxnode_interface.h"
30 #include "plugin_param_interface.h"
31 #include "plugin_param_view_interface.h"
32 #include "plugin_ui_page_interface.h"
33 #include "plugin_utilities.h"
34 #include <QFileInfo>
35 #include <QDir>
36 #include <QLabel>
37 #include <QHBoxLayout>
38 #include <QFormLayout>
39 //#include <QtConcurrent>
40 
41 #include "plugin_param_traits.h"
42 #include "../include/toonzqt/pluginloader.h"
43 
44 using namespace toonz;  // plugin namespace
45 
46 extern std::map<std::string, PluginInformation *> plugin_dict_;
47 
48 /*
49   PluginLoadController が main thread queue を使うことと,
50   QThread で他スレッドの待ち合わせがしにくい(sendor thread が QThread::wait()
51   でブロックしていると emit signal が処理できずデッドロックする)ので、
52   大人しく polling にした.
53  */
load_entries(const std::string & basepath)54 bool PluginLoader::load_entries(const std::string &basepath) {
55   static PluginLoadController *aw = NULL; /* main() から一度だけ呼ばれる */
56   if (!aw) {
57     aw = new PluginLoadController(basepath, NULL);
58   }
59   bool ret = aw->wait(16 /* ms */);
60   if (ret) aw = NULL; /* deleteLater で消えるはず */
61   return ret;
62 }
63 
create_host(const std::string & fxId)64 TFx *PluginLoader::create_host(const std::string &fxId) {
65   std::string id = fxId.substr(5);
66   auto it        = plugin_dict_.find(id);
67   if (it != plugin_dict_.end()) {
68     auto plugin = new RasterFxPluginHost(it->second);
69     plugin->notify();
70     return plugin;
71   }
72   return NULL;
73 }
74 
create_menu_items(std::function<void (QTreeWidgetItem *)> && l1_handler,std::function<void (QTreeWidgetItem *)> && l2_handler)75 std::map<std::string, QTreeWidgetItem *> PluginLoader::create_menu_items(
76     std::function<void(QTreeWidgetItem *)> &&l1_handler,
77     std::function<void(QTreeWidgetItem *)> &&l2_handler) {
78   std::map<std::string, QTreeWidgetItem *> vendors;
79   for (auto plugin : plugin_dict_) {
80     PluginDescription *desc = plugin.second->desc_;
81     if (vendors.count(desc->vendor_) == 0) {
82       auto vendor = new QTreeWidgetItem(
83           (QTreeWidget *)NULL,
84           QStringList(QString::fromStdString(desc->vendor_)));
85       vendors.insert(std::make_pair(desc->vendor_, vendor));
86       l1_handler(vendor);
87     }
88 
89     auto vendor = vendors[desc->vendor_];
90     auto item   = new QTreeWidgetItem(
91         (QTreeWidget *)NULL, QStringList(QString::fromStdString(desc->name_)));
92     item->setData(0, Qt::UserRole,
93                   QVariant("_plg_" + QString::fromStdString(desc->id_)));
94     l2_handler(item);
95     vendor->addChild(item);
96   }
97   return vendors;
98 }
99 
100 static bool copy_rendering_setting(toonz_rendering_setting_t *dst,
101                                    const TRenderSettings &src);
102 
103 class PluginSetupMessage final : public TThread::Message {
104   PluginInformation *pi_;
105 
106 public:
PluginSetupMessage(PluginInformation * pi)107   PluginSetupMessage(PluginInformation *pi) : pi_(pi) {}
108 
onDeliver()109   void onDeliver() override {
110     RasterFxPluginHost *fx = new RasterFxPluginHost(pi_);
111     if (pi_ && pi_->handler_) {
112       pi_->handler_->setup(fx);
113       /* fx は pi のラッパーとしてのみ構築されており、即座に削除される. 実
114          instance に引き継がれないので ここで createParam()
115          等を呼び出しても意味がない.
116          ここで createParamsByDesc() などを呼び出しても、 instance の parameter
117          は 0 になる.  */
118     }
119     delete fx;
120   }
121 
clone() const122   TThread::Message *clone() const override {
123     return new PluginSetupMessage(*this);
124   }
125 };
126 
~PluginInformation()127 PluginInformation::~PluginInformation() {
128   if (library_) {
129     if (library_.use_count() == 1 && fin_) {
130       fin_();
131     }
132   }
133 }
134 
add_ref()135 void PluginInformation::add_ref() { ++ref_count_; }
136 
release()137 void PluginInformation::release() {
138   if (--ref_count_ == 0) {
139     delete this;
140   }
141 }
142 
PluginDescription(const plugin_probe_t * const probe)143 PluginDescription::PluginDescription(const plugin_probe_t *const probe) {
144   name_       = probe->name ? probe->name : "unnamed-plugin";
145   vendor_     = probe->vendor ? probe->vendor : "";
146   id_         = probe->id ? probe->id : "unnamed-plugin.plugin";
147   note_       = probe->note ? probe->note : "";
148   url_        = probe->helpurl ? probe->helpurl : "";
149   clss_       = probe->clss;
150   fullname_   = id_ + "$" + name_ + "$" + vendor_;
151   plugin_ver_ = probe->plugin_ver;
152 }
153 
RasterFxPluginHost(PluginInformation * pinfo)154 RasterFxPluginHost::RasterFxPluginHost(PluginInformation *pinfo)
155     : TRasterFx(), pi_(pinfo), user_data_(nullptr) {
156   pi_->add_ref();
157 }
158 
create_param_view(toonz_node_handle_t node,toonz_param_view_handle_t * view)159 static int create_param_view(toonz_node_handle_t node,
160                              toonz_param_view_handle_t *view) {
161   ParamView *p = NULL;
162   try {
163     RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
164     if (!fx) {
165       printf("create_param_view: invalid toonz_node_handle_t\n");
166       return TOONZ_ERROR_INVALID_HANDLE;
167     }
168 
169     if ((p = fx->createParamView())) {
170       *view = p;
171     } else {
172       printf("create_param_view: invalid param name");
173       return TOONZ_ERROR_FAILED_TO_CREATE;
174     }
175   } catch (const std::exception &e) {
176     printf("create_param_view: exception: %s\n", e.what());
177     delete p;
178     return TOONZ_ERROR_UNKNOWN;
179   }
180   return TOONZ_OK;
181 }
182 
setup_input_port(toonz_node_handle_t node,const char * name,int type)183 static int setup_input_port(toonz_node_handle_t node, const char *name,
184                             int type) {
185   try {
186     RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
187     if (!fx) return TOONZ_ERROR_INVALID_HANDLE;
188     if (!fx->addPortDesc({true, name, type})) {
189       printf("add_input_port: failed to add: already have\n");
190       return TOONZ_ERROR_BUSY;
191     }
192   } catch (const std::exception &e) {
193     printf("setup_putput_port: exception: %s\n", e.what());
194     return TOONZ_ERROR_UNKNOWN;
195   }
196   return TOONZ_OK;
197 }
198 
setup_output_port(toonz_node_handle_t node,const char * name,int type)199 static int setup_output_port(toonz_node_handle_t node, const char *name,
200                              int type) {
201   try {
202     RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
203     if (!fx) return TOONZ_ERROR_INVALID_HANDLE;
204     if (!fx->addPortDesc({false, name, type})) {
205       printf("add_input_port: failed to add: already have\n");
206       return TOONZ_ERROR_BUSY;
207     }
208   } catch (const std::exception &e) {
209     printf("setup_putput_port: exception: %s\n", e.what());
210     return TOONZ_ERROR_UNKNOWN;
211   }
212   return TOONZ_OK;
213 }
214 
add_input_port(toonz_node_handle_t node,const char * name,int type,toonz_port_handle_t * port)215 static int add_input_port(toonz_node_handle_t node, const char *name, int type,
216                           toonz_port_handle_t *port) {
217   try {
218     RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
219     if (!fx) return TOONZ_ERROR_INVALID_HANDLE;
220     auto p = std::make_shared<TRasterFxPort>();
221     /* TRasterFxPort は non-copyable なスマートポインタなのでポインタで引き回す
222      */
223     if (!fx->addInputPort(name, p)) {  // overloaded version
224       printf("add_input_port: failed to add: already have\n");
225       return TOONZ_ERROR_BUSY;
226     }
227     *port = p.get();
228   } catch (const std::exception &e) {
229     printf("add_input_port: exception: %s\n", e.what());
230     return TOONZ_ERROR_UNKNOWN;
231   }
232   return TOONZ_OK;
233 }
234 
get_input_port(toonz_node_handle_t node,const char * name,toonz_port_handle_t * port)235 static int get_input_port(toonz_node_handle_t node, const char *name,
236                           toonz_port_handle_t *port) {
237   if (!(node && port)) {
238     return TOONZ_ERROR_NULL;
239   }
240 
241   RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
242   std::string portName(name);
243   TFxPort *tfxport = fx->getInputPort(portName);
244   if (!tfxport) {
245     return TOONZ_ERROR_NOT_FOUND;
246   }
247   *port = tfxport;
248 
249   return TOONZ_OK;
250 }
251 
add_output_port(toonz_node_handle_t node,const char * name,int type,toonz_port_handle_t * port)252 static int add_output_port(toonz_node_handle_t node, const char *name, int type,
253                            toonz_port_handle_t *port) {
254   TRasterFxPort *p = NULL;
255   try {
256     RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
257     if (!fx) return TOONZ_ERROR_INVALID_HANDLE;
258     p = new TRasterFxPort();
259     /* TRasterFxPort は non-copyable なスマートポインタなのでポインタで引き回す
260      */
261     if (fx->addOutputPort(name, p)) {  // overloaded version
262       delete p;
263       return TOONZ_ERROR_BUSY;
264     }
265     *port = p;
266   } catch (const std::exception &) {
267     delete p;
268     return TOONZ_ERROR_UNKNOWN;
269   }
270   return TOONZ_OK;
271 }
272 
get_rect(toonz_rect_t * rect,double * x0,double * y0,double * x1,double * y1)273 static int get_rect(toonz_rect_t *rect, double *x0, double *y0, double *x1,
274                     double *y1) {
275   if (!(rect && x0 && y0 && x1 && y1)) {
276     return -2;
277   }
278   *x0 = rect->x0;
279   *y0 = rect->y0;
280   *x1 = rect->x1;
281   *y1 = rect->y1;
282   return 0;
283 }
284 
set_rect(toonz_rect_t * rect,double x0,double y0,double x1,double y1)285 static int set_rect(toonz_rect_t *rect, double x0, double y0, double x1,
286                     double y1) {
287   if (!rect) {
288     return -2;
289   }
290   rect->x0 = x0;
291   rect->y0 = y0;
292   rect->x1 = x1;
293   rect->y1 = y1;
294   return 0;
295 }
296 
add_preference(toonz_node_handle_t node,const char * name,toonz_ui_page_handle_t * ui)297 static int add_preference(toonz_node_handle_t node, const char *name,
298                           toonz_ui_page_handle_t *ui) {
299   UIPage *p = NULL;
300   try {
301     RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
302     if (!fx) {
303       printf("add_preference: invalid toonz_node_handle_t\n");
304       return TOONZ_ERROR_INVALID_HANDLE;
305     }
306 
307     if ((p = fx->createUIPage(name))) {
308       *ui = p;
309     } else {
310       printf("add_preference: failed to get UIPage\n");
311       return TOONZ_ERROR_FAILED_TO_CREATE;
312     }
313   } catch (const std::exception &e) {
314     printf("add_preference: exception: %s\n", e.what());
315     delete p;
316     return TOONZ_ERROR_UNKNOWN;
317   }
318   return TOONZ_OK;
319 }
320 
add_param(toonz_node_handle_t node,const char * name,int type,toonz_param_handle_t * param)321 static int add_param(toonz_node_handle_t node, const char *name, int type,
322                      toonz_param_handle_t *param) {
323   Param *p = NULL;
324   try {
325     RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
326     if (!fx) {
327       printf("add_param: invalid toonz_node_handle_t\n");
328       return TOONZ_ERROR_INVALID_HANDLE;
329     }
330 
331     if ((p = fx->createParam(name, toonz_param_type_enum(type)))) {
332       *param = p;
333     } else {
334       printf("add_param: invalid type");
335       return TOONZ_ERROR_FAILED_TO_CREATE;
336     }
337   } catch (const std::exception &e) {
338     printf("add_param: exception: %s\n", e.what());
339     delete p;
340     return TOONZ_ERROR_UNKNOWN;
341   }
342   return TOONZ_OK;
343 }
344 
get_param(toonz_node_handle_t node,const char * name,toonz_param_handle_t * param)345 static int get_param(toonz_node_handle_t node, const char *name,
346                      toonz_param_handle_t *param) {
347   try {
348     RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
349     if (!fx) {
350       printf("get_param: invalid toonz_node_handle_t\n");
351       return TOONZ_ERROR_INVALID_HANDLE;
352     }
353 
354     if (Param *p = fx->getParam(name)) {
355       *param = p;
356     } else {
357       printf("get_param: invalid type");
358       return TOONZ_ERROR_NOT_FOUND;
359     }
360   } catch (const std::exception &) {
361   }
362   return TOONZ_OK;
363 }
364 
set_user_data(toonz_node_handle_t node,void * user_data)365 static int set_user_data(toonz_node_handle_t node, void *user_data) {
366   if (!node) {
367     return TOONZ_ERROR_NULL;
368   }
369   RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
370   fx->setUserData(user_data);
371   return TOONZ_OK;
372 }
373 
get_user_data(toonz_node_handle_t node,void ** user_data)374 static int get_user_data(toonz_node_handle_t node, void **user_data) {
375   if (!node || !user_data) {
376     return TOONZ_ERROR_NULL;
377   }
378   RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
379   *user_data             = fx->getUserData();
380   return TOONZ_OK;
381 }
382 
addPortDesc(port_description_t && desc)383 bool RasterFxPluginHost::addPortDesc(port_description_t &&desc) {
384   printf("addPortDesc: name:%s dir:%d type:%d\n", desc.name_.c_str(),
385          desc.input_, desc.type_);
386   auto ret = pi_->port_mapper_.insert(std::make_pair(desc.name_, desc));
387   return ret.second;
388 }
389 
notify()390 void RasterFxPluginHost::notify() {
391   /* 最低限必要な setup をしてから通知する */
392   QString nm = QString::fromStdString(pi_->desc_->name_.c_str());
393   setName(nm.toStdWString());
394 
395   createParamsByDesc();
396   createPortsByDesc();
397 
398   if (pi_ && pi_->handler_->create) pi_->handler_->create(this);
399 }
400 
~RasterFxPluginHost()401 RasterFxPluginHost::~RasterFxPluginHost() {
402   if (pi_ && pi_->handler_->destroy) {
403     pi_->handler_->destroy(this);
404     pi_->release();
405   }
406   inputs_.clear();
407 }
408 
409 /*
410  node を click するなどの要因で頻繁に呼ばれる.
411  click した場合は FxsData::setFxs から呼ばれ、新しいインスタンスは
412  FxsData::m_fxs に入れられ、 FxsData
413  のインスタンスと同時に(大抵の場合は)即座に消される.
414  */
clone(bool recursive) const415 TFx *RasterFxPluginHost::clone(bool recursive) const {
416   RasterFxPluginHost *plugin = newInstance(pi_);
417   plugin->user_data_         = user_data_;
418   // clone ports before TFx::clone().
419   for (auto &ip : pi_->port_mapper_) {
420     if (ip.second.input_) {
421 #if 0
422       /* addInputPort() 内で行われる port owner の更新は後勝ちだが,
423          clone された新しいインスタンスのほうが先に消えてしまう場合に, 無効なポインタを示す owner が port に残ってしまう. この問題が解決したら共有できるようにしたい.
424          (このため、 plugin 空間に通知される全ての handle には一貫性がない. ただし、後から一貫性がなくなるよりは遥かにいいだろう)
425       */
426       plugin->addInputPort(getInputPortName(i), ip);
427 #else
428       plugin->addInputPort(ip.first,
429                            std::shared_ptr<TFxPort>(new TRasterFxPort));
430 #endif
431     }
432   }
433 
434   printf("recursive:%d params:%d\n", (int)recursive, (int)params_.size());
435   // clone params before TFx::clone().
436   /* ui_pages_, param_views_ は pi に移ったが createParam
437    * の呼び出しだけはしておかないと Fx Settings 構築時に assert failed になる */
438   for (auto const &param : params_) {
439     /* 古い createParam() は desc
440      * をとらず、コンストラクト時にデフォルト値を持つタイプの T*Param
441      * を再作成できない */
442     plugin->createParam(param->desc());
443   }
444 
445   return TFx::clone(plugin, recursive);
446 }
447 
newInstance(PluginInformation * pi) const448 RasterFxPluginHost *RasterFxPluginHost::newInstance(
449     PluginInformation *pi) const {
450   return new RasterFxPluginHost(pi);
451 }
452 
getDeclaration() const453 const TPersistDeclaration *RasterFxPluginHost::getDeclaration() const {
454   printf("RasterFxPluginHost::getDeclaration()\n");
455   return pi_->decl_;
456 }
457 
PluginDeclaration(PluginInformation * pi)458 PluginDeclaration::PluginDeclaration(PluginInformation *pi)
459     : TFxDeclaration(TFxInfo(pi->desc_->id_, false)), pi_(pi) {}
460 
create() const461 TPersist *PluginDeclaration::create() const {
462   RasterFxPluginHost *fx = new RasterFxPluginHost(pi_);
463   fx->notify();
464   return fx;
465 }
466 
getPluginId() const467 std::string RasterFxPluginHost::getPluginId() const { return pi_->desc_->id_; };
468 
getUserData()469 void *RasterFxPluginHost::getUserData() { return user_data_; }
470 
setUserData(void * user_data)471 void RasterFxPluginHost::setUserData(void *user_data) {
472   user_data_ = user_data;
473 }
474 
doGetBBox(double frame,TRectD & bbox,const TRenderSettings & info)475 bool RasterFxPluginHost::doGetBBox(double frame, TRectD &bbox,
476                                    const TRenderSettings &info) {
477   using namespace plugin::utils;
478   bool ret = true; /* 負論理 */
479   if (pi_ && pi_->handler_->do_get_bbox) {
480     rendering_setting_t info_;
481     copy_rendering_setting(&info_, info);
482 
483     rect_t rc;
484     copy_rect(&rc, bbox);
485 
486     ret  = ret && pi_->handler_->do_get_bbox(this, &info_, frame, &rc);
487     bbox = restore_rect(&rc);
488   }
489   return !ret;
490 }
491 
doCompute(TTile & tile,double frame,const TRenderSettings & info)492 void RasterFxPluginHost::doCompute(TTile &tile, double frame,
493                                    const TRenderSettings &info) {
494   if (pi_ && pi_->handler_->do_compute) {
495     rendering_setting_t info_;
496     copy_rendering_setting(&info_, info);
497     pi_->handler_->do_compute(this, &info_, frame, &tile);
498   }
499 }
500 
getMemoryRequirement(const TRectD & rect,double frame,const TRenderSettings & info)501 int RasterFxPluginHost::getMemoryRequirement(const TRectD &rect, double frame,
502                                              const TRenderSettings &info) {
503   using namespace plugin::utils;
504   if (pi_ && pi_->handler_->get_memory_requirement) {
505     rendering_setting_t rs;
506     copy_rendering_setting(&rs, info);
507 
508     rect_t rc;
509     copy_rect(&rc, rect);
510 
511     size_t ignore =
512         pi_->handler_->get_memory_requirement(this, &rs, frame, &rc);
513     return 0;
514   }
515   return 0;
516 }
517 
canHandle(const TRenderSettings & info,double frame)518 bool RasterFxPluginHost::canHandle(const TRenderSettings &info, double frame) {
519   if (pi_ && pi_->handler_->can_handle) {
520     rendering_setting_t rs;
521     copy_rendering_setting(&rs, info);
522     return pi_->handler_->can_handle(this, &rs, frame);
523   }
524   /* 適切なデフォルト値は 'geometric' の場合とそうでない場合で異なる */
525   return isPluginZerary(); /* better-default depends on that is 'geometric' or
526                               not */
527 }
528 
addInputPort(const std::string & nm,std::shared_ptr<TFxPort> port)529 bool RasterFxPluginHost::addInputPort(const std::string &nm,
530                                       std::shared_ptr<TFxPort> port) {
531   /* setOwnFx は addInputPort 内で行われている. setFx()
532    * は接続なので自分自身に呼んではダメ */
533   // port->setFx(this);
534   bool ret = TFx::addInputPort(nm, *port.get());
535   if (ret) {
536     inputs_.push_back(port);
537   }
538   return ret;
539 }
540 
addOutputPort(const std::string & nm,TRasterFxPort * port)541 bool RasterFxPluginHost::addOutputPort(const std::string &nm,
542                                        TRasterFxPort *port) {
543   port->setFx(this);
544   return TFx::addOutputConnection(port);
545 }
546 
callStartRenderHandler()547 void RasterFxPluginHost::callStartRenderHandler() {
548   if (pi_ && pi_->handler_ && pi_->handler_->start_render) {
549     pi_->handler_->start_render(this);
550   }
551 }
552 
callEndRenderHandler()553 void RasterFxPluginHost::callEndRenderHandler() {
554   if (pi_ && pi_->handler_ && pi_->handler_->end_render) {
555     pi_->handler_->end_render(this);
556   }
557 }
558 
callStartRenderFrameHandler(const TRenderSettings * rs,double frame)559 void RasterFxPluginHost::callStartRenderFrameHandler(const TRenderSettings *rs,
560                                                      double frame) {
561   toonz_rendering_setting_t trs;
562   copy_rendering_setting(&trs, *rs);
563   if (pi_ && pi_->handler_ && pi_->handler_->on_new_frame) {
564     pi_->handler_->on_new_frame(this, &trs, frame);
565   }
566 }
567 
callEndRenderFrameHandler(const TRenderSettings * rs,double frame)568 void RasterFxPluginHost::callEndRenderFrameHandler(const TRenderSettings *rs,
569                                                    double frame) {
570   toonz_rendering_setting_t trs;
571   copy_rendering_setting(&trs, *rs);
572   if (pi_ && pi_->handler_ && pi_->handler_->on_end_frame) {
573     pi_->handler_->on_end_frame(this, &trs, frame);
574   }
575 }
576 
getUrl() const577 std::string RasterFxPluginHost::getUrl() const { return pi_->desc_->url_; }
578 
createUIPage(const char * name)579 UIPage *RasterFxPluginHost::createUIPage(const char *name) {
580   pi_->ui_pages_.push_back(NULL);
581   pi_->ui_pages_.back() = new UIPage(name);
582   return pi_->ui_pages_.back();
583 }
584 
585 // deprecated. for migration.
createParam(const char * name,toonz_param_type_enum e)586 Param *RasterFxPluginHost::createParam(const char *name,
587                                        toonz_param_type_enum e) {
588   toonz_param_desc_t *desc = new toonz_param_desc_t;
589   memset(desc, 0, sizeof(toonz_param_desc_t));
590   desc->base.ver   = {1, 0};
591   desc->key        = name;
592   desc->traits_tag = e;
593   return createParam(desc);
594 }
595 
createParam(const toonz_param_desc_t * desc)596 Param *RasterFxPluginHost::createParam(const toonz_param_desc_t *desc) {
597   TParamP p = parameter_factory(desc);
598   if (!p) return nullptr;
599 
600   p->setDescription(desc->note);
601   p->setUILabel(desc->base.label);
602 
603   bindPluginParam(this, desc->key, p);
604 
605   params_.push_back(std::make_shared<Param>(
606       this, desc->key, toonz_param_type_enum(desc->traits_tag), desc));
607   return params_.back().get();
608 }
609 
getParam(const char * name) const610 Param *RasterFxPluginHost::getParam(const char *name) const {
611   for (auto &param : params_) {
612     if (param->name() == name) {
613       return param.get();
614     }
615   }
616   return nullptr;
617 }
618 
createParamView()619 ParamView *RasterFxPluginHost::createParamView() {
620   pi_->param_views_.push_back(NULL);
621   pi_->param_views_.back() = new ParamView();
622   return pi_->param_views_.back();
623 }
624 
625 /* build で構築された GUI は plugin のインスタンスには紐づかない.
626  * 通常一度だけ呼ばれ使い回される.  */
build(ParamsPageSet * pages)627 void RasterFxPluginHost::build(ParamsPageSet *pages) {
628   printf(">>>> RasterFxPluginHost::build: ui_pages:%d\n",
629          (int)pi_->ui_pages_.size());
630   for (std::size_t i = 0, size = pi_->ui_pages_.size(); i < size; ++i) {
631     pi_->ui_pages_[i]->build(this, pages);
632   }
633   auto aboutpage = pages->createParamsPage();
634 
635 #if 1
636   /* FIXME: fxsettings で大きさの測定のためにいろいろやっているので使える
637      layout/widget に制限がありそう.
638      しかしなぜか最後の widget しか出ない */
639   aboutpage->beginGroup("Name");
640   aboutpage->addWidget(new QLabel(pi_->desc_->name_.c_str(), aboutpage));
641   aboutpage->endGroup();
642   aboutpage->beginGroup("Vendor");
643   aboutpage->addWidget(new QLabel(pi_->desc_->vendor_.c_str(), aboutpage));
644   aboutpage->endGroup();
645   aboutpage->beginGroup("Version");
646   auto version =
647       QString::fromStdString(std::to_string(pi_->desc_->plugin_ver_.major)) +
648       "." +
649       QString::fromStdString(std::to_string(pi_->desc_->plugin_ver_.minor));
650   aboutpage->addWidget(new QLabel(version, aboutpage));
651   aboutpage->endGroup();
652   aboutpage->beginGroup("Note");
653   aboutpage->addWidget(new QLabel(pi_->desc_->note_.c_str()), aboutpage);
654   aboutpage->endGroup();
655 #endif
656   pages->addParamsPage(aboutpage, "Version");
657   aboutpage->setPageSpace();
658 }
659 
660 template <typename T, uint32_t compat_maj, uint32_t compat_min>
is_compatible(const T & d)661 static inline bool is_compatible(const T &d) {
662   return d.ver.major == compat_maj && d.ver.minor == compat_min;
663 }
664 
665 template <uint32_t compat_maj, uint32_t compat_min>
is_compatible(const toonz_if_version_t & v)666 static inline bool is_compatible(const toonz_if_version_t &v) {
667   return v.major == compat_maj && v.minor == compat_min;
668 }
669 
check_base_sanity(const toonz_param_page_t * p)670 static int check_base_sanity(const toonz_param_page_t *p) {
671   int err = 0;
672   if (!is_compatible<toonz_param_base_t_, 1, 0>(p->base))
673     err |= TOONZ_PARAM_ERROR_VERSION;
674   if (p->base.label == NULL) err |= TOONZ_PARAM_ERROR_LABEL;
675   if (p->base.type != TOONZ_PARAM_DESC_TYPE_PAGE) err |= TOONZ_PARAM_ERROR_TYPE;
676   return err;
677 }
678 
check_base_sanity(const toonz_param_group_t * p)679 static int check_base_sanity(const toonz_param_group_t *p) {
680   int err = 0;
681   if (!is_compatible<toonz_param_base_t_, 1, 0>(p->base))
682     err |= TOONZ_PARAM_ERROR_VERSION;
683   if (p->base.label == NULL) err |= TOONZ_PARAM_ERROR_LABEL;
684   if (p->base.type != TOONZ_PARAM_DESC_TYPE_GROUP)
685     err |= TOONZ_PARAM_ERROR_TYPE;
686   return err;
687 }
688 
check_base_sanity(const toonz_param_desc_t * p)689 static int check_base_sanity(const toonz_param_desc_t *p) {
690   int err = 0;
691   if (!is_compatible<toonz_param_base_t_, 1, 0>(p->base))
692     err |= TOONZ_PARAM_ERROR_VERSION;
693   if (p->base.label == NULL) err |= TOONZ_PARAM_ERROR_LABEL;
694   if (p->base.type != TOONZ_PARAM_DESC_TYPE_PARAM)
695     err |= TOONZ_PARAM_ERROR_TYPE;
696   return err;
697 }
698 
setParamStructure(int n,toonz_param_page_t * p,int & err,void * & pos)699 bool RasterFxPluginHost::setParamStructure(int n, toonz_param_page_t *p,
700                                            int &err, void *&pos) {
701   /* 適当に現実的な最大値: あまりに大きい場合は領域の破壊を疑う */
702   static const int max_pages_  = 31;
703   static const int max_groups_ = 32;
704   static const int max_params_ = 65535;
705 
706   pos = p;
707   if (pi_) {
708     if (n > max_pages_ || p == NULL) {
709       /* parameter が null
710        * でないことは上位でチェックされているはずで、ここで返せるエラーは定義していない.
711        */
712       if (p == NULL) err |= TOONZ_PARAM_ERROR_UNKNOWN;
713       err |= TOONZ_PARAM_ERROR_PAGE_NUM;
714       return false;
715     }
716 
717     /* SAN 値チェック */
718     for (int k = 0; k < n; k++) {
719       toonz_param_page_t *pg = &p[k];
720       pos                    = pg;
721       if (int e = check_base_sanity(pg)) {
722         err = e;
723         return false;
724       }
725       if (pg->num > max_groups_) {
726         err |= TOONZ_PARAM_ERROR_GROUP_NUM;
727         return false;
728       }
729 
730       for (int l = 0; l < pg->num; l++) {
731         toonz_param_group_t *grp = &pg->array[l];
732         pos                      = grp;
733         if (int e = check_base_sanity(grp)) {
734           err = e;
735           return false;
736         }
737         if (grp->num > max_params_) {
738           err |= TOONZ_PARAM_ERROR_GROUP_NUM;
739           return false;
740         }
741         for (int i = 0; i < grp->num; i++) {
742           toonz_param_desc_t *desc = &grp->array[i];
743           pos                      = desc;
744           if (int e = check_base_sanity(desc)) err |= e;
745           if (desc->key == NULL)
746             err |= TOONZ_PARAM_ERROR_NO_KEY;
747           else {
748             if (!validateKeyName(desc->key)) err |= TOONZ_PARAM_ERROR_KEY_NAME;
749             for (auto it : params_) {
750               if (it->name() == desc->key) {
751                 err |= TOONZ_PARAM_ERROR_KEY_DUP;
752                 break;
753               }
754             }
755           }
756           if (desc->reserved_[0] ||
757               desc->reserved_[1])  // reserved fields must be zero
758             err |= TOONZ_PARAM_ERROR_POLLUTED;
759           err |= check_traits_sanity(desc);
760 
761           if (err) return false;
762         }
763       }
764     }
765 
766     if (err) return false;
767 
768     // deep copy param resources
769     std::vector<std::shared_ptr<void>> &param_resources = pi_->param_resources_;
770     std::vector<std::shared_ptr<std::string>> &strtbl = pi_->param_string_tbl_;
771 
772     auto patch_string = [&](const char *srcstr) {
773       strtbl.push_back(std::shared_ptr<std::string>(new std::string("")));
774       if (srcstr) strtbl.back()->assign(srcstr);
775       return strtbl.back()->c_str();
776     };
777 
778     auto deep_copy_base = [&](toonz_param_base_t_ &dst,
779                               const toonz_param_base_t_ &src) {
780       dst.ver   = src.ver;
781       dst.type  = src.type;
782       dst.label = patch_string(src.label);
783     };
784 
785     param_resources.clear();
786 
787     std::unique_ptr<toonz_param_page_t[]> origin_pages(
788         new toonz_param_page_t[n]);
789     for (int i = 0; i < n; i++) {
790       toonz_param_page_t &dst_page       = origin_pages[i];
791       const toonz_param_page_t &src_page = p[i];
792 
793       deep_copy_base(dst_page.base, src_page.base);
794 
795       const int group_num = dst_page.num = src_page.num;
796       dst_page.array                     = new toonz_param_group_t[group_num];
797 
798       for (int j = 0; j < group_num; j++) {
799         toonz_param_group_t &dst_group       = dst_page.array[j];
800         const toonz_param_group_t &src_group = src_page.array[j];
801 
802         deep_copy_base(dst_group.base, src_group.base);
803 
804         const int desc_num = dst_group.num = src_group.num;
805         dst_group.array                    = new toonz_param_desc_t[desc_num];
806         for (int k = 0; k < desc_num; k++) {
807           toonz_param_desc_t &dst_desc       = dst_group.array[k];
808           const toonz_param_desc_t &src_desc = src_group.array[k];
809 
810           deep_copy_base(dst_desc.base, src_desc.base);  // base
811 
812           dst_desc.key  = patch_string(src_desc.key);   // key
813           dst_desc.note = patch_string(src_desc.note);  // note
814           memcpy(dst_desc.reserved_, src_desc.reserved_,
815                  sizeof(src_desc.reserved_));         // reserved fields
816           dst_desc.traits_tag = src_desc.traits_tag;  // tag
817 
818           // traits
819           if (dst_desc.traits_tag == TOONZ_PARAM_TYPE_ENUM) {
820             dst_desc.traits.e.def = src_desc.traits.e.def;
821             int enums = dst_desc.traits.e.enums = src_desc.traits.e.enums;
822             auto a = std::shared_ptr<void>(new char *[enums]);
823             param_resources.push_back(a);
824             dst_desc.traits.e.array = static_cast<const char **>(a.get());
825             for (int i = 0; i < enums; i++)
826               dst_desc.traits.e.array[i] =
827                   patch_string(src_desc.traits.e.array[i]);
828           } else if (dst_desc.traits_tag == TOONZ_PARAM_TYPE_SPECTRUM) {
829             int points = dst_desc.traits.g.points = src_desc.traits.g.points;
830             auto ptr                              = std::shared_ptr<void>(
831                 new toonz_param_traits_spectrum_t::valuetype[points]);
832             param_resources.push_back(ptr);
833             dst_desc.traits.g.array =
834                 static_cast<toonz_param_traits_spectrum_t::valuetype *>(
835                     ptr.get());
836 
837             for (int i = 0; i < dst_desc.traits.g.points; i++)
838               memcpy(&dst_desc.traits.g.array[i], &src_desc.traits.g.array[i],
839                      sizeof(toonz_param_traits_spectrum_t::valuetype));
840           } else if (dst_desc.traits_tag == TOONZ_PARAM_TYPE_STRING) {
841             dst_desc.traits.s.def = patch_string(src_desc.traits.s.def);
842           } else if (dst_desc.traits_tag == TOONZ_PARAM_TYPE_TONECURVE) {
843             /* has no default values */
844           } else {
845             memcpy(&dst_desc.traits, &src_desc.traits, sizeof(src_desc.traits));
846           }
847         }
848       }
849     }
850 
851     pi_->param_page_num_ = n;
852     pi_->param_pages_    = std::move(origin_pages);
853     return true;
854   }
855   return false;
856 }
857 
createParamsByDesc()858 void RasterFxPluginHost::createParamsByDesc() {
859   printf("RasterFxPluginHost::createParamsByDesc: num:%d\n",
860          pi_->param_page_num_);
861   for (int k = 0; k < pi_->param_page_num_; k++) {
862     toonz_param_page_t *pg = &pi_->param_pages_[k];
863     void *page             = NULL;
864     int r                  = add_preference(this, pg->base.label, &page);
865     printf(
866         "RasterFxPluginHost::createParamsByDesc: add_preference: r:0x%x "
867         "page:%p\n",
868         r, page);
869 
870     for (int l = 0; l < pg->num; l++) {
871       toonz_param_group_t *grp = &pg->array[l];
872       begin_group(page, grp->base.label);
873 
874       for (int i = 0; i < grp->num; i++) {
875         toonz_param_desc_t *desc = &grp->array[i];
876         Param *p                 = createParam(desc);
877         printf("RasterFxPluginHost::createParam: p:%p key:%s tag:%d\n", p,
878                desc->key, desc->traits_tag);
879         if (p) {
880           void *v = NULL;
881           int r   = create_param_view(this, &v);
882           printf(
883               "RasterFxPluginHost::createParam: create_param_view: r:0x%x "
884               "v:%p\n",
885               r, v);
886           r = add_param_field(v, NULL);
887           printf(
888               "RasterFxPluginHost::createParam: add_param_field: r:0x%x v:%p "
889               "p:%p\n",
890               r, v, p);
891           /* set_param_range()
892            * の中で型チェックをしているので全型について呼び出してよい */
893 
894           r = bind_param(page, p, v);
895 
896           set_param_default<tpbind_dbl_t>(p, desc);
897           set_param_default<tpbind_int_t>(p, desc);
898           set_param_default<tpbind_rng_t>(p, desc);
899           set_param_default<tpbind_pnt_t>(p, desc);
900           set_param_default<tpbind_enm_t>(p, desc);
901           set_param_default<tpbind_col_t>(p, desc);
902           set_param_default<tpbind_bool_t>(p, desc);
903           set_param_default<tpbind_str_t>(p, desc);
904           set_param_default<tpbind_spc_t>(p, desc);
905           set_param_default<tpbind_tcv_t>(p, desc);
906 
907           set_param_range<tpbind_dbl_t>(p, desc);
908           set_param_range<tpbind_int_t>(p, desc);
909           set_param_range<tpbind_rng_t>(p, desc);
910           set_param_range<tpbind_pnt_t>(p, desc);
911 
912           printf("RasterFxPluginHost::createParam: bind_param: r:0x%x\n", r);
913         }
914       }
915       end_group(page, grp->base.label);
916     }
917   }
918 }
919 
920 /*
921 パラメタのキー名として適切か確認
922 */
validateKeyName(const char * name)923 bool RasterFxPluginHost::validateKeyName(const char *name) {
924   if (name[0] == '\0') return false;
925   if (!isalpha(name[0]) && name[0] != '_') return false;
926 
927   for (int i = 1; name[i] != '\0'; i++)
928     if (!isalnum(name[i]) && name[i] != '_') return false;
929 
930   /* XMLの仕様ではXMLから始まるタグ名は認められないので、ここで弾く */
931   if (strlen(name) >= 3 && (name[0] == 'X' || name[0] == 'x') &&
932       (name[1] == 'M' || name[1] == 'm') && (name[2] == 'L' || name[2] == 'l'))
933     return false;
934 
935   return true;
936 }
937 
938 /*
939  strict sanity check:
940  未初期化値を受け入れて互換性が崩れないよう厳しくチェックする
941  */
942 #define VERBOSE
check(const plugin_probe_t * begin,const plugin_probe_t * end)943 static inline bool check(const plugin_probe_t *begin,
944                          const plugin_probe_t *end) {
945   /*
946   printf("dump toonz_plugin_probe_t: ver:(%d, %d) (%s, %s, %s, %s) resv:[%p, %p,
947   %p, %p, %p] clss:0x%x resv:[%d, %d, %d, %d, %d]\n",
948              x->ver.major, x->ver.minor,
949              x->name, x->id, x->note, x->url,
950              x->reserved_ptr_[0], x->reserved_ptr_[1], x->reserved_ptr_[2],
951   x->reserved_ptr_[3], x->reserved_ptr_[4],
952              x->clss,
953              x->reserved_int_[0], x->reserved_int_[1], x->reserved_int_[2],
954   x->reserved_int_[3], x->reserved_int_[4], x->reserved_int_[5],
955   x->reserved_int_[6], x->reserved_int_[7]);
956   */
957   int idx = 0;
958   if (!is_compatible<plugin_probe_t, 1, 0>(*begin)) {
959 #if defined(VERBOSE)
960     printf("sanity check(): first interface version is unknown\n");
961 #endif
962     return false;
963   }
964 
965   toonz_if_version_t v = begin->ver;
966   for (auto x = begin; x < end; x++, idx++) {
967     /* 異なるバージョンの構造体の混在はエラーとする.
968        しかし toonz_plugin_probe_t は reservation filed
969        を持っており、サイズが変わらない限りは混在も対応可能だが、まずは sanity
970        check で落とす.
971 
972        For now we permit mixed versions. Not that we never support it since size
973        of toonz_plugin_probe_t is constant.
974     */
975     if (!(x->ver.major == v.major && x->ver.minor == v.minor)) {
976 #if defined(VERBOSE)
977       printf(
978           "sanity check(): versions are ambiguous: first:(%d, %d) "
979           "plugin[%d]:(%d, %d)\n",
980           v.major, v.minor, idx, x->ver.major, x->ver.minor);
981 #endif
982       return false;
983     }
984 
985     if (!x->clss) {
986 #if defined(VERBOSE)
987       printf("sanity check(): plugin[%d] class is zero\n", idx);
988 #endif
989       return false;
990     } else {
991       uint32_t m = x->clss & TOONZ_PLUGIN_CLASS_MODIFIER_MASK;
992       uint32_t c = x->clss & ~TOONZ_PLUGIN_CLASS_MODIFIER_MASK;
993       if (!(m == 0 || m == TOONZ_PLUGIN_CLASS_MODIFIER_GEOMETRIC)) {
994 #if defined(VERBOSE)
995         printf("sanity check(): plugin[%d] unknown modifier: 0x%x\n", idx, m);
996 #endif
997         return false;  // we don't know the modifier
998       }
999       if (!(c == TOONZ_PLUGIN_CLASS_POSTPROCESS_SLAB)) {
1000 #if defined(VERBOSE)
1001         printf("sanity check(): plugin[%d] unknown class: 0x%x\n", idx, c);
1002 #endif
1003         return false;  // we don't know the class
1004       }
1005     }
1006 
1007     // reservations are must be zero
1008     for (int i = 0; i < 3; i++)
1009       if (x->reserved_ptr_[i]) {
1010 #if defined(VERBOSE)
1011         printf(
1012             "sanity check(): plugin[%d] reserved_ptr_[%d] is NOT all zero-ed\n",
1013             idx, i);
1014 #endif
1015         return false;
1016       }
1017     for (int i = 0; i < 7; i++)
1018       if (x->reserved_int_[i]) {
1019 #if defined(VERBOSE)
1020         printf(
1021             "sanity check(): plugin[%d] reserved_int_[%d] is NOT all zero-ed\n",
1022             idx, i);
1023 #endif
1024         return false;
1025       }
1026     for (int i = 0; i < 3; i++)
1027       if (x->reserved_ptr_trail_[i]) {
1028 #if defined(VERBOSE)
1029         printf(
1030             "sanity check(): plugin[%d] reserved_ptr_trail_[%d] is NOT all "
1031             "zero-ed\n",
1032             idx, i);
1033 #endif
1034         return false;
1035       }
1036 
1037     if (x->handler == NULL) {
1038 #if defined(VERBOSE)
1039       printf("sanity check(): plugin[%d] handler is null\n", idx);
1040 #endif
1041       return false;  // handler must be NOT null
1042     }
1043   }
1044 
1045   // check if the end is empty
1046   const char *b = reinterpret_cast<const char *>(end);
1047   for (int i = 0; i < sizeof(plugin_probe_t); i++) {
1048     if (b[i] != 0) {
1049 #if defined(VERBOSE)
1050       printf("sanity check(): empty is NOT all zero-ed\n");
1051 #endif
1052       return false;  // must be zero
1053     }
1054   }
1055   return true;
1056 }
1057 
check_and_copy(nodal_rasterfx_handler_t * __restrict dst,const nodal_rasterfx_handler_t * __restrict src)1058 static inline bool check_and_copy(
1059     nodal_rasterfx_handler_t *__restrict dst,
1060     const nodal_rasterfx_handler_t *__restrict src) {
1061   // do we know the version?
1062   if (!(src->ver.major == 1 && src->ver.minor == 0)) return false;
1063   dst->ver                    = src->ver;
1064   dst->do_compute             = src->do_compute;
1065   dst->do_get_bbox            = src->do_get_bbox;
1066   dst->can_handle             = src->can_handle;
1067   dst->get_memory_requirement = src->get_memory_requirement;
1068   dst->on_new_frame           = src->on_new_frame;
1069   dst->on_end_frame           = src->on_end_frame;
1070   dst->create                 = src->create;
1071   dst->destroy                = src->destroy;
1072   dst->setup                  = src->setup;
1073   dst->start_render           = src->start_render;
1074   dst->end_render             = src->end_render;
1075   return true;
1076 }
1077 
uuid_matches(const UUID * x,const UUID * y)1078 static inline bool uuid_matches(const UUID *x, const UUID *y) {
1079   return x->uid0 == y->uid0 && x->uid1 == y->uid1 && x->uid2 == y->uid2 &&
1080          x->uid3 == y->uid3 && x->uid4 == y->uid4;
1081 }
1082 
1083 static UUID uuid_nodal_ = {0xCC14EA21, 0x13D8, 0x4A3B, 0x9375, 0xAA4F68C9DDDD};
1084 static UUID uuid_port_  = {0x2F89A423, 0x1D2D, 0x433F, 0xB93E, 0xCFFD83745F6F};
1085 static UUID uuid_tile_  = {0x882BD525, 0x937E, 0x427C, 0x9D68, 0x4ECA651F6562};
1086 // static UUID uuid_ui_page_ = {0xD2EF0310, 0x3414, 0x4753, 0x84CA,
1087 // 0xD5447C70DD89};
1088 static UUID uuid_fx_node_ = {0x26F9FC53, 0x632B, 0x422F, 0x87A0,
1089                              0x8A4547F55474};
1090 // static UUID uuid_param_view_ = {0x5133A63A, 0xDD92, 0x41BD, 0xA255,
1091 // 0x6F97BE7292EA};
1092 static UUID uuid_param_ = {0x2E3E4A55, 0x8539, 0x4520, 0xA266, 0x15D32189EC4D};
1093 static UUID uuid_setup_ = {0xcfde9107, 0xc59d, 0x414c, 0xae4a, 0x3d115ba97933};
1094 static UUID uuid_null_  = {0, 0, 0, 0, 0};
1095 
1096 template <typename T, int major, int minor>
1097 T *base_interface_factory() {
1098   T *t = new T;
1099   memset(t, 0, sizeof(T));
1100   t->ver.major = major;
1101   t->ver.minor = minor;
1102   return t;
1103 }
1104 
1105 template <typename T, uint32_t major, uint32_t minor>
1106 struct interface_t {
factoryinterface_t1107   static T *factory() {
1108     T *t = base_interface_factory<T, major, minor>();
1109     return t;
1110   }
1111 };
1112 
1113 extern "C" {
1114 int set_parameter_pages(toonz_node_handle_t, int num,
1115                         toonz_param_page_t *params);
1116 int set_parameter_pages_with_error(toonz_node_handle_t, int num,
1117                                    toonz_param_page_t *params, int *, void **);
1118 }
1119 
1120 template <uint32_t major, uint32_t minor>
1121 struct interface_t<setup_interface_t, major, minor> {
factoryinterface_t1122   static setup_interface_t *factory() {
1123     setup_interface_t *t =
1124         base_interface_factory<setup_interface_t, major, minor>();
1125     t->set_parameter_pages            = set_parameter_pages;
1126     t->set_parameter_pages_with_error = set_parameter_pages_with_error;
1127     t->add_input_port                 = setup_input_port;
1128     return t;
1129   }
1130 };
1131 
1132 /*
1133 template < uint32_t major, uint32_t minor >
1134 struct interface_t < ui_page_interface_t, major, minor > {
1135         static ui_page_interface_t* factory()
1136         {
1137                 ui_page_interface_t* t = base_interface_factory<
1138 ui_page_interface_t, major, minor >();
1139                 t->begin_group = begin_group;
1140                 t->end_group = end_group;
1141                 t->bind_param = bind_param;
1142                 return t;
1143         }
1144 };
1145 
1146 template < uint32_t major, uint32_t minor >
1147 struct interface_t < param_view_interface_t, major, minor > {
1148         static param_view_interface_t* factory()
1149         {
1150                 param_view_interface_t* t = base_interface_factory<
1151 param_view_interface_t, major, minor >();
1152                 t->add_param_field = add_param_field;
1153                 t->add_custom_field = add_custom_field;
1154                 t->add_lineedit = add_lineedit;
1155                 t->add_slider = add_slider;
1156                 t->add_spinbox = add_spinbox;
1157                 t->add_checkbox = add_checkbox;
1158                 t->add_radiobutton = add_radiobutton;
1159                 t->add_combobox = add_combobox;
1160                 return t;
1161         }
1162 };
1163 */
1164 
1165 template <uint32_t major, uint32_t minor>
1166 struct interface_t<param_interface_t, major, minor> {
factoryinterface_t1167   static param_interface_t *factory() {
1168     param_interface_t *t =
1169         base_interface_factory<param_interface_t, major, minor>();
1170     t->get_type = get_type;
1171     // t->hint_default_value = hint_default_value;
1172     // t->hint_value_range = hint_value_range;
1173     // t->hint_unit = hint_unit;
1174     // t->hint_item = hint_item;
1175     // t->get_value_type = get_value_type;
1176     t->get_value = get_value;
1177     // t->set_value = set_value;
1178     t->get_string_value   = get_string_value;
1179     t->get_spectrum_value = get_spectrum_value;
1180     return t;
1181   }
1182 };
1183 
1184 template <uint32_t major, uint32_t minor>
1185 struct interface_t<node_interface_t, major, minor> {
factoryinterface_t1186   static node_interface_t *factory() {
1187     node_interface_t *t =
1188         base_interface_factory<node_interface_t, major, minor>();
1189     // t->add_input_port = add_input_port;
1190     // t->add_output_port = add_output_port;
1191     t->get_input_port = get_input_port;
1192     t->get_rect       = get_rect;
1193     t->set_rect       = set_rect;
1194     // t->add_preference = add_preference;
1195     // t->add_param = add_param;
1196     t->get_param = get_param;
1197     // t->create_param_view = create_param_view;
1198     t->set_user_data = set_user_data;
1199     t->get_user_data = get_user_data;
1200     return t;
1201   }
1202 };
1203 
1204 template <uint32_t major, uint32_t minor>
1205 struct interface_t<toonz_tile_interface_t, major, minor> {
factoryinterface_t1206   static toonz_tile_interface_t *factory() {
1207     toonz_tile_interface_t *t =
1208         base_interface_factory<toonz_tile_interface_t, major, minor>();
1209     t->get_raw_address_unsafe = tile_interface_get_raw_address_unsafe;
1210     t->get_raw_stride         = tile_interface_get_raw_stride;
1211     t->get_element_type       = tile_interface_get_element_type;
1212     t->copy_rect              = tile_interface_copy_rect;
1213     t->create_from            = tile_interface_create_from;
1214     t->create                 = tile_interface_create;
1215     t->destroy                = tile_interface_destroy;
1216     t->get_rectangle          = tile_interface_get_rectangle;
1217     t->safen                  = tile_interface_safen;
1218     return t;
1219   }
1220 };
1221 
1222 template <uint32_t major, uint32_t minor>
1223 struct interface_t<port_interface_t, major, minor> {
factoryinterface_t1224   static port_interface_t *factory() {
1225     port_interface_t *t =
1226         base_interface_factory<port_interface_t, major, minor>();
1227     t->is_connected = is_connected;
1228     t->get_fx       = get_fx;
1229     return t;
1230   }
1231 };
1232 
1233 template <uint32_t major, uint32_t minor>
1234 struct interface_t<fxnode_interface_t, major, minor> {
factoryinterface_t1235   static fxnode_interface_t *factory() {
1236     fxnode_interface_t *t =
1237         base_interface_factory<fxnode_interface_t, major, minor>();
1238     t->get_bbox             = fxnode_get_bbox;
1239     t->can_handle           = fxnode_can_handle;
1240     t->get_input_port_count = fxnode_get_input_port_count;
1241     t->get_input_port       = fxnode_get_input_port;
1242     t->compute_to_tile      = fxnode_compute_to_tile;
1243     return t;
1244   }
1245 };
1246 
1247 /*
1248 template <>
1249 template < uint32_t major, uint32_t minor >
1250 struct interface_t< toonz_nodal_rasterfx_interface_t >::i_< major, minor > {
1251         static toonz_nodal_rasterfx_interface_t* factory()
1252         {
1253                 printf("toonz_nodal_rasterfx_interface_t::factory\n");
1254                 toonz_nodal_rasterfx_interface_t* t = base_interface_factory<
1255 toonz_nodal_rasterfx_interface_t, major, minor >();
1256                 return t;
1257         }
1258 };
1259 */
1260 
1261 template <typename T, uint32_t major, uint32_t minor>
1262 T *interface_factory() {
1263   // return interface_t< T >::i_< major, minor >().factory();
1264   return interface_t<T, major, minor>::factory();
1265 }
1266 
query_interface(const UUID * uuid,void ** interf)1267 static int query_interface(const UUID *uuid, void **interf) {
1268   typedef std::pair<const UUID *, int> uuid_dict_t;
1269   static const uuid_dict_t dict[] = {
1270       uuid_dict_t(&uuid_nodal_, 1), uuid_dict_t(&uuid_port_, 2),
1271       uuid_dict_t(&uuid_tile_, 3),
1272       // uuid_dict_t(&uuid_ui_page_, 4),
1273       uuid_dict_t(&uuid_fx_node_, 5),
1274       // uuid_dict_t(&uuid_param_view_, 6),
1275       uuid_dict_t(&uuid_param_, 7), uuid_dict_t(&uuid_setup_, 8),
1276       uuid_dict_t(&uuid_null_, 0)};
1277 
1278   if (!(uuid && interf)) return TOONZ_ERROR_NULL;
1279 
1280   try {
1281     const uuid_dict_t *it = &dict[0];
1282     while (it->first != &uuid_null_) {
1283       if (uuid_matches(it->first, uuid)) {
1284         switch (it->second) {
1285         case 1:
1286           *interf = interface_factory<toonz_node_interface_t, 1, 0>();
1287           break;
1288         case 2:
1289           *interf = interface_factory<toonz_port_interface_t, 1, 0>();
1290           break;
1291         case 3:
1292           *interf = interface_factory<toonz_tile_interface_t, 1, 0>();
1293           break;
1294         // case 4:
1295         //	*interf = interface_factory< toonz_ui_page_interface_t, 1, 0
1296         //>();
1297         //	break;
1298         case 5:
1299           *interf = interface_factory<toonz_fxnode_interface_t, 1, 0>();
1300           break;
1301         // case 6:
1302         //*interf = interface_factory< toonz_param_view_interface_t, 1, 0 >();
1303         // break;
1304         case 7:
1305           *interf = interface_factory<toonz_param_interface_t, 1, 0>();
1306           break;
1307         case 8:
1308           *interf = interface_factory<toonz_setup_interface_t, 1, 0>();
1309           break;
1310         default:
1311           return TOONZ_ERROR_NOT_IMPLEMENTED;
1312           break;
1313         }
1314       }
1315       it++;
1316     }
1317   } catch (const std::bad_alloc &) {
1318     return TOONZ_ERROR_OUT_OF_MEMORY;
1319   } catch (const std::exception &) {
1320     return TOONZ_ERROR_UNKNOWN;
1321   }
1322 
1323   return TOONZ_OK;
1324 }
1325 
release_interface(void * interf)1326 static void release_interface(void *interf) {
1327   if (interf) delete interf;
1328 }
1329 
Loader()1330 Loader::Loader() {}
1331 
walkDirectory(const QString & path)1332 void Loader::walkDirectory(const QString &path) {
1333   walkDirectory_(path);
1334   emit fixup();
1335 }
1336 
walkDictionary(const QString & path)1337 void Loader::walkDictionary(const QString &path) {
1338   /* only for emitting a signal for fixup */
1339   printf("walkDictionary: %s [dry]\n", path.toLocal8Bit().data());
1340   emit fixup();
1341 }
1342 
walkDirectory_(const QString & path)1343 void Loader::walkDirectory_(const QString &path) {
1344   printf("walkDirectory_: %s\n", path.toLocal8Bit().data());
1345   QDir dir(path, QString::fromStdString("*.plugin"), QDir::Name,
1346            QDir::AllDirs | QDir::Files | QDir::NoDot | QDir::NoDotDot);
1347   auto lst = dir.entryInfoList();
1348   for (auto &e : lst) {
1349     if (e.isDir()) {
1350       walkDirectory_(e.filePath());
1351     } else if (e.isFile()) {  // file or symlink-to-file
1352       doLoad(e.filePath());
1353     }
1354   }
1355 }
1356 
1357 #if defined(_WIN32) || defined(_CYGWIN_)
end_library(HMODULE mod)1358 static void end_library(HMODULE mod) { FreeLibrary(mod); }
1359 #else
end_library(void * mod)1360 static void end_library(void *mod) { dlclose(mod); }
1361 #endif
1362 
doLoad(const QString & file)1363 void Loader::doLoad(const QString &file) {
1364 #if defined(_WIN32) || defined(_CYGWIN_)
1365   HMODULE handle = LoadLibraryA(file.toLocal8Bit().data());
1366   printf("doLoad handle:%p path:%s\n", handle, file.toLocal8Bit().data());
1367 #else
1368   void *handle = dlopen(file.toUtf8().data(), RTLD_LOCAL);
1369   printf("doLoad handle:%p path:%s\n", handle, file.toUtf8().data());
1370 #endif
1371   PluginInformation *pi = new PluginInformation;
1372   if (handle) {
1373     pi->library_ = library_t(handle, end_library);  // shared_ptr
1374                                                     /*
1375 probe に使う plugin 情報を探す.
1376 テーブルを export
1377                                                     したほうが楽だが、開発者はデバッグしにくいので関数フォームも提供する.
1378 toonz_plugin_info で検索し、なければ toonz_plugin_probe() を呼び出す.
1379 */
1380 #if defined(_WIN32) || defined(_CYGWIN_)
1381     auto ini = (int (*)(host_interface_t *))GetProcAddress(handle,
1382                                                            "toonz_plugin_init");
1383     auto fin = (void (*)(void))GetProcAddress(handle,
1384                                               "toonz_plugin_exit");  // optional
1385     const plugin_probe_list_t *problist =
1386         reinterpret_cast<const plugin_probe_list_t *>(
1387             GetProcAddress(handle, "toonz_plugin_info_list"));
1388 #else
1389     auto ini = (int (*)(host_interface_t *))dlsym(handle, "toonz_plugin_init");
1390     auto fin = (void (*)(void))dlsym(handle, "toonz_plugin_exit");  // optional
1391     const plugin_probe_list_t *problist =
1392         reinterpret_cast<const plugin_probe_list_t *>(
1393             dlsym(handle, "toonz_plugin_info_list"));
1394 #endif
1395     pi->ini_ = ini;
1396     pi->fin_ = fin;
1397 
1398     const plugin_probe_t *probinfo_begin = NULL;
1399     const plugin_probe_t *probinfo_end   = NULL;
1400     try {
1401       if (problist) {
1402         if (!is_compatible<plugin_probe_list_t, 1, 0>(*problist))
1403           throw std::domain_error(
1404               "invaid toonz_plugin_info_list: version unmatched");
1405         probinfo_begin = problist->begin;
1406         probinfo_end   = problist->end;
1407       }
1408 
1409       if (!probinfo_begin || !probinfo_end) {
1410         printf("use function-formed prober:toonz_plugin_probe\n");
1411 // look at function-formed
1412 #if defined(_WIN32) || defined(_CYGWIN_)
1413         void *probe = GetProcAddress(handle, "toonz_plugin_probe");
1414 #else
1415         void *probe = dlsym(handle, "toonz_plugin_probe");
1416 #endif
1417         if (probe) {
1418           printf("function-formed prober found\n");
1419           const plugin_probe_list_t *lst =
1420               (reinterpret_cast<const plugin_probe_list_t *(*)(void)>(probe))();
1421           if (!lst || !is_compatible<plugin_probe_list_t, 1, 0>(*lst))
1422             throw std::domain_error("invalid plugin list");
1423           plugin_probe_t *begin = lst->begin;
1424           plugin_probe_t *end   = lst->end;
1425           if (!begin || !end)
1426             throw std::domain_error(
1427                 "invalid plugin information address (begin or end is null)");
1428           else if (begin >= end)
1429             throw std::domain_error(
1430                 "invalid plugin information address (begin >= end)");
1431           else if (begin == end - 1)
1432             throw std::domain_error(
1433                 "invalid plugin information address (information is empty)");
1434           probinfo_begin = begin;
1435           probinfo_end   = end;
1436         } else {
1437           throw std::domain_error(
1438               "found toonz_plugin_probe nor toonz_plugin_info");
1439         }
1440       } else {
1441       }
1442       int plugin_num = probinfo_end - probinfo_begin;
1443       printf("plugin count:%d begin:%p end:%p\n", plugin_num, probinfo_begin,
1444              probinfo_end);
1445 
1446       /* sanity check に失敗した場合は予期せぬアドレスを参照して toonz
1447          本体ごと落ちる可能性があるので
1448          致命的エラー扱いで早期に抜ける. */
1449       if (!probinfo_begin || !probinfo_end ||
1450           !check(probinfo_begin, probinfo_end))
1451         throw std::domain_error("ill-formed plugin information");
1452 
1453       for (const plugin_probe_t *probinfo = probinfo_begin;
1454            probinfo < probinfo_end; probinfo++) {
1455         pi->desc_                       = new PluginDescription(probinfo);
1456         nodal_rasterfx_handler_t *nodal = probinfo->handler;
1457         /* probinfo は sanity check 通過済みなのでチェック不要. handler は null
1458          * でないことのみが確認されている */
1459         if (is_compatible<nodal_rasterfx_handler_t, 1, 0>(*nodal)) {
1460           uint32_t c = probinfo->clss & ~(TOONZ_PLUGIN_CLASS_MODIFIER_MASK);
1461           uint32_t m = probinfo->clss & (TOONZ_PLUGIN_CLASS_MODIFIER_MASK);
1462           if (c == TOONZ_PLUGIN_CLASS_POSTPROCESS_SLAB) {
1463             pi->handler_ = new nodal_rasterfx_handler_t;
1464             if (!check_and_copy(pi->handler_, nodal))
1465               throw std::domain_error("ill-formed nodal interface");
1466           } else {
1467             // unknown plugin-class : gracefully end
1468             /* sanity check しているので来ないはずだ */
1469           }
1470         } else {
1471           // unknown version : gracefully end
1472         }
1473 
1474         emit load_finished(pi);
1475 
1476         if (pi) {
1477           try {
1478             if (pi->ini_) {
1479               /* interface は plugin 内部で破壊されても他に影響させないため
1480                * plugin instance ごとに割り当てる.  */
1481               host_interface_t *host  = new host_interface_t;
1482               host->ver.major         = 1;
1483               host->ver.minor         = 0;
1484               host->query_interface   = query_interface;
1485               host->release_interface = release_interface;
1486               int ret                 = pi->ini_(host);
1487               if (ret) {
1488                 delete host;
1489                 std::domain_error(
1490                     "failed initialized: error on _toonz_plugin_init");
1491               }
1492               pi->host_ = host;
1493               pi->decl_ = new PluginDeclaration(pi);
1494             } else {
1495               /* もっと早期にエラーを出して終了することもできるが
1496                  センシティブすぎると見通しが立てにくいのである程度我慢してから出す
1497                  */
1498               throw std::domain_error("not found _toonz_plugin_init");
1499             }
1500           } catch (const std::exception &e) {
1501             printf("Exception occured after plugin loading: %s\n", e.what());
1502           }
1503 
1504           if (pi->handler_ && pi->handler_->setup) {
1505             PluginSetupMessage(pi).sendBlocking();
1506           }
1507 
1508           if (probinfo + 1 < probinfo_end) {
1509             /* for a next plugin on the library */
1510             auto prev = pi->library_;
1511             pi        = new PluginInformation;
1512             /* instance に依存しない unique なリソースは引き継ぐ必要がある */
1513             pi->library_ = prev;
1514             pi->ini_     = ini;
1515             pi->fin_     = fin;
1516           }
1517         }
1518       }
1519     } catch (const std::exception &e) {
1520       printf("Exception occured while plugin loading: %s\n", e.what());
1521       delete pi;
1522       pi = NULL;
1523     }
1524   }
1525 }
1526 
createPortsByDesc()1527 void RasterFxPluginHost::createPortsByDesc() {
1528   if (pi_) {
1529     for (auto pm : pi_->port_mapper_) {
1530       /* TRasterFxPort は non-copyable
1531        * なスマートポインタなのでポインタで引き回す */
1532       printf("createPortsByDesc: name:%s dir:%d type:%d\n", pm.first.c_str(),
1533              pm.second.input_, pm.second.type_);
1534       if (pm.second.input_) {
1535         auto p = std::make_shared<TRasterFxPort>();
1536         if (!addInputPort(pm.first, p)) {  // overloaded version
1537           printf("createPortsByDesc: failed to add: already have\n");
1538         }
1539       } else {
1540         auto p = new TRasterFxPort();
1541         /* TRasterFxPort は non-copyable
1542          * なスマートポインタなのでポインタで引き回す */
1543         if (addOutputPort(pm.first, p)) {  // overloaded version
1544           delete p;
1545           printf("createPortsByDesc: failed to add: already have\n");
1546         }
1547       }
1548     }
1549   }
1550 }
1551 
1552 /*
1553  TODO: addfxcontextmenu に移したほうがいい
1554  */
PluginLoadController(const std::string & basedir,QObject * listener)1555 PluginLoadController::PluginLoadController(const std::string &basedir,
1556                                            QObject *listener) {
1557   Loader *ld = new Loader;
1558 
1559   ld->moveToThread(&work_entity);
1560   connect(&work_entity, &QThread::finished, ld, &QObject::deleteLater);
1561   /* AddFxContextMenu から呼ばれていたが、プラグインの検索が load_entries()
1562      を通じて起動時に呼ばれるようにした関係で,
1563      (あまりよくはないが)listner の有無によって receiver を分けるようにしている.
1564      listener がいる場合は従来通り context menu の構築のために
1565      AddFxContextMenu::fixup() に接続するが
1566      それ以外では plugin_dict_ への追加のため PluginLoadController::finished
1567      に接続する.
1568   */
1569   if (listener) {
1570     AddFxContextMenu *a = qobject_cast<AddFxContextMenu *>(listener);
1571     connect(ld, &Loader::fixup, a, &AddFxContextMenu::fixup);
1572     connect(this, &PluginLoadController::start, ld, &Loader::walkDictionary);
1573   } else {
1574     connect(this, &PluginLoadController::start, ld, &Loader::walkDirectory);
1575     connect(ld, &Loader::load_finished, this, &PluginLoadController::result);
1576     if (!connect(ld, &Loader::fixup, this, &PluginLoadController::finished))
1577       assert(false);
1578   }
1579   work_entity.start();
1580 
1581   QString pluginbase = (TEnv::getStuffDir() + "plugins").getQString();
1582   printf("plugin search directory:%s\n", pluginbase.toLocal8Bit().data());
1583   emit start(pluginbase);
1584 }
1585 
finished()1586 void PluginLoadController::finished() {
1587   printf("===== PluginLoadController::finished() =====\n");
1588   work_entity.exit();
1589 }
1590 
result(PluginInformation * pi)1591 void PluginLoadController::result(PluginInformation *pi) {
1592   /* slot receives PluginInformation on the main thread たぶん */
1593   printf("PluginLoadController::result() pi:%p\n", pi);
1594   if (pi) {
1595     /* addfxcontextmenu.cpp の dict に登録する */
1596     plugin_dict_.insert(
1597         std::pair<std::string, PluginInformation *>(pi->desc_->id_, pi));
1598   }
1599 }
1600 
copy_rendering_setting(toonz_rendering_setting_t * dst,const TRenderSettings & src)1601 static bool copy_rendering_setting(toonz_rendering_setting_t *dst,
1602                                    const TRenderSettings &src) {
1603   plugin::utils::copy_affine(&dst->affine, src.m_affine);
1604   dst->gamma                  = src.m_gamma;
1605   dst->time_stretch_from      = src.m_timeStretchFrom;
1606   dst->time_stretch_to        = src.m_timeStretchTo;
1607   dst->stereo_scopic_shift    = src.m_stereoscopicShift;
1608   dst->bpp                    = src.m_bpp;
1609   dst->max_tile_size          = src.m_maxTileSize;
1610   dst->quality                = src.m_quality;
1611   dst->field_prevalence       = src.m_fieldPrevalence;
1612   dst->stereoscopic           = src.m_stereoscopic;
1613   dst->is_swatch              = src.m_isSwatch;
1614   dst->user_cachable          = src.m_userCachable;
1615   dst->apply_shrink_to_viewer = src.m_applyShrinkToViewer;
1616   dst->context                = &src;
1617   dst->is_canceled            = src.m_isCanceled;
1618   plugin::utils::copy_rect(&dst->camera_box, src.m_cameraBox);
1619   return true;
1620 }
1621 
1622 //#include "pluginhost.moc"
1623