1 
2 /* Instrument command line application support functions */
3 
4 /*
5  * Argyll Color Correction System
6  *
7  * Author: Graeme W. Gill
8  * Date:   10/3/2001
9  *
10  * Copyright 2001 - 2013 Graeme W. Gill
11  * All rights reserved.
12  *
13  * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
14  * see the License2.txt file for licencing details.
15  */
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <stdarg.h>
20 #include <ctype.h>
21 #include <string.h>
22 #include <time.h>
23 #ifndef SALONEINSTLIB
24 #include "copyright.h"
25 #include "aconfig.h"
26 #else
27 #include "sa_config.h"
28 #endif /* !SALONEINSTLIB */
29 #include "numsup.h"
30 #include "xspect.h"
31 #include "conv.h"
32 #include "insttypes.h"
33 
34 #include "icoms.h"
35 #include "inst.h"
36 #include "rspec.h"
37 #include "insttypeinst.h"
38 #include "instappsup.h"
39 
40 /* ================================================================= */
41 /* a default user interaction handler */
42 
43 typedef struct _uicontext {
44 	int emit_ret;				/* Emit \n on inst_triggered */
45 	int cut;					/* The character that caused the termination */
46 	int uih[256];				/* User interrupt handling key table. Value can be: */
47 								/* DUIH_OK, DUIH_ABORT, DUIH_TERM, DUIH_TRIG, DUIH_CMND */
48 } uicontext;
49 
50 static uicontext def_uicntx = { 1, 0, { 0 } };
51 
def_uicallback(void * cntx,inst_ui_purp purp)52 static inst_code def_uicallback(void *cntx, inst_ui_purp purp) {
53 	uicontext *p = (uicontext *)cntx;
54 
55 	if (purp == inst_triggered) {
56 		if (p->emit_ret)
57 			printf("\n");
58 		return inst_ok;
59 
60 	} else if (purp == inst_negcoms
61 	        || purp == inst_armed
62 	        || purp == inst_measuring) {
63 		int c;
64 
65 		c = poll_con_char();
66 		if (c != 0) {
67 			p->cut = c;
68 			c = p->uih[c];
69 			if (c & (DUIH_ABORT | DUIH_TERM | DUIH_CMND))
70 				return inst_user_abort;
71 			if (c & DUIH_TRIG)
72 				return inst_user_trig;
73 		}
74 
75 	} else if (purp == inst_measuring) {
76 		return inst_ok;
77 	}
78 	return inst_ok;
79 }
80 
81 /* Return the default uicallback function */
inst_get_uicallback()82 inst_code (*inst_get_uicallback())(void *, inst_ui_purp) {
83 	return &def_uicallback;
84 }
85 
86 /* Return the default uicallback context */
inst_get_uicontext()87 void *inst_get_uicontext() {
88 	return (void *)&def_uicntx;
89 }
90 
91 /* Install the default uicallback function in the given inst */
inst_set_uicallback(inst * p)92 void inst_set_uicallback(inst *p) {
93 	p->set_uicallback(p, def_uicallback, (void *)&def_uicntx);
94 }
95 
96 /* Set the return on trigger to true or false */
inst_set_uicb_trigret(int set)97 void inst_set_uicb_trigret(int set) {
98 	uicontext *p = &def_uicntx;
99 	p->emit_ret = set;
100 }
101 
102 /* Reset user interaction handling to default (Esc, ^C, q or 'Q' = Abort) */
inst_reset_uih()103 void inst_reset_uih() {
104 	uicontext *p = &def_uicntx;
105 	int i;
106 
107 	for (i = 0; i < 255; i++)
108 		p->uih[i] = DUIH_NONE;
109 
110 	p->uih[0x1b] = DUIH_ABORT;	/* Escape */
111 	p->uih['q']  = DUIH_ABORT;	/* q */
112 	p->uih['Q']  = DUIH_ABORT;	/* Q */
113 	p->uih[0x03] = DUIH_ABORT;	/* ^C */
114 }
115 
116 /* Set a key range to the given handling type */
117 /* min & max are between 0 and 255, status is one of */
118 /* DUIH_OK, DUIH_USER, DUIH_TERM, DUIH_TRIG, DUIH_CMND */
inst_set_uih(int min,int max,int status)119 void inst_set_uih(
120 int min,		/* Start key code */
121 int max,		/* End key code (inclusive) */
122 int status		/* ICOM_OK, ICOM_USER, ICOM_TERM, ICOM_TRIG, ICOM_CMND */
123 ) {
124 	uicontext *p = &def_uicntx;
125 	int i;
126 
127 	if (min < 0)
128 		min = 0;
129 	else if (min > 255)
130 		min = 255;
131 	if (max < 0)
132 		max = 0;
133 	else if (max > 255)
134 		max = 255;
135 
136 	if (status != DUIH_NONE
137 	 && status != DUIH_ABORT
138 	 && status != DUIH_TERM
139 	 && status != DUIH_CMND
140 	 && status != DUIH_TRIG)
141 		status = DUIH_NONE;
142 
143 	for (i = min; i <= max; i++) {
144 		p->uih[i] = status;
145 	}
146 }
147 
148 /* Get the character that caused the user interrupt */
149 /* + its key type in the upper 8 bits. */
150 /* Clear it to 0x00 after reading it. */
inst_get_uih_char()151 int inst_get_uih_char() {
152 	uicontext *p = &def_uicntx;
153 	int c = p->cut;
154 	c |= p->uih[c];
155 	p->cut = 0;
156 	return c;
157 }
158 
159 /* ================================================================= */
160 
161 /* A default calibration user interaction handler using the console. */
162 /* This handles both normal and display based calibration interaction */
163 /* with the instrument, if a disp_setup function and pointer to disp_win_info */
164 /* is provided. */
inst_handle_calibrate(inst * p,inst_cal_type calt,inst_cal_cond calc,inst_code (* disp_setup)(inst * p,inst_cal_cond calc,disp_win_info * dwi),disp_win_info * dwi,int doimmediately)165 inst_code inst_handle_calibrate(
166 	inst *p,
167 	inst_cal_type calt,		/* Calibration type to do */
168 	inst_cal_cond calc,		/* Current current condition */
169 	inst_code (*disp_setup) (inst *p,inst_cal_cond calc, disp_win_info *dwi),
170 							/* Callback for handling a display calibration - May be NULL */
171 	disp_win_info *dwi,		/* Information to be able to open a display test patch - May be NULL */
172 	int doimmediately		/* If nz, don't wait for user, calibrate immediatley */
173 ) {
174 	inst_code rv = inst_ok, ev;
175 	int usermes = 0;			/* User was given a message */
176 	inst_calc_id_type idtype;	/* Condition identifier type */
177 	char id[200];				/* Condition identifier */
178 	int ch;
179 
180 	a1logd(p->log,1,"inst_handle_calibrate called\n");
181 
182 	/* Untill we're done with the calibration */
183 	for (;;) {
184 
185 		a1logd(p->log,1,"About to call calibrate at top of loop\n");
186 	    ev = p->calibrate(p, &calt, &calc, &idtype, id);
187 		a1logd(p->log,1,"Calibrate returned calt 0x%x, calc 0x%x, ev 0x%x\n",calt,calc,ev);
188 
189 		/* We're done */
190 		if ((ev & inst_mask) == inst_ok) {
191 			if ((calc & inst_calc_cond_mask) == inst_calc_message) {
192 				/* (Or could create our own message text based on value of idtype) */
193 				printf("%s\n",id);
194 			}
195 			if (usermes)
196 				printf("Calibration complete\n");
197 			fflush(stdout);
198 			a1logd(p->log,1,"inst_handle_calibrate done 0x%x\n",ev);
199 			return ev;
200 		}
201 
202 		/* User aborted */
203 		if ((ev & inst_mask) == inst_user_abort) {
204 			a1logd(p->log,1,"inst_handle_calibrate user aborted 0x%x\n",ev);
205 			return ev;
206 		}
207 
208 		/* Retry on an error */
209 		if ((ev & inst_mask) != inst_cal_setup) {
210 			if ((ev & inst_mask) == inst_unsupported) {
211 				a1logd(p->log,1,"inst_handle_calibrate err 0x%x, calibration type 0x%x not supported\n",ev, calt);
212 				return inst_unsupported;
213 			}
214 
215 			printf("Calibration failed with '%s' (%s)\n",
216 				       p->inst_interp_error(p, ev), p->interp_error(p, ev));
217 
218 			if (doimmediately)
219 				return inst_user_abort;
220 
221 			printf("Hit any key to retry, or Esc or Q to abort:\n");
222 
223 			empty_con_chars();
224 			ch = next_con_char();
225 			printf("\n");
226 			if (ch == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
227 				a1logd(p->log,1,"inst_handle_calibrate user aborted 0x%x\n",inst_user_abort);
228 				fflush(stdout);
229 				return inst_user_abort;
230 			}
231 
232 		} else {
233 
234 			printf("\n");
235 
236 			/* Get user to do/setup calibration */
237 			switch (calc & inst_calc_cond_mask) {
238 				case inst_calc_uop_ref_white:
239 					printf("Do a reflective white calibration,\n");
240 					printf(" and then hit any key to continue,\n");
241 					break;
242 
243 				case inst_calc_uop_trans_white:
244 					printf("Do a transmissive white calibration,\n");
245 					printf(" and then hit any key to continue,\n");
246 					break;
247 
248 				case inst_calc_uop_trans_dark:
249 					printf("Do a transmissive dark calibration,\n");
250 					printf(" and then hit any key to continue,\n");
251 					break;
252 
253 				case inst_calc_man_ref_white:
254 					printf("Place the instrument on its reflective white reference S/N %s,\n",id);
255 					printf(" and then hit any key to continue,\n");
256 					break;
257 
258 				case inst_calc_man_ref_whitek:
259 					printf("Click the instrument on its reflective white reference %s,\n",id);
260 					break;
261 
262 				case inst_calc_man_ref_dark:
263 					printf("Place the instrument on light trap, or in the dark,\n");
264 					printf("and distant from any surface,\n");
265 					printf(" and then hit any key to continue,\n");
266 					break;
267 
268 				case inst_calc_man_dark_gloss:
269 					printf("Place the instrument on black gloss reference\n");
270 					printf(" and then hit any key to continue,\n");
271 					break;
272 
273 				case inst_calc_man_em_dark:
274 					printf("Place cap on the instrument, or place on a dark surface,\n");
275 					printf("or place on the calibration reference,\n");
276 					printf(" and then hit any key to continue,\n");
277 					break;
278 
279 				case inst_calc_man_am_dark:
280 					printf("Place ambient adapter and cap on the instrument,\n");
281 					printf("or place on the calibration reference,\n");
282 					printf(" and then hit any key to continue,\n");
283 					break;
284 
285 				case inst_calc_man_cal_smode:
286 					printf("Set instrument sensor to calibration position,\n");
287 					printf(" and then hit any key to continue,\n");
288 					break;
289 
290 				case inst_calc_man_trans_white:
291 					printf("Place the instrument on its transmissive white source,\n");
292 					printf(" and then hit any key to continue,\n");
293 					break;
294 
295 				case inst_calc_man_trans_dark:
296 					printf("Use the appropriate tramissive blocking to block the transmission path,\n");
297 					printf(" and then hit any key to continue,\n");
298 					break;
299 
300 				case inst_calc_change_filter:
301 					printf("Change filter on instrument to %s,\n",id);
302 					printf(" and then hit any key to continue,\n");
303 					break;
304 
305 				case inst_calc_message:
306 					printf("%s\n",id);
307 					printf(" Hit any key to continue,\n");
308 					break;
309 
310 				case inst_calc_emis_white:
311 					if (disp_setup == NULL || dwi == NULL) { /* No way of creating a test window */
312 						printf("Place the instrument on a 100%% white test patch,\n");
313 						printf(" and then hit any key to continue,\n");
314 					} else {
315 						/* We need to display a 100% white patch to proceed with this */
316 						/* type of calibration */
317 						if ((rv = disp_setup(p, calc, dwi)) != inst_ok)
318 							return rv;
319 					}
320 					break;
321 
322 				case inst_calc_emis_80pc:
323 					if (disp_setup == NULL || dwi == NULL) { /* No way of creating a test window */
324 						printf("Place the instrument on a 80%% white test patch,\n");
325 						printf(" and then hit any key to continue,\n");
326 					} else {
327 						/* We need to display a 80% white patch to proceed with this */
328 						/* type of calibration */
329 						if ((rv = disp_setup(p, calc, dwi)) != inst_ok)
330 							return rv;
331 					}
332 					break;
333 
334 				case inst_calc_emis_grey:
335 				case inst_calc_emis_grey_darker:
336 				case inst_calc_emis_grey_ligher:
337 					if (dwi == NULL) {	/* No way of creating a test window */
338 						if ((calc & inst_calc_cond_mask) == inst_calc_emis_grey) {
339 							p->cal_gy_level = 0.6;
340 							p->cal_gy_count = 0;
341 						} else if ((calc & inst_calc_cond_mask) == inst_calc_emis_grey_darker) {
342 							p->cal_gy_level *= 0.7;
343 							p->cal_gy_count++;
344 						} else if ((calc & inst_calc_cond_mask) == inst_calc_emis_grey_ligher) {
345 							p->cal_gy_level *= 1.4;
346 							if (p->cal_gy_level > 1.0)
347 								p->cal_gy_level = 1.0;
348 							p->cal_gy_count++;
349 						}
350 						if (p->cal_gy_count > 4) {
351 							printf("Cell ratio calibration failed - too many tries at setting grey level.\n");
352 							a1logd(p->log,1,"inst_handle_calibrate too many tries at setting grey level 0x%x\n",inst_internal_error);
353 							return inst_internal_error;
354 						} else {
355 							printf("Place the instrument on a %d%% white test patch,\n", (int)(p->cal_gy_level * 100.0 + 0.5));
356 							printf(" and then hit any key to continue,\n");
357 						}
358 					} else {
359 
360 						/* We need to display a test patch to proceed with this
361 						 * type of calibration. Typically this will be:
362 						 *
363 						 * inst_calc_xxxx_grey:
364 						 *		set p->cal_gy_level = 0.6
365 						 *		set p->cal_gy_count = 0;
366 						 *
367 						 * inst_calc_xxxx_grey_darker:
368 						 *		set p->cal_gy_level *= 0.7
369 						 *		set p->cal_gy_count++
370 						 *
371 						 * inst_calc_xxxx_grey_ligher:
372 						 *		set p->cal_gy_level *= 1.4
373 						 *		set p->cal_gy_count++
374 						 *
375 						 * and return failure if p->cal_gy_count > 4
376 						 */
377 
378 						if ((rv = disp_setup(p, calc, dwi)) != inst_ok)
379 							return rv;
380 					}
381 					break;
382 
383 				default:
384 					/* Something isn't being handled */
385 					a1logd(p->log,1,"inst_handle_calibrate unhandled calc case 0x%x, err 0x%x\n",calc,inst_internal_error);
386 					return inst_internal_error;
387 			}
388 			if (calc & inst_calc_optional_flag)
389 				printf(" or hit Esc or Q to abort, or S to skip: ");
390 			else
391 				printf(" or hit Esc or Q to abort: ");
392 			fflush(stdout);
393 
394 			usermes = 1;
395 
396 			/* If we should wait for user to say we're in the right condition, */
397 			/* and this isn't a calibration that requires clicking the instrument */
398 			/* on the calibration tile, then wait for the an OK or abort or skip. */
399 			if (!doimmediately
400 			 && (calc & inst_calc_cond_mask) != inst_calc_man_ref_whitek) {
401 				empty_con_chars();
402 				ch = next_con_char();
403 				printf("\n");
404 				/* If optional calib. and user wants to skip it */
405 				/* Loop back to calibrate() with inst_calc_optional_flag still set */
406 				if ((calc & inst_calc_optional_flag) != 0 && (ch == 's' || ch == 'S')) {
407 					printf("Skipped\n");
408 					goto oloop;
409 				}
410 				if (ch == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
411 					a1logd(p->log,1,"inst_handle_calibrate user aborted 0x%x\n",inst_user_abort);
412 					return inst_user_abort;
413 				}
414 			}
415 			/* Remove any skip flag and continue with the calibration */
416 			calc &= inst_calc_cond_mask;
417 		}
418  oloop:;
419 	}
420 }
421 
422 /* ============================================================================= */
423 
424 /* A helper function to display -y flag usage for each instrument type available */
425 /* Return accumulated capabilities2 of all the instruments */
426 /* Return all possible capabilities if there are no instruments */
427 /* If docbib is nz, then only display the base calibration display types */
inst_show_disptype_options(FILE * fp,char * oline,icompaths * icmps,int docbib)428 inst2_capability inst_show_disptype_options(FILE *fp, char *oline, icompaths *icmps, int docbib) {
429 	int i, j;
430 	char buf[200], *bp;
431 	char extra[40];
432 	int olen, pstart;
433 	int notall = 0;				/* Not all instruments are USB */
434 	int gotone = 0;				/* Found at least one USB instrument */
435 	inst2_capability acap = 0;	/* Accumulate capabilities */
436 
437 	if (icmps == NULL)
438 		return 0;
439 
440 	/* Locate the end of the option */
441 	for (bp = oline; *bp != '\000' && *bp == ' '; bp++)
442 		;
443 	for (; *bp != '\000' && *bp != ' '; bp++)
444 		;
445 	pstart = bp - oline;
446 	if (pstart > 10)
447 		pstart = 10;
448 	strncpy(buf, oline, pstart);
449 	buf[pstart++] = ' ';
450 
451 	olen = strlen(oline);		/* lenth of option part of line */
452 
453 	for (i = 0; icmps != NULL && i < icmps->ndpaths[dtix_inst]; i++) {
454 		inst *it;
455 		inst2_capability cap;
456 		int k;
457 
458 		if ((it = new_inst(icmps->dpaths[dtix_inst][i], 1, g_log, NULL, NULL)) == NULL) {
459 			notall = 1;
460 			continue;
461 		}
462 		gotone = 1;
463 
464 		it->capabilities(it, NULL, &cap, NULL);
465 		acap |= cap;
466 
467 		if (cap & inst2_disptype) {
468 			int nsel;
469 			inst_disptypesel *sels;
470 
471 			if (it->get_disptypesel(it, &nsel, &sels, 1, 0) != inst_ok) {
472 				it->del(it);
473 				continue;
474 			}
475 			for (j = 0; j < nsel; j++) {
476 				int m;
477 
478 				if (docbib && sels[j].cbid == 0)
479 					continue;			/* Skip non cbid type */
480 
481 				m = pstart;
482 				for (k = 0; k < (INST_DTYPE_SEL_LEN-1); k++) {
483 					if (sels[j].sel[k] == '\000')
484 						break;
485 					if (m > pstart)
486 						buf[m++] = '|';
487 					buf[m++] = sels[j].sel[k];
488 				}
489 				while (m < (olen+1))	/* Indent it by 1 */
490 					buf[m++] = ' ';
491 				buf[m++] = '\000';
492 
493 				extra[0] = '\000';
494 				if ((sels[j].flags & inst_dtflags_default) || sels[j].cbid != 0) {
495 					strcat(extra, " [");
496 			        if (sels[j].flags & inst_dtflags_default) {
497 						strcat(extra, "Default");
498 						if (sels[j].cbid != 0)
499 							strcat(extra, ",");
500 					}
501 					if (sels[j].cbid != 0) {
502 						sprintf(extra + strlen(extra), "CB%d",sels[j].cbid);
503 					}
504 					strcat(extra, "]");
505 				}
506 
507 				fprintf(fp, "%s%s: %s%s\n",buf, inst_sname(it->itype), sels[j].desc, extra);
508 
509 				if (j == 0) {
510 					for (m = 0; m < pstart; m++)
511 						buf[m] = ' ';
512 				}
513 			}
514 		}
515 		it->del(it);
516 	}
517 	/* Output a default desciption if not all instruments are USB */
518 	if (notall) {
519 		int m = pstart;
520 		buf[m++] = 'l';
521 		buf[m++] = '|';
522 		buf[m++] = 'c';
523 		while (m < olen)
524 			buf[m++] = ' ';
525 		buf[m++] = '\000';
526 		fprintf(fp, "%s%s\n",buf, " Other: l = LCD, c = CRT");
527 	}
528 	if (!gotone)
529 		acap = ~0;
530 
531 	return acap;
532 }
533 
534 /* A helper function to turn a -y flag into a selection index */
535 /* If docbib is nz, then only allow base calibration display types */
536 /* Return -1 on error */
inst_get_disptype_index(inst * it,int c,int docbib)537 int inst_get_disptype_index(inst *it, int c, int docbib) {
538 	inst2_capability cap;
539 	int j, k;
540 
541 	it->capabilities(it, NULL, &cap, NULL);
542 
543 	if (cap & inst2_disptype) {
544 		int nsel;
545 		inst_disptypesel *sels;
546 
547 		if (it->get_disptypesel(it, &nsel, &sels, 1, 0) != inst_ok) {
548 			return -1;
549 		}
550 		for (j = 0; j < nsel; j++) {
551 			if (docbib && sels[j].cbid == 0)
552 				continue;			/* Skip non cbid type */
553 
554 			for (k = 0; k < (INST_DTYPE_SEL_LEN-1); k++) {
555 				if (sels[j].sel[k] == '\000')
556 					break;
557 				if (sels[j].sel[k] == c) {
558 					return j;
559 				}
560 			}
561 		}
562 	}
563 	return -1;
564 }
565 
566 /* ================================================================= */
567 
568 
569 
570 
571 
572 
573 
574 
575 
576 
577 
578 
579 
580 
581 
582 
583 
584 
585 
586 
587