The back ends now contain their own copies of the Config structure,
[u/mdw/putty] / unix / pterm.c
index 94b732d..f0f71e8 100644 (file)
@@ -9,6 +9,7 @@
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
+#include <signal.h>
 #include <stdio.h>
 #include <time.h>
 #include <errno.h>
@@ -23,6 +24,7 @@
 #include <X11/Xutil.h>
 
 #define PUTTY_DO_GLOBALS              /* actually _define_ globals */
+
 #include "putty.h"
 #include "terminal.h"
 
@@ -37,19 +39,24 @@ struct gui_data {
     GtkBox *hbox;
     GtkAdjustment *sbar_adjust;
     GdkPixmap *pixmap;
-    GdkFont *fonts[2];                 /* normal and bold (for now!) */
+    GdkFont *fonts[4];                 /* normal, bold, wide, widebold */
+    struct {
+       int charset;
+       int is_wide;
+    } fontinfo[4];
     GdkCursor *rawcursor, *textcursor, *blankcursor, *currcursor;
     GdkColor cols[NCOLOURS];
     GdkColormap *colmap;
     wchar_t *pastein_data;
+    int direct_to_font;
     int pastein_data_len;
-    char *pasteout_data;
-    int pasteout_data_len;
+    char *pasteout_data, *pasteout_data_utf8;
+    int pasteout_data_len, pasteout_data_utf8_len;
     int font_width, font_height;
     int ignore_sbar;
     int mouseptr_visible;
     guint term_paste_idle_id;
-    GdkAtom compound_text_atom;
+    GdkAtom compound_text_atom, utf8_string_atom;
     int alt_keycode;
     int alt_digits;
     char wintitle[sizeof(((Config *)0)->wintitle)];
@@ -76,6 +83,23 @@ char *x_get_default(char *key)
     return XGetDefault(GDK_DISPLAY(), app_name, key);
 }
 
+/*
+ * Default settings that are specific to pterm.
+ */
+char *platform_default_s(char *name)
+{
+    if (!strcmp(name, "Font"))
+       return "fixed";        /* COE_NORMAL works badly in an xterm */
+    return NULL;
+}
+
+int platform_default_i(char *name, int def)
+{
+    if (!strcmp(name, "CloseOnExit"))
+       return COE_ALWAYS;             /* COE_NORMAL works badly in an xterm */
+    return def;
+}
+
 void ldisc_update(void *frontend, int echo, int edit)
 {
     /*
@@ -830,7 +854,28 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
        printf("\n");
 #endif
 
-       ldisc_send(inst->ldisc, output+start, end-start, 1);
+       if (!inst->direct_to_font) {
+           /*
+            * The stuff we've just generated is assumed to be
+            * ISO-8859-1! This sounds insane, but `man
+            * XLookupString' agrees: strings of this type returned
+            * from the X server are hardcoded to 8859-1. Strictly
+            * speaking we should be doing this using some sort of
+            * GtkIMContext, which (if we're lucky) would give us
+            * our data directly in Unicode; but that's not
+            * supported in GTK 1.2 as far as I can tell, and it's
+            * poorly documented even in 2.0, so it'll have to
+            * wait.
+            */
+           lpage_send(inst->ldisc, CS_ISO8859_1, output+start, end-start, 1);
+       } else {
+           /*
+            * In direct-to-font mode, we just send the string
+            * exactly as we received it.
+            */
+           ldisc_send(inst->ldisc, output+start, end-start, 1);
+       }
+
        show_mouseptr(inst, 0);
        term_seen_key_event(inst->term);
        term_out(inst->term);
@@ -1197,10 +1242,48 @@ void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
     struct gui_data *inst = (struct gui_data *)frontend;
     if (inst->pasteout_data)
        sfree(inst->pasteout_data);
