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