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