I discovered a bug in my patch to support input methods on pterm -- it
broke ctrl-space (which should send NUL) so it had no effect.

Here's the updated patch. The fix relates to this caveat I mentioned
in my initial patch:

 * I chose to work around a bug in mb_to_wc() where it crashes if passed
   "\0"; you might prefer to fix it instead.

The workaround was broken -- this time round I did what I should have
done in the first place and fixed mb_to_wc().

-- PMM

diff -ur putty-0.58/unix/gtkwin.c putty-0.58-patched/unix/gtkwin.c
--- putty-0.58/unix/gtkwin.c    2005-04-05 20:37:47.000000000 +0100
+++ putty-0.58-patched/unix/gtkwin.c    2006-03-04 22:43:30.000000000 +0000
@@ -37,6 +37,12 @@
 #define NCFGCOLOURS (lenof(((Config *)0)->colours))
 #define NEXTCOLOURS 240 /* 216 colour-cube plus 24 shades of grey */
 #define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS)
+/* Index of foreground/background colours in cols[] */
+#define FGCOLIDX 256
+#define BGCOLIDX 258
+
+/* Support X Input Methods ? */
+#define USE_XIM
 
 GdkAtom compound_text_atom, utf8_string_atom;
 
@@ -96,6 +102,13 @@
     int ngtkargs;
     guint32 input_event_time; /* Timestamp of the most recent input event. */
     int reconfiguring;
