From 5319c659ad23fde34606f49847e70867c5826ea9 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 16 Aug 2015 09:02:31 +0100 Subject: [PATCH] Make the use of server-side backing pixmaps in GTK optional. We won't be able to use them in GTK3, or when compiling with GTK2 and -DGDK_DISABLE_DEPRECATED. This applies to the one we use for the main terminal window, and also the small one we use for the preview pane in the unified font selector. --- unix/gtkfont.c | 27 ++++++++++++++++++++++---- unix/gtkfont.h | 17 +++++++++++++++++ unix/gtkwin.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 89 insertions(+), 6 deletions(-) diff --git a/unix/gtkfont.c b/unix/gtkfont.c index 55878307..cd0097b1 100644 --- a/unix/gtkfont.c +++ b/unix/gtkfont.c @@ -1970,7 +1970,9 @@ typedef struct unifontsel_internal { GtkWidget *filter_buttons[4]; int n_filter_buttons; GtkWidget *preview_area; +#ifndef NO_BACKING_PIXMAPS GdkPixmap *preview_pixmap; +#endif int preview_width, preview_height; GdkColor preview_fg, preview_bg; int filter_flags; @@ -2379,21 +2381,27 @@ static void unifontsel_draw_preview_text_inner(unifont_drawctx *dctx, static void unifontsel_draw_preview_text(unifontsel_internal *fs) { unifont_drawctx dctx; + GdkWindow *target; - if (!fs->preview_pixmap) +#ifndef NO_BACKING_PIXMAPS + target = fs->preview_pixmap; +#else + target = gtk_widget_get_window(fs->preview_area); +#endif + if (!target) /* we may be called when we haven't created everything yet */ return; dctx.type = DRAWTYPE_DEFAULT; #ifdef DRAW_TEXT_GDK if (dctx.type == DRAWTYPE_GDK) { - dctx.u.gdk.target = fs->preview_pixmap; - dctx.u.gdk.gc = gdk_gc_new(fs->preview_pixmap); + dctx.u.gdk.target = target; + dctx.u.gdk.gc = gdk_gc_new(target); } #endif #ifdef DRAW_TEXT_CAIRO if (dctx.type == DRAWTYPE_CAIRO) { dctx.u.cairo.widget = GTK_WIDGET(fs->preview_area); - dctx.u.cairo.cr = gdk_cairo_create(fs->preview_pixmap); + dctx.u.cairo.cr = gdk_cairo_create(target); } #endif @@ -2839,6 +2847,7 @@ static gint unifontsel_expose_area(GtkWidget *widget, GdkEventExpose *event, { unifontsel_internal *fs = (unifontsel_internal *)data; +#ifndef NO_BACKING_PIXMAPS if (fs->preview_pixmap) { gdk_draw_pixmap(gtk_widget_get_window(widget), (gtk_widget_get_style(widget)->fg_gc @@ -2848,12 +2857,17 @@ static gint unifontsel_expose_area(GtkWidget *widget, GdkEventExpose *event, event->area.x, event->area.y, event->area.width, event->area.height); } +#else + unifontsel_draw_preview_text(fs); +#endif + return TRUE; } static gint unifontsel_configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data) { +#ifndef NO_BACKING_PIXMAPS unifontsel_internal *fs = (unifontsel_internal *)data; int ox, oy, nx, ny, x, y; @@ -2877,6 +2891,7 @@ static gint unifontsel_configure_area(GtkWidget *widget, unifontsel_draw_preview_text(fs); } +#endif gdk_window_invalidate_rect(gtk_widget_get_window(widget), NULL, FALSE); @@ -3071,7 +3086,9 @@ unifontsel *unifontsel_new(const char *wintitle) * Preview widget. */ fs->preview_area = gtk_drawing_area_new(); +#ifndef NO_BACKING_PIXMAPS fs->preview_pixmap = NULL; +#endif fs->preview_width = 0; fs->preview_height = 0; fs->preview_fg.pixel = fs->preview_bg.pixel = 0; @@ -3179,8 +3196,10 @@ void unifontsel_destroy(unifontsel *fontsel) unifontsel_internal *fs = (unifontsel_internal *)fontsel; fontinfo *info; +#ifndef NO_BACKING_PIXMAPS if (fs->preview_pixmap) gdk_pixmap_unref(fs->preview_pixmap); +#endif freetree234(fs->fonts_by_selorder); while ((info = delpos234(fs->fonts_by_realname, 0)) != NULL) diff --git a/unix/gtkfont.h b/unix/gtkfont.h index b56640c5..f99097f2 100644 --- a/unix/gtkfont.h +++ b/unix/gtkfont.h @@ -29,6 +29,23 @@ #define DRAW_TEXT_CAIRO #endif +#if GTK_CHECK_VERSION(3,0,0) || defined GDK_DISABLE_DEPRECATED +/* + * Where the facility is available, we prefer to render text on to a + * persistent server-side pixmap, and redraw windows by simply + * blitting rectangles of that pixmap into them as needed. This is + * better for performance since we avoid expensive font rendering + * calls where possible, and it's particularly good over a non-local X + * connection because the response to an expose event can now be a + * very simple rectangle-copy operation rather than a lot of fiddly + * drawing or bitmap transfer. + * + * However, GTK is deprecating the use of server-side pixmaps, so we + * have to disable this mode under some circumstances. + */ +#define NO_BACKING_PIXMAPS +#endif + /* * Exports from gtkfont.c. */ diff --git a/unix/gtkwin.c b/unix/gtkwin.c index c62bddb4..4062816c 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -79,7 +79,9 @@ struct gui_data { GtkWidget *menu, *specialsmenu, *specialsitem1, *specialsitem2, *restartitem; GtkWidget *sessionsmenu; +#ifndef NO_BACKING_PIXMAPS GdkPixmap *pixmap; +#endif #if GTK_CHECK_VERSION(2,0,0) GtkIMContext *imc; #endif @@ -491,6 +493,7 @@ gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data) need_size = 1; } +#ifndef NO_BACKING_PIXMAPS if (inst->pixmap) { gdk_pixmap_unref(inst->pixmap); inst->pixmap = NULL; @@ -499,6 +502,7 @@ gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data) inst->pixmap = gdk_pixmap_new(gtk_widget_get_window(widget), (w * inst->font_width + 2*inst->window_border), (h * inst->font_height + 2*inst->window_border), -1); +#endif draw_backing_rect(inst); @@ -520,6 +524,7 @@ gint expose_area(GtkWidget *widget, GdkEventExpose *event, gpointer data) { struct gui_data *inst = (struct gui_data *)data; +#ifndef NO_BACKING_PIXMAPS /* * Pass the exposed rectangle to terminal.c, which will call us * back to do the actual painting. @@ -533,6 +538,35 @@ gint expose_area(GtkWidget *widget, GdkEventExpose *event, gpointer data) event->area.x, event->area.y, event->area.width, event->area.height); } +#else + if (inst->term) { + Context ctx = get_ctx(inst); + /* + * As in window.c, we clear the 'immediately' flag in the + * term_paint() call if the terminal has an update pending, in + * case we're constrained within this event to only draw on + * the exposed rectangle of the window. (Because if the whole + * of a character cell needs a redraw due to a terminal + * contents change, the last thing we want is to give it a + * _partial_ redraw here due to system-imposed clipping, and + * then have the next main terminal update believe it's been + * redrawn in full.) + * + * I don't actually know if GTK expose events will constrain + * us in this way, but it's best to be careful... + */ + term_paint(inst->term, ctx, + (event->area.x - inst->window_border) / inst->font_width, + (event->area.y - inst->window_border) / inst->font_height, + (event->area.x + event->area.width - + inst->window_border) / inst->font_width, + (event->area.y + event->area.height - + inst->window_border) / inst->font_height, + !inst->term->window_update_pending); + free_ctx(ctx); + } +#endif + return TRUE; } @@ -2283,23 +2317,30 @@ Context get_ctx(void *frontend) { struct gui_data *inst = (struct gui_data *)frontend; struct draw_ctx *dctx; + GdkWindow *target; if (!gtk_widget_get_window(inst->area)) return NULL; +#ifndef NO_BACKING_PIXMAPS + target = inst->pixmap; +#else + target = gtk_widget_get_window(inst->area); +#endif + dctx = snew(struct draw_ctx); dctx->inst = inst; dctx->uctx.type = inst->drawtype; #ifdef DRAW_TEXT_GDK if (dctx->uctx.type == DRAWTYPE_GDK) { - dctx->uctx.u.gdk.target = inst->pixmap; + dctx->uctx.u.gdk.target = target; dctx->uctx.u.gdk.gc = gdk_gc_new(gtk_widget_get_window(inst->area)); } #endif #ifdef DRAW_TEXT_CAIRO if (dctx->uctx.type == DRAWTYPE_CAIRO) { dctx->uctx.u.cairo.widget = GTK_WIDGET(inst->area); - dctx->uctx.u.cairo.cr = gdk_cairo_create(inst->pixmap); + dctx->uctx.u.cairo.cr = gdk_cairo_create(target); cairo_get_matrix(dctx->uctx.u.cairo.cr, &dctx->uctx.u.cairo.origmatrix); cairo_set_line_width(dctx->uctx.u.cairo.cr, 1.0); @@ -2335,6 +2376,7 @@ void free_ctx(Context ctx) static void draw_update(struct draw_ctx *dctx, int x, int y, int w, int h) { +#ifndef NO_BACKING_PIXMAPS #ifdef DRAW_TEXT_GDK if (dctx->uctx.type == DRAWTYPE_GDK) { gdk_draw_pixmap(gtk_widget_get_window(dctx->inst->area), @@ -2350,6 +2392,7 @@ static void draw_update(struct draw_ctx *dctx, int x, int y, int w, int h) gdk_gc_unref(gc); } #endif +#endif } static void draw_set_colour(struct draw_ctx *dctx, int col) @@ -2492,6 +2535,7 @@ static void draw_stretch_after(struct draw_ctx *dctx, int x, int y, int h, int hdouble, int hbothalf) { #ifdef DRAW_TEXT_GDK +#ifndef NO_BACKING_PIXMAPS if (dctx->uctx.type == DRAWTYPE_GDK) { /* * I can't find any plausible StretchBlt equivalent in the X @@ -2531,7 +2575,10 @@ static void draw_stretch_after(struct draw_ctx *dctx, int x, int y, } } } +#else +#error No way to implement stretching in GDK without a reliable backing pixmap #endif +#endif /* DRAW_TEXT_GDK */ #ifdef DRAW_TEXT_CAIRO if (dctx->uctx.type == DRAWTYPE_CAIRO) { cairo_set_matrix(dctx->uctx.u.cairo.cr, -- 2.45.2