1 /* Copyright (c) 2008, 2009 Nicira Networks, Inc.
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.
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.
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/>.
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.
41 #include "command-line.h"
42 #include "extras/ezio/byteq.h"
43 #include "extras/ezio/tty.h"
44 #include "extras/ezio/vt.h"
47 #include "poll-loop.h"
48 #include "socket-util.h"
53 #define THIS_MODULE VLM_ezio_term
56 /* EZIO button status. */
64 /* -e, --ezio: EZIO3 serial device file. */
65 static char *ezio_dev = "/dev/ttyS1";
67 /* -i, --input: Terminal from which to accept additional keyboard input. */
68 static char *input_dev = NULL;
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 *);
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 *);
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,
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 *);
92 static void handle_buttons(struct updater *, struct scanner *,
93 struct byteq *, struct ezio *);
95 static void usage(void) NO_RETURN;
96 static void parse_options(int argc, char *argv[]);
99 main(int argc, char *argv[])
101 struct terminal *terminal;
102 struct updater *updater;
103 struct scanner *scanner;
104 struct inputdev *inputdev;
107 int ezio_fd, pty_fd, dummy_fd;
111 set_program_name(argv[0]);
114 parse_options(argc, argv);
115 signal(SIGPIPE, SIG_IGN);
120 /* Make sure that the ezio3 terminfo entry is available. */
121 dummy_fd = open("/dev/null", O_RDWR);
123 if (setupterm("ezio3", dummy_fd, &retval) == ERR) {
125 ovs_fatal(0, "Missing terminfo entry for ezio3. "
126 "Did you run \"make install\"?");
128 ovs_fatal(0, "Missing terminfo database. Is ncurses "
129 "properly installed?");
132 del_curterm(cur_term);
135 ovs_error(errno, "failed to open /dev/null");
138 /* Lock serial port. */
139 retval = tty_lock(ezio_dev);
141 ovs_fatal(retval, "%s: lock failed", ezio_dev);
144 /* Open EZIO and configure as 2400 bps, N-8-1, in raw mode. */
145 ezio_fd = open(ezio_dev, O_RDWR | O_NOCTTY);
147 ovs_fatal(errno, "%s: open", ezio_dev);
149 retval = tty_set_raw_mode(ezio_fd, B2400);
151 ovs_fatal(retval, "%s: failed to configure tty parameters", ezio_dev);
154 /* Open keyboard device for input. */
156 retval = inputdev_open(input_dev, &inputdev);
158 ovs_fatal(retval, "%s: failed to open input device", input_dev);
164 /* Open pty master. */
165 pty_fd = tty_open_master_pty();
167 ovs_fatal(-pty_fd, "failed to open master pty");
169 tty_set_window_size(pty_fd, 2, 40);
171 /* Start child process. */
175 child_argv[0] = getenv("SHELL");
176 if (!child_argv[0]) {
177 child_argv[0] = "/bin/sh";
179 child_argv[1] = NULL;
180 retval = tty_fork_child(pty_fd, child_argv);
182 retval = tty_fork_child(pty_fd, argv);
185 ovs_fatal(retval, "failed to fork child process");
188 die_if_already_running();
191 terminal = terminal_create();
192 updater = updater_create();
193 scanner = scanner_create();
195 for (i = 0; i < 8; i++) {
196 ezio_set_default_icon(&ezio, i);
200 /* Get button presses and keyboard input into inputq, then push the
201 * inputq to the pty. */
202 handle_buttons(updater, scanner, &inputq, &ezio);
204 retval = inputdev_run(inputdev, &inputq);
206 VLOG_ERR("error reading from input device: %s",
208 inputdev_close(inputdev);
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));
218 /* Process data from pty in terminal emulator. */
219 retval = terminal_run(terminal, &ezio, pty_fd);
221 VLOG_ERR("error reading from terminal: %s",
222 retval == EOF ? "end of file" : strerror(retval));
226 /* Scroll left and right through text. */
227 scanner_run(scanner, &ezio);
229 /* Update the display to match what should be shown. */
230 retval = updater_run(updater, &ezio, ezio_fd);
232 VLOG_ERR("error writing to ezio: %s",
233 retval == EOF ? "end of file" : strerror(retval));
237 inputdev_update(inputdev, &ezio);
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();
246 updater_wait(updater, ezio_fd);
247 if (!byteq_is_empty(&inputq)) {
248 poll_fd_wait(pty_fd, POLLOUT);
251 inputdev_wait(inputdev);
255 terminal_destroy(terminal);
256 updater_destroy(updater);
257 scanner_destroy(scanner);
263 send_keys(struct byteq *q, const char *s)
265 size_t n = strlen(s);
266 if (byteq_avail(q) >= n) {
272 handle_buttons(struct updater *up, struct scanner *s,
273 struct byteq *q, struct ezio *ezio)
275 while (updater_has_buttons(up)) {
276 int btns = updater_get_buttons(up);
279 send_keys(q, "\x1b\x5b\x41"); /* Up arrow. */
282 case BTN_UP | BTN_ESC:
283 send_keys(q, "\x1b[5~"); /* Page up. */
287 send_keys(q, "\x1b\x5b\x42"); /* Down arrow. */
290 case BTN_DOWN | BTN_ESC:
291 send_keys(q, "\x1b[6~"); /* Page down. */
299 send_keys(q, "\x7f");
302 case BTN_UP | BTN_DOWN:
303 scanner_left(s, ezio);
306 case BTN_ESC | BTN_ENTER:
307 scanner_right(s, ezio);
310 case BTN_UP | BTN_DOWN | BTN_ENTER | BTN_ESC:
311 send_keys(q, "\x04"); /* End of file. */
314 case BTN_UP | BTN_ENTER | BTN_ESC:
318 case BTN_DOWN | BTN_ENTER | BTN_ESC:
325 /* EZIO screen updater. */
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. */
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). */
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). */
342 #define EZIO_INIT 0x28 /* Initialize EZIO. */
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). */
348 #define EZIO_DEFINE_ICON 0x40 /* Define icon. */
349 #define EZIO_ICON_SHIFT 3 /* Shift count for icon number (0-7). */
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. */
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
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. */
370 /* Current state of EZIO device. */
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? */
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? */
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 *);
397 /* Creates and returns a new updater. */
398 static struct updater *
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;
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);
418 /* Destroys updater 'up. */
420 updater_destroy(struct updater *up)
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.
430 * Returns 0 if successful, otherwise a positive errno value. */
432 updater_run(struct updater *up, const struct ezio *shadow, int ezio_fd)
435 while (read(ezio_fd, &c, 1) > 0) {
436 if ((c & 0xf0) == 0xb0) {
437 recv_button_state(up, ~c & 0x0f);
441 up->up_to_date = false;
443 struct ezio *visible = &up->visible;
447 /* Flush the buffer out to the EZIO device. */
448 retval = byteq_write(&up->obuf, ezio_fd);
449 if (retval == EAGAIN) {
452 VLOG_WARN("error writing ezio: %s", strerror(retval));
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;
462 if (up->tokens > UP_BUCKET_SIZE) {
463 up->tokens = UP_BUCKET_SIZE;
466 if (up->tokens <= 0) {
467 /* Still out of tokens. */
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);
492 send_command(up, EZIO_SCROLL_RIGHT);
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);
513 /* We're fully up-to-date. */
514 up->up_to_date = true;
517 up->tokens -= UP_TOKENS_PER_BYTE * byteq_used(&up->obuf);
521 /* Calls poll-loop functions that will cause poll_block() to wake up when
522 * updater_run() has work to do. */
524 updater_wait(struct updater *up, int ezio_fd)
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();
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);
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. */
546 updater_get_buttons(struct updater *up)
548 return byteq_get(&up->ibuf);
551 /* Any buttons pushed? */
553 updater_has_buttons(const struct updater *up)
555 return !byteq_is_empty(&up->ibuf);
558 /* Adds 'btns' to the queue of pushed buttons */
560 buttons_pushed(struct updater *up, enum btn_status btns)
562 if (!byteq_is_full(&up->ibuf)) {
563 byteq_put(&up->ibuf, btns);
567 /* Updates the buttons-pushed queue based on the current button 'status'. */
569 recv_button_state(struct updater *up, enum btn_status status)
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();
577 stable_msec = time_msec() - up->last_change;
582 up->releasing = false;
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);
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);
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);
601 } else if ((status & up->last_status) == up->last_status) {
602 /* More buttons pushed than at last poll. */
604 /* Some, but not all, buttons were released. Ignore the buttons
605 * until all are released. */
606 up->releasing = true;
610 up->repeat_count = 0;
612 up->last_status = status;
616 range(int value, int min, int max)
618 return value < min ? min : value > max ? max : value;
622 send_command(struct updater *up, uint8_t command)
624 byteq_put(&up->obuf, EZIO_CMD);
625 byteq_put(&up->obuf, command);
628 /* Moves the cursor to 0-based position (x, y). Updates 'up->visible' to
629 * reflect the change. */
631 set_cursor_position(struct updater *up, int x, int y)
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);
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. */
644 icons_differ(const struct ezio *a, const struct ezio *b, int *idx)
648 for (i = 0; i < ARRAY_SIZE(a->icons); i++) {
649 if (memcmp(&a->icons[i], &b->icons[i], sizeof a->icons[i])) {
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. */
661 update_char(struct updater *up, const struct ezio *shadow, int x, int y)
663 if (x != up->visible.x || y != up->visible.y) {
664 set_cursor_position(up, x, y);
666 byteq_put(&up->obuf, shadow->chars[y][x]);
667 up->visible.chars[y][x] = shadow->chars[y][x];
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. */
674 update_cursor_status(struct updater *up, const struct ezio *shadow)
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;
683 send_command(up, command);
684 up->visible.show_cursor = shadow->show_cursor;
685 up->visible.blink_cursor = shadow->blink_cursor;
688 /* An input device, such as a tty. */
692 int fd; /* File descriptor. */
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. */
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
704 inputdev_open(const char *name, struct inputdev **devp)
706 struct inputdev *dev;
711 if (!strcmp(name, "vt")) {
712 fd = vt_open(O_RDWR | O_NOCTTY);
716 } else if (!strcmp(name, "-")) {
717 fd = dup(STDIN_FILENO);
722 fd = open(name, O_RDWR | O_NOCTTY);
728 retval = tty_set_raw_mode(fd, B0);
731 VLOG_WARN("%s: failed to configure tty parameters: %s",
732 name, strerror(retval));
736 dev = xmalloc(sizeof *dev);
738 dev->is_tty = isatty(fd);
739 byteq_init(&dev->outq);
740 ezio_init(&dev->visible);
745 /* Closes and destroys input device 'dev'. */
747 inputdev_close(struct inputdev *dev)
755 /* Reads input from 'dev' into 'q'. Returns 0 if successful, otherwise a
756 * positive errno value. */
758 inputdev_run(struct inputdev *dev, struct byteq *q)
760 int retval = byteq_read(q, dev->fd);
761 return retval == EAGAIN ? 0 : retval;
764 /* Dumps data from 'dev''s output queue to the underlying file descriptor,
765 * updating the tty screen display. */
767 flush_inputdev(struct inputdev *dev)
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");
777 /* Updates the tty screen display on 'dev' to match 'e'. */
779 inputdev_update(struct inputdev *dev, const struct ezio *e)
781 struct byteq *q = &dev->outq;
789 if (!byteq_is_empty(q)) {
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) {
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++) {
810 c = y == 0 || y == 3 ? '-' : e->chars[y - 1][x];
815 } else if (c < 0x20 || c > 0x7d) {
819 if (x == e->x_ofs + 15) {
823 byteq_put(q, "+||+"[y]);
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;
831 sprintf(cup, "\033[%d;%dH", y, x); /* Position cursor. */
832 byteq_put_string(q, cup);
837 /* Calls poll-loop functions that will cause poll_block() to wake up when
838 * inputdev_run() has work to do. */
840 inputdev_wait(struct inputdev *dev)
843 if (dev->is_tty && !byteq_is_empty(&dev->outq)) {
846 poll_fd_wait(dev->fd, flags);
849 /* Scrolls the display left and right automatically to display all the
853 SCANNER_LEFT, /* Moving left. */
854 SCANNER_RIGHT /* Moving right. */
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. */
863 static void find_min_max(struct ezio *, int *min, int *max);
865 static struct scanner *
868 struct scanner *s = xmalloc(sizeof *s);
869 s->state = SCANNER_RIGHT;
871 s->last_move = LLONG_MIN;
876 scanner_destroy(struct scanner *s)
882 scanner_run(struct scanner *s, struct ezio *ezio)
884 long long int now = time_msec();
885 if (now >= s->last_move + 750) {
892 find_min_max(ezio, &min, &max);
893 if (max - min + 1 <= 16) {
900 if (ezio->x_ofs + 15 < max) {
903 s->state = SCANNER_LEFT;
909 if (ezio->x_ofs > min) {
912 s->state = SCANNER_RIGHT;
922 scanner_wait(struct scanner *s)
924 long long int now = time_msec();
925 long long int expires = s->last_move + 750;
926 if (now >= expires) {
927 poll_immediate_wake();
929 poll_timer_wait(expires - now);
935 scanner_left(struct scanner *s, struct ezio *ezio)
938 if (ezio->x_ofs > 0) {
944 scanner_right(struct scanner *s, struct ezio *ezio)
947 if (ezio->x_ofs < 40 - 16) {
953 find_min_max(struct ezio *ezio, int *min, int *max)
958 for (x = 0; x < 40; x++) {
959 if (ezio->chars[0][x] != ' ' || ezio->chars[1][x] != ' ') {
966 for (x = 39; x >= 0; x--) {
967 if (ezio->chars[0][x] != ' ' || ezio->chars[1][x] != ' ') {
973 if (ezio->show_cursor) {
974 if (ezio->x < *min) {
977 if (ezio->x > *max) {
984 parse_options(int argc, char *argv[])
987 OPT_DUMMY = UCHAR_MAX + 1,
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'},
1000 char *short_options = long_options_to_short_options(long_options);
1005 c = getopt_long(argc, argv, short_options, long_options, NULL);
1016 input_dev = optarg ? optarg : "-";
1023 OVS_PRINT_VERSION(0, 0);
1026 DAEMON_OPTION_HANDLERS
1027 VLOG_OPTION_HANDLERS
1036 free(short_options);
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);