+#ifdef USE_XIM
+    GdkICAttr *ic_attr;
+    GdkIC *ic;
+    GdkFont *ic_fontset;
+    int cursor_x;
+    int cursor_y;
+#endif
 };
 
 struct draw_ctx {
@@ -407,13 +420,138 @@
 void draw_backing_rect(struct gui_data *inst)
 {
     GdkGC *gc = gdk_gc_new(inst->area->window);
-    gdk_gc_set_foreground(gc, &inst->cols[258]);    /* default background */
+    gdk_gc_set_foreground(gc, &inst->cols[BGCOLIDX]);    /* default background 
*/
     gdk_draw_rectangle(inst->pixmap, gc, 1, 0, 0,
                       inst->cfg.width * inst->font_width + 
2*inst->cfg.window_border,
                       inst->cfg.height * inst->font_height + 
2*inst->cfg.window_border);
     gdk_gc_unref(gc);
 }
 
+#ifdef USE_XIM
+
+/* These macros should evaluate true if A and B differ */
+#define COLORCMP(A,B) ((A).pixel != (B).pixel)
+#define FONTCMP(A,B) (!gdk_font_equal(A,B))
+#define SIMPLECMP(A,B) ((A) != (B))
+
+#define IC_SET_IF_CHANGED(FIELD,VALUE,MASKBIT) 
IC_CMP_SET_IF_CHANGED(SIMPLECMP,FIELD,VALUE,MASKBIT)
+
+/* This macro assumes the following local variables: attr, inst, attrmask */
+#define FILL_IN_ATTRS() do { \
+    if (attr->style & GDK_IM_PREEDIT_POSITION) { \
+       int width = inst->cfg.width * inst->font_width; \
+       int height = inst->cfg.height * inst->font_height; \
+       int border = inst->cfg.window_border; \
+       IC_SET_IF_CHANGED(spot_location.x, border + inst->cursor_x * 
inst->font_width, GDK_IC_SPOT_LOCATION); \
+       IC_SET_IF_CHANGED(spot_location.y, border + (inst->cursor_y+1) * 
inst->font_height - 1, GDK_IC_SPOT_LOCATION); \
+       IC_SET_IF_CHANGED(preedit_area.x, border, GDK_IC_PREEDIT_AREA); \
+       IC_SET_IF_CHANGED(preedit_area.y, border, GDK_IC_PREEDIT_AREA); \
+       IC_SET_IF_CHANGED(preedit_area.width, width, GDK_IC_PREEDIT_AREA); \
+       IC_SET_IF_CHANGED(preedit_area.height, height, GDK_IC_PREEDIT_AREA); \
+       IC_CMP_SET_IF_CHANGED(FONTCMP,preedit_fontset, inst->ic_fontset, 
GDK_IC_PREEDIT_FONTSET); \
+    } \
+    IC_SET_IF_CHANGED(preedit_colormap, inst->colmap, 
GDK_IC_PREEDIT_COLORMAP); \
+    /* These fields are GtkColors and can't be compared with a simple == */ \
+    IC_CMP_SET_IF_CHANGED(COLORCMP,preedit_foreground, inst->cols[FGCOLIDX], 
GDK_IC_PREEDIT_FOREGROUND); \
+    IC_CMP_SET_IF_CHANGED(COLORCMP,preedit_background, inst->cols[BGCOLIDX], 
GDK_IC_PREEDIT_BACKGROUND); \
+    } while (0)
+
+/* We define IC_CMP_SET_IF_CHANGED suitably to give two functions:
+ * set_ic_attrs() sets all the fields in the attr struct: this
+ * is used only when initially creating the IC
+ * update_ic_attrs() updates only the fields which have changed,
+ * and then calls gdk_ic_set_attr() to tell GDK about them.
+ */
+
+static void update_ic_attrs(struct gui_data *inst)
+{
+    GdkICAttributesType attrmask = 0;
+    GdkICAttr *attr = inst->ic_attr;
+
+    if (!inst->ic) {
+       return;
+    }
+
+#define IC_CMP_SET_IF_CHANGED(CMPMACRO,FIELD,VALUE,MASKBIT) do { \
+    if (CMPMACRO(attr->FIELD, VALUE)) { attr->FIELD = (VALUE); attrmask |= 
MASKBIT; } \
+    } while (0)
+    FILL_IN_ATTRS();
+#undef IC_CMP_SET_IF_CHANGED
+    
+    if (attrmask)
+       gdk_ic_set_attr(inst->ic, inst->ic_attr, attrmask);
+}
+
+/* Fill in a GdkICAttr struct from the current 
width/height/font/border/colours.
+ * Essentially we set all attributes which aren't set-once.
+ * Returns a GdkICAttributesType mask with bits set where we've set
+ * fields in attr. We assume attr->style is valid.
+ * This is called only when first creating the IC.
+ */
+static GdkICAttributesType set_ic_attrs(struct gui_data *inst, GdkICAttr *attr)
+{
+    GdkICAttributesType attrmask = 0;
+
+#define IC_CMP_SET_IF_CHANGED(CMPMACRO,FIELD,VALUE,MASKBIT) do { \
+      attr->FIELD = (VALUE); attrmask |= MASKBIT; \
+    } while (0)
+    FILL_IN_ATTRS();
+#undef IC_CMP_SET_IF_CHANGED
+
+    return attrmask;
+}
+
+
+void realize_area (GtkWidget *widget, gpointer data)
+{
+    /* When we've been realized we can set up the input method.
+     * In theory we should hook unrealize to destroy things, but
+     * nothing else here does that (since the widget lives for the
+     * whole lifetime of the pterm process).
+     */
+    struct gui_data *inst = (struct gui_data *)data;
+    GdkEventMask mask;
+    GdkICAttr *attr;
+    GdkICAttributesType attrmask = GDK_IC_ALL_REQ;
+    /* we (the application) support this: roughly, over-the-spot only */
+    GdkIMStyle style = GDK_IM_PREEDIT_NONE
+                     | GDK_IM_PREEDIT_NOTHING
+                     | GDK_IM_PREEDIT_POSITION
+                     | GDK_IM_STATUS_NONE
+                     | GDK_IM_STATUS_NOTHING;
+
+    if (!gdk_im_ready()) {
+       return;
+    }
+
+    inst->ic_attr = attr = gdk_ic_attr_new();
+    if (!attr) {
+       return;
+    }
+    
+    /* merge styles with what the input method supports: */
+    attr->style = gdk_im_decide_style(style);
+    attr->client_window = inst->area->window;
+
+    attrmask |= set_ic_attrs(inst, attr);
+
+    inst->ic = gdk_ic_new(attr,attrmask);
+    if (!inst->ic) {
+       return;
+    }
+    
+    /* Make sure our window gets all events the IM needs */
+    mask = gdk_window_get_events(inst->area->window);
+    mask |= gdk_ic_get_events(inst->ic);
+    gdk_window_set_events(inst->area->window, mask);
+
+    /* If we already have focus, start editing now: */
+    if (GTK_WIDGET_HAS_FOCUS(inst->area)) {
+       gdk_im_begin(inst->ic, inst->area->window);
+    }
+}
+#endif
+
 gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
 {
     struct gui_data *inst = (struct gui_data *)data;
@@ -451,6 +589,10 @@
     if (inst->term)
        term_invalidate(inst->term);
 
+#ifdef USE_XIM
+    update_ic_attrs(inst);
+#endif
+
     return TRUE;
 }
 
