--- src/common/image_cache.c.orig 2012-01-30 21:22:34.378310759 +1000 +++ src/common/image_cache.c 2012-01-30 21:22:34.405961563 +1000 @@ -1,8 +1,8 @@ /* This file is part of darktable, - copyright (c) 2009--2010 johannes hanika. + copyright (c) 2009--2011 johannes hanika. 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. @@ -26,16 +26,22 @@ #include "common/debug.h" #include #include #include +#if defined(__SUNOS__) +#include +#else #include +#endif #include #include #include +#include -#define DT_IMAGE_CACHE_FILE_VERSION 4 +#define DT_IMAGE_CACHE_FILE_MAGIC 0xD71337 +#define DT_IMAGE_CACHE_FILE_VERSION 5 #define DT_IMAGE_CACHE_FILE_NAME "mipmaps" int dt_image_cache_check_consistency(dt_image_cache_t *cache) { #if 1//def _DEBUG @@ -86,11 +92,11 @@ return; } char cachedir[1024]; char dbfilename[1024]; - dt_get_user_cache_dir(cachedir,1024); + dt_util_get_user_cache_dir(cachedir,1024); gchar *filename = dt_conf_get_string("cachefile"); if(!filename || filename[0] == '\0') snprintf(dbfilename, 512, "%s/%s", cachedir,DT_IMAGE_CACHE_FILE_NAME); else if(filename[0] != '/') snprintf(dbfilename, 512, "%s/%s", cachedir, filename); else snprintf(dbfilename, 512, "%s", filename); @@ -99,11 +105,11 @@ int written = 0; FILE *f = fopen(dbfilename, "wb"); if(!f) goto write_error; // write version info: - const int32_t magic = 0xD71337 + DT_IMAGE_CACHE_FILE_VERSION; + const int32_t magic = DT_IMAGE_CACHE_FILE_MAGIC + DT_IMAGE_CACHE_FILE_VERSION; written = fwrite(&magic, sizeof(int32_t), 1, f); if(written != 1) goto write_error; written = fwrite(&darktable.thumbnail_size, sizeof(int32_t), 1, f); if(written != 1) goto write_error; @@ -193,11 +199,11 @@ } free(buf); } } // write marker at the end - int32_t endmarker = 0xD71337; + int32_t endmarker = DT_IMAGE_CACHE_FILE_MAGIC; written = fwrite(&endmarker, sizeof(int32_t), 1, f); if(written != 1) goto write_error; fclose(f); dt_pthread_mutex_unlock(&(cache->mutex)); return; @@ -212,34 +218,62 @@ int dt_image_cache_read(dt_image_cache_t *cache) { dt_pthread_mutex_lock(&(cache->mutex)); char cachedir[1024]; char dbfilename[1024]; - dt_get_user_cache_dir (cachedir,1024); + dt_util_get_user_cache_dir (cachedir,1024); gchar *filename = dt_conf_get_string ("cachefile"); if(!filename || filename[0] == '\0') snprintf (dbfilename, 512, "%s/%s", cachedir, DT_IMAGE_CACHE_FILE_NAME); else if(filename[0] != '/') snprintf (dbfilename, 512, "%s/%s", cachedir, filename); else snprintf (dbfilename, 512, "%s", filename); g_free(filename); FILE *f = fopen(dbfilename, "rb"); - if(!f) goto read_error; + if(!f) + { + if (errno == ENOENT) + { + fprintf(stderr, "[image_cache_read] cache is empty, file `%s' doesn't exist\n", dbfilename); + } + else + { + fprintf(stderr, "[image_cache_read] failed to open the cache from `%s'\n", dbfilename); + } + goto read_finalize; + } int32_t num = 0, rd = 0; // read version info: - const int32_t magic = 0xD71337 + DT_IMAGE_CACHE_FILE_VERSION; + const int32_t magic = DT_IMAGE_CACHE_FILE_MAGIC + DT_IMAGE_CACHE_FILE_VERSION; int32_t magic_file = 0; rd = fread(&magic_file, sizeof(int32_t), 1, f); - if(rd != 1 || magic_file != magic) goto read_error; + if(rd != 1) goto read_error; + if(magic_file != magic) + { + if(magic_file > DT_IMAGE_CACHE_FILE_MAGIC && magic_file < magic) + fprintf(stderr, "[image_cache_read] cache version too old, dropping `%s' cache\n", dbfilename); + else + fprintf(stderr, "[image_cache_read] invalid cache file, dropping `%s' cache\n", dbfilename); + goto read_finalize; + } rd = fread(&magic_file, sizeof(int32_t), 1, f); - if(rd != 1 || magic_file != darktable.thumbnail_size) goto read_error; + if(rd != 1) goto read_error; + if(magic_file != darktable.thumbnail_size) + { + fprintf(stderr, "[image_cache_read] cache settings changed, dropping `%s' cache\n", dbfilename); + goto read_finalize; + } // read metadata: rd = fread(&num, sizeof(int32_t), 1, f); if(rd != 1) goto read_error; - if(cache->num_lines != num) goto read_error; + if(cache->num_lines != num) + { + fprintf(stderr, "[image_cache_read] cache settings changed, dropping `%s' cache\n", dbfilename); + goto read_finalize; + } rd = fread(&num, sizeof(int16_t), 1, f); if(rd != 1) goto read_error; cache->lru = num; rd = fread(&num, sizeof(int16_t), 1, f); if(rd != 1) goto read_error; @@ -332,13 +366,14 @@ fclose(f); dt_pthread_mutex_unlock(&(cache->mutex)); return 0; read_error: + fprintf(stderr, "[image_cache_read] failed to recover the cache from `%s'\n", dbfilename); +read_finalize: if(f) fclose(f); g_unlink(dbfilename); - fprintf(stderr, "[image_cache_read] failed to recover the cache from `%s'\n", dbfilename); dt_pthread_mutex_unlock(&(cache->mutex)); return 1; } /* copy file from src to dest, overwrites destination */ @@ -362,11 +397,11 @@ static void _image_cache_backup() { char cachedir[1024]; char dbfilename[1024]; - dt_get_user_cache_dir (cachedir,1024); + dt_util_get_user_cache_dir (cachedir,1024); gchar *filename = dt_conf_get_string ("cachefile"); if(!filename || filename[0] == '\0') snprintf (dbfilename, 1024, "%s/%s", cachedir, DT_IMAGE_CACHE_FILE_NAME); else if(filename[0] != '/') snprintf (dbfilename, 512, "%s/%s", cachedir, filename); else snprintf (dbfilename, 512, "%s", filename); @@ -380,11 +415,11 @@ static void _image_cache_restore() { char cachedir[1024]; char dbfilename[1024]; - dt_get_user_cache_dir(cachedir,1024); + dt_util_get_user_cache_dir(cachedir,1024); gchar *filename = dt_conf_get_string ("cachefile"); if(!filename || filename[0] == '\0') snprintf (dbfilename, 512, "%s/%s", cachedir,DT_IMAGE_CACHE_FILE_NAME); else if(filename[0] != '/') snprintf (dbfilename, 512, "%s/%s", cachedir, filename); else snprintf (dbfilename, 512, "%s", filename); @@ -608,18 +643,19 @@ { if(cache->line[cache->by_id[pos]].image.id < id) min = pos; else max = pos; pos = (min + max)/2; } + pos++; memmove(cache->by_id+pos+1,cache->by_id+pos,(oldpos-pos)*sizeof(int16_t)); cache->by_id[pos] = cache_line; } else if (oldpos < cache->num_lines - 1 && cache->line[cache->by_id[oldpos+1]].image.id < id) { // if new id should be in the middle and after old position unsigned int min = oldpos+1, max = cache->num_lines-1; - unsigned int pos = max/2; + unsigned int pos = (min + max)/2; while (pos != min) { if(cache->line[cache->by_id[pos]].image.id < id) min = pos; else max = pos; pos = (min + max)/2; --- src/common/darktable.c.orig 2012-01-30 21:22:34.544028903 +1000 +++ src/common/darktable.c 2012-01-30 21:22:34.570675414 +1000 @@ -42,24 +42,32 @@ #include "gui/presets.h" #include #include #include #include +#include #include #include #include +#if !defined(__APPLE__) && !defined(__FreeBSD__) +#include +#endif #ifdef __APPLE__ #include #endif #ifdef _OPENMP # include #endif +#ifdef __SUNOS__ +#include +#endif + darktable_t darktable; -const char dt_supported_extensions[] = "3fr,arw,bay,bmq,cap,cine,cr2,crw,cs1,dc2,dcr,dng,erf,fff,exr,ia,iiq,jpg,jpeg,k25,kc2,kdc,mdc,mef,mos,mrw,nef,nrw,orf,pef,pfm,pxn,qtk,raf,raw,rdc,rw2,rwl,sr2,srf,sti,tif,tiff,x3f"; +const char dt_supported_extensions[] = "3fr,arw,bay,bmq,cap,cine,cr2,crw,cs1,dc2,dcr,dng,erf,fff,exr,ia,iiq,jpg,jpeg,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"; static int usage(const char *argv0) { printf("usage: %s [-d {all,cache,control,dev,fswatch,camctl,perf,pwstorage,opencl,sql}] [IMG_1234.{RAW,..}|image_folder/]", argv0); #ifdef HAVE_OPENCL @@ -69,21 +77,87 @@ return 1; } typedef void (dt_signal_handler_t)(int) ; static dt_signal_handler_t *_dt_sigill_old_handler = NULL; +static dt_signal_handler_t *_dt_sigsegv_old_handler = NULL; + +#if defined(__APPLE__) || defined(__SUNOS__) +static int dprintf(int fd,const char *fmt, ...) +{ + va_list ap; + FILE *f = fdopen(fd,"a"); + va_start(ap, fmt); + int rc = vfprintf(f, fmt, ap); + fclose(f); + va_end(ap); + return rc; +} +#endif + +static +void _dt_sigsegv_handler(int param) +{ + FILE *fd; + gchar buf[PIPE_BUF]; + gchar *name_used; + int fout; + gboolean delete_file = FALSE; + + if((fout = g_file_open_tmp("darktable_bt_XXXXXX.txt", &name_used, NULL)) == -1) + fout = STDOUT_FILENO; // just print everything to stdout + + dprintf(fout, "this is %s reporting a segfault:\n\n", PACKAGE_STRING); + gchar *command = g_strdup_printf("gdb %s %d -batch -x %s/gdb_commands", darktable.progname, (int)getpid(), DARKTABLE_DATADIR); + + if((fd = popen(command, "r")) != NULL) + { + gboolean read_something = FALSE; + while((fgets(buf, PIPE_BUF, fd)) != NULL) + { + read_something = TRUE; + dprintf(fout, "%s", buf); + } + pclose(fd); + if(fout != STDOUT_FILENO) + { + if(read_something) + g_printerr("backtrace written to %s\n", name_used); + else + { + delete_file = TRUE; + g_printerr("an error occured while trying to execute gdb. please check if gdb is installed on your system.\n"); + } + } + } + else + { + delete_file = TRUE; + g_printerr("an error occured while trying to execute gdb.\n"); + } + + if(fout != STDOUT_FILENO) + close(fout); + if(delete_file) + g_unlink(name_used); + g_free(command); + g_free(name_used); + + /* pass it further to the old handler*/ + _dt_sigsegv_old_handler(param); +} static void _dt_sigill_handler(int param) { GtkWidget *dlg = gtk_message_dialog_new(NULL,GTK_DIALOG_MODAL,GTK_MESSAGE_ERROR,GTK_BUTTONS_OK,_( "darktable has trapped an illegal instruction which probably means that \ an invalid processor optimized codepath is used for your cpu, please try reproduce the crash running 'gdb darktable' from \ the console and post the backtrace log to mailing list with information about your CPU and where you got the package from.")); gtk_dialog_run(GTK_DIALOG(dlg)); - - /* pass it further the old handler*/ + + /* pass it further to the old handler*/ _dt_sigill_old_handler(param); } #if defined(__i386__) && defined(__PIC__) #define cpuid(level, a, b, c, d) \ @@ -138,17 +212,64 @@ exit(11); } } +static void strip_semicolons_from_keymap(const char* path) +{ + char pathtmp[1024]; + FILE *fin = fopen(path, "r"); + FILE *fout; + int i; + int c = '\0'; + + snprintf(pathtmp, 1024, "%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); + g_file_delete(g_file_new_for_path(path), NULL, NULL); + g_file_move(g_file_new_for_path(pathtmp), g_file_new_for_path(path), 0, + NULL, NULL, NULL, NULL); +} + int dt_init(int argc, char *argv[], const int init_gui) { +#ifndef __APPLE__ + _dt_sigsegv_old_handler = signal(SIGSEGV,&_dt_sigsegv_handler); +#endif + #ifndef __SSE2__ fprintf(stderr, "[dt_init] unfortunately we depend on SSE2 instructions at this time.\n"); fprintf(stderr, "[dt_init] please contribute a backport patch (or buy a newer processor).\n"); return 1; #endif + +#ifdef M_MMAP_THRESHOLD + mallopt(M_MMAP_THRESHOLD,128*1024) ; /* use mmap() for large allocations */ +#endif + bindtextdomain (GETTEXT_PACKAGE, DARKTABLE_LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); @@ -226,11 +347,11 @@ gegl_init(&argc, &argv); #endif // thread-safe init: dt_exif_init(); char datadir[1024]; - dt_get_user_config_dir (datadir,1024); + dt_util_get_user_config_dir (datadir,1024); char filename[1024]; snprintf(filename, 1024, "%s/darktablerc", datadir); // Initialize the filesystem watcher darktable.fswatch=dt_fswatch_new(); @@ -239,10 +360,11 @@ // Initialize the camera control darktable.camctl=dt_camctl_new(); #endif // has to go first for settings needed by all the others. darktable.conf = (dt_conf_t *)malloc(sizeof(dt_conf_t)); + memset(darktable.conf, 0, sizeof(dt_conf_t)); dt_conf_init(darktable.conf, filename); // get max lighttable thumbnail size: darktable.thumbnail_size = CLAMPS(dt_conf_get_int("plugins/lighttable/thumbnail_size"), 160, 1300); // and make sure it can be mip-mapped all the way from mip4 to mip0 @@ -255,11 +377,11 @@ // check and migrate database into new XDG structure char dbfilename[2048]= {0}; gchar *conf_db = dt_conf_get_string("database"); if (conf_db && conf_db[0] != '/') { - char *homedir = getenv ("HOME"); + char *homedir = dt_util_get_home_dir(NULL); snprintf (dbfilename,2048,"%s/%s", homedir, conf_db); if (g_file_test (dbfilename, G_FILE_TEST_EXISTS)) { fprintf(stderr, "[init] moving database into new XDG directory structure\n"); // move database into place @@ -278,11 +400,11 @@ char cachefilename[2048]= {0}; char cachedir[2048]= {0}; gchar *conf_cache = dt_conf_get_string("cachefile"); if (conf_cache && conf_cache[0] != '/') { - char *homedir = getenv ("HOME"); + char *homedir = dt_util_get_home_dir(NULL); snprintf (cachefilename,2048,"%s/%s",homedir, conf_cache); if (g_file_test (cachefilename,G_FILE_TEST_EXISTS)) { fprintf(stderr, "[init] moving cache into new XDG directory structure\n"); char destcachename[2048]= {0}; @@ -318,11 +440,11 @@ fprintf(stderr, "[init] could not open database "); if(dbname) fprintf(stderr, "`%s'!\n", dbname); else fprintf(stderr, "\n"); #ifndef HAVE_GCONF fprintf(stderr, "[init] maybe your %s/darktablerc is corrupt?\n",datadir); - dt_get_datadir(dbfilename, 512); + dt_util_get_datadir(dbfilename, 512); fprintf(stderr, "[init] try `cp %s/darktablerc %s/darktablerc'\n", dbfilename,datadir); #else fprintf(stderr, "[init] check your /apps/darktable/database gconf entry!\n"); #endif sqlite3_close(darktable.db); @@ -333,10 +455,11 @@ dt_pthread_mutex_init(&(darktable.db_insert), NULL); dt_pthread_mutex_init(&(darktable.plugin_threadsafe), NULL); darktable.control = (dt_control_t *)malloc(sizeof(dt_control_t)); + memset(darktable.control, 0, sizeof(dt_control_t)); if(init_gui) { dt_control_init(darktable.control); } else @@ -346,61 +469,94 @@ { dt_control_create_database_schema(); dt_gui_presets_init(); // also 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_listeners = NULL; darktable.collection = dt_collection_new(NULL); darktable.opencl = (dt_opencl_t *)malloc(sizeof(dt_opencl_t)); + memset(darktable.opencl, 0, sizeof(dt_opencl_t)); dt_opencl_init(darktable.opencl, argc, argv); darktable.blendop = (dt_blendop_t *)malloc(sizeof(dt_blendop_t)); + memset(darktable.blendop, 0, sizeof(dt_blendop_t)); dt_develop_blend_init(darktable.blendop); darktable.points = (dt_points_t *)malloc(sizeof(dt_points_t)); + memset(darktable.points, 0, sizeof(dt_points_t)); dt_points_init(darktable.points, dt_get_num_threads()); int thumbnails = dt_conf_get_int ("mipmap_cache_thumbnails"); thumbnails = MIN(1000000, MAX(20, thumbnails)); darktable.mipmap_cache = (dt_mipmap_cache_t *)malloc(sizeof(dt_mipmap_cache_t)); + memset(darktable.mipmap_cache, 0, sizeof(dt_mipmap_cache_t)); dt_mipmap_cache_init(darktable.mipmap_cache, thumbnails); darktable.image_cache = (dt_image_cache_t *)malloc(sizeof(dt_image_cache_t)); + memset(darktable.image_cache, 0, sizeof(dt_image_cache_t)); dt_image_cache_init(darktable.image_cache, MIN(10000, MAX(500, thumbnails)), load_cached); - darktable.view_manager = (dt_view_manager_t *)malloc(sizeof(dt_view_manager_t)); - dt_view_manager_init(darktable.view_manager); + // 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 *)malloc(sizeof(dt_gui_gtk_t)); + memset(darktable.gui,0,sizeof(dt_gui_gtk_t)); if(dt_gui_gtk_init(darktable.gui, argc, argv)) return 1; } + else darktable.gui = NULL; + darktable.view_manager = (dt_view_manager_t *)malloc(sizeof(dt_view_manager_t)); + memset(darktable.view_manager, 0, sizeof(dt_view_manager_t)); + dt_view_manager_init(darktable.view_manager); - // load the darkroom mode plugins once: dt_iop_load_modules_so(); if(init_gui) { darktable.lib = (dt_lib_t *)malloc(sizeof(dt_lib_t)); + memset(darktable.lib, 0, sizeof(dt_lib_t)); dt_lib_init(darktable.lib); dt_control_load_config(darktable.control); g_strlcpy(darktable.control->global_settings.dbname, filename, 512); // overwrite if relocated. darktable.imageio = (dt_imageio_t *)malloc(sizeof(dt_imageio_t)); + memset(darktable.imageio, 0, sizeof(dt_imageio_t)); dt_imageio_init(darktable.imageio); } + if(init_gui) + { + // Loading the keybindings + char keyfile[1024]; + + // First dump the default keymapping + snprintf(keyfile, 1024, "%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, 1024, "%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 + } + int id = 0; if(init_gui && image_to_load) { char* filename; if(g_str_has_prefix(image_to_load, "file://")) @@ -487,28 +643,38 @@ } void dt_cleanup() { dt_ctl_switch_mode_to(DT_MODE_NONE); + const int init_gui = (darktable.gui != NULL); - dt_control_write_config(darktable.control); - dt_control_shutdown(darktable.control); + if(init_gui) + { + dt_control_write_config(darktable.control); + dt_control_shutdown(darktable.control); - dt_lib_cleanup(darktable.lib); - free(darktable.lib); + dt_lib_cleanup(darktable.lib); + free(darktable.lib); + } dt_view_manager_cleanup(darktable.view_manager); free(darktable.view_manager); - dt_imageio_cleanup(darktable.imageio); - free(darktable.imageio); - dt_gui_gtk_cleanup(darktable.gui); - free(darktable.gui); + if(init_gui) + { + dt_imageio_cleanup(darktable.imageio); + free(darktable.imageio); + dt_gui_gtk_cleanup(darktable.gui); + 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); - dt_control_cleanup(darktable.control); - free(darktable.control); + if(init_gui) + { + dt_control_cleanup(darktable.control); + free(darktable.control); + } dt_conf_cleanup(darktable.conf); free(darktable.conf); dt_points_cleanup(darktable.points); free(darktable.points); dt_iop_unload_modules_so(); @@ -563,112 +729,10 @@ if(posix_memalign(&ptr, alignment, size)) return NULL; return ptr; #endif } -void -dt_get_user_config_dir (char *data, size_t bufsize) -{ - g_snprintf (data,bufsize,"%s/.config/darktable",getenv("HOME")); - if (g_file_test (data,G_FILE_TEST_EXISTS)==FALSE) - g_mkdir_with_parents (data,0700); -} - -void -dt_get_user_cache_dir (char *data, size_t bufsize) -{ - g_snprintf (data,bufsize,"%s/.cache/darktable",getenv("HOME")); - if (g_file_test (data,G_FILE_TEST_EXISTS)==FALSE) - g_mkdir_with_parents (data,0700); -} - - -void -dt_get_user_local_dir (char *data, size_t bufsize) -{ - g_snprintf(data,bufsize,"%s/.local",getenv("HOME")); - if (g_file_test (data,G_FILE_TEST_EXISTS)==FALSE) - g_mkdir_with_parents (data,0700); - -} - -void dt_get_plugindir(char *datadir, size_t bufsize) -{ -#if defined(__MACH__) || defined(__APPLE__) - gchar *curr = g_get_current_dir(); - int contains = 0; - for(int k=0; darktable.progname[k] != 0; k++) if(darktable.progname[k] == '/') - { - contains = 1; - break; - } - if(darktable.progname[0] == '/') // absolute path - snprintf(datadir, bufsize, "%s", darktable.progname); - else if(contains) // relative path - snprintf(datadir, bufsize, "%s/%s", curr, darktable.progname); - else - { - // no idea where we have been called. use compiled in path - g_free(curr); - snprintf(datadir, bufsize, "%s/darktable", DARKTABLE_LIBDIR); - return; - } - size_t len = MIN(strlen(datadir), bufsize); - char *t = datadir + len; // strip off bin/darktable - for(; t>datadir && *t!='/'; t--); - t--; - if(*t == '.' && *(t-1) != '.') - { - for(; t>datadir && *t!='/'; t--); - t--; - } - for(; t>datadir && *t!='/'; t--); - g_strlcpy(t, "/lib/darktable", bufsize-(t-datadir)); - g_free(curr); -#else - snprintf(datadir, bufsize, "%s/darktable", DARKTABLE_LIBDIR); -#endif -} - -void dt_get_datadir(char *datadir, size_t bufsize) -{ -#if defined(__MACH__) || defined(__APPLE__) - gchar *curr = g_get_current_dir(); - int contains = 0; - for(int k=0; darktable.progname[k] != 0; k++) if(darktable.progname[k] == '/') - { - contains = 1; - break; - } - if(darktable.progname[0] == '/') // absolute path - snprintf(datadir, bufsize, "%s", darktable.progname); - else if(contains) // relative path - snprintf(datadir, bufsize, "%s/%s", curr, darktable.progname); - else - { - // no idea where we have been called. use compiled in path - g_free(curr); - snprintf(datadir, bufsize, "%s", DARKTABLE_DATADIR); - return; - } - size_t len = MIN(strlen(datadir), bufsize); - char *t = datadir + len; // strip off bin/darktable - for(; t>datadir && *t!='/'; t--); - t--; - if(*t == '.' && *(t-1) != '.') - { - for(; t>datadir && *t!='/'; t--); - t--; - } - for(; t>datadir && *t!='/'; t--); - g_strlcpy(t, "/share/darktable", bufsize-(t-datadir)); - g_free(curr); -#else - snprintf(datadir, bufsize, "%s", DARKTABLE_DATADIR); -#endif -} - void dt_show_times(const dt_times_t *start, const char *prefix, const char *suffix, ...) { dt_times_t end; char buf[120]; /* Arbitrary size, should be lots big enough for everything used in DT */ int i; --- src/common/darktable.h.orig 2012-01-30 21:22:34.689304776 +1000 +++ src/common/darktable.h 2012-01-30 21:22:34.716428579 +1000 @@ -1,8 +1,8 @@ /* This file is part of darktable, - copyright (c) 2009--2010 johannes hanika. + copyright (c) 2009--2011 johannes hanika. 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. @@ -17,16 +17,17 @@ */ #ifndef DARKTABLE_H #define DARKTABLE_H #ifndef _XOPEN_SOURCE -#define _XOPEN_SOURCE 600 // for localtime_r +#define _XOPEN_SOURCE 600 // for localtime_r and dprintf #endif #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "common/dtpthread.h" +#include "common/utility.h" #include #include #include #include #include @@ -38,11 +39,11 @@ #else #define omp_get_max_threads() 1 #define omp_get_thread_num() 0 #endif -#define DT_MODULE_VERSION 3 // version of dt's module interface +#define DT_MODULE_VERSION 4 // version of dt's module interface #define DT_VERSION 36 // version of dt's database tables #define DT_CONFIG_VERSION 34 // dt gconf var version // every module has to define this: #ifdef _DEBUG @@ -159,18 +160,10 @@ void dt_cleanup(); void dt_print(dt_debug_thread_t thread, const char *msg, ...); void dt_gettime_t(char *datetime, time_t t); void dt_gettime(char *datetime); void *dt_alloc_align(size_t alignment, size_t size); -void dt_get_datadir(char *datadir, size_t bufsize); -void dt_get_plugindir(char *datadir, size_t bufsize); -/** get the user directory of darktable, ~/.config/darktable */ -void dt_get_user_config_dir(char *data, size_t bufsize); -/** get the user directory of darktable, ~/.cache/darktable */ -void dt_get_user_cache_dir(char *data, size_t bufsize); -/** get the user local directory , ~/.local */ -void dt_get_user_local_dir(char *data, size_t bufsize); static inline double dt_get_wtime(void) { struct timeval time; gettimeofday(&time, NULL); --- src/common/camera_control.c.orig 2012-01-30 21:22:34.835647973 +1000 +++ src/common/camera_control.c 2012-01-30 21:22:34.858378499 +1000 @@ -1,8 +1,8 @@ /* This file is part of darktable, - copyright (c) 2010 Henrik Andersson. + copyright (c) 2010-2011 Henrik Andersson. 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. @@ -18,11 +18,15 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include +#ifndef __SUNOS__ #include +#else +#include +#endif #include "common/camera_control.h" /***/ typedef enum _camctl_camera_job_type_t { @@ -155,10 +159,36 @@ char buffer[4096]; vsprintf( buffer, format, args ); dt_print(DT_DEBUG_CAMCTL,"[camera_control] gphoto2 message: %s\n",buffer); } + +static gboolean _camera_timeout_job(gpointer data) +{ + dt_camera_t *cam = (dt_camera_t *)data; + dt_print(DT_DEBUG_CAMCTL,"[camera_control] Calling timeout func for camera 0x%x.\n",cam); + cam->timeout(cam->gpcam, cam->gpcontext); + return TRUE; +} + +static int _camera_start_timeout_func(Camera *c,unsigned int timeout,CameraTimeoutFunc func, void *data) +{ + dt_print(DT_DEBUG_CAMCTL,"[camera_control] start timeout %d seconds for camera 0x%x requested by driver.\n",timeout,data); + dt_camera_t *cam = (dt_camera_t*)data; + cam->timeout = func; + return g_timeout_add_seconds(timeout, _camera_timeout_job, cam); +} + +static void _camera_stop_timeout_func(Camera *c, int id,void *data) +{ + dt_camera_t *cam = (dt_camera_t*)data; + dt_print(DT_DEBUG_CAMCTL,"[camera_control] Removing timeout %d for camera 0x%x.\n",id,cam); + g_source_remove(id); + cam->timeout = NULL; +} + + void _camera_add_job(const dt_camctl_t *c, const dt_camera_t *camera, gpointer job) { dt_camera_t *cam=(dt_camera_t *)camera; dt_pthread_mutex_lock(&cam->jobqueue_lock); cam->jobqueue = g_list_append(cam->jobqueue,job); @@ -492,10 +522,18 @@ } // read a full copy of config to configuration cache gp_camera_get_config( cam->gpcam, &cam->configuration, c->gpcontext ); + // initialize timeout callbacks eg. keep alive, some cameras needs it. + cam->gpcontext = camctl->gpcontext; + gp_camera_set_timeout_funcs(cam->gpcam, + (CameraTimeoutStartFunc)_camera_start_timeout_func, + (CameraTimeoutStopFunc)_camera_stop_timeout_func, + cam); + + dt_pthread_mutex_init(&cam->jobqueue_lock, NULL); dt_print(DT_DEBUG_CAMCTL,"[camera_control] device %s on port %s initialized\n", cam->model,cam->port); } else @@ -710,10 +748,11 @@ // Start up camera event polling thread dt_print(DT_DEBUG_CAMCTL,"[camera_control] enabling tether mode\n"); camctl->active_camera=camera; camera->is_tethering=TRUE; pthread_create(&camctl->camera_event_thread, NULL, &_camera_event_thread, (void *)c); + } else { camera->is_tethering=FALSE; dt_print(DT_DEBUG_CAMCTL,"[camera_control] disabling tether mode\n"); @@ -952,14 +991,20 @@ { if( gp_camera_wait_for_event( cam->gpcam, 100, &event, &data, c->gpcontext ) == GP_OK ) { if( event == GP_EVENT_UNKNOWN ) { - if( strstr( (char *)data, "4006" ) ) + /* this is really some undefined behavior, seems like its + camera driver dependent... very ugly! */ + if( strstr( (char *)data, "4006" ) || // Nikon PTP driver + (strstr((char *)data, "PTP Property") && strstr((char *)data, "changed")) // Some Canon driver maybe all ?? + + ) { // Property change event occured on camera // let's update cache and signalling + dt_print(DT_DEBUG_CAMCTL, "[camera_control] Camera configuration change event, lets update internal configuration cache.\n"); _camera_configuration_update(c,cam); } } else if( event == GP_EVENT_FILE_ADDED ) { --- src/gui/gtk.c.orig 2012-01-30 21:22:35.148980859 +1000 +++ src/gui/gtk.c 2012-01-30 21:22:35.178299614 +1000 @@ -37,10 +37,11 @@ #include "common/image_cache.h" #include "develop/develop.h" #include "develop/imageop.h" #include "dtgtk/label.h" #include "dtgtk/button.h" +#include "gui/accelerators.h" #include "gui/contrast.h" #include "gui/gtk.h" #include "gui/metadata.h" #include "gui/iop_history.h" #include "gui/iop_modulegroups.h" @@ -81,10 +82,139 @@ static void init_center(GtkWidget *container); static void init_center_bottom(GtkWidget *container); static void init_colorpicker(GtkWidget *container); static void init_lighttable_box(GtkWidget* container); + +static void key_accel_changed(GtkAccelMap *object, + gchar *accel_path, + guint accel_key, + GdkModifierType accel_mods, + gpointer user_data) +{ + char path[256]; + + // Updating all the stored accelerator keys/mods for key_pressed shortcuts + + dt_accel_path_view(path, 256, "filmstrip", "scroll forward"); + gtk_accel_map_lookup_entry(path, + &darktable.control->accels.filmstrip_forward); + dt_accel_path_view(path, 256, "filmstrip", "scroll back"); + gtk_accel_map_lookup_entry(path, + &darktable.control->accels.filmstrip_back); + + // Lighttable + dt_accel_path_view(path, 256, "lighttable", "scroll up"); + gtk_accel_map_lookup_entry(path, + &darktable.control->accels.lighttable_up); + dt_accel_path_view(path, 256, "lighttable", "scroll down"); + gtk_accel_map_lookup_entry(path, + &darktable.control->accels.lighttable_down); + dt_accel_path_view(path, 256, "lighttable", "scroll left"); + gtk_accel_map_lookup_entry(path, + &darktable.control->accels.lighttable_left); + dt_accel_path_view(path, 256, "lighttable", "scroll right"); + gtk_accel_map_lookup_entry(path, + &darktable.control->accels.lighttable_right); + dt_accel_path_view(path, 256, "lighttable", "scroll center"); + gtk_accel_map_lookup_entry(path, + &darktable.control->accels.lighttable_center); + dt_accel_path_view(path, 256, "lighttable", "preview"); + gtk_accel_map_lookup_entry(path, + &darktable.control->accels.lighttable_preview); + + // Global + dt_accel_path_global(path, 256, "toggle side borders"); + gtk_accel_map_lookup_entry(path, + &darktable.control->accels.global_sideborders); + +} + +static void brightness_key_accel_callback(GtkAccelGroup *accel_group, + GObject *acceleratable, guint keyval, + GdkModifierType modifier, + gpointer data) +{ + GtkWidget *widget; + + if(data) + dt_gui_brightness_increase(); + else + dt_gui_brightness_decrease(); + + widget = darktable.gui->widgets.center; + gtk_widget_queue_draw(widget); + widget = darktable.gui->widgets.navigation; + gtk_widget_queue_draw(widget); +} + +static void contrast_key_accel_callback(GtkAccelGroup *accel_group, + GObject *acceleratable, guint keyval, + GdkModifierType modifier, + gpointer data) +{ + GtkWidget *widget; + + if(data) + dt_gui_contrast_increase(); + else + dt_gui_contrast_decrease(); + + widget = darktable.gui->widgets.center; + gtk_widget_queue_draw(widget); + widget = darktable.gui->widgets.navigation; + gtk_widget_queue_draw(widget); +} + +static void fullscreen_key_accel_callback(GtkAccelGroup *accel_group, + GObject *acceleratable, guint keyval, + GdkModifierType modifier, + gpointer data) +{ + GtkWidget *widget; + int fullscreen; + + if(data) + { + widget = darktable.gui->widgets.main_window; + fullscreen = dt_conf_get_bool("ui_last/fullscreen"); + if(fullscreen) gtk_window_unfullscreen(GTK_WINDOW(widget)); + else gtk_window_fullscreen (GTK_WINDOW(widget)); + fullscreen ^= 1; + dt_conf_set_bool("ui_last/fullscreen", fullscreen); + dt_dev_invalidate(darktable.develop); + } + else + { + widget = darktable.gui->widgets.main_window; + gtk_window_unfullscreen(GTK_WINDOW(widget)); + fullscreen = 0; + dt_conf_set_bool("ui_last/fullscreen", fullscreen); + dt_dev_invalidate(darktable.develop); + } + + widget = darktable.gui->widgets.center; + gtk_widget_queue_draw(widget); + widget = darktable.gui->widgets.navigation; + gtk_widget_queue_draw(widget); +} + +static void view_switch_key_accel_callback(GtkAccelGroup *accel_group, + GObject *acceleratable, guint keyval, + GdkModifierType modifier, + gpointer data) +{ + GtkWidget *widget; + + dt_ctl_switch_mode(); + + widget = darktable.gui->widgets.center; + gtk_widget_queue_draw(widget); + widget = darktable.gui->widgets.navigation; + gtk_widget_queue_draw(widget); +} + static gboolean borders_button_pressed (GtkWidget *w, GdkEventButton *event, gpointer user_data) { GtkWidget *widget; long int which = (long int)user_data; @@ -163,10 +293,15 @@ /* conenct the signals */ g_signal_connect (G_OBJECT (w), "focus-in-event", G_CALLBACK(_widget_focus_in_block_key_accelerators), (gpointer)w); g_signal_connect (G_OBJECT (w), "focus-out-event", G_CALLBACK(_widget_focus_out_unblock_key_accelerators), (gpointer)w); } +//static gint _strcmp(gconstpointer a, gconstpointer b) +//{ +// return strcmp((const char*)b, (const char*)a); +//} + static gboolean expose_borders (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { // draw arrows on borders if(!dt_control_running()) return TRUE; @@ -438,11 +573,11 @@ GtkWidget *dialog = gtk_about_dialog_new(); gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(dialog), PACKAGE_NAME); gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(dialog), PACKAGE_VERSION); gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(dialog), "copyright (c) johannes hanika, henrik andersson, tobias ellinghaus et al. 2009-2011"); gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog), _("organize and develop images from digital cameras")); - gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog), "http://darktable.sf.net/"); + gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(dialog), "http://www.darktable.org/"); gtk_about_dialog_set_logo_icon_name(GTK_ABOUT_DIALOG(dialog), "darktable"); const char *authors[] = { _("* developers *"), "Henrik Andersson", @@ -917,11 +1052,15 @@ dt_pthread_mutex_unlock(&darktable.control->cond_mutex); widget = darktable.gui->widgets.center; gtk_widget_queue_draw(widget); } -static void _gui_switch_view_key_accel_callback(void *p) +static void _gui_switch_view_key_accel_callback(GtkAccelGroup *accel_group, + GObject *acceleratable, + guint keyval, + GdkModifierType modifier, + gpointer p) { int view=(long int)p; dt_ctl_gui_mode_t mode=DT_MODE_NONE; /* do some setup before switch view*/ switch (view) @@ -950,11 +1089,13 @@ /* try switch to mode */ dt_ctl_switch_mode_to (mode); } -static void quit_callback(void *p) +static void quit_callback(GtkAccelGroup *accel_group, + GObject *acceleratable, guint keyval, + GdkModifierType modifier) { quit(); } static gboolean @@ -981,77 +1122,30 @@ oldh = event->height; return dt_control_configure(da, event, user_data); } -void dt_gui_key_accel_register(guint state, guint keyval, void (*callback)(void *), void *data) -{ - dt_gui_key_accel_t *a = (dt_gui_key_accel_t *)malloc(sizeof(dt_gui_key_accel_t)); - a->state = state; - a->keyval = keyval; - a->callback = callback; - a->data = data; - darktable.gui->key_accels = g_list_append(darktable.gui->key_accels, a); -} - -void dt_gui_key_accel_unregister(void (*callback)(void *)) -{ - GList *i = darktable.gui->key_accels; - while(i) - { - dt_gui_key_accel_t *a = (dt_gui_key_accel_t *)i->data; - GList *ii = g_list_next(i); - if(a->callback == callback) - { - free(a); - darktable.gui->key_accels = g_list_delete_link(darktable.gui->key_accels, i); - } - i = ii; - } -} - static gboolean key_pressed_override (GtkWidget *w, GdkEventKey *event, gpointer user_data) { - GList *i = darktable.gui->key_accels; // fprintf(stderr,"Key Press state: %d hwkey: %d\n",event->state, event->hardware_keycode); - /* check if we should handle key press */ - if (dt_control_is_key_accelerators_on (darktable.control) !=1) - return FALSE; - - // we're only interested in ctrl, shift, mod1 (alt) - int estate = event->state & 0xf; - - while(i) - { - dt_gui_key_accel_t *a = (dt_gui_key_accel_t *)i->data; - // if a->state == 0, i.e. no modifiers are selected, no modifiers are allowed, in fact. - if( - ( - (!a->state && (!estate || (!(estate&GDK_MOD1_MASK) && !(estate&GDK_CONTROL_MASK)) ) ) || - (a->state && (a->state == (a->state & estate))) - ) && a->keyval == event->keyval) - { - a->callback(a->data); - return TRUE; - } - i = g_list_next(i); - } - return dt_control_key_pressed_override(event->hardware_keycode); + + return dt_control_key_pressed_override(event->keyval, + event->state & KEY_STATE_MASK); } static gboolean key_pressed (GtkWidget *w, GdkEventKey *event, gpointer user_data) { - return dt_control_key_pressed(event->hardware_keycode); + return dt_control_key_pressed(event->keyval, event->state & KEY_STATE_MASK); } static gboolean key_released (GtkWidget *w, GdkEventKey *event, gpointer user_data) { - return dt_control_key_released(event->hardware_keycode); + return dt_control_key_released(event->keyval, event->state & KEY_STATE_MASK); } static gboolean button_pressed (GtkWidget *w, GdkEventButton *event, gpointer user_data) { @@ -1096,11 +1190,11 @@ int dt_gui_gtk_init(dt_gui_gtk_t *gui, int argc, char *argv[]) { // unset gtk rc from kde: char path[1024], datadir[1024]; - dt_get_datadir(datadir, 1024); + dt_util_get_datadir(datadir, 1024); gchar *themefile = dt_conf_get_string("themefile"); if(themefile && themefile[0] == '/') snprintf(path, 1023, "%s", themefile); else snprintf(path, 1023, "%s/%s", datadir, themefile ? themefile : "darktable.gtkrc"); if(!g_file_test(path, G_FILE_TEST_EXISTS)) snprintf(path, 1023, "%s/%s", DARKTABLE_DATADIR, themefile ? themefile : "darktable.gtkrc"); @@ -1129,13 +1223,28 @@ g_free(themefile); return 1; } g_free(themefile); + // Initializing the shortcut groups + darktable.control->accelerators = gtk_accel_group_new(); + + darktable.control->accelerator_list = NULL; + + // Connecting the callback to update keyboard accels for key_pressed + g_signal_connect(G_OBJECT(gtk_accel_map_get()), + "changed", + G_CALLBACK(key_accel_changed), + NULL); + // Initializing widgets init_widgets(); + // Adding the global shortcut group to the main window + gtk_window_add_accel_group(GTK_WINDOW(darktable.gui->widgets.main_window), + darktable.control->accelerators); + // set constant width from gconf key int panel_width = dt_conf_get_int("panel_width"); if(panel_width < 20 || panel_width > 500) { // fix for unset/insane values. @@ -1166,11 +1275,11 @@ dt_gui_devices_init(); #endif dt_gui_background_jobs_init(); /* Have the delete event (window close) end the program */ - dt_get_datadir(datadir, 1024); + dt_util_get_datadir(datadir, 1024); snprintf(path, 1024, "%s/icons", datadir); gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), path); widget = darktable.gui->widgets.center; @@ -1275,19 +1384,86 @@ gtk_container_set_focus_vadjustment (box, gtk_scrolled_window_get_vadjustment (swin)); dt_ctl_get_display_profile(widget, &darktable.control->xprofile_data, &darktable.control->xprofile_size); darktable.gui->redraw_widgets = NULL; - darktable.gui->key_accels = NULL; // register keys for view switching - dt_gui_key_accel_register(0, GDK_t, _gui_switch_view_key_accel_callback, (void *)DT_GUI_VIEW_SWITCH_TO_TETHERING); - dt_gui_key_accel_register(0, GDK_l, _gui_switch_view_key_accel_callback, (void *)DT_GUI_VIEW_SWITCH_TO_LIBRARY); - dt_gui_key_accel_register(0, GDK_d, _gui_switch_view_key_accel_callback, (void *)DT_GUI_VIEW_SWITCH_TO_DARKROOM); + dt_accel_register_global(NC_("accel", "capture view"), GDK_t, 0); + dt_accel_register_global(NC_("accel", "lighttable view"), GDK_l, 0); + dt_accel_register_global(NC_("accel", "darkroom view"), GDK_d, 0); + + dt_accel_connect_global( + "capture view", + g_cclosure_new(G_CALLBACK(_gui_switch_view_key_accel_callback), + (gpointer)DT_GUI_VIEW_SWITCH_TO_TETHERING, NULL)); + dt_accel_connect_global( + "lighttable view", + g_cclosure_new(G_CALLBACK(_gui_switch_view_key_accel_callback), + (gpointer)DT_GUI_VIEW_SWITCH_TO_LIBRARY, NULL)); + dt_accel_connect_global( + "darkroom view", + g_cclosure_new(G_CALLBACK(_gui_switch_view_key_accel_callback), + (gpointer)DT_GUI_VIEW_SWITCH_TO_DARKROOM, NULL)); // register ctrl-q to quit: - dt_gui_key_accel_register(GDK_CONTROL_MASK, GDK_q, quit_callback, (void *)0); + dt_accel_register_global(NC_("accel", "quit"), GDK_q, GDK_CONTROL_MASK); + + dt_accel_connect_global( + "quit", + g_cclosure_new(G_CALLBACK(quit_callback), NULL, NULL)); + + // Contrast and brightness accelerators + dt_accel_register_global(NC_("accel", "increase brightness"), + GDK_F10, 0); + dt_accel_register_global(NC_("accel", "decrease brightness"), + GDK_F9, 0); + dt_accel_register_global(NC_("accel", "increase contrast"), + GDK_F8, 0); + dt_accel_register_global(NC_("accel", "decrease contrast"), + GDK_F7, 0); + + dt_accel_connect_global( + "increase brightness", + g_cclosure_new(G_CALLBACK(brightness_key_accel_callback), + (gpointer)1, NULL)); + dt_accel_connect_global( + "decrease brightness", + g_cclosure_new(G_CALLBACK(brightness_key_accel_callback), + (gpointer)0, NULL)); + dt_accel_connect_global( + "increase contrast", + g_cclosure_new(G_CALLBACK(contrast_key_accel_callback), + (gpointer)1, NULL)); + dt_accel_connect_global( + "decrease contrast", + g_cclosure_new(G_CALLBACK(contrast_key_accel_callback), + (gpointer)0, NULL)); + + // Full-screen accelerators + dt_accel_register_global(NC_("accel", "toggle fullscreen"), GDK_F11, 0); + dt_accel_register_global(NC_("accel", "leave fullscreen"), GDK_Escape, 0); + + dt_accel_connect_global( + "toggle fullscreen", + g_cclosure_new(G_CALLBACK(fullscreen_key_accel_callback), + (gpointer)1, NULL)); + dt_accel_connect_global( + "leave fullscreen", + g_cclosure_new(G_CALLBACK(fullscreen_key_accel_callback), + (gpointer)0, NULL)); + + // Side-border hide/show + dt_accel_register_global(NC_("accel", "toggle side borders"), GDK_Tab, 0); + + // View-switch + dt_accel_register_global(NC_("accel", "switch view"), GDK_period, 0); + + dt_accel_connect_global( + "switch view", + g_cclosure_new(G_CALLBACK(view_switch_key_accel_callback), NULL, NULL)); + darktable.gui->reset = 0; for(int i=0; i<3; i++) darktable.gui->bgcolor[i] = 0.1333; /* apply contrast to theme */ dt_gui_contrast_init (); @@ -1729,10 +1905,11 @@ int i; GtkWidget *widget; GtkWidget** widgets[] = { + &darktable.gui->widgets.metadata_label_imageid, &darktable.gui->widgets.metadata_label_filename, &darktable.gui->widgets.metadata_label_model, &darktable.gui->widgets.metadata_label_maker, &darktable.gui->widgets.metadata_label_aperture, &darktable.gui->widgets.metadata_label_exposure, @@ -1749,10 +1926,11 @@ &darktable.gui->widgets.metadata_label_rights }; gchar* labels[] = { + _("image id"), _("filename"), _("model"), _("maker"), _("aperture"), _("exposure"), @@ -1798,11 +1976,11 @@ gtk_widget_show(widget); // Attaching the information labels container = widget; - for(i = 0; i < 16; i++) + for(i = 0; i < 17; i++) { // Attaching the field title widget = gtk_label_new(labels[i]); gtk_table_attach(GTK_TABLE(container), widget, 0, 1, i, i + 1, GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); @@ -1875,11 +2053,11 @@ gtk_widget_show(widget); // Adding the expander container = widget; - widget = gtk_expander_new(_("import")); + widget = gtk_expander_new(C_("global import", "import")); darktable.gui->widgets.import_expander = widget; gtk_widget_set_can_focus(widget, TRUE); gtk_expander_set_expanded(GTK_EXPANDER(widget), TRUE); gtk_expander_set_spacing(GTK_EXPANDER(widget), 10); gtk_container_add(GTK_CONTAINER(container), widget); --- src/gui/camera_import_dialog.c.orig 2012-01-30 21:22:35.312554363 +1000 +++ src/gui/camera_import_dialog.c 2012-01-30 21:22:35.338393435 +1000 @@ -14,17 +14,21 @@ You should have received a copy of the GNU General Public License along with darktable. If not, see . */ +#define _XOPEN_SOURCE 600 // for strptime + +#include #include "develop/develop.h" #include "control/control.h" #include "control/jobs.h" #include "control/conf.h" #include "common/exif.h" #include "common/variables.h" #include "common/camera_control.h" +#include "common/utility.h" #include "dtgtk/button.h" #include "dtgtk/label.h" #include "gui/camera_import_dialog.h" /* @@ -162,21 +166,24 @@ static void _update_example(_camera_import_dialog_t *dialog) { // create path/filename and execute a expand.. gchar *path=g_build_path(G_DIR_SEPARATOR_S,dialog->settings.basedirectory->value,dialog->settings.subdirectory->value,"/",(char *)NULL); - dt_variables_expand( dialog->vp, path, FALSE); + gchar *fixed_path=dt_util_fix_path(path); + dt_variables_expand( dialog->vp, fixed_path, FALSE); + gchar *ep=g_strdup(dt_variables_get_result(dialog->vp)); dt_variables_expand( dialog->vp, dialog->settings.namepattern->value, TRUE); gchar *ef=g_strdup(dt_variables_get_result(dialog->vp)); gchar *str=g_strdup_printf("%s\n%s",ep,ef); // then set result set gtk_label_set_text(GTK_LABEL(dialog->settings.example),str); // Clenaup g_free(path); + g_free(fixed_path); g_free(ep); g_free(ef); g_free(str); } @@ -255,11 +262,11 @@ void _camera_import_dialog_new(_camera_import_dialog_t *data) { - data->dialog=gtk_dialog_new_with_buttons(_("import images from camera"),NULL,GTK_DIALOG_MODAL,_("cancel"),GTK_RESPONSE_NONE,_("import"),GTK_RESPONSE_ACCEPT,NULL); + data->dialog=gtk_dialog_new_with_buttons(_("import images from camera"),NULL,GTK_DIALOG_MODAL,_("cancel"),GTK_RESPONSE_NONE,C_("camera import", "import"),GTK_RESPONSE_ACCEPT,NULL); GtkWidget *content = gtk_dialog_get_content_area (GTK_DIALOG (data->dialog)); // List - setup store data->store = gtk_list_store_new (2,GDK_TYPE_PIXBUF,G_TYPE_STRING); @@ -334,10 +341,11 @@ data->settings.general.date_override=gtk_check_button_new_with_label(_("override today's date")); gtk_box_pack_start(GTK_BOX(hbox),data->settings.general.date_override,FALSE,FALSE,0); g_object_set(data->settings.general.date_override,"tooltip-text",_("check this, if you want to override the timestamp used when expanding variables:\n$(YEAR), $(MONTH), $(DAY),\n$(HOUR), $(MINUTE), $(SECONDS)"),(char *)NULL); data->settings.general.date_entry=gtk_entry_new(); + gtk_widget_set_sensitive( data->settings.general.date_entry, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->settings.general.date_override))); gtk_box_pack_start(GTK_BOX(hbox),data->settings.general.date_entry,TRUE,TRUE,0); g_signal_connect (G_OBJECT (data->settings.general.date_override), "clicked",G_CALLBACK (_check_button_callback),data); gtk_box_pack_start(GTK_BOX(data->settings.page),hbox,FALSE,FALSE,0); @@ -537,10 +545,24 @@ dt_control_job_wait (data->preview_job); } return FALSE; } +static time_t parse_date_time(const char* date_time_text) +{ + struct tm t; + memset(&t, 0, sizeof(t)); + + const char* end = NULL; + if((end = strptime(date_time_text, "%Y-%m-%dT%T", &t)) && *end == 0) + return mktime(&t); + if((end = strptime(date_time_text, "%Y-%m-%d", &t)) && *end == 0) + return mktime(&t); + + return 0; +} + void _camera_import_dialog_run(_camera_import_dialog_t *data) { gtk_widget_show_all(data->dialog); // Populate store @@ -598,10 +620,14 @@ data->params->jobcode = data->import.jobname->value; data->params->basedirectory = data->settings.basedirectory->value; data->params->subdirectory = data->settings.subdirectory->value; data->params->filenamepattern = data->settings.namepattern->value; + data->params->time_override = 0; + if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->settings.general.date_override))) + data->params->time_override = parse_date_time(gtk_entry_get_text(GTK_ENTRY(data->settings.general.date_entry))); + if( data->params->jobcode == NULL || strlen(data->params->jobcode) <=0 ) data->params->jobcode = dt_conf_get_string("plugins/capture/camera/import/jobcode"); if( data->params->basedirectory == NULL || strlen( data->params->basedirectory ) <= 0 ) { @@ -622,10 +648,17 @@ GtkWidget *dialog=gtk_message_dialog_new(NULL,GTK_DIALOG_DESTROY_WITH_PARENT,GTK_MESSAGE_ERROR,GTK_BUTTONS_OK,_("please set the filenamepattern settings before importing")); g_signal_connect_swapped (dialog, "response",G_CALLBACK (gtk_widget_destroy),dialog); gtk_dialog_run (GTK_DIALOG (dialog)); all_good=FALSE; } + else if( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(data->settings.general.date_override)) && data->params->time_override == 0) + { + GtkWidget *dialog=gtk_message_dialog_new(NULL,GTK_DIALOG_DESTROY_WITH_PARENT,GTK_MESSAGE_ERROR,GTK_BUTTONS_OK,_("please use YYYY-MM-DD format for date override")); + g_signal_connect_swapped (dialog, "response",G_CALLBACK (gtk_widget_destroy),dialog); + gtk_dialog_run (GTK_DIALOG (dialog)); + all_good=FALSE; + } } else { data->params->result=NULL; all_good=TRUE; --- src/dtview/main.c.orig 2012-01-30 21:22:35.478232982 +1000 +++ src/dtview/main.c 2012-01-30 21:22:35.510573277 +1000 @@ -14,11 +14,11 @@ You should have received a copy of the GNU General Public License along with darktable. If not, see . */ -#define _XOPEN_SOURCE 500 +#define _XOPEN_SOURCE 600 #include double drand48(void); void srand48(long int); #include #include @@ -28,10 +28,11 @@ #include "common/points.h" #include "common/image.h" #include "common/image_cache.h" #include "common/imageio.h" #include "common/imageio_module.h" +#include "control/conf.h" #include #include #include int running; @@ -110,12 +111,12 @@ } static void shutdown() { - // TODO: // close all dt related stuff. + dt_cleanup(); } static void handle_event(const SDL_Event *event) { @@ -249,10 +250,11 @@ return 0; } int main(int argc, char *arg[]) { + gtk_init (&argc, &arg); random_state = use_random = 0; for(int k=1; k 1.0f) H -= 1.0f; + } + + HSL[0] = H; + HSL[1] = S; + HSL[2] = L; +} + + +static inline float _Hue_2_RGB(float v1, float v2, float vH) +{ + if (vH < 0.0f) vH += 1.0f; + if (vH > 1.0f) vH -= 1.0f; + if ((6.0f * vH) < 1.0f) return (v1 + (v2 - v1) * 6.0f * vH); + if ((2.0f * vH) < 1.0f) return (v2); + if ((3.0f * vH) < 2.0f) return (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f); + return (v1); +} + + +static inline void _HSL_2_RGB(const float *HSL, float *RGB) +{ + float H = HSL[0]; + float S = HSL[1]; + float L = HSL[2]; + + float var_1, var_2; + + if (S == 0.0f) + { + RGB[0] = RGB[1] = RGB[2] = L; + } + else + { + if (L < 0.5f) var_2 = L * (1.0f + S); + else var_2 = (L + S) - (S * L); + + var_1 = 2 * L - var_2; + + RGB[0] = _Hue_2_RGB(var_1, var_2, H + (1.0f / 3.0f)); + RGB[1] = _Hue_2_RGB(var_1, var_2, H); + RGB[2] = _Hue_2_RGB(var_1, var_2, H - (1.0f / 3.0f)); + } +} + + +static inline void _Lab_2_LCH(const float *Lab, float *LCH) +{ + float var_H = atan2f(Lab[2], Lab[1]); + + if (var_H > 0.0f) var_H = var_H / (2.0f*M_PI); + else var_H = 1.0f - fabs(var_H) / (2.0f*M_PI); + + LCH[0] = Lab[0]; + LCH[1] = sqrtf(Lab[1]*Lab[1] + Lab[2]*Lab[2]); + LCH[2] = var_H; +} + + + +static inline void _LCH_2_Lab(const float *LCH, float *Lab) +{ + Lab[0] = LCH[0]; + Lab[1] = cosf(2.0f*M_PI*LCH[2]) * LCH[1]; + Lab[2] = sinf(2.0f*M_PI*LCH[2]) * LCH[1]; +} + + + /* normal blend */ static void _blend_normal(dt_iop_colorspace_type_t cst,const float opacity,const float *a, float *b,int stride, int flag) { float ta[3], tb[3]; int channels = _blend_colorspace_channels(cst); @@ -864,10 +968,175 @@ const float doublemax=max*2.0; return (b>halfmax) ? fmax(a,doublemax*(b-halfmax)) : fmin(a,doublemax*b); */ } + +/* lightness blend */ +static void _blend_lightness(dt_iop_colorspace_type_t cst,const float opacity,const float *a, float *b,int stride, int flag) +{ + float ta[3], tb[3]; + float tta[3], ttb[3]; + int channels = _blend_colorspace_channels(cst); + for(int j=0;j 0.5f ? -opacity*(1.0f - d) / d : opacity; + ttb[2] = fmod((tta[2] * (1.0 - s)) + ttb[2] * s + 1.0f, 1.0f); + + _LCH_2_Lab(ttb, tb); + _blend_Lab_rescale(tb, &b[j]); + } + else if(cst==iop_cs_rgb) + { + _RGB_2_HSL(&a[j], tta); _RGB_2_HSL(&b[j], ttb); + + /* blend hue along shortest distance on color circle */ + float d = fabs(tta[0] - ttb[0]); + float s = d > 0.5f ? -opacity*(1.0f - d) / d : opacity; + ttb[0] = fmod((tta[0] * (1.0 - s)) + ttb[0] * s + 1.0f, 1.0f); + ttb[1] = tta[1]; + ttb[2] = tta[2]; + + _HSL_2_RGB(ttb, &b[j]); + } + else + for(int k=0;k 0.5f ? -opacity*(1.0f - d) / d : opacity; + ttb[2] = fmod((tta[2] * (1.0 - s)) + ttb[2] * s + 1.0f, 1.0f); + + _LCH_2_Lab(ttb, tb); + _blend_Lab_rescale(tb, &b[j]); + } + else if(cst==iop_cs_rgb) + { + _RGB_2_HSL(&a[j], tta); _RGB_2_HSL(&b[j], ttb); + + /* blend hue along shortest distance on color circle */ + float d = fabs(tta[0] - ttb[0]); + float s = d > 0.5f ? -opacity*(1.0f - d) / d : opacity; + ttb[0] = fmod((tta[0] * (1.0 - s)) + ttb[0] * s + 1.0f, 1.0f); + + ttb[1] = (tta[1] * (1.0 - opacity)) + ttb[1] * opacity; + ttb[2] = tta[2]; + + _HSL_2_RGB(ttb, &b[j]); + } + else + for(int k=0;kcolors; @@ -920,10 +1189,22 @@ blend = _blend_linearlight; break; case DEVELOP_BLEND_PINLIGHT: blend = _blend_pinlight; break; + case DEVELOP_BLEND_LIGHTNESS: + blend = _blend_lightness; + break; + case DEVELOP_BLEND_CHROMA: + blend = _blend_chroma; + break; + case DEVELOP_BLEND_HUE: + blend = _blend_hue; + break; + case DEVELOP_BLEND_COLOR: + blend = _blend_color; + break; /* fallback to normal blend */ case DEVELOP_BLEND_NORMAL: default: blend = _blend_normal; @@ -947,11 +1228,14 @@ */ if(cst==iop_cs_RAW) ch = 1; #ifdef _OPENMP - #pragma omp parallel for default(none) shared(in,roi_out,out,blend,d,stderr,ch) + #ifndef __SUNOS__ + #pragma omp parallel for default(none) shared(stderr) + #endif + #pragma omp parallel shared(in,roi_out,out,blend,d,ch) #endif for (int y=0; yheight; y++) { int index = (ch*y*roi_out->width); blend(cst, opacity, in+index, out+index, roi_out->width*ch, blendflag); } @@ -998,27 +1282,24 @@ } const int devid = piece->pipe->devid; const float opacity = fmin(fmax(0,(d->opacity/100.0)),1.0); const int blendflag = self->flags() & IOP_FLAGS_BLEND_ONLY_LIGHTNESS; - - /* opencl does not allow reading from and writing to the same image buffer -> we need an intermediate one :-( */ - dev_m = dt_opencl_alloc_device(roi_in->width, roi_in->height, devid, 4*sizeof(float)); - if (dev_m == NULL) goto error; - size_t origin[] = {0, 0, 0}; - size_t region[] = {roi_in->width, roi_in->height, 1}; - err = dt_opencl_enqueue_copy_image(darktable.opencl->dev[devid].cmd_queue, dev_out, dev_m, origin, origin, region, 0, NULL, NULL); - if(err != CL_SUCCESS) goto error; - - size_t sizes[] = {roi_in->width, roi_in->height, 1}; - dt_opencl_set_kernel_arg(darktable.opencl, devid, kernel, 0, sizeof(cl_mem), (void *)&dev_in); - dt_opencl_set_kernel_arg(darktable.opencl, devid, kernel, 1, sizeof(cl_mem), (void *)&dev_m); - dt_opencl_set_kernel_arg(darktable.opencl, devid, kernel, 2, sizeof(cl_mem), (void *)&dev_out); - dt_opencl_set_kernel_arg(darktable.opencl, devid, kernel, 3, sizeof(int), (void *)&(d->mode)); - dt_opencl_set_kernel_arg(darktable.opencl, devid, kernel, 4, sizeof(float), (void *)&opacity); - dt_opencl_set_kernel_arg(darktable.opencl, devid, kernel, 5, sizeof(int), (void *)&blendflag); - err = dt_opencl_enqueue_kernel_2d(darktable.opencl, devid, kernel, sizes); + const int mode = d->mode; + const int width = roi_in->width; + const int height = roi_in->height; + + size_t sizes[] = { ROUNDUP(width, 4), ROUNDUP(height, 4), 1}; + dt_opencl_set_kernel_arg(devid, kernel, 0, sizeof(cl_mem), (void *)&dev_in); + dt_opencl_set_kernel_arg(devid, kernel, 1, sizeof(cl_mem), (void *)&dev_out); + dt_opencl_set_kernel_arg(devid, kernel, 2, sizeof(cl_mem), (void *)&dev_out); + dt_opencl_set_kernel_arg(devid, kernel, 3, sizeof(int), (void *)&width); + dt_opencl_set_kernel_arg(devid, kernel, 4, sizeof(int), (void *)&height); + dt_opencl_set_kernel_arg(devid, kernel, 5, sizeof(int), (void *)&mode); + dt_opencl_set_kernel_arg(devid, kernel, 6, sizeof(float), (void *)&opacity); + dt_opencl_set_kernel_arg(devid, kernel, 7, sizeof(int), (void *)&blendflag); + err = dt_opencl_enqueue_kernel_2d(devid, kernel, sizes); if(err != CL_SUCCESS) goto error; dt_opencl_release_mem_object(dev_m); return TRUE; error: @@ -1031,13 +1312,13 @@ /** global init of blendops */ void dt_develop_blend_init(dt_blendop_t *gd) { #ifdef HAVE_OPENCL const int program = 3; // blendop.cl, from programs.conf - gd->kernel_blendop_Lab = dt_opencl_create_kernel(darktable.opencl, program, "blendop_Lab"); - gd->kernel_blendop_RAW = dt_opencl_create_kernel(darktable.opencl, program, "blendop_RAW"); - gd->kernel_blendop_rgb = dt_opencl_create_kernel(darktable.opencl, program, "blendop_rgb"); + gd->kernel_blendop_Lab = dt_opencl_create_kernel(program, "blendop_Lab"); + gd->kernel_blendop_RAW = dt_opencl_create_kernel(program, "blendop_RAW"); + gd->kernel_blendop_rgb = dt_opencl_create_kernel(program, "blendop_rgb"); #else gd->kernel_blendop_Lab = gd->kernel_blendop_RAW = gd->kernel_blendop_rgb = -1; #endif } --- src/control/jobs/control_jobs.c.orig 2012-01-30 21:23:06.134421148 +1000 +++ src/control/jobs/control_jobs.c 2012-01-30 21:23:11.457050671 +1000 @@ -87,20 +87,21 @@ total ++; while(t) { imgid = (long int)t->data; dt_image_t *img = dt_image_cache_get(imgid, 'r'); + dt_image_buffer_t mip = dt_image_get_blocking(img, DT_IMAGE_FULL, 'r'); if(img->filters == 0 || img->bpp != sizeof(uint16_t)) { dt_control_log(_("exposure bracketing only works on raw images")); + dt_image_release(img, DT_IMAGE_FULL, 'r'); dt_image_cache_release(img, 'r'); free(pixels); free(weight); goto error; } - dt_image_buffer_t mip = dt_image_get_blocking(img, DT_IMAGE_FULL, 'r'); - filter = img->filters; + filter = dt_image_flipped_filter(img); if(mip != DT_IMAGE_FULL) { dt_control_log(_("failed to get raw buffer from image `%s'"), img->filename); dt_image_cache_release(img, 'r'); free(pixels); @@ -128,20 +129,23 @@ goto error; } // if no valid exif data can be found, assume peleng fisheye at f/16, 8mm, with half of the light lost in the system => f/22 const float eap = img->exif_aperture > 0.0f ? img->exif_aperture : 22.0f; const float efl = img->exif_focal_length > 0.0f ? img->exif_focal_length : 8.0f; - const float aperture = M_PI * powf(efl / (2.0f * eap), 2.0f); - const float cal = 100.0f/(aperture*img->exif_exposure*img->exif_iso); + const float rad = .5f * efl/eap; + const float aperture = M_PI * rad * rad; + const float iso = img->exif_iso > 0.0f ? img->exif_iso : 100.0f; + const float exp = img->exif_exposure > 0.0f ? img->exif_exposure : 1.0f; + const float cal = 100.0f/(aperture*exp*iso); whitelevel = fmaxf(whitelevel, cal); #ifdef _OPENMP #pragma omp parallel for schedule(static) default(none) shared(img, pixels, weight, wd, ht) #endif for(int k=0; kpixels)[k]; - const float w = .001f + (in >= 1000 ? (in < 65000 ? in/65000.0f : 0.0f) : img->exif_exposure * 0.01f); + const float w = .001f + (in >= 1000 ? (in < 65000 ? in/65000.0f : 0.0f) : exp * 0.01f); pixels[k] += w * in * cal; weight[k] += w; } t = g_list_delete_link(t, t); @@ -303,11 +307,10 @@ // We need a list of files to regenerate .xmp files if there are duplicates GList *list = NULL; DT_DEBUG_SQLITE3_PREPARE_V2(darktable.db, "select distinct folder || '/' || filename from images, film_rolls where images.film_id = film_rolls.id and images.id in (select imgid from selected_images)", -1, &stmt, NULL); - DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, imgid); if(sqlite3_step(stmt) == SQLITE_ROW) { list = g_list_append(list, g_strdup((const gchar *)sqlite3_column_text(stmt, 0))); } sqlite3_finalize(stmt); @@ -432,15 +435,26 @@ { if(dt_conf_get_bool("ask_before_remove")) { GtkWidget *dialog; GtkWidget *win = darktable.gui->widgets.main_window; + + sqlite3_stmt *stmt = NULL; + int number = 0; + + DT_DEBUG_SQLITE3_PREPARE_V2(darktable.db, "select count(imgid) from selected_images", -1, &stmt, NULL); + if(sqlite3_step(stmt) == SQLITE_ROW) + { + number = sqlite3_column_int(stmt, 0); + } + dialog = gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, - _("do you really want to remove all selected images from the collection?")); + ngettext("do you really want to remove %d selected image from the collection?", + "do you really want to remove %d selected images from the collection?", number), number); gtk_window_set_title(GTK_WINDOW(dialog), _("remove images?")); gint res = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); if(res != GTK_RESPONSE_YES) return; } @@ -453,15 +467,26 @@ { if(dt_conf_get_bool("ask_before_delete")) { GtkWidget *dialog; GtkWidget *win = darktable.gui->widgets.main_window; + + sqlite3_stmt *stmt = NULL; + int number = 0; + + DT_DEBUG_SQLITE3_PREPARE_V2(darktable.db, "select count(imgid) from selected_images", -1, &stmt, NULL); + if(sqlite3_step(stmt) == SQLITE_ROW) + { + number = sqlite3_column_int(stmt, 0); + } + dialog = gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, - _("do you really want to physically delete all selected images from disk?")); + ngettext("do you really want to PHYSICALLY delete %d selected image from disk?", + "do you really want to PHYSICALLY delete %d selected images from disk?", number), number); gtk_window_set_title(GTK_WINDOW(dialog), _("delete images?")); gint res = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); if(res != GTK_RESPONSE_YES) return; } @@ -513,11 +538,14 @@ // use min of user request and mipmap cache entries const int full_entries = dt_conf_get_int ("parallel_export"); // GCC won't accept that this variable is used in a macro, considers // it set but not used, which makes for instance Fedora break. const __attribute__((__unused__)) int num_threads = MAX(1, MIN(full_entries, darktable.mipmap_cache->num_entries[DT_IMAGE_FULL]) - 1); - #pragma omp parallel default(none) private(imgid, size) shared(j, fraction, stderr, w, h, mformat, mstorage, t, sdata, job) num_threads(num_threads) if(num_threads > 1) +#ifndef __SUNOS__ + #pragma omp default(none) shared(stderr) +#endif + #pragma omp parallel private(imgid, size) shared(j, fraction, w, h, mformat, mstorage, t, sdata, job) num_threads(num_threads) if(num_threads > 1) { #endif // get a thread-safe fdata struct (one jpeg struct per thread etc): dt_imageio_module_data_t *fdata = mformat->get_params(mformat, &size); fdata->max_width = dt_conf_get_int ("plugins/lighttable/export/width");