X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=unix%2Fgtkwin.c;h=eca119eb76799476530cd471b8b105f380e49050;hb=095072fa46b2d7b8beafaddb2f873d2f500a1e10;hp=3ed58b08287a83e590f4aa08a1929b715a124fad;hpb=eac66b0281f30be97094fe94174e65477b018812;p=PuTTY.git diff --git a/unix/gtkwin.c b/unix/gtkwin.c index 3ed58b08..eca119eb 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -56,6 +56,7 @@ struct clipboard_data_instance; struct gui_data { GtkWidget *window, *area, *sbar; + gboolean sbar_visible; GtkBox *hbox; GtkAdjustment *sbar_adjust; GtkWidget *menu, *specialsmenu, *specialsitem1, *specialsitem2, @@ -93,11 +94,7 @@ struct gui_data { GtkIMContext *imc; #endif unifont *fonts[4]; /* normal, bold, wide, widebold */ -#if GTK_CHECK_VERSION(2,0,0) - const char *geometry; -#else int xpos, ypos, gotpos, gravity; -#endif GdkCursor *rawcursor, *textcursor, *blankcursor, *waitcursor, *currcursor; GdkColor cols[NALLCOLOURS]; #if !GTK_CHECK_VERSION(3,0,0) @@ -340,6 +337,8 @@ void move_window(void *frontend, int x, int y) * though. */ #if GTK_CHECK_VERSION(2,0,0) + /* in case we reset this at startup due to a geometry string */ + gtk_window_set_gravity(GTK_WINDOW(inst->window), GDK_GRAVITY_NORTH_EAST); gtk_window_move(GTK_WINDOW(inst->window), x, y); #else gdk_window_move(gtk_widget_get_window(inst->window), x, y); @@ -639,6 +638,8 @@ char *dup_keyval_name(guint keyval) } #endif +static void change_font_size(struct gui_data *inst, int increment); + gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) { struct gui_data *inst = (struct gui_data *)data; @@ -839,6 +840,23 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) } } + if (event->keyval == GDK_KEY_greater && + (event->state & GDK_CONTROL_MASK)) { +#ifdef KEY_EVENT_DIAGNOSTICS + debug((" - Ctrl->: increase font size\n")); +#endif + change_font_size(inst, +1); + return TRUE; + } + if (event->keyval == GDK_KEY_less && + (event->state & GDK_CONTROL_MASK)) { +#ifdef KEY_EVENT_DIAGNOSTICS + debug((" - Ctrl-<: increase font size\n")); +#endif + change_font_size(inst, -1); + return TRUE; + } + /* * Shift-PgUp and Shift-PgDn don't even generate keystrokes * at all. @@ -2013,6 +2031,12 @@ void set_raw_mouse_mode(void *frontend, int activate) update_mouseptr(inst); } +#if GTK_CHECK_VERSION(2,0,0) +static void compute_whole_window_size(struct gui_data *inst, + int wchars, int hchars, + int *wpix, int *hpix); +#endif + void request_resize(void *frontend, int w, int h) { struct gui_data *inst = (struct gui_data *)frontend; @@ -2094,14 +2118,9 @@ void request_resize(void *frontend, int w, int h) #else /* GTK_CHECK_VERSION(3,0,0) */ - /* - * In GTK3, we can do this by using gtk_window_resize_to_geometry, - * which uses the fact that we've already set up the main window's - * WM hints to reflect the terminal drawing area's resize - * increment (i.e. character cell) and the fixed amount of stuff - * round the edges. - */ - gtk_window_resize_to_geometry(GTK_WINDOW(inst->window), w, h); + int wp, hp; + compute_whole_window_size(inst, w, h, &wp, &hp); + gtk_window_resize(GTK_WINDOW(inst->window), wp, hp); #endif @@ -2832,7 +2851,9 @@ void set_sbar(void *frontend, int total, int start, int page) gtk_adjustment_set_step_increment(inst->sbar_adjust, 1); gtk_adjustment_set_page_increment(inst->sbar_adjust, page/2); inst->ignore_sbar = TRUE; +#if !GTK_CHECK_VERSION(3,18,0) gtk_adjustment_changed(inst->sbar_adjust); +#endif inst->ignore_sbar = FALSE; } @@ -2846,6 +2867,15 @@ void scrollbar_moved(GtkAdjustment *adj, gpointer data) term_scroll(inst->term, 1, (int)gtk_adjustment_get_value(adj)); } +static void show_scrollbar(struct gui_data *inst, gboolean visible) +{ + inst->sbar_visible = visible; + if (visible) + gtk_widget_show(inst->sbar); + else + gtk_widget_hide(inst->sbar); +} + void sys_cursor(void *frontend, int x, int y) { /* @@ -3596,22 +3626,144 @@ char *setup_fonts_ucs(struct gui_data *inst) return NULL; } +#if GTK_CHECK_VERSION(3,0,0) +struct find_app_menu_bar_ctx { + GtkWidget *area, *menubar; +}; +static void find_app_menu_bar(GtkWidget *widget, gpointer data) +{ + struct find_app_menu_bar_ctx *ctx = (struct find_app_menu_bar_ctx *)data; + if (widget != ctx->area && GTK_IS_MENU_BAR(widget)) + ctx->menubar = widget; +} +#endif + +static void compute_geom_hints(struct gui_data *inst, GdkGeometry *geom) +{ + /* + * Unused fields in geom. + */ + geom->max_width = geom->max_height = -1; + geom->min_aspect = geom->max_aspect = 0; + + /* + * Set up the geometry fields we care about, with reference to + * just the drawing area. We'll correct for other widgets in a + * moment. + */ + geom->min_width = inst->font_width + 2*inst->window_border; + geom->min_height = inst->font_height + 2*inst->window_border; + geom->base_width = 2*inst->window_border; + geom->base_height = 2*inst->window_border; + geom->width_inc = inst->font_width; + geom->height_inc = inst->font_height; + + /* + * If we've got a scrollbar visible, then we must include its + * width as part of the base and min width, and also ensure that + * our window's minimum height is at least the height required by + * the scrollbar. + * + * In the latter case, we must also take care to arrange that + * (geom->min_height - geom->base_height) is an integer multiple of + * geom->height_inc, because if it's not, then some window managers + * (we know of xfwm4) get confused, with the effect that they + * resize our window to a height based on min_height instead of + * base_height, which we then round down and the window ends up + * too short. + */ + if (inst->sbar_visible) { + GtkRequisition req; + int min_sb_height; + +#if GTK_CHECK_VERSION(3,0,0) + gtk_widget_get_preferred_size(inst->sbar, &req, NULL); +#else + gtk_widget_size_request(inst->sbar, &req); +#endif + + /* Compute rounded-up scrollbar height. */ + min_sb_height = req.height; + min_sb_height += geom->height_inc - 1; + min_sb_height -= ((min_sb_height - geom->base_height%geom->height_inc) + % geom->height_inc); + + geom->min_width += req.width; + geom->base_width += req.width; + if (geom->min_height < min_sb_height) + geom->min_height = min_sb_height; + } + +#if GTK_CHECK_VERSION(3,0,0) + /* + * And if we're running a gtkapp.c based program and + * GtkApplicationWindow has given us a menu bar inside the window, + * then we must take that into account as well. + * + * In its unbounded wisdom, GtkApplicationWindow doesn't actually + * give us a direct function call to _find_ the menu bar widget. + * Fortunately, we can find it by enumerating the children of the + * top-level window and looking for one we didn't put there + * ourselves. + */ + { + struct find_app_menu_bar_ctx actx, *ctx = &actx; + ctx->area = inst->area; + ctx->menubar = NULL; + gtk_container_foreach(GTK_CONTAINER(inst->window), + find_app_menu_bar, ctx); + + if (ctx->menubar) { + GtkRequisition req; + int min_menu_width; + gtk_widget_get_preferred_size(ctx->menubar, NULL, &req); + + /* + * This time, the height adjustment is easy (the menu bar + * sits above everything), but we have to take care with + * the _width_ to ensure we keep min_width and base_width + * congruent modulo width_inc. + */ + geom->min_height += req.height; + geom->base_height += req.height; + + min_menu_width = req.width; + min_menu_width += geom->width_inc - 1; + min_menu_width -= + ((min_menu_width - geom->base_width%geom->width_inc) + % geom->width_inc); + if (geom->min_width < min_menu_width) + geom->min_width = min_menu_width; + } + } +#endif +} + void set_geom_hints(struct gui_data *inst) { GdkGeometry geom; - geom.min_width = inst->font_width + 2*inst->window_border; - geom.min_height = inst->font_height + 2*inst->window_border; - geom.max_width = geom.max_height = -1; - geom.base_width = 2*inst->window_border; - geom.base_height = 2*inst->window_border; - geom.width_inc = inst->font_width; - geom.height_inc = inst->font_height; - geom.min_aspect = geom.max_aspect = 0; - gtk_window_set_geometry_hints(GTK_WINDOW(inst->window), inst->area, &geom, - GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE | - GDK_HINT_RESIZE_INC); + gint flags = GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE | GDK_HINT_RESIZE_INC; + compute_geom_hints(inst, &geom); +#if GTK_CHECK_VERSION(2,0,0) + if (inst->gotpos) + flags |= GDK_HINT_USER_POS; +#endif + gtk_window_set_geometry_hints(GTK_WINDOW(inst->window), + NULL, &geom, flags); } +#if GTK_CHECK_VERSION(2,0,0) +static void compute_whole_window_size(struct gui_data *inst, + int wchars, int hchars, + int *wpix, int *hpix) +{ + GdkGeometry geom; + compute_geom_hints(inst, &geom); + if (wpix) *wpix = geom.base_width + wchars * geom.width_inc; + if (hpix) *hpix = geom.base_height + hchars * geom.height_inc; +} +#endif + void clear_scrollback_menuitem(GtkMenuItem *item, gpointer data) { struct gui_data *inst = (struct gui_data *)data; @@ -3732,16 +3884,16 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data) } } + need_size = FALSE; + /* * If the scrollbar needs to be shown, hidden, or moved * from one end to the other of the window, do so now. */ if (conf_get_int(oldconf, CONF_scrollbar) != conf_get_int(newconf, CONF_scrollbar)) { - if (conf_get_int(newconf, CONF_scrollbar)) - gtk_widget_show(inst->sbar); - else - gtk_widget_hide(inst->sbar); + show_scrollbar(inst, conf_get_int(newconf, CONF_scrollbar)); + need_size = TRUE; } if (conf_get_int(oldconf, CONF_scrollbar_on_left) != conf_get_int(newconf, CONF_scrollbar_on_left)) { @@ -3762,7 +3914,6 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data) * Redo the whole tangled fonts and Unicode mess if * necessary. */ - need_size = FALSE; if (strcmp(conf_get_fontspec(oldconf, CONF_font)->name, conf_get_fontspec(newconf, CONF_font)->name) || strcmp(conf_get_fontspec(oldconf, CONF_boldfont)->name, @@ -3840,6 +3991,67 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data) inst->reconfiguring = FALSE; } +static void change_font_size(struct gui_data *inst, int increment) +{ + static const int conf_keys[lenof(inst->fonts)] = { + CONF_font, CONF_boldfont, CONF_widefont, CONF_wideboldfont, + }; + FontSpec *oldfonts[lenof(inst->fonts)]; + FontSpec *newfonts[lenof(inst->fonts)]; + char *errmsg = NULL; + int i; + + for (i = 0; i < lenof(newfonts); i++) + oldfonts[i] = newfonts[i] = NULL; + + for (i = 0; i < lenof(inst->fonts); i++) { + if (inst->fonts[i]) { + char *newname = unifont_size_increment(inst->fonts[i], increment); + if (!newname) + goto cleanup; + newfonts[i] = fontspec_new(newname); + sfree(newname); + } + } + + for (i = 0; i < lenof(newfonts); i++) { + if (newfonts[i]) { + oldfonts[i] = fontspec_copy( + conf_get_fontspec(inst->conf, conf_keys[i])); + conf_set_fontspec(inst->conf, conf_keys[i], newfonts[i]); + } + } + + errmsg = setup_fonts_ucs(inst); + if (errmsg) + goto cleanup; + + /* Success, so suppress putting everything back */ + for (i = 0; i < lenof(newfonts); i++) { + if (oldfonts[i]) { + fontspec_free(oldfonts[i]); + oldfonts[i] = NULL; + } + } + + set_geom_hints(inst); + request_resize(inst, conf_get_int(inst->conf, CONF_width), + conf_get_int(inst->conf, CONF_height)); + term_invalidate(inst->term); + gtk_widget_queue_draw(inst->area); + + cleanup: + for (i = 0; i < lenof(oldfonts); i++) { + if (oldfonts[i]) { + conf_set_fontspec(inst->conf, conf_keys[i], oldfonts[i]); + fontspec_free(oldfonts[i]); + } + if (newfonts[i]) + fontspec_free(newfonts[i]); + } + sfree(errmsg); +} + void dup_session_menuitem(GtkMenuItem *item, gpointer gdata) { struct gui_data *inst = (struct gui_data *)gdata; @@ -4079,11 +4291,8 @@ struct gui_data *new_session_window(Conf *conf, const char *geometry_string) inst->cumulative_scroll = 0.0; #endif +#ifndef NOT_X_WINDOWS if (geometry_string) { -#if GTK_CHECK_VERSION(2,0,0) - inst->geometry = geometry_string; -#else - /* On GTK 1, we have to do this using raw Xlib */ int flags, x, y; unsigned int w, h; flags = XParseGeometry(geometry_string, &x, &y, &w, &h); @@ -4099,8 +4308,8 @@ struct gui_data *new_session_window(Conf *conf, const char *geometry_string) inst->gravity = ((flags & XNegative ? 1 : 0) | (flags & YNegative ? 2 : 0)); } -#endif } +#endif if (!compound_text_atom) compound_text_atom = gdk_atom_intern("COMPOUND_TEXT", FALSE); @@ -4120,7 +4329,7 @@ struct gui_data *new_session_window(Conf *conf, const char *geometry_string) exit(1); } } - inst->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + inst->window = make_gtk_toplevel_window(inst); { const char *winclass = conf_get_str(inst->conf, CONF_winclass); if (*winclass) @@ -4139,23 +4348,6 @@ struct gui_data *new_session_window(Conf *conf, const char *geometry_string) init_clipboard(inst); - set_geom_hints(inst); - -#if GTK_CHECK_VERSION(3,0,0) - gtk_window_set_default_geometry(GTK_WINDOW(inst->window), - inst->width, inst->height); -#else - { - int w = inst->font_width * inst->width + 2*inst->window_border; - int h = inst->font_height * inst->height + 2*inst->window_border; -#if GTK_CHECK_VERSION(2,0,0) - gtk_widget_set_size_request(inst->area, w, h); -#else - gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area), w, h); -#endif - } -#endif - inst->sbar_adjust = GTK_ADJUSTMENT(gtk_adjustment_new(0,0,0,0,0,0)); inst->sbar = gtk_vscrollbar_new(inst->sbar_adjust); inst->hbox = GTK_BOX(gtk_hbox_new(FALSE, 0)); @@ -4173,15 +4365,52 @@ struct gui_data *new_session_window(Conf *conf, const char *geometry_string) gtk_container_add(GTK_CONTAINER(inst->window), GTK_WIDGET(inst->hbox)); gtk_widget_show(inst->area); - if (conf_get_int(inst->conf, CONF_scrollbar)) - gtk_widget_show(inst->sbar); - else - gtk_widget_hide(inst->sbar); + show_scrollbar(inst, conf_get_int(inst->conf, CONF_scrollbar)); gtk_widget_show(GTK_WIDGET(inst->hbox)); + /* + * We must call gtk_widget_realize before setting up the geometry + * hints, so that GtkApplicationWindow will have actually created + * its menu bar (if it's going to) and hence compute_geom_hints + * can find it to take its size into account. + */ + gtk_widget_realize(inst->window); + set_geom_hints(inst); + +#if GTK_CHECK_VERSION(3,0,0) + { + int wp, hp; + compute_whole_window_size(inst, inst->width, inst->height, &wp, &hp); + gtk_window_set_default_size(GTK_WINDOW(inst->window), wp, hp); + } +#else + { + int w = inst->font_width * inst->width + 2*inst->window_border; + int h = inst->font_height * inst->height + 2*inst->window_border; #if GTK_CHECK_VERSION(2,0,0) - if (inst->geometry) { - gtk_window_parse_geometry(GTK_WINDOW(inst->window), inst->geometry); + gtk_widget_set_size_request(inst->area, w, h); +#else + gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area), w, h); +#endif + } +#endif + +#if GTK_CHECK_VERSION(2,0,0) + if (inst->gotpos) { + static const GdkGravity gravities[] = { + GDK_GRAVITY_NORTH_WEST, + GDK_GRAVITY_NORTH_EAST, + GDK_GRAVITY_SOUTH_WEST, + GDK_GRAVITY_SOUTH_EAST, + }; + int x = inst->xpos, y = inst->ypos; + int wp, hp; + compute_whole_window_size(inst, inst->width, inst->height, &wp, &hp); + if (inst->gravity & 1) x += (gdk_screen_width() - wp); + if (inst->gravity & 2) y += (gdk_screen_height() - hp); + gtk_window_set_gravity(GTK_WINDOW(inst->window), + gravities[inst->gravity & 3]); + gtk_window_move(GTK_WINDOW(inst->window), x, y); } #else if (inst->gotpos) {