1 /*
2    This file is part of darktable,
3    Copyright (C) 2014-2021 darktable developers.
4 
5    darktable is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9 
10    darktable is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with darktable.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 #include "lua/luastorage.h"
19 #include "common/file_location.h"
20 #include "common/image.h"
21 #include "common/imageio.h"
22 #include "common/imageio_module.h"
23 #include "control/jobs.h"
24 #include "lua/call.h"
25 #include "lua/glist.h"
26 #include "lua/image.h"
27 #include "lua/widget/widget.h"
28 #include <common/darktable.h>
29 #include <stdio.h>
30 
31 typedef struct
32 {
33   gboolean data_created;
34 } lua_storage_t;
35 
36 typedef struct
37 {
38   char *name;
39   GList *supported_formats;
40   lua_widget widget;
41 } lua_storage_gui_t;
42 
push_lua_data(lua_State * L,lua_storage_t * d)43 static void push_lua_data(lua_State*L,lua_storage_t *d)
44 {
45   if(!d->data_created)
46   {
47     lua_pushlightuserdata(L, d);
48     lua_newtable(L);
49     lua_settable(L, LUA_REGISTRYINDEX);
50     d->data_created = true;
51   }
52   lua_pushlightuserdata(L, d);
53   lua_gettable(L, LUA_REGISTRYINDEX);
54 }
name_wrapper(const struct dt_imageio_module_storage_t * self)55 static const char *name_wrapper(const struct dt_imageio_module_storage_t *self)
56 {
57   return ((lua_storage_gui_t *)self->gui_data)->name;
58 }
empty_wrapper(struct dt_imageio_module_storage_t * self)59 static void empty_wrapper(struct dt_imageio_module_storage_t *self){};
default_supported_wrapper(struct dt_imageio_module_storage_t * self,struct dt_imageio_module_format_t * format)60 static int default_supported_wrapper(struct dt_imageio_module_storage_t *self,
61                                      struct dt_imageio_module_format_t *format)
62 {
63   if(g_list_find(((lua_storage_gui_t *)self->gui_data)->supported_formats, format))
64   {
65     return TRUE;
66   }
67   else
68   {
69     return FALSE;
70   }
71 }
default_dimension_wrapper(struct dt_imageio_module_storage_t * self,dt_imageio_module_data_t * data,uint32_t * width,uint32_t * height)72 static int default_dimension_wrapper(struct dt_imageio_module_storage_t *self, dt_imageio_module_data_t *data,
73                                      uint32_t *width, uint32_t *height)
74 {
75   return 0;
76 };
77 
store_wrapper(struct dt_imageio_module_storage_t * self,struct dt_imageio_module_data_t * self_data,const int imgid,dt_imageio_module_format_t * format,dt_imageio_module_data_t * fdata,const int num,const int total,const gboolean high_quality,const gboolean upscale,const gboolean export_masks,dt_colorspaces_color_profile_type_t icc_type,const gchar * icc_filename,dt_iop_color_intent_t icc_intent,dt_export_metadata_t * metadata)78 static int store_wrapper(struct dt_imageio_module_storage_t *self, struct dt_imageio_module_data_t *self_data,
79                          const int imgid, dt_imageio_module_format_t *format, dt_imageio_module_data_t *fdata,
80                          const int num, const int total, const gboolean high_quality, const gboolean upscale,
81                          const gboolean export_masks, dt_colorspaces_color_profile_type_t icc_type,
82                          const gchar *icc_filename, dt_iop_color_intent_t icc_intent, dt_export_metadata_t *metadata)
83 {
84 
85   /* construct a temporary file name */
86   char tmpdir[PATH_MAX] = { 0 };
87   gboolean from_cache = FALSE;
88   dt_loc_get_tmp_dir(tmpdir, sizeof(tmpdir));
89 
90   char dirname[PATH_MAX] = { 0 };
91   dt_image_full_path(imgid, dirname, sizeof(dirname), &from_cache);
92   dt_image_path_append_version(imgid, dirname, sizeof(dirname));
93   gchar *filename = g_path_get_basename(dirname);
94   gchar *end = g_strrstr(filename, ".") + 1;
95   g_strlcpy(end, format->extension(fdata), sizeof(dirname) - (end - dirname));
96 
97   gchar *complete_name = g_build_filename(tmpdir, filename, (char *)NULL);
98 
99   if(dt_imageio_export(imgid, complete_name, format, fdata, high_quality, upscale, TRUE, export_masks, icc_type,
100                        icc_filename, icc_intent, self, self_data, num, total, metadata) != 0)
101   {
102     fprintf(stderr, "[%s] could not export to file: `%s'!\n", self->name(self), complete_name);
103     g_free(complete_name);
104     g_free(filename);
105     return 1;
106   }
107 
108   lua_storage_t *d = (lua_storage_t *)self_data;
109 
110   dt_lua_lock();
111   lua_State *L = darktable.lua_state.state;
112 
113   push_lua_data(L, d);
114   dt_lua_goto_subtable(L, "files");
115   luaA_push(L, dt_lua_image_t, &(imgid));
116   lua_pushstring(L, complete_name);
117   lua_settable(L, -3);
118   lua_pop(L, 1);
119 
120 
121 
122 
123   lua_getfield(L, LUA_REGISTRYINDEX, "dt_lua_storages");
124   lua_getfield(L, -1, self->plugin_name);
125   lua_getfield(L, -1, "store");
126 
127   if(lua_isnil(L, -1))
128   {
129     lua_pop(L, 3);
130     dt_lua_unlock();
131     g_free(filename);
132     return 0;
133   }
134 
135   luaA_push_type(L, self->parameter_lua_type, self_data);
136   luaA_push(L, dt_lua_image_t, &imgid);
137   luaA_push_type(L, format->parameter_lua_type, fdata);
138   lua_pushstring(L, complete_name);
139   lua_pushinteger(L, num);
140   lua_pushinteger(L, total);
141   lua_pushboolean(L, high_quality);
142   push_lua_data(L, d);
143   dt_lua_goto_subtable(L, "extra");
144   dt_lua_treated_pcall(L, 8, 0);
145   lua_pop(L, 2);
146   dt_lua_unlock();
147   g_free(filename);
148   return false;
149 }
150 
151 // FIXME: return 0 on success and 1 on error!
initialize_store_wrapper(struct dt_imageio_module_storage_t * self,dt_imageio_module_data_t * data,dt_imageio_module_format_t ** format,dt_imageio_module_data_t ** fdata,GList ** images,const gboolean high_quality,const gboolean upscale)152 static int initialize_store_wrapper(struct dt_imageio_module_storage_t *self, dt_imageio_module_data_t *data,
153                                     dt_imageio_module_format_t **format, dt_imageio_module_data_t **fdata,
154                                     GList **images, const gboolean high_quality, const gboolean upscale)
155 {
156   dt_lua_lock();
157   lua_State *L = darktable.lua_state.state;
158 
159   lua_getfield(L, LUA_REGISTRYINDEX, "dt_lua_storages");
160   lua_getfield(L, -1, self->plugin_name);
161   lua_getfield(L, -1, "initialize_store");
162 
163   if(lua_isnil(L, -1))
164   {
165     lua_pop(L, 3);
166     dt_lua_unlock();
167     return 1;
168   }
169 
170   luaA_push_type(L, self->parameter_lua_type, data);
171   luaA_push_type(L, (*format)->parameter_lua_type, *fdata);
172 
173   lua_newtable(L);
174   for(const GList *imgids = *images; imgids; imgids = g_list_next(imgids))
175   {
176     luaA_push(L, dt_lua_image_t, &(imgids->data));
177     luaL_ref(L, -2);
178   }
179   lua_pushboolean(L, high_quality);
180 
181   lua_storage_t *d = (lua_storage_t *)data;
182   push_lua_data(L, d);
183   dt_lua_goto_subtable(L, "extra");
184 
185   dt_lua_treated_pcall(L, 5, 1);
186   if(!lua_isnoneornil(L, -1))
187   {
188     g_list_free(*images);
189     if(lua_type(L, -1) != LUA_TTABLE)
190     {
191       dt_print(DT_DEBUG_LUA, "LUA ERROR initialization function of storage did not return nil or table\n");
192       dt_lua_unlock();
193       return 1;
194     }
195     GList *new_images = NULL;
196     lua_pushnil(L);
197     while(lua_next(L, -2))
198     {
199       dt_lua_image_t imgid;
200       luaA_to(L, dt_lua_image_t, &imgid, -1);
201       new_images = g_list_prepend(new_images, GINT_TO_POINTER(imgid));
202       lua_pop(L, 1);
203     }
204     new_images = g_list_reverse(new_images);
205     *images = new_images;
206   }
207   lua_pop(L, 3);
208   dt_lua_unlock();
209   return 0;
210 }
finalize_store_wrapper(struct dt_imageio_module_storage_t * self,dt_imageio_module_data_t * data)211 static void finalize_store_wrapper(struct dt_imageio_module_storage_t *self, dt_imageio_module_data_t *data)
212 {
213   dt_lua_lock();
214   lua_State *L = darktable.lua_state.state;
215 
216   lua_getfield(L, LUA_REGISTRYINDEX, "dt_lua_storages");
217   lua_getfield(L, -1, self->plugin_name);
218   lua_getfield(L, -1, "finalize_store");
219 
220   if(lua_isnil(L, -1))
221   {
222     lua_pop(L, 3);
223     dt_lua_unlock();
224     return;
225   }
226 
227   luaA_push_type(L, self->parameter_lua_type, data);
228 
229   lua_storage_t *d = (lua_storage_t *)data;
230   push_lua_data(L, d);
231   dt_lua_goto_subtable(L, "files");
232 
233   push_lua_data(L, d);
234   dt_lua_goto_subtable(L, "extra");
235 
236   dt_lua_treated_pcall(L, 3, 0);
237   lua_pop(L, 2);
238   dt_lua_unlock();
239 }
params_size_wrapper(struct dt_imageio_module_storage_t * self)240 static size_t params_size_wrapper(struct dt_imageio_module_storage_t *self)
241 {
242   return 0;
243 }
get_params_wrapper(struct dt_imageio_module_storage_t * self)244 static void *get_params_wrapper(struct dt_imageio_module_storage_t *self)
245 {
246   lua_storage_t *d = malloc(sizeof(lua_storage_t));
247   d->data_created = false;
248   return d;
249 }
250 
251 typedef struct
252 {
253   lua_storage_t *data;
254 } free_param_wrapper_data;
255 
free_param_wrapper_destroy(void * data)256 static void free_param_wrapper_destroy(void * data)
257 {
258   if(!data) return;
259   free_param_wrapper_data *params = data;
260   lua_storage_t *d = params->data;
261   if(d->data_created)
262   {
263     // if we reach here, then the main job hasn't been executed.
264     // This means that we are in an error path, and might be in the GUI thread
265     // we take the lock anyway to avoid a memory leak, but this might freeze the UI
266     dt_lua_lock();
267     lua_pushlightuserdata(darktable.lua_state.state, d);
268     lua_pushnil(darktable.lua_state.state);
269     lua_settable(darktable.lua_state.state, LUA_REGISTRYINDEX);
270     dt_lua_unlock();
271   }
272   free(d);
273   free(params);
274 }
free_param_wrapper_job(dt_job_t * job)275 static int32_t free_param_wrapper_job(dt_job_t *job)
276 {
277   free_param_wrapper_data *params = dt_control_job_get_params(job);
278   lua_storage_t *d = params->data;
279   if(d->data_created)
280   {
281     dt_lua_lock();
282     lua_pushlightuserdata(darktable.lua_state.state, d);
283     lua_pushnil(darktable.lua_state.state);
284     lua_settable(darktable.lua_state.state, LUA_REGISTRYINDEX);
285     dt_lua_unlock();
286     d->data_created = false;
287   }
288   return 0;
289 }
290 
291 
free_params_wrapper(struct dt_imageio_module_storage_t * self,dt_imageio_module_data_t * data)292 static void free_params_wrapper(struct dt_imageio_module_storage_t *self, dt_imageio_module_data_t *data)
293 {
294   dt_job_t *job = dt_control_job_create(&free_param_wrapper_job, "lua: destroy storage param");
295   if(!job) return;
296   free_param_wrapper_data *t = (free_param_wrapper_data *)calloc(1, sizeof(free_param_wrapper_data));
297   if(!t)
298   {
299     dt_control_job_dispose(job);
300     return;
301   }
302   dt_control_job_set_params(job, t, free_param_wrapper_destroy);
303   t->data = (lua_storage_t *)data;
304   dt_control_add_job(darktable.control, DT_JOB_QUEUE_SYSTEM_BG, job);
305 }
306 
set_params_wrapper(struct dt_imageio_module_storage_t * self,const void * params,const int size)307 static int set_params_wrapper(struct dt_imageio_module_storage_t *self, const void *params, const int size)
308 {
309   return 0;
310 }
311 
ask_user_confirmation_wrapper(struct dt_imageio_module_storage_t * self)312 static char *ask_user_confirmation_wrapper(struct dt_imageio_module_storage_t *self)
313 {
314   return NULL;
315 }
316 
version_wrapper()317 static int version_wrapper()
318 {
319   return 0;
320 }
321 
gui_init_wrapper(struct dt_imageio_module_storage_t * self)322 static void gui_init_wrapper(struct dt_imageio_module_storage_t *self)
323 {
324   lua_storage_gui_t *gui_data = self->gui_data;
325   self->widget = gui_data->widget->widget;
326 }
327 
gui_reset_wrapper(struct dt_imageio_module_storage_t * self)328 static void gui_reset_wrapper(struct dt_imageio_module_storage_t *self)
329 {
330   lua_storage_gui_t *gui_data = self->gui_data;
331   dt_lua_async_call_alien(dt_lua_widget_trigger_callback,
332       0, NULL, NULL,
333       LUA_ASYNC_TYPENAME, "lua_widget", gui_data->widget,
334       LUA_ASYNC_TYPENAME, "const char*", "reset",
335       LUA_ASYNC_DONE);
336 }
337 
gui_cleanup_wrapper(struct dt_imageio_module_storage_t * self)338 static void gui_cleanup_wrapper(struct dt_imageio_module_storage_t *self)
339 {
340   self->widget = NULL;
341 }
342 
343 
344 static dt_imageio_module_storage_t ref_storage = {
345   .plugin_name = { 0 },
346   .module = NULL,
347   .widget = NULL,
348   .gui_data = NULL,
349   .name = name_wrapper,
350   .gui_init = gui_init_wrapper,
351   .gui_cleanup = gui_cleanup_wrapper,
352   .gui_reset = gui_reset_wrapper,
353   .init = NULL,
354   .supported = default_supported_wrapper,
355   .dimension = default_dimension_wrapper,
356   .recommended_dimension = default_dimension_wrapper,
357   .store = store_wrapper,
358   .finalize_store = finalize_store_wrapper,
359   .initialize_store = initialize_store_wrapper,
360   .params_size = params_size_wrapper,
361   .get_params = get_params_wrapper,
362   .free_params = free_params_wrapper,
363   .set_params = set_params_wrapper,
364   .export_dispatched = empty_wrapper,
365   .ask_user_confirmation = ask_user_confirmation_wrapper,
366   .parameter_lua_type = LUAA_INVALID_TYPE,
367   .version = version_wrapper,
368 
369 };
370 
371 
372 
register_storage(lua_State * L)373 static int register_storage(lua_State *L)
374 {
375   lua_settop(L, 7);
376   lua_getfield(L, LUA_REGISTRYINDEX, "dt_lua_storages");
377   lua_newtable(L);
378 
379   dt_imageio_module_storage_t *storage = malloc(sizeof(dt_imageio_module_storage_t));
380   memcpy(storage, &ref_storage, sizeof(dt_imageio_module_storage_t));
381   storage->gui_data = malloc(sizeof(lua_storage_gui_t));
382   lua_storage_gui_t *data = storage->gui_data;
383 
384   const char *plugin_name = luaL_checkstring(L, 1);
385   lua_pushvalue(L, 1);
386   lua_setfield(L, -2, "plugin_name");
387   g_strlcpy(storage->plugin_name, plugin_name, sizeof(storage->plugin_name));
388 
389   const char *name = luaL_checkstring(L, 2);
390   lua_pushvalue(L, 2);
391   lua_setfield(L, -2, "name");
392   data->name = strdup(name);
393   data->supported_formats = NULL;
394   data->widget = NULL;
395 
396   if(!lua_isnoneornil(L, 3))
397   {
398     luaL_checktype(L, 3, LUA_TFUNCTION);
399     lua_pushvalue(L, 3);
400     lua_setfield(L, -2, "store");
401   }
402 
403   if(lua_isnil(L, 4))
404   {
405     storage->finalize_store = NULL;
406   }
407   else
408   {
409     luaL_checktype(L, 4, LUA_TFUNCTION);
410     lua_pushvalue(L, 4);
411     lua_setfield(L, -2, "finalize_store");
412   }
413 
414   if(!lua_isnoneornil(L, 5))
415   {
416     luaL_checktype(L, 5, LUA_TFUNCTION);
417     lua_pushvalue(L, 5);
418     lua_setfield(L, -2, "supported");
419   }
420 
421   if(lua_isnil(L, 6))
422   {
423     storage->initialize_store = NULL;
424   }
425   else
426   {
427     luaL_checktype(L, 6, LUA_TFUNCTION);
428     lua_pushvalue(L, 6);
429     lua_setfield(L, -2, "initialize_store");
430   }
431 
432   if(lua_isnil(L, 7))
433   {
434     storage->gui_init = empty_wrapper;
435     storage->gui_reset = empty_wrapper;
436     storage->gui_cleanup = empty_wrapper;
437   }
438   else
439   {
440     lua_widget widget;
441     luaA_to(L, lua_widget, &widget, 7);
442     dt_lua_widget_bind(L, widget);
443     data->widget = widget;
444   }
445 
446 
447   lua_setfield(L, -2, plugin_name);
448 
449   char tmp[1024];
450   snprintf(tmp, sizeof(tmp), "dt_imageio_module_data_pseudo_%s", storage->plugin_name);
451   luaA_Type type_id = luaA_type_add(L, tmp, storage->params_size(storage));
452   storage->parameter_lua_type = dt_lua_init_type_type(darktable.lua_state.state, type_id);
453   luaA_struct_type(darktable.lua_state.state, type_id);
454   dt_lua_register_storage_type(darktable.lua_state.state, storage, type_id);
455 
456   if(!lua_isnoneornil(L, 5))
457   {
458     for(GList *it = darktable.imageio->plugins_format; it; it = g_list_next(it))
459     {
460       lua_pushvalue(L, 5);
461       dt_imageio_module_format_t *format = (dt_imageio_module_format_t *)it->data;
462       dt_imageio_module_data_t *sdata = storage->get_params(storage);
463       dt_imageio_module_data_t *fdata = format->get_params(format);
464       luaA_push_type(L, storage->parameter_lua_type, sdata);
465       luaA_push_type(L, format->parameter_lua_type, fdata);
466       format->free_params(format, fdata);
467       storage->free_params(storage, sdata);
468       dt_lua_treated_pcall(L, 2, 1);
469       int result = lua_toboolean(L, -1);
470       lua_pop(L, 1);
471       if(result)
472       {
473         data->supported_formats = g_list_prepend(data->supported_formats, format);
474       }
475     }
476   }
477   else
478   {
479     // all formats are supported
480     for(GList *it = darktable.imageio->plugins_format; it; it = g_list_next(it))
481     {
482       dt_imageio_module_format_t *format = (dt_imageio_module_format_t *)it->data;
483       data->supported_formats = g_list_prepend(data->supported_formats, format);
484     }
485   }
486 
487   storage->gui_init(storage);
488   if(storage->widget) g_object_ref(storage->widget);
489   dt_imageio_insert_storage(storage);
490 
491   return 0;
492 }
493 
destroy_storage(lua_State * L)494 static int destroy_storage(lua_State *L)
495 {
496   const char *module_name = luaL_checkstring(L, 1);
497   dt_imageio_module_storage_t *storage = dt_imageio_get_storage_by_name(module_name);
498   dt_imageio_remove_storage(storage);
499   // free the storage?
500   storage->gui_cleanup(storage);
501   if(storage->widget) g_object_unref(storage->widget);
502   if(storage->module) g_module_close(storage->module);
503   free(storage);
504   return 0;
505 }
506 
dt_lua_init_luastorages(lua_State * L)507 int dt_lua_init_luastorages(lua_State *L)
508 {
509   dt_lua_push_darktable_lib(L);
510   lua_pushstring(L, "destroy_storage");
511   lua_pushcfunction(L, &destroy_storage);
512   lua_settable(L, -3);
513   lua_pop(L, 1);
514 
515   dt_lua_push_darktable_lib(L);
516   lua_pushstring(L, "register_storage");
517   lua_pushcfunction(L, &register_storage);
518   lua_settable(L, -3);
519   lua_pop(L, 1);
520 
521   lua_newtable(L);
522   lua_setfield(L, LUA_REGISTRYINDEX, "dt_lua_storages");
523   return 0;
524 }
525 
526 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
527 // vim: shiftwidth=2 expandtab tabstop=2 cindent
528 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
529