Commit 4d3b6f6e126553107a78999bd1070b086ae3c023
1 parent
c0be16d3
Add an ncurses UI.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3976 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
21 changed files
with
1238 additions
and
34 deletions
Makefile
| ... | ... | @@ -99,6 +99,9 @@ OBJS+=$(addprefix audio/, $(AUDIO_OBJS)) |
| 99 | 99 | ifdef CONFIG_SDL |
| 100 | 100 | OBJS+=sdl.o x_keymap.o |
| 101 | 101 | endif |
| 102 | +ifdef CONFIG_CURSES | |
| 103 | +OBJS+=curses.o | |
| 104 | +endif | |
| 102 | 105 | OBJS+=vnc.o d3des.o |
| 103 | 106 | |
| 104 | 107 | ifdef CONFIG_COCOA |
| ... | ... | @@ -122,6 +125,9 @@ sdl.o: sdl.c keymaps.c sdl_keysym.h |
| 122 | 125 | vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h d3des.c d3des.h |
| 123 | 126 | $(CC) $(CFLAGS) $(CPPFLAGS) $(CONFIG_VNC_TLS_CFLAGS) -c -o $@ $< |
| 124 | 127 | |
| 128 | +curses.o: curses.c keymaps.c curses_keys.h | |
| 129 | + $(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $< | |
| 130 | + | |
| 125 | 131 | audio/sdlaudio.o: audio/sdlaudio.c |
| 126 | 132 | $(CC) $(CFLAGS) $(CPPFLAGS) $(SDL_CFLAGS) -c -o $@ $< |
| 127 | 133 | ... | ... |
Makefile.target
| ... | ... | @@ -647,7 +647,7 @@ main.o: CFLAGS+=-p |
| 647 | 647 | endif |
| 648 | 648 | |
| 649 | 649 | $(QEMU_PROG): $(OBJS) ../libqemu_common.a libqemu.a |
| 650 | - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) | |
| 650 | + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(CURSES_LIBS) | |
| 651 | 651 | |
| 652 | 652 | endif # !CONFIG_USER_ONLY |
| 653 | 653 | ... | ... |
configure
| ... | ... | @@ -108,6 +108,7 @@ linux_user="no" |
| 108 | 108 | darwin_user="no" |
| 109 | 109 | build_docs="no" |
| 110 | 110 | uname_release="" |
| 111 | +curses="yes" | |
| 111 | 112 | |
| 112 | 113 | # OS specific |
| 113 | 114 | targetos=`uname -s` |
| ... | ... | @@ -323,6 +324,8 @@ for opt do |
| 323 | 324 | ;; |
| 324 | 325 | --disable-werror) werror="no" |
| 325 | 326 | ;; |
| 327 | + --disable-curses) curses="no" | |
| 328 | + ;; | |
| 326 | 329 | *) echo "ERROR: unknown option $opt"; show_help="yes" |
| 327 | 330 | ;; |
| 328 | 331 | esac |
| ... | ... | @@ -669,6 +672,20 @@ EOF |
| 669 | 672 | fi |
| 670 | 673 | fi |
| 671 | 674 | |
| 675 | +########################################## | |
| 676 | +# curses probe | |
| 677 | + | |
| 678 | +if test "$curses" = "yes" ; then | |
| 679 | + curses=no | |
| 680 | + cat > $TMPC << EOF | |
| 681 | +#include <curses.h> | |
| 682 | +int main(void) { return curses_version(); } | |
| 683 | +EOF | |
| 684 | + if $cc -o $TMPE $TMPC -lcurses 2> /dev/null ; then | |
| 685 | + curses=yes | |
| 686 | + fi | |
| 687 | +fi # test "$curses" | |
| 688 | + | |
| 672 | 689 | # Check if tools are available to build documentation. |
| 673 | 690 | if [ -x "`which texi2html 2>/dev/null`" ] && \ |
| 674 | 691 | [ -x "`which pod2man 2>/dev/null`" ]; then |
| ... | ... | @@ -720,6 +737,7 @@ echo "SDL support $sdl" |
| 720 | 737 | if test "$sdl" != "no" ; then |
| 721 | 738 | echo "SDL static link $sdl_static" |
| 722 | 739 | fi |
| 740 | +echo "curses support $curses" | |
| 723 | 741 | echo "mingw32 support $mingw32" |
| 724 | 742 | echo "Adlib support $adlib" |
| 725 | 743 | echo "AC97 support $ac97" |
| ... | ... | @@ -974,8 +992,13 @@ if test "$sdl1" = "yes" ; then |
| 974 | 992 | fi |
| 975 | 993 | fi |
| 976 | 994 | if test "$cocoa" = "yes" ; then |
| 977 | - echo "#define CONFIG_COCOA 1" >> $config_h | |
| 978 | - echo "CONFIG_COCOA=yes" >> $config_mak | |
| 995 | + echo "#define CONFIG_COCOA 1" >> $config_h | |
| 996 | + echo "CONFIG_COCOA=yes" >> $config_mak | |
| 997 | +fi | |
| 998 | +if test "$curses" = "yes" ; then | |
| 999 | + echo "#define CONFIG_CURSES 1" >> $config_h | |
| 1000 | + echo "CONFIG_CURSES=yes" >> $config_mak | |
| 1001 | + echo "CURSES_LIBS=-lcurses" >> $config_mak | |
| 979 | 1002 | fi |
| 980 | 1003 | |
| 981 | 1004 | # XXX: suppress that |
| ... | ... | @@ -1040,7 +1063,8 @@ if test "$target_user_only" = "no" -a "$check_gfx" = "yes" \ |
| 1040 | 1063 | -a "$sdl" = "no" -a "$cocoa" = "no" ; then |
| 1041 | 1064 | echo "ERROR: QEMU requires SDL or Cocoa for graphical output" |
| 1042 | 1065 | echo "To build QEMU without graphical output configure with --disable-gfx-check" |
| 1043 | - echo "Note that this will disable all output from the virtual graphics card." | |
| 1066 | + echo "Note that this will disable all output from the virtual graphics card" | |
| 1067 | + echo "except through VNC or curses." | |
| 1044 | 1068 | exit 1; |
| 1045 | 1069 | fi |
| 1046 | 1070 | ... | ... |
console.c
| ... | ... | @@ -121,6 +121,7 @@ struct TextConsole { |
| 121 | 121 | vga_hw_update_ptr hw_update; |
| 122 | 122 | vga_hw_invalidate_ptr hw_invalidate; |
| 123 | 123 | vga_hw_screen_dump_ptr hw_screen_dump; |
| 124 | + vga_hw_text_update_ptr hw_text_update; | |
| 124 | 125 | void *hw; |
| 125 | 126 | |
| 126 | 127 | int g_width, g_height; |
| ... | ... | @@ -135,6 +136,7 @@ struct TextConsole { |
| 135 | 136 | TextAttributes t_attrib_default; /* default text attributes */ |
| 136 | 137 | TextAttributes t_attrib; /* currently active text attributes */ |
| 137 | 138 | TextCell *cells; |
| 139 | + int text_x[2], text_y[2], cursor_invalidate; | |
| 138 | 140 | |
| 139 | 141 | enum TTYState state; |
| 140 | 142 | int esc_params[MAX_ESC_PARAMS]; |
| ... | ... | @@ -171,6 +173,12 @@ void vga_hw_screen_dump(const char *filename) |
| 171 | 173 | consoles[0]->hw_screen_dump(consoles[0]->hw, filename); |
| 172 | 174 | } |
| 173 | 175 | |
| 176 | +void vga_hw_text_update(console_ch_t *chardata) | |
| 177 | +{ | |
| 178 | + if (active_console && active_console->hw_text_update) | |
| 179 | + active_console->hw_text_update(active_console->hw, chardata); | |
| 180 | +} | |
| 181 | + | |
| 174 | 182 | /* convert a RGBA color to a color index usable in graphic primitives */ |
| 175 | 183 | static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba) |
| 176 | 184 | { |
| ... | ... | @@ -515,12 +523,25 @@ static void text_console_resize(TextConsole *s) |
| 515 | 523 | s->cells = cells; |
| 516 | 524 | } |
| 517 | 525 | |
| 526 | +static inline void text_update_xy(TextConsole *s, int x, int y) | |
| 527 | +{ | |
| 528 | + s->text_x[0] = MIN(s->text_x[0], x); | |
| 529 | + s->text_x[1] = MAX(s->text_x[1], x); | |
| 530 | + s->text_y[0] = MIN(s->text_y[0], y); | |
| 531 | + s->text_y[1] = MAX(s->text_y[1], y); | |
| 532 | +} | |
| 533 | + | |
| 518 | 534 | static void update_xy(TextConsole *s, int x, int y) |
| 519 | 535 | { |
| 520 | 536 | TextCell *c; |
| 521 | 537 | int y1, y2; |
| 522 | 538 | |
| 523 | 539 | if (s == active_console) { |
| 540 | + if (!s->ds->depth) { | |
| 541 | + text_update_xy(s, x, y); | |
| 542 | + return; | |
| 543 | + } | |
| 544 | + | |
| 524 | 545 | y1 = (s->y_base + y) % s->total_height; |
| 525 | 546 | y2 = y1 - s->y_displayed; |
| 526 | 547 | if (y2 < 0) |
| ... | ... | @@ -542,6 +563,12 @@ static void console_show_cursor(TextConsole *s, int show) |
| 542 | 563 | |
| 543 | 564 | if (s == active_console) { |
| 544 | 565 | int x = s->x; |
| 566 | + | |
| 567 | + if (!s->ds->depth) { | |
| 568 | + s->cursor_invalidate = 1; | |
| 569 | + return; | |
| 570 | + } | |
| 571 | + | |
| 545 | 572 | if (x >= s->width) { |
| 546 | 573 | x = s->width - 1; |
| 547 | 574 | } |
| ... | ... | @@ -571,6 +598,14 @@ static void console_refresh(TextConsole *s) |
| 571 | 598 | |
| 572 | 599 | if (s != active_console) |
| 573 | 600 | return; |
| 601 | + if (!s->ds->depth) { | |
| 602 | + s->text_x[0] = 0; | |
| 603 | + s->text_y[0] = 0; | |
| 604 | + s->text_x[1] = s->width - 1; | |
| 605 | + s->text_y[1] = s->height - 1; | |
| 606 | + s->cursor_invalidate = 1; | |
| 607 | + return; | |
| 608 | + } | |
| 574 | 609 | |
| 575 | 610 | vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height, |
| 576 | 611 | color_table[0][COLOR_BLACK]); |
| ... | ... | @@ -648,6 +683,14 @@ static void console_put_lf(TextConsole *s) |
| 648 | 683 | c++; |
| 649 | 684 | } |
| 650 | 685 | if (s == active_console && s->y_displayed == s->y_base) { |
| 686 | + if (!s->ds->depth) { | |
| 687 | + s->text_x[0] = 0; | |
| 688 | + s->text_y[0] = 0; | |
| 689 | + s->text_x[1] = s->width - 1; | |
| 690 | + s->text_y[1] = s->height - 1; | |
| 691 | + return; | |
| 692 | + } | |
| 693 | + | |
| 651 | 694 | vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0, |
| 652 | 695 | s->width * FONT_WIDTH, |
| 653 | 696 | (s->height - 1) * FONT_HEIGHT); |
| ... | ... | @@ -998,21 +1041,7 @@ void console_select(unsigned int index) |
| 998 | 1041 | s = consoles[index]; |
| 999 | 1042 | if (s) { |
| 1000 | 1043 | active_console = s; |
| 1001 | - if (s->console_type != GRAPHIC_CONSOLE) { | |
| 1002 | - if (s->g_width != s->ds->width || | |
| 1003 | - s->g_height != s->ds->height) { | |
| 1004 | - if (s->console_type == TEXT_CONSOLE_FIXED_SIZE) { | |
| 1005 | - dpy_resize(s->ds, s->g_width, s->g_height); | |
| 1006 | - } else { | |
| 1007 | - s->g_width = s->ds->width; | |
| 1008 | - s->g_height = s->ds->height; | |
| 1009 | - text_console_resize(s); | |
| 1010 | - } | |
| 1011 | - } | |
| 1012 | - console_refresh(s); | |
| 1013 | - } else { | |
| 1014 | - vga_hw_invalidate(); | |
| 1015 | - } | |
| 1044 | + vga_hw_invalidate(); | |
| 1016 | 1045 | } |
| 1017 | 1046 | } |
| 1018 | 1047 | |
| ... | ... | @@ -1116,6 +1145,52 @@ void kbd_put_keysym(int keysym) |
| 1116 | 1145 | } |
| 1117 | 1146 | } |
| 1118 | 1147 | |
| 1148 | +static void text_console_invalidate(void *opaque) | |
| 1149 | +{ | |
| 1150 | + TextConsole *s = (TextConsole *) opaque; | |
| 1151 | + | |
| 1152 | + if (s->console_type != GRAPHIC_CONSOLE) { | |
| 1153 | + if (s->g_width != s->ds->width || | |
| 1154 | + s->g_height != s->ds->height) { | |
| 1155 | + if (s->console_type == TEXT_CONSOLE_FIXED_SIZE) | |
| 1156 | + dpy_resize(s->ds, s->g_width, s->g_height); | |
| 1157 | + else { | |
| 1158 | + s->g_width = s->ds->width; | |
| 1159 | + s->g_height = s->ds->height; | |
| 1160 | + text_console_resize(s); | |
| 1161 | + } | |
| 1162 | + } | |
| 1163 | + } | |
| 1164 | + console_refresh(s); | |
| 1165 | +} | |
| 1166 | + | |
| 1167 | +static void text_console_update(void *opaque, console_ch_t *chardata) | |
| 1168 | +{ | |
| 1169 | + TextConsole *s = (TextConsole *) opaque; | |
| 1170 | + int i, j, src; | |
| 1171 | + | |
| 1172 | + if (s->text_x[0] <= s->text_x[1]) { | |
| 1173 | + src = (s->y_base + s->text_y[0]) * s->width; | |
| 1174 | + chardata += s->text_y[0] * s->width; | |
| 1175 | + for (i = s->text_y[0]; i <= s->text_y[1]; i ++) | |
| 1176 | + for (j = 0; j < s->width; j ++, src ++) | |
| 1177 | + console_write_ch(chardata ++, s->cells[src].ch | | |
| 1178 | + (s->cells[src].t_attrib.fgcol << 12) | | |
| 1179 | + (s->cells[src].t_attrib.bgcol << 8) | | |
| 1180 | + (s->cells[src].t_attrib.bold << 21)); | |
| 1181 | + dpy_update(s->ds, s->text_x[0], s->text_y[0], | |
| 1182 | + s->text_x[1] - s->text_x[0], i - s->text_y[0]); | |
| 1183 | + s->text_x[0] = s->width; | |
| 1184 | + s->text_y[0] = s->height; | |
| 1185 | + s->text_x[1] = 0; | |
| 1186 | + s->text_y[1] = 0; | |
| 1187 | + } | |
| 1188 | + if (s->cursor_invalidate) { | |
| 1189 | + dpy_cursor(s->ds, s->x, s->y); | |
| 1190 | + s->cursor_invalidate = 0; | |
| 1191 | + } | |
| 1192 | +} | |
| 1193 | + | |
| 1119 | 1194 | static TextConsole *new_console(DisplayState *ds, console_type_t console_type) |
| 1120 | 1195 | { |
| 1121 | 1196 | TextConsole *s; |
| ... | ... | @@ -1150,6 +1225,7 @@ static TextConsole *new_console(DisplayState *ds, console_type_t console_type) |
| 1150 | 1225 | TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update, |
| 1151 | 1226 | vga_hw_invalidate_ptr invalidate, |
| 1152 | 1227 | vga_hw_screen_dump_ptr screen_dump, |
| 1228 | + vga_hw_text_update_ptr text_update, | |
| 1153 | 1229 | void *opaque) |
| 1154 | 1230 | { |
| 1155 | 1231 | TextConsole *s; |
| ... | ... | @@ -1160,13 +1236,14 @@ TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update, |
| 1160 | 1236 | s->hw_update = update; |
| 1161 | 1237 | s->hw_invalidate = invalidate; |
| 1162 | 1238 | s->hw_screen_dump = screen_dump; |
| 1239 | + s->hw_text_update = text_update; | |
| 1163 | 1240 | s->hw = opaque; |
| 1164 | 1241 | return s; |
| 1165 | 1242 | } |
| 1166 | 1243 | |
| 1167 | 1244 | int is_graphic_console(void) |
| 1168 | 1245 | { |
| 1169 | - return active_console->console_type == GRAPHIC_CONSOLE; | |
| 1246 | + return active_console && active_console->console_type == GRAPHIC_CONSOLE; | |
| 1170 | 1247 | } |
| 1171 | 1248 | |
| 1172 | 1249 | void console_color_init(DisplayState *ds) |
| ... | ... | @@ -1234,6 +1311,10 @@ CharDriverState *text_console_init(DisplayState *ds, const char *p) |
| 1234 | 1311 | s->g_width = width; |
| 1235 | 1312 | s->g_height = height; |
| 1236 | 1313 | |
| 1314 | + s->hw_invalidate = text_console_invalidate; | |
| 1315 | + s->hw_text_update = text_console_update; | |
| 1316 | + s->hw = s; | |
| 1317 | + | |
| 1237 | 1318 | /* Set text attribute defaults */ |
| 1238 | 1319 | s->t_attrib_default.bold = 0; |
| 1239 | 1320 | s->t_attrib_default.uline = 0; | ... | ... |
console.h
| ... | ... | @@ -79,6 +79,7 @@ struct DisplayState { |
| 79 | 79 | int dst_x, int dst_y, int w, int h); |
| 80 | 80 | void (*dpy_fill)(struct DisplayState *s, int x, int y, |
| 81 | 81 | int w, int h, uint32_t c); |
| 82 | + void (*dpy_text_cursor)(struct DisplayState *s, int x, int y); | |
| 82 | 83 | void (*mouse_set)(int x, int y, int on); |
| 83 | 84 | void (*cursor_define)(int width, int height, int bpp, int hot_x, int hot_y, |
| 84 | 85 | uint8_t *image, uint8_t *mask); |
| ... | ... | @@ -94,17 +95,32 @@ static inline void dpy_resize(DisplayState *s, int w, int h) |
| 94 | 95 | s->dpy_resize(s, w, h); |
| 95 | 96 | } |
| 96 | 97 | |
| 98 | +static inline void dpy_cursor(DisplayState *s, int x, int y) | |
| 99 | +{ | |
| 100 | + if (s->dpy_text_cursor) | |
| 101 | + s->dpy_text_cursor(s, x, y); | |
| 102 | +} | |
| 103 | + | |
| 104 | +typedef unsigned long console_ch_t; | |
| 105 | +static inline void console_write_ch(console_ch_t *dest, uint32_t ch) | |
| 106 | +{ | |
| 107 | + cpu_to_le32wu((uint32_t *) dest, ch); | |
| 108 | +} | |
| 109 | + | |
| 97 | 110 | typedef void (*vga_hw_update_ptr)(void *); |
| 98 | 111 | typedef void (*vga_hw_invalidate_ptr)(void *); |
| 99 | 112 | typedef void (*vga_hw_screen_dump_ptr)(void *, const char *); |
| 113 | +typedef void (*vga_hw_text_update_ptr)(void *, console_ch_t *); | |
| 100 | 114 | |
| 101 | 115 | TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update, |
| 102 | 116 | vga_hw_invalidate_ptr invalidate, |
| 103 | 117 | vga_hw_screen_dump_ptr screen_dump, |
| 118 | + vga_hw_text_update_ptr text_update, | |
| 104 | 119 | void *opaque); |
| 105 | 120 | void vga_hw_update(void); |
| 106 | 121 | void vga_hw_invalidate(void); |
| 107 | 122 | void vga_hw_screen_dump(const char *filename); |
| 123 | +void vga_hw_text_update(console_ch_t *chardata); | |
| 108 | 124 | |
| 109 | 125 | int is_graphic_console(void); |
| 110 | 126 | CharDriverState *text_console_init(DisplayState *ds, const char *p); |
| ... | ... | @@ -124,6 +140,9 @@ int vnc_display_open(DisplayState *ds, const char *display); |
| 124 | 140 | int vnc_display_password(DisplayState *ds, const char *password); |
| 125 | 141 | void do_info_vnc(void); |
| 126 | 142 | |
| 143 | +/* curses.c */ | |
| 144 | +void curses_display_init(DisplayState *ds, int full_screen); | |
| 145 | + | |
| 127 | 146 | /* x_keymap.c */ |
| 128 | 147 | extern uint8_t _translate_keycode(const int key); |
| 129 | 148 | ... | ... |
curses.c
0 โ 100644
| 1 | +/* | |
| 2 | + * QEMU curses/ncurses display driver | |
| 3 | + * | |
| 4 | + * Copyright (c) 2005 Andrzej Zaborowski <balrog@zabor.org> | |
| 5 | + * | |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 7 | + * of this software and associated documentation files (the "Software"), to deal | |
| 8 | + * in the Software without restriction, including without limitation the rights | |
| 9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 10 | + * copies of the Software, and to permit persons to whom the Software is | |
| 11 | + * furnished to do so, subject to the following conditions: | |
| 12 | + * | |
| 13 | + * The above copyright notice and this permission notice shall be included in | |
| 14 | + * all copies or substantial portions of the Software. | |
| 15 | + * | |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 22 | + * THE SOFTWARE. | |
| 23 | + */ | |
| 24 | + | |
| 25 | +#include "qemu-common.h" | |
| 26 | +#include "console.h" | |
| 27 | +#include "sysemu.h" | |
| 28 | + | |
| 29 | +#include <curses.h> | |
| 30 | + | |
| 31 | +#ifndef _WIN32 | |
| 32 | +#include <signal.h> | |
| 33 | +#include <sys/ioctl.h> | |
| 34 | +#include <termios.h> | |
| 35 | +#endif | |
| 36 | + | |
| 37 | +#define FONT_HEIGHT 16 | |
| 38 | +#define FONT_WIDTH 8 | |
| 39 | + | |
| 40 | +static console_ch_t screen[160 * 100]; | |
| 41 | +static WINDOW *screenpad = NULL; | |
| 42 | +static int width, height, gwidth, gheight, invalidate; | |
| 43 | +static int px, py, sminx, sminy, smaxx, smaxy; | |
| 44 | + | |
| 45 | +static void curses_update(DisplayState *ds, int x, int y, int w, int h) | |
| 46 | +{ | |
| 47 | + chtype *line; | |
| 48 | + | |
| 49 | + line = ((chtype *) screen) + y * width; | |
| 50 | + for (h += y; y < h; y ++, line += width) | |
| 51 | + mvwaddchnstr(screenpad, y, 0, line, width); | |
| 52 | + | |
| 53 | + pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1); | |
| 54 | + refresh(); | |
| 55 | +} | |
| 56 | + | |
| 57 | +static void curses_calc_pad(void) | |
| 58 | +{ | |
| 59 | + if (is_graphic_console()) { | |
| 60 | + width = gwidth; | |
| 61 | + height = gheight; | |
| 62 | + } else { | |
| 63 | + width = COLS; | |
| 64 | + height = LINES; | |
| 65 | + } | |
| 66 | + | |
| 67 | + if (screenpad) | |
| 68 | + delwin(screenpad); | |
| 69 | + | |
| 70 | + clear(); | |
| 71 | + refresh(); | |
| 72 | + | |
| 73 | + screenpad = newpad(height, width); | |
| 74 | + | |
| 75 | + if (width > COLS) { | |
| 76 | + px = (width - COLS) / 2; | |
| 77 | + sminx = 0; | |
| 78 | + smaxx = COLS; | |
| 79 | + } else { | |
| 80 | + px = 0; | |
| 81 | + sminx = (COLS - width) / 2; | |
| 82 | + smaxx = sminx + width; | |
| 83 | + } | |
| 84 | + | |
| 85 | + if (height > LINES) { | |
| 86 | + py = (height - LINES) / 2; | |
| 87 | + sminy = 0; | |
| 88 | + smaxy = LINES; | |
| 89 | + } else { | |
| 90 | + py = 0; | |
| 91 | + sminy = (LINES - height) / 2; | |
| 92 | + smaxy = sminy + height; | |
| 93 | + } | |
| 94 | +} | |
| 95 | + | |
| 96 | +static void curses_resize(DisplayState *ds, int w, int h) | |
| 97 | +{ | |
| 98 | + if (w == gwidth && h == gheight) | |
| 99 | + return; | |
| 100 | + | |
| 101 | + gwidth = w; | |
| 102 | + gheight = h; | |
| 103 | + | |
| 104 | + curses_calc_pad(); | |
| 105 | +} | |
| 106 | + | |
| 107 | +#ifndef _WIN32 | |
| 108 | +#ifdef SIGWINCH | |
| 109 | +static void curses_winch_handler(int signum) | |
| 110 | +{ | |
| 111 | + struct winsize { | |
| 112 | + unsigned short ws_row; | |
| 113 | + unsigned short ws_col; | |
| 114 | + unsigned short ws_xpixel; /* unused */ | |
| 115 | + unsigned short ws_ypixel; /* unused */ | |
| 116 | + } ws; | |
| 117 | + | |
| 118 | + /* terminal size changed */ | |
| 119 | + if (ioctl(1, TIOCGWINSZ, &ws) == -1) | |
| 120 | + return; | |
| 121 | + | |
| 122 | + resize_term(ws.ws_row, ws.ws_col); | |
| 123 | + curses_calc_pad(); | |
| 124 | + invalidate = 1; | |
| 125 | + | |
| 126 | + /* some systems require this */ | |
| 127 | + signal(SIGWINCH, curses_winch_handler); | |
| 128 | +} | |
| 129 | +#endif | |
| 130 | +#endif | |
| 131 | + | |
| 132 | +static void curses_cursor_position(DisplayState *ds, int x, int y) | |
| 133 | +{ | |
| 134 | + if (x >= 0) { | |
| 135 | + x = sminx + x - px; | |
| 136 | + y = sminy + y - py; | |
| 137 | + | |
| 138 | + if (x >= 0 && y >= 0 && x < COLS && y < LINES) { | |
| 139 | + move(y, x); | |
| 140 | + curs_set(1); | |
| 141 | + /* it seems that curs_set(1) must always be called before | |
| 142 | + * curs_set(2) for the latter to have effect */ | |
| 143 | + if (!is_graphic_console()) | |
| 144 | + curs_set(2); | |
| 145 | + return; | |
| 146 | + } | |
| 147 | + } | |
| 148 | + | |
| 149 | + curs_set(0); | |
| 150 | +} | |
| 151 | + | |
| 152 | +/* generic keyboard conversion */ | |
| 153 | + | |
| 154 | +#include "curses_keys.h" | |
| 155 | +#include "keymaps.c" | |
| 156 | + | |
| 157 | +static kbd_layout_t *kbd_layout = 0; | |
| 158 | +static int keycode2keysym[CURSES_KEYS]; | |
| 159 | + | |
| 160 | +static void curses_refresh(DisplayState *ds) | |
| 161 | +{ | |
| 162 | + int chr, nextchr, keysym, keycode; | |
| 163 | + | |
| 164 | + if (invalidate) { | |
| 165 | + clear(); | |
| 166 | + refresh(); | |
| 167 | + curses_calc_pad(); | |
| 168 | + ds->width = FONT_WIDTH * width; | |
| 169 | + ds->height = FONT_HEIGHT * height; | |
| 170 | + vga_hw_invalidate(); | |
| 171 | + invalidate = 0; | |
| 172 | + } | |
| 173 | + | |
| 174 | + vga_hw_text_update(screen); | |
| 175 | + | |
| 176 | + nextchr = ERR; | |
| 177 | + while (1) { | |
| 178 | + /* while there are any pending key strokes to process */ | |
| 179 | + if (nextchr == ERR) | |
| 180 | + chr = getch(); | |
| 181 | + else { | |
| 182 | + chr = nextchr; | |
| 183 | + nextchr = ERR; | |
| 184 | + } | |
| 185 | + | |
| 186 | + if (chr == ERR) | |
| 187 | + break; | |
| 188 | + | |
| 189 | + /* this shouldn't occur when we use a custom SIGWINCH handler */ | |
| 190 | + if (chr == KEY_RESIZE) { | |
| 191 | + clear(); | |
| 192 | + refresh(); | |
| 193 | + curses_calc_pad(); | |
| 194 | + curses_update(ds, 0, 0, width, height); | |
| 195 | + ds->width = FONT_WIDTH * width; | |
| 196 | + ds->height = FONT_HEIGHT * height; | |
| 197 | + continue; | |
| 198 | + } | |
| 199 | + | |
| 200 | + keycode = curses2keycode[chr]; | |
| 201 | + if (keycode == -1) | |
| 202 | + continue; | |
| 203 | + | |
| 204 | + /* alt key */ | |
| 205 | + if (keycode == 1) { | |
| 206 | + nextchr = getch(); | |
| 207 | + | |
| 208 | + if (nextchr != ERR) { | |
| 209 | + keycode = curses2keycode[nextchr]; | |
| 210 | + nextchr = ERR; | |
| 211 | + if (keycode == -1) | |
| 212 | + continue; | |
| 213 | + | |
| 214 | + keycode |= ALT; | |
| 215 | + | |
| 216 | + /* process keys reserved for qemu */ | |
| 217 | + if (keycode >= QEMU_KEY_CONSOLE0 && | |
| 218 | + keycode < QEMU_KEY_CONSOLE0 + 9) { | |
| 219 | + erase(); | |
| 220 | + wnoutrefresh(stdscr); | |
| 221 | + console_select(keycode - QEMU_KEY_CONSOLE0); | |
| 222 | + | |
| 223 | + invalidate = 1; | |
| 224 | + continue; | |
| 225 | + } | |
| 226 | + } | |
| 227 | + } | |
| 228 | + | |
| 229 | + if (kbd_layout && !(keycode & GREY)) { | |
| 230 | + keysym = keycode2keysym[keycode & KEY_MASK]; | |
| 231 | + if (keysym == -1) | |
| 232 | + keysym = chr; | |
| 233 | + | |
| 234 | + keycode &= ~KEY_MASK; | |
| 235 | + keycode |= keysym2scancode(kbd_layout, keysym); | |
| 236 | + } | |
| 237 | + | |
| 238 | + if (is_graphic_console()) { | |
| 239 | + /* since terminals don't know about key press and release | |
| 240 | + * events, we need to emit both for each key received */ | |
| 241 | + if (keycode & SHIFT) | |
| 242 | + kbd_put_keycode(SHIFT_CODE); | |
| 243 | + if (keycode & CNTRL) | |
| 244 | + kbd_put_keycode(CNTRL_CODE); | |
| 245 | + if (keycode & ALT) | |
| 246 | + kbd_put_keycode(ALT_CODE); | |
| 247 | + if (keycode & GREY) | |
| 248 | + kbd_put_keycode(GREY_CODE); | |
| 249 | + kbd_put_keycode(keycode & KEY_MASK); | |
| 250 | + if (keycode & GREY) | |
| 251 | + kbd_put_keycode(GREY_CODE); | |
| 252 | + kbd_put_keycode((keycode & KEY_MASK) | KEY_RELEASE); | |
| 253 | + if (keycode & ALT) | |
| 254 | + kbd_put_keycode(ALT_CODE | KEY_RELEASE); | |
| 255 | + if (keycode & CNTRL) | |
| 256 | + kbd_put_keycode(CNTRL_CODE | KEY_RELEASE); | |
| 257 | + if (keycode & SHIFT) | |
| 258 | + kbd_put_keycode(SHIFT_CODE | KEY_RELEASE); | |
| 259 | + } else { | |
| 260 | + keysym = curses2keysym[chr]; | |
| 261 | + if (keysym == -1) | |
| 262 | + keysym = chr; | |
| 263 | + | |
| 264 | + kbd_put_keysym(keysym); | |
| 265 | + } | |
| 266 | + } | |
| 267 | +} | |
| 268 | + | |
| 269 | +static void curses_cleanup(void *opaque) | |
| 270 | +{ | |
| 271 | + endwin(); | |
| 272 | +} | |
| 273 | + | |
| 274 | +static void curses_atexit(void) | |
| 275 | +{ | |
| 276 | + curses_cleanup(NULL); | |
| 277 | +} | |
| 278 | + | |
| 279 | +static void curses_setup(void) | |
| 280 | +{ | |
| 281 | + int i, colour_default[8] = { | |
| 282 | + COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN, | |
| 283 | + COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE, | |
| 284 | + }; | |
| 285 | + | |
| 286 | + /* input as raw as possible, let everything be interpreted | |
| 287 | + * by the guest system */ | |
| 288 | + initscr(); noecho(); intrflush(stdscr, FALSE); | |
| 289 | + nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE); | |
| 290 | + start_color(); raw(); scrollok(stdscr, FALSE); | |
| 291 | + | |
| 292 | + for (i = 0; i < 64; i ++) | |
| 293 | + init_pair(i, colour_default[i & 7], colour_default[i >> 3]); | |
| 294 | +} | |
| 295 | + | |
| 296 | +static void curses_keyboard_setup(void) | |
| 297 | +{ | |
| 298 | + int i, keycode, keysym; | |
| 299 | + | |
| 300 | +#if defined(__APPLE__) | |
| 301 | + /* always use generic keymaps */ | |
| 302 | + if (!keyboard_layout) | |
| 303 | + keyboard_layout = "en-us"; | |
| 304 | +#endif | |
| 305 | + if(keyboard_layout) { | |
| 306 | + kbd_layout = init_keyboard_layout(keyboard_layout); | |
| 307 | + if (!kbd_layout) | |
| 308 | + exit(1); | |
| 309 | + } | |
| 310 | + | |
| 311 | + for (i = 0; i < CURSES_KEYS; i ++) | |
| 312 | + keycode2keysym[i] = -1; | |
| 313 | + | |
| 314 | + for (i = 0; i < CURSES_KEYS; i ++) { | |
| 315 | + if (curses2keycode[i] == -1) | |
| 316 | + continue; | |
| 317 | + | |
| 318 | + keycode = curses2keycode[i] & KEY_MASK; | |
| 319 | + if (keycode2keysym[keycode] >= 0) | |
| 320 | + continue; | |
| 321 | + | |
| 322 | + for (keysym = 0; keysym < CURSES_KEYS; keysym ++) | |
| 323 | + if (curses2keycode[keysym] == keycode) { | |
| 324 | + keycode2keysym[keycode] = keysym; | |
| 325 | + break; | |
| 326 | + } | |
| 327 | + | |
| 328 | + if (keysym >= CURSES_KEYS) | |
| 329 | + keycode2keysym[keycode] = i; | |
| 330 | + } | |
| 331 | +} | |
| 332 | + | |
| 333 | +void curses_display_init(DisplayState *ds, int full_screen) | |
| 334 | +{ | |
| 335 | +#ifndef _WIN32 | |
| 336 | + if (!isatty(1)) { | |
| 337 | + fprintf(stderr, "We need a terminal output\n"); | |
| 338 | + exit(1); | |
| 339 | + } | |
| 340 | +#endif | |
| 341 | + | |
| 342 | + curses_setup(); | |
| 343 | + curses_keyboard_setup(); | |
| 344 | + atexit(curses_atexit); | |
| 345 | + | |
| 346 | +#ifndef _WIN32 | |
| 347 | + signal(SIGINT, SIG_DFL); | |
| 348 | + signal(SIGQUIT, SIG_DFL); | |
| 349 | +#ifdef SIGWINCH | |
| 350 | + /* some curses implementations provide a handler, but we | |
| 351 | + * want to be sure this is handled regardless of the library */ | |
| 352 | + signal(SIGWINCH, curses_winch_handler); | |
| 353 | +#endif | |
| 354 | +#endif | |
| 355 | + | |
| 356 | + ds->data = (void *) screen; | |
| 357 | + ds->linesize = 0; | |
| 358 | + ds->depth = 0; | |
| 359 | + ds->width = 640; | |
| 360 | + ds->height = 400; | |
| 361 | + ds->dpy_update = curses_update; | |
| 362 | + ds->dpy_resize = curses_resize; | |
| 363 | + ds->dpy_refresh = curses_refresh; | |
| 364 | + ds->dpy_text_cursor = curses_cursor_position; | |
| 365 | + | |
| 366 | + invalidate = 1; | |
| 367 | + | |
| 368 | + /* Standard VGA initial text mode dimensions */ | |
| 369 | + curses_resize(ds, 80, 25); | |
| 370 | +} | ... | ... |
curses_keys.h
0 โ 100644
| 1 | +/* | |
| 2 | + * Keycode and keysyms conversion tables for curses | |
| 3 | + * | |
| 4 | + * Copyright (c) 2005 Andrzej Zaborowski <balrog@zabor.org> | |
| 5 | + * | |
| 6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 7 | + * of this software and associated documentation files (the "Software"), to deal | |
| 8 | + * in the Software without restriction, including without limitation the rights | |
| 9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 10 | + * copies of the Software, and to permit persons to whom the Software is | |
| 11 | + * furnished to do so, subject to the following conditions: | |
| 12 | + * | |
| 13 | + * The above copyright notice and this permission notice shall be included in | |
| 14 | + * all copies or substantial portions of the Software. | |
| 15 | + * | |
| 16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| 19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 22 | + * THE SOFTWARE. | |
| 23 | + */ | |
| 24 | +#define KEY_RELEASE 0x80 | |
| 25 | +#define KEY_MASK 0x7f | |
| 26 | +#define SHIFT_CODE 0x2a | |
| 27 | +#define SHIFT 0x0080 | |
| 28 | +#define GREY_CODE 0xe0 | |
| 29 | +#define GREY 0x0100 | |
| 30 | +#define CNTRL_CODE 0x1d | |
| 31 | +#define CNTRL 0x0200 | |
| 32 | +#define ALT_CODE 0x38 | |
| 33 | +#define ALT 0x0400 | |
| 34 | + | |
| 35 | +/* curses won't detect a Control + Alt + 1, so use Alt + 1 */ | |
| 36 | +#define QEMU_KEY_CONSOLE0 (2 | ALT) /* (curses2keycode['1'] | ALT) */ | |
| 37 | + | |
| 38 | +#define CURSES_KEYS KEY_MAX /* KEY_MAX defined in <curses.h> */ | |
| 39 | + | |
| 40 | +int curses2keycode[CURSES_KEYS] = { | |
| 41 | + [0 ... (CURSES_KEYS - 1)] = -1, | |
| 42 | + | |
| 43 | + [0x01b] = 1, /* Escape */ | |
| 44 | + ['1'] = 2, | |
| 45 | + ['2'] = 3, | |
| 46 | + ['3'] = 4, | |
| 47 | + ['4'] = 5, | |
| 48 | + ['5'] = 6, | |
| 49 | + ['6'] = 7, | |
| 50 | + ['7'] = 8, | |
| 51 | + ['8'] = 9, | |
| 52 | + ['9'] = 10, | |
| 53 | + ['0'] = 11, | |
| 54 | + ['-'] = 12, | |
| 55 | + ['='] = 13, | |
| 56 | + [0x07f] = 14, /* Backspace */ | |
| 57 | + [0x107] = 14, /* Backspace */ | |
| 58 | + | |
| 59 | + ['\t'] = 15, /* Tab */ | |
| 60 | + ['q'] = 16, | |
| 61 | + ['w'] = 17, | |
| 62 | + ['e'] = 18, | |
| 63 | + ['r'] = 19, | |
| 64 | + ['t'] = 20, | |
| 65 | + ['y'] = 21, | |
| 66 | + ['u'] = 22, | |
| 67 | + ['i'] = 23, | |
| 68 | + ['o'] = 24, | |
| 69 | + ['p'] = 25, | |
| 70 | + ['['] = 26, | |
| 71 | + [']'] = 27, | |
| 72 | + ['\n'] = 28, /* Return */ | |
| 73 | + ['\r'] = 28, /* Return */ | |
| 74 | + [0x157] = 28, /* Return */ | |
| 75 | + | |
| 76 | + ['a'] = 30, | |
| 77 | + ['s'] = 31, | |
| 78 | + ['d'] = 32, | |
| 79 | + ['f'] = 33, | |
| 80 | + ['g'] = 34, | |
| 81 | + ['h'] = 35, | |
| 82 | + ['j'] = 36, | |
| 83 | + ['k'] = 37, | |
| 84 | + ['l'] = 38, | |
| 85 | + [';'] = 39, | |
| 86 | + ['\''] = 40, /* Single quote */ | |
| 87 | + ['`'] = 41, | |
| 88 | + ['\\'] = 43, /* Backslash */ | |
| 89 | + | |
| 90 | + ['z'] = 44, | |
| 91 | + ['x'] = 45, | |
| 92 | + ['c'] = 46, | |
| 93 | + ['v'] = 47, | |
| 94 | + ['b'] = 48, | |
| 95 | + ['n'] = 49, | |
| 96 | + ['m'] = 50, | |
| 97 | + [','] = 51, | |
| 98 | + ['.'] = 52, | |
| 99 | + ['/'] = 53, | |
| 100 | + | |
| 101 | + [' '] = 57, | |
| 102 | + | |
| 103 | + [0x109] = 59, /* Function Key 1 */ | |
| 104 | + [0x10a] = 60, /* Function Key 2 */ | |
| 105 | + [0x10b] = 61, /* Function Key 3 */ | |
| 106 | + [0x10c] = 62, /* Function Key 4 */ | |
| 107 | + [0x10d] = 63, /* Function Key 5 */ | |
| 108 | + [0x10e] = 64, /* Function Key 6 */ | |
| 109 | + [0x10f] = 65, /* Function Key 7 */ | |
| 110 | + [0x110] = 66, /* Function Key 8 */ | |
| 111 | + [0x111] = 67, /* Function Key 9 */ | |
| 112 | + [0x112] = 68, /* Function Key 10 */ | |
| 113 | + [0x113] = 87, /* Function Key 11 */ | |
| 114 | + [0x114] = 88, /* Function Key 12 */ | |
| 115 | + | |
| 116 | + [0x106] = 71 | GREY, /* Home */ | |
| 117 | + [0x103] = 72 | GREY, /* Up Arrow */ | |
| 118 | + [0x153] = 73 | GREY, /* Page Up */ | |
| 119 | + [0x104] = 75 | GREY, /* Left Arrow */ | |
| 120 | + [0x105] = 77 | GREY, /* Right Arrow */ | |
| 121 | + [0x168] = 79 | GREY, /* End */ | |
| 122 | + [0x102] = 80 | GREY, /* Down Arrow */ | |
| 123 | + [0x152] = 81 | GREY, /* Page Down */ | |
| 124 | + [0x14b] = 82 | GREY, /* Insert */ | |
| 125 | + [0x14a] = 83 | GREY, /* Delete */ | |
| 126 | + | |
| 127 | + ['!'] = 2 | SHIFT, | |
| 128 | + ['@'] = 3 | SHIFT, | |
| 129 | + ['#'] = 4 | SHIFT, | |
| 130 | + ['$'] = 5 | SHIFT, | |
| 131 | + ['%'] = 6 | SHIFT, | |
| 132 | + ['^'] = 7 | SHIFT, | |
| 133 | + ['&'] = 8 | SHIFT, | |
| 134 | + ['*'] = 9 | SHIFT, | |
| 135 | + ['('] = 10 | SHIFT, | |
| 136 | + [')'] = 11 | SHIFT, | |
| 137 | + ['_'] = 12 | SHIFT, | |
| 138 | + ['+'] = 13 | SHIFT, | |
| 139 | + | |
| 140 | + [0x161] = 15 | SHIFT, /* Shift + Tab */ | |
| 141 | + ['Q'] = 16 | SHIFT, | |
| 142 | + ['W'] = 17 | SHIFT, | |
| 143 | + ['E'] = 18 | SHIFT, | |
| 144 | + ['R'] = 19 | SHIFT, | |
| 145 | + ['T'] = 20 | SHIFT, | |
| 146 | + ['Y'] = 21 | SHIFT, | |
| 147 | + ['U'] = 22 | SHIFT, | |
| 148 | + ['I'] = 23 | SHIFT, | |
| 149 | + ['O'] = 24 | SHIFT, | |
| 150 | + ['P'] = 25 | SHIFT, | |
| 151 | + ['{'] = 26 | SHIFT, | |
| 152 | + ['}'] = 27 | SHIFT, | |
| 153 | + | |
| 154 | + ['A'] = 30 | SHIFT, | |
| 155 | + ['S'] = 31 | SHIFT, | |
| 156 | + ['D'] = 32 | SHIFT, | |
| 157 | + ['F'] = 33 | SHIFT, | |
| 158 | + ['G'] = 34 | SHIFT, | |
| 159 | + ['H'] = 35 | SHIFT, | |
| 160 | + ['J'] = 36 | SHIFT, | |
| 161 | + ['K'] = 37 | SHIFT, | |
| 162 | + ['L'] = 38 | SHIFT, | |
| 163 | + [':'] = 39 | SHIFT, | |
| 164 | + ['"'] = 40 | SHIFT, | |
| 165 | + ['~'] = 41 | SHIFT, | |
| 166 | + ['|'] = 43 | SHIFT, | |
| 167 | + | |
| 168 | + ['Z'] = 44 | SHIFT, | |
| 169 | + ['X'] = 45 | SHIFT, | |
| 170 | + ['C'] = 46 | SHIFT, | |
| 171 | + ['V'] = 47 | SHIFT, | |
| 172 | + ['B'] = 48 | SHIFT, | |
| 173 | + ['N'] = 49 | SHIFT, | |
| 174 | + ['M'] = 50 | SHIFT, | |
| 175 | + ['<'] = 51 | SHIFT, | |
| 176 | + ['>'] = 52 | SHIFT, | |
| 177 | + ['?'] = 53 | SHIFT, | |
| 178 | + | |
| 179 | + [0x115] = 59 | SHIFT, /* Shift + Function Key 1 */ | |
| 180 | + [0x116] = 60 | SHIFT, /* Shift + Function Key 2 */ | |
| 181 | + [0x117] = 61 | SHIFT, /* Shift + Function Key 3 */ | |
| 182 | + [0x118] = 62 | SHIFT, /* Shift + Function Key 4 */ | |
| 183 | + [0x119] = 63 | SHIFT, /* Shift + Function Key 5 */ | |
| 184 | + [0x11a] = 64 | SHIFT, /* Shift + Function Key 6 */ | |
| 185 | + [0x11b] = 65 | SHIFT, /* Shift + Function Key 7 */ | |
| 186 | + [0x11c] = 66 | SHIFT, /* Shift + Function Key 8 */ | |
| 187 | + | |
| 188 | + [0x011] = 16 | CNTRL, /* Control + q */ | |
| 189 | + [0x017] = 17 | CNTRL, /* Control + w */ | |
| 190 | + [0x005] = 18 | CNTRL, /* Control + e */ | |
| 191 | + [0x012] = 19 | CNTRL, /* Control + r */ | |
| 192 | + [0x014] = 20 | CNTRL, /* Control + t */ | |
| 193 | + [0x019] = 21 | CNTRL, /* Control + y */ | |
| 194 | + [0x015] = 22 | CNTRL, /* Control + u */ | |
| 195 | + [0x009] = 23 | CNTRL, /* Control + i */ | |
| 196 | + [0x00f] = 24 | CNTRL, /* Control + o */ | |
| 197 | + [0x010] = 25 | CNTRL, /* Control + p */ | |
| 198 | + | |
| 199 | + [0x001] = 30 | CNTRL, /* Control + a */ | |
| 200 | + [0x013] = 31 | CNTRL, /* Control + s */ | |
| 201 | + [0x014] = 32 | CNTRL, /* Control + d */ | |
| 202 | + [0x006] = 33 | CNTRL, /* Control + f */ | |
| 203 | + [0x007] = 34 | CNTRL, /* Control + g */ | |
| 204 | + [0x008] = 35 | CNTRL, /* Control + h */ | |
| 205 | + [0x00a] = 36 | CNTRL, /* Control + j */ | |
| 206 | + [0x00b] = 37 | CNTRL, /* Control + k */ | |
| 207 | + [0x00c] = 38 | CNTRL, /* Control + l */ | |
| 208 | + | |
| 209 | + [0x01a] = 44 | CNTRL, /* Control + z */ | |
| 210 | + [0x018] = 45 | CNTRL, /* Control + x */ | |
| 211 | + [0x003] = 46 | CNTRL, /* Control + c */ | |
| 212 | + [0x016] = 47 | CNTRL, /* Control + v */ | |
| 213 | + [0x002] = 48 | CNTRL, /* Control + b */ | |
| 214 | + [0x00e] = 49 | CNTRL, /* Control + n */ | |
| 215 | + /* Control + m collides with the keycode for Enter */ | |
| 216 | + | |
| 217 | +}; | |
| 218 | + | |
| 219 | +int curses2keysym[CURSES_KEYS] = { | |
| 220 | + [0 ... (CURSES_KEYS - 1)] = -1, | |
| 221 | + | |
| 222 | + ['\n'] = '\n', | |
| 223 | + ['\r'] = '\n', | |
| 224 | + | |
| 225 | + [0x07f] = QEMU_KEY_BACKSPACE, | |
| 226 | + | |
| 227 | + [0x102] = QEMU_KEY_DOWN, | |
| 228 | + [0x103] = QEMU_KEY_UP, | |
| 229 | + [0x104] = QEMU_KEY_LEFT, | |
| 230 | + [0x105] = QEMU_KEY_RIGHT, | |
| 231 | + [0x106] = QEMU_KEY_HOME, | |
| 232 | + [0x107] = QEMU_KEY_BACKSPACE, | |
| 233 | + | |
| 234 | + [0x14a] = QEMU_KEY_DELETE, | |
| 235 | + [0x152] = QEMU_KEY_PAGEDOWN, | |
| 236 | + [0x153] = QEMU_KEY_PAGEUP, | |
| 237 | + [0x157] = '\n', | |
| 238 | + [0x168] = QEMU_KEY_END, | |
| 239 | + | |
| 240 | +}; | |
| 241 | + | |
| 242 | +typedef struct { | |
| 243 | + const char* name; | |
| 244 | + int keysym; | |
| 245 | +} name2keysym_t; | |
| 246 | + | |
| 247 | +static name2keysym_t name2keysym[] = { | |
| 248 | + /* Plain ASCII */ | |
| 249 | + { "space", 0x020 }, | |
| 250 | + { "exclam", 0x021 }, | |
| 251 | + { "quotedbl", 0x022 }, | |
| 252 | + { "numbersign", 0x023 }, | |
| 253 | + { "dollar", 0x024 }, | |
| 254 | + { "percent", 0x025 }, | |
| 255 | + { "ampersand", 0x026 }, | |
| 256 | + { "apostrophe", 0x027 }, | |
| 257 | + { "parenleft", 0x028 }, | |
| 258 | + { "parenright", 0x029 }, | |
| 259 | + { "asterisk", 0x02a }, | |
| 260 | + { "plus", 0x02b }, | |
| 261 | + { "comma", 0x02c }, | |
| 262 | + { "minus", 0x02d }, | |
| 263 | + { "period", 0x02e }, | |
| 264 | + { "slash", 0x02f }, | |
| 265 | + { "0", 0x030 }, | |
| 266 | + { "1", 0x031 }, | |
| 267 | + { "2", 0x032 }, | |
| 268 | + { "3", 0x033 }, | |
| 269 | + { "4", 0x034 }, | |
| 270 | + { "5", 0x035 }, | |
| 271 | + { "6", 0x036 }, | |
| 272 | + { "7", 0x037 }, | |
| 273 | + { "8", 0x038 }, | |
| 274 | + { "9", 0x039 }, | |
| 275 | + { "colon", 0x03a }, | |
| 276 | + { "semicolon", 0x03b }, | |
| 277 | + { "less", 0x03c }, | |
| 278 | + { "equal", 0x03d }, | |
| 279 | + { "greater", 0x03e }, | |
| 280 | + { "question", 0x03f }, | |
| 281 | + { "at", 0x040 }, | |
| 282 | + { "A", 0x041 }, | |
| 283 | + { "B", 0x042 }, | |
| 284 | + { "C", 0x043 }, | |
| 285 | + { "D", 0x044 }, | |
| 286 | + { "E", 0x045 }, | |
| 287 | + { "F", 0x046 }, | |
| 288 | + { "G", 0x047 }, | |
| 289 | + { "H", 0x048 }, | |
| 290 | + { "I", 0x049 }, | |
| 291 | + { "J", 0x04a }, | |
| 292 | + { "K", 0x04b }, | |
| 293 | + { "L", 0x04c }, | |
| 294 | + { "M", 0x04d }, | |
| 295 | + { "N", 0x04e }, | |
| 296 | + { "O", 0x04f }, | |
| 297 | + { "P", 0x050 }, | |
| 298 | + { "Q", 0x051 }, | |
| 299 | + { "R", 0x052 }, | |
| 300 | + { "S", 0x053 }, | |
| 301 | + { "T", 0x054 }, | |
| 302 | + { "U", 0x055 }, | |
| 303 | + { "V", 0x056 }, | |
| 304 | + { "W", 0x057 }, | |
| 305 | + { "X", 0x058 }, | |
| 306 | + { "Y", 0x059 }, | |
| 307 | + { "Z", 0x05a }, | |
| 308 | + { "bracketleft", 0x05b }, | |
| 309 | + { "backslash", 0x05c }, | |
| 310 | + { "bracketright", 0x05d }, | |
| 311 | + { "asciicircum", 0x05e }, | |
| 312 | + { "underscore", 0x05f }, | |
| 313 | + { "grave", 0x060 }, | |
| 314 | + { "a", 0x061 }, | |
| 315 | + { "b", 0x062 }, | |
| 316 | + { "c", 0x063 }, | |
| 317 | + { "d", 0x064 }, | |
| 318 | + { "e", 0x065 }, | |
| 319 | + { "f", 0x066 }, | |
| 320 | + { "g", 0x067 }, | |
| 321 | + { "h", 0x068 }, | |
| 322 | + { "i", 0x069 }, | |
| 323 | + { "j", 0x06a }, | |
| 324 | + { "k", 0x06b }, | |
| 325 | + { "l", 0x06c }, | |
| 326 | + { "m", 0x06d }, | |
| 327 | + { "n", 0x06e }, | |
| 328 | + { "o", 0x06f }, | |
| 329 | + { "p", 0x070 }, | |
| 330 | + { "q", 0x071 }, | |
| 331 | + { "r", 0x072 }, | |
| 332 | + { "s", 0x073 }, | |
| 333 | + { "t", 0x074 }, | |
| 334 | + { "u", 0x075 }, | |
| 335 | + { "v", 0x076 }, | |
| 336 | + { "w", 0x077 }, | |
| 337 | + { "x", 0x078 }, | |
| 338 | + { "y", 0x079 }, | |
| 339 | + { "z", 0x07a }, | |
| 340 | + { "braceleft", 0x07b }, | |
| 341 | + { "bar", 0x07c }, | |
| 342 | + { "braceright", 0x07d }, | |
| 343 | + { "asciitilde", 0x07e }, | |
| 344 | + | |
| 345 | + /* Latin-1 extensions */ | |
| 346 | + { "nobreakspace", 0x0a0 }, | |
| 347 | + { "exclamdown", 0x0a1 }, | |
| 348 | + { "cent", 0x0a2 }, | |
| 349 | + { "sterling", 0x0a3 }, | |
| 350 | + { "currency", 0x0a4 }, | |
| 351 | + { "yen", 0x0a5 }, | |
| 352 | + { "brokenbar", 0x0a6 }, | |
| 353 | + { "section", 0x0a7 }, | |
| 354 | + { "diaeresis", 0x0a8 }, | |
| 355 | + { "copyright", 0x0a9 }, | |
| 356 | + { "ordfeminine", 0x0aa }, | |
| 357 | + { "guillemotleft", 0x0ab }, | |
| 358 | + { "notsign", 0x0ac }, | |
| 359 | + { "hyphen", 0x0ad }, | |
| 360 | + { "registered", 0x0ae }, | |
| 361 | + { "macron", 0x0af }, | |
| 362 | + { "degree", 0x0b0 }, | |
| 363 | + { "plusminus", 0x0b1 }, | |
| 364 | + { "twosuperior", 0x0b2 }, | |
| 365 | + { "threesuperior", 0x0b3 }, | |
| 366 | + { "acute", 0x0b4 }, | |
| 367 | + { "mu", 0x0b5 }, | |
| 368 | + { "paragraph", 0x0b6 }, | |
| 369 | + { "periodcentered", 0x0b7 }, | |
| 370 | + { "cedilla", 0x0b8 }, | |
| 371 | + { "onesuperior", 0x0b9 }, | |
| 372 | + { "masculine", 0x0ba }, | |
| 373 | + { "guillemotright", 0x0bb }, | |
| 374 | + { "onequarter", 0x0bc }, | |
| 375 | + { "onehalf", 0x0bd }, | |
| 376 | + { "threequarters", 0x0be }, | |
| 377 | + { "questiondown", 0x0bf }, | |
| 378 | + { "Agrave", 0x0c0 }, | |
| 379 | + { "Aacute", 0x0c1 }, | |
| 380 | + { "Acircumflex", 0x0c2 }, | |
| 381 | + { "Atilde", 0x0c3 }, | |
| 382 | + { "Adiaeresis", 0x0c4 }, | |
| 383 | + { "Aring", 0x0c5 }, | |
| 384 | + { "AE", 0x0c6 }, | |
| 385 | + { "Ccedilla", 0x0c7 }, | |
| 386 | + { "Egrave", 0x0c8 }, | |
| 387 | + { "Eacute", 0x0c9 }, | |
| 388 | + { "Ecircumflex", 0x0ca }, | |
| 389 | + { "Ediaeresis", 0x0cb }, | |
| 390 | + { "Igrave", 0x0cc }, | |
| 391 | + { "Iacute", 0x0cd }, | |
| 392 | + { "Icircumflex", 0x0ce }, | |
| 393 | + { "Idiaeresis", 0x0cf }, | |
| 394 | + { "ETH", 0x0d0 }, | |
| 395 | + { "Eth", 0x0d0 }, | |
| 396 | + { "Ntilde", 0x0d1 }, | |
| 397 | + { "Ograve", 0x0d2 }, | |
| 398 | + { "Oacute", 0x0d3 }, | |
| 399 | + { "Ocircumflex", 0x0d4 }, | |
| 400 | + { "Otilde", 0x0d5 }, | |
| 401 | + { "Odiaeresis", 0x0d6 }, | |
| 402 | + { "multiply", 0x0d7 }, | |
| 403 | + { "Ooblique", 0x0d8 }, | |
| 404 | + { "Oslash", 0x0d8 }, | |
| 405 | + { "Ugrave", 0x0d9 }, | |
| 406 | + { "Uacute", 0x0da }, | |
| 407 | + { "Ucircumflex", 0x0db }, | |
| 408 | + { "Udiaeresis", 0x0dc }, | |
| 409 | + { "Yacute", 0x0dd }, | |
| 410 | + { "THORN", 0x0de }, | |
| 411 | + { "Thorn", 0x0de }, | |
| 412 | + { "ssharp", 0x0df }, | |
| 413 | + { "agrave", 0x0e0 }, | |
| 414 | + { "aacute", 0x0e1 }, | |
| 415 | + { "acircumflex", 0x0e2 }, | |
| 416 | + { "atilde", 0x0e3 }, | |
| 417 | + { "adiaeresis", 0x0e4 }, | |
| 418 | + { "aring", 0x0e5 }, | |
| 419 | + { "ae", 0x0e6 }, | |
| 420 | + { "ccedilla", 0x0e7 }, | |
| 421 | + { "egrave", 0x0e8 }, | |
| 422 | + { "eacute", 0x0e9 }, | |
| 423 | + { "ecircumflex", 0x0ea }, | |
| 424 | + { "ediaeresis", 0x0eb }, | |
| 425 | + { "igrave", 0x0ec }, | |
| 426 | + { "iacute", 0x0ed }, | |
| 427 | + { "icircumflex", 0x0ee }, | |
| 428 | + { "idiaeresis", 0x0ef }, | |
| 429 | + { "eth", 0x0f0 }, | |
| 430 | + { "ntilde", 0x0f1 }, | |
| 431 | + { "ograve", 0x0f2 }, | |
| 432 | + { "oacute", 0x0f3 }, | |
| 433 | + { "ocircumflex", 0x0f4 }, | |
| 434 | + { "otilde", 0x0f5 }, | |
| 435 | + { "odiaeresis", 0x0f6 }, | |
| 436 | + { "division", 0x0f7 }, | |
| 437 | + { "oslash", 0x0f8 }, | |
| 438 | + { "ooblique", 0x0f8 }, | |
| 439 | + { "ugrave", 0x0f9 }, | |
| 440 | + { "uacute", 0x0fa }, | |
| 441 | + { "ucircumflex", 0x0fb }, | |
| 442 | + { "udiaeresis", 0x0fc }, | |
| 443 | + { "yacute", 0x0fd }, | |
| 444 | + { "thorn", 0x0fe }, | |
| 445 | + { "ydiaeresis", 0x0ff }, | |
| 446 | + | |
| 447 | + /* Special keys */ | |
| 448 | + { "BackSpace", 0x07f }, | |
| 449 | + { "Tab", '\t' }, | |
| 450 | + { "Return", '\r' }, | |
| 451 | + { "Right", 0x105 }, | |
| 452 | + { "Left", 0x104 }, | |
| 453 | + { "Up", 0x103 }, | |
| 454 | + { "Down", 0x102 }, | |
| 455 | + { "Page_Down", 0x152 }, | |
| 456 | + { "Page_Up", 0x153 }, | |
| 457 | + { "Insert", 0x14b }, | |
| 458 | + { "Delete", 0x14a }, | |
| 459 | + { "Home", 0x106 }, | |
| 460 | + { "End", 0x168 }, | |
| 461 | + { "F1", 0x109 }, | |
| 462 | + { "F2", 0x10a }, | |
| 463 | + { "F3", 0x10b }, | |
| 464 | + { "F4", 0x10c }, | |
| 465 | + { "F5", 0x10d }, | |
| 466 | + { "F6", 0x10e }, | |
| 467 | + { "F7", 0x10f }, | |
| 468 | + { "F8", 0x110 }, | |
| 469 | + { "F9", 0x111 }, | |
| 470 | + { "F10", 0x112 }, | |
| 471 | + { "F11", 0x113 }, | |
| 472 | + { "F12", 0x114 }, | |
| 473 | + { "F13", 0x115 }, | |
| 474 | + { "F14", 0x116 }, | |
| 475 | + { "F15", 0x117 }, | |
| 476 | + { "F16", 0x118 }, | |
| 477 | + { "F17", 0x119 }, | |
| 478 | + { "F18", 0x11a }, | |
| 479 | + { "F19", 0x11b }, | |
| 480 | + { "F20", 0x11c }, | |
| 481 | + { "Escape", 27 }, | |
| 482 | + | |
| 483 | + { 0, 0 }, | |
| 484 | +}; | ... | ... |
hw/cirrus_vga.c
| ... | ... | @@ -3257,7 +3257,8 @@ void pci_cirrus_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base, |
| 3257 | 3257 | ds, vga_ram_base, vga_ram_offset, vga_ram_size); |
| 3258 | 3258 | cirrus_init_common(s, device_id, 1); |
| 3259 | 3259 | |
| 3260 | - graphic_console_init(s->ds, s->update, s->invalidate, s->screen_dump, s); | |
| 3260 | + graphic_console_init(s->ds, s->update, s->invalidate, s->screen_dump, | |
| 3261 | + s->text_update, s); | |
| 3261 | 3262 | |
| 3262 | 3263 | s->pci_dev = (PCIDevice *)d; |
| 3263 | 3264 | ... | ... |
hw/jazz_led.c
| ... | ... | @@ -285,6 +285,22 @@ static void jazz_led_screen_dump(void *opaque, const char *filename) |
| 285 | 285 | printf("jazz_led_screen_dump() not implemented\n"); |
| 286 | 286 | } |
| 287 | 287 | |
| 288 | +static void jazz_led_text_update(void *opaque, console_ch_t *chardata) | |
| 289 | +{ | |
| 290 | + LedState *s = opaque; | |
| 291 | + char buf[2]; | |
| 292 | + | |
| 293 | + dpy_cursor(s->ds, -1, -1); | |
| 294 | + dpy_resize(s->ds, 2, 1); | |
| 295 | + | |
| 296 | + /* TODO: draw the segments */ | |
| 297 | + snprintf(buf, 2, "%02hhx\n", s->segments); | |
| 298 | + console_write_ch(chardata++, 0x00200100 | buf[0]); | |
| 299 | + console_write_ch(chardata++, 0x00200100 | buf[1]); | |
| 300 | + | |
| 301 | + dpy_update(s->ds, 0, 0, 2, 1); | |
| 302 | +} | |
| 303 | + | |
| 288 | 304 | void jazz_led_init(DisplayState *ds, target_phys_addr_t base) |
| 289 | 305 | { |
| 290 | 306 | LedState *s; |
| ... | ... | @@ -301,5 +317,7 @@ void jazz_led_init(DisplayState *ds, target_phys_addr_t base) |
| 301 | 317 | io = cpu_register_io_memory(0, led_read, led_write, s); |
| 302 | 318 | cpu_register_physical_memory(s->base, 1, io); |
| 303 | 319 | |
| 304 | - graphic_console_init(ds, jazz_led_update_display, jazz_led_invalidate_display, jazz_led_screen_dump, s); | |
| 320 | + graphic_console_init(ds, jazz_led_update_display, | |
| 321 | + jazz_led_invalidate_display, jazz_led_screen_dump, | |
| 322 | + jazz_led_text_update, s); | |
| 305 | 323 | } | ... | ... |
hw/omap_lcdc.c
| ... | ... | @@ -495,7 +495,7 @@ struct omap_lcd_panel_s *omap_lcdc_init(target_phys_addr_t base, qemu_irq irq, |
| 495 | 495 | cpu_register_physical_memory(s->base, 0x100, iomemtype); |
| 496 | 496 | |
| 497 | 497 | graphic_console_init(ds, omap_update_display, |
| 498 | - omap_invalidate_display, omap_screen_dump, s); | |
| 498 | + omap_invalidate_display, omap_screen_dump, NULL, s); | |
| 499 | 499 | |
| 500 | 500 | return s; |
| 501 | 501 | } | ... | ... |
hw/pl110.c
| ... | ... | @@ -426,7 +426,7 @@ void *pl110_init(DisplayState *ds, uint32_t base, qemu_irq irq, |
| 426 | 426 | s->versatile = versatile; |
| 427 | 427 | s->irq = irq; |
| 428 | 428 | graphic_console_init(ds, pl110_update_display, pl110_invalidate_display, |
| 429 | - NULL, s); | |
| 429 | + NULL, NULL, s); | |
| 430 | 430 | /* ??? Save/restore. */ |
| 431 | 431 | return s; |
| 432 | 432 | } | ... | ... |
hw/pxa2xx_lcd.c
| ... | ... | @@ -1002,7 +1002,7 @@ struct pxa2xx_lcdc_s *pxa2xx_lcdc_init(target_phys_addr_t base, qemu_irq irq, |
| 1002 | 1002 | cpu_register_physical_memory(base, 0x00100000, iomemtype); |
| 1003 | 1003 | |
| 1004 | 1004 | graphic_console_init(ds, pxa2xx_update_display, |
| 1005 | - pxa2xx_invalidate_display, pxa2xx_screen_dump, s); | |
| 1005 | + pxa2xx_invalidate_display, pxa2xx_screen_dump, NULL, s); | |
| 1006 | 1006 | |
| 1007 | 1007 | switch (s->ds->depth) { |
| 1008 | 1008 | case 0: | ... | ... |
hw/ssd0303.c
| ... | ... | @@ -270,6 +270,6 @@ void ssd0303_init(DisplayState *ds, i2c_bus *bus, int address) |
| 270 | 270 | s->i2c.recv = ssd0303_recv; |
| 271 | 271 | s->i2c.send = ssd0303_send; |
| 272 | 272 | graphic_console_init(ds, ssd0303_update_display, ssd0303_invalidate_display, |
| 273 | - NULL, s); | |
| 273 | + NULL, NULL, s); | |
| 274 | 274 | dpy_resize(s->ds, 96 * MAGNIFY, 16 * MAGNIFY); |
| 275 | 275 | } | ... | ... |
hw/ssd0323.c
| ... | ... | @@ -280,7 +280,7 @@ void *ssd0323_init(DisplayState *ds, qemu_irq *cmd_p) |
| 280 | 280 | s = (ssd0323_state *)qemu_mallocz(sizeof(ssd0323_state)); |
| 281 | 281 | s->ds = ds; |
| 282 | 282 | graphic_console_init(ds, ssd0323_update_display, ssd0323_invalidate_display, |
| 283 | - NULL, s); | |
| 283 | + NULL, NULL, s); | |
| 284 | 284 | dpy_resize(s->ds, 128 * MAGNIFY, 64 * MAGNIFY); |
| 285 | 285 | s->col_end = 63; |
| 286 | 286 | s->row_end = 79; | ... | ... |
hw/tcx.c
| ... | ... | @@ -537,12 +537,13 @@ void tcx_init(DisplayState *ds, target_phys_addr_t addr, uint8_t *vram_base, |
| 537 | 537 | s->cplane_offset = vram_offset; |
| 538 | 538 | cpu_register_physical_memory(addr + 0x0a000000ULL, size, vram_offset); |
| 539 | 539 | graphic_console_init(s->ds, tcx24_update_display, |
| 540 | - tcx24_invalidate_display, tcx24_screen_dump, s); | |
| 540 | + tcx24_invalidate_display, | |
| 541 | + tcx24_screen_dump, NULL, s); | |
| 541 | 542 | } else { |
| 542 | 543 | cpu_register_physical_memory(addr + 0x00300000ULL, TCX_THC_NREGS_8, |
| 543 | 544 | dummy_memory); |
| 544 | 545 | graphic_console_init(s->ds, tcx_update_display, tcx_invalidate_display, |
| 545 | - tcx_screen_dump, s); | |
| 546 | + tcx_screen_dump, NULL, s); | |
| 546 | 547 | } |
| 547 | 548 | // NetBSD writes here even with 8-bit display |
| 548 | 549 | cpu_register_physical_memory(addr + 0x00301000ULL, TCX_THC_NREGS_24, | ... | ... |
hw/vga.c
| ... | ... | @@ -1660,6 +1660,165 @@ static void vga_reset(VGAState *s) |
| 1660 | 1660 | s->graphic_mode = -1; /* force full update */ |
| 1661 | 1661 | } |
| 1662 | 1662 | |
| 1663 | +#define TEXTMODE_X(x) ((x) % width) | |
| 1664 | +#define TEXTMODE_Y(x) ((x) / width) | |
| 1665 | +#define VMEM2CHTYPE(v) ((v & 0xff0007ff) | \ | |
| 1666 | + ((v & 0x00000800) << 10) | ((v & 0x00007000) >> 1)) | |
| 1667 | +/* relay text rendering to the display driver | |
| 1668 | + * instead of doing a full vga_update_display() */ | |
| 1669 | +static void vga_update_text(void *opaque, console_ch_t *chardata) | |
| 1670 | +{ | |
| 1671 | + VGAState *s = (VGAState *) opaque; | |
| 1672 | + int graphic_mode, i, cursor_offset, cursor_visible; | |
| 1673 | + int cw, cheight, width, height, size, c_min, c_max; | |
| 1674 | + uint32_t *src; | |
| 1675 | + console_ch_t *dst, val; | |
| 1676 | + char msg_buffer[80]; | |
| 1677 | + int full_update; | |
| 1678 | + full_update = 0; | |
| 1679 | + | |
| 1680 | + if (!(s->ar_index & 0x20)) { | |
| 1681 | + graphic_mode = GMODE_BLANK; | |
| 1682 | + } else { | |
| 1683 | + graphic_mode = s->gr[6] & 1; | |
| 1684 | + } | |
| 1685 | + if (graphic_mode != s->graphic_mode) { | |
| 1686 | + s->graphic_mode = graphic_mode; | |
| 1687 | + full_update = 1; | |
| 1688 | + } | |
| 1689 | + if (s->last_width == -1) { | |
| 1690 | + s->last_width = 0; | |
| 1691 | + full_update = 1; | |
| 1692 | + } | |
| 1693 | + | |
| 1694 | + switch (graphic_mode) { | |
| 1695 | + case GMODE_TEXT: | |
| 1696 | + /* TODO: update palette */ | |
| 1697 | + full_update |= update_basic_params(s); | |
| 1698 | + | |
| 1699 | + /* total width & height */ | |
| 1700 | + cheight = (s->cr[9] & 0x1f) + 1; | |
| 1701 | + cw = 8; | |
| 1702 | + if (!(s->sr[1] & 0x01)) | |
| 1703 | + cw = 9; | |
| 1704 | + if (s->sr[1] & 0x08) | |
| 1705 | + cw = 16; /* NOTE: no 18 pixel wide */ | |
| 1706 | + width = (s->cr[0x01] + 1); | |
| 1707 | + if (s->cr[0x06] == 100) { | |
| 1708 | + /* ugly hack for CGA 160x100x16 - explain me the logic */ | |
| 1709 | + height = 100; | |
| 1710 | + } else { | |
| 1711 | + height = s->cr[0x12] | | |
| 1712 | + ((s->cr[0x07] & 0x02) << 7) | | |
| 1713 | + ((s->cr[0x07] & 0x40) << 3); | |
| 1714 | + height = (height + 1) / cheight; | |
| 1715 | + } | |
| 1716 | + | |
| 1717 | + size = (height * width); | |
| 1718 | + if (size > CH_ATTR_SIZE) { | |
| 1719 | + if (!full_update) | |
| 1720 | + return; | |
| 1721 | + | |
| 1722 | + sprintf(msg_buffer, "%i x %i Text mode", width, height); | |
| 1723 | + break; | |
| 1724 | + } | |
| 1725 | + | |
| 1726 | + if (width != s->last_width || height != s->last_height || | |
| 1727 | + cw != s->last_cw || cheight != s->last_ch) { | |
| 1728 | + s->last_scr_width = width * cw; | |
| 1729 | + s->last_scr_height = height * cheight; | |
| 1730 | + dpy_resize(s->ds, width, height); | |
| 1731 | + s->last_width = width; | |
| 1732 | + s->last_height = height; | |
| 1733 | + s->last_ch = cheight; | |
| 1734 | + s->last_cw = cw; | |
| 1735 | + full_update = 1; | |
| 1736 | + } | |
| 1737 | + | |
| 1738 | + /* Update "hardware" cursor */ | |
| 1739 | + cursor_offset = ((s->cr[0x0e] << 8) | s->cr[0x0f]) - s->start_addr; | |
| 1740 | + if (cursor_offset != s->cursor_offset || | |
| 1741 | + s->cr[0xa] != s->cursor_start || | |
| 1742 | + s->cr[0xb] != s->cursor_end || full_update) { | |
| 1743 | + cursor_visible = !(s->cr[0xa] & 0x20); | |
| 1744 | + if (cursor_visible && cursor_offset < size && cursor_offset >= 0) | |
| 1745 | + dpy_cursor(s->ds, | |
| 1746 | + TEXTMODE_X(cursor_offset), | |
| 1747 | + TEXTMODE_Y(cursor_offset)); | |
| 1748 | + else | |
| 1749 | + dpy_cursor(s->ds, -1, -1); | |
| 1750 | + s->cursor_offset = cursor_offset; | |
| 1751 | + s->cursor_start = s->cr[0xa]; | |
| 1752 | + s->cursor_end = s->cr[0xb]; | |
| 1753 | + } | |
| 1754 | + | |
| 1755 | + src = (uint32_t *) s->vram_ptr + s->start_addr; | |
| 1756 | + dst = chardata; | |
| 1757 | + | |
| 1758 | + if (full_update) { | |
| 1759 | + for (i = 0; i < size; src ++, dst ++, i ++) | |
| 1760 | + console_write_ch(dst, VMEM2CHTYPE(*src)); | |
| 1761 | + | |
| 1762 | + dpy_update(s->ds, 0, 0, width, height); | |
| 1763 | + } else { | |
| 1764 | + c_max = 0; | |
| 1765 | + | |
| 1766 | + for (i = 0; i < size; src ++, dst ++, i ++) { | |
| 1767 | + console_write_ch(&val, VMEM2CHTYPE(*src)); | |
| 1768 | + if (*dst != val) { | |
| 1769 | + *dst = val; | |
| 1770 | + c_max = i; | |
| 1771 | + break; | |
| 1772 | + } | |
| 1773 | + } | |
| 1774 | + c_min = i; | |
| 1775 | + for (; i < size; src ++, dst ++, i ++) { | |
| 1776 | + console_write_ch(&val, VMEM2CHTYPE(*src)); | |
| 1777 | + if (*dst != val) { | |
| 1778 | + *dst = val; | |
| 1779 | + c_max = i; | |
| 1780 | + } | |
| 1781 | + } | |
| 1782 | + | |
| 1783 | + if (c_min <= c_max) { | |
| 1784 | + i = TEXTMODE_Y(c_min); | |
| 1785 | + dpy_update(s->ds, 0, i, width, TEXTMODE_Y(c_max) - i + 1); | |
| 1786 | + } | |
| 1787 | + } | |
| 1788 | + | |
| 1789 | + return; | |
| 1790 | + case GMODE_GRAPH: | |
| 1791 | + if (!full_update) | |
| 1792 | + return; | |
| 1793 | + | |
| 1794 | + s->get_resolution(s, &width, &height); | |
| 1795 | + sprintf(msg_buffer, "%i x %i Graphic mode", width, height); | |
| 1796 | + break; | |
| 1797 | + case GMODE_BLANK: | |
| 1798 | + default: | |
| 1799 | + if (!full_update) | |
| 1800 | + return; | |
| 1801 | + | |
| 1802 | + sprintf(msg_buffer, "VGA Blank mode"); | |
| 1803 | + break; | |
| 1804 | + } | |
| 1805 | + | |
| 1806 | + /* Display a message */ | |
| 1807 | + dpy_cursor(s->ds, -1, -1); | |
| 1808 | + dpy_resize(s->ds, 60, 3); | |
| 1809 | + | |
| 1810 | + for (dst = chardata, i = 0; i < 60 * 3; i ++) | |
| 1811 | + console_write_ch(dst ++, ' '); | |
| 1812 | + | |
| 1813 | + size = strlen(msg_buffer); | |
| 1814 | + width = (60 - size) / 2; | |
| 1815 | + dst = chardata + 60 + width; | |
| 1816 | + for (i = 0; i < size; i ++) | |
| 1817 | + console_write_ch(dst ++, 0x00200100 | msg_buffer[i]); | |
| 1818 | + | |
| 1819 | + dpy_update(s->ds, 0, 0, 60, 3); | |
| 1820 | +} | |
| 1821 | + | |
| 1663 | 1822 | static CPUReadMemoryFunc *vga_mem_read[3] = { |
| 1664 | 1823 | vga_mem_readb, |
| 1665 | 1824 | vga_mem_readw, |
| ... | ... | @@ -1830,6 +1989,7 @@ void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base, |
| 1830 | 1989 | s->update = vga_update_display; |
| 1831 | 1990 | s->invalidate = vga_invalidate_display; |
| 1832 | 1991 | s->screen_dump = vga_screen_dump; |
| 1992 | + s->text_update = vga_update_text; | |
| 1833 | 1993 | } |
| 1834 | 1994 | |
| 1835 | 1995 | /* used by both ISA and PCI */ |
| ... | ... | @@ -1971,7 +2131,8 @@ int isa_vga_init(DisplayState *ds, uint8_t *vga_ram_base, |
| 1971 | 2131 | vga_common_init(s, ds, vga_ram_base, vga_ram_offset, vga_ram_size); |
| 1972 | 2132 | vga_init(s); |
| 1973 | 2133 | |
| 1974 | - graphic_console_init(s->ds, s->update, s->invalidate, s->screen_dump, s); | |
| 2134 | + graphic_console_init(s->ds, s->update, s->invalidate, s->screen_dump, | |
| 2135 | + s->text_update, s); | |
| 1975 | 2136 | |
| 1976 | 2137 | #ifdef CONFIG_BOCHS_VBE |
| 1977 | 2138 | /* XXX: use optimized standard vga accesses */ |
| ... | ... | @@ -1995,7 +2156,8 @@ int isa_vga_mm_init(DisplayState *ds, uint8_t *vga_ram_base, |
| 1995 | 2156 | vga_common_init(s, ds, vga_ram_base, vga_ram_offset, vga_ram_size); |
| 1996 | 2157 | vga_mm_init(s, vram_base, ctrl_base, it_shift); |
| 1997 | 2158 | |
| 1998 | - graphic_console_init(s->ds, s->update, s->invalidate, s->screen_dump, s); | |
| 2159 | + graphic_console_init(s->ds, s->update, s->invalidate, s->screen_dump, | |
| 2160 | + s->text_update, s); | |
| 1999 | 2161 | |
| 2000 | 2162 | #ifdef CONFIG_BOCHS_VBE |
| 2001 | 2163 | /* XXX: use optimized standard vga accesses */ |
| ... | ... | @@ -2023,7 +2185,8 @@ int pci_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base, |
| 2023 | 2185 | vga_common_init(s, ds, vga_ram_base, vga_ram_offset, vga_ram_size); |
| 2024 | 2186 | vga_init(s); |
| 2025 | 2187 | |
| 2026 | - graphic_console_init(s->ds, s->update, s->invalidate, s->screen_dump, s); | |
| 2188 | + graphic_console_init(s->ds, s->update, s->invalidate, s->screen_dump, | |
| 2189 | + s->text_update, s); | |
| 2027 | 2190 | |
| 2028 | 2191 | s->pci_dev = &d->dev; |
| 2029 | 2192 | ... | ... |
hw/vga_int.h
| ... | ... | @@ -139,6 +139,7 @@ |
| 139 | 139 | vga_hw_update_ptr update; \ |
| 140 | 140 | vga_hw_invalidate_ptr invalidate; \ |
| 141 | 141 | vga_hw_screen_dump_ptr screen_dump; \ |
| 142 | + vga_hw_text_update_ptr text_update; \ | |
| 142 | 143 | /* hardware mouse cursor support */ \ |
| 143 | 144 | uint32_t invalidated_y_table[VGA_MAX_HEIGHT / 32]; \ |
| 144 | 145 | void (*cursor_invalidate)(struct VGAState *s); \ | ... | ... |
hw/vmware_vga.c
| ... | ... | @@ -949,6 +949,14 @@ static void vmsvga_screen_dump(void *opaque, const char *filename) |
| 949 | 949 | } |
| 950 | 950 | } |
| 951 | 951 | |
| 952 | +static void vmsvga_text_update(void *opaque, console_ch_t *chardata) | |
| 953 | +{ | |
| 954 | + struct vmsvga_state_s *s = (struct vmsvga_state_s *) opaque; | |
| 955 | + | |
| 956 | + if (s->text_update) | |
| 957 | + s->text_update(opaque, chardata); | |
| 958 | +} | |
| 959 | + | |
| 952 | 960 | #ifdef DIRECT_VRAM |
| 953 | 961 | static uint32_t vmsvga_vram_readb(void *opaque, target_phys_addr_t addr) |
| 954 | 962 | { |
| ... | ... | @@ -1101,7 +1109,8 @@ static void vmsvga_init(struct vmsvga_state_s *s, DisplayState *ds, |
| 1101 | 1109 | iomemtype); |
| 1102 | 1110 | |
| 1103 | 1111 | graphic_console_init(ds, vmsvga_update_display, |
| 1104 | - vmsvga_invalidate_display, vmsvga_screen_dump, s); | |
| 1112 | + vmsvga_invalidate_display, vmsvga_screen_dump, | |
| 1113 | + vmsvga_text_update, s); | |
| 1105 | 1114 | |
| 1106 | 1115 | #ifdef EMBED_STDVGA |
| 1107 | 1116 | vga_common_init((VGAState *) s, ds, | ... | ... |
monitor.c
vl.c
| ... | ... | @@ -172,6 +172,7 @@ BlockDriverState *bs_snapshots; |
| 172 | 172 | int vga_ram_size; |
| 173 | 173 | static DisplayState display_state; |
| 174 | 174 | int nographic; |
| 175 | +int curses; | |
| 175 | 176 | const char* keyboard_layout = NULL; |
| 176 | 177 | int64_t ticks_per_sec; |
| 177 | 178 | int ram_size; |
| ... | ... | @@ -7652,6 +7653,9 @@ static void help(int exitcode) |
| 7652 | 7653 | " (default is CL-GD5446 PCI VGA)\n" |
| 7653 | 7654 | "-no-acpi disable ACPI\n" |
| 7654 | 7655 | #endif |
| 7656 | +#ifdef CONFIG_CURSES | |
| 7657 | + "-curses use a curses/ncurses interface instead of SDL\n" | |
| 7658 | +#endif | |
| 7655 | 7659 | "-no-reboot exit instead of rebooting\n" |
| 7656 | 7660 | "-loadvm file start right away with a saved state (loadvm in monitor)\n" |
| 7657 | 7661 | "-vnc display start a VNC server on display\n" |
| ... | ... | @@ -7757,6 +7761,7 @@ enum { |
| 7757 | 7761 | QEMU_OPTION_smp, |
| 7758 | 7762 | QEMU_OPTION_vnc, |
| 7759 | 7763 | QEMU_OPTION_no_acpi, |
| 7764 | + QEMU_OPTION_curses, | |
| 7760 | 7765 | QEMU_OPTION_no_reboot, |
| 7761 | 7766 | QEMU_OPTION_show_cursor, |
| 7762 | 7767 | QEMU_OPTION_daemonize, |
| ... | ... | @@ -7853,6 +7858,9 @@ const QEMUOption qemu_options[] = { |
| 7853 | 7858 | { "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice }, |
| 7854 | 7859 | { "smp", HAS_ARG, QEMU_OPTION_smp }, |
| 7855 | 7860 | { "vnc", HAS_ARG, QEMU_OPTION_vnc }, |
| 7861 | +#ifdef CONFIG_CURSES | |
| 7862 | + { "curses", 0, QEMU_OPTION_curses }, | |
| 7863 | +#endif | |
| 7856 | 7864 | |
| 7857 | 7865 | /* temporary options */ |
| 7858 | 7866 | { "usb", 0, QEMU_OPTION_usb }, |
| ... | ... | @@ -8189,6 +8197,7 @@ int main(int argc, char **argv) |
| 8189 | 8197 | #endif |
| 8190 | 8198 | snapshot = 0; |
| 8191 | 8199 | nographic = 0; |
| 8200 | + curses = 0; | |
| 8192 | 8201 | kernel_filename = NULL; |
| 8193 | 8202 | kernel_cmdline = ""; |
| 8194 | 8203 | cyls = heads = secs = 0; |
| ... | ... | @@ -8363,6 +8372,11 @@ int main(int argc, char **argv) |
| 8363 | 8372 | pstrcpy(monitor_device, sizeof(monitor_device), "stdio"); |
| 8364 | 8373 | nographic = 1; |
| 8365 | 8374 | break; |
| 8375 | +#ifdef CONFIG_CURSES | |
| 8376 | + case QEMU_OPTION_curses: | |
| 8377 | + curses = 1; | |
| 8378 | + break; | |
| 8379 | +#endif | |
| 8366 | 8380 | case QEMU_OPTION_portrait: |
| 8367 | 8381 | graphic_rotate = 1; |
| 8368 | 8382 | break; |
| ... | ... | @@ -8903,13 +8917,23 @@ int main(int argc, char **argv) |
| 8903 | 8917 | /* terminal init */ |
| 8904 | 8918 | memset(&display_state, 0, sizeof(display_state)); |
| 8905 | 8919 | if (nographic) { |
| 8920 | + if (curses) { | |
| 8921 | + fprintf(stderr, "fatal: -nographic can't be used with -curses\n"); | |
| 8922 | + exit(1); | |
| 8923 | + } | |
| 8906 | 8924 | /* nearly nothing to do */ |
| 8907 | 8925 | dumb_display_init(ds); |
| 8908 | 8926 | } else if (vnc_display != NULL) { |
| 8909 | 8927 | vnc_display_init(ds); |
| 8910 | 8928 | if (vnc_display_open(ds, vnc_display) < 0) |
| 8911 | 8929 | exit(1); |
| 8912 | - } else { | |
| 8930 | + } else | |
| 8931 | +#if defined(CONFIG_CURSES) | |
| 8932 | + if (curses) { | |
| 8933 | + curses_display_init(ds, full_screen); | |
| 8934 | + } else | |
| 8935 | +#endif | |
| 8936 | + { | |
| 8913 | 8937 | #if defined(CONFIG_SDL) |
| 8914 | 8938 | sdl_display_init(ds, full_screen, no_frame); |
| 8915 | 8939 | #elif defined(CONFIG_COCOA) | ... | ... |