Add a configuration option for TCP keepalives (SO_KEEPALIVE), default off.
[sgt/putty] / unix / pterm.c
index e3ea2f9..8e2554d 100644 (file)
@@ -22,6 +22,7 @@
 #include <gdk/gdkx.h>
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
+#include <X11/Xatom.h>
 
 #define PUTTY_DO_GLOBALS              /* actually _define_ globals */
 
@@ -1332,6 +1333,51 @@ void palette_reset(void *frontend)
     set_window_background(inst);
 }
 
+/* Ensure that all the cut buffers exist - according to the ICCCM, we must
+ * do this before we start using cut buffers.
+ */
+void init_cutbuffers()
+{
+    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+                   XA_CUT_BUFFER0, XA_STRING, 8, PropModeAppend, "", 0);
+    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+                   XA_CUT_BUFFER1, XA_STRING, 8, PropModeAppend, "", 0);
+    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+                   XA_CUT_BUFFER2, XA_STRING, 8, PropModeAppend, "", 0);
+    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+                   XA_CUT_BUFFER3, XA_STRING, 8, PropModeAppend, "", 0);
+    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+                   XA_CUT_BUFFER4, XA_STRING, 8, PropModeAppend, "", 0);
+    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+                   XA_CUT_BUFFER5, XA_STRING, 8, PropModeAppend, "", 0);
+    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+                   XA_CUT_BUFFER6, XA_STRING, 8, PropModeAppend, "", 0);
+    XChangeProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
+                   XA_CUT_BUFFER7, XA_STRING, 8, PropModeAppend, "", 0);
+}
+
+/* Store the data in a cut-buffer. */
+void store_cutbuffer(char * ptr, int len)
+{
+    /* ICCCM says we must rotate the buffers before storing to buffer 0. */
+    XRotateBuffers(GDK_DISPLAY(), 1);
+    XStoreBytes(GDK_DISPLAY(), ptr, len);
+}
+
+/* Retrieve data from a cut-buffer.
+ * Returned data needs to be freed with XFree().
+ */
+char * retrieve_cutbuffer(int * nbytes)
+{
+    char * ptr;
+    ptr = XFetchBytes(GDK_DISPLAY(), nbytes);
+    if (*nbytes <= 0 && ptr != 0) {
+       XFree(ptr);
+       ptr = 0;
+    }
+    return ptr;
+}
+
 void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
 {
     struct gui_data *inst = (struct gui_data *)frontend;
@@ -1400,6 +1446,8 @@ void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
            sresize(inst->pasteout_data, inst->pasteout_data_len, char);
     }
 
