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