-    inst->pasteout_data = smalloc(len);
-    inst->pasteout_data_len = len;
-    wc_to_mb(0, 0, data, len, inst->pasteout_data, inst->pasteout_data_len,
-            NULL, NULL);
+    if (inst->pasteout_data_utf8)
+       sfree(inst->pasteout_data_utf8);
+
+    /*
+     * Set up UTF-8 paste data. This only happens if we aren't in
+     * direct-to-font mode using the D800 hack.
+     */
+    if (!inst->direct_to_font) {
+       wchar_t *tmp = data;
+       int tmplen = len;
+
+       inst->pasteout_data_utf8 = smalloc(len*6);
+       inst->pasteout_data_utf8_len = len*6;
+       inst->pasteout_data_utf8_len =
+           charset_from_unicode(&tmp, &tmplen, inst->pasteout_data_utf8,
+                                inst->pasteout_data_utf8_len,
+                                CS_UTF8, NULL, NULL, 0);
+       if (inst->pasteout_data_utf8_len == 0) {
+           sfree(inst->pasteout_data_utf8);
+           inst->pasteout_data_utf8 = NULL;
+       } else {
+           inst->pasteout_data_utf8 =
+               srealloc(inst->pasteout_data_utf8,
+                        inst->pasteout_data_utf8_len);
+       }
+    } else {
+       inst->pasteout_data_utf8 = NULL;
+       inst->pasteout_data_utf8_len = 0;
+    }
+
+    inst->pasteout_data = smalloc(len*6);
+    inst->pasteout_data_len = len*6;
+    inst->pasteout_data_len = wc_to_mb(line_codepage, 0, data, len,
+                                      inst->pasteout_data,
+                                      inst->pasteout_data_len, NULL, NULL);
+    if (inst->pasteout_data_len == 0) {
+       sfree(inst->pasteout_data);
+       inst->pasteout_data = NULL;
+    } else {
+       inst->pasteout_data =
+           srealloc(inst->pasteout_data, inst->pasteout_data_len);
+    }
 
     if (gtk_selection_owner_set(inst->area, GDK_SELECTION_PRIMARY,
                                GDK_CURRENT_TIME)) {
@@ -1208,6 +1291,9 @@ void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
                                 GDK_SELECTION_TYPE_STRING, 1);
        gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
                                 inst->compound_text_atom, 1);
+       if (inst->pasteout_data_utf8)
+           gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
+                                    inst->utf8_string_atom, 1);
     }
 }
 
@@ -1215,8 +1301,13 @@ void selection_get(GtkWidget *widget, GtkSelectionData *seldata,
                   guint info, guint time_stamp, gpointer data)
 {
     struct gui_data *inst = (struct gui_data *)data;
-    gtk_selection_data_set(seldata, GDK_SELECTION_TYPE_STRING, 8,
-                          inst->pasteout_data, inst->pasteout_data_len);
+    if (seldata->target == inst->utf8_string_atom)
+       gtk_selection_data_set(seldata, seldata->target, 8,
+                              inst->pasteout_data_utf8,
+                              inst->pasteout_data_utf8_len);
+    else
+       gtk_selection_data_set(seldata, seldata->target, 8,
+                              inst->pasteout_data, inst->pasteout_data_len);
 }
 
 gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata,
@@ -1226,8 +1317,12 @@ gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata,
     term_deselect(inst->term);
     if (inst->pasteout_data)
        sfree(inst->pasteout_data);
+    if (inst->pasteout_data_utf8)
+       sfree(inst->pasteout_data_utf8);
     inst->pasteout_data = NULL;
     inst->pasteout_data_len = 0;
+    inst->pasteout_data_utf8 = NULL;
+    inst->pasteout_data_utf8_len = 0;
     return TRUE;
 }
 
@@ -1239,8 +1334,25 @@ void request_paste(void *frontend)
      * moment is to call gtk_selection_convert(), and when the data
      * comes back _then_ we can call term_do_paste().
      */
-    gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
-                         GDK_SELECTION_TYPE_STRING, GDK_CURRENT_TIME);
+
+    if (!inst->direct_to_font) {
+       /*
+        * First we attempt to retrieve the selection as a UTF-8
+        * string (which we will convert to the correct code page
+        * before sending to the session, of course). If that
+        * fails, selection_received() will be informed and will
+        * fall back to an ordinary string.
+        */
+       gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
+                             inst->utf8_string_atom, GDK_CURRENT_TIME);
+    } else {
+       /*
+        * If we're in direct-to-font mode, we disable UTF-8
+        * pasting, and go straight to ordinary string data.
+        */
+       gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
+                             GDK_SELECTION_TYPE_STRING, GDK_CURRENT_TIME);
+    }
 }
 
 gint idle_paste_func(gpointer data);   /* forward ref */
