X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/c6958dfe2ab270f7d02d02b21d4a4008478a5ea9..fdc94dd1746632e036367456be5a77b11512ac1b:/unix/pterm.c diff --git a/unix/pterm.c b/unix/pterm.c index 7efce806..54ab31d8 100644 --- a/unix/pterm.c +++ b/unix/pterm.c @@ -33,13 +33,23 @@ #define CAT(x,y) CAT2(x,y) #define ASSERT(x) enum {CAT(assertion_,__LINE__) = 1 / (x)} -#define NCOLOURS (lenof(((Config *)0)->colours)) +/* Colours come in two flavours: configurable, and xterm-extended. */ +#define NCFGCOLOURS (lenof(((Config *)0)->colours)) +#define NEXTCOLOURS 240 /* 216 colour-cube plus 24 shades of grey */ +#define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS) 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; @@ -55,7 +65,7 @@ struct gui_data { } fontinfo[4]; int xpos, ypos, gotpos, gravity; GdkCursor *rawcursor, *textcursor, *blankcursor, *currcursor; - GdkColor cols[NCOLOURS]; + GdkColor cols[NALLCOLOURS]; GdkColormap *colmap; wchar_t *pastein_data; int direct_to_font; @@ -376,7 +386,7 @@ static void show_mouseptr(struct gui_data *inst, int 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_gc_set_foreground(gc, &inst->cols[258]); /* 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); @@ -1022,7 +1032,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 +1139,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 +1162,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 +1221,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; @@ -1293,23 +1331,20 @@ static void real_palette_set(struct gui_data *inst, int n, int r, int g, int b) void set_window_background(struct gui_data *inst) { if (inst->area && inst->area->window) - gdk_window_set_background(inst->area->window, &inst->cols[18]); + gdk_window_set_background(inst->area->window, &inst->cols[258]); if (inst->window && inst->window->window) - gdk_window_set_background(inst->window->window, &inst->cols[18]); + gdk_window_set_background(inst->window->window, &inst->cols[258]); } void palette_set(void *frontend, int n, int r, int g, int b) { struct gui_data *inst = (struct gui_data *)frontend; - static const int first[21] = { - 0, 2, 4, 6, 8, 10, 12, 14, - 1, 3, 5, 7, 9, 11, 13, 15, - 16, 17, 18, 20, 22 - }; - real_palette_set(inst, first[n], r, g, b); - if (first[n] >= 18) - real_palette_set(inst, first[n] + 1, r, g, b); - if (first[n] == 18) + if (n >= 16) + n += 256 - 16; + if (n > NALLCOLOURS) + return; + real_palette_set(inst, n, r, g, b); + if (n == 258) set_window_background(inst); } @@ -1318,30 +1353,44 @@ void palette_reset(void *frontend) struct gui_data *inst = (struct gui_data *)frontend; /* This maps colour indices in inst->cfg to those used in inst->cols. */ static const int ww[] = { - 6, 7, 8, 9, 10, 11, 12, 13, - 14, 15, 16, 17, 18, 19, 20, 21, - 0, 1, 2, 3, 4, 5 + 256, 257, 258, 259, 260, 261, + 0, 8, 1, 9, 2, 10, 3, 11, + 4, 12, 5, 13, 6, 14, 7, 15 }; - gboolean success[NCOLOURS]; + gboolean success[NALLCOLOURS]; int i; - assert(lenof(ww) == NCOLOURS); + assert(lenof(ww) == NCFGCOLOURS); if (!inst->colmap) { inst->colmap = gdk_colormap_get_system(); } else { - gdk_colormap_free_colors(inst->colmap, inst->cols, NCOLOURS); + gdk_colormap_free_colors(inst->colmap, inst->cols, NALLCOLOURS); + } + + for (i = 0; i < NCFGCOLOURS; i++) { + inst->cols[ww[i]].red = inst->cfg.colours[i][0] * 0x0101; + inst->cols[ww[i]].green = inst->cfg.colours[i][1] * 0x0101; + inst->cols[ww[i]].blue = inst->cfg.colours[i][2] * 0x0101; } - for (i = 0; i < NCOLOURS; i++) { - inst->cols[i].red = inst->cfg.colours[ww[i]][0] * 0x0101; - inst->cols[i].green = inst->cfg.colours[ww[i]][1] * 0x0101; - inst->cols[i].blue = inst->cfg.colours[ww[i]][2] * 0x0101; + for (i = 0; i < NEXTCOLOURS; i++) { + if (i < 216) { + int r = i / 36, g = (i / 6) % 6, b = i % 6; + inst->cols[i+16].red = r * 0x3333; + inst->cols[i+16].green = g * 0x3333; + inst->cols[i+16].blue = b * 0x3333; + } else { + int shade = i - 216; + shade = (shade + 1) * 0xFFFF / (NEXTCOLOURS - 216 + 1); + inst->cols[i+16].red = inst->cols[i+16].green = + inst->cols[i+16].blue = shade; + } } - gdk_colormap_alloc_colors(inst->colmap, inst->cols, NCOLOURS, + gdk_colormap_alloc_colors(inst->colmap, inst->cols, NALLCOLOURS, FALSE, FALSE, success); - for (i = 0; i < NCOLOURS; i++) { + for (i = 0; i < NALLCOLOURS; i++) { if (!success[i]) g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n", appname, i, inst->cfg.colours[i][0], @@ -1814,21 +1863,23 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, ncombining = 1; nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT); - nfg = 2 * (nfg & 0xF) + (nfg & 0x10 ? 1 : 0); nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT); - nbg = 2 * (nbg & 0xF) + (nbg & 0x10 ? 1 : 0); if (attr & ATTR_REVERSE) { t = nfg; nfg = nbg; nbg = t; } - if (inst->cfg.bold_colour && (attr & ATTR_BOLD)) - nfg |= 1; - if (inst->cfg.bold_colour && (attr & ATTR_BLINK)) - nbg |= 1; + if (inst->cfg.bold_colour && (attr & ATTR_BOLD)) { + if (nfg < 16) nfg |= 8; + else if (nfg >= 256) nfg |= 1; + } + if (inst->cfg.bold_colour && (attr & ATTR_BLINK)) { + if (nbg < 16) nbg |= 8; + else if (nbg >= 256) nbg |= 1; + } if (attr & TATTR_ACTCURS) { - nfg = NCOLOURS-2; - nbg = NCOLOURS-1; + nfg = 260; + nbg = 261; } fontid = shadow = 0; @@ -1973,7 +2024,7 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, y*inst->font_height+inst->cfg.window_border, x*inst->font_width+inst->cfg.window_border + 2*i+1, y*inst->font_height+inst->cfg.window_border, - len * inst->font_width - i, inst->font_height); + len * widefactor * inst->font_width - i, inst->font_height); } len *= 2; if ((lattr & LATTR_MODE) != LATTR_WIDE) { @@ -1987,9 +2038,9 @@ void do_text_internal(Context ctx, int x, int y, wchar_t *text, int len, gdk_draw_pixmap(inst->pixmap, gc, inst->pixmap, x*inst->font_width+inst->cfg.window_border, y*inst->font_height+inst->cfg.window_border+dt*i+db, - x*widefactor*inst->font_width+inst->cfg.window_border, + x*inst->font_width+inst->cfg.window_border, y*inst->font_height+inst->cfg.window_border+dt*(i+1), - len * inst->font_width, inst->font_height-i-1); + len * widefactor * inst->font_width, inst->font_height-i-1); } } } @@ -2049,6 +2100,9 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, active = 0; do_text_internal(ctx, x, y, text, len, attr, lattr); + if (attr & TATTR_COMBINING) + len = 1; + if (attr & ATTR_WIDE) { widefactor = 2; } else { @@ -2071,11 +2125,11 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, * if it's passive. */ if (passive) { - gdk_gc_set_foreground(gc, &inst->cols[NCOLOURS-1]); + gdk_gc_set_foreground(gc, &inst->cols[261]); 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; @@ -2097,7 +2151,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) @@ -2109,7 +2163,7 @@ void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, length = inst->font_height; } - gdk_gc_set_foreground(gc, &inst->cols[NCOLOURS-1]); + gdk_gc_set_foreground(gc, &inst->cols[261]); if (passive) { for (i = 0; i < length; i++) { if (i % 2 == 0) { @@ -2497,17 +2551,6 @@ int do_cmdline(int argc, char **argv, int do_everything, return err; } -static void block_signal(int sig, int block_it) { - sigset_t ss; - - sigemptyset(&ss); - sigaddset(&ss, sig); - if(sigprocmask(block_it ? SIG_BLOCK : SIG_UNBLOCK, &ss, 0) < 0) { - perror("sigprocmask"); - exit(1); - } -} - /* * This function retrieves the character set encoding of a font. It * returns the character set without the X11 hack (in case the user @@ -2734,8 +2777,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); } @@ -2801,15 +2844,17 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data) { /* This maps colour indices in inst->cfg to those used in inst->cols. */ static const int ww[] = { - 6, 7, 8, 9, 10, 11, 12, 13, - 14, 15, 16, 17, 18, 19, 20, 21, - 0, 1, 2, 3, 4, 5 + 256, 257, 258, 259, 260, 261, + 0, 8, 1, 9, 2, 10, 3, 11, + 4, 12, 5, 13, 6, 14, 7, 15 }; struct gui_data *inst = (struct gui_data *)data; char *title = dupcat(appname, " Reconfiguration", NULL); Config cfg2, oldcfg; int i, need_size; + assert(lenof(ww) == NCFGCOLOURS); + cfg2 = inst->cfg; /* structure copy */ if (do_config_box(title, &cfg2, 1)) { @@ -2839,20 +2884,20 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data) * have to the new default, on the assumption that the user * is most likely to want an immediate update. */ - 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]) { - real_palette_set(inst, i, cfg2.colours[ww[i]][0], - cfg2.colours[ww[i]][1], - cfg2.colours[ww[i]][2]); + for (i = 0; i < NCFGCOLOURS; i++) { + if (oldcfg.colours[i][0] != cfg2.colours[i][0] || + oldcfg.colours[i][1] != cfg2.colours[i][1] || + oldcfg.colours[i][2] != cfg2.colours[i][2]) { + real_palette_set(inst, ww[i], cfg2.colours[i][0], + cfg2.colours[i][1], + cfg2.colours[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) { + if (i == 258) { set_window_background(inst); draw_backing_rect(inst); } @@ -3157,22 +3202,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); @@ -3214,7 +3288,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); @@ -3361,7 +3434,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 | @@ -3433,6 +3505,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);