Line |
Branch |
Exec |
Source |
1 |
|
|
/* |
2 |
|
|
* Copyright (c) 2009 Tias Guns |
3 |
|
|
* Copyright (c) 2009 Soren Hauberg |
4 |
|
|
* |
5 |
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
6 |
|
|
* of this software and associated documentation files (the "Software"), to deal |
7 |
|
|
* in the Software without restriction, including without limitation the rights |
8 |
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
9 |
|
|
* copies of the Software, and to permit persons to whom the Software is |
10 |
|
|
* furnished to do so, subject to the following conditions: |
11 |
|
|
* |
12 |
|
|
* The above copyright notice and this permission notice shall be included in |
13 |
|
|
* all copies or substantial portions of the Software. |
14 |
|
|
* |
15 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 |
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 |
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
18 |
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
19 |
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
20 |
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
21 |
|
|
* THE SOFTWARE. |
22 |
|
|
*/ |
23 |
|
|
|
24 |
|
|
#include "config.h" |
25 |
|
|
|
26 |
|
|
#include <stdlib.h> |
27 |
|
|
#include <ctype.h> |
28 |
|
|
#include <string.h> |
29 |
|
|
#include <dirent.h> |
30 |
|
|
#include <glib/gi18n.h> |
31 |
|
|
|
32 |
|
|
#include <X11/extensions/XInput.h> |
33 |
|
|
|
34 |
|
|
#include "calibrator-gui.h" |
35 |
|
|
#include "calibrator.h" |
36 |
|
|
|
37 |
|
|
static GMainLoop *mainloop = NULL; |
38 |
|
|
|
39 |
|
|
/** |
40 |
|
|
* find a calibratable touchscreen device (using XInput) |
41 |
|
|
* |
42 |
|
|
* if pre_device is NULL, the last calibratable device is selected. |
43 |
|
|
* retuns number of devices found, |
44 |
|
|
* the data of the device is returned in the last 3 function parameters |
45 |
|
|
*/ |
46 |
|
✗ |
static int find_device(const char* pre_device, gboolean verbose, gboolean list_devices, |
47 |
|
|
XID* device_id, const char** device_name, XYinfo* device_axis) |
48 |
|
|
{ |
49 |
|
✗ |
gboolean pre_device_is_id = TRUE; |
50 |
|
✗ |
int found = 0; |
51 |
|
|
|
52 |
|
✗ |
Display* display = XOpenDisplay(NULL); |
53 |
|
✗ |
if (display == NULL) { |
54 |
|
✗ |
fprintf(stderr, "Unable to connect to X server\n"); |
55 |
|
✗ |
exit(1); |
56 |
|
|
} |
57 |
|
|
|
58 |
|
|
int xi_opcode, event, error; |
59 |
|
✗ |
if (!XQueryExtension(display, "XInputExtension", &xi_opcode, &event, &error)) { |
60 |
|
✗ |
fprintf(stderr, "X Input extension not available.\n"); |
61 |
|
✗ |
exit(1); |
62 |
|
|
} |
63 |
|
|
|
64 |
|
|
/* verbose, get Xi version */ |
65 |
|
✗ |
if (verbose) { |
66 |
|
✗ |
XExtensionVersion *version = XGetExtensionVersion(display, INAME); |
67 |
|
|
|
68 |
|
✗ |
if (version && (version != (XExtensionVersion*) NoSuchExtension)) { |
69 |
|
✗ |
printf("DEBUG: %s version is %i.%i\n", |
70 |
|
✗ |
INAME, version->major_version, version->minor_version); |
71 |
|
✗ |
XFree(version); |
72 |
|
|
} |
73 |
|
|
} |
74 |
|
|
|
75 |
|
✗ |
if (pre_device != NULL) { |
76 |
|
|
/* check whether the pre_device is an ID (only digits) */ |
77 |
|
✗ |
int len = strlen(pre_device); |
78 |
|
|
int loop; |
79 |
|
✗ |
for (loop=0; loop<len; loop++) { |
80 |
|
✗ |
if (!isdigit(pre_device[loop])) { |
81 |
|
✗ |
pre_device_is_id = FALSE; |
82 |
|
✗ |
break; |
83 |
|
|
} |
84 |
|
|
} |
85 |
|
|
} |
86 |
|
|
|
87 |
|
|
|
88 |
|
✗ |
if (verbose) |
89 |
|
✗ |
printf("DEBUG: Skipping virtual master devices and devices without axis valuators.\n"); |
90 |
|
|
int ndevices; |
91 |
|
|
XDeviceInfoPtr list, slist; |
92 |
|
✗ |
slist=list=(XDeviceInfoPtr) XListInputDevices (display, &ndevices); |
93 |
|
|
int i; |
94 |
|
✗ |
for (i=0; i<ndevices; i++, list++) |
95 |
|
|
{ |
96 |
|
✗ |
if (list->use == IsXKeyboard || list->use == IsXPointer) /* virtual master device */ |
97 |
|
✗ |
continue; |
98 |
|
|
|
99 |
|
|
/* if we are looking for a specific device */ |
100 |
|
✗ |
if (pre_device != NULL) { |
101 |
|
✗ |
if ((pre_device_is_id && list->id == (XID) atoi(pre_device)) || |
102 |
|
✗ |
(!pre_device_is_id && strcmp(list->name, pre_device) == 0)) { |
103 |
|
|
/* OK, fall through */ |
104 |
|
|
} else { |
105 |
|
|
/* skip, not this device */ |
106 |
|
✗ |
continue; |
107 |
|
|
} |
108 |
|
|
} |
109 |
|
|
|
110 |
|
✗ |
XAnyClassPtr any = (XAnyClassPtr) (list->inputclassinfo); |
111 |
|
|
int j; |
112 |
|
✗ |
for (j=0; j<list->num_classes; j++) |
113 |
|
|
{ |
114 |
|
|
|
115 |
|
✗ |
if (any->class == ValuatorClass) |
116 |
|
|
{ |
117 |
|
✗ |
XValuatorInfoPtr V = (XValuatorInfoPtr) any; |
118 |
|
✗ |
XAxisInfoPtr ax = (XAxisInfoPtr) V->axes; |
119 |
|
|
|
120 |
|
✗ |
if (V->mode != Absolute) { |
121 |
|
✗ |
if (verbose) |
122 |
|
✗ |
printf("DEBUG: Skipping device '%s' id=%i, does not report Absolute events.\n", |
123 |
|
✗ |
list->name, (int)list->id); |
124 |
|
✗ |
} else if (V->num_axes < 2 || |
125 |
|
✗ |
(ax[0].min_value == -1 && ax[0].max_value == -1) || |
126 |
|
✗ |
(ax[1].min_value == -1 && ax[1].max_value == -1)) { |
127 |
|
✗ |
if (verbose) |
128 |
|
✗ |
printf("DEBUG: Skipping device '%s' id=%i, does not have two calibratable axes.\n", |
129 |
|
✗ |
list->name, (int)list->id); |
130 |
|
|
} else { |
131 |
|
|
/* a calibratable device (has 2 axis valuators) */ |
132 |
|
✗ |
found++; |
133 |
|
✗ |
*device_id = list->id; |
134 |
|
✗ |
*device_name = g_strdup(list->name); |
135 |
|
✗ |
device_axis->x_min = ax[0].min_value; |
136 |
|
✗ |
device_axis->x_max = ax[0].max_value; |
137 |
|
✗ |
device_axis->y_min = ax[1].min_value; |
138 |
|
✗ |
device_axis->y_max = ax[1].max_value; |
139 |
|
|
|
140 |
|
✗ |
if (list_devices) |
141 |
|
✗ |
printf("Device \"%s\" id=%i\n", *device_name, (int)*device_id); |
142 |
|
|
} |
143 |
|
|
|
144 |
|
|
} |
145 |
|
|
|
146 |
|
|
/* |
147 |
|
|
* Increment 'any' to point to the next item in the linked |
148 |
|
|
* list. The length is in bytes, so 'any' must be cast to |
149 |
|
|
* a character pointer before being incremented. |
150 |
|
|
*/ |
151 |
|
✗ |
any = (XAnyClassPtr) ((char *) any + any->length); |
152 |
|
|
} |
153 |
|
|
|
154 |
|
|
} |
155 |
|
✗ |
XFreeDeviceList(slist); |
156 |
|
✗ |
XCloseDisplay(display); |
157 |
|
|
|
158 |
|
✗ |
return found; |
159 |
|
|
} |
160 |
|
|
|
161 |
|
✗ |
static void usage(char* cmd, unsigned thr_misclick) |
162 |
|
|
{ |
163 |
|
✗ |
fprintf(stderr, "Usage: %s [-h|--help] [-v|--verbose] [--list] [--device <device name or id>] [--precalib <minx> <maxx> <miny> <maxy>] [--misclick <nr of pixels>] [--output-type <auto|xorg.conf.d|hal|xinput>] [--fake]\n", cmd); |
164 |
|
✗ |
fprintf(stderr, "\t-h, --help: print this help message\n"); |
165 |
|
✗ |
fprintf(stderr, "\t-v, --verbose: print debug messages during the process\n"); |
166 |
|
✗ |
fprintf(stderr, "\t--list: list calibratable input devices and quit\n"); |
167 |
|
✗ |
fprintf(stderr, "\t--device <device name or id>: select a specific device to calibrate\n"); |
168 |
|
✗ |
fprintf(stderr, "\t--precalib: manually provide the current calibration setting (eg. the values in xorg.conf)\n"); |
169 |
|
✗ |
fprintf(stderr, "\t--misclick: set the misclick threshold (0=off, default: %i pixels)\n", |
170 |
|
|
thr_misclick); |
171 |
|
✗ |
fprintf(stderr, "\t--fake: emulate a fake device (for testing purposes)\n"); |
172 |
|
✗ |
} |
173 |
|
|
|
174 |
|
✗ |
static struct Calib* CalibratorXorgPrint(const char* const device_name0, const XYinfo *axis0, const gboolean verbose0, const int thr_misclick, const int thr_doubleclick) |
175 |
|
|
{ |
176 |
|
✗ |
struct Calib* c = (struct Calib*)calloc(1, sizeof(struct Calib)); |
177 |
|
✗ |
c->threshold_misclick = thr_misclick; |
178 |
|
✗ |
c->threshold_doubleclick = thr_doubleclick; |
179 |
|
|
|
180 |
|
✗ |
printf("Calibrating standard Xorg driver \"%s\"\n", device_name0); |
181 |
|
✗ |
printf("\tcurrent calibration values: min_x=%lf, max_x=%lf and min_y=%lf, max_y=%lf\n", |
182 |
|
✗ |
axis0->x_min, axis0->x_max, axis0->y_min, axis0->y_max); |
183 |
|
✗ |
printf("\tIf these values are estimated wrong, either supply it manually with the --precalib option, or run the 'get_precalib.sh' script to automatically get it (through HAL).\n"); |
184 |
|
|
|
185 |
|
✗ |
return c; |
186 |
|
|
} |
187 |
|
|
|
188 |
|
✗ |
static struct Calib* main_common(int argc, char** argv) |
189 |
|
|
{ |
190 |
|
✗ |
gboolean verbose = FALSE; |
191 |
|
✗ |
gboolean list_devices = FALSE; |
192 |
|
✗ |
gboolean fake = FALSE; |
193 |
|
✗ |
gboolean precalib = FALSE; |
194 |
|
✗ |
XYinfo pre_axis = {-1, -1, -1, -1}; |
195 |
|
✗ |
const char* pre_device = NULL; |
196 |
|
✗ |
unsigned thr_misclick = 15; |
197 |
|
✗ |
unsigned thr_doubleclick = 7; |
198 |
|
|
|
199 |
|
|
/* parse input */ |
200 |
|
✗ |
if (argc > 1) { |
201 |
|
|
int i; |
202 |
|
✗ |
for (i=1; i!=argc; i++) { |
203 |
|
|
/* Display help ? */ |
204 |
|
✗ |
if (strcmp("-h", argv[i]) == 0 || |
205 |
|
✗ |
strcmp("--help", argv[i]) == 0) { |
206 |
|
✗ |
fprintf(stderr, "xinput_calibrator, v%s\n\n", "0.0.0"); |
207 |
|
✗ |
usage(argv[0], thr_misclick); |
208 |
|
✗ |
exit(0); |
209 |
|
|
} else |
210 |
|
|
|
211 |
|
|
/* Verbose output ? */ |
212 |
|
✗ |
if (strcmp("-v", argv[i]) == 0 || |
213 |
|
✗ |
strcmp("--verbose", argv[i]) == 0) { |
214 |
|
✗ |
verbose = TRUE; |
215 |
|
|
} else |
216 |
|
|
|
217 |
|
|
/* Just list devices ? */ |
218 |
|
✗ |
if (strcmp("--list", argv[i]) == 0) { |
219 |
|
✗ |
list_devices = TRUE; |
220 |
|
|
} else |
221 |
|
|
|
222 |
|
|
/* Select specific device ? */ |
223 |
|
✗ |
if (strcmp("--device", argv[i]) == 0) { |
224 |
|
✗ |
if (argc > i+1) |
225 |
|
✗ |
pre_device = argv[++i]; |
226 |
|
|
else { |
227 |
|
✗ |
fprintf(stderr, "Error: --device needs a device name or id as argument; use --list to list the calibratable input devices.\n\n"); |
228 |
|
✗ |
usage(argv[0], thr_misclick); |
229 |
|
✗ |
exit(1); |
230 |
|
|
} |
231 |
|
|
} else |
232 |
|
|
|
233 |
|
|
/* Get pre-calibration ? */ |
234 |
|
✗ |
if (strcmp("--precalib", argv[i]) == 0) { |
235 |
|
✗ |
precalib = TRUE; |
236 |
|
✗ |
if (argc > i+1) |
237 |
|
✗ |
pre_axis.x_min = atoi(argv[++i]); |
238 |
|
✗ |
if (argc > i+1) |
239 |
|
✗ |
pre_axis.x_max = atoi(argv[++i]); |
240 |
|
✗ |
if (argc > i+1) |
241 |
|
✗ |
pre_axis.y_min = atoi(argv[++i]); |
242 |
|
✗ |
if (argc > i+1) |
243 |
|
✗ |
pre_axis.y_max = atoi(argv[++i]); |
244 |
|
|
} else |
245 |
|
|
|
246 |
|
|
/* Get mis-click threshold ? */ |
247 |
|
✗ |
if (strcmp("--misclick", argv[i]) == 0) { |
248 |
|
✗ |
if (argc > i+1) |
249 |
|
✗ |
thr_misclick = atoi(argv[++i]); |
250 |
|
|
else { |
251 |
|
✗ |
fprintf(stderr, "Error: --misclick needs a number (the pixel threshold) as argument. Set to 0 to disable mis-click detection.\n\n"); |
252 |
|
✗ |
usage(argv[0], thr_misclick); |
253 |
|
✗ |
exit(1); |
254 |
|
|
} |
255 |
|
|
} else |
256 |
|
|
|
257 |
|
|
/* Fake calibratable device ? */ |
258 |
|
✗ |
if (strcmp("--fake", argv[i]) == 0) { |
259 |
|
✗ |
fake = TRUE; |
260 |
|
|
} |
261 |
|
|
|
262 |
|
|
/* unknown option */ |
263 |
|
|
else { |
264 |
|
✗ |
fprintf(stderr, "Unknown option: %s\n\n", argv[i]); |
265 |
|
✗ |
usage(argv[0], thr_misclick); |
266 |
|
✗ |
exit(0); |
267 |
|
|
} |
268 |
|
|
} |
269 |
|
|
} |
270 |
|
|
|
271 |
|
|
|
272 |
|
|
/* Choose the device to calibrate */ |
273 |
|
✗ |
XID device_id = (XID) -1; |
274 |
|
✗ |
const char* device_name = NULL; |
275 |
|
✗ |
XYinfo device_axis = {-1, -1, -1, -1}; |
276 |
|
✗ |
if (fake) { |
277 |
|
|
/* Fake a calibratable device */ |
278 |
|
✗ |
device_name = "Fake_device"; |
279 |
|
✗ |
device_axis.x_min=0; |
280 |
|
✗ |
device_axis.x_max=1000; |
281 |
|
✗ |
device_axis.y_min=0; |
282 |
|
✗ |
device_axis.y_max=1000; |
283 |
|
|
|
284 |
|
✗ |
if (verbose) { |
285 |
|
✗ |
printf("DEBUG: Faking device: %s\n", device_name); |
286 |
|
|
} |
287 |
|
|
} else { |
288 |
|
|
/* Find the right device */ |
289 |
|
✗ |
int nr_found = find_device(pre_device, verbose, list_devices, &device_id, &device_name, &device_axis); |
290 |
|
|
|
291 |
|
✗ |
if (list_devices) { |
292 |
|
|
/* printed the list in find_device */ |
293 |
|
✗ |
if (nr_found == 0) |
294 |
|
✗ |
printf("No calibratable devices found.\n"); |
295 |
|
✗ |
exit(0); |
296 |
|
|
} |
297 |
|
|
|
298 |
|
✗ |
if (nr_found == 0) { |
299 |
|
✗ |
if (pre_device == NULL) |
300 |
|
✗ |
fprintf (stderr, "Error: No calibratable devices found.\n"); |
301 |
|
|
else |
302 |
|
✗ |
fprintf (stderr, "Error: Device \"%s\" not found; use --list to list the calibratable input devices.\n", pre_device); |
303 |
|
✗ |
exit(1); |
304 |
|
|
|
305 |
|
✗ |
} else if (nr_found > 1) { |
306 |
|
✗ |
printf ("Warning: multiple calibratable devices found, calibrating last one (%s)\n\tuse --device to select another one.\n", device_name); |
307 |
|
|
} |
308 |
|
|
|
309 |
|
✗ |
if (verbose) { |
310 |
|
✗ |
printf("DEBUG: Selected device: %s\n", device_name); |
311 |
|
|
} |
312 |
|
|
} |
313 |
|
|
|
314 |
|
|
/* override min/max XY from command line ? */ |
315 |
|
✗ |
if (precalib) { |
316 |
|
✗ |
if (pre_axis.x_min != -1) |
317 |
|
✗ |
device_axis.x_min = pre_axis.x_min; |
318 |
|
✗ |
if (pre_axis.x_max != -1) |
319 |
|
✗ |
device_axis.x_max = pre_axis.x_max; |
320 |
|
✗ |
if (pre_axis.y_min != -1) |
321 |
|
✗ |
device_axis.y_min = pre_axis.y_min; |
322 |
|
✗ |
if (pre_axis.y_max != -1) |
323 |
|
✗ |
device_axis.y_max = pre_axis.y_max; |
324 |
|
|
|
325 |
|
✗ |
if (verbose) { |
326 |
|
✗ |
printf("DEBUG: Setting precalibration: %lf, %lf, %lf, %lf\n", |
327 |
|
|
device_axis.x_min, device_axis.x_max, |
328 |
|
|
device_axis.y_min, device_axis.y_max); |
329 |
|
|
} |
330 |
|
|
} |
331 |
|
|
|
332 |
|
|
/* lastly, presume a standard Xorg driver (evtouch, mutouch, ...) */ |
333 |
|
✗ |
return CalibratorXorgPrint(device_name, &device_axis, |
334 |
|
|
verbose, thr_misclick, thr_doubleclick); |
335 |
|
|
} |
336 |
|
|
|
337 |
|
✗ |
static gboolean output_xorgconfd(const XYinfo new_axis, int swap_xy, int new_swap_xy) |
338 |
|
|
{ |
339 |
|
✗ |
const char* sysfs_name = "!!Name_Of_TouchScreen!!"; |
340 |
|
|
|
341 |
|
|
/* xorg.conf.d snippet */ |
342 |
|
✗ |
printf(" copy the snippet below into '/etc/X11/xorg.conf.d/99-calibration.conf'\n"); |
343 |
|
✗ |
printf("Section \"InputClass\"\n"); |
344 |
|
✗ |
printf(" Identifier \"calibration\"\n"); |
345 |
|
✗ |
printf(" MatchProduct \"%s\"\n", sysfs_name); |
346 |
|
✗ |
printf(" Option \"MinX\" \"%lf\"\n", new_axis.x_min); |
347 |
|
✗ |
printf(" Option \"MaxX\" \"%lf\"\n", new_axis.x_max); |
348 |
|
✗ |
printf(" Option \"MinY\" \"%lf\"\n", new_axis.y_min); |
349 |
|
✗ |
printf(" Option \"MaxY\" \"%lf\"\n", new_axis.y_max); |
350 |
|
✗ |
if (swap_xy != 0) |
351 |
|
✗ |
printf(" Option \"SwapXY\" \"%d\" # unless it was already set to 1\n", new_swap_xy); |
352 |
|
✗ |
printf("EndSection\n"); |
353 |
|
|
|
354 |
|
✗ |
return TRUE; |
355 |
|
|
} |
356 |
|
|
|
357 |
|
✗ |
static gboolean finish_data(const XYinfo new_axis, int swap_xy) |
358 |
|
|
{ |
359 |
|
✗ |
gboolean success = TRUE; |
360 |
|
|
|
361 |
|
|
/* we suppose the previous 'swap_xy' value was 0 */ |
362 |
|
|
/* (unfortunately there is no way to verify this (yet)) */ |
363 |
|
✗ |
int new_swap_xy = swap_xy; |
364 |
|
|
|
365 |
|
✗ |
printf("\n\n--> Making the calibration permanent <--\n"); |
366 |
|
✗ |
success &= output_xorgconfd(new_axis, swap_xy, new_swap_xy); |
367 |
|
|
|
368 |
|
✗ |
return success; |
369 |
|
|
} |
370 |
|
|
|
371 |
|
|
static void |
372 |
|
✗ |
calibration_finished_cb (CcCalibArea *area, |
373 |
|
|
gpointer user_data) |
374 |
|
|
{ |
375 |
|
|
gboolean success; |
376 |
|
|
XYinfo axis; |
377 |
|
|
gboolean swap_xy; |
378 |
|
|
|
379 |
|
✗ |
success = cc_calib_area_finish (area); |
380 |
|
✗ |
if (success) |
381 |
|
|
{ |
382 |
|
✗ |
cc_calib_area_get_axis (area, &axis, &swap_xy); |
383 |
|
✗ |
success = finish_data (axis, swap_xy); |
384 |
|
|
} |
385 |
|
|
else |
386 |
|
✗ |
fprintf(stderr, "Error: unable to apply or save configuration values\n"); |
387 |
|
|
|
388 |
|
✗ |
g_main_loop_quit (mainloop); |
389 |
|
✗ |
} |
390 |
|
|
|
391 |
|
✗ |
int main(int argc, char** argv) |
392 |
|
|
{ |
393 |
|
|
|
394 |
|
✗ |
struct Calib* calibrator = main_common(argc, argv); |
395 |
|
|
CcCalibArea *calib_area; |
396 |
|
|
|
397 |
|
✗ |
bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); |
398 |
|
✗ |
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); |
399 |
|
✗ |
textdomain (GETTEXT_PACKAGE); |
400 |
|
|
|
401 |
|
✗ |
gtk_init (); |
402 |
|
|
|
403 |
|
✗ |
g_setenv ("G_MESSAGES_DEBUG", "all", TRUE); |
404 |
|
|
|
405 |
|
✗ |
calib_area = cc_calib_area_new (NULL, |
406 |
|
|
NULL, /* monitor */ |
407 |
|
|
NULL, /* NULL to accept input from any device */ |
408 |
|
|
calibration_finished_cb, |
409 |
|
|
NULL, |
410 |
|
|
calibrator->threshold_doubleclick, |
411 |
|
|
calibrator->threshold_misclick); |
412 |
|
|
|
413 |
|
✗ |
mainloop = g_main_loop_new (NULL, FALSE); |
414 |
|
✗ |
g_main_loop_run (mainloop); |
415 |
|
|
|
416 |
|
✗ |
cc_calib_area_free (calib_area); |
417 |
|
|
|
418 |
|
✗ |
free(calibrator); |
419 |
|
|
|
420 |
|
✗ |
return 0; |
421 |
|
|
} |
422 |
|
|
|