Ansel
0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
ansel-cmstest/main.c
Go to the documentation of this file.
1
/*
2
This file is part of darktable,
3
Copyright (C) 2013-2014, 2016 Tobias Ellinghaus.
4
Copyright (C) 2014 Pascal de Bruijn.
5
Copyright (C) 2014, 2016-2017 Roman Lebedev.
6
Copyright (C) 2016 Kai-Uwe Behrmann.
7
Copyright (C) 2017 Matthias Andree.
8
Copyright (C) 2020-2021 Pascal Obry.
9
Copyright (C) 2021 Ralf Brown.
10
Copyright (C) 2022 Aurélien PIERRE.
11
Copyright (C) 2022 Martin Bařinka.
12
13
darktable is free software: you can redistribute it and/or modify
14
it under the terms of the GNU General Public License as published by
15
the Free Software Foundation, either version 3 of the License, or
16
(at your option) any later version.
17
18
darktable is distributed in the hope that it will be useful,
19
but WITHOUT ANY WARRANTY; without even the implied warranty of
20
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
GNU General Public License for more details.
22
23
You should have received a copy of the GNU General Public License
24
along with darktable. If not, see <http://www.gnu.org/licenses/>.
25
*/
26
27
/*
28
You can compile this tool standalone.
29
Dependencies: libX11, libXrandr, liblcms2, libglib and optionally libcolord
30
Compile with something like this:
31
gcc -W -Wall -std=c99 `pkg-config --cflags --libs glib-2.0 lcms2 colord x11 xrandr` \
32
-DHAVE_X11 -DHAVE_COLORD -Ddarktable_package_version=\"'standalone'\" main.c -o ansel-cmstest
33
*/
34
35
#ifdef HAVE_CONFIG_H
36
#include "config.h"
37
#endif
38
39
#include "
system/macros.h
"
40
#include "
system/mem_alloc.h
"
41
#include <glib-object.h>
42
#include <glib.h>
43
#include <lcms2.h>
44
#include <stdio.h>
45
#include <stdlib.h>
46
#include <string.h>
47
48
#ifdef HAVE_X11
49
#include <X11/Xatom.h>
50
#include <X11/Xlib.h>
51
#include <X11/extensions/Xrandr.h>
52
#ifdef HAVE_COLORD
53
#include <colord.h>
54
#endif
// HAVE_COLORD
55
#endif
// HAVE_X11
56
57
#ifdef HAVE_X11
58
typedef
struct
monitor_t
59
{
60
int
screen;
61
int
crtc
;
62
Window
root;
63
int
atom_id
;
64
char
*
name
;
65
66
gboolean
is_primary
;
67
68
// X atom
69
char
*
x_atom_name
;
70
size_t
x_atom_length
;
71
unsigned
char
*
x_atom_data
;
72
73
#ifdef HAVE_COLORD
74
// colord
75
char
*
colord_filename
;
76
#endif
77
}
monitor_t
;
78
79
char
*
get_profile_description
(
unsigned
char
*data,
long
data_size)
80
{
81
cmsUInt32Number
size
;
82
gchar *buf =
NULL
;
83
wchar_t
*
wbuf
=
NULL
;
84
gchar *
utf8
=
NULL
;
85
char
*result =
NULL
;
86
87
if
(
IS_NULL_PTR
(data) || data_size == 0)
return
NULL
;
88
89
cmsHPROFILE
p
=
cmsOpenProfileFromMem
(data, data_size);
90
if
(
IS_NULL_PTR
(
p
))
return
NULL
;
91
92
size
=
cmsGetProfileInfoASCII
(
p
,
cmsInfoDescription
,
"en"
,
"US"
,
NULL
, 0);
93
if
(
size
== 0)
goto
error
;
94
95
buf = (
char
*)
calloc
(
size
+ 1,
sizeof
(
char
));
96
size
=
cmsGetProfileInfoASCII
(
p
,
cmsInfoDescription
,
"en"
,
"US"
, buf,
size
);
97
if
(
size
== 0)
goto
error
;
98
99
// most unix like systems should work with this, but at least Windows doesn't
100
if
(
sizeof
(
wchar_t
) != 4 ||
g_utf8_validate
(buf, -1,
NULL
))
101
result =
g_strdup
(buf);
// better a little weird than totally borked
102
else
103
{
104
wbuf
= (
wchar_t
*)
calloc
(
size
+ 1,
sizeof
(
wchar_t
));
105
size
=
cmsGetProfileInfo
(
p
,
cmsInfoDescription
,
"en"
,
"US"
,
wbuf
,
sizeof
(
wchar_t
) *
size
);
106
if
(
size
== 0)
goto
error
;
107
utf8
=
g_ucs4_to_utf8
((
gunichar
*)
wbuf
, -1,
NULL
,
NULL
,
NULL
);
108
if
(
IS_NULL_PTR
(
utf8
))
goto
error
;
109
result =
g_strdup
(
utf8
);
110
}
111
112
dt_free
(buf);
113
dt_free
(
wbuf
);
114
dt_free
(
utf8
);
115
cmsCloseProfile
(
p
);
116
return
result;
117
118
error
:
119
if
(buf) result =
g_strdup
(buf);
// better a little weird than totally borked
120
dt_free
(buf);
121
dt_free
(
wbuf
);
122
dt_free
(
utf8
);
123
cmsCloseProfile
(
p
);
124
return
result;
125
}
126
127
// sort them according to screen. then for each screen we want the screen's primary first, followed by the rest
128
// in the original order
129
static
gint
sort_monitor_list
(
gconstpointer
a,
gconstpointer
b)
130
{
131
monitor_t
*
monitor_a
= (
monitor_t
*)a;
132
monitor_t
*
monitor_b
= (
monitor_t
*)b;
133
134
if
(
monitor_a
->screen !=
monitor_b
->screen)
135
return
monitor_a
->screen -
monitor_b
->screen;
136
137
if
(
monitor_a
->is_primary)
return
-1;
138
if
(
monitor_b
->is_primary)
return
1;
139
140
return
monitor_a
->atom_id -
monitor_b
->atom_id;
141
}
142
#endif
// HAVE_X11
143
144
int
main
(
int
argc
__attribute__
((
unused
)),
char
*
arg
[]
__attribute__
((
unused
)))
145
{
146
printf
(
"ansel-cmstest version %s\n"
,
darktable_package_version
);
147
#ifndef HAVE_X11
148
printf
(
"this executable doesn't do anything for non-X11 systems currently\n"
);
149
return
EXIT_FAILURE
;
150
#else
// HAVE_X11
151
152
#ifdef HAVE_COLORD
153
printf
(
"this executable was built with colord support enabled\n"
);
154
#else
// HAVE_COLORD
155
printf
(
"this executable was built without colord support\n"
);
156
#endif
// HAVE_COLORD
157
158
#ifdef USE_COLORDGTK
159
printf
(
"ansel itself was built with colord support enabled\n"
);
160
#else
161
printf
(
"ansel itself was built without colord support\n"
);
162
#endif
// USE_COLORDGTK
163
164
printf
(
"\n"
);
165
166
// get a list of all possible screens from xrandr
167
GList
*
monitor_list
=
NULL
;
168
const
char
*
disp_name_env
;
169
char
disp_name
[100];
170
171
// find the base display name
172
if
((
disp_name_env
=
g_getenv
(
"DISPLAY"
)) !=
NULL
)
173
{
174
char
*
pp
;
175
g_strlcpy
(
disp_name
,
disp_name_env
,
sizeof
(
disp_name
));
176
if
((
pp
=
g_strrstr
(
disp_name
,
":"
)) !=
NULL
)
177
{
178
if
((
pp
=
g_strstr_len
(
pp
, -1,
"."
)) ==
NULL
)
179
g_strlcat
(
disp_name
,
".0"
,
sizeof
(
disp_name
));
180
else
{
181
if
(
pp
[1] ==
'\0'
)
182
g_strlcat
(
disp_name
,
"0"
,
sizeof
(
disp_name
));
183
else
184
{
185
pp
[1] =
'0'
;
186
pp
[2] =
'\0'
;
187
}
188
}
189
}
190
}
191
else
192
g_strlcpy
(
disp_name
,
":0.0"
,
sizeof
(
disp_name
));
193
194
Display
*display =
XOpenDisplay
(
disp_name
);
195
if
(
IS_NULL_PTR
(display))
196
{
197
fprintf
(
stderr
,
"can't open display `%s'\n"
,
XDisplayName
(
disp_name
));
198
return
EXIT_FAILURE
;
199
}
200
201
int
max_screen
=
ScreenCount
(display);
202
for
(
int
screen = 0; screen <
max_screen
; ++screen)
203
{
204
int
atom_id
= 0;
// the id of the x atom. might be changed when sorting in the primary later!
205
206
Window
root =
RootWindow
(display, screen);
207
XRRScreenResources
*
rsrc
=
XRRGetScreenResources
(display, root);
208
209
// see if there is a primary screen.
210
XID
primary
=
XRRGetOutputPrimary
(display, root);
211
gboolean
have_primary
=
FALSE
;
212
int
primary_id
= -1;
213
if
(
rsrc
)
214
{
215
for
(
int
crtc
= 0;
crtc
<
rsrc
->ncrtc;
crtc
++)
216
{
217
XRRCrtcInfo
*
crtc_info
=
XRRGetCrtcInfo
(display,
rsrc
,
rsrc
->crtcs[
crtc
]);
218
if
(
IS_NULL_PTR
(
crtc_info
))
219
continue
;
220
221
if
(
crtc_info
->mode !=
None
&&
crtc_info
->noutput > 0)
222
{
223
for
(
int
output = 0; output <
crtc_info
->noutput; output++)
224
{
225
if
(
crtc_info
->outputs[output] ==
primary
)
226
{
227
primary_id
=
crtc
;
228
break
;
229
}
230
}
231
}
232
233
XRRFreeCrtcInfo
(
crtc_info
);
234
}
235
}
236
if
(
primary_id
== -1)
237
printf
(
"couldn't locate primary CRTC!\n"
);
238
else
239
{
240
printf
(
"primary CRTC is at CRTC %d\n"
,
primary_id
);
241
have_primary
=
TRUE
;
242
}
243
244
// now iterate over the CRTCs again and add the relevant ones to the list
245
if
(
rsrc
)
246
{
247
for
(
int
crtc
= 0;
crtc
<
rsrc
->ncrtc; ++
crtc
)
248
{
249
XRROutputInfo
*
output_info
=
NULL
;
250
XRRCrtcInfo
*
crtc_info
=
XRRGetCrtcInfo
(display,
rsrc
,
rsrc
->crtcs[
crtc
]);
251
if
(
IS_NULL_PTR
(
crtc_info
))
252
{
253
printf
(
"can't get CRTC info for screen %d CRTC %d\n"
, screen,
crtc
);
254
goto
end;
255
}
256
// only handle those that are attached though
257
if
(
crtc_info
->mode ==
None
||
crtc_info
->noutput <= 0)
258
{
259
printf
(
"CRTC for screen %d CRTC %d has no mode or no output, skipping\n"
, screen,
crtc
);
260
goto
end;
261
}
262
263
// Choose the primary output of the CRTC if we have one, else default to the first. i.e. we punt with
264
// mirrored displays.
265
gboolean
is_primary
=
FALSE
;
266
int
output = 0;
267
if
(
have_primary
)
268
{
269
for
(
int
j
= 0;
j
<
crtc_info
->noutput;
j
++)
270
{
271
if
(
crtc_info
->outputs[
j
] ==
primary
)
272
{
273
output =
j
;
274
is_primary
=
TRUE
;
275
break
;
276
}
277
}
278
}
279
280
output_info
=
XRRGetOutputInfo
(display,
rsrc
,
crtc_info
->outputs[output]);
281
if
(
IS_NULL_PTR
(
output_info
))
282
{
283
printf
(
"can't get output info for screen %d CRTC %d output %d\n"
, screen,
crtc
, output);
284
goto
end;
285
}
286
287
if
(
output_info
->connection ==
RR_Disconnected
)
288
{
289
printf
(
"screen %d CRTC %d output %d is disconnected, skipping\n"
, screen,
crtc
, output);
290
goto
end;
291
}
292
293
monitor_t
*
monitor
= (
monitor_t
*)
calloc
(1,
sizeof
(
monitor_t
));
294
#if 0
295
// in case we also want the edid data
296
Atom
edid_atom
=
XInternAtom
(display,
"EDID"
,
False
),
actual_type
;
297
int
actual_format
;
298
unsigned
long
nitems
,
bytes_after
;
299
unsigned
char
*
prop
;
300
int
res
=
XRRGetOutputProperty
(display,
rsrc
->outputs[output],
edid_atom
, 0,
G_MAXLONG
,
FALSE
,
301
FALSE
,
XA_INTEGER
, &
actual_type
, &
actual_format
, &
nitems
, &
bytes_after
, &
prop
);
302
if
(
res
==
Success
&&
actual_type
==
XA_INTEGER
&&
actual_format
== 8 &&
nitems
!= 0)
303
{
304
printf
(
"EDID for %s has size %lu\n"
,
output_info
->name,
nitems
);
305
// TODO: parse the edid blob in prop. since that is really ugly code I left it out for now.
306
}
307
if
(
prop
)
308
XFree
(
prop
);
309
#endif
310
311
monitor
->root = root;
312
monitor
->screen = screen;
313
monitor
->crtc =
crtc
;
314
monitor
->is_primary =
is_primary
;
315
monitor
->atom_id =
atom_id
++;
316
monitor
->name =
g_strdup
(
output_info
->name);
317
monitor_list
=
g_list_prepend
(
monitor_list
,
monitor
);
318
319
end:
320
XRRFreeCrtcInfo
(
crtc_info
);
321
XRRFreeOutputInfo
(
output_info
);
322
}
323
}
324
XRRFreeScreenResources
(
rsrc
);
325
}
326
327
// sort the list of monitors so that the primary one is first. also updates the atom_id.
328
monitor_list
=
g_list_sort
(
monitor_list
,
sort_monitor_list
);
329
int
atom_id
= 0;
330
int
last_screen
= -1;
331
for
(
GList
*
iter
=
monitor_list
;
iter
;
iter
=
g_list_next
(
iter
))
332
{
333
monitor_t
*
monitor
= (
monitor_t
*)
iter
->data;
334
if
(
monitor
->screen !=
last_screen
)
atom_id
= 0;
335
last_screen
=
monitor
->screen;
336
monitor
->atom_id =
atom_id
++;
337
}
338
339
// get the profile from the X atom
340
for
(
GList
*
iter
=
monitor_list
;
iter
;
iter
=
g_list_next
(
iter
))
341
{
342
monitor_t
*
monitor
= (
monitor_t
*)
iter
->data;
343
if
(
monitor
->atom_id == 0)
344
monitor
->x_atom_name =
g_strdup
(
"_ICC_PROFILE"
);
345
else
346
monitor
->x_atom_name =
g_strdup_printf
(
"_ICC_PROFILE_%d"
,
monitor
->atom_id);
347
348
Atom
atom
=
XInternAtom
(display,
monitor
->x_atom_name,
FALSE
),
actual_type
;
349
int
actual_format
;
350
unsigned
long
nitems
,
bytes_after
;
351
unsigned
char
*
prop
;
352
353
int
res
=
XGetWindowProperty
(display,
monitor
->root,
atom
, 0,
G_MAXLONG
,
FALSE
,
XA_CARDINAL
, &
actual_type
,
354
&
actual_format
, &
nitems
, &
bytes_after
, &
prop
);
355
356
if
(
res
==
Success
&&
actual_type
==
XA_CARDINAL
&&
actual_format
== 8)
357
{
358
monitor
->x_atom_length =
nitems
;
359
monitor
->x_atom_data =
prop
;
360
}
361
else
362
XFree
(
prop
);
363
}
364
365
366
#ifdef HAVE_COLORD
367
// and also the profile from colord
368
CdClient
*
client
=
cd_client_new
();
369
if
(
IS_NULL_PTR
(
client
) || !
cd_client_connect_sync
(
client
,
NULL
,
NULL
))
370
{
371
fprintf
(
stderr
,
"error connecting to colord\n"
);
372
}
373
else
374
for
(
GList
*
iter
=
monitor_list
;
iter
;
iter
=
g_list_next
(
iter
))
375
{
376
monitor_t
*
monitor
= (
monitor_t
*)
iter
->data;
377
378
CdDevice
*
device
=
cd_client_find_device_by_property_sync
(
client
,
CD_DEVICE_METADATA_XRANDR_NAME
,
379
monitor
->name,
NULL
,
NULL
);
380
if
(
device
&&
cd_device_connect_sync
(
device
,
NULL
,
NULL
))
381
{
382
CdProfile
*profile =
cd_device_get_default_profile
(
device
);
383
if
(profile)
384
{
385
if
(
cd_profile_connect_sync
(profile,
NULL
,
NULL
))
386
{
387
CdIcc
*icc =
cd_profile_load_icc
(profile,
CD_ICC_LOAD_FLAGS_FALLBACK_MD5
,
NULL
,
NULL
);
388
if
(icc)
389
{
390
monitor
->colord_filename =
g_strdup
(
cd_icc_get_filename
(icc));
391
g_object_unref
(icc);
392
}
393
}
394
g_object_unref
(profile);
395
}
396
}
397
if
(
device
)
g_object_unref
(
device
);
398
}
399
if
(
client
)
g_object_unref
(
client
);
400
#endif
// HAVE_COLORD
401
402
403
// check if they are the same and print out some metadata like name, filename, filesize, ...
404
gboolean
any_profile_mismatch
=
FALSE
,
any_unprofiled_monitor
=
FALSE
;
405
for
(
GList
*
iter
=
monitor_list
;
iter
;
iter
=
g_list_next
(
iter
))
406
{
407
monitor_t
*
monitor
= (
monitor_t
*)
iter
->data;
408
char
*message =
NULL
;
409
410
char
*
monitor_name
=
monitor
->name ?
monitor
->name :
"(unknown)"
;
411
char
*
x_atom_name
=
monitor
->x_atom_name ?
monitor
->x_atom_name :
"(not found)"
;
412
char
*
tmp
=
get_profile_description
(
monitor
->x_atom_data,
monitor
->x_atom_length);
413
char
*
x_atom_description
=
tmp
?
tmp
:
g_strdup
(
"(none)"
);
414
415
#ifndef HAVE_COLORD
416
if
(
monitor
->x_atom_length == 0)
417
{
418
message =
"the X atom seems to be missing"
;
419
any_unprofiled_monitor
=
TRUE
;
420
}
421
#else
// HAVE_COLORD
422
char
*
colord_filename
=
monitor
->colord_filename ?
monitor
->colord_filename :
"(none)"
,
423
*
colord_description
;
424
if
(
IS_NULL_PTR
(
monitor
->colord_filename)
425
||
g_file_test
(
monitor
->colord_filename,
G_FILE_TEST_IS_REGULAR
) ==
FALSE
)
426
{
427
colord_description
=
g_strdup
(
"(file not found)"
);
428
if
(
monitor
->x_atom_length > 0)
429
{
430
any_profile_mismatch
=
TRUE
;
431
message =
"the X atom and colord returned different profiles"
;
432
}
433
else
434
{
435
any_unprofiled_monitor
=
TRUE
;
436
message =
"the X atom and colord returned the same profile"
;
437
}
438
}
439
else
440
{
441
unsigned
char
*
tmp_data
=
NULL
;
442
size_t
size
= 0;
443
g_file_get_contents
(
monitor
->colord_filename, (gchar **)&
tmp_data
, &
size
,
NULL
);
444
gboolean
profiles_equal
= (
size
==
monitor
->x_atom_length
445
&& (
size
== 0 ||
memcmp
(
monitor
->x_atom_data,
tmp_data
,
size
) == 0));
446
if
(!
profiles_equal
)
any_profile_mismatch
=
TRUE
;
447
if
(
size
== 0 &&
monitor
->x_atom_length == 0)
any_unprofiled_monitor
=
TRUE
;
448
message =
profiles_equal
?
"the X atom and colord returned the same profile"
449
:
"the X atom and colord returned different profiles"
;
450
tmp
=
get_profile_description
(
tmp_data
,
size
);
451
colord_description
=
tmp
?
tmp
:
g_strdup
(
"(none)"
);
452
dt_free
(
tmp_data
);
453
}
454
#endif
// HAVE_COLORD
455
456
457
// print it
458
printf
(
"\n%s"
,
monitor_name
);
459
if
(message)
printf
(
"\t%s"
, message);
460
printf
(
"\n\tX atom:\t%s (%"
G_GSIZE_FORMAT
" bytes)\n\t\tdescription: %s\n"
,
x_atom_name
,
monitor
->x_atom_length,
461
x_atom_description
);
462
#ifdef HAVE_COLORD
463
printf
(
"\tcolord:\t\"%s\"\n\t\tdescription: %s\n"
,
colord_filename
,
colord_description
);
464
#endif
465
466
dt_free
(
x_atom_description
);
467
#ifdef HAVE_COLORD
468
dt_free
(
colord_description
);
469
#endif
470
471
}
472
473
474
// conclusion
475
if
(
any_profile_mismatch
||
any_unprofiled_monitor
)
476
{
477
printf
(
"\nBetter check your system setup\n"
);
478
if
(
any_profile_mismatch
)
printf
(
" - some monitors reported different profiles\n"
);
479
if
(
any_unprofiled_monitor
)
printf
(
" - some monitors lacked a profile\n"
);
480
printf
(
"You may experience inconsistent color rendition between color managed applications\n"
);
481
}
482
else
483
printf
(
"\nYour system seems to be correctly configured\n"
);
484
485
486
// cleanup
487
XCloseDisplay
(display);
488
for
(
GList
*
iter
=
monitor_list
;
iter
;
iter
=
g_list_next
(
iter
))
489
{
490
monitor_t
*
monitor
= (
monitor_t
*)
iter
->data;
491
dt_free
(
monitor
->name);
492
dt_free
(
monitor
->x_atom_name);
493
XFree
(
monitor
->x_atom_data);
494
#ifdef HAVE_COLORD
495
dt_free
(
monitor
->colord_filename);
496
#endif
497
}
498
g_list_free_full
(
monitor_list
,
dt_free_gpointer
);
499
monitor_list
=
NULL
;
500
501
return
EXIT_SUCCESS
;
502
503
#endif
// HAVE_X11
504
}
505
506
507
// clang-format off
508
// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
509
// vim: shiftwidth=2 expandtab tabstop=2 cindent
510
// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
511
// clang-format on
error
static void error(char *msg)
Definition
ashift_lsd.c:202
TRUE
#define TRUE
Definition
ashift_lsd.c:162
FALSE
#define FALSE
Definition
ashift_lsd.c:158
L
const float L
Definition
colorspaces_inline_conversions.h:494
p
const float p
Definition
colorspaces_inline_conversions.h:680
darktable_package_version
const char darktable_package_version[]
macros.h
IS_NULL_PTR
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition
macros.h:65
mem_alloc.h
dt_free_gpointer
static void dt_free_gpointer(gpointer ptr)
Definition
mem_alloc.h:104
dt_free
#define dt_free(ptr)
Definition
mem_alloc.h:97
size
size_t size
Definition
mipmap_cache.c:3
name
const char * name
Definition
pdf.h:90
main
int main()
Definition
prova.c:47
__attribute__
float dt_aligned_pixel_simd_t __attribute__((vector_size(16), aligned(16)))
Apply one channel's tone curve to each of the three colour channels, or pass the channel through unto...
Definition
simd.h:55
src
apps
ansel-cmstest
main.c
Generated by
1.9.8