@@ -482,6 +624,9 @@
     char output[32];
     wchar_t ucsoutput[2];
     int ucsval, start, end, special, use_ucsoutput;
+#ifdef USE_XIM
+    int use_keystring = FALSE;
+#endif
 
     /* Remember the timestamp. */
     inst->input_event_time = event->time;
@@ -628,8 +773,12 @@
        if (event->state & GDK_MOD1_MASK) {
            start = 0;
            if (end == 1) end = 0;
-       } else
+       } else {
+#ifdef USE_XIM
+           use_keystring = TRUE;
+#endif
            start = 1;
+       }
 
        /* Control-` is the same as Control-\ (unless gtk has a better idea) */
        if (!event->string[0] && event->keyval == '`' &&
@@ -637,6 +786,9 @@
            output[1] = '\x1C';
            use_ucsoutput = FALSE;
            end = 2;
+#ifdef USE_XIM
+           use_keystring = FALSE;
+#endif
        }
 
        /* Control-Break is the same as Control-C */
@@ -646,6 +798,9 @@
            use_ucsoutput = FALSE;
            end = 2;
            special = TRUE;
+#ifdef USE_XIM
+           use_keystring = FALSE;
+#endif
        }
 
        /* We handle Return ourselves, because it needs to be flagged as
@@ -655,6 +810,9 @@
            use_ucsoutput = FALSE;
            end = 2;
            special = TRUE;
+#ifdef USE_XIM
+           use_keystring = FALSE;
+#endif
        }
 
        /* Control-2, Control-Space and Control-@ are NUL */
@@ -666,6 +824,9 @@
            output[1] = '\0';
            use_ucsoutput = FALSE;
            end = 2;
+#ifdef USE_XIM
+           use_keystring = FALSE;
+#endif
        }
 
        /* Control-Shift-Space is 160 (ISO8859 nonbreaking space) */
@@ -675,6 +836,9 @@
            output[1] = '\240';
            use_ucsoutput = FALSE;
            end = 2;
+#ifdef USE_XIM
+           use_keystring = FALSE;
+#endif
        }
 
        /* We don't let GTK tell us what Backspace is! We know better. */
@@ -684,6 +848,9 @@
            use_ucsoutput = FALSE;
            end = 2;
            special = TRUE;
+#ifdef USE_XIM
+           use_keystring = FALSE;
+#endif
        }
        /* For Shift Backspace, do opposite of what is configured. */
        if (event->keyval == GDK_BackSpace &&
@@ -692,6 +859,9 @@
            use_ucsoutput = FALSE;
            end = 2;
            special = TRUE;
+#ifdef USE_XIM
+           use_keystring = FALSE;
+#endif
        }
 
        /* Shift-Tab is ESC [ Z */
