forked from darktable-org/darktable
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontrol.c
More file actions
1693 lines (1484 loc) · 55.2 KB
/
Copy pathcontrol.c
File metadata and controls
1693 lines (1484 loc) · 55.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
This file is part of darktable,
copyright (c) 2009--2011 johannes hanika.
copyright (c) 2010--2011 henrik andersson.
Copyright (c) 2012 James C. McPherson
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 <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "common/darktable.h"
#include "control/control.h"
#include "control/conf.h"
#include "develop/develop.h"
#include "common/image_cache.h"
#include "common/imageio.h"
#include "common/debug.h"
#include "lua/dt_lua.h"
#include "bauhaus/bauhaus.h"
#include "views/view.h"
#include "gui/gtk.h"
#include "gui/contrast.h"
#include "gui/draw.h"
#ifdef GDK_WINDOWING_QUARTZ
# include <Carbon/Carbon.h>
# include <ApplicationServices/ApplicationServices.h>
# include <CoreServices/CoreServices.h>
#endif
#ifdef G_OS_WIN32
# include <windows.h>
#endif
#include <stdlib.h>
#include <strings.h>
#include <assert.h>
#include <math.h>
#include <string.h>
#include <glib/gstdio.h>
#include <gdk/gdkkeysyms.h>
/* the queue can have scheduled jobs but all
the workers is sleeping, so this kicks the workers
on timed interval.
*/
static void * _control_worker_kicker(void *ptr);
void dt_ctl_settings_default(dt_control_t *c)
{
dt_conf_set_string ("database", "library.db");
dt_conf_set_int ("config_version", DT_CONFIG_VERSION);
dt_conf_set_bool ("write_sidecar_files", TRUE);
dt_conf_set_bool ("ask_before_delete", TRUE);
dt_conf_set_int ("parallel_export", 1);
dt_conf_set_int ("worker_threads", 2);
dt_conf_set_int ("cache_memory", 536870912);
dt_conf_set_int ("database_cache_quality", 89);
dt_conf_set_bool ("ui_last/fullscreen", FALSE);
dt_conf_set_bool ("ui_last/maximized", FALSE);
dt_conf_set_int ("ui_last/view", DT_MODE_NONE);
dt_conf_set_int ("ui_last/window_x", 0);
dt_conf_set_int ("ui_last/window_y", 0);
dt_conf_set_int ("ui_last/window_w", 900);
dt_conf_set_int ("ui_last/window_h", 500);
dt_conf_set_int ("ui_last/panel_left", -1);
dt_conf_set_int ("ui_last/panel_right", -1);
dt_conf_set_int ("ui_last/panel_top", 0);
dt_conf_set_int ("ui_last/panel_bottom", 0);
dt_conf_set_int ("ui_last/expander_library", 1<<DT_LIBRARY);
dt_conf_set_int ("ui_last/expander_navigation", -1);
dt_conf_set_int ("ui_last/expander_histogram", -1);
dt_conf_set_int ("ui_last/expander_history", -1);
dt_conf_set_int ("ui_last/initial_rating", DT_LIB_FILTER_STAR_1);
// import settings
dt_conf_set_string ("capture/camera/storage/basedirectory", "$(PICTURES_FOLDER)/Darktable");
dt_conf_set_string ("capture/camera/storage/subpath", "$(YEAR)$(MONTH)$(DAY)_$(JOBCODE)");
dt_conf_set_string ("capture/camera/storage/namepattern", "$(YEAR)$(MONTH)$(DAY)_$(SEQUENCE).$(FILE_EXTENSION)");
dt_conf_set_string ("capture/camera/import/jobcode", "noname");
dt_conf_set_int ("plugins/collection/film_id", 1);
dt_conf_set_int ("plugins/collection/filter_flags", 3);
dt_conf_set_int ("plugins/collection/query_flags", 3);
dt_conf_set_int ("plugins/collection/rating", 1);
dt_conf_set_int ("plugins/lighttable/collect/num_rules", 0);
dt_conf_set_int ("plugins/collection/sort", 0);
dt_conf_set_bool ("plugins/collection/descending", 0);
// reasonable thumbnail res:
dt_conf_set_int ("plugins/lighttable/thumbnail_width", 1300);
dt_conf_set_int ("plugins/lighttable/thumbnail_height", 1000);
}
void dt_ctl_settings_init(dt_control_t *s)
{
// same thread as init
s->gui_thread = pthread_self();
// init global defaults.
dt_pthread_mutex_init(&(s->global_mutex), NULL);
dt_pthread_mutex_init(&(s->image_mutex), NULL);
s->global_settings.version = DT_VERSION;
// TODO: move the mouse_over_id of lighttable to something general in
// control: gui-thread selected img or so?
s->global_settings.lib_image_mouse_over_id = -1;
// TODO: move these to darkroom settings blob:
s->global_settings.dev_closeup = 0;
s->global_settings.dev_zoom_x = 0;
s->global_settings.dev_zoom_y = 0;
s->global_settings.dev_zoom = DT_ZOOM_FIT;
memcpy(&(s->global_defaults), &(s->global_settings), sizeof(dt_ctl_settings_t));
}
// There are systems where absolute paths don't start with '/' (like Windows).
// Since the bug which introduced absolute paths to the db was fixed before a
// Windows build was available this shouldn't matter though.
static void dt_control_sanitize_database()
{
sqlite3_stmt *stmt;
sqlite3_stmt *innerstmt;
DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
"select id, filename from images where filename like '/%'",
-1, &stmt, NULL);
DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
"update images set filename = ?1 where id = ?2", -1, &innerstmt, NULL);
while (sqlite3_step(stmt) == SQLITE_ROW)
{
int id = sqlite3_column_int(stmt, 0);
const char* path = (const char*)sqlite3_column_text(stmt, 1);
gchar* filename = g_path_get_basename (path);
DT_DEBUG_SQLITE3_BIND_TEXT(innerstmt, 1, filename, -1, SQLITE_TRANSIENT);
DT_DEBUG_SQLITE3_BIND_INT(innerstmt, 2, id);
sqlite3_step(innerstmt);
sqlite3_reset(innerstmt);
sqlite3_clear_bindings(innerstmt);
g_free(filename);
}
sqlite3_finalize(stmt);
sqlite3_finalize(innerstmt);
// TODO: Create in attached memory database
// temporary stuff for some ops, need this for some reason with newer sqlite3:
DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db),
"CREATE TABLE memory.color_labels_temp (imgid INTEGER PRIMARY KEY)",
NULL, NULL, NULL);
DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db),
"CREATE TABLE memory.tmp_selection (imgid INTEGER)", NULL, NULL, NULL);
DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db),
"CREATE TABLE memory.tagq (tmpid INTEGER PRIMARY KEY, id INTEGER)",
NULL, NULL, NULL);
DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db),
"CREATE TABLE memory.taglist "
"(tmpid INTEGER PRIMARY KEY, id INTEGER UNIQUE ON CONFLICT REPLACE, "
"count INTEGER)",
NULL, NULL, NULL);
}
int dt_control_load_config(dt_control_t *c)
{
dt_conf_set_int("ui_last/view", DT_MODE_NONE);
int width = dt_conf_get_int("ui_last/window_w");
int height = dt_conf_get_int("ui_last/window_h");
gint x = dt_conf_get_int("ui_last/window_x");
gint y = dt_conf_get_int("ui_last/window_y");
GtkWidget *widget = dt_ui_main_window(darktable.gui->ui);
gtk_window_move(GTK_WINDOW(widget),x,y);
gtk_window_resize(GTK_WINDOW(widget), width, height);
int fullscreen = dt_conf_get_bool("ui_last/fullscreen");
if(fullscreen) gtk_window_fullscreen (GTK_WINDOW(widget));
else
{
gtk_window_unfullscreen(GTK_WINDOW(widget));
int maximized = dt_conf_get_bool("ui_last/maximized");
if(maximized) gtk_window_maximize(GTK_WINDOW(widget));
else gtk_window_unmaximize(GTK_WINDOW(widget));
}
return 0;
}
int dt_control_write_config(dt_control_t *c)
{
GtkWidget *widget = dt_ui_main_window(darktable.gui->ui);
gint x, y;
gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
dt_conf_set_int ("ui_last/window_x", x);
dt_conf_set_int ("ui_last/window_y", y);
dt_conf_set_int ("ui_last/window_w", widget->allocation.width);
dt_conf_set_int ("ui_last/window_h", widget->allocation.height);
dt_conf_set_bool("ui_last/maximized",
(gdk_window_get_state(widget->window) & GDK_WINDOW_STATE_MAXIMIZED));
sqlite3_stmt *stmt;
dt_pthread_mutex_lock(&(darktable.control->global_mutex));
DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
"update settings set settings = ?1 where rowid = 1", -1, &stmt, NULL);
DT_DEBUG_SQLITE3_BIND_BLOB(stmt, 1, &(darktable.control->global_settings),
sizeof(dt_ctl_settings_t), SQLITE_STATIC);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
dt_pthread_mutex_unlock(&(darktable.control->global_mutex));
return 0;
}
// Get the display ICC profile of the monitor associated with the widget.
// For X display, uses the ICC profile specifications version 0.2 from
// http://burtonini.com/blog/computers/xicc
// Based on code from Gimp's modules/cdisplay_lcms.c
#ifdef GDK_WINDOWING_QUARTZ
typedef struct
{
guchar *data;
gsize len;
}
ProfileTransfer;
enum
{
openReadSpool = 1, /* start read data process */
openWriteSpool = 2, /* start write data process */
readSpool = 3, /* read specified number of bytes */
writeSpool = 4, /* write specified number of bytes */
closeSpool = 5 /* complete data transfer process */
};
#ifndef __LP64__
static OSErr dt_ctl_lcms_flatten_profile(SInt32 command,
SInt32 *size, void *data, void *refCon)
{
// ProfileTransfer *transfer = static_cast<ProfileTransfer*>(refCon);
ProfileTransfer *transfer = (ProfileTransfer *)refCon;
switch (command)
{
case openWriteSpool:
g_return_val_if_fail(transfer->data==NULL && transfer->len==0, -1);
break;
case writeSpool:
transfer->data = (guchar *)
g_realloc(transfer->data, transfer->len + *size);
memcpy(transfer->data + transfer->len, data, *size);
transfer->len += *size;
break;
default:
break;
}
return 0;
}
#endif /* __LP64__ */
#endif /* GDK_WINDOWING_QUARTZ */
void dt_ctl_get_display_profile(GtkWidget *widget,
guint8 **buffer, gint *buffer_size)
{
// thanks to ufraw for this!
*buffer = NULL;
*buffer_size = 0;
#if defined GDK_WINDOWING_X11
GdkScreen *screen = gtk_widget_get_screen(widget);
if ( screen==NULL )
screen = gdk_screen_get_default();
int monitor = gdk_screen_get_monitor_at_window (screen, widget->window);
char *atom_name;
if (monitor > 0)
atom_name = g_strdup_printf("_ICC_PROFILE_%d", monitor);
else
atom_name = g_strdup("_ICC_PROFILE");
GdkAtom type = GDK_NONE;
gint format = 0;
gdk_property_get(gdk_screen_get_root_window(screen),
gdk_atom_intern(atom_name, FALSE), GDK_NONE,
0, 64 * 1024 * 1024, FALSE,
&type, &format, buffer_size, buffer);
g_free(atom_name);
#elif defined GDK_WINDOWING_QUARTZ
GdkScreen *screen = gtk_widget_get_screen(widget);
if ( screen==NULL )
screen = gdk_screen_get_default();
int monitor = gdk_screen_get_monitor_at_window(screen, widget->window);
CMProfileRef prof = NULL;
CMGetProfileByAVID(monitor, &prof);
if ( prof==NULL )
return;
ProfileTransfer transfer = { NULL, 0 };
//The following code does not work on 64bit OSX.
//Disable if we are compiling there.
#ifndef __LP64__
Boolean foo;
CMFlattenProfile(prof, 0, dt_ctl_lcms_flatten_profile, &transfer, &foo);
CMCloseProfile(prof);
#endif
*buffer = transfer.data;
*buffer_size = transfer.len;
#elif defined G_OS_WIN32
(void)widget;
HDC hdc = GetDC (NULL);
if ( hdc==NULL )
return;
DWORD len = 0;
GetICMProfile (hdc, &len, NULL);
gchar *path = g_new (gchar, len);
if (GetICMProfile (hdc, &len, path))
{
gsize size;
g_file_get_contents(path, (gchar**)buffer, &size, NULL);
*buffer_size = size;
}
g_free (path);
ReleaseDC (NULL, hdc);
#endif
}
void dt_control_create_database_schema()
{
DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db),
"create table settings (settings blob)", NULL, NULL, NULL);
DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db),
"create table film_rolls "
"(id integer primary key, datetime_accessed char(20), "
"folder varchar(1024))", NULL, NULL, NULL);
DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db),
"create table images (id integer primary key, film_id integer, "
"width int, height int, filename varchar, maker varchar, model varchar, "
"lens varchar, exposure real, aperture real, iso real, focal_length real, "
"focus_distance real, datetime_taken char(20), flags integer, "
"output_width integer, output_height integer, crop real, "
"raw_parameters integer, raw_denoise_threshold real, "
"raw_auto_bright_threshold real, raw_black real, raw_maximum real, "
"caption varchar, description varchar, license varchar, sha1sum char(40), "
"orientation integer ,histogram blob, lightmap blob)", NULL, NULL, NULL);
DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db),
"create table selected_images (imgid integer)", NULL, NULL, NULL);
DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db),
"create table history (imgid integer, num integer, module integer, "
"operation varchar(256), op_params blob, enabled integer, "
"blendop_params blob, blendop_version integer)", NULL, NULL, NULL);
DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db),
"create table tags (id integer primary key, name varchar, icon blob, "
"description varchar, flags integer)", NULL, NULL, NULL);
DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db),
"create table tagxtag (id1 integer, id2 integer, count integer, "
"primary key(id1, id2))", NULL, NULL, NULL);
DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db),
"create table tagged_images (imgid integer, tagid integer, "
"primary key(imgid, tagid))", NULL, NULL, NULL);
DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db),
"create table styles (name varchar,description varchar)", NULL, NULL, NULL);
DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db),
"create table style_items (styleid integer, num integer, module integer, "
"operation varchar(256), op_params blob, enabled integer, "
"blendop_params blob, blendop_version integer)", NULL, NULL, NULL);
DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db),
"create table color_labels (imgid integer, color integer)",
NULL, NULL, NULL);
DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db),
"create table meta_data (id integer,key integer,value varchar)",
NULL, NULL, NULL);
}
void dt_control_init(dt_control_t *s)
{
dt_ctl_settings_init(s);
memset(s->vimkey, 0, sizeof(s->vimkey));
s->vimkey_cnt = 0;
// s->last_expose_time = dt_get_wtime();
s->key_accelerators_on = 1;
s->log_pos = s->log_ack = 0;
s->log_busy = 0;
s->log_message_timeout_id = 0;
dt_pthread_mutex_init(&(s->log_mutex), NULL);
s->progress = 200.0f;
dt_conf_set_int("ui_last/view", DT_MODE_NONE);
// if config is old, replace with new defaults.
if(DT_CONFIG_VERSION > dt_conf_get_int("config_version"))
dt_ctl_settings_default(s);
pthread_cond_init(&s->cond, NULL);
dt_pthread_mutex_init(&s->cond_mutex, NULL);
dt_pthread_mutex_init(&s->queue_mutex, NULL);
dt_pthread_mutex_init(&s->run_mutex, NULL);
// start threads
s->num_threads = CLAMP(dt_conf_get_int ("worker_threads"), 1, 8);
s->thread = (pthread_t *)malloc(sizeof(pthread_t)*s->num_threads);
dt_pthread_mutex_lock(&s->run_mutex);
s->running = 1;
dt_pthread_mutex_unlock(&s->run_mutex);
for(int k=0; k<s->num_threads; k++)
pthread_create(&s->thread[k], NULL, dt_control_work, s);
/* create queue kicker thread */
pthread_create(&s->kick_on_workers_thread, NULL, _control_worker_kicker, s);
for(int k=0; k<DT_CTL_WORKER_RESERVED; k++)
{
s->new_res[k] = 0;
pthread_create(&s->thread_res[k], NULL, dt_control_work_res, s);
#if 0
/* check if thread created is the worker thread, then
set scheduling information for the thread to nice level. */
if (k == DT_CTL_WORKER_7)
{
int res;
struct sched_param sched_params;
sched_params.sched_priority = sched_get_priority_min(SCHED_RR);
if((res=pthread_setschedparam(s->thread_res[k], SCHED_RR, &sched_params))!=0)
fprintf(stderr,"Failed to set background thread scheduling to nice level: %d.",res);
}
#endif
}
s->button_down = 0;
s->button_down_which = 0;
// init database schema:
int rc;
sqlite3_stmt *stmt;
rc = sqlite3_prepare_v2(dt_database_get(darktable.db),
"select settings from settings", -1, &stmt, NULL);
if(rc == SQLITE_OK && sqlite3_step(stmt) == SQLITE_ROW)
{
dt_pthread_mutex_lock(&(darktable.control->global_mutex));
darktable.control->global_settings.version = -1;
const void *set = sqlite3_column_blob(stmt, 0);
int len = sqlite3_column_bytes(stmt, 0);
if(sizeof(dt_ctl_settings_t) == len)
memcpy(&(darktable.control->global_settings), set, len);
sqlite3_finalize(stmt);
if(darktable.control->global_settings.version != DT_VERSION)
{
fprintf(stderr,
"[load_config] wrong version %d (should be %d), substituting defaults.\n",
darktable.control->global_settings.version, DT_VERSION);
memcpy(&(darktable.control->global_settings),
&(darktable.control->global_defaults), sizeof(dt_ctl_settings_t));
dt_pthread_mutex_unlock(&(darktable.control->global_mutex));
// drop all, restart. TODO: freeze this version or have update facility!
sqlite3_exec(dt_database_get(darktable.db),
"drop table settings", NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"drop table film_rolls", NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"drop table images", NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"drop table selected_images", NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"drop table mipmaps", NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"drop table mipmap_timestamps", NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"drop table history", NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"drop table tags", NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"drop table tagxtag", NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"drop table tagged_images", NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"drop table styles", NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"drop table style_items", NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"drop table meta_data", NULL, NULL, NULL);
goto create_tables;
}
else
{
// silly check if old table is still present:
sqlite3_exec(dt_database_get(darktable.db),
"delete from color_labels where imgid=0", NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"insert into color_labels values (0, 0)", NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"insert into color_labels values (0, 1)", NULL, NULL, NULL);
DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
"select max(color) from color_labels where imgid=0", -1, &stmt, NULL);
int col = 0;
// still the primary key option set?
if(sqlite3_step(stmt) == SQLITE_ROW)
col = MAX(col, sqlite3_column_int(stmt, 0));
sqlite3_finalize(stmt);
if(col != 1) sqlite3_exec(dt_database_get(darktable.db),
"drop table color_labels", NULL, NULL, NULL);
// insert new tables, if not there (statement will just fail if so):
sqlite3_exec(dt_database_get(darktable.db),
"create table color_labels (imgid integer, color integer)",
NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"drop table mipmaps", NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"drop table mipmap_timestamps", NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"create table styles (name varchar, description varchar)",
NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"create table style_items (styleid integer, num integer, "
"module integer, operation varchar(256), op_params blob, "
"enabled integer)", NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"create table meta_data (id integer, key integer,value varchar)",
NULL, NULL, NULL);
// add columns where needed. will just fail otherwise:
sqlite3_exec(dt_database_get(darktable.db),
"alter table images add column orientation integer",
NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"update images set orientation = -1 where orientation is NULL",
NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"alter table images add column focus_distance real",
NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"update images set focus_distance = -1 where focus_distance is NULL",
NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"alter table images add column histogram blob",
NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"alter table images add column lightmap blob",
NULL, NULL, NULL);
// add column for blendops
sqlite3_exec(dt_database_get(darktable.db),
"alter table history add column blendop_params blob",
NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"alter table history add column blendop_version integer",
NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"update history set blendop_version = 1 where blendop_version is NULL",
NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"alter table style_items add column blendop_params blob",
NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"alter table style_items add column blendop_version integer",
NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"update style_items set blendop_version = 1 where "
"blendop_version is NULL",
NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"alter table presets add column blendop_params blob",
NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"alter table presets add column blendop_version integer",
NULL, NULL, NULL);
sqlite3_exec(dt_database_get(darktable.db),
"update presets set blendop_version = 1 where blendop_version is NULL",
NULL, NULL, NULL);
dt_pthread_mutex_unlock(&(darktable.control->global_mutex));
}
}
else
{
// db not yet there, create it
sqlite3_finalize(stmt);
create_tables:
dt_control_create_database_schema();
DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
"insert into settings (settings) values (?1)", -1, &stmt, NULL);
DT_DEBUG_SQLITE3_BIND_BLOB(stmt, 1, &(darktable.control->global_defaults),
sizeof(dt_ctl_settings_t), SQLITE_STATIC);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
}
dt_control_sanitize_database();
}
void dt_control_key_accelerators_on(struct dt_control_t *s)
{
gtk_window_add_accel_group(GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)),
darktable.control->accelerators);
if(!s->key_accelerators_on)
s->key_accelerators_on = 1;
}
void dt_control_key_accelerators_off(struct dt_control_t *s)
{
gtk_window_remove_accel_group(
GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)),
darktable.control->accelerators);
s->key_accelerators_on = 0;
}
int dt_control_is_key_accelerators_on(struct dt_control_t *s)
{
return s->key_accelerators_on;
}
void dt_control_change_cursor(dt_cursor_t curs)
{
GtkWidget *widget = dt_ui_main_window(darktable.gui->ui);
GdkCursor* cursor = gdk_cursor_new(curs);
gdk_window_set_cursor(widget->window, cursor);
gdk_cursor_destroy(cursor);
}
int dt_control_running()
{
// FIXME: when shutdown, run_mutex is not inited anymore!
dt_control_t *s = darktable.control;
dt_pthread_mutex_lock(&s->run_mutex);
int running = s->running;
dt_pthread_mutex_unlock(&s->run_mutex);
return running;
}
void dt_control_quit()
{
dt_gui_gtk_quit();
// thread safe quit, 1st pass:
dt_pthread_mutex_lock(&darktable.control->cond_mutex);
dt_pthread_mutex_lock(&darktable.control->run_mutex);
darktable.control->running = 0;
dt_pthread_mutex_unlock(&darktable.control->run_mutex);
dt_pthread_mutex_unlock(&darktable.control->cond_mutex);
// let gui pick up the running = 0 state and die
gtk_widget_queue_draw(dt_ui_center(darktable.gui->ui));
if(darktable.lua_state) lua_close(darktable.lua_state);
}
void dt_control_shutdown(dt_control_t *s)
{
dt_pthread_mutex_lock(&s->cond_mutex);
dt_pthread_mutex_lock(&s->run_mutex);
s->running = 0;
dt_pthread_mutex_unlock(&s->run_mutex);
dt_pthread_mutex_unlock(&s->cond_mutex);
pthread_cond_broadcast(&s->cond);
/* cancel background job if any */
dt_control_job_cancel(&s->job_res[DT_CTL_WORKER_7]);
/* first wait for kick_on_workers_thread */
pthread_join(s->kick_on_workers_thread, NULL);
// gdk_threads_leave();
int k;
for(k=0; k<s->num_threads; k++)
// pthread_kill(s->thread[k], 9);
pthread_join(s->thread[k], NULL);
for(k=0; k<DT_CTL_WORKER_RESERVED; k++)
// pthread_kill(s->thread_res[k], 9);
pthread_join(s->thread_res[k], NULL);
// gdk_threads_enter();
}
void dt_control_cleanup(dt_control_t *s)
{
// vacuum TODO: optional?
// DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "PRAGMA incremental_vacuum(0)", NULL, NULL, NULL);
// DT_DEBUG_SQLITE3_EXEC(dt_database_get(darktable.db), "vacuum", NULL, NULL, NULL);
dt_pthread_mutex_destroy(&s->queue_mutex);
dt_pthread_mutex_destroy(&s->cond_mutex);
dt_pthread_mutex_destroy(&s->log_mutex);
dt_pthread_mutex_destroy(&s->run_mutex);
}
void dt_control_job_init(dt_job_t *j, const char *msg, ...)
{
memset(j, 0, sizeof(dt_job_t));
#ifdef DT_CONTROL_JOB_DEBUG
va_list ap;
va_start(ap, msg);
vsnprintf(j->description, DT_CONTROL_DESCRIPTION_LEN, msg, ap);
va_end(ap);
#endif
j->state = DT_JOB_STATE_INITIALIZED;
dt_pthread_mutex_init (&j->state_mutex,NULL);
dt_pthread_mutex_init (&j->wait_mutex,NULL);
}
void dt_control_job_set_state_callback(dt_job_t *j,dt_job_state_change_callback cb,void *user_data)
{
j->state_changed_cb = cb;
j->user_data = user_data;
}
void dt_control_job_print(dt_job_t *j)
{
#ifdef DT_CONTROL_JOB_DEBUG
dt_print(DT_DEBUG_CONTROL, "%s", j->description);
#endif
}
void _control_job_set_state(dt_job_t *j,int state)
{
dt_pthread_mutex_lock (&j->state_mutex);
j->state = state;
/* pass state change to callback */
if (j->state_changed_cb)
j->state_changed_cb (j,state);
dt_pthread_mutex_unlock (&j->state_mutex);
}
int dt_control_job_get_state(dt_job_t *j)
{
dt_pthread_mutex_lock (&j->state_mutex);
int state = j->state;
dt_pthread_mutex_unlock (&j->state_mutex);
return state;
}
void dt_control_job_cancel(dt_job_t *j)
{
_control_job_set_state (j,DT_JOB_STATE_CANCELLED);
}
void dt_control_job_wait(dt_job_t *j)
{
int state = dt_control_job_get_state (j);
/* if job execution not is finished let's wait for signal */
if (state==DT_JOB_STATE_RUNNING || state==DT_JOB_STATE_CANCELLED)
{
dt_pthread_mutex_lock (&j->wait_mutex);
dt_pthread_mutex_unlock (&j->wait_mutex);
}
}
int32_t dt_control_run_job_res(dt_control_t *s, int32_t res)
{
assert(res < DT_CTL_WORKER_RESERVED && res >= 0);
dt_job_t *j = NULL;
dt_pthread_mutex_lock(&s->queue_mutex);
if(s->new_res[res]) j = s->job_res + res;
s->new_res[res] = 0;
dt_pthread_mutex_unlock(&s->queue_mutex);
if(!j) return -1;
/* change state to running */
dt_pthread_mutex_lock (&j->wait_mutex);
if (dt_control_job_get_state (j) == DT_JOB_STATE_QUEUED)
{
dt_print(DT_DEBUG_CONTROL, "[run_job+] %02d %f ", res, dt_get_wtime());
dt_control_job_print(j);
dt_print(DT_DEBUG_CONTROL, "\n");
_control_job_set_state (j,DT_JOB_STATE_RUNNING);
/* execute job */
j->result = j->execute (j);
_control_job_set_state (j,DT_JOB_STATE_FINISHED);
dt_print(DT_DEBUG_CONTROL, "[run_job-] %02d %f ", res, dt_get_wtime());
dt_control_job_print(j);
dt_print(DT_DEBUG_CONTROL, "\n");
}
dt_pthread_mutex_unlock (&j->wait_mutex);
return 0;
}
int32_t dt_control_run_job(dt_control_t *s)
{
dt_job_t *j=NULL,*bj=NULL;
dt_pthread_mutex_lock(&s->queue_mutex);
/* check if queue is empty */
if(g_list_length(s->queue) == 0)
{
dt_pthread_mutex_unlock(&s->queue_mutex);
return -1;
}
/* go thru the queue and find one normal job and a background job
that is up for execution.*/
time_t ts_now = time(NULL);
GList *jobitem=g_list_first(s->queue);
if(jobitem)
do
{
dt_job_t *tj = jobitem->data;
/* check if it's a scheduled job and is waiting to be executed */
if(!bj && (tj->ts_execute > tj->ts_added) && tj->ts_execute <= ts_now)
bj = tj;
else if ((tj->ts_execute < tj->ts_added) && !j)
j = tj;
/* if we got a normal job, and a background job, we are finished */
if(bj && j) break;
} while ((jobitem = g_list_next(jobitem)));
/* remove the found jobs from queue */
if (bj)
s->queue = g_list_remove(s->queue, bj);
if (j)
s->queue = g_list_remove(s->queue, j);
/* unlock the queue */
dt_pthread_mutex_unlock(&s->queue_mutex);
/* push background job on reserved backgruond worker */
if(bj)
{
dt_control_add_job_res(s,bj,DT_CTL_WORKER_7);
g_free (bj);
}
/* dont continue if we dont have have a job to execute */
if(!j)
return -1;
/* change state to running */
dt_pthread_mutex_lock (&j->wait_mutex);
if (dt_control_job_get_state (j) == DT_JOB_STATE_QUEUED)
{
dt_print(DT_DEBUG_CONTROL, "[run_job+] %02d %f ",
DT_CTL_WORKER_RESERVED+dt_control_get_threadid(), dt_get_wtime());
dt_control_job_print(j);
dt_print(DT_DEBUG_CONTROL, "\n");
_control_job_set_state (j,DT_JOB_STATE_RUNNING);
/* execute job */
j->result = j->execute (j);
_control_job_set_state (j,DT_JOB_STATE_FINISHED);
dt_print(DT_DEBUG_CONTROL, "[run_job-] %02d %f ",
DT_CTL_WORKER_RESERVED+dt_control_get_threadid(), dt_get_wtime());
dt_control_job_print(j);
dt_print(DT_DEBUG_CONTROL, "\n");
/* free job */
dt_pthread_mutex_unlock (&j->wait_mutex);
g_free(j);
j = NULL;
}
if(j) dt_pthread_mutex_unlock (&j->wait_mutex);
return 0;
}
int32_t dt_control_add_job_res(dt_control_t *s, dt_job_t *job, int32_t res)
{
// TODO: pthread cancel and restart in tough cases?
dt_pthread_mutex_lock(&s->queue_mutex);
dt_print(DT_DEBUG_CONTROL, "[add_job_res] %d ", res);
dt_control_job_print(job);
dt_print(DT_DEBUG_CONTROL, "\n");
_control_job_set_state (job,DT_JOB_STATE_QUEUED);
s->job_res[res] = *job;
s->new_res[res] = 1;
dt_pthread_mutex_unlock(&s->queue_mutex);
dt_pthread_mutex_lock(&s->cond_mutex);
pthread_cond_broadcast(&s->cond);
dt_pthread_mutex_unlock(&s->cond_mutex);
return 0;
}
/* Background jobs will be timestamped and added to queue
the queue will then check ts and detect if its background job
and place it on the job_res if its available...
*/
int32_t dt_control_add_background_job(dt_control_t *s, dt_job_t *job, time_t delay)
{
/* setup timestamps */
job->ts_added = time(NULL);
job->ts_execute = job->ts_added+delay;
/* pass the job further to scheduled jobs worker */
return dt_control_add_job(s,job);
}
int32_t dt_control_add_job(dt_control_t *s, dt_job_t *job)
{
/* set ts_added if unset */
if (job->ts_added == 0)
job->ts_added = time(NULL);
dt_pthread_mutex_lock(&s->queue_mutex);
/* check if equivalent job exist in queue, and discard job
if duplicate found .*/
GList *jobitem = g_list_first(s->queue);
if(jobitem)
do {
if(!memcmp(job, jobitem->data, sizeof(dt_job_t)))
{
dt_print(DT_DEBUG_CONTROL, "[add_job] found job already in queue\n");
_control_job_set_state (job,DT_JOB_STATE_DISCARDED);
dt_pthread_mutex_unlock(&s->queue_mutex);
return -1;
}
} while((jobitem=g_list_next(jobitem)));
dt_print(DT_DEBUG_CONTROL, "[add_job] %d ", g_list_length(s->queue));
dt_control_job_print(job);
dt_print(DT_DEBUG_CONTROL, "\n");
/* add job to queue if not full, otherwise discard the job */
if( g_list_length(s->queue) < DT_CONTROL_MAX_JOBS)
{
/* allocate storage for the job, and set job state */
dt_job_t *thejob = g_malloc(sizeof(dt_job_t));
memcpy(thejob,job,sizeof(dt_job_t));
_control_job_set_state (thejob,DT_JOB_STATE_QUEUED);
s->queue = g_list_append(s->queue, thejob);
dt_pthread_mutex_unlock(&s->queue_mutex);
}
else
{
dt_print(DT_DEBUG_CONTROL, "[add_job] too many jobs in queue!\n");
_control_job_set_state (job,DT_JOB_STATE_DISCARDED);
dt_pthread_mutex_unlock(&s->queue_mutex);
return -1;
}
// notify workers
dt_pthread_mutex_lock(&s->cond_mutex);
pthread_cond_broadcast(&s->cond);
dt_pthread_mutex_unlock(&s->cond_mutex);
return 0;
}
int32_t dt_control_revive_job(dt_control_t *s, dt_job_t *job)
{
int32_t found_j = -1;
dt_pthread_mutex_lock(&s->queue_mutex);
dt_print(DT_DEBUG_CONTROL, "[revive_job] ");
dt_control_job_print(job);
dt_print(DT_DEBUG_CONTROL, "\n");
/* find equivalent job and move it to top of the stack */
GList *jobitem = g_list_first(s->queue);
if (jobitem)
do {
if(!memcmp(job, jobitem->data, sizeof(dt_job_t)))
{
s->queue = g_list_remove_link(s->queue, jobitem);
s->queue = g_list_insert(s->queue, jobitem->data, 0);
g_free(jobitem);
found_j = 1;
break;
}
} while((jobitem=g_list_next(jobitem)));
/* unlock the queue */
dt_pthread_mutex_unlock(&s->queue_mutex);
/* notify workers */
dt_pthread_mutex_lock(&s->cond_mutex);
pthread_cond_broadcast(&s->cond);
dt_pthread_mutex_unlock(&s->cond_mutex);
return found_j;
}