Fix an always-false if statement which was causing the window border
[u/mdw/putty] / unix / gtkwin.c
index 303052a..4a1df45 100644 (file)
@@ -13,6 +13,7 @@
 #include <stdio.h>
 #include <time.h>
 #include <errno.h>
+#include <locale.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <X11/Xutil.h>
 #include <X11/Xatom.h>
 
+#if GTK_CHECK_VERSION(2,0,0)
+#include <gtk/gtkimmodule.h>
+#endif
+
 #define PUTTY_DO_GLOBALS              /* actually _define_ globals */
 
+#define MAY_REFER_TO_GTK_IN_HEADERS
+
 #include "putty.h"
 #include "terminal.h"
 #include "gtkfont.h"
@@ -68,6 +75,9 @@ struct gui_data {
        *restartitem;
     GtkWidget *sessionsmenu;
     GdkPixmap *pixmap;
+#if GTK_CHECK_VERSION(2,0,0)
+    GtkIMContext *imc;
+#endif
     unifont *fonts[4];                 /* normal, bold, wide, widebold */
     int xpos, ypos, gotpos, gravity;
     GdkCursor *rawcursor, *textcursor, *blankcursor, *waitcursor, *currcursor;
@@ -104,14 +114,14 @@ struct gui_data {
     guint32 input_event_time; /* Timestamp of the most recent input event. */
     int reconfiguring;
     /* Cached things out of conf that we refer to a lot */
-    int bold_colour;
+    int bold_style;
     int window_border;
     int cursor_type;
 };
 
 static void cache_conf_values(struct gui_data *inst)
 {
-    inst->bold_colour = conf_get_int(inst->conf, CONF_bold_colour);
+    inst->bold_style = conf_get_int(inst->conf, CONF_bold_style);
     inst->window_border = conf_get_int(inst->conf, CONF_window_border);
     inst->cursor_type = conf_get_int(inst->conf, CONF_cursor_type);
 }
@@ -151,24 +161,20 @@ void connection_fatal(void *frontend, char *p, ...)
 /*
  * Default settings that are specific to pterm.
  */
-FontSpec platform_default_fontspec(const char *name)
+FontSpec *platform_default_fontspec(const char *name)
 {
-    FontSpec ret;
     if (!strcmp(name, "Font"))
-       strcpy(ret.name, "server:fixed");
+       return fontspec_new("server:fixed");
     else
-       *ret.name = '\0';
-    return ret;
+        return fontspec_new("");
 }
 
-Filename platform_default_filename(const char *name)
+Filename *platform_default_filename(const char *name)
 {
-    Filename ret;
     if (!strcmp(name, "LogFileName"))
-       strcpy(ret.path, "putty.log");
+       return filename_from_str("putty.log");
     else
-       *ret.path = '\0';
-    return ret;
+       return filename_from_str("");
 }
 
 char *platform_default_s(const char *name)
@@ -493,6 +499,10 @@ gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
     if (inst->term)
        term_invalidate(inst->term);
 
+#if GTK_CHECK_VERSION(2,0,0)
+    gtk_im_context_set_client_window(inst->imc, widget->window);
+#endif
+
     return TRUE;
 }
 
@@ -524,6 +534,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
     char output[256];
     wchar_t ucsoutput[2];
     int ucsval, start, end, special, output_charset, use_ucsoutput;
+    int nethack_mode, app_keypad_mode;
 
     /* Remember the timestamp. */
     inst->input_event_time = event->time;
@@ -543,21 +554,26 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
      * inconvenience in having to type a zero before a single-digit
      * character code.
      */
