Slight improvement to cursor blink timing: since the cursor doesn't
[u/mdw/putty] / unix / pterm.c
index cc47d1d..59a99c1 100644 (file)
@@ -40,6 +40,13 @@ GdkAtom compound_text_atom, utf8_string_atom;
 extern char **pty_argv;               /* declared in pty.c */
 extern int use_pty_argv;
 
+/*
+ * Timers are global across all sessions (even if we were handling
+ * multiple sessions, which we aren't), so the current timer ID is
+ * a global variable.
+ */
+static guint timer_id = 0;
+
 struct gui_data {
     GtkWidget *window, *area, *sbar;
     GtkBox *hbox;
@@ -1022,7 +1029,6 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
 
        show_mouseptr(inst, 0);
        term_seen_key_event(inst->term);
-       term_out(inst->term);
     }
 
     return TRUE;
@@ -1130,9 +1136,9 @@ void frontend_keypress(void *handle)
        exit(0);
 }
 
-gint timer_func(gpointer data)
+void notify_remote_exit(void *frontend)
 {
-    struct gui_data *inst = (struct gui_data *)data;
+    struct gui_data *inst = (struct gui_data *)frontend;
     int exitcode;
 
     if (!inst->exited &&
@@ -1153,10 +1159,40 @@ gint timer_func(gpointer data)
        }
        gtk_widget_show(inst->restartitem);
     }
+}
 
-    term_update(inst->term);
-    term_blink(inst->term, 0);
-    return TRUE;
+static gint timer_trigger(gpointer data)
+{
+    long now = GPOINTER_TO_INT(data);
+    long next;
+    long ticks;
+
+    if (run_timers(now, &next)) {
+       ticks = next - GETTICKCOUNT();
+       timer_id = gtk_timeout_add(ticks > 0 ? ticks : 1, timer_trigger,
+                                  GINT_TO_POINTER(next));
+    }
+
+    /*
+     * Never let a timer resume. If we need another one, we've
+     * asked for it explicitly above.
+     */
+    return FALSE;
+}
+
+void timer_change_notify(long next)
+{
+    long ticks;
+
+    if (timer_id)
+       gtk_timeout_remove(timer_id);
+
+    ticks = next - GETTICKCOUNT();
+    if (ticks <= 0)
+       ticks = 1;                     /* just in case */
+
+    timer_id = gtk_timeout_add(ticks, timer_trigger,
+                              GINT_TO_POINTER(next));
 }
 
 void fd_input_func(gpointer data, gint sourcefd, GdkInputCondition condition)
