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
58typedef 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
79char *get_profile_description(unsigned char *data, long data_size)
80{
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
93 if(size == 0) goto error;
94
95 buf = (char *)calloc(size + 1, sizeof(char));
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;
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);
116 return result;
117
118error:
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);
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
130{
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
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
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;
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);
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]);
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 {
228 break;
229 }
230 }
231 }
232
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);
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 {
250 XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(display, rsrc, rsrc->crtcs[crtc]);
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;
275 break;
276 }
277 }
278 }
279
280 output_info = XRRGetOutputInfo(display, rsrc, crtc_info->outputs[output]);
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,
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);
318
319end:
322 }
323 }
325 }
326
327 // sort the list of monitors so that the primary one is first. also updates the atom_id.
329 int atom_id = 0;
330 int last_screen = -1;
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
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
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
370 {
371 fprintf(stderr, "error connecting to colord\n");
372 }
373 else
375 {
376 monitor_t *monitor = (monitor_t *)iter->data;
377
379 monitor->name, NULL, NULL);
381 {
383 if(profile)
384 {
385 if(cd_profile_connect_sync(profile, NULL, NULL))
386 {
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 }
398 }
400#endif // HAVE_COLORD
401
402
403 // check if they are the same and print out some metadata like name, filename, filesize, ...
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";
420 }
421#else // HAVE_COLORD
422 char *colord_filename = monitor->colord_filename ? monitor->colord_filename : "(none)",
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 {
431 message = "the X atom and colord returned different profiles";
432 }
433 else
434 {
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));
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";
451 colord_description = tmp ? tmp : g_strdup("(none)");
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,
462#ifdef HAVE_COLORD
463 printf("\tcolord:\t\"%s\"\n\t\tdescription: %s\n", colord_filename, colord_description);
464#endif
465
467#ifdef HAVE_COLORD
469#endif
470
471 }
472
473
474 // conclusion
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);
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 }
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
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
const char darktable_package_version[]
#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
static void dt_free_gpointer(gpointer ptr)
Definition mem_alloc.h:104
#define dt_free(ptr)
Definition mem_alloc.h:97
size_t size
Definition mipmap_cache.c:3
const char * name
Definition pdf.h:90
int main()
Definition prova.c:47
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