timeval: Make time_init() static and remove calls to it.
[sliver-openvswitch.git] / extras / ezio / ezio-term.c
1 /* Copyright (c) 2008, 2009, 2010 Nicira Networks, Inc.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16
17 #include <config.h>
18 #include <assert.h>
19 #include <curses.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <getopt.h>
23 #include <inttypes.h>
24 #include <signal.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <term.h>
28 #include <unistd.h>
29 #include "byteq.h"
30 #include "command-line.h"
31 #include "extras/ezio/tty.h"
32 #include "extras/ezio/vt.h"
33 #include "daemon.h"
34 #include "ezio.h"
35 #include "poll-loop.h"
36 #include "socket-util.h"
37 #include "terminal.h"
38 #include "timeval.h"
39 #include "util.h"
40
41 #define THIS_MODULE VLM_ezio_term
42 #include "vlog.h"
43
44 /* EZIO button status. */
45 enum btn_status {
46     BTN_UP    = 1 << 0,
47     BTN_DOWN  = 1 << 1,
48     BTN_ENTER = 1 << 2,
49     BTN_ESC   = 1 << 3
50 };
51
52 /* -e, --ezio: EZIO3 serial device file. */
53 static char *ezio_dev = "/dev/ttyS1";
54
55 /* -i, --input: Terminal from which to accept additional keyboard input. */
56 static char *input_dev = NULL;
57
58 struct inputdev;
59 static int inputdev_open(const char *name, struct inputdev **);
60 static void inputdev_close(struct inputdev *);
61 static int inputdev_run(struct inputdev *, struct byteq *);
62 static void inputdev_update(struct inputdev *, const struct ezio *);
63 static void inputdev_wait(struct inputdev *);
64
65 static struct scanner *scanner_create(void);
66 static void scanner_destroy(struct scanner *);
67 static void scanner_run(struct scanner *, struct ezio *);
68 static void scanner_wait(struct scanner *);
69 static void scanner_left(struct scanner *, struct ezio *);
70 static void scanner_right(struct scanner *, struct ezio *);
71
72 static struct updater *updater_create(void);
73 static void updater_destroy(struct updater *);
74 static int updater_run(struct updater *, const struct ezio *shadow,
75                        int ezio_fd);
76 static void updater_wait(struct updater *, int ezio_fd);
77 enum btn_status updater_get_buttons(struct updater *);
78 bool updater_has_buttons(const struct updater *);
79
80 static void handle_buttons(struct updater *, struct scanner *,
81                            struct byteq *, struct ezio *);
82
83 static void usage(void) NO_RETURN;
84 static void parse_options(int argc, char *argv[]);
85
86 int
87 main(int argc, char *argv[])
88 {
89     struct terminal *terminal;
90     struct updater *updater;
91     struct scanner *scanner;
92     struct inputdev *inputdev;
93     struct byteq inputq;
94     struct ezio ezio;
95     int ezio_fd, pty_fd, dummy_fd;
96     int retval;
97     int i;
98
99     proctitle_init(argc, argv);
100     set_program_name(argv[0]);
101     vlog_init();
102     parse_options(argc, argv);
103     signal(SIGPIPE, SIG_IGN);
104
105     argc -= optind;
106     argv += optind;
107
108     /* Make sure that the ezio3 terminfo entry is available. */
109     dummy_fd = get_null_fd();
110     if (dummy_fd >= 0) {
111         if (setupterm("ezio3", dummy_fd, &retval) == ERR) {
112             if (retval == 0) {
113                 ovs_fatal(0, "Missing terminfo entry for ezio3.  "
114                           "Did you run \"make install\"?");
115             } else {
116                 ovs_fatal(0, "Missing terminfo database.  Is ncurses "
117                           "properly installed?");
118             }
119         }
120         del_curterm(cur_term);
121     }
122
123     /* Lock serial port. */
124     retval = tty_lock(ezio_dev);
125     if (retval) {
126         ovs_fatal(retval, "%s: lock failed", ezio_dev);
127     }
128
129     /* Open EZIO and configure as 2400 bps, N-8-1, in raw mode. */
130     ezio_fd = open(ezio_dev, O_RDWR | O_NOCTTY);
131     if (ezio_fd < 0) {
132         ovs_fatal(errno, "%s: open", ezio_dev);
133     }
134     retval = tty_set_raw_mode(ezio_fd, B2400);
135     if (retval) {
136         ovs_fatal(retval, "%s: failed to configure tty parameters", ezio_dev);
137     }
138
139     /* Open keyboard device for input. */
140     if (input_dev) {
141         retval = inputdev_open(input_dev, &inputdev);
142         if (retval) {
143             ovs_fatal(retval, "%s: failed to open input device", input_dev);
144         }
145     } else {
146         inputdev = NULL;
147     }
148
149     /* Open pty master. */
150     pty_fd = tty_open_master_pty();
151     if (pty_fd < 0) {
152         ovs_fatal(-pty_fd, "failed to open master pty");
153     }
154     tty_set_window_size(pty_fd, 2, 40);
155
156     /* Start child process. */
157     if (argc < 1) {
158         char *child_argv[2];
159
160         child_argv[0] = getenv("SHELL");
161         if (!child_argv[0]) {
162             child_argv[0] = "/bin/sh";
163         }
164         child_argv[1] = NULL;
165         retval = tty_fork_child(pty_fd, child_argv);
166     } else {
167         retval = tty_fork_child(pty_fd, argv);
168     }
169     if (retval) {
170         ovs_fatal(retval, "failed to fork child process");
171     }
172
173     die_if_already_running();
174     daemonize();
175
176     terminal = terminal_create();
177     updater = updater_create();
178     scanner = scanner_create();
179     ezio_init(&ezio);
180     for (i = 0; i < 8; i++) {
181         ezio_set_default_icon(&ezio, i);
182     }
183     byteq_init(&inputq);
184     for (;;) {
185         /* Get button presses and keyboard input into inputq, then push the
186          * inputq to the pty. */
187         handle_buttons(updater, scanner, &inputq, &ezio);
188         if (inputdev) {
189             retval = inputdev_run(inputdev, &inputq);
190             if (retval) {
191                 VLOG_ERR("error reading from input device: %s",
192                          strerror(retval));
193                 inputdev_close(inputdev);
194                 inputdev = NULL;
195             }
196         }
197         retval = byteq_write(&inputq, pty_fd);
198         if (retval && retval != EAGAIN) {
199             VLOG_ERR("error passing through input: %s",
200                      retval == EOF ? "end of file" : strerror(retval));
201         }
202
203         /* Process data from pty in terminal emulator. */
204         retval = terminal_run(terminal, &ezio, pty_fd);
205         if (retval) {
206             VLOG_ERR("error reading from terminal: %s",
207                      retval == EOF ? "end of file" : strerror(retval));
208             break;
209         }
210
211         /* Scroll left and right through text. */
212         scanner_run(scanner, &ezio);
213
214         /* Update the display to match what should be shown. */
215         retval = updater_run(updater, &ezio, ezio_fd);
216         if (retval) {
217             VLOG_ERR("error writing to ezio: %s",
218                      retval == EOF ? "end of file" : strerror(retval));
219             break;
220         }
221         if (inputdev) {
222             inputdev_update(inputdev, &ezio);
223         }
224
225         /* Wait for something to happen. */
226         terminal_wait(terminal, pty_fd);
227         scanner_wait(scanner);
228         if (updater_has_buttons(updater)) {
229             poll_immediate_wake();
230         }
231         updater_wait(updater, ezio_fd);
232         if (!byteq_is_empty(&inputq)) {
233             poll_fd_wait(pty_fd, POLLOUT);
234         }
235         if (inputdev) {
236             inputdev_wait(inputdev);
237         }
238         poll_block();
239     }
240     terminal_destroy(terminal);
241     updater_destroy(updater);
242     scanner_destroy(scanner);
243
244     return 0;
245 }
246
247 static void
248 send_keys(struct byteq *q, const char *s)
249 {
250     size_t n = strlen(s);
251     if (byteq_avail(q) >= n) {
252         byteq_putn(q, s, n);
253     }
254 }
255
256 static void
257 handle_buttons(struct updater *up, struct scanner *s,
258                struct byteq *q, struct ezio *ezio)
259 {
260     while (updater_has_buttons(up)) {
261         int btns = updater_get_buttons(up);
262         switch (btns) {
263         case BTN_UP:
264             send_keys(q, "\x1b\x5b\x41"); /* Up arrow. */
265             break;
266
267         case BTN_UP | BTN_ESC:
268             send_keys(q, "\x1b[5~"); /* Page up. */
269             break;
270
271         case BTN_DOWN:
272             send_keys(q, "\x1b\x5b\x42"); /* Down arrow. */
273             break;
274
275         case BTN_DOWN | BTN_ESC:
276             send_keys(q, "\x1b[6~"); /* Page down. */
277             break;
278
279         case BTN_ENTER:
280             send_keys(q, "\r");
281             break;
282
283         case BTN_ESC:
284             send_keys(q, "\x7f");
285             break;
286
287         case BTN_UP | BTN_DOWN:
288             scanner_left(s, ezio);
289             break;
290
291         case BTN_ESC | BTN_ENTER:
292             scanner_right(s, ezio);
293             break;
294
295         case BTN_UP | BTN_DOWN | BTN_ENTER | BTN_ESC:
296             send_keys(q, "\x04"); /* End of file. */
297             break;
298
299         case BTN_UP | BTN_ENTER | BTN_ESC:
300             send_keys(q, "y");
301             break;
302
303         case BTN_DOWN | BTN_ENTER | BTN_ESC:
304             send_keys(q, "n");
305             break;
306         }
307     }
308 }
309 \f
310 /* EZIO screen updater. */
311
312 /* EZIO command codes. */
313 #define EZIO_CMD                0xfe /* Command prefix byte. */
314 #define EZIO_CLEAR              0x01 /* Clear screen. */
315 #define EZIO_HOME               0x02 /* Move to (0, 0). */
316 #define EZIO_READ               0x06 /* Poll keyboard. */
317
318 #define EZIO_ENTRY_MODE         0x04 /* Set entry mode: */
319 #define   EZIO_LTOR_MODE        0x02 /* ...left-to-right (vs. r-to-l). */
320 #define   EZIO_SHIFT_MODE       0x01 /* ...scroll with output (vs. don't). */
321
322 #define EZIO_DISPLAY_MODE       0x08 /* Set display mode: */
323 #define   EZIO_ENABLE_DISPLAY   0x04 /* ...turn on display (vs. blank). */
324 #define   EZIO_SHOW_CURSOR      0x02 /* ...show cursor (vs. hide). */
325 #define   EZIO_BLOCK_CURSOR     0x01 /* ...block cursor (vs. underline). */
326
327 #define EZIO_INIT               0x28 /* Initialize EZIO. */
328
329 #define EZIO_MOVE_CURSOR        0x80 /* Set cursor position. */
330 #define   EZIO_COL_SHIFT        0    /* Shift count for column (0-based). */
331 #define   EZIO_ROW_SHIFT        6    /* Shift count for row (0-based). */
332
333 #define EZIO_DEFINE_ICON        0x40 /* Define icon. */
334 #define   EZIO_ICON_SHIFT       3    /* Shift count for icon number (0-7). */
335
336 #define EZIO_SCROLL_LEFT        0x18 /* Scroll display left 1 position. */
337 #define EZIO_SCROLL_RIGHT       0x1c /* Scroll display right 1 position. */
338 #define EZIO_CURSOR_LEFT        0x10 /* Move cursor left 1 position. */
339 #define EZIO_CURSOR_RIGHT       0x14 /* Move cursor right 1 position. */
340
341 /* Rate limiting: the EZIO runs at 2400 bps, which is 240 bytes per second.
342  * Kernel tty buffers, on the other hand, tend to be at least 4 kB.  That
343  * means that, if we keep the kernel buffer filled, then the queued data will
344  * be 4,096 kB / 240 bytes/s ~= 17 seconds ahead of what is actually
345  * displayed.  This is not a happy situation.  So we rate-limit with a token
346  * bucket.
347  *
348  * The parameters below work out as: (6 tokens/ms * 1000 ms) / (25
349  * tokens/byte) = 240 bytes/s. */
350 #define UP_TOKENS_PER_MS 6       /* Tokens acquired per millisecond. */
351 #define UP_BUCKET_SIZE (6 * 100) /* Capacity of the token bukect. */
352 #define UP_TOKENS_PER_BYTE 25    /* Tokens required to output a byte. */
353
354 struct updater {
355     /* Current state of EZIO device. */
356     struct ezio visible;
357
358     /* Output state. */
359     struct byteq obuf;          /* Output being sent to serial port. */
360     int tokens;                 /* Token bucket content. */
361     long long int last_fill;    /* Last time we increased 'tokens'.*/
362     bool up_to_date;            /* Does visible state match shadow state? */
363
364     /* Input state. */
365     struct byteq ibuf;           /* Queued button pushes. */
366     long long int last_poll;     /* Last time we sent a button poll request. */
367     enum btn_status last_status; /* Last received button status. */
368     long long int last_change;   /* Time when status most recently changed. */
369     int repeat_count;            /* Autorepeat count. */
370     bool releasing;              /* Waiting for button release? */
371 };
372
373 static void send_command(struct updater *, uint8_t command);
374 static void recv_button_state(struct updater *, enum btn_status status);
375 static int range(int value, int min, int max);
376 static void send_command(struct updater *, uint8_t command);
377 static void set_cursor_position(struct updater *, int x, int y);
378 static bool icons_differ(const struct ezio *, const struct ezio *, int *idx);
379 static void update_char(struct updater *, const struct ezio *, int x, int y);
380 static void update_cursor_status(struct updater *, const struct ezio *);
381
382 /* Creates and returns a new updater. */
383 static struct updater *
384 updater_create(void)
385 {
386     struct updater *up = xmalloc(sizeof *up);
387     ezio_init(&up->visible);
388     byteq_init(&up->obuf);
389     up->tokens = UP_BUCKET_SIZE;
390     up->last_fill = time_msec();
391     byteq_init(&up->ibuf);
392     up->last_poll = LLONG_MIN;
393     up->last_status = 0;
394     up->last_change = time_msec();
395     up->releasing = false;
396     send_command(up, EZIO_INIT);
397     send_command(up, EZIO_INIT);
398     send_command(up, EZIO_CLEAR);
399     send_command(up, EZIO_HOME);
400     return up;
401 }
402
403 /* Destroys updater 'up. */
404 static void
405 updater_destroy(struct updater *up)
406 {
407     free(up);
408 }
409
410 /* Sends EZIO commands over file descriptor 'ezio_fd' to the EZIO represented
411  * by updater 'up', to make the EZIO display the contents of 'shadow'.
412  * Rate-limiting can cause the update to be only partial, but the next call to
413  * updater_run() will resume the update.
414  *
415  * Returns 0 if successful, otherwise a positive errno value. */
416 static int
417 updater_run(struct updater *up, const struct ezio *shadow, int ezio_fd)
418 {
419     uint8_t c;
420     while (read(ezio_fd, &c, 1) > 0) {
421         if ((c & 0xf0) == 0xb0) {
422             recv_button_state(up, ~c & 0x0f);
423         }
424     }
425
426     up->up_to_date = false;
427     for (;;) {
428         struct ezio *visible = &up->visible;
429         int idx, x, y;
430         int retval;
431
432         /* Flush the buffer out to the EZIO device. */
433         retval = byteq_write(&up->obuf, ezio_fd);
434         if (retval == EAGAIN) {
435             return 0;
436         } else if (retval) {
437             VLOG_WARN("error writing ezio: %s", strerror(retval));
438             return retval;
439         }
440
441         /* Make sure we have some tokens before we write anything more. */
442         if (up->tokens <= 0) {
443             long long int now = time_msec();
444             if (now > up->last_fill) {
445                 up->tokens += (now - up->last_fill) * UP_TOKENS_PER_MS;
446                 up->last_fill = now;
447                 if (up->tokens > UP_BUCKET_SIZE) {
448                     up->tokens = UP_BUCKET_SIZE;
449                 }
450             }
451             if (up->tokens <= 0) {
452                 /* Still out of tokens. */
453                 return 0;
454             }
455         }
456
457         /* Consider what else we might want to send. */
458         if (time_msec() >= up->last_poll + 100) {
459             /* Send a button-read command. */
460             send_command(up, EZIO_READ);
461             up->last_poll = time_msec();
462         } else if (visible->show_cursor && !shadow->show_cursor) {
463             /* Turn off the cursor. */
464             update_cursor_status(up, shadow);
465         } else if (icons_differ(shadow, visible, &idx)) {
466             /* Update the icons. */
467             send_command(up, EZIO_DEFINE_ICON + (idx << EZIO_ICON_SHIFT));
468             byteq_putn(&up->obuf, &shadow->icons[idx][0], 8);
469             set_cursor_position(up, shadow->x, shadow->y);
470             memcpy(visible->icons[idx], shadow->icons[idx], 8);
471         } else if (visible->x_ofs != shadow->x_ofs) {
472             /* Scroll to the correct horizontal position. */
473             if (visible->x_ofs < shadow->x_ofs) {
474                 send_command(up, EZIO_SCROLL_LEFT);
475                 visible->x_ofs++;
476             } else {
477                 send_command(up, EZIO_SCROLL_RIGHT);
478                 visible->x_ofs--;
479             }
480         } else if (ezio_chars_differ(shadow, visible, shadow->x_ofs,
481                                      shadow->x_ofs + 16, &x, &y)) {
482             /* Update the visible region. */
483             update_char(up, shadow, x, y);
484         } else if (ezio_chars_differ(shadow, visible, 0, 40, &x, &y)) {
485             /* Update the off-screen region. */
486             update_char(up, shadow, x, y);
487         } else if ((visible->x != shadow->x || visible->y != shadow->y)
488                    && shadow->show_cursor) {
489             /* Update the cursor position.  (This has to follow updating the
490              * display content, because updating display content changes the
491              * cursor position.) */
492             set_cursor_position(up, shadow->x, shadow->y);
493         } else if (visible->show_cursor != shadow->show_cursor
494                    || visible->blink_cursor != shadow->blink_cursor) {
495             /* Update the cursor type. */
496             update_cursor_status(up, shadow);
497         } else {
498             /* We're fully up-to-date. */
499             up->up_to_date = true;
500             return 0;
501         }
502         up->tokens -= UP_TOKENS_PER_BYTE * byteq_used(&up->obuf);
503     }
504 }
505
506 /* Calls poll-loop functions that will cause poll_block() to wake up when
507  * updater_run() has work to do. */
508 static void
509 updater_wait(struct updater *up, int ezio_fd)
510 {
511     if (!byteq_is_empty(&up->obuf)) {
512         poll_fd_wait(ezio_fd, POLLOUT);
513     } else if (up->tokens <= 0) {
514         poll_timer_wait((-up->tokens / UP_TOKENS_PER_MS) + 1);
515     } else if (!up->up_to_date) {
516         poll_immediate_wake();
517     }
518
519     if (!up->last_status && time_msec() - up->last_change > 100) {
520         /* No button presses in a while.  Sleep longer. */
521         poll_timer_wait(100);
522     } else {
523         poll_timer_wait(50);
524     }
525 }
526
527 /* Returns a button or buttons that were pushed.  Must not be called if
528  * updater_has_buttons() would return false.  One or more BTN_* flags will be
529  * set in the return value. */
530 enum btn_status
531 updater_get_buttons(struct updater *up)
532 {
533     return byteq_get(&up->ibuf);
534 }
535
536 /* Any buttons pushed? */
537 bool
538 updater_has_buttons(const struct updater *up)
539 {
540     return !byteq_is_empty(&up->ibuf);
541 }
542
543 /* Adds 'btns' to the queue of pushed buttons */
544 static void
545 buttons_pushed(struct updater *up, enum btn_status btns)
546 {
547     if (!byteq_is_full(&up->ibuf)) {
548         byteq_put(&up->ibuf, btns);
549     }
550 }
551
552 /* Updates the buttons-pushed queue based on the current button 'status'. */
553 static void
554 recv_button_state(struct updater *up, enum btn_status status)
555 {
556     /* Calculate milliseconds since button status last changed. */
557     long long int stable_msec;
558     if (status != up->last_status) {
559         up->last_change = time_msec();
560         stable_msec = 0;
561     } else {
562         stable_msec = time_msec() - up->last_change;
563     }
564
565     if (up->releasing) {
566         if (!status) {
567             up->releasing = false;
568         }
569     } else if (up->last_status) {
570         if (!(status & up->last_status)) {
571             /* Button(s) were pushed and released. */
572             if (!up->repeat_count) {
573                 buttons_pushed(up, up->last_status);
574             }
575         } else if (stable_msec >= 150 && !up->repeat_count) {
576             /* Buttons have been stable for a while, so push them once. */
577             buttons_pushed(up, status);
578             up->repeat_count++;
579         } else if (stable_msec >= 1000) {
580             /* Autorepeat 10/second after 1 second hold time. */
581             int n = (stable_msec - 1000) / 100 + 1;
582             while (up->repeat_count < n) {
583                 buttons_pushed(up, status);
584                 up->repeat_count++;
585             }
586         } else if ((status & up->last_status) == up->last_status) {
587             /* More buttons pushed than at last poll. */
588         } else {
589             /* Some, but not all, buttons were released.  Ignore the buttons
590              * until all are released. */
591             up->releasing = true;
592         }
593     }
594     if (!status) {
595         up->repeat_count = 0;
596     }
597     up->last_status = status;
598 }
599
600 static int
601 range(int value, int min, int max)
602 {
603     return value < min ? min : value > max ? max : value;
604 }
605
606 static void
607 send_command(struct updater *up, uint8_t command)
608 {
609     byteq_put(&up->obuf, EZIO_CMD);
610     byteq_put(&up->obuf, command);
611 }
612
613 /* Moves the cursor to 0-based position (x, y).  Updates 'up->visible' to
614  * reflect the change. */
615 static void
616 set_cursor_position(struct updater *up, int x, int y)
617 {
618     int command = EZIO_MOVE_CURSOR;
619     command |= range(x, 0, 39) << EZIO_COL_SHIFT;
620     command |= range(y, 0, 1) << EZIO_ROW_SHIFT;
621     send_command(up, command);
622     up->visible.x = x;
623     up->visible.y = y;
624 }
625
626 /* If any of the icons differ from 'a' to 'b', returns true and sets '*idx' to
627  * the index of the first icon that differs.  Otherwise, returns false.  */
628 static bool
629 icons_differ(const struct ezio *a, const struct ezio *b, int *idx)
630 {
631     int i;
632
633     for (i = 0; i < ARRAY_SIZE(a->icons); i++) {
634         if (memcmp(&a->icons[i], &b->icons[i], sizeof a->icons[i])) {
635             *idx = i;
636             return true;
637         }
638     }
639     return false;
640 }
641
642 /* Queues commands in 'up''s output buffer to update the character at 0-based
643  * position (x,y) to match the character that 'shadow' has there.  Updates
644  * 'up->visible' to reflect the change. */
645 static void
646 update_char(struct updater *up, const struct ezio *shadow, int x, int y)
647 {
648     if (x != up->visible.x || y != up->visible.y) {
649         set_cursor_position(up, x, y);
650     }
651     byteq_put(&up->obuf, shadow->chars[y][x]);
652     up->visible.chars[y][x] = shadow->chars[y][x];
653     up->visible.x++;
654 }
655
656 /* Queues commands in 'up''s output buffer to change the EZIO's cursor shape to
657  * match that in 'shadow'.  Updates 'up->visible' to reflect the change. */
658 static void
659 update_cursor_status(struct updater *up, const struct ezio *shadow)
660 {
661     uint8_t command = EZIO_DISPLAY_MODE | EZIO_ENABLE_DISPLAY;
662     if (shadow->show_cursor) {
663         command |= EZIO_SHOW_CURSOR;
664         if (shadow->blink_cursor) {
665             command |= EZIO_BLOCK_CURSOR;
666         }
667     }
668     send_command(up, command);
669     up->visible.show_cursor = shadow->show_cursor;
670     up->visible.blink_cursor = shadow->blink_cursor;
671 }
672 \f
673 /* An input device, such as a tty. */
674
675 struct inputdev {
676     /* Input. */
677     int fd;                     /* File descriptor. */
678
679     /* State for mirroring the EZIO display to the device. */
680     bool is_tty;                /* We only attempt to mirror to ttys. */
681     struct byteq outq;          /* Output queue. */
682     struct ezio visible;        /* Data that we have displayed. */
683 };
684
685 /* Opens 'name' as a input device.  If successful, returns 0 and stores a
686  * pointer to the input device in '*devp'.  On failure, returns a positive
687  * errno value. */
688 static int
689 inputdev_open(const char *name, struct inputdev **devp)
690 {
691     struct inputdev *dev;
692     int retval;
693     int fd;
694
695     *devp = NULL;
696     if (!strcmp(name, "vt")) {
697         fd = vt_open(O_RDWR | O_NOCTTY);
698         if (fd < 0) {
699             return -fd;
700         }
701     } else if (!strcmp(name, "-")) {
702         fd = dup(STDIN_FILENO);
703         if (fd < 0) {
704             return errno;
705         }
706     } else {
707         fd = open(name, O_RDWR | O_NOCTTY);
708         if (fd < 0) {
709             return errno;
710         }
711     }
712
713     retval = tty_set_raw_mode(fd, B0);
714     if (retval) {
715         close(fd);
716         VLOG_WARN("%s: failed to configure tty parameters: %s",
717                   name, strerror(retval));
718         return retval;
719     }
720
721     dev = xmalloc(sizeof *dev);
722     dev->fd = fd;
723     dev->is_tty = isatty(fd);
724     byteq_init(&dev->outq);
725     ezio_init(&dev->visible);
726     *devp = dev;
727     return 0;
728 }
729
730 /* Closes and destroys input device 'dev'. */
731 static void
732 inputdev_close(struct inputdev *dev)
733 {
734     if (dev) {
735         close(dev->fd);
736         free(dev);
737     }
738 }
739
740 /* Reads input from 'dev' into 'q'.  Returns 0 if successful, otherwise a
741  * positive errno value. */
742 static int
743 inputdev_run(struct inputdev *dev, struct byteq *q)
744 {
745     int retval = byteq_read(q, dev->fd);
746     return retval == EAGAIN ? 0 : retval;
747 }
748
749 /* Dumps data from 'dev''s output queue to the underlying file descriptor,
750  * updating the tty screen display. */
751 static void
752 flush_inputdev(struct inputdev *dev)
753 {
754     int retval = byteq_write(&dev->outq, dev->fd);
755     if (retval && retval != EAGAIN) {
756         VLOG_WARN("error writing input device, "
757                   "disabling further output");
758         dev->is_tty = false;
759     }
760 }
761
762 /* Updates the tty screen display on 'dev' to match 'e'. */
763 static void
764 inputdev_update(struct inputdev *dev, const struct ezio *e)
765 {
766     struct byteq *q = &dev->outq;
767     int x, y;
768
769     if (!dev->is_tty) {
770         return;
771     }
772
773     flush_inputdev(dev);
774     if (!byteq_is_empty(q)) {
775         return;
776     }
777
778     if (!ezio_chars_differ(e, &dev->visible, 0, 40, &x, &y)
779         && e->x == dev->visible.x
780         && e->y == dev->visible.y
781         && e->x_ofs == dev->visible.x_ofs
782         && e->show_cursor == dev->visible.show_cursor) {
783         return;
784     }
785     dev->visible = *e;
786
787     byteq_put_string(q, "\033[H\033[2J"); /* Clear screen. */
788     for (y = 0; y < 4; y++) {
789         byteq_put(q, "+||+"[y]);
790         for (x = 0; x < 40; x++) {
791             int c;
792             if (x == e->x_ofs) {
793                 byteq_put(q, '[');
794             }
795             c = y == 0 || y == 3 ? '-' : e->chars[y - 1][x];
796             if (c == 6) {
797                 c = '\\';
798             } else if (c == 7) {
799                 c = '~';
800             } else if (c < 0x20 || c > 0x7d) {
801                 c = '?';
802             }
803             byteq_put(q, c);
804             if (x == e->x_ofs + 15) {
805                 byteq_put(q, ']');
806             }
807         }
808         byteq_put(q, "+||+"[y]);
809         byteq_put(q, '\r');
810         byteq_put(q, '\n');
811     }
812     if (e->show_cursor) {
813         int x = range(e->x, 0, 39) + 2 + (e->x >= e->x_ofs) + (e->x > e->x_ofs + 15);
814         int y = range(e->y, 0, 1) + 2;
815         char cup[16];
816         sprintf(cup, "\033[%d;%dH", y, x); /* Position cursor. */
817         byteq_put_string(q, cup);
818     }
819     flush_inputdev(dev);
820 }
821
822 /* Calls poll-loop functions that will cause poll_block() to wake up when
823  * inputdev_run() has work to do. */
824 static void
825 inputdev_wait(struct inputdev *dev)
826 {
827     int flags = POLLIN;
828     if (dev->is_tty && !byteq_is_empty(&dev->outq)) {
829         flags |= POLLOUT;
830     }
831     poll_fd_wait(dev->fd, flags);
832 }
833 \f
834 /* Scrolls the display left and right automatically to display all the
835  * content. */
836
837 enum scanner_state {
838     SCANNER_LEFT,               /* Moving left. */
839     SCANNER_RIGHT               /* Moving right. */
840 };
841
842 struct scanner {
843     enum scanner_state state;   /* Current state. */
844     int wait;                   /* No. of cycles to pause before continuing. */
845     long long int last_move;    /* Last time the state machine ran. */
846 };
847
848 static void find_min_max(struct ezio *, int *min, int *max);
849
850 static struct scanner *
851 scanner_create(void)
852 {
853     struct scanner *s = xmalloc(sizeof *s);
854     s->state = SCANNER_RIGHT;
855     s->wait = 0;
856     s->last_move = LLONG_MIN;
857     return s;
858 }
859
860 static void
861 scanner_destroy(struct scanner *s)
862 {
863     free(s);
864 }
865
866 static void
867 scanner_run(struct scanner *s, struct ezio *ezio)
868 {
869     long long int now = time_msec();
870     if (now >= s->last_move + 750) {
871         s->last_move = now;
872         if (s->wait) {
873             s->wait--;
874         } else {
875             int min, max;
876
877             find_min_max(ezio, &min, &max);
878             if (max - min + 1 <= 16) {
879                 ezio->x_ofs = min;
880                 return;
881             }
882
883             switch (s->state) {
884             case SCANNER_RIGHT:
885                 if (ezio->x_ofs + 15 < max) {
886                     ezio->x_ofs++;
887                 } else {
888                     s->state = SCANNER_LEFT;
889                     s->wait = 1;
890                 }
891                 break;
892
893             case SCANNER_LEFT:
894                 if (ezio->x_ofs > min) {
895                     ezio->x_ofs--;
896                 } else {
897                     s->state = SCANNER_RIGHT;
898                     s->wait = 1;
899                 }
900                 break;
901             }
902         }
903     }
904 }
905
906 static void
907 scanner_wait(struct scanner *s)
908 {
909     poll_timer_wait_until(s->last_move + 750);
910 }
911
912 static void
913 scanner_left(struct scanner *s, struct ezio *ezio)
914 {
915     s->wait = 7;
916     if (ezio->x_ofs > 0) {
917         ezio->x_ofs--;
918     }
919 }
920
921 static void
922 scanner_right(struct scanner *s, struct ezio *ezio)
923 {
924     s->wait = 7;
925     if (ezio->x_ofs < 40 - 16) {
926         ezio->x_ofs++;
927     }
928 }
929
930 static void
931 find_min_max(struct ezio *ezio, int *min, int *max)
932 {
933     int x;
934
935     *min = 0;
936     for (x = 0; x < 40; x++) {
937         if (ezio->chars[0][x] != ' ' || ezio->chars[1][x] != ' ') {
938             *min = x;
939             break;
940         }
941     }
942
943     *max = 15;
944     for (x = 39; x >= 0; x--) {
945         if (ezio->chars[0][x] != ' ' || ezio->chars[1][x] != ' ') {
946             *max = x;
947             break;
948         }
949     }
950
951     if (ezio->show_cursor) {
952         if (ezio->x < *min) {
953             *min = ezio->x;
954         }
955         if (ezio->x > *max) {
956             *max = ezio->x;
957         }
958     }
959 }
960 \f
961 static void
962 parse_options(int argc, char *argv[])
963 {
964     enum {
965         OPT_DUMMY = UCHAR_MAX + 1,
966         VLOG_OPTION_ENUMS
967     };
968     static struct option long_options[] = {
969         {"ezio3", required_argument, 0, 'e'},
970         {"input", required_argument, 0, 'i'},
971         {"verbose", optional_argument, 0, 'v'},
972         {"help", no_argument, 0, 'h'},
973         {"version", no_argument, 0, 'V'},
974         DAEMON_LONG_OPTIONS,
975         VLOG_LONG_OPTIONS,
976         {0, 0, 0, 0},
977     };
978     char *short_options = long_options_to_short_options(long_options);
979
980     for (;;) {
981         int c;
982
983         c = getopt_long(argc, argv, short_options, long_options, NULL);
984         if (c == -1) {
985             break;
986         }
987
988         switch (c) {
989         case 'e':
990             ezio_dev = optarg;
991             break;
992
993         case 'i':
994             input_dev = optarg ? optarg : "-";
995             break;
996
997         case 'h':
998             usage();
999
1000         case 'V':
1001             OVS_PRINT_VERSION(0, 0);
1002             exit(EXIT_SUCCESS);
1003
1004         DAEMON_OPTION_HANDLERS
1005         VLOG_OPTION_HANDLERS
1006
1007         case '?':
1008             exit(EXIT_FAILURE);
1009
1010         default:
1011             abort();
1012         }
1013     }
1014     free(short_options);
1015 }
1016
1017 static void
1018 usage(void)
1019 {
1020     printf("%s: EZIO3 terminal front-end\n"
1021            "Provides a front-end to a 16x2 EZIO3 LCD display that makes\n"
1022            "it look more like a conventional terminal\n"
1023            "usage: %s [OPTIONS] [-- COMMAND [ARG...]]\n"
1024            "where COMMAND is a command to run with stdin, stdout, and\n"
1025            "stderr directed to the EZIO3 display.\n"
1026            "\nSettings (defaults in parentheses):\n"
1027            "  -e, --ezio=TTY         set EZIO3 serial device (/dev/ttyS1)\n"
1028            "  -i, --input=TERMINAL   also read input from TERMINAL;\n"
1029            "                         specify - for stdin, or vt to allocate\n"
1030            "                         and switch to a free virtual terminal\n"
1031            "\nOther options:\n"
1032            "  -v, --verbose=MODULE:FACILITY:LEVEL  configure logging levels\n"
1033            "  -v, --verbose               set maximum verbosity level\n"
1034            "  -h, --help             display this help message\n"
1035            "  -V, --version          display version information\n",
1036            program_name, program_name);
1037     exit(EXIT_SUCCESS);
1038 }