@@ -1182,8 +1218,7 @@ void destroy(GtkWidget *widget, gpointer data)
 gint focus_event(GtkWidget *widget, GdkEventFocus *event, gpointer data)
 {
     struct gui_data *inst = (struct gui_data *)data;
-    inst->term->has_focus = event->in;
-    term_out(inst->term);
+    term_set_focus(inst->term, event->in);
     term_update(inst->term);
     show_mouseptr(inst, 1);
     return FALSE;
@@ -2078,7 +2113,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
            gdk_draw_rectangle(inst->pixmap, gc, 0,
                               x*inst->font_width+inst->cfg.window_border,
                               y*inst->font_height+inst->cfg.window_border,
-                              len*inst->font_width-1, inst->font_height-1);
+                              len*widefactor*inst->font_width-1, inst->font_height-1);
        }
     } else {
        int uheight;
@@ -2100,7 +2135,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len,
            starty = y * inst->font_height + inst->cfg.window_border + uheight;
            dx = 1;
            dy = 0;
-           length = len * char_width;
+           length = len * widefactor * char_width;
        } else {
            int xadjust = 0;
            if (attr & TATTR_RIGHTCURS)
@@ -2737,8 +2772,8 @@ void setup_fonts_ucs(struct gui_data *inst)
     inst->font_width = gdk_char_width(inst->fonts[0], ' ');
     inst->font_height = inst->fonts[0]->ascent + inst->fonts[0]->descent;
 
-    inst->direct_to_font = init_ucs(&inst->ucsdata,
-                                   inst->cfg.line_codepage, font_charset,
+    inst->direct_to_font = init_ucs(&inst->ucsdata, inst->cfg.line_codepage,
+                                   inst->cfg.utf8_override, font_charset,
                                    inst->cfg.vtmode);
 }
 
@@ -3160,22 +3195,51 @@ void update_specials_menu(void *frontend)
     else
        specials = NULL;
 
+    /* I believe this disposes of submenus too. */
     gtk_container_foreach(GTK_CONTAINER(inst->specialsmenu),
                          (GtkCallback)gtk_widget_destroy, NULL);
     if (specials) {
        int i;
-       GtkWidget *menuitem;
-       for (i = 0; specials[i].name; i++) {
-           if (*specials[i].name) {
+       GtkWidget *menu = inst->specialsmenu;
+       /* A lame "stack" for submenus that will do for now. */
+       GtkWidget *saved_menu = NULL;
+       int nesting = 1;
+       for (i = 0; nesting > 0; i++) {
+           GtkWidget *menuitem = NULL;
+           switch (specials[i].code) {
+             case TS_SUBMENU:
+               assert (nesting < 2);
+               saved_menu = menu; /* XXX lame stacking */
+               menu = gtk_menu_new();
+               menuitem = gtk_menu_item_new_with_label(specials[i].name);
+               gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
+               gtk_container_add(GTK_CONTAINER(saved_menu), menuitem);
+               gtk_widget_show(menuitem);
+               menuitem = NULL;
+               nesting++;
+               break;
+             case TS_EXITMENU:
+               nesting--;
+               if (nesting) {
+                   menu = saved_menu; /* XXX lame stacking */
+                   saved_menu = NULL;
+               }
+               break;
+             case TS_SEP:
+               menuitem = gtk_menu_item_new();
+               break;
+             default:
                menuitem = gtk_menu_item_new_with_label(specials[i].name);
                gtk_object_set_data(GTK_OBJECT(menuitem), "user-data",
                                    GINT_TO_POINTER(specials[i].code));
                gtk_signal_connect(GTK_OBJECT(menuitem), "activate",
                                   GTK_SIGNAL_FUNC(special_menuitem), inst);
-           } else
-               menuitem = gtk_menu_item_new();
-           gtk_container_add(GTK_CONTAINER(inst->specialsmenu), menuitem);
-           gtk_widget_show(menuitem);
+               break;
+           }
+           if (menuitem) {
+               gtk_container_add(GTK_CONTAINER(menu), menuitem);
+               gtk_widget_show(menuitem);
+           }
        }
        gtk_widget_show(inst->specialsitem1);
        gtk_widget_show(inst->specialsitem2);
@@ -3217,7 +3281,6 @@ static void start_backend(struct gui_data *inst)
        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);
 
@@ -3364,7 +3427,6 @@ int pt_main(int argc, char **argv)
     if (inst->cfg.scrollbar)
        gtk_signal_connect(GTK_OBJECT(inst->sbar_adjust), "value_changed",
                           GTK_SIGNAL_FUNC(scrollbar_moved), inst);
-    gtk_timeout_add(20, timer_func, inst);
     gtk_widget_add_events(GTK_WIDGET(inst->area),
                          GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK |
                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
@@ -3436,6 +3498,8 @@ int pt_main(int argc, char **argv)
        inst->specialsitem1 = menuitem;
        MKMENUITEM(NULL, NULL);
        inst->specialsitem2 = menuitem;
+       gtk_widget_hide(inst->specialsitem1);
+       gtk_widget_hide(inst->specialsitem2);
        MKMENUITEM("Clear Scrollback", clear_scrollback_menuitem);
        MKMENUITEM("Reset Terminal", reset_terminal_menuitem);
        MKMENUITEM("Copy All", copy_all_menuitem);