Control of 'addr' is now handed over to {platform_,}new_connection() and
[u/mdw/putty] / unix / pterm.c
index 92adcb1..1e6db62 100644 (file)
@@ -58,8 +58,8 @@ struct gui_data {
     wchar_t *pastein_data;
     int direct_to_font;
     int pastein_data_len;
-    char *pasteout_data, *pasteout_data_utf8;
-    int pasteout_data_len, pasteout_data_utf8_len;
+    char *pasteout_data, *pasteout_data_ctext, *pasteout_data_utf8;
+    int pasteout_data_len, pasteout_data_ctext_len, pasteout_data_utf8_len;
     int font_width, font_height;
     int width, height;
     int ignore_sbar;
@@ -344,7 +344,7 @@ void get_window_pixels(void *frontend, int *x, int *y)
 char *get_window_title(void *frontend, int icon)
 {
     struct gui_data *inst = (struct gui_data *)frontend;
-    return icon ? inst->wintitle : inst->icontitle;
+    return icon ? inst->icontitle : inst->wintitle;
 }
 
 gint delete_window(GtkWidget *widget, GdkEvent *event, gpointer data)
@@ -368,11 +368,20 @@ static void show_mouseptr(struct gui_data *inst, int show)
     inst->mouseptr_visible = show;
 }
 
+void draw_backing_rect(struct gui_data *inst)
+{
+    GdkGC *gc = gdk_gc_new(inst->area->window);
+    gdk_gc_set_foreground(gc, &inst->cols[18]);   /* 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);
+}
+
 gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
 {
     struct gui_data *inst = (struct gui_data *)data;
     int w, h, need_size = 0;
-    GdkGC *gc;
 
     /*
      * See if the terminal size has changed, in which case we must
@@ -397,12 +406,7 @@ gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
                                  (inst->cfg.height * inst->font_height +
                                   2*inst->cfg.window_border), -1);
 
-    gc = gdk_gc_new(inst->area->window);
-    gdk_gc_set_foreground(gc, &inst->cols[18]);   /* 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);
+    draw_backing_rect(inst);
 
     if (need_size && inst->term) {
        term_size(inst->term, h, w, inst->cfg.savelines);
@@ -1323,16 +1327,20 @@ 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);
+    if (inst->pasteout_data_ctext)
+       sfree(inst->pasteout_data_ctext);
     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.
+     * Set up UTF-8 and compound text 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;
+       XTextProperty tp;
+       char *list[1];
 
        inst->pasteout_data_utf8 = snewn(len*6, char);
        inst->pasteout_data_utf8_len = len*6;
@@ -1346,11 +1354,26 @@ void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
        } else {
            inst->pasteout_data_utf8 =
                sresize(inst->pasteout_data_utf8,
-                       inst->pasteout_data_utf8_len, char);
+                       inst->pasteout_data_utf8_len + 1, char);
+           inst->pasteout_data_utf8[inst->pasteout_data_utf8_len] = '\0';
+       }
+
+       /*
+        * Now let Xlib convert our UTF-8 data into compound text.
+        */
+       list[0] = inst->pasteout_data_utf8;
+       if (Xutf8TextListToTextProperty(GDK_DISPLAY(), list, 1,
+                                       XCompoundTextStyle, &tp) == 0) {
+           inst->pasteout_data_ctext = snewn(tp.nitems+1, char);
+           memcpy(inst->pasteout_data_ctext, tp.value, tp.nitems);
+           inst->pasteout_data_ctext_len = tp.nitems;
+           XFree(tp.value);
        }
     } else {
        inst->pasteout_data_utf8 = NULL;
        inst->pasteout_data_utf8_len = 0;
+       inst->pasteout_data_ctext = NULL;
+       inst->pasteout_data_ctext_len = 0;
     }
 
     inst->pasteout_data = snewn(len*6, char);
@@ -1371,8 +1394,9 @@ void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
                                GDK_CURRENT_TIME)) {
        gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
                                 GDK_SELECTION_TYPE_STRING, 1);
