|
|
1.1 ! root 1: /* ! 2: * QEMU curses/ncurses display driver ! 3: * ! 4: * Copyright (c) 2005 Andrzej Zaborowski <[email protected]> ! 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: #include <curses.h> ! 25: ! 26: #ifndef _WIN32 ! 27: #include <signal.h> ! 28: #include <sys/ioctl.h> ! 29: #include <termios.h> ! 30: #endif ! 31: ! 32: #ifdef __OpenBSD__ ! 33: #define resize_term resizeterm ! 34: #endif ! 35: ! 36: #include "qemu-common.h" ! 37: #include "console.h" ! 38: #include "sysemu.h" ! 39: ! 40: #define FONT_HEIGHT 16 ! 41: #define FONT_WIDTH 8 ! 42: ! 43: static console_ch_t screen[160 * 100]; ! 44: static WINDOW *screenpad = NULL; ! 45: static int width, height, gwidth, gheight, invalidate; ! 46: static int px, py, sminx, sminy, smaxx, smaxy; ! 47: ! 48: static void curses_update(DisplayState *ds, int x, int y, int w, int h) ! 49: { ! 50: chtype *line; ! 51: ! 52: line = ((chtype *) screen) + y * width; ! 53: for (h += y; y < h; y ++, line += width) ! 54: mvwaddchnstr(screenpad, y, 0, line, width); ! 55: ! 56: pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1); ! 57: refresh(); ! 58: } ! 59: ! 60: static void curses_calc_pad(void) ! 61: { ! 62: if (is_fixedsize_console()) { ! 63: width = gwidth; ! 64: height = gheight; ! 65: } else { ! 66: width = COLS; ! 67: height = LINES; ! 68: } ! 69: ! 70: if (screenpad) ! 71: delwin(screenpad); ! 72: ! 73: clear(); ! 74: refresh(); ! 75: ! 76: screenpad = newpad(height, width); ! 77: ! 78: if (width > COLS) { ! 79: px = (width - COLS) / 2; ! 80: sminx = 0; ! 81: smaxx = COLS; ! 82: } else { ! 83: px = 0; ! 84: sminx = (COLS - width) / 2; ! 85: smaxx = sminx + width; ! 86: } ! 87: ! 88: if (height > LINES) { ! 89: py = (height - LINES) / 2; ! 90: sminy = 0; ! 91: smaxy = LINES; ! 92: } else { ! 93: py = 0; ! 94: sminy = (LINES - height) / 2; ! 95: smaxy = sminy + height; ! 96: } ! 97: } ! 98: ! 99: static void curses_resize(DisplayState *ds) ! 100: { ! 101: if (ds_get_width(ds) == gwidth && ds_get_height(ds) == gheight) ! 102: return; ! 103: ! 104: gwidth = ds_get_width(ds); ! 105: gheight = ds_get_height(ds); ! 106: ! 107: curses_calc_pad(); ! 108: ds->surface->width = width * FONT_WIDTH; ! 109: ds->surface->height = height * FONT_HEIGHT; ! 110: } ! 111: ! 112: #ifndef _WIN32 ! 113: #if defined(SIGWINCH) && defined(KEY_RESIZE) ! 114: static void curses_winch_handler(int signum) ! 115: { ! 116: struct winsize { ! 117: unsigned short ws_row; ! 118: unsigned short ws_col; ! 119: unsigned short ws_xpixel; /* unused */ ! 120: unsigned short ws_ypixel; /* unused */ ! 121: } ws; ! 122: ! 123: /* terminal size changed */ ! 124: if (ioctl(1, TIOCGWINSZ, &ws) == -1) ! 125: return; ! 126: ! 127: resize_term(ws.ws_row, ws.ws_col); ! 128: curses_calc_pad(); ! 129: invalidate = 1; ! 130: ! 131: /* some systems require this */ ! 132: signal(SIGWINCH, curses_winch_handler); ! 133: } ! 134: #endif ! 135: #endif ! 136: ! 137: static void curses_cursor_position(DisplayState *ds, int x, int y) ! 138: { ! 139: if (x >= 0) { ! 140: x = sminx + x - px; ! 141: y = sminy + y - py; ! 142: ! 143: if (x >= 0 && y >= 0 && x < COLS && y < LINES) { ! 144: move(y, x); ! 145: curs_set(1); ! 146: /* it seems that curs_set(1) must always be called before ! 147: * curs_set(2) for the latter to have effect */ ! 148: if (!is_graphic_console()) ! 149: curs_set(2); ! 150: return; ! 151: } ! 152: } ! 153: ! 154: curs_set(0); ! 155: } ! 156: ! 157: /* generic keyboard conversion */ ! 158: ! 159: #include "curses_keys.h" ! 160: ! 161: static kbd_layout_t *kbd_layout = NULL; ! 162: ! 163: static void curses_refresh(DisplayState *ds) ! 164: { ! 165: int chr, nextchr, keysym, keycode, keycode_alt; ! 166: ! 167: if (invalidate) { ! 168: clear(); ! 169: refresh(); ! 170: curses_calc_pad(); ! 171: ds->surface->width = FONT_WIDTH * width; ! 172: ds->surface->height = FONT_HEIGHT * height; ! 173: vga_hw_invalidate(); ! 174: invalidate = 0; ! 175: } ! 176: ! 177: vga_hw_text_update(screen); ! 178: ! 179: nextchr = ERR; ! 180: while (1) { ! 181: /* while there are any pending key strokes to process */ ! 182: if (nextchr == ERR) ! 183: chr = getch(); ! 184: else { ! 185: chr = nextchr; ! 186: nextchr = ERR; ! 187: } ! 188: ! 189: if (chr == ERR) ! 190: break; ! 191: ! 192: #ifdef KEY_RESIZE ! 193: /* this shouldn't occur when we use a custom SIGWINCH handler */ ! 194: if (chr == KEY_RESIZE) { ! 195: clear(); ! 196: refresh(); ! 197: curses_calc_pad(); ! 198: curses_update(ds, 0, 0, width, height); ! 199: ds->surface->width = FONT_WIDTH * width; ! 200: ds->surface->height = FONT_HEIGHT * height; ! 201: continue; ! 202: } ! 203: #endif ! 204: ! 205: keycode = curses2keycode[chr]; ! 206: keycode_alt = 0; ! 207: ! 208: /* alt key */ ! 209: if (keycode == 1) { ! 210: nextchr = getch(); ! 211: ! 212: if (nextchr != ERR) { ! 213: chr = nextchr; ! 214: keycode_alt = ALT; ! 215: keycode = curses2keycode[nextchr]; ! 216: nextchr = ERR; ! 217: ! 218: if (keycode != -1) { ! 219: keycode |= ALT; ! 220: ! 221: /* process keys reserved for qemu */ ! 222: if (keycode >= QEMU_KEY_CONSOLE0 && ! 223: keycode < QEMU_KEY_CONSOLE0 + 9) { ! 224: erase(); ! 225: wnoutrefresh(stdscr); ! 226: console_select(keycode - QEMU_KEY_CONSOLE0); ! 227: ! 228: invalidate = 1; ! 229: continue; ! 230: } ! 231: } ! 232: } ! 233: } ! 234: ! 235: if (kbd_layout) { ! 236: keysym = -1; ! 237: if (chr < CURSES_KEYS) ! 238: keysym = curses2keysym[chr]; ! 239: ! 240: if (keysym == -1) { ! 241: if (chr < ' ') ! 242: keysym = (chr + '@' - 'A' + 'a') | KEYSYM_CNTRL; ! 243: else ! 244: keysym = chr; ! 245: } ! 246: ! 247: keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK); ! 248: if (keycode == 0) ! 249: continue; ! 250: ! 251: keycode |= (keysym & ~KEYSYM_MASK) >> 16; ! 252: keycode |= keycode_alt; ! 253: } ! 254: ! 255: if (keycode == -1) ! 256: continue; ! 257: ! 258: if (is_graphic_console()) { ! 259: /* since terminals don't know about key press and release ! 260: * events, we need to emit both for each key received */ ! 261: if (keycode & SHIFT) ! 262: kbd_put_keycode(SHIFT_CODE); ! 263: if (keycode & CNTRL) ! 264: kbd_put_keycode(CNTRL_CODE); ! 265: if (keycode & ALT) ! 266: kbd_put_keycode(ALT_CODE); ! 267: if (keycode & ALTGR) { ! 268: kbd_put_keycode(SCANCODE_EMUL0); ! 269: kbd_put_keycode(ALT_CODE); ! 270: } ! 271: if (keycode & GREY) ! 272: kbd_put_keycode(GREY_CODE); ! 273: kbd_put_keycode(keycode & KEY_MASK); ! 274: if (keycode & GREY) ! 275: kbd_put_keycode(GREY_CODE); ! 276: kbd_put_keycode((keycode & KEY_MASK) | KEY_RELEASE); ! 277: if (keycode & ALTGR) { ! 278: kbd_put_keycode(SCANCODE_EMUL0); ! 279: kbd_put_keycode(ALT_CODE | KEY_RELEASE); ! 280: } ! 281: if (keycode & ALT) ! 282: kbd_put_keycode(ALT_CODE | KEY_RELEASE); ! 283: if (keycode & CNTRL) ! 284: kbd_put_keycode(CNTRL_CODE | KEY_RELEASE); ! 285: if (keycode & SHIFT) ! 286: kbd_put_keycode(SHIFT_CODE | KEY_RELEASE); ! 287: } else { ! 288: keysym = curses2qemu[chr]; ! 289: if (keysym == -1) ! 290: keysym = chr; ! 291: ! 292: kbd_put_keysym(keysym); ! 293: } ! 294: } ! 295: } ! 296: ! 297: static void curses_atexit(void) ! 298: { ! 299: endwin(); ! 300: } ! 301: ! 302: static void curses_setup(void) ! 303: { ! 304: int i, colour_default[8] = { ! 305: COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN, ! 306: COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE, ! 307: }; ! 308: ! 309: /* input as raw as possible, let everything be interpreted ! 310: * by the guest system */ ! 311: initscr(); noecho(); intrflush(stdscr, FALSE); ! 312: nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE); ! 313: start_color(); raw(); scrollok(stdscr, FALSE); ! 314: ! 315: for (i = 0; i < 64; i ++) ! 316: init_pair(i, colour_default[i & 7], colour_default[i >> 3]); ! 317: } ! 318: ! 319: static void curses_keyboard_setup(void) ! 320: { ! 321: #if defined(__APPLE__) ! 322: /* always use generic keymaps */ ! 323: if (!keyboard_layout) ! 324: keyboard_layout = "en-us"; ! 325: #endif ! 326: if(keyboard_layout) { ! 327: kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout); ! 328: if (!kbd_layout) ! 329: exit(1); ! 330: } ! 331: } ! 332: ! 333: void curses_display_init(DisplayState *ds, int full_screen) ! 334: { ! 335: DisplayChangeListener *dcl; ! 336: #ifndef _WIN32 ! 337: if (!isatty(1)) { ! 338: fprintf(stderr, "We need a terminal output\n"); ! 339: exit(1); ! 340: } ! 341: #endif ! 342: ! 343: curses_setup(); ! 344: curses_keyboard_setup(); ! 345: atexit(curses_atexit); ! 346: ! 347: #ifndef _WIN32 ! 348: #if defined(SIGWINCH) && defined(KEY_RESIZE) ! 349: /* some curses implementations provide a handler, but we ! 350: * want to be sure this is handled regardless of the library */ ! 351: signal(SIGWINCH, curses_winch_handler); ! 352: #endif ! 353: #endif ! 354: ! 355: dcl = (DisplayChangeListener *) qemu_mallocz(sizeof(DisplayChangeListener)); ! 356: dcl->dpy_update = curses_update; ! 357: dcl->dpy_resize = curses_resize; ! 358: dcl->dpy_refresh = curses_refresh; ! 359: dcl->dpy_text_cursor = curses_cursor_position; ! 360: register_displaychangelistener(ds, dcl); ! 361: qemu_free_displaysurface(ds); ! 362: ds->surface = qemu_create_displaysurface_from(640, 400, 0, 0, (uint8_t*) screen); ! 363: ! 364: invalidate = 1; ! 365: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.