@@ -699,6 +869,9 @@
            (event->keyval == GDK_Tab && (event->state & GDK_SHIFT_MASK))) {
            end = 1 + sprintf(output+1, "\033[Z");
            use_ucsoutput = FALSE;
+#ifdef USE_XIM
+           use_keystring = FALSE;
+#endif
        }
 
        /*
@@ -724,6 +897,9 @@
                else
                    output[1] = keys[0];
                use_ucsoutput = FALSE;
+#ifdef USE_XIM
+               use_keystring = FALSE;
+#endif
                goto done;
            }
        }
@@ -777,6 +953,9 @@
                } else
                    end = 1 + sprintf(output+1, "\033O%c", xkey);
                use_ucsoutput = FALSE;
+#ifdef USE_XIM
+               use_keystring = FALSE;
+#endif
                goto done;
            }
        }
@@ -881,6 +1060,9 @@
            if (inst->term->vt52_mode && code > 0 && code <= 6) {
                end = 1 + sprintf(output+1, "\x1B%c", " HLMEIG"[code]);
                use_ucsoutput = FALSE;
+#ifdef USE_XIM
+               use_keystring = FALSE;
+#endif
                goto done;
            }
 
@@ -906,6 +1088,9 @@
                if (event->state & GDK_CONTROL_MASK) index += 24;
                end = 1 + sprintf(output+1, "\x1B[%c", codes[index]);
                use_ucsoutput = FALSE;
+#ifdef USE_XIM
+               use_keystring = FALSE;
+#endif
                goto done;
            }
            if (inst->cfg.funky_type == FUNKY_SCO &&     /* SCO small keypad */
@@ -918,6 +1103,9 @@
                    end = 1 + sprintf(output+1, "\x1B[%c", codes[code-1]);
                }
                use_ucsoutput = FALSE;
+#ifdef USE_XIM
+               use_keystring = FALSE;
+#endif
                goto done;
            }
            if ((inst->term->vt52_mode || inst->cfg.funky_type == FUNKY_VT100P) 
&&
@@ -933,12 +1121,18 @@
                else
                    end = 1 + sprintf(output+1,
                                      "\x1BO%c", code + 'P' - 11 - offt);
+#ifdef USE_XIM
+               use_keystring = FALSE;
+#endif
                use_ucsoutput = FALSE;
                goto done;
            }
            if (inst->cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 
15) {
                end = 1 + sprintf(output+1, "\x1B[[%c", code + 'A' - 11);
                use_ucsoutput = FALSE;
+#ifdef USE_XIM
+               use_keystring = FALSE;
+#endif
                goto done;
            }
            if (inst->cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 
14) {
@@ -947,16 +1141,25 @@
                else
                    end = 1 + sprintf(output+1, "\x1BO%c", code + 'P' - 11);
                use_ucsoutput = FALSE;
+#ifdef USE_XIM
+               use_keystring = FALSE;
+#endif
                goto done;
            }
            if (inst->cfg.rxvt_homeend && (code == 1 || code == 4)) {
                end = 1 + sprintf(output+1, code == 1 ? "\x1B[H" : "\x1BOw");
                use_ucsoutput = FALSE;
+#ifdef USE_XIM
+               use_keystring = FALSE;
+#endif
                goto done;
            }
            if (code) {
                end = 1 + sprintf(output+1, "\x1B[%d~", code);
                use_ucsoutput = FALSE;
+#ifdef USE_XIM
+               use_keystring = FALSE;
+#endif
                goto done;
            }
        }
@@ -992,6 +1195,9 @@
                    end = 1 + sprintf(output+1, "\033[%c", xkey);
                }
                use_ucsoutput = FALSE;
+#ifdef USE_XIM
+               use_keystring = FALSE;
+#endif
                goto done;
            }
        }
@@ -1000,6 +1206,37 @@
 
     done:
 
