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