/* This file is part of darktable, copyright (c) 2009--2012 johannes hanika. copyright (c) 2010--2012 henrik andersson. copyright (c) 2010--2012 tobias ellinghaus. darktable is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. darktable is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with darktable. If not, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) #include #endif #ifdef __APPLE__ #include #endif #include "common/collection.h" #include "common/colorspaces.h" #include "common/darktable.h" #include "common/exif.h" #include "common/fswatch.h" #include "common/pwstorage/pwstorage.h" #include "common/selection.h" #include "common/system_signal_handling.h" #ifdef HAVE_GPHOTO2 #include "common/camera_control.h" #endif #include "bauhaus/bauhaus.h" #include "common/cpuid.h" #include "common/film.h" #include "common/grealpath.h" #include "common/image.h" #include "common/image_cache.h" #include "common/imageio_module.h" #include "common/mipmap_cache.h" #include "common/noiseprofiles.h" #include "common/opencl.h" #include "common/points.h" #include "common/resource_limits.h" #include "control/conf.h" #include "control/control.h" #include "control/crawler.h" #include "control/jobs/control_jobs.h" #include "control/signal.h" #include "develop/blend.h" #include "develop/imageop.h" #include "gui/gtk.h" #include "gui/guides.h" #include "gui/presets.h" #include "libs/lib.h" #include "lua/init.h" #include "views/undo.h" #include "views/view.h" #include #include #include #include #include #include #include #include #include #include #include #if defined(__SSE__) #include #endif #ifdef HAVE_GRAPHICSMAGICK #include #endif #include "dbus.h" #if defined(__SUNOS__) #include #endif #ifdef _OPENMP #include #endif #ifdef USE_LUA #include "lua/configuration.h" #endif darktable_t darktable; const char dt_supported_extensions[] = "3fr,arw,bay,bmq,cap,cine,cr2,crw,cs1,dc2,dcr,dng,erf,exr,fff,hdr,ia,iiq," "jpeg,jpg,k25,kc2,kdc,mdc,mef,mos,mrw,nef,nrw,orf,pef,pfm,pxn,qtk,raf," "raw,rdc,rw2,rwl,sr2,srf,srw,sti,tif,tiff,x3f,png,ari" #ifdef HAVE_OPENJPEG ",j2c,j2k,jp2,jpc" #endif #ifdef HAVE_GRAPHICSMAGICK ",gif,jpc,jp2,bmp,dcm,jng,miff,mng,pbm,pnm,ppm,pgm" #endif ; static int usage(const char *argv0) { printf("usage: %s [-d " "{all,cache,camctl,camsupport,control,dev,fswatch,input,lighttable,masks,memory,nan,opencl,perf,pwstorage,print,sql}]" " [IMG_1234.{RAW,..}|image_folder/]", argv0); #ifdef HAVE_OPENCL printf(" [--disable-opencl]"); #endif printf(" [--library ]"); printf(" [--datadir ]"); printf(" [--moduledir ]"); printf(" [--tmpdir ]"); printf(" [--configdir ]"); printf(" [--cachedir ]"); printf(" [--localedir ]"); #ifdef USE_LUA printf(" [--luacmd ]"); #endif printf(" [--conf =]"); printf(" [--noiseprofiles ]"); printf("\n"); return 1; } gboolean dt_supported_image(const gchar *filename) { gboolean supported = FALSE; char **extensions = g_strsplit(dt_supported_extensions, ",", 100); char *ext = g_strrstr(filename, "."); if(!ext) { g_strfreev(extensions); return FALSE; } ext++; for(char **i = extensions; *i != NULL; i++) if(!g_ascii_strncasecmp(ext, *i, strlen(*i))) { supported = TRUE; break; } g_strfreev(extensions); return supported; } static void strip_semicolons_from_keymap(const char *path) { char pathtmp[PATH_MAX] = { 0 }; FILE *fin = fopen(path, "r"); FILE *fout; int i; int c = '\0'; snprintf(pathtmp, sizeof(pathtmp), "%s_tmp", path); fout = fopen(pathtmp, "w"); // First ignoring the first three lines for(i = 0; i < 3; i++) { c = fgetc(fin); while(c != '\n') c = fgetc(fin); } // Then ignore the first two characters of each line, copying the rest out while(c != EOF) { fseek(fin, 2, SEEK_CUR); do { c = fgetc(fin); if(c != EOF) fputc(c, fout); } while(c != '\n' && c != EOF); } fclose(fin); fclose(fout); GFile *gpath = g_file_new_for_path(path); GFile *gpathtmp = g_file_new_for_path(pathtmp); g_file_delete(gpath, NULL, NULL); g_file_move(gpathtmp, gpath, 0, NULL, NULL, NULL, NULL); g_object_unref(gpath); g_object_unref(gpathtmp); } static gchar *dt_make_path_absolute(const gchar *input) { gchar *filename = NULL; if(g_str_has_prefix(input, "file://")) // in this case we should take care of %XX encodings in the string // (for example %20 = ' ') { input += strlen("file://"); filename = g_uri_unescape_string(input, NULL); } else filename = g_strdup(input); if(g_path_is_absolute(filename) == FALSE) { char *current_dir = g_get_current_dir(); char *tmp_filename = g_build_filename(current_dir, filename, NULL); g_free(filename); filename = g_realpath(tmp_filename); if(filename == NULL) { g_free(current_dir); g_free(tmp_filename); g_free(filename); return NULL; } g_free(current_dir); g_free(tmp_filename); } return filename; } int dt_load_from_string(const gchar *input, gboolean open_image_in_dr, gboolean *single_image) { int id = 0; if(input == NULL || input[0] == '\0') return 0; char *filename = dt_make_path_absolute(input); if(filename == NULL) { dt_control_log(_("found strange path `%s'"), input); return 0; } if(g_file_test(filename, G_FILE_TEST_IS_DIR)) { // import a directory into a film roll unsigned int last_char = strlen(filename) - 1; if(filename[last_char] == '/') filename[last_char] = '\0'; id = dt_film_import(filename); if(id) { dt_film_open(id); dt_ctl_switch_mode_to(DT_LIBRARY); } else { dt_control_log(_("error loading directory `%s'"), filename); } if(single_image) *single_image = FALSE; } else { // import a single image gchar *directory = g_path_get_dirname((const gchar *)filename); dt_film_t film; const int filmid = dt_film_new(&film, directory); id = dt_image_import(filmid, filename, TRUE); g_free(directory); if(id) { dt_film_open(filmid); // make sure buffers are loaded (load full for testing) dt_mipmap_buffer_t buf; dt_mipmap_cache_get(darktable.mipmap_cache, &buf, id, DT_MIPMAP_FULL, DT_MIPMAP_BLOCKING, 'r'); gboolean loaded = (buf.buf != NULL); dt_mipmap_cache_release(darktable.mipmap_cache, &buf); if(!loaded) { id = 0; dt_control_log(_("file `%s' has unknown format!"), filename); } else { if(open_image_in_dr) { dt_control_set_mouse_over_id(id); dt_ctl_switch_mode_to(DT_DEVELOP); } } } else { dt_control_log(_("error loading file `%s'"), filename); } if(single_image) *single_image = TRUE; } g_free(filename); return id; } static void dt_codepaths_init() { #ifdef HAVE_BUILTIN_CPU_SUPPORTS __builtin_cpu_init(); #endif memset(&(darktable.codepath), 0, sizeof(darktable.codepath)); // first, enable whatever codepath this CPU supports { #ifdef HAVE_BUILTIN_CPU_SUPPORTS darktable.codepath.SSE2 = (__builtin_cpu_supports("sse") && __builtin_cpu_supports("sse2")); #else dt_cpu_flags_t flags = dt_detect_cpu_features(); darktable.codepath.SSE2 = ((flags & (CPU_FLAG_SSE)) && (flags & (CPU_FLAG_SSE2))); #endif } // second, apply overrides from conf // NOTE: all intrinsics sets can only be overridden to OFF if(!dt_conf_get_bool("codepaths/sse2")) darktable.codepath.SSE2 = 0; // last: do we have any intrinsics sets enabled? darktable.codepath._no_intrinsics = !(darktable.codepath.SSE2); // if there is no SSE, we must enable plain codepath by default, // else, enable it conditionally. #if defined(__SSE__) // disabled by default, needs to be manually enabled if needed. // disabling all optimized codepaths enables it automatically. if(dt_conf_get_bool("codepaths/openmp_simd") || darktable.codepath._no_intrinsics) #endif { darktable.codepath.OPENMP_SIMD = 1; fprintf(stderr, "[dt_codepaths_init] will be using HIGHLY EXPERIMENTAL plain OpenMP SIMD codepath.\n"); } #if defined(__SSE__) if(darktable.codepath._no_intrinsics) #endif { fprintf(stderr, "[dt_codepaths_init] SSE2-optimized codepath is disabled or unavailable.\n"); fprintf(stderr, "[dt_codepaths_init] expect a LOT of functionality to be broken. you have been warned.\n"); } } int dt_init(int argc, char *argv[], const gboolean init_gui, const gboolean load_data, lua_State *L) { #ifndef __WIN32__ if(getuid() == 0 || geteuid() == 0) printf( "WARNING: either your user id or the effective user id are 0. are you running darktable as root?\n"); #endif #if defined(__SSE__) // make everything go a lot faster. _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); #endif dt_set_signal_handlers(); #include "is_supported_platform.h" int sse2_supported = 0; #ifdef HAVE_BUILTIN_CPU_SUPPORTS // NOTE: _may_i_use_cpu_feature() looks better, but only avaliable in ICC __builtin_cpu_init(); sse2_supported = __builtin_cpu_supports("sse2"); #else sse2_supported = dt_detect_cpu_features() & CPU_FLAG_SSE2; #endif if(!sse2_supported) { fprintf(stderr, "[dt_init] SSE2 instruction set is unavailable.\n"); fprintf(stderr, "[dt_init] expect a LOT of functionality to be broken. you have been warned.\n"); } #ifdef M_MMAP_THRESHOLD mallopt(M_MMAP_THRESHOLD, 128 * 1024); /* use mmap() for large allocations */ #endif // make sure that stack/frame limits are good (musl) dt_set_rlimits(); // we have to have our share dir in XDG_DATA_DIRS, // otherwise GTK+ won't find our logo for the about screen (and maybe other things) { const gchar *xdg_data_dirs = g_getenv("XDG_DATA_DIRS"); gchar *new_xdg_data_dirs = NULL; gboolean set_env = TRUE; if(xdg_data_dirs != NULL && *xdg_data_dirs != '\0') { // check if DARKTABLE_SHAREDIR is already in there gboolean found = FALSE; gchar **tokens = g_strsplit(xdg_data_dirs, ":", 0); // xdg_data_dirs is neither NULL nor empty => tokens != NULL for(char **iter = tokens; *iter != NULL; iter++) if(!strcmp(DARKTABLE_SHAREDIR, *iter)) { found = TRUE; break; } g_strfreev(tokens); if(found) set_env = FALSE; else new_xdg_data_dirs = g_strjoin(":", DARKTABLE_SHAREDIR, xdg_data_dirs, NULL); } else { // see http://standards.freedesktop.org/basedir-spec/latest/ar01s03.html for a reason to use those as a // default if(!g_strcmp0(DARKTABLE_SHAREDIR, "/usr/local/share") || !g_strcmp0(DARKTABLE_SHAREDIR, "/usr/local/share/") || !g_strcmp0(DARKTABLE_SHAREDIR, "/usr/share") || !g_strcmp0(DARKTABLE_SHAREDIR, "/usr/share/")) new_xdg_data_dirs = g_strdup("/usr/local/share/:/usr/share/"); else new_xdg_data_dirs = g_strdup_printf("%s:/usr/local/share/:/usr/share/", DARKTABLE_SHAREDIR); } if(set_env) g_setenv("XDG_DATA_DIRS", new_xdg_data_dirs, 1); g_free(new_xdg_data_dirs); } setlocale(LC_ALL, ""); bindtextdomain(GETTEXT_PACKAGE, DARKTABLE_LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); textdomain(GETTEXT_PACKAGE); // init all pointers to 0: memset(&darktable, 0, sizeof(darktable_t)); darktable.progname = argv[0]; // database char *dbfilename_from_command = NULL; char *noiseprofiles_from_command = NULL; char *datadir_from_command = NULL; char *moduledir_from_command = NULL; char *tmpdir_from_command = NULL; char *configdir_from_command = NULL; char *cachedir_from_command = NULL; #ifdef HAVE_OPENCL gboolean exclude_opencl = FALSE; gboolean print_statistics = strcmp(argv[0], "darktable-cltest"); #endif #ifdef USE_LUA char *lua_command = NULL; #endif darktable.num_openmp_threads = 1; #ifdef _OPENMP darktable.num_openmp_threads = omp_get_num_procs(); #endif darktable.unmuted = 0; GSList *config_override = NULL; for(int k = 1; k < argc; k++) { if(argv[k][0] == '-') { if(!strcmp(argv[k], "--help")) { return usage(argv[0]); } if(!strcmp(argv[k], "-h")) { return usage(argv[0]); } else if(!strcmp(argv[k], "--version")) { #ifdef USE_LUA const char *lua_api_version = strcmp(LUA_API_VERSION_SUFFIX, "") ? STR(LUA_API_VERSION_MAJOR) "." STR(LUA_API_VERSION_MINOR) "." STR(LUA_API_VERSION_PATCH) "-" LUA_API_VERSION_SUFFIX : STR(LUA_API_VERSION_MAJOR) "." STR(LUA_API_VERSION_MINOR) "." STR(LUA_API_VERSION_PATCH); #endif printf("this is %s\ncopyright (c) 2009-2016 johannes hanika\n" PACKAGE_BUGREPORT "\n\ncompile options:\n" " bit depth is %s\n" #ifdef _DEBUG " debug build\n" #else " normal build\n" #endif #if defined(__SSE2__) && defined(__SSE__) " SSE2 optimized codepath enabled\n" #else " SSE2 optimized codepath disabled\n" #endif #ifdef _OPENMP " OpenMP support enabled\n" #else " OpenMP support disabled\n" #endif #ifdef HAVE_OPENCL " OpenCL support enabled\n" #else " OpenCL support disabled\n" #endif #ifdef USE_LUA " Lua support enabled, API version %s\n" #else " Lua support disabled\n" #endif #ifdef USE_COLORDGTK " Colord support enabled\n" #else " Colord support disabled\n" #endif #ifdef HAVE_GPHOTO2 " gPhoto2 support enabled\n" #else " gPhoto2 support disabled\n" #endif #ifdef HAVE_GRAPHICSMAGICK " GraphicsMagick support enabled\n" #else " GraphicsMagick support disabled\n" #endif #ifdef HAVE_OPENEXR " OpenEXR support enabled\n" #else " OpenEXR support disabled\n" #endif , darktable_package_string, (sizeof(void *) == 8 ? "64 bit" : sizeof(void *) == 4 ? "32 bit" : "unknown") #if USE_LUA , lua_api_version #endif ); return 1; } else if(!strcmp(argv[k], "--library") && argc > k + 1) { dbfilename_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--datadir") && argc > k + 1) { datadir_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--moduledir") && argc > k + 1) { moduledir_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--tmpdir") && argc > k + 1) { tmpdir_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--configdir") && argc > k + 1) { configdir_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--cachedir") && argc > k + 1) { cachedir_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--localedir") && argc > k + 1) { bindtextdomain(GETTEXT_PACKAGE, argv[++k]); argv[k-1] = NULL; argv[k] = NULL; } else if(argv[k][1] == 'd' && argc > k + 1) { if(!strcmp(argv[k + 1], "all")) darktable.unmuted = 0xffffffff; // enable all debug information else if(!strcmp(argv[k + 1], "cache")) darktable.unmuted |= DT_DEBUG_CACHE; // enable debugging for lib/film/cache module else if(!strcmp(argv[k + 1], "control")) darktable.unmuted |= DT_DEBUG_CONTROL; // enable debugging for scheduler module else if(!strcmp(argv[k + 1], "dev")) darktable.unmuted |= DT_DEBUG_DEV; // develop module else if(!strcmp(argv[k + 1], "fswatch")) darktable.unmuted |= DT_DEBUG_FSWATCH; // fswatch module else if(!strcmp(argv[k + 1], "input")) darktable.unmuted |= DT_DEBUG_INPUT; // input devices else if(!strcmp(argv[k + 1], "camctl")) darktable.unmuted |= DT_DEBUG_CAMCTL; // camera control module else if(!strcmp(argv[k + 1], "perf")) darktable.unmuted |= DT_DEBUG_PERF; // performance measurements else if(!strcmp(argv[k + 1], "pwstorage")) darktable.unmuted |= DT_DEBUG_PWSTORAGE; // pwstorage module else if(!strcmp(argv[k + 1], "opencl")) darktable.unmuted |= DT_DEBUG_OPENCL; // gpu accel via opencl else if(!strcmp(argv[k + 1], "sql")) darktable.unmuted |= DT_DEBUG_SQL; // SQLite3 queries else if(!strcmp(argv[k + 1], "memory")) darktable.unmuted |= DT_DEBUG_MEMORY; // some stats on mem usage now and then. else if(!strcmp(argv[k + 1], "lighttable")) darktable.unmuted |= DT_DEBUG_LIGHTTABLE; // lighttable related stuff. else if(!strcmp(argv[k + 1], "nan")) darktable.unmuted |= DT_DEBUG_NAN; // check for NANs when processing the pipe. else if(!strcmp(argv[k + 1], "masks")) darktable.unmuted |= DT_DEBUG_MASKS; // masks related stuff. else if(!strcmp(argv[k + 1], "lua")) darktable.unmuted |= DT_DEBUG_LUA; // lua errors are reported on console else if(!strcmp(argv[k + 1], "print")) darktable.unmuted |= DT_DEBUG_PRINT; // print errors are reported on console else if(!strcmp(argv[k + 1], "camsupport")) darktable.unmuted |= DT_DEBUG_CAMERA_SUPPORT; // camera support warnings are reported on console else return usage(argv[0]); k++; argv[k-1] = NULL; argv[k] = NULL; } else if(argv[k][1] == 't' && argc > k + 1) { darktable.num_openmp_threads = CLAMP(atol(argv[k + 1]), 1, 100); printf("[dt_init] using %d threads for openmp parallel sections\n", darktable.num_openmp_threads); k++; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--conf") && argc > k + 1) { gchar *keyval = g_strdup(argv[++k]), *c = keyval; argv[k-1] = NULL; argv[k] = NULL; gchar *end = keyval + strlen(keyval); while(*c != '=' && c < end) c++; if(*c == '=' && *(c + 1) != '\0') { *c++ = '\0'; dt_conf_string_entry_t *entry = (dt_conf_string_entry_t *)g_malloc(sizeof(dt_conf_string_entry_t)); entry->key = g_strdup(keyval); entry->value = g_strdup(c); config_override = g_slist_append(config_override, entry); } g_free(keyval); } else if(!strcmp(argv[k], "--noiseprofiles") && argc > k + 1) { noiseprofiles_from_command = argv[++k]; argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--luacmd") && argc > k + 1) { #ifdef USE_LUA lua_command = argv[++k]; #else ++k; #endif argv[k-1] = NULL; argv[k] = NULL; } else if(!strcmp(argv[k], "--disable-opencl")) { #ifdef HAVE_OPENCL exclude_opencl = TRUE; #endif argv[k] = NULL; } else if(!strcmp(argv[k], "--")) { // "--" confuses the argument parser of glib/gtk. remove it. argv[k] = NULL; break; } else return usage(argv[0]); // fail on unrecognized options } } // remove the NULLs to not confuse gtk_init() later. for(int i = 1; i < argc; i++) { int k; for(k = i; k < argc; k++) if(argv[k] != NULL) break; if(k > i) { k -= i; for(int j = i + k; j < argc; j++) { argv[j-k] = argv[j]; argv[j] = NULL; } argc -= k; } } if(darktable.unmuted & DT_DEBUG_MEMORY) { fprintf(stderr, "[memory] at startup\n"); dt_print_mem_usage(); } // we need this REALLY early so that error messages can be shown if(init_gui) gtk_init(&argc, &argv); #ifdef _OPENMP omp_set_num_threads(darktable.num_openmp_threads); #endif dt_loc_init_datadir(datadir_from_command); dt_loc_init_plugindir(moduledir_from_command); if(dt_loc_init_tmp_dir(tmpdir_from_command)) { fprintf(stderr, "error: invalid temporary directory: %s\n", darktable.tmpdir); return usage(argv[0]); } dt_loc_init_user_config_dir(configdir_from_command); dt_loc_init_user_cache_dir(cachedir_from_command); #ifdef USE_LUA dt_lua_init_early(L); #endif // thread-safe init: dt_exif_init(); char datadir[PATH_MAX] = { 0 }; dt_loc_get_user_config_dir(datadir, sizeof(datadir)); char darktablerc[PATH_MAX] = { 0 }; snprintf(darktablerc, sizeof(darktablerc), "%s/darktablerc", datadir); // initialize the config backend. this needs to be done first... darktable.conf = (dt_conf_t *)calloc(1, sizeof(dt_conf_t)); dt_conf_init(darktable.conf, darktablerc, config_override); g_slist_free_full(config_override, g_free); // set the interface language const gchar *lang = dt_conf_get_string("ui_last/gui_language"); if(lang != NULL && lang[0] != '\0') { setenv("LANGUAGE", lang, 1); if(setlocale(LC_ALL, lang) != NULL) gtk_disable_setlocale(); setlocale(LC_MESSAGES, lang); setenv("LANG", lang, 1); } g_free((gchar *)lang); // detect cpu features and decide which codepaths to enable dt_codepaths_init(); // get the list of color profiles darktable.color_profiles = dt_colorspaces_init(); // initialize the database darktable.db = dt_database_init(dbfilename_from_command, load_data); if(darktable.db == NULL) { printf("ERROR : cannot open database\n"); return 1; } else if(!dt_database_get_lock_acquired(darktable.db)) { #ifndef MAC_INTEGRATION // send the images to the other instance via dbus fprintf(stderr, "trying to open the images in the running instance\n"); GDBusConnection *connection = NULL; for(int i = 1; i < argc; i++) { // make the filename absolute ... if(argv[i] == NULL || *argv[i] == '\0') continue; gchar *filename = dt_make_path_absolute(argv[i]); if(filename == NULL) continue; if(!connection) connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); // ... and send it to the running instance of darktable g_dbus_connection_call_sync(connection, "org.darktable.service", "/darktable", "org.darktable.service.Remote", "Open", g_variant_new("(s)", filename), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL); g_free(filename); } if(connection) g_object_unref(connection); #endif return 1; } // Initialize the signal system darktable.signals = dt_control_signal_init(); // Make sure that the database and xmp files are in sync before starting the fswatch. // We need conf and db to be up and running for that which is the case here. // FIXME: is this also useful in non-gui mode? GList *changed_xmp_files = NULL; if(init_gui && dt_conf_get_bool("run_crawler_on_start")) { changed_xmp_files = dt_control_crawler_run(); } // Initialize the filesystem watcher darktable.fswatch = dt_fswatch_new(); // FIXME: move there into dt_database_t dt_pthread_mutex_init(&(darktable.db_insert), NULL); dt_pthread_mutex_init(&(darktable.plugin_threadsafe), NULL); dt_pthread_mutex_init(&(darktable.capabilities_threadsafe), NULL); darktable.control = (dt_control_t *)calloc(1, sizeof(dt_control_t)); if(init_gui) { dt_control_init(darktable.control); } else { if(dbfilename_from_command && !strcmp(dbfilename_from_command, ":memory:")) dt_gui_presets_init(); // init preset db schema. darktable.control->running = 0; darktable.control->accelerators = NULL; dt_pthread_mutex_init(&darktable.control->run_mutex, NULL); } // initialize collection query darktable.collection = dt_collection_new(NULL); /* initialize selection */ darktable.selection = dt_selection_new(); /* capabilities set to NULL */ darktable.capabilities = NULL; // Initialize the password storage engine darktable.pwstorage = dt_pwstorage_new(); darktable.guides = dt_guides_init(); #ifdef HAVE_GRAPHICSMAGICK /* GraphicsMagick init */ InitializeMagick(darktable.progname); // *SIGH* dt_set_signal_handlers(); #endif darktable.opencl = (dt_opencl_t *)calloc(1, sizeof(dt_opencl_t)); #ifdef HAVE_OPENCL dt_opencl_init(darktable.opencl, exclude_opencl, print_statistics); #endif darktable.blendop = (dt_blendop_t *)calloc(1, sizeof(dt_blendop_t)); dt_develop_blend_init(darktable.blendop); darktable.points = (dt_points_t *)calloc(1, sizeof(dt_points_t)); dt_points_init(darktable.points, dt_get_num_threads()); darktable.noiseprofile_parser = dt_noiseprofile_init(noiseprofiles_from_command); // must come before mipmap_cache, because that one will need to access // image dimensions stored in here: darktable.image_cache = (dt_image_cache_t *)calloc(1, sizeof(dt_image_cache_t)); dt_image_cache_init(darktable.image_cache); darktable.mipmap_cache = (dt_mipmap_cache_t *)calloc(1, sizeof(dt_mipmap_cache_t)); dt_mipmap_cache_init(darktable.mipmap_cache); // The GUI must be initialized before the views, because the init() // functions of the views depend on darktable.control->accels_* to register // their keyboard accelerators if(init_gui) { darktable.gui = (dt_gui_gtk_t *)calloc(1, sizeof(dt_gui_gtk_t)); if(dt_gui_gtk_init(darktable.gui)) return 1; dt_bauhaus_init(); } else darktable.gui = NULL; darktable.view_manager = (dt_view_manager_t *)calloc(1, sizeof(dt_view_manager_t)); dt_view_manager_init(darktable.view_manager); // check whether we were able to load darkroom view. if we failed, we'll crash everywhere later on. if(!darktable.develop) return 1; darktable.imageio = (dt_imageio_t *)calloc(1, sizeof(dt_imageio_t)); dt_imageio_init(darktable.imageio); // load the darkroom mode plugins once: dt_iop_load_modules_so(); if(init_gui) { #ifdef HAVE_GPHOTO2 // Initialize the camera control. // this is done late so that the gui can react to the signal sent but before switching to lighttable! darktable.camctl = dt_camctl_new(); #endif darktable.lib = (dt_lib_t *)calloc(1, sizeof(dt_lib_t)); dt_lib_init(darktable.lib); dt_control_load_config(darktable.control); } dt_control_gui_mode_t mode = DT_LIBRARY; // april 1st: you have to earn using dt first! or know that you can switch views with keyboard shortcuts time_t now; time(&now); struct tm lt; localtime_r(&now, <); if(lt.tm_mon == 3 && lt.tm_mday == 1) mode = DT_KNIGHT; if(init_gui) { // init the gui part of views dt_view_manager_gui_init(darktable.view_manager); // Loading the keybindings char keyfile[PATH_MAX] = { 0 }; // First dump the default keymapping snprintf(keyfile, sizeof(keyfile), "%s/keyboardrc_default", datadir); gtk_accel_map_save(keyfile); // Removing extraneous semi-colons from the default keymap strip_semicolons_from_keymap(keyfile); // Then load any modified keys if available snprintf(keyfile, sizeof(keyfile), "%s/keyboardrc", datadir); if(g_file_test(keyfile, G_FILE_TEST_EXISTS)) gtk_accel_map_load(keyfile); else gtk_accel_map_save(keyfile); // Save the default keymap if none is present // I doubt that connecting to dbus for darktable-cli makes sense darktable.dbus = dt_dbus_init(); // initialize undo struct darktable.undo = dt_undo_init(); #ifndef MAC_INTEGRATION // load image(s) specified on cmdline // If only one image is listed, attempt to load it in darkroom int last_id = 0; gboolean only_single_images = TRUE; int loaded_images = 0; for(int i = 1; i < argc; i++) { gboolean single_image = FALSE; if(argv[i] == NULL || *argv[i] == '\0') continue; int new_id = dt_load_from_string(argv[i], FALSE, &single_image); if(new_id > 0) { last_id = new_id; loaded_images++; if(!single_image) only_single_images = FALSE; } } if(loaded_images == 1 && only_single_images) { dt_control_set_mouse_over_id(last_id); mode = DT_DEVELOP; } #endif } if(darktable.unmuted & DT_DEBUG_MEMORY) { fprintf(stderr, "[memory] after successful startup\n"); dt_print_mem_usage(); } dt_image_local_copy_synch(); /* init lua last, since it's user made stuff it must be in the real environment */ #ifdef USE_LUA dt_lua_init(darktable.lua_state.state, lua_command); #endif if(init_gui) dt_ctl_switch_mode_to(mode); // last but not least construct the popup that asks the user about images whose xmp files are newer than the // db entry if(init_gui && changed_xmp_files) { dt_control_crawler_show_image_list(changed_xmp_files); } return 0; } void dt_cleanup() { const int init_gui = (darktable.gui != NULL); #ifdef HAVE_PRINT dt_printers_abort_discovery(); #endif #ifdef USE_LUA dt_lua_finalize_early(); #endif if(init_gui) { dt_ctl_switch_mode_to(DT_MODE_NONE); dt_dbus_destroy(darktable.dbus); dt_control_write_config(darktable.control); dt_control_shutdown(darktable.control); dt_lib_cleanup(darktable.lib); free(darktable.lib); } #ifdef USE_LUA dt_lua_finalize(); #endif dt_view_manager_cleanup(darktable.view_manager); free(darktable.view_manager); if(init_gui) { dt_imageio_cleanup(darktable.imageio); free(darktable.imageio); free(darktable.gui); } dt_image_cache_cleanup(darktable.image_cache); free(darktable.image_cache); dt_mipmap_cache_cleanup(darktable.mipmap_cache); free(darktable.mipmap_cache); if(init_gui) { dt_control_cleanup(darktable.control); free(darktable.control); dt_undo_cleanup(darktable.undo); } dt_colorspaces_cleanup(darktable.color_profiles); dt_conf_cleanup(darktable.conf); free(darktable.conf); dt_points_cleanup(darktable.points); free(darktable.points); dt_iop_unload_modules_so(); dt_opencl_cleanup(darktable.opencl); free(darktable.opencl); #ifdef HAVE_GPHOTO2 dt_camctl_destroy((dt_camctl_t *)darktable.camctl); #endif dt_pwstorage_destroy(darktable.pwstorage); dt_fswatch_destroy(darktable.fswatch); #ifdef HAVE_GRAPHICSMAGICK DestroyMagick(); #endif dt_guides_cleanup(darktable.guides); dt_database_destroy(darktable.db); if(init_gui) { dt_bauhaus_cleanup(); } dt_capabilities_cleanup(); dt_pthread_mutex_destroy(&(darktable.db_insert)); dt_pthread_mutex_destroy(&(darktable.plugin_threadsafe)); dt_pthread_mutex_destroy(&(darktable.capabilities_threadsafe)); dt_exif_cleanup(); } void dt_print(dt_debug_thread_t thread, const char *msg, ...) { if(darktable.unmuted & thread) { va_list ap; va_start(ap, msg); vprintf(msg, ap); va_end(ap); fflush(stdout); } } void dt_gettime_t(char *datetime, size_t datetime_len, time_t t) { struct tm tt; (void)localtime_r(&t, &tt); strftime(datetime, datetime_len, "%Y:%m:%d %H:%M:%S", &tt); } void dt_gettime(char *datetime, size_t datetime_len) { dt_gettime_t(datetime, datetime_len, time(NULL)); } void *dt_alloc_align(size_t alignment, size_t size) { #if defined(__FreeBSD_version) && __FreeBSD_version < 700013 return malloc(size); #elif defined(__WIN32__) return _aligned_malloc(size, alignment); #else void *ptr = NULL; if(posix_memalign(&ptr, alignment, size)) return NULL; return ptr; #endif } #ifdef __WIN32__ void dt_free_align(void *mem) { _aligned_free(mem); } #endif void dt_show_times(const dt_times_t *start, const char *prefix, const char *suffix, ...) { dt_times_t end; char buf[160]; /* Arbitrary size, should be lots big enough for everything used in DT */ int i; /* Skip all the calculations an everything if -d perf isn't on */ if(darktable.unmuted & DT_DEBUG_PERF) { dt_get_times(&end); i = snprintf(buf, sizeof(buf), "%s took %.3f secs (%.3f CPU)", prefix, end.clock - start->clock, end.user - start->user); if(suffix != NULL) { va_list ap; va_start(ap, suffix); buf[i++] = ' '; vsnprintf(buf + i, sizeof buf - i, suffix, ap); va_end(ap); } dt_print(DT_DEBUG_PERF, "%s\n", buf); } } void dt_configure_defaults() { const int atom_cores = dt_get_num_atom_cores(); const int threads = dt_get_num_threads(); const size_t mem = dt_get_total_memory(); const int bits = (sizeof(void *) == 4) ? 32 : 64; fprintf(stderr, "[defaults] found a %d-bit system with %zu kb ram and %d cores (%d atom based)\n", bits, mem, threads, atom_cores); if(mem > (2u << 20) && threads > 4) { fprintf(stderr, "[defaults] setting high quality defaults\n"); dt_conf_set_int("worker_threads", 8); dt_conf_set_bool("plugins/lighttable/low_quality_thumbnails", FALSE); } if(mem < (1u << 20) || threads <= 2 || bits < 64 || atom_cores > 0) { fprintf(stderr, "[defaults] setting very conservative defaults\n"); dt_conf_set_int("worker_threads", 1); dt_conf_set_int("host_memory_limit", 500); dt_conf_set_int("singlebuffer_limit", 8); dt_conf_set_string("plugins/darkroom/demosaic/quality", "always bilinear (fast)"); dt_conf_set_bool("plugins/lighttable/low_quality_thumbnails", TRUE); } } int dt_capabilities_check(char *capability) { GList *capabilities = darktable.capabilities; while(capabilities) { if(!strcmp(capabilities->data, capability)) { return TRUE; } capabilities = g_list_next(capabilities); } return FALSE; } void dt_capabilities_add(char *capability) { dt_pthread_mutex_lock(&darktable.capabilities_threadsafe); if(!dt_capabilities_check(capability)) darktable.capabilities = g_list_append(darktable.capabilities, capability); dt_pthread_mutex_unlock(&darktable.capabilities_threadsafe); } void dt_capabilities_remove(char *capability) { dt_pthread_mutex_lock(&darktable.capabilities_threadsafe); darktable.capabilities = g_list_remove(darktable.capabilities, capability); dt_pthread_mutex_unlock(&darktable.capabilities_threadsafe); } void dt_capabilities_cleanup() { while(darktable.capabilities) darktable.capabilities = g_list_delete_link(darktable.capabilities, darktable.capabilities); } // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh // vim: shiftwidth=2 expandtab tabstop=2 cindent // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;