+    store_cutbuffer(inst->pasteout_data, inst->pasteout_data_len);
+
     if (gtk_selection_owner_set(inst->area, GDK_SELECTION_PRIMARY,
                                inst->input_event_time)) {
        gtk_selection_add_target(inst->area, GDK_SELECTION_PRIMARY,
@@ -1496,6 +1544,7 @@ void selection_received(GtkWidget *widget, GtkSelectionData *seldata,
     char *text;
     int length, count, ret;
     int free_list_required = 0;
+    int free_required = 0;
     int charset;
 
     if (seldata->target == utf8_string_atom && seldata->length <= 0) {
@@ -1521,42 +1570,56 @@ void selection_received(GtkWidget *widget, GtkSelectionData *seldata,
     }
 
     /*
-     * Any other failure should just go foom.
+     * If we have data, but it's not of a type we can deal with,
+     * we have to ignore the data.
      */
-    if (seldata->length <= 0 ||
-       (seldata->type != GDK_SELECTION_TYPE_STRING &&
-        seldata->type != compound_text_atom &&
-        seldata->type != utf8_string_atom))
-       return;                        /* Nothing happens. */
-
+    if (seldata->length > 0 &&
+       seldata->type != GDK_SELECTION_TYPE_STRING &&
+       seldata->type != compound_text_atom &&
+       seldata->type != utf8_string_atom)
+       return;
 
     /*
-     * Convert COMPOUND_TEXT into UTF-8.
+     * If we have no data, try looking in a cut buffer.
      */
-    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,
-                                 inst->input_event_time);
+    if (seldata->length <= 0) {
+       text = retrieve_cutbuffer(&length);
+       if (length == 0)
            return;
-       }
-       text = list[0];
-       length = strlen(list[0]);
-       charset = CS_UTF8;
-       free_list_required = 1;
+       /* Xterm is rumoured to expect Latin-1, though I havn't checked the
+        * source, so use that as a de-facto standard. */
+       charset = CS_ISO8859_1;
+       free_required = 1;
     } else {
-       text = (char *)seldata->data;
-       length = seldata->length;
-       charset = (seldata->type == utf8_string_atom ?
-                  CS_UTF8 : inst->ucsdata.line_codepage);
+       /*
+        * 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,
+                                     inst->input_event_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)
@@ -1575,6 +1638,8 @@ void selection_received(GtkWidget *widget, GtkSelectionData *seldata,
 
     if (free_list_required)
        XFreeStringList(list);
+    if (free_required)
+       XFree(text);
 }
 
 gint idle_paste_func(gpointer data)
@@ -2558,23 +2623,27 @@ void setup_fonts_ucs(struct gui_data *inst)
     }
     font_charset = set_font_info(inst, 0);
 
-    if (inst->cfg.boldfont.name[0]) {
-       name = inst->cfg.boldfont.name;
-       guessed = FALSE;
+    if (inst->cfg.shadowbold) {
+       inst->fonts[1] = NULL;
     } 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 if (!guessed) {
-       fprintf(stderr, "%s: unable to load bold font \"%s\"\n", appname,
-               inst->cfg.boldfont.name);
-       exit(1);
+       if (inst->cfg.boldfont.name[0]) {
+           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 if (!guessed) {
+           fprintf(stderr, "%s: unable to load bold font \"%s\"\n", appname,
+                   inst->cfg.boldfont.name);
+           exit(1);
+       }
+       if (guessed)
+           sfree(name);
     }
-    if (guessed)
-       sfree(name);
 
     if (inst->cfg.widefont.name[0]) {
        name = inst->cfg.widefont.name;
@@ -2594,33 +2663,37 @@ void setup_fonts_ucs(struct gui_data *inst)
     if (guessed)
        sfree(name);
 
-    if (inst->cfg.wideboldfont.name[0]) {
-       name = inst->cfg.wideboldfont.name;
-       guessed = FALSE;
+    if (inst->cfg.shadowbold) {
+       inst->fonts[3] = NULL;
     } 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 if (!guessed) {
-       fprintf(stderr, "%s: unable to load wide/bold font \"%s\"\n", appname,
-               inst->cfg.wideboldfont.name);
-       exit(1);
+       if (inst->cfg.wideboldfont.name[0]) {
+           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 if (!guessed) {
+           fprintf(stderr, "%s: unable to load wide/bold font \"%s\"\n", appname,
+                   inst->cfg.wideboldfont.name);
+           exit(1);
+       }
+       if (guessed)
+           sfree(name);
     }
-    if (guessed)
-       sfree(name);
 
     inst->font_width = gdk_char_width(inst->fonts[0], ' ');
     inst->font_height = inst->fonts[0]->ascent + inst->fonts[0]->descent;
@@ -2777,7 +2850,8 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data)
             strcmp(oldcfg.widefont.name, cfg2.widefont.name) ||
             strcmp(oldcfg.wideboldfont.name, cfg2.wideboldfont.name) ||
             strcmp(oldcfg.line_codepage, cfg2.line_codepage) ||
-           oldcfg.vtmode != cfg2.vtmode) {
+           oldcfg.vtmode != cfg2.vtmode ||
+           oldcfg.shadowbold != cfg2.shadowbold) {
             setup_fonts_ucs(inst);
             need_size = 1;
         } else
@@ -3108,6 +3182,7 @@ int pt_main(int argc, char **argv)
         utf8_string_atom = gdk_atom_intern("UTF8_STRING", FALSE);
 
     setup_fonts_ucs(inst);
+    init_cutbuffers();
 
     inst->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
 
@@ -3292,7 +3367,8 @@ int pt_main(int argc, char **argv)
 
        error = inst->back->init((void *)inst, &inst->backhandle,
                                  &inst->cfg, inst->cfg.host, inst->cfg.port,
-                                 &realhost, inst->cfg.tcp_nodelay);
+                                 &realhost, inst->cfg.tcp_nodelay,
+                                inst->cfg.tcp_keepalives);
 
        if (error) {
            char *msg = dupprintf("Unable to open connection to %s:\n%s",