Re-engineering of terminal emulator, phase 1.
[u/mdw/putty] / unix / pterm.c
index 483122a..d374879 100644 (file)
@@ -44,7 +44,8 @@ struct gui_data {
     GtkWidget *window, *area, *sbar;
     GtkBox *hbox;
     GtkAdjustment *sbar_adjust;
-    GtkWidget *menu, *specialsmenu, *specialsitem1, *specialsitem2;
+    GtkWidget *menu, *specialsmenu, *specialsitem1, *specialsitem2,
+       *restartitem;
     GtkWidget *sessionsmenu;
     GdkPixmap *pixmap;
     GdkFont *fonts[4];                 /* normal, bold, wide, widebold */
@@ -94,6 +95,8 @@ static int send_raw_mouse;
 
 static char *app_name = "pterm";
 
+static void start_backend(struct gui_data *inst);
+
 char *x_get_default(const char *key)
 {
     return XGetDefault(GDK_DISPLAY(), app_name, key);
@@ -711,7 +714,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
                 * in xterm function key mode we change which two...
                 */
              case GDK_KP_Add:
-               if (inst->cfg.funky_type == 2) {
+               if (inst->cfg.funky_type == FUNKY_XTERM) {
                    if (event->state & GDK_SHIFT_MASK)
                        xkey = 'l';
                    else
@@ -841,7 +844,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
                break;
            }
            /* Reorder edit keys to physical order */
-           if (inst->cfg.funky_type == 3 && code <= 6)
+           if (inst->cfg.funky_type == FUNKY_VT400 && code <= 6)
                code = "\0\2\1\4\5\3\6"[code];
 
            if (inst->term->vt52_mode && code > 0 && code <= 6) {
@@ -850,7 +853,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
                goto done;
            }
 
-           if (inst->cfg.funky_type == 5 &&     /* SCO function keys */
+           if (inst->cfg.funky_type == FUNKY_SCO &&     /* SCO function keys */
                code >= 11 && code <= 34) {
                char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{";
                int index = 0;
@@ -874,7 +877,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
                use_ucsoutput = FALSE;
                goto done;
            }
-           if (inst->cfg.funky_type == 5 &&     /* SCO small keypad */
+           if (inst->cfg.funky_type == FUNKY_SCO &&     /* SCO small keypad */
                code >= 1 && code <= 6) {
                char codes[] = "HL.FIG";
                if (code == 3) {
@@ -886,7 +889,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
                use_ucsoutput = FALSE;
                goto done;
            }
-           if ((inst->term->vt52_mode || inst->cfg.funky_type == 4) &&
+           if ((inst->term->vt52_mode || inst->cfg.funky_type == FUNKY_VT100P) &&
                code >= 11 && code <= 24) {
                int offt = 0;
                if (code > 15)
@@ -902,12 +905,12 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
                use_ucsoutput = FALSE;
                goto done;
            }
-           if (inst->cfg.funky_type == 1 && code >= 11 && code <= 15) {
+           if (inst->cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) {
                end = 1 + sprintf(output+1, "\x1B[[%c", code + 'A' - 11);
                use_ucsoutput = FALSE;
                goto done;
            }
-           if (inst->cfg.funky_type == 2 && code >= 11 && code <= 14) {
+           if (inst->cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) {
                if (inst->term->vt52_mode)
                    end = 1 + sprintf(output+1, "\x1B%c", code + 'P' - 11);
                else
@@ -981,7 +984,8 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
             * should never matter.
             */
            output[end] = '\0';        /* NUL-terminate */
-           ldisc_send(inst->ldisc, output+start, -2, 1);
+           if (inst->ldisc)
+               ldisc_send(inst->ldisc, output+start, -2, 1);
        } else if (!inst->direct_to_font) {
            if (!use_ucsoutput) {
                /*
@@ -996,21 +1000,24 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
                 * 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);
+               if (inst->ldisc)
+                   lpage_send(inst->ldisc, CS_ISO8859_1, output+start,
+                              end-start, 1);
            } else {
                /*
                 * We generated our own Unicode key data from the
                 * keysym, so use that instead.
                 */
-               luni_send(inst->ldisc, ucsoutput+start, end-start, 1);
+               if (inst->ldisc)
+                   luni_send(inst->ldisc, ucsoutput+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);
+           if (inst->ldisc)
+               ldisc_send(inst->ldisc, output+start, end-start, 1);
        }
 
        show_mouseptr(inst, 0);
@@ -1134,6 +1141,17 @@ gint timer_func(gpointer data)
        if (inst->cfg.close_on_exit == FORCE_ON ||
            (inst->cfg.close_on_exit == AUTO && exitcode == 0))
            exit(0);                   /* just go. */
+       if (inst->ldisc) {
+           ldisc_free(inst->ldisc);
+           inst->ldisc = NULL;
+       }
+       if (inst->back) {
+           inst->back->free(inst->backhandle);
+           inst->backhandle = NULL;
+           inst->back = NULL;
+           update_specials_menu(inst);
+       }
+       gtk_widget_show(inst->restartitem);
     }
 
     term_update(inst->term);
@@ -1371,7 +1389,7 @@ char * retrieve_cutbuffer(int * nbytes)
 {
     char * ptr;
     ptr = XFetchBytes(GDK_DISPLAY(), nbytes);
-    if (nbytes <= 0 && ptr != 0) {
+    if (*nbytes <= 0 && ptr != 0) {
        XFree(ptr);
        ptr = 0;
     }
@@ -1424,7 +1442,10 @@ void write_clip(void *frontend, wchar_t * data, int len, int must_deselect)
            memcpy(inst->pasteout_data_ctext, tp.value, tp.nitems);
            inst->pasteout_data_ctext_len = tp.nitems;
            XFree(tp.value);
-       }
+       } else {
+            inst->pasteout_data_ctext = NULL;
+            inst->pasteout_data_ctext_len = 0;
+        }
     } else {
        inst->pasteout_data_utf8 = NULL;
        inst->pasteout_data_utf8_len = 0;
@@ -1591,34 +1612,35 @@ void selection_received(GtkWidget *widget, GtkSelectionData *seldata,
        charset = CS_ISO8859_1;
        free_required = 1;
     } else {
-    /*
-     * 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;
+       /*
+        * 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);
        }
-       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)
@@ -1776,7 +1798,7 @@ void free_ctx(Context ctx)
  *
  * We are allowed to fiddle with the contents of `text'.
  */
-void do_text_internal(Context ctx, int x, int y, char *text, int len,
+void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len,
                      unsigned long attr, int lattr)
 {
     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
@@ -1819,7 +1841,7 @@ void do_text_internal(Context ctx, int x, int y, char *text, int len,
            shadow = 1;
     }
 
-    if (lattr != LATTR_NORM) {
+    if ((lattr & LATTR_MODE) != LATTR_NORM) {
        x *= 2;
        if (x >= inst->term->cols)
            return;
@@ -1854,7 +1876,7 @@ void do_text_internal(Context ctx, int x, int y, char *text, int len,
 
        wcs = snewn(len+1, wchar_t);
        for (i = 0; i < len; i++) {
-           wcs[i] = (wchar_t) ((attr & CSET_MASK) + (text[i] & CHAR_MASK));
+           wcs[i] = text[i];
        }
 
        if (inst->fonts[fontid] == NULL) {
@@ -1891,6 +1913,11 @@ void do_text_internal(Context ctx, int x, int y, char *text, int len,
                             x*inst->font_width+inst->cfg.window_border,
                             y*inst->font_height+inst->cfg.window_border+inst->fonts[0]->ascent,
                             gwcs, len*2);
+           if (shadow)
+               gdk_draw_text_wc(inst->pixmap, inst->fonts[fontid], gc,
+                                x*inst->font_width+inst->cfg.window_border+inst->cfg.shadowboldoffset,
+                                y*inst->font_height+inst->cfg.window_border+inst->fonts[0]->ascent,
+                                gwcs, len*2);
            sfree(gwcs);
        } else {
            gcs = snewn(len+1, gchar);
@@ -1900,18 +1927,16 @@ void do_text_internal(Context ctx, int x, int y, char *text, int len,
                          x*inst->font_width+inst->cfg.window_border,
                          y*inst->font_height+inst->cfg.window_border+inst->fonts[0]->ascent,
                          gcs, len);
+           if (shadow)
+               gdk_draw_text(inst->pixmap, inst->fonts[fontid], gc,
+                             x*inst->font_width+inst->cfg.window_border+inst->cfg.shadowboldoffset,
+                             y*inst->font_height+inst->cfg.window_border+inst->fonts[0]->ascent,
+                             gcs, len);
            sfree(gcs);
        }
        sfree(wcs);
     }
 
-    if (shadow) {
-       gdk_draw_text(inst->pixmap, inst->fonts[fontid], gc,
-                     x*inst->font_width+inst->cfg.window_border + inst->cfg.shadowboldoffset,
-                     y*inst->font_height+inst->cfg.window_border+inst->fonts[0]->ascent,
-                     text, len);
-    }
-
     if (attr & ATTR_UNDER) {
        int uheight = inst->fonts[0]->ascent + 1;
        if (uheight >= inst->font_height)
@@ -1922,7 +1947,7 @@ void do_text_internal(Context ctx, int x, int y, char *text, int len,
                      y*inst->font_height + uheight + inst->cfg.window_border);
     }
 
-    if (lattr != LATTR_NORM) {
+    if ((lattr & LATTR_MODE) != LATTR_NORM) {
        /*
         * I can't find any plausible StretchBlt equivalent in the
         * X server, so I'm going to do this the slow and painful
@@ -1941,10 +1966,10 @@ void do_text_internal(Context ctx, int x, int y, char *text, int len,
                            len * inst->font_width - i, inst->font_height);
        }
        len *= 2;
-       if (lattr != LATTR_WIDE) {
+       if ((lattr & LATTR_MODE) != LATTR_WIDE) {
            int dt, db;
            /* Now stretch vertically, in the same way. */
-           if (lattr == LATTR_BOT)
+           if ((lattr & LATTR_MODE) == LATTR_BOT)
                dt = 0, db = 1;
            else
                dt = 1, db = 0;
@@ -1960,7 +1985,7 @@ void do_text_internal(Context ctx, int x, int y, char *text, int len,
     }
 }
 
-void do_text(Context ctx, int x, int y, char *text, int len,
+void do_text(Context ctx, int x, int y, wchar_t *text, int len,
             unsigned long attr, int lattr)
 {
     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
@@ -1976,7 +2001,7 @@ void do_text(Context ctx, int x, int y, char *text, int len,
        widefactor = 1;
     }
 
-    if (lattr != LATTR_NORM) {
+    if ((lattr & LATTR_MODE) != LATTR_NORM) {
        x *= 2;
        if (x >= inst->term->cols)
            return;
@@ -1993,14 +2018,14 @@ void do_text(Context ctx, int x, int y, char *text, int len,
                    len*widefactor*inst->font_width, inst->font_height);
 }
 
-void do_cursor(Context ctx, int x, int y, char *text, int len,
+void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
               unsigned long attr, int lattr)
 {
     struct draw_ctx *dctx = (struct draw_ctx *)ctx;
     struct gui_data *inst = dctx->inst;
     GdkGC *gc = dctx->gc;
 
-    int passive, widefactor;
+    int active, passive, widefactor;
 
     if (attr & TATTR_PASCURS) {
        attr &= ~TATTR_PASCURS;
@@ -2009,7 +2034,9 @@ void do_cursor(Context ctx, int x, int y, char *text, int len,
        passive = 0;
     if ((attr & TATTR_ACTCURS) && inst->cfg.cursor_type != 0) {
        attr &= ~TATTR_ACTCURS;
-    }
+        active = 1;
+    } else
+        active = 0;
     do_text_internal(ctx, x, y, text, len, attr, lattr);
 
     if (attr & ATTR_WIDE) {
@@ -2018,7 +2045,7 @@ void do_cursor(Context ctx, int x, int y, char *text, int len,
        widefactor = 1;
     }
 
-    if (lattr != LATTR_NORM) {
+    if ((lattr & LATTR_MODE) != LATTR_NORM) {
        x *= 2;
        if (x >= inst->term->cols)
            return;
@@ -2046,7 +2073,7 @@ void do_cursor(Context ctx, int x, int y, char *text, int len,
 
        int char_width;
 
-       if ((attr & ATTR_WIDE) || lattr != LATTR_NORM)
+       if ((attr & ATTR_WIDE) || (lattr & LATTR_MODE) != LATTR_NORM)
            char_width = 2*inst->font_width;
        else
            char_width = inst->font_width;
@@ -2081,10 +2108,10 @@ void do_cursor(Context ctx, int x, int y, char *text, int len,
                startx += dx;
                starty += dy;
            }
-       } else {
+       } else if (active) {
            gdk_draw_line(inst->pixmap, gc, startx, starty,
                          startx + (length-1) * dx, starty + (length-1) * dy);
-       }
+       } /* else no cursor (e.g., blinked off) */
     }
 
     gdk_draw_pixmap(inst->area->window, gc, inst->pixmap,
@@ -2728,7 +2755,8 @@ void reset_terminal_menuitem(GtkMenuItem *item, gpointer data)
 {
     struct gui_data *inst = (struct gui_data *)data;
     term_pwron(inst->term);
-    ldisc_send(inst->ldisc, NULL, 0, 0);
+    if (inst->ldisc)
+       ldisc_send(inst->ldisc, NULL, 0, 0);
 }
 
 void copy_all_menuitem(GtkMenuItem *item, gpointer data)
@@ -2743,7 +2771,8 @@ void special_menuitem(GtkMenuItem *item, gpointer data)
     int code = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(item),
                                                   "user-data"));
 
-    inst->back->special(inst->backhandle, code);
+    if (inst->back)
+       inst->back->special(inst->backhandle, code);
 }
 
 void about_menuitem(GtkMenuItem *item, gpointer data)
@@ -2784,11 +2813,13 @@ 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_send(inst->ldisc, NULL, 0, 0);
+        if (inst->ldisc)
+           ldisc_send(inst->ldisc, NULL, 0, 0);
         /* Pass new config data to the terminal */
         term_reconfig(inst->term, &cfg2);
         /* Pass new config data to the back end */
-        inst->back->reconfig(inst->backhandle, &cfg2);
+        if (inst->back)
+           inst->back->reconfig(inst->backhandle, &cfg2);
 
         /*
          * Just setting inst->cfg is sufficient to cause colour
@@ -3079,6 +3110,17 @@ void new_session_menuitem(GtkMenuItem *item, gpointer data)
     fork_and_exec_self(inst, -1, NULL);
 }
 
+void restart_session_menuitem(GtkMenuItem *item, gpointer data)
+{
+    struct gui_data *inst = (struct gui_data *)data;
+
+    if (!inst->back) {
+       logevent(inst, "----- Session restarted -----");
+       start_backend(inst);
+       inst->exited = FALSE;
+    }
+}
+
 void saved_session_menuitem(GtkMenuItem *item, gpointer data)
 {
     struct gui_data *inst = (struct gui_data *)data;
@@ -3100,7 +3142,11 @@ void update_specials_menu(void *frontend)
 
     const struct telnet_special *specials;
 
-    specials = inst->back->get_specials(inst->backhandle);
+    if (inst->back)
+       specials = inst->back->get_specials(inst->backhandle);
+    else
+       specials = NULL;
+
     gtk_container_foreach(GTK_CONTAINER(inst->specialsmenu),
                          (GtkCallback)gtk_widget_destroy, NULL);
     if (specials) {
@@ -3126,9 +3172,51 @@ void update_specials_menu(void *frontend)
     }
 }
 
-int pt_main(int argc, char **argv)
+static void start_backend(struct gui_data *inst)
 {
     extern Backend *select_backend(Config *cfg);
+    char *realhost;
+    const char *error;
+
+    inst->back = select_backend(&inst->cfg);
+
+    error = inst->back->init((void *)inst, &inst->backhandle,
+                            &inst->cfg, inst->cfg.host, inst->cfg.port,
+                            &realhost, inst->cfg.tcp_nodelay,
+                            inst->cfg.tcp_keepalives);
+
+    if (error) {
+       char *msg = dupprintf("Unable to open connection to %s:\n%s",
+                             inst->cfg.host, error);
+       inst->exited = TRUE;
+       fatal_message_box(inst->window, msg);
+       sfree(msg);
+       exit(0);
+    }
+
+    if (inst->cfg.wintitle[0]) {
+       set_title(inst, inst->cfg.wintitle);
+       set_icon(inst, inst->cfg.wintitle);
+    } else {
+       char *title = make_default_wintitle(realhost);
+       set_title(inst, title);
+       set_icon(inst, title);
+       sfree(title);
+    }
+    inst->back->provide_logctx(inst->backhandle, inst->logctx);
+    update_specials_menu(inst);
+
+    term_provide_resize_fn(inst->term, inst->back->size, inst->backhandle);
+
+    inst->ldisc =
+       ldisc_create(&inst->cfg, inst->term, inst->back, inst->backhandle,
+                    inst);
+
+    gtk_widget_hide(inst->restartitem);
+}
+
+int pt_main(int argc, char **argv)
+{
     extern int cfgbox(Config *cfg);
     struct gui_data *inst;
 
@@ -3294,6 +3382,9 @@ int pt_main(int argc, char **argv)
 } while (0)
        if (new_session)
            MKMENUITEM("New Session", new_session_menuitem);
+        MKMENUITEM("Restart Session", restart_session_menuitem);
+       inst->restartitem = menuitem;
+       gtk_widget_hide(inst->restartitem);
         MKMENUITEM("Duplicate Session", dup_session_menuitem);
        if (saved_sessions) {
            struct sesslist sesslist;
@@ -3359,41 +3450,8 @@ int pt_main(int argc, char **argv)
 
     term_size(inst->term, inst->cfg.height, inst->cfg.width, inst->cfg.savelines);
 
-    inst->back = select_backend(&inst->cfg);
-    {
-       char *realhost;
-       const char *error;
-
-       error = inst->back->init((void *)inst, &inst->backhandle,
-                                 &inst->cfg, inst->cfg.host, inst->cfg.port,
-                                 &realhost, inst->cfg.tcp_nodelay);
-
-       if (error) {
-           char *msg = dupprintf("Unable to open connection to %s:\n%s",
-                                  inst->cfg.host, error);
-            inst->exited = TRUE;
-           fatal_message_box(inst->window, msg);
-            sfree(msg);
-           return 0;
-       }
-
-        if (inst->cfg.wintitle[0]) {
-            set_title(inst, inst->cfg.wintitle);
-            set_icon(inst, inst->cfg.wintitle);
-       } else {
-            char *title = make_default_wintitle(realhost);
-            set_title(inst, title);
-            set_icon(inst, title);
-            sfree(title);
-        }
-    }
-    inst->back->provide_logctx(inst->backhandle, inst->logctx);
-    update_specials_menu(inst);
-
-    term_provide_resize_fn(inst->term, inst->back->size, inst->backhandle);
+    start_backend(inst);
 
-    inst->ldisc =
-       ldisc_create(&inst->cfg, inst->term, inst->back, inst->backhandle, inst);
     ldisc_send(inst->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */
 
     /* now we're reday to deal with the child exit handler being