1 /* Copyright (c) 2008, 2009 Nicira Networks, Inc.
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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
29 #include "command-line.h"
30 #include "extras/ezio/byteq.h"
31 #include "extras/ezio/tty.h"
32 #include "extras/ezio/vt.h"
35 #include "poll-loop.h"
36 #include "socket-util.h"
41 #define THIS_MODULE VLM_ezio_term
44 /* EZIO button status. */
52 /* -e, --ezio: EZIO3 serial device file. */
53 static char *ezio_dev = "/dev/ttyS1";
55 /* -i, --input: Terminal from which to accept additional keyboard input. */
56 static char *input_dev = NULL;
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 *);
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 *);
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,
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 *);
80 static void handle_buttons(struct updater *, struct scanner *,
81 struct byteq *, struct ezio *);
83 static void usage(void) NO_RETURN;
84 static void parse_options(int argc, char *argv[]);
87 main(int argc, char *argv[])
89 struct terminal *terminal;
90 struct updater *updater;
91 struct scanner *scanner;
92 struct inputdev *inputdev;
95 int ezio_fd, pty_fd, dummy_fd;
99 set_program_name(argv[0]);
102 parse_options(argc, argv);
103 signal(SIGPIPE, SIG_IGN);
108 /* Make sure that the ezio3 terminfo entry is available. */
109 dummy_fd = open("/dev/null", O_RDWR);
111 if (setupterm("ezio3", dummy_fd, &retval) == ERR) {
113 ovs_fatal(0, "Missing terminfo entry for ezio3. "
114 "Did you run \"make install\"?");
116 ovs_fatal(0, "Missing terminfo database. Is ncurses "
117 "properly installed?");
120 del_curterm(cur_term);
123 ovs_error(errno, "failed to open /dev/null");
126 /* Lock serial port. */
127 retval = tty_lock(ezio_dev);
129 ovs_fatal(retval, "%s: lock failed", ezio_dev);
132 /* Open EZIO and configure as 2400 bps, N-8-1, in raw mode. */
133 ezio_fd = open(ezio_dev, O_RDWR | O_NOCTTY);
135 ovs_fatal(errno, "%s: open", ezio_dev);
137 retval = tty_set_raw_mode(ezio_fd, B2400);
139 ovs_fatal(retval, "%s: failed to configure tty parameters", ezio_dev);
142 /* Open keyboard device for input. */
144 retval = inputdev_open(input_dev, &inputdev);
146 ovs_fatal(retval, "%s: failed to open input device", input_dev);
152 /* Open pty master. */
153 pty_fd = tty_open_master_pty();
155 ovs_fatal(-pty_fd, "failed to open master pty");
157 tty_set_window_size(pty_fd, 2, 40);
159 /* Start child process. */
163 child_argv[0] = getenv("SHELL");
164 if (!child_argv[0]) {
165 child_argv[0] = "/bin/sh";
167 child_argv[1] = NULL;
168 retval = tty_fork_child(pty_fd, child_argv);
170 retval = tty_fork_child(pty_fd, argv);
173 ovs_fatal(retval, "failed to fork child process");
176 die_if_already_running();
179 terminal = terminal_create();
180 updater = updater_create();
181 scanner = scanner_create();
183 for (i = 0; i < 8; i++) {
184 ezio_set_default_icon(&ezio, i);
188 /* Get button presses and keyboard input into inputq, then push the
189 * inputq to the pty. */
190 handle_buttons(updater, scanner, &inputq, &ezio);
192 retval = inputdev_run(inputdev, &inputq);
194 VLOG_ERR("error reading from input device: %s",
196 inputdev_close(inputdev);
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));
206 /* Process data from pty in terminal emulator. */
207 retval = terminal_run(terminal, &ezio, pty_fd);
209 VLOG_ERR("error reading from terminal: %s",
210 retval == EOF ? "end of file" : strerror(retval));
214 /* Scroll left and right through text. */
215 scanner_run(scanner, &ezio);
217 /* Update the display to match what should be shown. */
218 retval = updater_run(updater, &ezio, ezio_fd);
220 VLOG_ERR("error writing to ezio: %s",
221 retval == EOF ? "end of file" : strerror(retval));
225 inputdev_update(inputdev, &ezio);
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();
234 updater_wait(updater, ezio_fd);
235 if (!byteq_is_empty(&inputq)) {
236 poll_fd_wait(pty_fd, POLLOUT);
239 inputdev_wait(inputdev);
243 terminal_destroy(terminal);
244 updater_destroy(updater);
245 scanner_destroy(scanner);
251 send_keys(struct byteq *q, const char *s)
253 size_t n = strlen(s);
254 if (byteq_avail(q) >= n) {
260 handle_buttons(struct updater *up, struct scanner *s,
261 struct byteq *q, struct ezio *ezio)
263 while (updater_has_buttons(up)) {
264 int btns = updater_get_buttons(up);
267 send_keys(q, "\x1b\x5b\x41"); /* Up arrow. */
270 case BTN_UP | BTN_ESC:
271 send_keys(q, "\x1b[5~"); /* Page up. */
275 send_keys(q, "\x1b\x5b\x42"); /* Down arrow. */
278 case BTN_DOWN | BTN_ESC:
279 send_keys(q, "\x1b[6~"); /* Page down. */
287 send_keys(q, "\x7f");
290 case BTN_UP | BTN_DOWN:
291 scanner_left(s, ezio);
294 case BTN_ESC | BTN_ENTER:
295 scanner_right(s, ezio);
298 case BTN_UP | BTN_DOWN | BTN_ENTER | BTN_ESC:
299 send_keys(q, "\x04"); /* End of file. */
302 case BTN_UP | BTN_ENTER | BTN_ESC:
306 case BTN_DOWN | BTN_ENTER | BTN_ESC:
313 /* EZIO screen updater. */
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. */
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). */
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). */
330 #define EZIO_INIT 0x28 /* Initialize EZIO. */
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). */
336 #define EZIO_DEFINE_ICON 0x40 /* Define icon. */
337 #define EZIO_ICON_SHIFT 3 /* Shift count for icon number (0-7). */
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. */
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
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. */
358 /* Current state of EZIO device. */
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? */
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? */
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 *);
385 /* Creates and returns a new updater. */
386 static struct updater *
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;
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);
406 /* Destroys updater 'up. */
408 updater_destroy(struct updater *up)
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.
418 * Returns 0 if successful, otherwise a positive errno value. */
420 updater_run(struct updater *up, const struct ezio *shadow, int ezio_fd)
423 while (read(ezio_fd, &c, 1) > 0) {
424 if ((c & 0xf0) == 0xb0) {
425 recv_button_state(up, ~c & 0x0f);
429 up->up_to_date = false;
431 struct ezio *visible = &up->visible;
435 /* Flush the buffer out to the EZIO device. */
436 retval = byteq_write(&up->obuf, ezio_fd);
437 if (retval == EAGAIN) {
440 VLOG_WARN("error writing ezio: %s", strerror(retval));
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;
450 if (up->tokens > UP_BUCKET_SIZE) {
451 up->tokens = UP_BUCKET_SIZE;
454 if (up->tokens <= 0) {
455 /* Still out of tokens. */
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);
480 send_command(up, EZIO_SCROLL_RIGHT);
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);
501 /* We're fully up-to-date. */
502 up->up_to_date = true;
505 up->tokens -= UP_TOKENS_PER_BYTE * byteq_used(&up->obuf);
509 /* Calls poll-loop functions that will cause poll_block() to wake up when
510 * updater_run() has work to do. */
512 updater_wait(struct updater *up, int ezio_fd)
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();
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);
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. */
534 updater_get_buttons(struct updater *up)
536 return byteq_get(&up->ibuf);
539 /* Any buttons pushed? */
541 updater_has_buttons(const struct updater *up)
543 return !byteq_is_empty(&up->ibuf);
546 /* Adds 'btns' to the queue of pushed buttons */
548 buttons_pushed(struct updater *up, enum btn_status btns)
550 if (!byteq_is_full(&up->ibuf)) {
551 byteq_put(&up->ibuf, btns);
555 /* Updates the buttons-pushed queue based on the current button 'status'. */
557 recv_button_state(struct updater *up, enum btn_status status)
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();
565 stable_msec = time_msec() - up->last_change;
570 up->releasing = false;
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);
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);
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);
589 } else if ((status & up->last_status) == up->last_status) {
590 /* More buttons pushed than at last poll. */
592 /* Some, but not all, buttons were released. Ignore the buttons
593 * until all are released. */
594 up->releasing = true;
598 up->repeat_count = 0;
600 up->last_status = status;
604 range(int value, int min, int max)
606 return value < min ? min : value > max ? max : value;
610 send_command(struct updater *up, uint8_t command)
612 byteq_put(&up->obuf, EZIO_CMD);
613 byteq_put(&up->obuf, command);
616 /* Moves the cursor to 0-based position (x, y). Updates 'up->visible' to
617 * reflect the change. */
619 set_cursor_position(struct updater *up, int x, int y)
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);
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. */
632 icons_differ(const struct ezio *a, const struct ezio *b, int *idx)
636 for (i = 0; i < ARRAY_SIZE(a->icons); i++) {
637 if (memcmp(&a->icons[i], &b->icons[i], sizeof a->icons[i])) {
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. */
649 update_char(struct updater *up, const struct ezio *shadow, int x, int y)
651 if (x != up->visible.x || y != up->visible.y) {
652 set_cursor_position(up, x, y);
654 byteq_put(&up->obuf, shadow->chars[y][x]);
655 up->visible.chars[y][x] = shadow->chars[y][x];
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. */
662 update_cursor_status(struct updater *up, const struct ezio *shadow)
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;
671 send_command(up, command);
672 up->visible.show_cursor = shadow->show_cursor;
673 up->visible.blink_cursor = shadow->blink_cursor;
676 /* An input device, such as a tty. */
680 int fd; /* File descriptor. */
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. */
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
692 inputdev_open(const char *name, struct inputdev **devp)
694 struct inputdev *dev;
699 if (!strcmp(name, "vt")) {
700 fd = vt_open(O_RDWR | O_NOCTTY);
704 } else if (!strcmp(name, "-")) {
705 fd = dup(STDIN_FILENO);
710 fd = open(name, O_RDWR | O_NOCTTY);
716 retval = tty_set_raw_mode(fd, B0);
719 VLOG_WARN("%s: failed to configure tty parameters: %s",
720 name, strerror(retval));
724 dev = xmalloc(sizeof *dev);
726 dev->is_tty = isatty(fd);
727 byteq_init(&dev->outq);
728 ezio_init(&dev->visible);
733 /* Closes and destroys input device 'dev'. */
735 inputdev_close(struct inputdev *dev)
743 /* Reads input from 'dev' into 'q'. Returns 0 if successful, otherwise a
744 * positive errno value. */
746 inputdev_run(struct inputdev *dev, struct byteq *q)
748 int retval = byteq_read(q, dev->fd);
749 return retval == EAGAIN ? 0 : retval;
752 /* Dumps data from 'dev''s output queue to the underlying file descriptor,
753 * updating the tty screen display. */
755 flush_inputdev(struct inputdev *dev)
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");
765 /* Updates the tty screen display on 'dev' to match 'e'. */
767 inputdev_update(struct inputdev *dev, const struct ezio *e)
769 struct byteq *q = &dev->outq;
777 if (!byteq_is_empty(q)) {
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) {
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++) {
798 c = y == 0 || y == 3 ? '-' : e->chars[y - 1][x];
803 } else if (c < 0x20 || c > 0x7d) {
807 if (x == e->x_ofs + 15) {
811 byteq_put(q, "+||+"[y]);
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;
819 sprintf(cup, "\033[%d;%dH", y, x); /* Position cursor. */
820 byteq_put_string(q, cup);
825 /* Calls poll-loop functions that will cause poll_block() to wake up when
826 * inputdev_run() has work to do. */
828 inputdev_wait(struct inputdev *dev)
831 if (dev->is_tty && !byteq_is_empty(&dev->outq)) {
834 poll_fd_wait(dev->fd, flags);
837 /* Scrolls the display left and right automatically to display all the
841 SCANNER_LEFT, /* Moving left. */
842 SCANNER_RIGHT /* Moving right. */
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. */
851 static void find_min_max(struct ezio *, int *min, int *max);
853 static struct scanner *
856 struct scanner *s = xmalloc(sizeof *s);
857 s->state = SCANNER_RIGHT;
859 s->last_move = LLONG_MIN;
864 scanner_destroy(struct scanner *s)
870 scanner_run(struct scanner *s, struct ezio *ezio)
872 long long int now = time_msec();
873 if (now >= s->last_move + 750) {
880 find_min_max(ezio, &min, &max);
881 if (max - min + 1 <= 16) {
888 if (ezio->x_ofs + 15 < max) {
891 s->state = SCANNER_LEFT;
897 if (ezio->x_ofs > min) {
900 s->state = SCANNER_RIGHT;
910 scanner_wait(struct scanner *s)
912 long long int now = time_msec();
913 long long int expires = s->last_move + 750;
914 if (now >= expires) {
915 poll_immediate_wake();
917 poll_timer_wait(expires - now);
923 scanner_left(struct scanner *s, struct ezio *ezio)
926 if (ezio->x_ofs > 0) {
932 scanner_right(struct scanner *s, struct ezio *ezio)
935 if (ezio->x_ofs < 40 - 16) {
941 find_min_max(struct ezio *ezio, int *min, int *max)
946 for (x = 0; x < 40; x++) {
947 if (ezio->chars[0][x] != ' ' || ezio->chars[1][x] != ' ') {
954 for (x = 39; x >= 0; x--) {
955 if (ezio->chars[0][x] != ' ' || ezio->chars[1][x] != ' ') {
961 if (ezio->show_cursor) {
962 if (ezio->x < *min) {
965 if (ezio->x > *max) {
972 parse_options(int argc, char *argv[])
975 OPT_DUMMY = UCHAR_MAX + 1,
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'},
988 char *short_options = long_options_to_short_options(long_options);
993 c = getopt_long(argc, argv, short_options, long_options, NULL);
1004 input_dev = optarg ? optarg : "-";
1011 OVS_PRINT_VERSION(0, 0);
1014 DAEMON_OPTION_HANDLERS
1015 VLOG_OPTION_HANDLERS
1024 free(short_options);
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);