-       gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
-                                compound_text_atom, 1);
+       if (inst->pasteout_data_ctext)
+           gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
+                                    compound_text_atom, 1);
        if (inst->pasteout_data_utf8)
            gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
                                     utf8_string_atom, 1);
@@ -1390,6 +1414,10 @@ void selection_get(GtkWidget *widget, GtkSelectionData *seldata,
        gtk_selection_data_set(seldata, seldata->target, 8,
                               inst->pasteout_data_utf8,
                               inst->pasteout_data_utf8_len);
+    else if (seldata->target == compound_text_atom)
+       gtk_selection_data_set(seldata, seldata->target, 8,
+                              inst->pasteout_data_ctext,
+                              inst->pasteout_data_ctext_len);
     else
        gtk_selection_data_set(seldata, seldata->target, 8,
                               inst->pasteout_data, inst->pasteout_data_len);
@@ -1402,10 +1430,14 @@ gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata,
     term_deselect(inst->term);
     if (inst->pasteout_data)
        sfree(inst->pasteout_data);
+    if (inst->pasteout_data_ctext)
+       sfree(inst->pasteout_data_ctext);
     if (inst->pasteout_data_utf8)
        sfree(inst->pasteout_data_utf8);
     inst->pasteout_data = NULL;
     inst->pasteout_data_len = 0;
+    inst->pasteout_data_ctext = NULL;
+    inst->pasteout_data_ctext_len = 0;
     inst->pasteout_data_utf8 = NULL;
     inst->pasteout_data_utf8_len = 0;
     return TRUE;
@@ -1446,10 +1478,26 @@ void selection_received(GtkWidget *widget, GtkSelectionData *seldata,
                        guint time, gpointer data)
 {
     struct gui_data *inst = (struct gui_data *)data;
+    XTextProperty tp;
+    char **list;
+    char *text;
+    int length, count, ret;
+    int free_list_required = 0;
+    int charset;
 
     if (seldata->target == utf8_string_atom && seldata->length <= 0) {
        /*
-        * Failed to get a UTF-8 selection string. Try an ordinary
+        * Failed to get a UTF-8 selection string. Try compound
+        * text next.
+        */
+       gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
+                             compound_text_atom, GDK_CURRENT_TIME);
+       return;
+    }
+
+    if (seldata->target == compound_text_atom && seldata->length <= 0) {
+       /*
+        * Failed to get UTF-8 or compound text. Try an ordinary
         * string.
         */
        gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
@@ -1462,24 +1510,55 @@ void selection_received(GtkWidget *widget, GtkSelectionData *seldata,
      */
     if (seldata->length <= 0 ||
        (seldata->type != GDK_SELECTION_TYPE_STRING &&
+        seldata->type != compound_text_atom &&
         seldata->type != utf8_string_atom))
        return;                        /* Nothing happens. */
 
+
+    /*
+     * Convert COMPOUND_TEXT into UTF-8.
+     */
+    if (seldata->type == compound_text_atom) {
+       tp.value = seldata->data;
+       tp.encoding = (Atom) seldata->type;
+       tp.format = seldata->format;
+       tp.nitems = seldata->length;
+       ret = Xutf8TextPropertyToTextList(GDK_DISPLAY(), &tp, &list, &count);
+       if (ret != 0 || count != 1) {
+           /*
+            * Compound text failed; fall back to STRING.
+            */
+           gtk_selection_convert(inst->area, GDK_SELECTION_PRIMARY,
+                                 GDK_SELECTION_TYPE_STRING, GDK_CURRENT_TIME);
+           return;
+       }
+       text = list[0];
+       length = strlen(list[0]);
+       charset = CS_UTF8;
+       free_list_required = 1;
+    } else {
+       text = (char *)seldata->data;
+       length = seldata->length;
+       charset = (seldata->type == utf8_string_atom ?
+                  CS_UTF8 : inst->ucsdata.line_codepage);
+    }
+
     if (inst->pastein_data)
        sfree(inst->pastein_data);
 
-    inst->pastein_data = snewn(seldata->length, wchar_t);
-    inst->pastein_data_len = seldata->length;
+    inst->pastein_data = snewn(length, wchar_t);
+    inst->pastein_data_len = length;
     inst->pastein_data_len =
-       mb_to_wc((seldata->type == utf8_string_atom ?
-                 CS_UTF8 : inst->ucsdata.line_codepage),
-                0, seldata->data, seldata->length,
+       mb_to_wc(charset, 0, text, length,
                 inst->pastein_data, inst->pastein_data_len);
 
     term_do_paste(inst->term);
 
     if (term_paste_pending(inst->term))
        inst->term_paste_idle_id = gtk_idle_add(idle_paste_func, inst);
+
+    if (free_list_required)
+       XFreeStringList(list);
 }
 
 gint idle_paste_func(gpointer data)
@@ -2388,9 +2467,63 @@ void uxsel_input_remove(int id) {
     gdk_input_remove(id);
 }
 
+char *guess_derived_font_name(GdkFont *font, int bold, int wide)
+{
+    XFontStruct *xfs = GDK_FONT_XFONT(font);
+    Display *disp = GDK_FONT_XDISPLAY(font);
+    Atom fontprop = XInternAtom(disp, "FONT", False);
+    unsigned long ret;
+    if (XGetFontProperty(xfs, fontprop, &ret)) {
+       char *name = XGetAtomName(disp, (Atom)ret);
+       if (name && name[0] == '-') {
+           char *strings[13];
+           char *dupname, *extrafree = NULL, *ret;
+           char *p, *q;
+           int nstr;
+
+           p = q = dupname = dupstr(name); /* skip initial minus */
+           nstr = 0;
+
+           while (*p && nstr < lenof(strings)) {
+               if (*p == '-') {
+                   *p = '\0';
+                   strings[nstr++] = p+1;
+               }
+               p++;
+           }
+
+           if (nstr < lenof(strings))
+               return NULL;           /* XLFD was malformed */
+
+           if (bold)
+               strings[2] = "bold";
+
+           if (wide) {
+               /* 4 is `wideness', which obviously may have changed. */
+               /* 5 is additional style, which may be e.g. `ja' or `ko'. */
+               strings[4] = strings[5] = "*";
+               strings[11] = extrafree = dupprintf("%d", 2*atoi(strings[11]));
+           }
+
+           ret = dupcat("-", strings[ 0], "-", strings[ 1], "-", strings[ 2],
+                        "-", strings[ 3], "-", strings[ 4], "-", strings[ 5],
+                        "-", strings[ 6], "-", strings[ 7], "-", strings[ 8],
+                        "-", strings[ 9], "-", strings[10], "-", strings[11],
+                        "-", strings[12], NULL);
+           sfree(extrafree);
+           sfree(dupname);
+
+           return ret;
+       }
+    }
+    return NULL;
+}
+
 void setup_fonts_ucs(struct gui_data *inst)
 {
     int font_charset;
+    char *name;
+    int guessed;
 
     if (inst->fonts[0])
         gdk_font_unref(inst->fonts[0]);
@@ -2408,36 +2541,70 @@ void setup_fonts_ucs(struct gui_data *inst)
        exit(1);
     }
     font_charset = set_font_info(inst, 0);
+
     if (inst->cfg.boldfont.name[0]) {
-       inst->fonts[1] = gdk_font_load(inst->cfg.boldfont.name);
-       if (!inst->fonts[1]) {
-           fprintf(stderr, "%s: unable to load bold font \"%s\"\n", appname,
-                   inst->cfg.boldfont.name);
-           exit(1);
-       }
+       name = inst->cfg.boldfont.name;
+       guessed = FALSE;
+    } else {
+       name = guess_derived_font_name(inst->fonts[0], TRUE, FALSE);
+       guessed = TRUE;
+    }
+    inst->fonts[1] = name ? gdk_font_load(name) : NULL;
+    if (inst->fonts[1]) {
        set_font_info(inst, 1);
-    } else
-       inst->fonts[1] = NULL;
+    } else if (!guessed) {
+       fprintf(stderr, "%s: unable to load bold font \"%s\"\n", appname,
+               inst->cfg.boldfont.name);
+       exit(1);
+    }
+    if (guessed)
+       sfree(name);
+
     if (inst->cfg.widefont.name[0]) {
-       inst->fonts[2] = gdk_font_load(inst->cfg.widefont.name);
-       if (!inst->fonts[2]) {
-           fprintf(stderr, "%s: unable to load wide font \"%s\"\n", appname,
-                   inst->cfg.widefont.name);
-           exit(1);
-       }
+       name = inst->cfg.widefont.name;
+       guessed = FALSE;
+    } else {
+       name = guess_derived_font_name(inst->fonts[0], FALSE, TRUE);
+       guessed = TRUE;
+    }
+    inst->fonts[2] = name ? gdk_font_load(name) : NULL;
+    if (inst->fonts[2]) {
        set_font_info(inst, 2);
-    } else
-       inst->fonts[2] = NULL;
+    } else if (!guessed) {
+       fprintf(stderr, "%s: unable to load wide font \"%s\"\n", appname,
+               inst->cfg.widefont.name);
+       exit(1);
+    }
+    if (guessed)
+       sfree(name);
+
     if (inst->cfg.wideboldfont.name[0]) {
-       inst->fonts[3] = gdk_font_load(inst->cfg.wideboldfont.name);
-       if (!inst->fonts[3]) {
-           fprintf(stderr, "%s: unable to load wide/bold font \"%s\"\n",
-                    appname, inst->cfg.wideboldfont.name);
-           exit(1);
-       }
+       name = inst->cfg.wideboldfont.name;
+       guessed = FALSE;
+    } else {
+       /*
+        * Here we have some choices. We can widen the bold font,
+        * bolden the wide font, or widen and bolden the standard
+        * font. Try them all, in that order!
+        */
+       if (inst->cfg.widefont.name[0])
+           name = guess_derived_font_name(inst->fonts[2], TRUE, FALSE);
+       else if (inst->cfg.boldfont.name[0])
+           name = guess_derived_font_name(inst->fonts[1], FALSE, TRUE);
+       else
+           name = guess_derived_font_name(inst->fonts[0], TRUE, TRUE);
+       guessed = TRUE;
+    }
+    inst->fonts[3] = name ? gdk_font_load(name) : NULL;
+    if (inst->fonts[3]) {
        set_font_info(inst, 3);
-    } else
-       inst->fonts[3] = NULL;
+    } else if (!guessed) {
+       fprintf(stderr, "%s: unable to load wide/bold font \"%s\"\n", appname,
+               inst->cfg.wideboldfont.name);
+       exit(1);
+    }
+    if (guessed)
+       sfree(name);
 
     inst->font_width = gdk_char_width(inst->fonts[0], ' ');
     inst->font_height = inst->fonts[0]->ascent + inst->fonts[0]->descent;
@@ -2546,10 +2713,21 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data)
         for (i = 0; i < NCOLOURS; i++) {
             if (oldcfg.colours[ww[i]][0] != cfg2.colours[ww[i]][0] ||
                 oldcfg.colours[ww[i]][1] != cfg2.colours[ww[i]][1] ||
-                oldcfg.colours[ww[i]][2] != cfg2.colours[ww[i]][2])
+                oldcfg.colours[ww[i]][2] != cfg2.colours[ww[i]][2]) {
                 real_palette_set(inst, i, cfg2.colours[ww[i]][0],
                                  cfg2.colours[ww[i]][1],
                                  cfg2.colours[ww[i]][2]);
+
+               /*
+                * If the default background has changed, we must
+                * repaint the space in between the window border
+                * and the text area.
+                */
+               if (i == 18) {
+                   set_window_background(inst);
+                   draw_backing_rect(inst);
+               }
+           }
         }
 
         /*
@@ -2610,6 +2788,12 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data)
        }
 
         term_invalidate(inst->term);
+
+       /*
+        * We do an explicit full redraw here to ensure the window
+        * border has been redrawn as well as the text area.
+        */
+       gtk_widget_queue_draw(inst->area);
     }
     sfree(title);
 }