@@ -1250,8 +1362,22 @@ void selection_received(GtkWidget *widget, GtkSelectionData *seldata,
 {
     struct gui_data *inst = (struct gui_data *)data;
 
+    if (seldata->target == inst->utf8_string_atom && seldata->length <= 0) {
+       /*
+        * Failed to get a UTF-8 selection string. Try an ordinary
+        * string.
+        */
+       gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
+                             GDK_SELECTION_TYPE_STRING, GDK_CURRENT_TIME);
+       return;
+    }
+
+    /*
+     * Any other failure should just go foom.
+     */
     if (seldata->length <= 0 ||
-       seldata->type != GDK_SELECTION_TYPE_STRING)
+       (seldata->type != GDK_SELECTION_TYPE_STRING &&
+        seldata->type != inst->utf8_string_atom))
        return;                        /* Nothing happens. */
 
     if (inst->pastein_data)
@@ -1259,8 +1385,11 @@ void selection_received(GtkWidget *widget, GtkSelectionData *seldata,
 
     inst->pastein_data = smalloc(seldata->length * sizeof(wchar_t));
     inst->pastein_data_len = seldata->length;
-    mb_to_wc(0, 0, seldata->data, seldata->length,
-            inst->pastein_data, inst->pastein_data_len);
+    inst->pastein_data_len =
+       mb_to_wc((seldata->type == inst->utf8_string_atom ?
+                 CS_UTF8 : line_codepage),
+                0, seldata->data, seldata->length,
+                inst->pastein_data, inst->pastein_data_len);
 
     term_do_paste(inst->term);
 
@@ -1352,7 +1481,7 @@ void beep(void *frontend, int mode)
        gdk_beep();
 }
 
-int CharWidth(Context ctx, int uc)
+int char_width(Context ctx, int uc)
 {
     /*
      * Under X, any fixed-width font really _is_ fixed-width.
@@ -1398,12 +1527,7 @@ void do_text_internal(Context ctx, int x, int y, char *text, int len,
     struct gui_data *inst = dctx->inst;
     GdkGC *gc = dctx->gc;
 
-    int nfg, nbg, t, fontid, shadow;
-
-    /*
-     * NYI:
-     *  - Unicode, code pages, and ATTR_WIDE for CJK support.
-     */
+    int nfg, nbg, t, fontid, shadow, rlen, widefactor;
 
     nfg = 2 * ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT);
     nbg = 2 * ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT);
@@ -1422,9 +1546,17 @@ void do_text_internal(Context ctx, int x, int y, char *text, int len,
     }
 
     fontid = shadow = 0;
+
+    if (attr & ATTR_WIDE) {
+       widefactor = 2;
+       fontid |= 2;
+    } else {
+       widefactor = 1;
+    }
+
     if ((attr & ATTR_BOLD) && !cfg.bold_colour) {
-       if (inst->fonts[1])
-           fontid = 1;
+       if (inst->fonts[fontid | 1])
+           fontid |= 1;
        else
            shadow = 1;
     }
@@ -1433,21 +1565,87 @@ void do_text_internal(Context ctx, int x, int y, char *text, int len,
        x *= 2;
        if (x >= inst->term->cols)
            return;
-       if (x + len*2 > inst->term->cols)
-           len = (inst->term->cols-x)/2;    /* trim to LH half */
+       if (x + len*2*widefactor > inst->term->cols)
+           len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */
+       rlen = len * 2;
+    } else
+       rlen = len;
+
+    {
+       GdkRectangle r;
+
+       r.x = x*inst->font_width+cfg.window_border;
+       r.y = y*inst->font_height+cfg.window_border;
+       r.width = rlen*widefactor*inst->font_width;
+       r.height = inst->font_height;
+       gdk_gc_set_clip_rectangle(gc, &r);
     }
 
     gdk_gc_set_foreground(gc, &inst->cols[nbg]);
     gdk_draw_rectangle(inst->pixmap, gc, 1,
                       x*inst->font_width+cfg.window_border,
                       y*inst->font_height+cfg.window_border,
-                      len*inst->font_width, inst->font_height);
+                      rlen*widefactor*inst->font_width, inst->font_height);
 
     gdk_gc_set_foreground(gc, &inst->cols[nfg]);
