static int lua_job_valid(lua_State*L) { dt_progress_t *progress; luaA_to(L, dt_lua_backgroundjob_t, &progress, 1); if(lua_isnone(L, 3)) { dt_lua_unlock(false); gboolean i_own_lock = dt_control_gdk_lock(); dt_pthread_mutex_lock(&darktable.control->progress_system.mutex); GList *iter = g_list_find(darktable.control->progress_system.list, progress); dt_pthread_mutex_unlock(&darktable.control->progress_system.mutex); if(i_own_lock) dt_control_gdk_unlock(); dt_lua_lock(); if(iter) lua_pushboolean(L, true); else lua_pushboolean(L, false); return 1; } else { int validity = lua_toboolean(L, 3); if(validity) return luaL_argerror(L, 3, "a job can not be made valid"); dt_lua_unlock(false); dt_control_progress_destroy(darktable.control, progress); dt_lua_lock(); return 0; } }
static void finalize_store_wrapper(struct dt_imageio_module_storage_t *self, dt_imageio_module_data_t *data) { dt_lua_lock(); lua_State *L = darktable.lua_state.state; lua_getfield(L, LUA_REGISTRYINDEX, "dt_lua_storages"); lua_getfield(L, -1, self->plugin_name); lua_getfield(L, -1, "finalize_store"); if(lua_isnil(L, -1)) { lua_pop(L, 3); dt_lua_unlock(); return; } luaA_push_type(L, self->parameter_lua_type, data); lua_storage_t *d = (lua_storage_t *)data; push_lua_data(L,d); dt_lua_goto_subtable(L,"files"); push_lua_data(L,d); dt_lua_goto_subtable(L,"extra"); dt_lua_treated_pcall(L,3,0); lua_pop(L, 2); dt_lua_unlock(); }
static int lua_job_progress(lua_State *L) { dt_progress_t *progress; luaA_to(L, dt_lua_backgroundjob_t, &progress, 1); dt_lua_unlock(false); gboolean i_own_lock = dt_control_gdk_lock(); dt_pthread_mutex_lock(&darktable.control->progress_system.mutex); GList *iter = g_list_find(darktable.control->progress_system.list, progress); dt_pthread_mutex_unlock(&darktable.control->progress_system.mutex); if(i_own_lock) dt_control_gdk_unlock(); dt_lua_lock(); if(!iter) luaL_error(L,"Accessing an invalid job"); if(lua_isnone(L, 3)) { dt_lua_unlock(false); double result = dt_control_progress_get_progress(progress); dt_lua_lock(); if(!dt_control_progress_has_progress_bar(progress)) lua_pushnil(L); else lua_pushnumber(L, result); return 1; } else { double value; luaA_to(L,progress_double,&value,3); dt_lua_unlock(false); dt_control_progress_set_progress(darktable.control, progress, value); dt_lua_lock(); return 0; } }
static void 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) { gboolean has_lock = dt_lua_lock(); lua_State *L =darktable.lua_state.state; lua_getfield(L,LUA_REGISTRYINDEX,"dt_lua_storages"); lua_getfield(L,-1,self->plugin_name); lua_getfield(L,-1,"initialize_store"); if(lua_isnil(L,-1)) { lua_pop(L,3); dt_lua_unlock(has_lock); return; } luaA_push_typeid(L,self->parameter_lua_type,data); luaA_push_typeid(L,format->parameter_lua_type,fdata); GList* imgids =*images; lua_newtable(L); while(imgids) { luaA_push(L,dt_lua_image_t,&(imgids->data)); luaL_ref(L,-2); imgids = g_list_next(imgids); } lua_pushboolean(L,high_quality); lua_storage_t *d = (lua_storage_t*) data; if(!d->data_created) { lua_pushlightuserdata(L,d); lua_newtable(L); lua_settable(L,LUA_REGISTRYINDEX); d->data_created = true; } lua_pushlightuserdata(L,data); lua_gettable(L,LUA_REGISTRYINDEX); dt_lua_do_chunk_silent(L,5,1); if(!lua_isnoneornil(L,-1)) { luaL_checktype(L,-1,LUA_TTABLE); g_list_free(*images); GList *new_images=NULL; lua_pushnil(L); while(lua_next(L,-2)){ dt_lua_image_t imgid; luaA_to(L,dt_lua_image_t,&imgid,-1); new_images = g_list_prepend(new_images,GINT_TO_POINTER(imgid)); lua_pop(L,1); } new_images = g_list_reverse(new_images); *images = new_images; } lua_pop(L,3); dt_lua_unlock(has_lock); }
static int selection_cb(lua_State *L) { GList *image = dt_collection_get_selected(darktable.collection, -1); if(lua_gettop(L) > 0) { GList * new_selection = NULL; luaL_checktype(L,-1,LUA_TTABLE); lua_pushnil(L); while (lua_next(L, -2) != 0) { /* uses 'key' (at index -2) and 'value' (at index -1) */ int imgid; luaA_to(L,dt_lua_image_t,&imgid,-1); new_selection = g_list_prepend(new_selection,GINT_TO_POINTER(imgid)); lua_pop(L,1); } new_selection = g_list_reverse(new_selection); dt_lua_unlock(true);// we need the gdk lock to update ui information dt_selection_clear(darktable.selection); dt_selection_select_list(darktable.selection,new_selection); dt_lua_lock(); g_list_free(new_selection); } lua_newtable(L); while(image) { luaA_push(L,dt_lua_image_t,&image->data); luaL_ref(L,-2); image = g_list_delete_link(image, image); } return 1; }
static int write_image(lua_State *L) { /* check that param 1 is a module_format_t */ luaL_argcheck(L,dt_lua_isa(L,1,dt_imageio_module_format_t),-1,"dt_imageio_module_format_t expected"); lua_getmetatable(L,1); lua_getfield(L,-1,"__luaA_Type"); luaA_Type format_type = luaL_checkint(L,-1); lua_pop(L,1); lua_getfield(L,-1,"__associated_object"); dt_imageio_module_format_t * format = lua_touserdata(L,-1); lua_pop(L,2); dt_imageio_module_data_t* fdata = format->get_params(format); luaA_to_type(L,format_type,fdata,1); /* check that param 2 is an image */ dt_lua_image_t imgid; luaA_to(L,dt_lua_image_t,&imgid,2); /* check that param 3 is a string (filename) */ const char * filename = luaL_checkstring(L,3); dt_lua_unlock(false); gboolean high_quality = dt_conf_get_bool("plugins/lighttable/export/high_quality_processing"); gboolean result = dt_imageio_export(imgid,filename,format,fdata,high_quality,FALSE,NULL,NULL); dt_lua_lock(); lua_pushboolean(L,result); format->free_params(format,fdata); return 1; }
int dt_lua_gtk_wrap(lua_State*L) { lua_pushvalue(L,lua_upvalueindex(1)); lua_insert(L,1); if(pthread_equal(darktable.control->gui_thread, pthread_self())) { return dt_lua_do_chunk_raise(L,lua_gettop(L)-1,LUA_MULTRET); } else { dt_lua_unlock(); gtk_wrap_communication communication; g_mutex_init(&communication.end_mutex); g_cond_init(&communication.end_cond); communication.L = L; g_mutex_lock(&communication.end_mutex); g_main_context_invoke(NULL,dt_lua_gtk_wrap_callback,&communication); g_cond_wait(&communication.end_cond,&communication.end_mutex); g_mutex_unlock(&communication.end_mutex); g_mutex_clear(&communication.end_mutex); dt_lua_lock(); if(communication.retval == LUA_OK) { return lua_gettop(L); } else { return lua_error(L); } } }
static int gtk_wrap(lua_State*L) { lua_pushvalue(L,lua_upvalueindex(1)); lua_insert(L,1); if(pthread_equal(darktable.control->gui_thread, pthread_self())) { lua_call(L, lua_gettop(L)-1, LUA_MULTRET); return lua_gettop(L); } else { #ifdef _DEBUG dt_print(DT_DEBUG_LUA, "LUA DEBUG : %s called from %s %llu\n", __FUNCTION__, lua_tostring(L, lua_upvalueindex(2)), lua_tointeger(L, lua_upvalueindex(3))); #endif dt_lua_unlock(); gtk_wrap_communication communication; g_mutex_init(&communication.end_mutex); g_cond_init(&communication.end_cond); communication.L = L; g_mutex_lock(&communication.end_mutex); g_main_context_invoke(NULL,dt_lua_gtk_wrap_callback,&communication); g_cond_wait(&communication.end_cond,&communication.end_mutex); g_mutex_unlock(&communication.end_mutex); g_mutex_clear(&communication.end_mutex); dt_lua_lock(); #ifdef _DEBUG dt_print(DT_DEBUG_LUA, "LUA DEBUG : %s return for call from from %s %llu\n", __FUNCTION__, lua_tostring(L, lua_upvalueindex(2)), lua_tointeger(L, lua_upvalueindex(3))); #endif if(communication.retval == LUA_OK) { return lua_gettop(L); } else { return lua_error(L); } } }
static void run_async_thread_main(gpointer data,gpointer user_data) { // lua lock ownership transfered from parent thread int thread_num = GPOINTER_TO_INT(data); lua_State*L = darktable.lua_state.state; lua_State* thread = get_thread(L,thread_num); if(!thread) { dt_print(DT_DEBUG_LUA, "LUA ERROR : no thread found, this should never happen\n"); return; } dt_lua_finish_callback cb = lua_touserdata(thread,1); void * cb_data = lua_touserdata(thread,2); int nresults = lua_tointeger(thread, 3); lua_pushcfunction(thread,create_backtrace); lua_insert(thread,4); int thread_result = lua_pcall(thread, lua_gettop(thread)-5,nresults,4); if(cb) { cb(thread,thread_result,cb_data); } else { dt_lua_check_print_error(thread,thread_result); } drop_thread(L,thread_num); dt_lua_unlock(); return; }
static int lua_create_job(lua_State *L) { const char * message = luaL_checkstring(L, 1); gboolean has_progress_bar = lua_toboolean(L, 2); int cancellable = FALSE; if(!lua_isnoneornil(L,3)) { luaL_checktype(L, 3, LUA_TFUNCTION); cancellable = TRUE; } dt_lua_unlock(false); dt_progress_t *progress = dt_control_progress_create(darktable.control, has_progress_bar, message); if(cancellable) { dt_control_progress_make_cancellable(darktable.control, progress, lua_job_cancelled, progress); } dt_lua_lock(); luaA_push(L, dt_lua_backgroundjob_t, &progress); if(cancellable) { lua_getuservalue(L, -1); lua_pushvalue(L, 3); lua_setfield(L, -2, "cancel_callback"); lua_pop(L, 1); } return 1; }
void dt_lua_finalize_early() { darktable.lua_state.ending = true; dt_lua_lock(); dt_lua_event_trigger(darktable.lua_state.state,"exit",0); dt_lua_unlock(); g_main_context_wakeup(darktable.lua_state.context); }
static int sleep_cb(lua_State*L) { const int delay = luaL_optinteger(L, 1, 0); dt_lua_unlock(); g_usleep(delay*1000); dt_lua_lock(); return 0; }
static int execute_cb(lua_State*L) { const char *cmd = luaL_optstring(L, 1, NULL); dt_lua_unlock(); int stat = system(cmd); dt_lua_lock(); lua_pushinteger(L,stat); return 1; }
static int32_t shortcut_callback_job(struct dt_job_t *job) { gboolean has_lock = dt_lua_lock(); shortcut_callback_data *t = (shortcut_callback_data*)job->param; lua_pushstring(darktable.lua_state.state,t->name); free(t->name); run_event("shortcut",1); dt_lua_unlock(has_lock); return 0; }
static int32_t on_image_imported_callback_job(dt_job_t *job) { gboolean has_lock = dt_lua_lock(); on_image_imported_callback_data_t *t = dt_control_job_get_params(job); luaA_push(darktable.lua_state.state,dt_lua_image_t,&t->imgid); dt_lua_event_trigger(darktable.lua_state.state,"post-import-image",1); free(t); // i am not sure if the free() may happen before the dt_lua_event_trigger as a pointer to the imgid inside of it is pushed to the lua stack dt_lua_unlock(has_lock); return 0; }
static void on_export_image_tmpfile(gpointer instance, int imgid, char *filename, gpointer user_data){ gboolean has_lock = dt_lua_lock(); luaA_push(darktable.lua_state.state,dt_lua_image_t,&imgid); lua_pushstring(darktable.lua_state.state,filename); run_event("intermediate-export-image",2); dt_lua_unlock(has_lock); }
void dt_lua_redraw_screen() { if(darktable.gui != NULL) { dt_lua_unlock(false); dt_control_signal_raise(darktable.signals, DT_SIGNAL_FILMROLLS_CHANGED); // just for good measure dt_control_queue_redraw(); dt_lua_lock(); } }
static void finalize_store_wrapper (struct dt_imageio_module_storage_t *self, dt_imageio_module_data_t *data) { gboolean has_lock = dt_lua_lock(); lua_State *L =darktable.lua_state.state; lua_getfield(L,LUA_REGISTRYINDEX,"dt_lua_storages"); lua_getfield(L,-1,self->plugin_name); lua_getfield(L,-1,"finalize_store"); if(lua_isnil(L,-1)) { lua_pop(L,3); dt_lua_unlock(has_lock); return; } luaA_push_typeid(L,self->parameter_lua_type,data); lua_storage_t *d = (lua_storage_t*) data; GList* imgids =d->imgids; GList* file_names = d->file_names; lua_newtable(L); while(imgids) { luaA_push(L,dt_lua_image_t,&(imgids->data)); lua_pushstring(L,file_names->data); lua_settable(L,-3); imgids = g_list_next(imgids); file_names = g_list_next(file_names); } if(!d->data_created) { lua_pushlightuserdata(darktable.lua_state.state,d); lua_newtable(darktable.lua_state.state); lua_settable(darktable.lua_state.state,LUA_REGISTRYINDEX); d->data_created = true; } lua_pushlightuserdata(L,data); lua_gettable(L,LUA_REGISTRYINDEX); dt_lua_do_chunk_silent(L,3,0); lua_pop(L,2); dt_lua_unlock(has_lock); }
static gboolean alien_job_dispatch (GSource* source, GSourceFunc callback, gpointer user_data) { gpointer message; message = g_async_queue_try_pop (darktable.lua_state.alien_job_queue); if (message == NULL) { return TRUE; } async_call_data* data = (async_call_data*)message; dt_lua_lock(); lua_State* L= darktable.lua_state.state; lua_State *new_thread = lua_newthread(L); int reference = save_thread(L); lua_pushlightuserdata(new_thread,data->cb); lua_pushlightuserdata(new_thread,data->cb_data); lua_pushinteger(new_thread,data->nresults); lua_pushcfunction(new_thread,data->pusher); GList* cur_elt = data->extra; while(cur_elt) { GList * type_type_elt = cur_elt; cur_elt = g_list_next(cur_elt); GList * type_elt = cur_elt; cur_elt = g_list_next(cur_elt); GList * data_elt = cur_elt; cur_elt = g_list_next(cur_elt); switch(GPOINTER_TO_INT(type_type_elt->data)) { case LUA_ASYNC_TYPEID_WITH_FREE: // skip the destructor cur_elt = g_list_next(cur_elt); // do not break case LUA_ASYNC_TYPEID: luaA_push_type(new_thread,GPOINTER_TO_INT(type_elt->data),data_elt->data); break; case LUA_ASYNC_TYPENAME_WITH_FREE: // skip the destructor cur_elt = g_list_next(cur_elt); // do not break case LUA_ASYNC_TYPENAME: luaA_push_type(new_thread,luaA_type_find(L,type_elt->data),&data_elt->data); break; case LUA_ASYNC_DONE: default: // should never happen g_assert(false); break; } } run_async_thread(L,reference); dt_lua_unlock(); alien_job_destroy(data); return G_SOURCE_CONTINUE; }
static int32_t view_changed_callback_job(dt_job_t *job) { dt_lua_lock(); view_changed_callback_data_t *t = dt_control_job_get_params(job); dt_lua_module_entry_push(darktable.lua_state.state, "view", t->old_view->module_name); dt_lua_module_entry_push(darktable.lua_state.state, "view", t->new_view->module_name); free(t); dt_lua_event_trigger(darktable.lua_state.state, "view-changed", 2); dt_lua_unlock(); return 0; }
gboolean dt_lua_gtk_wrap_callback(gpointer data) { dt_lua_lock_silent(); gtk_wrap_communication *communication = (gtk_wrap_communication*)data; g_mutex_lock(&communication->end_mutex); communication->retval = dt_lua_do_chunk(communication->L,lua_gettop(communication->L)-1,LUA_MULTRET); g_cond_signal(&communication->end_cond); g_mutex_unlock(&communication->end_mutex); dt_lua_unlock(); return false; }
gboolean dt_lua_gtk_wrap_callback(gpointer data) { dt_lua_lock_silent(); gtk_wrap_communication *communication = (gtk_wrap_communication*)data; g_mutex_lock(&communication->end_mutex); // TODO : propre stack unwinding communication->retval = lua_pcall(communication->L,lua_gettop(communication->L)-1,LUA_MULTRET,0); g_cond_signal(&communication->end_cond); g_mutex_unlock(&communication->end_mutex); dt_lua_unlock(); return false; }
static int32_t on_mouse_over_image_changed_callback_job(dt_job_t *job) { dt_lua_lock(); on_mouse_over_image_changed_callback_data_t *t = dt_control_job_get_params(job); int n_params = (t->imgid != -1); if(n_params) luaA_push(darktable.lua_state.state, dt_lua_image_t, &t->imgid); dt_lua_event_trigger(darktable.lua_state.state, "mouse-over-image-changed", n_params); free(t); dt_lua_unlock(); return 0; }
static int32_t widget_callback_job(dt_job_t *job) { dt_lua_lock(); lua_State* L= darktable.lua_state.state; widget_callback_data* data = (widget_callback_data*)dt_control_job_get_params(job); dt_lua_widget_trigger_callback_glist(L,data->object,data->event_name,data->extra); free(data->event_name); free(data); dt_lua_unlock(); return 0; }
void dt_lua_init(lua_State *L, const char *lua_command) { char tmp_path[PATH_MAX] = { 0 }; // init the lua environment lua_CFunction *cur_type = init_funcs; while(*cur_type) { (*cur_type)(L); cur_type++; } assert(lua_gettop(L) == 0); // if you are here, you have probably added an initialisation function that is not stack clean // build the table containing the configuration info lua_getglobal(L, "package"); dt_lua_goto_subtable(L, "loaded"); lua_pushstring(L, "darktable"); dt_lua_push_darktable_lib(L); lua_settable(L, -3); lua_pop(L, 1); lua_getglobal(L, "package"); lua_getfield(L, -1, "path"); lua_pushstring(L, ";"); dt_loc_get_datadir(tmp_path, sizeof(tmp_path)); lua_pushstring(L, tmp_path); lua_pushstring(L, "/lua/?.lua"); lua_pushstring(L, ";"); dt_loc_get_user_config_dir(tmp_path, sizeof(tmp_path)); lua_pushstring(L, tmp_path); lua_pushstring(L, "/lua/?.lua"); lua_concat(L, 7); lua_setfield(L, -2, "path"); lua_pop(L, 1); lua_pushcfunction(L,run_early_script); lua_pushstring(L,lua_command); if(darktable.gui) { dt_lua_do_chunk_later(L,1); } else { dt_lua_do_chunk_silent(L,1,0); } // allow other threads to wake up and do their job dt_lua_unlock(); }
static int32_t on_mouse_over_image_changed_callback_job(dt_job_t *job) { gboolean has_lock = dt_lua_lock(); on_mouse_over_image_changed_callback_data_t *t = dt_control_job_get_params(job); int n_params = (t->imgid != -1); if(n_params) luaA_push(darktable.lua_state.state, dt_lua_image_t, &t->imgid); dt_lua_event_trigger(darktable.lua_state.state, "mouse-over-image-changed", n_params); free(t); // i am not sure if the free() may happen before the dt_lua_event_trigger as a pointer to the imgid // inside of it is pushed to the lua stack dt_lua_unlock(has_lock); return 0; }
static int read_cb(lua_State*L) { luaL_checkudata(L,1,LUA_FILEHANDLE); luaL_Stream *stream = lua_touserdata(L, 1); int myfileno = fileno(stream->f); fd_set fdset; FD_ZERO(&fdset); FD_SET(myfileno, &fdset); dt_lua_unlock(); select(myfileno + 1, &fdset, NULL, NULL, 0); dt_lua_lock(); return 0; }
static int32_t lua_job_canceled_job(dt_job_t *job) { dt_progress_t *progress = dt_control_job_get_params(job); lua_State * L = darktable.lua_state.state; gboolean has_lock = dt_lua_lock(); luaA_push(L, dt_lua_backgroundjob_t, &progress); lua_getuservalue(L, -1); lua_getfield(L, -1, "cancel_callback"); lua_pushvalue(L, -3); dt_lua_do_chunk(L, 1, 0); lua_pop(L, 2); dt_lua_unlock(has_lock); return 0; }
static gboolean string_job_dispatch (GSource* source, GSourceFunc callback, gpointer user_data) { gpointer message; message = g_async_queue_try_pop (darktable.lua_state.string_job_queue); if (message == NULL) { return TRUE; } string_call_data* data = (string_call_data*)message; dt_lua_lock(); lua_State* L= darktable.lua_state.state; lua_State *new_thread = lua_newthread(L); int reference = save_thread(L); lua_pushlightuserdata(new_thread,data->cb); lua_pushlightuserdata(new_thread,data->cb_data); lua_pushinteger(new_thread,data->nresults); int load_result = luaL_loadstring(new_thread, data->function); if(load_result != LUA_OK) { if(data->cb) { data->cb(new_thread,load_result,data->cb_data); } else { dt_lua_check_print_error(new_thread,load_result); } drop_thread(L,reference); dt_lua_unlock(); string_data_destroy(data); return G_SOURCE_CONTINUE; } run_async_thread(L,reference); dt_lua_unlock(); string_data_destroy(data); return G_SOURCE_CONTINUE; }
void dt_lua_finalize_early() { darktable.lua_state.ending = true; dt_lua_lock(); dt_lua_event_trigger(darktable.lua_state.state,"exit",0); dt_lua_unlock(); int i = 10; while(i && darktable.lua_state.pending_threads){ dt_print(DT_DEBUG_LUA, "LUA : waiting for %d threads to finish...\n", darktable.lua_state.pending_threads); sleep(1);// give them a little time to finish i--; } if(darktable.lua_state.pending_threads) dt_print(DT_DEBUG_LUA, "LUA : all threads did not finish properly.\n"); }