bugs: Advantages for incremental library separation by analogy with incremental
[Ale.git] / ui / ui_tty.h
blobfebb63d092fc36820de42feb79a3b8d4bf1093e1
1 // Copyright 2004 David Hilvert <dhilvert@auricle.dyndns.org>,
2 // <dhilvert@ugcs.caltech.edu>
4 /* This file is part of the Anti-Lamenessing Engine.
6 The Anti-Lamenessing Engine is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 The Anti-Lamenessing Engine is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with the Anti-Lamenessing Engine; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #ifndef __ui_tty_h__
22 #define __ui_tty_h__
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <assert.h>
29 #if HAVE_TIME_H
30 #include <time.h>
31 #endif
32 #if HAVE_SYS_TIME_H
33 #include <sys/time.h>
34 #endif
36 #include "../d2.h"
37 #include "ui.h"
38 #include "util.h"
41 * TTY user interface
44 class ui_tty : public ui {
45 private:
46 int terminal_width;
47 char *buffer;
48 int buffer_index;
49 int status_index;
50 int dirty;
52 #ifdef USE_PTHREAD
53 pthread_mutex_t lock;
54 pthread_t update_thread;
55 #endif
57 void clear_buffer() {
58 buffer[0] = '\0';
59 buffer_index = 0;
62 void write_buffer() {
63 if (buffer_index < 0)
64 return;
65 fputc('\r', ui_stream);
66 fputs(buffer, ui_stream);
67 status_index = buffer_index;
70 void status_clear() {
71 while (status_index < terminal_width) {
72 status_index++;
73 fprintf(ui_stream, " ");
77 void line_clear() {
78 fputc('\r', ui_stream);
79 for (int i = 0; i < terminal_width; i++) {
80 fprintf(ui_stream, " ");
85 int status_printf(int count, ...) {
86 if (buffer_index < 0)
87 return -1;
89 int n;
91 char *local_buffer = (char *) calloc(terminal_width - status_index, sizeof(char));
93 assert(local_buffer);
95 if (!local_buffer)
96 return -1;
98 for (int i = 0; i < count; i++) {
99 char *format = NULL;
100 va_list ap;
102 va_start(ap, count);
104 for (int arg = 0; arg < i + 1; arg++)
105 format = va_arg(ap, char *);
106 for (int arg = i + 1; arg < count; arg++)
107 va_arg(ap, char *);
109 assert (format);
111 n = vsnprintf(local_buffer, terminal_width - status_index, format, ap);
112 va_end(ap);
114 if (n < 0 || n > terminal_width - status_index - 1)
115 continue;
117 fputs(local_buffer, ui_stream);
119 status_index += n;
121 free(local_buffer);
123 return 0;
128 * If we reach this point, then there was no valid string produced.
131 status_clear();
133 free(local_buffer);
136 * Failure.
139 return -1;
142 void pad_align_status() {
143 for (int i = 0; i < status.steps - status.steps_completed; i++) {
144 status_printf(1, " ");
147 void pad_match_status() {
148 status_printf(1, " ");
151 int write_match_status() {
152 return status_printf(1, format_string_working(), status.match_value);
155 void write_status() {
157 if (status.code == status.UNDEFINED
158 || status.code == status.FRAME_DONE
159 || status.code == status.SET_DONE
160 || status.code == status.IP_STEP_DONE) {
161 status_clear();
162 return;
165 if (status.code == status.GLOBAL_ALIGN
166 || status.code == status.ALIGN
167 || status.code == status.POSTMATCH
168 || status.code == status.EXPOSURE_PASS_2) {
169 pad_align_status();
170 if (write_match_status()) {
171 clear_buffer();
172 fprintf(ui_stream, "\n");
173 write_match_status();
177 status_printf(1, " | ");
179 switch (status.code) {
180 case status_type::LOAD_FILE:
181 status_printf(4, "Loading Image (%s)",
182 "Loading Image", "Loading", "load",
183 status.cache ? "caching" : "cache is full");
184 break;
185 case status_type::EXPOSURE_PASS_1:
186 status_printf(3, "Registering exposure (first pass)", "Registering exposure", "regexp1");
187 break;
188 case status_type::LODCLUSTER_CREATE:
189 status_printf(3, "Creating LOD cluster, scale %g", "Creating LOD clusters", "lodcluster",
190 pow(2, -status.align_lod));
191 break;
192 case status_type::PREMATCH:
193 status_printf(3, "Calculating pre-alignment match", "Calculating match", "prematch");
194 break;
195 case status_type::MULTI:
196 status_printf(3, "Assigning multi-alignment pixels", "Multi-alignment", "multi");
197 break;
198 case status_type::GLOBAL_ALIGN:
199 case status_type::ALIGN:
200 status_printf(5, "%s [perturb=%6.3g] [lod=%6.3g] [exp_mult=%6.3g %6.3g %6.3g]",
201 "%s [perturb=%6.3g] [lod=%6.3g]",
202 "%s [perturb=%6.3g]",
203 "%s...",
204 "align",
205 (status.code == status_type::GLOBAL_ALIGN)
206 ? "Global alignment"
207 : "Aligning",
208 status.perturb_size,
209 pow(2, -status.align_lod),
210 status.exp_multiplier[0],
211 status.exp_multiplier[1],
212 status.exp_multiplier[2]);
213 break;
214 case status_type::POSTMATCH:
215 status_printf(3, "Calculating post-alignment match", "Calculating match", "postmatch");
216 break;
217 case status_type::EXPOSURE_PASS_2:
218 status_printf(3, "Registering exposure (second pass)", "Registering exposure", "regexp2");
219 break;
220 case status_type::RENDERA:
221 status_printf(3, "Rendering alignment reference image", "Rendering", "render-a");
222 break;
223 case status_type::RENDERD:
224 status_printf(3, "Rendering default chain", "Rendering", "render-d");
225 break;
226 case status_type::RENDERO:
227 status_printf(3, "Rendering chain %d", "Rendering", "render-o%d", status.onum);
228 break;
229 case status_type::WRITED:
230 status_printf(4, "Writing default chain to '%s'",
231 "Writing '%s'", "Writing", "write-d", d2::image_rw::output_name());
232 break;
233 case status_type::WRITEO:
234 status_printf(3, "Writing image for chain %d", "Writing", "write-o%d", status.onum);
235 break;
236 case status_type::IP_RENDER:
237 status_printf(3, "Frame '%s'%s",
238 "Frame '%s'", "Processing",
239 d2::image_rw::name(status.frame_num), status.irani_peleg_stage
240 ? ((status.irani_peleg_stage == 1) ? " [simulate ]" : " [backproject]")
241 : "");
242 break;
243 case status_type::IP_UPDATE:
244 status_printf(3, "Updating approximation", "Updating", "update");
245 break;
246 case status_type::IP_WRITE:
247 status_printf(3, "Writing '%s'", "Writing", "write", d2::image_rw::output_name());
248 break;
249 case status_type::D3_CONTROL_POINT_SOLVE:
250 status_printf(1, "Aligning control points, %g%% done, error=%g",
251 log(status.cp_cur_perturb / status.cp_max_perturb)
252 / log(status.cp_min_perturb / status.cp_max_perturb),
253 status.cp_cur_error);
254 break;
255 case status_type::D3_SUBDIVIDING_SPACE:
256 status_printf(2, "Subdividing space, frame pair (%u, %u), y=%u, x=%u, spaces=%u",
257 "Subdividing space", status.frame_num,
258 status.secondary_frame_num, status.y_coordinate, status.x_coordinate,
259 status.total_spaces);
260 break;
261 case status_type::D3_UPDATING_OCCUPANCY:
262 status_printf(2, "Updating occupancy, step %u/%u, frame %u, space %u/%u",
263 "Updating occupancy", status.steps_completed,
264 status.steps, status.frame_num,
265 status.space_num, status.total_spaces);
266 break;
267 case status_type::D3_RENDER:
268 if (status.filtering == 0 && status.focusing == 0) {
269 status_printf(1, "space %u/%u",
270 status.space_num, status.total_spaces);
271 } else if (status.filtering == 1 && status.focusing == 0) {
272 status_printf(1, "frame %u, y=%u, x=%u",
273 status.frame_num, status.y_coordinate, status.x_coordinate);
274 } else if (status.filtering == 0 && status.focusing == 1) {
275 status_printf(1, "y=%u, x=%u, view=%u", status.y_coordinate, status.x_coordinate,
276 status.view_num);
277 } else if (status.filtering == 1 && status.focusing == 1) {
278 status_printf(1, "view=%u, y=%u, x=%u, frame=%u",
279 status.view_num, status.y_coordinate, status.x_coordinate,
280 status.frame_num);
282 break;
284 default:
285 break;
288 status_clear();
291 void write_all() {
292 write_buffer();
293 write_status();
297 void printf(const char *format, ...) {
298 #ifdef USE_PTHREAD
299 pthread_mutex_lock(&lock);
300 #endif
301 va_list ap;
302 int n = -1;
304 if (buffer_index >= 0 && buffer_index < terminal_width /* && format[strlen(format) - 1] != '\n' */) {
305 va_start(ap, format);
306 n = vsnprintf(buffer + buffer_index, terminal_width - buffer_index, format, ap);
307 va_end(ap);
310 if (n >= 0 && n < terminal_width - buffer_index) {
312 * The message fits in the buffer, so update the index
313 * and write buffer and status information.
316 buffer_index += n;
318 if (format[strlen(format) - 1] == '\n') {
319 line_clear();
320 write_buffer();
321 } else
322 write_all();
324 } else {
326 * The message does not fit in the buffer, so write any
327 * existing buffer and append the new text to the stream.
329 if (buffer_index >= 0) {
330 assert(buffer_index < terminal_width);
331 buffer[buffer_index] = '\0';
332 write_buffer();
334 buffer_index = -1;
335 va_start(ap, format);
336 vfprintf(ui_stream, format, ap);
337 va_end(ap);
341 * This is not the only case that produces a newline,
342 * but ignoring other cases should be safe.
344 if (format[strlen(format) - 1] == '\n') {
345 buffer_index = 0;
346 buffer[0] = '\0';
348 #ifdef USE_PTHREAD
349 pthread_mutex_unlock(&lock);
350 #endif
353 void update() {
354 static time_t last_update = 0;
355 time_t now = time(NULL);
358 * Handle DONE status.
361 if (status.code == status_type::FRAME_DONE) {
362 printf(".");
363 #ifdef USE_PTHREAD
364 pthread_mutex_lock(&lock);
365 #endif
366 fputc('\n', ui_stream);
367 buffer_index = 0;
368 buffer[0] = '\0';
369 #ifdef USE_PTHREAD
370 pthread_mutex_unlock(&lock);
371 #endif
372 } else if (status.code == status_type::SET_DONE) {
373 #ifdef USE_PTHREAD
374 pthread_mutex_lock(&lock);
375 #endif
376 fputc('\n', ui_stream);
377 buffer_index = 0;
378 buffer[0] = '\0';
379 #ifdef USE_PTHREAD
380 pthread_mutex_unlock(&lock);
381 #endif
382 } else {
385 * Handle optional output.
388 #ifdef USE_PTHREAD
389 pthread_mutex_lock(&lock);
390 #endif
391 if (now == last_update) {
392 dirty = 1;
393 } else {
394 dirty = 0;
395 last_update = now;
396 write_all();
398 #ifdef USE_PTHREAD
399 pthread_mutex_unlock(&lock);
400 #endif
404 public:
406 #ifdef USE_PTHREAD
407 static void *update_loop(void *vu) {
408 ui_tty *u = (ui_tty *) vu;
410 for(;;) {
411 #ifdef HAVE_NANOSLEEP
412 struct timespec t;
413 t.tv_sec = 0;
414 t.tv_nsec = 100000000;
415 nanosleep(&t, NULL);
416 #else
417 sleep(1);
418 #endif
419 if (u->dirty) {
420 pthread_mutex_lock(&u->lock);
421 u->write_all();
422 u->dirty = 0;
423 pthread_mutex_unlock(&u->lock);
427 return NULL;
429 #endif
432 * Constructor may throw an exception to signal that using ui_wo would
433 * be more appropriate.
435 ui_tty() {
436 int exception_value = 1;
438 if (!isatty(fileno(ui_stream)))
439 throw exception_value;
442 * Don't use the last column, as this may cause
443 * wrapping in some environments (BSD, Hurd).
446 terminal_width = get_terminal_width(ui_stream) - 1;
448 if (terminal_width < 0)
449 throw exception_value;
451 buffer = (char *) calloc(terminal_width + 1, sizeof(char));
453 assert (buffer);
455 if (!buffer)
456 throw exception_value;
458 buffer[0] = '\0';
459 buffer_index = 0;
461 dirty = 0;
464 * Start an updating thread if possible.
466 #ifdef USE_PTHREAD
467 pthread_mutex_init(&lock, NULL);
468 pthread_attr_t pattr;
469 pthread_attr_init(&pattr);
470 pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_JOINABLE);
471 pthread_create(&update_thread, NULL, update_loop, (void *) this);
472 #endif
475 ~ui_tty() {
476 #ifdef USE_PTHREAD
477 pthread_cancel(update_thread);
478 pthread_join(update_thread, NULL);
479 #endif
480 free(buffer);
484 #endif