-    gdk_draw_text(inst->pixmap, inst->fonts[fontid], gc,
-                 x*inst->font_width+cfg.window_border,
-                 y*inst->font_height+cfg.window_border+inst->fonts[0]->ascent,
-                 text, len);
+    {
+       GdkWChar *gwcs;
+       gchar *gcs;
+       wchar_t *wcs;
+       int i;
+
+       wcs = smalloc(sizeof(wchar_t) * (len+1));
+       for (i = 0; i < len; i++) {
+           wcs[i] = (wchar_t) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
+       }
+
+       if (inst->fonts[fontid] == NULL) {
+           /*
+            * The font for this contingency does not exist.
+            * Typically this means we've been given ATTR_WIDE
+            * character and have no wide font. So we display
+            * nothing at all; such is life.
+            */
+       } else if (inst->fontinfo[fontid].is_wide) {
+           /*
+            * At least one version of gdk_draw_text_wc() has a
+            * weird bug whereby it reads `len' elements of the
+            * input string, but only draws `len/2'. Hence I'm
+            * going to make its input array twice as long as it
+            * theoretically needs to be, and pass in twice the
+            * actual number of characters. If a fixed gdk actually
+            * takes the doubled length seriously, then (a) the
+            * array will stand scrutiny up to the full length, (b)
+            * the spare elements of the array are full of zeroes
+            * which will probably be an empty glyph in the font,
+            * and (c) the clip rectangle should prevent it causing
+            * trouble anyway.
+            */
+           gwcs = smalloc(sizeof(GdkWChar) * (len*2+1));
+           memset(gwcs, 0, sizeof(GdkWChar) * (len*2+1));
+           /*
+            * FIXME: when we have a wide-char equivalent of
+            * from_unicode, use it instead of this.
+            */
+           for (i = 0; i <= len; i++)
+               gwcs[i] = wcs[i];
+           gdk_draw_text_wc(inst->pixmap, inst->fonts[fontid], gc,
+                            x*inst->font_width+cfg.window_border,
+                            y*inst->font_height+cfg.window_border+inst->fonts[0]->ascent,
+                            gwcs, len*2);
+           sfree(gwcs);
+       } else {
+           gcs = smalloc(sizeof(GdkWChar) * (len+1));
+           wc_to_mb(inst->fontinfo[fontid].charset, 0,
+                    wcs, len, gcs, len, ".", NULL);
+           gdk_draw_text(inst->pixmap, inst->fonts[fontid], gc,
+                         x*inst->font_width+cfg.window_border,
+                         y*inst->font_height+cfg.window_border+inst->fonts[0]->ascent,
+                         gcs, len);
+           sfree(gcs);
+       }
+       sfree(wcs);
+    }
 
     if (shadow) {
        gdk_draw_text(inst->pixmap, inst->fonts[fontid], gc,
@@ -1462,7 +1660,7 @@ void do_text_internal(Context ctx, int x, int y, char *text, int len,
            uheight = inst->font_height - 1;
        gdk_draw_line(inst->pixmap, gc, x*inst->font_width+cfg.window_border,
                      y*inst->font_height + uheight + cfg.window_border,
-                     (x+len)*inst->font_width-1+cfg.window_border,
+                     (x+len)*widefactor*inst->font_width-1+cfg.window_border,
                      y*inst->font_height + uheight + cfg.window_border);
     }
 
@@ -1476,7 +1674,7 @@ void do_text_internal(Context ctx, int x, int y, char *text, int len,
         * try thinking of a better way. :-(
         */
        int i;
-       for (i = 0; i < len * inst->font_width; i++) {
+       for (i = 0; i < len * widefactor * inst->font_width; i++) {
            gdk_draw_pixmap(inst->pixmap, gc, inst->pixmap,
                            x*inst->font_width+cfg.window_border + 2*i,
                            y*inst->font_height+cfg.window_border,
@@ -1496,7 +1694,7 @@ void do_text_internal(Context ctx, int x, int y, char *text, int len,
                gdk_draw_pixmap(inst->pixmap, gc, inst->pixmap,
                                x*inst->font_width+cfg.window_border,
                                y*inst->font_height+cfg.window_border+dt*i+db,
-                               x*inst->font_width+cfg.window_border,
+                               x*widefactor*inst->font_width+cfg.window_border,
                                y*inst->font_height+cfg.window_border+dt*(i+1),
                                len * inst->font_width, inst->font_height-i-1);
            }
@@ -1510,15 +1708,22 @@ void do_text(Context ctx, int x, int y, char *text, int len,
     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
     struct gui_data *inst = dctx->inst;
     GdkGC *gc = dctx->gc;
+    int widefactor;
 
     do_text_internal(ctx, x, y, text, len, attr, lattr);
 
+    if (attr & ATTR_WIDE) {
+       widefactor = 2;
+    } else {
+       widefactor = 1;
+    }
+
     if (lattr != LATTR_NORM) {
        x *= 2;
        if (x >= inst->term->cols)
            return;
-       if (x + len*2 > inst->term->cols)
-           len = (inst->term->cols-x)/2;    /* trim to LH half */
+       if (x + len*2*widefactor > inst->term->cols)
+           len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */
        len *= 2;
     }
 
@@ -1527,7 +1732,7 @@ void do_text(Context ctx, int x, int y, char *text, int len,
                    y*inst->font_height+cfg.window_border,
                    x*inst->font_width+cfg.window_border,
                    y*inst->font_height+cfg.window_border,
-                   len*inst->font_width, inst->font_height);
+                   len*widefactor*inst->font_width, inst->font_height);
 }
 
 void do_cursor(Context ctx, int x, int y, char *text, int len,
@@ -1537,7 +1742,7 @@ void do_cursor(Context ctx, int x, int y, char *text, int len,
     struct gui_data *inst = dctx->inst;
     GdkGC *gc = dctx->gc;
 
-    int passive;
+    int passive, widefactor;
 
     if (attr & TATTR_PASCURS) {
        attr &= ~TATTR_PASCURS;
@@ -1549,12 +1754,18 @@ void do_cursor(Context ctx, int x, int y, char *text, int len,
     }
     do_text_internal(ctx, x, y, text, len, attr, lattr);
 
+    if (attr & ATTR_WIDE) {
+       widefactor = 2;
+    } else {
+       widefactor = 1;
+    }
+
     if (lattr != LATTR_NORM) {
        x *= 2;
        if (x >= inst->term->cols)
            return;
-       if (x + len*2 > inst->term->cols)
-           len = (inst->term->cols-x)/2;    /* trim to LH half */
+       if (x + len*2*widefactor > inst->term->cols)
+           len = (inst->term->cols-x)/2/widefactor;/* trim to LH half */
        len *= 2;
     }
 
@@ -1623,7 +1834,7 @@ void do_cursor(Context ctx, int x, int y, char *text, int len,
                    y*inst->font_height+cfg.window_border,
                    x*inst->font_width+cfg.window_border,
                    y*inst->font_height+cfg.window_border,
-                   len*inst->font_width, inst->font_height);
+                   len*widefactor*inst->font_width, inst->font_height);
 }
 
 GdkCursor *make_mouse_ptr(struct gui_data *inst, int cursor_val)
@@ -1767,18 +1978,22 @@ int do_cmdline(int argc, char **argv, int do_everything)
     extern char **pty_argv;           /* declared in pty.c */
 
     /*
-     * Macros to make argument handling easier.
+     * Macros to make argument handling easier. Note that because
+     * they need to call `continue', they cannot be contained in
+     * the usual do {...} while (0) wrapper to make them
+     * syntactically single statements; hence it is not legal to
+     * use one of these macros as an unbraced statement between
+     * `if' and `else'.
      */
-#define EXPECTS_ARG do { \
+#define EXPECTS_ARG { \
     if (--argc <= 0) { \
        err = 1; \
        fprintf(stderr, "pterm: %s expects an argument\n", p); \
+        continue; \
     } else \
        val = *++argv; \
-} while (0)
-#define SECOND_PASS_ONLY do { \
-    if (!do_everything) continue; \
-} while (0)
+}
+#define SECOND_PASS_ONLY { if (!do_everything) continue; }
 
     /*
      * TODO:
@@ -1801,6 +2016,24 @@ int do_cmdline(int argc, char **argv, int do_everything)
            strncpy(cfg.boldfont, val, sizeof(cfg.boldfont));
            cfg.boldfont[sizeof(cfg.boldfont)-1] = '\0';
 
+       } else if (!strcmp(p, "-fw")) {
+           EXPECTS_ARG;
+           SECOND_PASS_ONLY;
+           strncpy(cfg.widefont, val, sizeof(cfg.widefont));
+           cfg.widefont[sizeof(cfg.widefont)-1] = '\0';
+
+       } else if (!strcmp(p, "-fwb")) {
+           EXPECTS_ARG;
+           SECOND_PASS_ONLY;
+           strncpy(cfg.wideboldfont, val, sizeof(cfg.wideboldfont));
+           cfg.wideboldfont[sizeof(cfg.wideboldfont)-1] = '\0';
+
+       } else if (!strcmp(p, "-cs")) {
+           EXPECTS_ARG;
+           SECOND_PASS_ONLY;
+           strncpy(cfg.line_codepage, val, sizeof(cfg.line_codepage));
+           cfg.line_codepage[sizeof(cfg.line_codepage)-1] = '\0';
+
        } else if (!strcmp(p, "-geometry")) {
            int flags, x, y, w, h;
            EXPECTS_ARG;
@@ -1938,11 +2171,76 @@ static void block_signal(int sig, int block_it) {
   }
 }
 
+/*
+ * This function retrieves the character set encoding of a font. It
+ * returns the character set without the X11 hack (in case the user
+ * asks to use the font's own encoding).
+ */
+static int set_font_info(struct gui_data *inst, int fontid)
+{
+    GdkFont *font = inst->fonts[fontid];
+    XFontStruct *xfs = GDK_FONT_XFONT(font);
+    Display *disp = GDK_FONT_XDISPLAY(font);
+    Atom charset_registry, charset_encoding;
+    unsigned long registry_ret, encoding_ret;
+    int retval = CS_NONE;
+
+    charset_registry = XInternAtom(disp, "CHARSET_REGISTRY", False);
+    charset_encoding = XInternAtom(disp, "CHARSET_ENCODING", False);
+    inst->fontinfo[fontid].charset = CS_NONE;
+    inst->fontinfo[fontid].is_wide = 0;
+    if (XGetFontProperty(xfs, charset_registry, &registry_ret) &&
+       XGetFontProperty(xfs, charset_encoding, &encoding_ret)) {
+       char *reg, *enc;
+       reg = XGetAtomName(disp, (Atom)registry_ret);
+       enc = XGetAtomName(disp, (Atom)encoding_ret);
+       if (reg && enc) {
+           char *encoding = dupcat(reg, "-", enc, NULL);
+           retval = inst->fontinfo[fontid].charset =
+               charset_from_xenc(encoding);
+           /* FIXME: when libcharset supports wide encodings fix this. */
+           if (!strcasecmp(encoding, "iso10646-1")) {
+               inst->fontinfo[fontid].is_wide = 1;
+               retval = CS_UTF8;
+           }
+
+           /*
+            * Hack for X line-drawing characters: if the primary
+            * font is encoded as ISO-8859-anything, and has valid
+            * glyphs in the first 32 char positions, it is assumed
+            * that those glyphs are the VT100 line-drawing
+            * character set.
+            * 
+            * Actually, we'll hack even harder by only checking
+            * position 0x19 (vertical line, VT100 linedrawing
+            * `x'). Then we can check it easily by seeing if the
+            * ascent and descent differ.
+            */
+           if (inst->fontinfo[fontid].charset == CS_ISO8859_1) {
+               int lb, rb, wid, asc, desc;
+               gchar text[2];
+
+               text[1] = '\0';
+               text[0] = '\x12';
+               gdk_string_extents(inst->fonts[fontid], text,
+                                  &lb, &rb, &wid, &asc, &desc);
+               if (asc != desc)
+                   inst->fontinfo[fontid].charset = CS_ISO8859_1_X11;
+           }
+
+           sfree(encoding);
+       }
+    }
+
+    return retval;
+}
+
 int main(int argc, char **argv)
 {
     extern int pty_master_fd;         /* declared in pty.c */
     extern void pty_pre_init(void);    /* declared in pty.c */
     struct gui_data *inst;
+    int font_charset;
 
     /* defer any child exit handling until we're ready to deal with
      * it */
@@ -1970,6 +2268,7 @@ int main(int argc, char **argv)
        fprintf(stderr, "pterm: unable to load font \"%s\"\n", cfg.font);
        exit(1);
     }
+    font_charset = set_font_info(inst, 0);
     if (cfg.boldfont[0]) {
        inst->fonts[1] = gdk_font_load(cfg.boldfont);
        if (!inst->fonts[1]) {
@@ -1977,15 +2276,37 @@ int main(int argc, char **argv)
                    cfg.boldfont);
            exit(1);
        }
+       set_font_info(inst, 1);
     } else
        inst->fonts[1] = NULL;
+    if (cfg.widefont[0]) {
+       inst->fonts[2] = gdk_font_load(cfg.widefont);
+       if (!inst->fonts[2]) {
+           fprintf(stderr, "pterm: unable to load wide font \"%s\"\n",
+                   cfg.boldfont);
+           exit(1);
+       }
+       set_font_info(inst, 2);
+    } else
+       inst->fonts[2] = NULL;
+    if (cfg.wideboldfont[0]) {
+       inst->fonts[3] = gdk_font_load(cfg.wideboldfont);
+       if (!inst->fonts[3]) {
+           fprintf(stderr, "pterm: unable to load wide/bold font \"%s\"\n",
+                   cfg.boldfont);
+           exit(1);
+       }
+       set_font_info(inst, 3);
+    } else
+       inst->fonts[3] = NULL;
 
     inst->font_width = gdk_char_width(inst->fonts[0], ' ');
     inst->font_height = inst->fonts[0]->ascent + inst->fonts[0]->descent;
 
     inst->compound_text_atom = gdk_atom_intern("COMPOUND_TEXT", FALSE);
+    inst->utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE);
 
-    init_ucs();
+    inst->direct_to_font = init_ucs(font_charset);
 
     inst->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
@@ -2085,19 +2406,21 @@ int main(int argc, char **argv)
     inst->currcursor = inst->textcursor;
     show_mouseptr(inst, 1);
 
-    inst->term = term_init(inst);
+    inst->term = term_init(&cfg, inst);
     inst->logctx = log_init(inst);
     term_provide_logctx(inst->term, inst->logctx);
 
     inst->back = &pty_backend;
-    inst->back->init((void *)inst->term, &inst->backhandle, NULL, 0, NULL, 0);
+    inst->back->init((void *)inst->term, &inst->backhandle, &cfg,
+                    NULL, 0, NULL, 0);
     inst->back->provide_logctx(inst->backhandle, inst->logctx);
 
     term_provide_resize_fn(inst->term, inst->back->size, inst->backhandle);
 
     term_size(inst->term, cfg.height, cfg.width, cfg.savelines);
 
-    inst->ldisc = ldisc_create(inst->term, inst->back, inst->backhandle, inst);
+    inst->ldisc =
+       ldisc_create(&cfg, inst->term, inst->back, inst->backhandle, inst);
     ldisc_send(inst->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */
 
     inst->master_fd = pty_master_fd;