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