-    if (event->type == GDK_KEY_RELEASE &&
-       (event->keyval == GDK_Meta_L || event->keyval == GDK_Alt_L ||
-        event->keyval == GDK_Meta_R || event->keyval == GDK_Alt_R) &&
-       inst->alt_keycode >= 0 && inst->alt_digits > 1) {
+    if (event->type == GDK_KEY_RELEASE) {
+        if ((event->keyval == GDK_Meta_L || event->keyval == GDK_Alt_L ||
+             event->keyval == GDK_Meta_R || event->keyval == GDK_Alt_R) &&
+            inst->alt_keycode >= 0 && inst->alt_digits > 1) {
 #ifdef KEY_DEBUGGING
-       printf("Alt key up, keycode = %d\n", inst->alt_keycode);
+            printf("Alt key up, keycode = %d\n", inst->alt_keycode);
+#endif
+            /*
+             * FIXME: we might usefully try to do something clever here
+             * about interpreting the generated key code in a way that's
+             * appropriate to the line code page.
+             */
+            output[0] = inst->alt_keycode;
+            end = 1;
+            goto done;
+        }
+#if GTK_CHECK_VERSION(2,0,0)
+        if (gtk_im_context_filter_keypress(inst->imc, event))
+            return TRUE;
 #endif
-       /*
-        * FIXME: we might usefully try to do something clever here
-        * about interpreting the generated key code in a way that's
-        * appropriate to the line code page.
-        */
-       output[0] = inst->alt_keycode;
-       end = 1;
-       goto done;
     }
 
     if (event->type == GDK_KEY_PRESS) {
@@ -660,6 +676,10 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
        special = FALSE;
        use_ucsoutput = FALSE;
 
+        nethack_mode = conf_get_int(inst->conf, CONF_nethack_keypad);
+        app_keypad_mode = (inst->term->app_keypad_keys &&
+                           !conf_get_int(inst->conf, CONF_no_applic_k));
+
        /* ALT+things gives leading Escape. */
        output[0] = '\033';
 #if !GTK_CHECK_VERSION(2,0,0)
@@ -673,13 +693,73 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
        output_charset = CS_ISO8859_1;
        strncpy(output+1, event->string, lenof(output)-1);
 #else
+        /*
+         * Most things can now be passed to
+         * gtk_im_context_filter_keypress without breaking anything
+         * below this point. An exception is the numeric keypad if
+         * we're in Nethack or application mode: the IM will eat
+         * numeric keypad presses if Num Lock is on, but we don't want
+         * it to.
+         */
+       if (app_keypad_mode &&
+            (event->keyval == GDK_Num_Lock ||
+             event->keyval == GDK_KP_Divide ||
+             event->keyval == GDK_KP_Multiply ||
+             event->keyval == GDK_KP_Subtract ||
+             event->keyval == GDK_KP_Add ||
+             event->keyval == GDK_KP_Enter ||
+             event->keyval == GDK_KP_0 ||
+             event->keyval == GDK_KP_Insert ||
+             event->keyval == GDK_KP_1 ||
+             event->keyval == GDK_KP_End ||
+             event->keyval == GDK_KP_2 ||
+             event->keyval == GDK_KP_Down ||
+             event->keyval == GDK_KP_3 ||
+             event->keyval == GDK_KP_Page_Down ||
+             event->keyval == GDK_KP_4 ||
+             event->keyval == GDK_KP_Left ||
+             event->keyval == GDK_KP_5 ||
+             event->keyval == GDK_KP_Begin ||
+             event->keyval == GDK_KP_6 ||
+             event->keyval == GDK_KP_Right ||
+             event->keyval == GDK_KP_7 ||
+             event->keyval == GDK_KP_Home ||
+             event->keyval == GDK_KP_8 ||
+             event->keyval == GDK_KP_Up ||
+             event->keyval == GDK_KP_9 ||
+             event->keyval == GDK_KP_Page_Up ||
+             event->keyval == GDK_KP_Decimal ||
+             event->keyval == GDK_KP_Delete)) {
+            /* app keypad; do nothing */
+        } else if (nethack_mode &&
+                   (event->keyval == GDK_KP_1 ||
+                    event->keyval == GDK_KP_End ||
+                    event->keyval == GDK_KP_2 ||
+                    event->keyval == GDK_KP_Down ||
+                    event->keyval == GDK_KP_3 ||
+                    event->keyval == GDK_KP_Page_Down ||
+                    event->keyval == GDK_KP_4 ||
+                    event->keyval == GDK_KP_Left ||
+                    event->keyval == GDK_KP_5 ||
+                    event->keyval == GDK_KP_Begin ||
+                    event->keyval == GDK_KP_6 ||
+                    event->keyval == GDK_KP_Right ||
+                    event->keyval == GDK_KP_7 ||
+                    event->keyval == GDK_KP_Home ||
+                    event->keyval == GDK_KP_8 ||
+                    event->keyval == GDK_KP_Up ||
+                    event->keyval == GDK_KP_9 ||
+                    event->keyval == GDK_KP_Page_Up)) {
+            /* nethack mode; do nothing */
+        } else {
+            if (gtk_im_context_filter_keypress(inst->imc, event))
+                return TRUE;
+        }
+
        /*
         * GDK 2.0 arranges to have done some translation for us: in
         * GDK 2.0, event->string is encoded in the current locale.
         *
-        * (However, it's also deprecated; we really ought to be
-        * using a GTKIMContext.)
-        *
         * So we use the standard C library function mbstowcs() to
         * convert from the current locale into Unicode; from there
         * we can convert to whatever PuTTY is currently working in.
@@ -804,7 +884,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
        /*
         * NetHack keypad mode.
         */
-       if (conf_get_int(inst->conf, CONF_nethack_keypad)) {
+       if (nethack_mode) {
            char *keys = NULL;
            switch (event->keyval) {
              case GDK_KP_1: case GDK_KP_End: keys = "bB\002"; break;
@@ -833,8 +913,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
        /*
         * Application keypad mode.
         */
-       if (inst->term->app_keypad_keys &&
-           !conf_get_int(inst->conf, CONF_no_applic_k)) {
+       if (app_keypad_mode) {
            int xkey = 0;
            switch (event->keyval) {
              case GDK_Num_Lock: xkey = 'P'; break;
@@ -1140,6 +1219,17 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
     return TRUE;
 }
 
+#if GTK_CHECK_VERSION(2,0,0)
+void input_method_commit_event(GtkIMContext *imc, gchar *str, gpointer data)
+{
+    struct gui_data *inst = (struct gui_data *)data;
+    if (inst->ldisc)
+        lpage_send(inst->ldisc, CS_UTF8, str, strlen(str), 1);
+    show_mouseptr(inst, 0);
+    term_seen_key_event(inst->term);
+}
+#endif
+
 gboolean button_internal(struct gui_data *inst, guint32 timestamp,
                         GdkEventType type, guint ebutton, guint state,
                         gdouble ex, gdouble ey)
@@ -1290,13 +1380,11 @@ static gint idle_exit_func(gpointer data)
            ldisc_free(inst->ldisc);
            inst->ldisc = NULL;
        }
-       if (inst->back) {
-           inst->back->free(inst->backhandle);
-           inst->backhandle = NULL;
-           inst->back = NULL;
-            term_provide_resize_fn(inst->term, NULL, NULL);
-           update_specials_menu(inst);
-       }
+        inst->back->free(inst->backhandle);
+        inst->backhandle = NULL;
+        inst->back = NULL;
+        term_provide_resize_fn(inst->term, NULL, NULL);
+        update_specials_menu(inst);
        gtk_widget_set_sensitive(inst->restartitem, TRUE);
     }
 
@@ -1313,13 +1401,18 @@ void notify_remote_exit(void *frontend)
 
 static gint timer_trigger(gpointer data)
 {
-    long now = GPOINTER_TO_LONG(data);
-    long next;
+    unsigned long now = GPOINTER_TO_LONG(data);
+    unsigned long next, then;
     long ticks;
 
     if (run_timers(now, &next)) {
-       ticks = next - GETTICKCOUNT();
-       timer_id = gtk_timeout_add(ticks > 0 ? ticks : 1, timer_trigger,
+       then = now;
+       now = GETTICKCOUNT();
+       if (now - then > next - then)
+           ticks = 0;
+       else
+           ticks = next - now;
+       timer_id = gtk_timeout_add(ticks, timer_trigger,
                                   LONG_TO_GPOINTER(next));
     }
 
@@ -1330,7 +1423,7 @@ static gint timer_trigger(gpointer data)
     return FALSE;
 }
 
-void timer_change_notify(long next)
+void timer_change_notify(unsigned long next)
 {
     long ticks;
 
@@ -1503,7 +1596,7 @@ void palette_set(void *frontend, int n, int r, int g, int b)
     struct gui_data *inst = (struct gui_data *)frontend;
     if (n >= 16)
        n += 256 - 16;
-    if (n > NALLCOLOURS)
+    if (n >= NALLCOLOURS)
        return;
     real_palette_set(inst, n, r, g, b);
     if (n == 258) {
@@ -2065,11 +2158,11 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
        nfg = nbg;
        nbg = t;
     }
-    if (inst->bold_colour && (attr & ATTR_BOLD)) {
+    if ((inst->bold_style & 2) && (attr & ATTR_BOLD)) {
        if (nfg < 16) nfg |= 8;
        else if (nfg >= 256) nfg |= 1;
     }
-    if (inst->bold_colour && (attr & ATTR_BLINK)) {
+    if ((inst->bold_style & 2) && (attr & ATTR_BLINK)) {
        if (nbg < 16) nbg |= 8;
        else if (nbg >= 256) nbg |= 1;
     }
@@ -2087,7 +2180,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
        widefactor = 1;
     }
 
-    if ((attr & ATTR_BOLD) && !inst->bold_colour) {
+    if ((attr & ATTR_BOLD) && (inst->bold_style & 1)) {
        bold = 1;
        fontid |= 1;
     } else {
@@ -2138,28 +2231,12 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
                       rlen*widefactor*inst->font_width, inst->font_height);
 
     gdk_gc_set_foreground(gc, &inst->cols[nfg]);
-    {
-       gchar *gcs;
-
-       /*
-        * FIXME: this length is hardwired on the assumption that
-        * conversions from wide to multibyte characters will
-        * never generate more than 10 bytes for a single wide
-        * character.
-        */
-       gcs = snewn(len*10+1, gchar);
-
-       for (combining = 0; combining < ncombining; combining++) {
-           int mblen = wc_to_mb(inst->fonts[fontid]->real_charset, 0,
-                                text + combining, len, gcs, len*10+1, ".",
-                                NULL, NULL);
-           unifont_draw_text(inst->pixmap, gc, inst->fonts[fontid],
-                             x*inst->font_width+inst->window_border,
-                             y*inst->font_height+inst->window_border+inst->fonts[0]->ascent,
-                             gcs, mblen, widefactor > 1, bold, inst->font_width);
-       }
-
-       sfree(gcs);
+    for (combining = 0; combining < ncombining; combining++) {
+        unifont_draw_text(inst->pixmap, gc, inst->fonts[fontid],
+                          x*inst->font_width+inst->window_border,
+                          y*inst->font_height+inst->window_border+inst->fonts[0]->ascent,
+                          text + combining, len, widefactor > 1,
+                          bold, inst->font_width);
     }
 
     if (attr & ATTR_UNDER) {
@@ -2348,6 +2425,17 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
                    x*inst->font_width+inst->window_border,
                    y*inst->font_height+inst->window_border,
                    len*widefactor*inst->font_width, inst->font_height);
+
+#if GTK_CHECK_VERSION(2,0,0)
+    {
+        GdkRectangle cursorrect;
+        cursorrect.x = x*inst->font_width+inst->window_border;
+        cursorrect.y = y*inst->font_height+inst->window_border;
+        cursorrect.width = len*widefactor*inst->font_width;
+        cursorrect.height = inst->font_height;
+        gtk_im_context_set_cursor_location(inst->imc, &cursorrect);
+    }
+#endif
 }
 
 GdkCursor *make_mouse_ptr(struct gui_data *inst, int cursor_val)
@@ -2505,6 +2593,13 @@ static void help(FILE *fp) {
     }
 }
 
+static void version(FILE *fp) {
+    if(fprintf(fp, "%s: %s\n", appname, ver) < 0 || fflush(fp) < 0) {
+       perror("output error");
+       exit(1);
+    }
+}
+
 int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
                struct gui_data *inst, Conf *conf)
 {
@@ -2557,36 +2652,36 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
         }
 
        if (!strcmp(p, "-fn") || !strcmp(p, "-font")) {
-           FontSpec fs;
+           FontSpec *fs;
            EXPECTS_ARG;
            SECOND_PASS_ONLY;
-           strncpy(fs.name, val, sizeof(fs.name));
-           fs.name[sizeof(fs.name)-1] = '\0';
-           conf_set_fontspec(conf, CONF_font, &fs);
+            fs = fontspec_new(val);
+           conf_set_fontspec(conf, CONF_font, fs);
+            fontspec_free(fs);
 
        } else if (!strcmp(p, "-fb")) {
-           FontSpec fs;
+           FontSpec *fs;
            EXPECTS_ARG;
            SECOND_PASS_ONLY;
-           strncpy(fs.name, val, sizeof(fs.name));
-           fs.name[sizeof(fs.name)-1] = '\0';
-           conf_set_fontspec(conf, CONF_boldfont, &fs);
+            fs = fontspec_new(val);
+           conf_set_fontspec(conf, CONF_boldfont, fs);
+            fontspec_free(fs);
 
        } else if (!strcmp(p, "-fw")) {
-           FontSpec fs;
+           FontSpec *fs;
            EXPECTS_ARG;
            SECOND_PASS_ONLY;
-           strncpy(fs.name, val, sizeof(fs.name));
-           fs.name[sizeof(fs.name)-1] = '\0';
-           conf_set_fontspec(conf, CONF_widefont, &fs);
+            fs = fontspec_new(val);
+           conf_set_fontspec(conf, CONF_widefont, fs);
+            fontspec_free(fs);
 
        } else if (!strcmp(p, "-fwb")) {
-           FontSpec fs;
+           FontSpec *fs;
            EXPECTS_ARG;
            SECOND_PASS_ONLY;
-           strncpy(fs.name, val, sizeof(fs.name));
-           fs.name[sizeof(fs.name)-1] = '\0';
-           conf_set_fontspec(conf, CONF_wideboldfont, &fs);
+            fs = fontspec_new(val);
+           conf_set_fontspec(conf, CONF_wideboldfont, fs);
+            fontspec_free(fs);
 
        } else if (!strcmp(p, "-cs")) {
            EXPECTS_ARG;
@@ -2666,13 +2761,13 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
            conf_set_str(conf, CONF_wintitle, val);
 
        } else if (!strcmp(p, "-log")) {
-           Filename fn;
+           Filename *fn;
            EXPECTS_ARG;
            SECOND_PASS_ONLY;
-           strncpy(fn.path, val, sizeof(fn.path));
-           fn.path[sizeof(fn.path)-1] = '\0';
-           conf_set_filename(conf, CONF_logfilename, &fn);
+            fn = filename_from_str(val);
+           conf_set_filename(conf, CONF_logfilename, fn);
            conf_set_int(conf, CONF_logtype, LGTYP_DEBUG);
+            filename_free(fn);
 
        } else if (!strcmp(p, "-ut-") || !strcmp(p, "+ut")) {
            SECOND_PASS_ONLY;
@@ -2714,6 +2809,10 @@ int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
            help(stdout);
            exit(0);
 
+       } else if(!strcmp(p, "-version") || !strcmp(p, "--version")) {
+           version(stdout);
+           exit(0);
+
         } else if (!strcmp(p, "-pgpfp")) {
             pgp_fingerprints();
             exit(1);
@@ -2745,69 +2844,92 @@ void uxsel_input_remove(int id) {
     gdk_input_remove(id);
 }
 
-void setup_fonts_ucs(struct gui_data *inst)
+int frontend_net_pending_error_idle_id;
+int frontend_got_net_pending_errors = FALSE;
+gboolean frontend_net_pending_errors(gpointer data)
+{
+    net_pending_errors();
+    gtk_idle_remove(frontend_net_pending_error_idle_id);
+    frontend_got_net_pending_errors = FALSE;
+    return FALSE;
+}
+void frontend_net_error_pending(void)
+{
+    if (!frontend_got_net_pending_errors) {
+        frontend_got_net_pending_errors = TRUE;
+        frontend_net_pending_error_idle_id =
+            gtk_idle_add(frontend_net_pending_errors, NULL);
+    }
+}
+
+char *setup_fonts_ucs(struct gui_data *inst)
 {
     int shadowbold = conf_get_int(inst->conf, CONF_shadowbold);
     int shadowboldoffset = conf_get_int(inst->conf, CONF_shadowboldoffset);
     FontSpec *fs;
-
-    if (inst->fonts[0])
-        unifont_destroy(inst->fonts[0]);
-    if (inst->fonts[1])
-        unifont_destroy(inst->fonts[1]);
-    if (inst->fonts[2])
-        unifont_destroy(inst->fonts[2]);
-    if (inst->fonts[3])
-        unifont_destroy(inst->fonts[3]);
+    unifont *fonts[4];
+    int i;
 
     fs = conf_get_fontspec(inst->conf, CONF_font);
-    inst->fonts[0] = unifont_create(inst->area, fs->name, FALSE, FALSE,
-                                   shadowboldoffset, shadowbold);
-    if (!inst->fonts[0]) {
-       fprintf(stderr, "%s: unable to load font \"%s\"\n", appname,
-               fs->name);
-       exit(1);
+    fonts[0] = multifont_create(inst->area, fs->name, FALSE, FALSE,
+                                shadowboldoffset, shadowbold);
+    if (!fonts[0]) {
+        return dupprintf("unable to load font \"%s\"", fs->name);
     }
 
     fs = conf_get_fontspec(inst->conf, CONF_boldfont);
     if (shadowbold || !fs->name[0]) {
-       inst->fonts[1] = NULL;
+       fonts[1] = NULL;
     } else {
-       inst->fonts[1] = unifont_create(inst->area, fs->name, FALSE, TRUE,
-                                       shadowboldoffset, shadowbold);
-       if (!inst->fonts[1]) {
-           fprintf(stderr, "%s: unable to load bold font \"%s\"\n", appname,
-                   fs->name);
-           exit(1);
+       fonts[1] = multifont_create(inst->area, fs->name, FALSE, TRUE,
+                                    shadowboldoffset, shadowbold);
+       if (!fonts[1]) {
+            if (fonts[0])
+                unifont_destroy(fonts[0]);
+           return dupprintf("unable to load bold font \"%s\"", fs->name);
        }
     }
 
     fs = conf_get_fontspec(inst->conf, CONF_widefont);
     if (fs->name[0]) {
-       inst->fonts[2] = unifont_create(inst->area, fs->name, TRUE, FALSE,
-                                       shadowboldoffset, shadowbold);
-       if (!inst->fonts[2]) {
-           fprintf(stderr, "%s: unable to load wide font \"%s\"\n", appname,
-                   fs->name);
-           exit(1);
+       fonts[2] = multifont_create(inst->area, fs->name, TRUE, FALSE,
+                                    shadowboldoffset, shadowbold);
+       if (!fonts[2]) {
+            for (i = 0; i < 2; i++)
+                if (fonts[i])
+                    unifont_destroy(fonts[i]);
+            return dupprintf("%s: unable to load wide font \"%s\"", fs->name);
        }
     } else {
-       inst->fonts[2] = NULL;
+       fonts[2] = NULL;
     }
 
     fs = conf_get_fontspec(inst->conf, CONF_wideboldfont);
     if (shadowbold || !fs->name[0]) {
-       inst->fonts[3] = NULL;
+       fonts[3] = NULL;
     } else {
-       inst->fonts[3] = unifont_create(inst->area, fs->name, TRUE, TRUE,
-                                       shadowboldoffset, shadowbold);
-       if (!inst->fonts[3]) {
-           fprintf(stderr, "%s: unable to load wide bold font \"%s\"\n", appname,
-                   fs->name);
-           exit(1);
+       fonts[3] = multifont_create(inst->area, fs->name, TRUE, TRUE,
+                                    shadowboldoffset, shadowbold);
+       if (!fonts[3]) {
+            for (i = 0; i < 3; i++)
+                if (fonts[i])
+                    unifont_destroy(fonts[i]);
+           return dupprintf("%s: unable to load wide bold font \"%s\"",
+                             fs->name);
        }
     }
 
+    /*
+     * Now we've got past all the possible error conditions, we can
+     * actually update our state.
+     */
+
+    for (i = 0; i < 4; i++) {
+        if (inst->fonts[i])
+            unifont_destroy(inst->fonts[i]);
+        inst->fonts[i] = fonts[i];
+    }
+
     inst->font_width = inst->fonts[0]->width;
     inst->font_height = inst->fonts[0]->height;
 
@@ -2816,6 +2938,8 @@ void setup_fonts_ucs(struct gui_data *inst)
                                    conf_get_int(inst->conf, CONF_utf8_override),
                                    inst->fonts[0]->public_charset,
                                    conf_get_int(inst->conf, CONF_vtmode));
+
+    return NULL;
 }
 
 void set_geom_hints(struct gui_data *inst)
@@ -2909,9 +3033,10 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data)
          * Flush the line discipline's edit buffer in the case
          * where local editing has just been disabled.
          */
-       ldisc_configure(inst->ldisc, inst->conf);
-        if (inst->ldisc)
+        if (inst->ldisc) {
+            ldisc_configure(inst->ldisc, inst->conf);
            ldisc_send(inst->ldisc, NULL, 0, 0);
+        }
         /* Pass new config data to the terminal */
         term_reconfig(inst->term, inst->conf);
         /* Pass new config data to the back end */
@@ -2944,7 +3069,7 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data)
                 * repaint the space in between the window border
                 * and the text area.
                 */
-               if (i == 258) {
+               if (ww[i] == 258) {
                    set_window_background(inst);
                    draw_backing_rect(inst);
                }
@@ -2981,6 +3106,7 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data)
          * Redo the whole tangled fonts and Unicode mess if
          * necessary.
          */
+        need_size = FALSE;
         if (strcmp(conf_get_fontspec(oldconf, CONF_font)->name,
                   conf_get_fontspec(newconf, CONF_font)->name) ||
            strcmp(conf_get_fontspec(oldconf, CONF_boldfont)->name,
@@ -2991,16 +3117,28 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data)
                   conf_get_fontspec(newconf, CONF_wideboldfont)->name) ||
            strcmp(conf_get_str(oldconf, CONF_line_codepage),
                   conf_get_str(newconf, CONF_line_codepage)) ||
+           conf_get_int(oldconf, CONF_utf8_override) !=
+           conf_get_int(newconf, CONF_utf8_override) ||
            conf_get_int(oldconf, CONF_vtmode) !=
            conf_get_int(newconf, CONF_vtmode) ||
            conf_get_int(oldconf, CONF_shadowbold) !=
            conf_get_int(newconf, CONF_shadowbold) ||
            conf_get_int(oldconf, CONF_shadowboldoffset) !=
            conf_get_int(newconf, CONF_shadowboldoffset)) {
-            setup_fonts_ucs(inst);
-            need_size = 1;
-        } else
-            need_size = 0;
+            char *errmsg = setup_fonts_ucs(inst);
+            if (errmsg) {
+                char *msgboxtext =
+                    dupprintf("Could not change fonts in terminal window: %s\n",
+                              errmsg);
+                messagebox(inst->window, "Font setup error", msgboxtext,
+                           string_width("Could not change fonts in terminal window:"),
+                           "OK", 'o', +1, 1,
+                           NULL);
+                sfree(errmsg);
+            } else {
+                need_size = TRUE;
+            }
+        }
 
         /*
          * Resize the window.
@@ -3446,6 +3584,8 @@ int pt_main(int argc, char **argv)
     extern int cfgbox(Conf *conf);
     struct gui_data *inst;
 
+    setlocale(LC_CTYPE, "");
+
     /*
      * Create an instance structure and initialise to zeroes
      */
@@ -3478,7 +3618,7 @@ int pt_main(int argc, char **argv)
     if (argc > 1 && !strncmp(argv[1], "---", 3)) {
        read_dupsession_data(inst, inst->conf, argv[1]);
        /* Splatter this argument so it doesn't clutter a ps listing */
-       memset(argv[1], 0, strlen(argv[1]));
+       smemclr(argv[1], strlen(argv[1]));
     } else {
        /* By default, we bring up the config dialog, rather than launching
         * a session. This gets set to TRUE if something happens to change
@@ -3507,7 +3647,17 @@ int pt_main(int argc, char **argv)
 
     inst->area = gtk_drawing_area_new();
 
-    setup_fonts_ucs(inst);
+#if GTK_CHECK_VERSION(2,0,0)
+    inst->imc = gtk_im_multicontext_new();
+#endif
+
+    {
+        char *errmsg = setup_fonts_ucs(inst);
+        if (errmsg) {
+            fprintf(stderr, "%s: %s\n", appname, errmsg);
+            exit(1);
+        }
+    }
     init_cutbuffers();
 
     inst->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
@@ -3597,6 +3747,10 @@ int pt_main(int argc, char **argv)
                       GTK_SIGNAL_FUNC(selection_get), inst);
     gtk_signal_connect(GTK_OBJECT(inst->area), "selection_clear_event",
                       GTK_SIGNAL_FUNC(selection_clear), inst);
+#if GTK_CHECK_VERSION(2,0,0)
+    g_signal_connect(G_OBJECT(inst->imc), "commit",
+                     G_CALLBACK(input_method_commit_event), inst);
+#endif
     if (conf_get_int(inst->conf, CONF_scrollbar))
        gtk_signal_connect(GTK_OBJECT(inst->sbar_adjust), "value_changed",
                           GTK_SIGNAL_FUNC(scrollbar_moved), inst);