* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+
#include "qemu/osdep.h"
#ifndef _WIN32
#endif
#include <locale.h>
#include <wchar.h>
-#include <langinfo.h>
#include <iconv.h>
#include "qapi/error.h"
-#include "qemu-common.h"
+#include "qemu/module.h"
#include "ui/console.h"
#include "ui/input.h"
#include "sysemu/sysemu.h"
+#if defined(__APPLE__) || defined(__OpenBSD__)
+#define _XOPEN_SOURCE_EXTENDED 1
+#endif
+
/* KEY_EVENT is defined in wincon.h and in curses.h. Avoid redefinition. */
#undef KEY_EVENT
#include <curses.h>
};
static DisplayChangeListener *dcl;
-static console_ch_t screen[160 * 100];
+static console_ch_t *screen;
static WINDOW *screenpad = NULL;
static int width, height, gwidth, gheight, invalidate;
static int px, py, sminx, sminy, smaxx, smaxy;
static const char *font_charset = "CP437";
-static cchar_t vga_to_curses[256];
+static cchar_t *vga_to_curses;
static void curses_update(DisplayChangeListener *dcl,
int x, int y, int w, int h)
{
console_ch_t *line;
- cchar_t curses_line[width];
+ g_autofree cchar_t *curses_line = g_new(cchar_t, width);
+ wchar_t wch[CCHARW_MAX];
+ attr_t attrs;
+ short colors;
+ int ret;
line = screen + y * width;
for (h += y; y < h; y ++, line += width) {
for (x = 0; x < width; x++) {
- chtype ch = line[x] & 0xff;
- chtype at = line[x] & ~0xff;
- if (vga_to_curses[ch].chars[0]) {
- curses_line[x] = vga_to_curses[ch];
- } else {
- curses_line[x] = (cchar_t) {
- .chars[0] = ch,
- };
+ chtype ch = line[x] & A_CHARTEXT;
+ chtype at = line[x] & A_ATTRIBUTES;
+ short color_pair = PAIR_NUMBER(line[x]);
+
+ ret = getcchar(&vga_to_curses[ch], wch, &attrs, &colors, NULL);
+ if (ret == ERR || wch[0] == 0) {
+ wch[0] = ch;
+ wch[1] = 0;
}
- curses_line[x].attr |= at;
+ setcchar(&curses_line[x], wch, at, color_pair, NULL);
}
mvwadd_wchnstr(screenpad, y, 0, curses_line, width);
}
static void curses_calc_pad(void)
{
- if (qemu_console_is_fixedsize(NULL)) {
+ if (qemu_console_is_fixedsize(dcl->con)) {
width = gwidth;
height = gheight;
} else {
curs_set(1);
/* it seems that curs_set(1) must always be called before
* curs_set(2) for the latter to have effect */
- if (!qemu_console_is_graphic(NULL)) {
+ if (!qemu_console_is_graphic(dcl->con)) {
curs_set(2);
}
return;
case ERR:
ret = -1;
break;
+ default:
+ abort();
}
return ret;
}
static void curses_refresh(DisplayChangeListener *dcl)
{
int chr, keysym, keycode, keycode_alt;
- enum maybe_keycode maybe_keycode;
+ enum maybe_keycode maybe_keycode = CURSES_KEYCODE;
curses_winch_check();
clear();
refresh();
curses_calc_pad();
- graphic_hw_invalidate(NULL);
+ graphic_hw_invalidate(dcl->con);
invalidate = 0;
}
- graphic_hw_text_update(NULL, screen);
+ graphic_hw_text_update(dcl->con, screen);
while (1) {
/* while there are any pending key strokes to process */
/* alt or esc key */
if (keycode == 1) {
- enum maybe_keycode next_maybe_keycode;
+ enum maybe_keycode next_maybe_keycode = CURSES_KEYCODE;
int nextchr = console_getch(&next_maybe_keycode);
if (nextchr != -1) {
/* process keys reserved for qemu */
if (keycode >= QEMU_KEY_CONSOLE0 &&
keycode < QEMU_KEY_CONSOLE0 + 9) {
- erase();
- wnoutrefresh(stdscr);
- console_select(keycode - QEMU_KEY_CONSOLE0);
-
- invalidate = 1;
+ QemuConsole *con = qemu_console_lookup_by_index(keycode - QEMU_KEY_CONSOLE0);
+ if (con) {
+ erase();
+ wnoutrefresh(stdscr);
+ unregister_displaychangelistener(dcl);
+ dcl->con = con;
+ register_displaychangelistener(dcl);
+
+ invalidate = 1;
+ }
continue;
}
}
if (keycode == -1)
continue;
- if (qemu_console_is_graphic(NULL)) {
+ if (qemu_console_is_graphic(dcl->con)) {
/* since terminals don't know about key press and release
* events, we need to emit both for each key received */
if (keycode & SHIFT) {
- qemu_input_event_send_key_number(NULL, SHIFT_CODE, true);
+ qemu_input_event_send_key_number(dcl->con, SHIFT_CODE, true);
qemu_input_event_send_key_delay(0);
}
if (keycode & CNTRL) {
- qemu_input_event_send_key_number(NULL, CNTRL_CODE, true);
+ qemu_input_event_send_key_number(dcl->con, CNTRL_CODE, true);
qemu_input_event_send_key_delay(0);
}
if (keycode & ALT) {
- qemu_input_event_send_key_number(NULL, ALT_CODE, true);
+ qemu_input_event_send_key_number(dcl->con, ALT_CODE, true);
qemu_input_event_send_key_delay(0);
}
if (keycode & ALTGR) {
- qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, true);
+ qemu_input_event_send_key_number(dcl->con, GREY | ALT_CODE, true);
qemu_input_event_send_key_delay(0);
}
- qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, true);
+ qemu_input_event_send_key_number(dcl->con, keycode & KEY_MASK, true);
qemu_input_event_send_key_delay(0);
- qemu_input_event_send_key_number(NULL, keycode & KEY_MASK, false);
+ qemu_input_event_send_key_number(dcl->con, keycode & KEY_MASK, false);
qemu_input_event_send_key_delay(0);
if (keycode & ALTGR) {
- qemu_input_event_send_key_number(NULL, GREY | ALT_CODE, false);
+ qemu_input_event_send_key_number(dcl->con, GREY | ALT_CODE, false);
qemu_input_event_send_key_delay(0);
}
if (keycode & ALT) {
- qemu_input_event_send_key_number(NULL, ALT_CODE, false);
+ qemu_input_event_send_key_number(dcl->con, ALT_CODE, false);
qemu_input_event_send_key_delay(0);
}
if (keycode & CNTRL) {
- qemu_input_event_send_key_number(NULL, CNTRL_CODE, false);
+ qemu_input_event_send_key_number(dcl->con, CNTRL_CODE, false);
qemu_input_event_send_key_delay(0);
}
if (keycode & SHIFT) {
- qemu_input_event_send_key_number(NULL, SHIFT_CODE, false);
+ qemu_input_event_send_key_number(dcl->con, SHIFT_CODE, false);
qemu_input_event_send_key_delay(0);
}
} else {
if (keysym == -1)
keysym = chr;
- kbd_put_keysym(keysym);
+ qemu_text_console_put_keysym(QEMU_TEXT_CONSOLE(dcl->con), keysym);
}
}
}
static void curses_atexit(void)
{
endwin();
+ g_free(vga_to_curses);
+ g_free(screen);
}
+/*
+ * In the following:
+ * - fch is the font glyph number
+ * - uch is the unicode value
+ * - wch is the wchar_t value (may not be unicode, e.g. on BSD/solaris)
+ * - mbch is the native local-dependent multibyte representation
+ */
+
/* Setup wchar glyph for one UCS-2 char */
-static void convert_ucs(int glyph, uint16_t ch, iconv_t conv)
+static void convert_ucs(unsigned char fch, uint16_t uch, iconv_t conv)
{
- wchar_t wch;
- char *pch, *pwch;
- size_t sch, swch;
-
- pch = (char *) &ch;
- pwch = (char *) &wch;
- sch = sizeof(ch);
- swch = sizeof(wch);
-
- if (iconv(conv, &pch, &sch, &pwch, &swch) == (size_t) -1) {
- fprintf(stderr, "Could not convert 0x%04x from UCS-2 to WCHAR_T: %s\n",
- ch, strerror(errno));
- } else {
- vga_to_curses[glyph].chars[0] = wch;
+ char mbch[MB_LEN_MAX];
+ wchar_t wch[2];
+ char *puch, *pmbch;
+ size_t such, smbch;
+ mbstate_t ps;
+
+ puch = (char *) &uch;
+ pmbch = (char *) mbch;
+ such = sizeof(uch);
+ smbch = sizeof(mbch);
+
+ if (iconv(conv, &puch, &such, &pmbch, &smbch) == (size_t) -1) {
+ fprintf(stderr, "Could not convert 0x%04x "
+ "from UCS-2 to a multibyte character: %s\n",
+ uch, strerror(errno));
+ return;
+ }
+
+ memset(&ps, 0, sizeof(ps));
+ if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
+ fprintf(stderr, "Could not convert 0x%04x "
+ "from a multibyte character to wchar_t: %s\n",
+ uch, strerror(errno));
+ return;
}
+
+ wch[1] = 0;
+ setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
}
/* Setup wchar glyph for one font character */
-static void convert_font(unsigned char ch, iconv_t conv)
+static void convert_font(unsigned char fch, iconv_t conv)
{
- wchar_t wch;
- char *pch, *pwch;
- size_t sch, swch;
-
- pch = (char *) &ch;
- pwch = (char *) &wch;
- sch = sizeof(ch);
- swch = sizeof(wch);
-
- if (iconv(conv, &pch, &sch, &pwch, &swch) == (size_t) -1) {
- fprintf(stderr, "Could not convert 0x%02x from %s to WCHAR_T: %s\n",
- ch, font_charset, strerror(errno));
- } else {
- vga_to_curses[ch].chars[0] = wch;
+ char mbch[MB_LEN_MAX];
+ wchar_t wch[2];
+ char *pfch, *pmbch;
+ size_t sfch, smbch;
+ mbstate_t ps;
+
+ pfch = (char *) &fch;
+ pmbch = (char *) &mbch;
+ sfch = sizeof(fch);
+ smbch = sizeof(mbch);
+
+ if (iconv(conv, &pfch, &sfch, &pmbch, &smbch) == (size_t) -1) {
+ fprintf(stderr, "Could not convert font glyph 0x%02x "
+ "from %s to a multibyte character: %s\n",
+ fch, font_charset, strerror(errno));
+ return;
+ }
+
+ memset(&ps, 0, sizeof(ps));
+ if (mbrtowc(&wch[0], mbch, sizeof(mbch) - smbch, &ps) == -1) {
+ fprintf(stderr, "Could not convert font glyph 0x%02x "
+ "from a multibyte character to wchar_t: %s\n",
+ fch, strerror(errno));
+ return;
}
+
+ wch[1] = 0;
+ setcchar(&vga_to_curses[fch], wch, 0, 0, NULL);
}
/* Convert one wchar to UCS-2 */
static uint16_t get_ucs(wchar_t wch, iconv_t conv)
{
- uint16_t ch;
- char *pch, *pwch;
- size_t sch, swch;
-
- pch = (char *) &ch;
- pwch = (char *) &wch;
- sch = sizeof(ch);
- swch = sizeof(wch);
-
- if (iconv(conv, &pwch, &swch, &pch, &sch) == (size_t) -1) {
- fprintf(stderr, "Could not convert 0x%02x from WCHAR_T to UCS-2: %s\n",
- wch, strerror(errno));
+ char mbch[MB_LEN_MAX];
+ uint16_t uch;
+ char *pmbch, *puch;
+ size_t smbch, such;
+ mbstate_t ps;
+ int ret;
+
+ memset(&ps, 0, sizeof(ps));
+ ret = wcrtomb(mbch, wch, &ps);
+ if (ret == -1) {
+ fprintf(stderr, "Could not convert 0x%04lx "
+ "from wchar_t to a multibyte character: %s\n",
+ (unsigned long)wch, strerror(errno));
+ return 0xFFFD;
+ }
+
+ pmbch = (char *) mbch;
+ puch = (char *) &uch;
+ smbch = ret;
+ such = sizeof(uch);
+
+ if (iconv(conv, &pmbch, &smbch, &puch, &such) == (size_t) -1) {
+ fprintf(stderr, "Could not convert 0x%04lx "
+ "from a multibyte character to UCS-2 : %s\n",
+ (unsigned long)wch, strerror(errno));
return 0xFFFD;
}
- return ch;
+ return uch;
}
/*
*/
static void font_setup(void)
{
+ iconv_t ucs2_to_nativecharset;
+ iconv_t nativecharset_to_ucs2;
+ iconv_t font_conv;
+ int i;
+ g_autofree gchar *local_codeset = g_get_codeset();
+
/*
* Control characters are normally non-printable, but VGA does have
* well-known glyphs for them.
*/
- static uint16_t control_characters[0x20] = {
+ static const uint16_t control_characters[0x20] = {
0x0020,
0x263a,
0x263b,
0x25bc
};
- iconv_t ucs_to_wchar_conv;
- iconv_t wchar_to_ucs_conv;
- iconv_t font_conv;
- int i;
-
- ucs_to_wchar_conv = iconv_open("WCHAR_T", "UCS-2");
- if (ucs_to_wchar_conv == (iconv_t) -1) {
+ ucs2_to_nativecharset = iconv_open(local_codeset, "UCS-2");
+ if (ucs2_to_nativecharset == (iconv_t) -1) {
fprintf(stderr, "Could not convert font glyphs from UCS-2: '%s'\n",
strerror(errno));
exit(1);
}
- wchar_to_ucs_conv = iconv_open("UCS-2", "WCHAR_T");
- if (wchar_to_ucs_conv == (iconv_t) -1) {
- iconv_close(ucs_to_wchar_conv);
+ nativecharset_to_ucs2 = iconv_open("UCS-2", local_codeset);
+ if (nativecharset_to_ucs2 == (iconv_t) -1) {
+ iconv_close(ucs2_to_nativecharset);
fprintf(stderr, "Could not convert font glyphs to UCS-2: '%s'\n",
strerror(errno));
exit(1);
}
- font_conv = iconv_open("WCHAR_T", font_charset);
+ font_conv = iconv_open(local_codeset, font_charset);
if (font_conv == (iconv_t) -1) {
- iconv_close(ucs_to_wchar_conv);
- iconv_close(wchar_to_ucs_conv);
+ iconv_close(ucs2_to_nativecharset);
+ iconv_close(nativecharset_to_ucs2);
fprintf(stderr, "Could not convert font glyphs from %s: '%s'\n",
font_charset, strerror(errno));
exit(1);
/* Control characters */
for (i = 0; i <= 0x1F; i++) {
- convert_ucs(i, control_characters[i], ucs_to_wchar_conv);
+ convert_ucs(i, control_characters[i], ucs2_to_nativecharset);
}
for (i = 0x20; i <= 0xFF; i++) {
}
/* DEL */
- convert_ucs(0x7F, 0x2302, ucs_to_wchar_conv);
+ convert_ucs(0x7F, 0x2302, ucs2_to_nativecharset);
- if (strcmp(nl_langinfo(CODESET), "UTF-8")) {
+ if (strcmp(local_codeset, "UTF-8")) {
/* Non-Unicode capable, use termcap equivalents for those available */
for (i = 0; i <= 0xFF; i++) {
- switch (get_ucs(vga_to_curses[i].chars[0], wchar_to_ucs_conv)) {
+ wchar_t wch[CCHARW_MAX];
+ attr_t attr;
+ short color;
+ int ret;
+
+ ret = getcchar(&vga_to_curses[i], wch, &attr, &color, NULL);
+ if (ret == ERR)
+ continue;
+
+ switch (get_ucs(wch[0], nativecharset_to_ucs2)) {
case 0x00a3:
vga_to_curses[i] = *WACS_STERLING;
break;
}
}
}
- iconv_close(ucs_to_wchar_conv);
- iconv_close(wchar_to_ucs_conv);
+ iconv_close(ucs2_to_nativecharset);
+ iconv_close(nativecharset_to_ucs2);
iconv_close(font_conv);
}
if (opts->u.curses.charset) {
font_charset = opts->u.curses.charset;
}
+ screen = g_new0(console_ch_t, 160 * 100);
+ vga_to_curses = g_new0(cchar_t, 256);
curses_setup();
curses_keyboard_setup();
atexit(curses_atexit);
curses_winch_init();
dcl = g_new0(DisplayChangeListener, 1);
+ dcl->con = qemu_console_lookup_default();
dcl->ops = &dcl_ops;
register_displaychangelistener(dcl);