+#ifdef USE_XIM
+    if (use_keystring) {
+       /* We copied from event->string into output earlier, but that might
+        * have been a truncated version. Remarks below about the encoding
+        * of the string apply.
+        * This code almost but doesn't quite neatly roll into the non-XIM
+        * case below. It could probably be improved upon but that would
+        * make this patch too invasive.
+        */
+       assert(!special);
+       assert(!use_ucsoutput);
+#ifdef KEY_DEBUGGING
+       printf("using keystring\n");
+#endif
+       if (!inst->ldisc || !event->length)
+           goto output_done;
+       if (!inst->direct_to_font) {
+           int codepage = (inst->ic) ? DEFAULT_CODEPAGE : CS_ISO8859_1;
+           lpage_send(inst->ldisc, codepage, event->string,
+                      event->length, 1);
+       } else {
+           /*
+            * In direct-to-font mode, we just send the string
+            * exactly as we received it.
+            */
+           ldisc_send(inst->ldisc, event->string, event->length, 1);
+       }
+       goto output_done;
+    }
+#endif
+
     if (end-start > 0) {
 #ifdef KEY_DEBUGGING
        int i;
@@ -1024,15 +1261,29 @@
                 * 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.
+                * 8859-1.
+                * The exception is that if we have and are using
+                * an input context then gdk uses XmbLookupString
+                * instead, and you get a multibyte string in the
+                * encoding of the locale of the input context.
+                * Unfortunately the X function call to find that
+                * locale (XLocaleOfIM()) requires a pointer to the
+                * XIM object, which gdk hides away from us behind
+                * an abstraction layer. So we have to assume that
+                * the current locale is the right one.
+                * I also note in passing that if the current locale
+                * happens to be a utf8 one then we will carefully
+                * convert this utf8 string to UCS and back again
+                * in lpage_send and luni_send...
                 */
+               int codepage = CS_ISO8859_1;
+#ifdef USE_XIM
+               if (inst->ic) {
+                   codepage = DEFAULT_CODEPAGE;
+               }
+#endif
                if (inst->ldisc)
-                   lpage_send(inst->ldisc, CS_ISO8859_1, output+start,
+                   lpage_send(inst->ldisc, codepage, output+start,
                               end-start, 1);
            } else {
                /*
@@ -1050,7 +1301,7 @@
            if (inst->ldisc)
                ldisc_send(inst->ldisc, output+start, end-start, 1);
        }
-
+    output_done:
        show_mouseptr(inst, 0);
        term_seen_key_event(inst->term);
     }
@@ -1245,6 +1496,15 @@
     term_set_focus(inst->term, event->in);
     term_update(inst->term);
     show_mouseptr(inst, 1);
+#ifdef USE_XIM
+    if (inst->ic) {
+       if (event->in) {
+           gdk_im_begin(inst->ic, inst->area->window);
+       } else {
+           gdk_im_end();
+       }
+    }
+#endif
     return FALSE;
 }
 
@@ -1355,9 +1615,9 @@
 void set_window_background(struct gui_data *inst)
 {
     if (inst->area && inst->area->window)
-       gdk_window_set_background(inst->area->window, &inst->cols[258]);
+       gdk_window_set_background(inst->area->window, &inst->cols[BGCOLIDX]);
     if (inst->window && inst->window->window)
-       gdk_window_set_background(inst->window->window, &inst->cols[258]);
+       gdk_window_set_background(inst->window->window, &inst->cols[BGCOLIDX]);
 }
 
 void palette_set(void *frontend, int n, int r, int g, int b)
@@ -1368,8 +1628,13 @@
     if (n > NALLCOLOURS)
        return;
     real_palette_set(inst, n, r, g, b);
-    if (n == 258)
+    if (n == BGCOLIDX)
        set_window_background(inst);
+
+#ifdef USE_XIM
+    if (n == FGCOLIDX || n == BGCOLIDX)
+       update_ic_attrs(inst);
+#endif
 }
 
 void palette_reset(void *frontend)
@@ -1422,6 +1687,10 @@
     }
 
     set_window_background(inst);
+
+#ifdef USE_XIM
+    update_ic_attrs(inst);
+#endif
 }
 
 /* Ensure that all the cut buffers exist - according to the ICCCM, we must
@@ -1815,9 +2084,13 @@
 
 void sys_cursor(void *frontend, int x, int y)
 {
-    /*
-     * This is meaningless under X.
-     */
+#ifdef USE_XIM
+    /* The input method wants to know the current cursor location */
+    struct gui_data *inst = (struct gui_data *)frontend;
+    inst->cursor_x = x;
+    inst->cursor_y = y;
+    update_ic_attrs(inst);
+#endif
 }
 
 /*
@@ -2722,6 +2995,11 @@
     if (inst->fonts[3])
         gdk_font_unref(inst->fonts[3]);
 
+#ifdef USE_XIM
+    if (inst->ic_fontset)
+       gdk_font_unref(inst->ic_fontset);
+#endif
+
     inst->fonts[0] = gdk_font_load(inst->cfg.font.name);
     if (!inst->fonts[0]) {
        fprintf(stderr, "%s: unable to load font \"%s\"\n", appname,
@@ -2767,6 +3045,29 @@
                inst->cfg.widefont.name);
        exit(1);
     }
+#ifdef USE_XIM
+    /* The input method wants not a font but a fontset consisting
+     * of the normal and the wide-char font.
+     */
+    {
+       char *fontsetname;
+       if (!inst->fonts[2]) {
+       /* No wide font, assume the normal font is good enough */
+           fontsetname = inst->cfg.font.name;
+       } else {
+           /* We need a comma-separated list of fonts */
+           fontsetname = dupcat(inst->cfg.font.name, ",", name, NULL);
+       }
+       inst->ic_fontset = gdk_fontset_load(fontsetname);
+       if (!inst->ic_fontset) {
+           fprintf(stderr, "%s: unable to load fontset \"%s\"\n", appname, 
fontsetname);
+           exit(1);
+       }
+       if (fontsetname != inst->cfg.font.name)
+           sfree(fontsetname);
+    }
+#endif
+
     if (guessed)
        sfree(name);
 
@@ -2931,7 +3232,7 @@
                 * repaint the space in between the window border
                 * and the text area.
                 */
-               if (i == 258) {
+               if (i == BGCOLIDX) {
                    set_window_background(inst);
                    draw_backing_rect(inst);
                }
@@ -3350,6 +3651,14 @@
      * it */
     block_signal(SIGCHLD, 1);
 
+#ifdef USE_XIM
+    /* Set locale. This is required for XIM support. It must go before 
gtk_init().
+     * FIXME it would be more consistent to do this whether we have XIM support
+     * or not.
+     */
+    gdk_set_locale();
+#endif
+
     inst->progname = argv[0];
     /*
      * Copy the original argv before letting gtk_init fiddle with
@@ -3467,6 +3776,10 @@
                       GTK_SIGNAL_FUNC(selection_get), inst);
     gtk_signal_connect(GTK_OBJECT(inst->area), "selection_clear_event",
                       GTK_SIGNAL_FUNC(selection_clear), inst);
+#ifdef USE_XIM
+    gtk_signal_connect(GTK_OBJECT(inst->area), "realize",
+                      GTK_SIGNAL_FUNC(realize_area), inst);
+#endif
     if (inst->cfg.scrollbar)
        gtk_signal_connect(GTK_OBJECT(inst->sbar_adjust), "value_changed",
                           GTK_SIGNAL_FUNC(scrollbar_moved), inst);
diff -ur putty-0.58/unix/uxucs.c putty-0.58-patched/unix/uxucs.c
--- putty-0.58/unix/uxucs.c     2005-04-05 20:37:47.000000000 +0100
+++ putty-0.58-patched/unix/uxucs.c     2006-03-04 22:41:10.000000000 +0000
@@ -35,6 +35,12 @@
            size_t i = mbrtowc(wcstr+n, mbstr, (size_t)mblen, &state);
            if (i == (size_t)-1 || i == (size_t)-2)
                break;
+           /* If we just read NUL then the widechar entry is filled
+            * but the return value is 0 instead of the length of the
+            * multibyte NUL. We assume it's always 1.
+            */
+           if (i == 0)
+              i = 1;
            n++;
            mbstr += i;
            mblen -= i;


-- 
To UNSUBSCRIBE, email to [EMAIL PROTECTED]
with a subject of "unsubscribe". Trouble? Contact [EMAIL PROTECTED]

Reply via email to