diff --git a/cmake/platforms/unix.cmake b/cmake/platforms/unix.cmake index 4d056d0a..4ae8c313 100644 --- a/cmake/platforms/unix.cmake +++ b/cmake/platforms/unix.cmake @@ -89,7 +89,7 @@ if(GTK_FOUND) list(APPEND CMAKE_REQUIRED_INCLUDES ${GTK_INCLUDE_DIRS}) check_include_file(gdk/gdkx.h HAVE_GDK_GDKX_H) - if(X11_FOUND AND HAVE_GDK_GDKX_H) + if(X11_FOUND AND X11_Xrender_FOUND AND HAVE_GDK_GDKX_H) set(NOT_X_WINDOWS OFF PARENT_SCOPE) else() set(NOT_X_WINDOWS ON PARENT_SCOPE) diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 87ee6574..df5d275d 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -153,7 +153,7 @@ if(GTK_FOUND) be_list(pterm pterm) target_link_libraries(pterm guiterminal eventloop settings utils ptermxpms - ${GTK_LIBRARIES} ${X11_LIBRARIES}) + ${GTK_LIBRARIES} ${X11_LIBRARIES} ${X11_Xrender_LIB}) installed_program(pterm) if(GTK_VERSION GREATER_EQUAL 3) @@ -170,7 +170,7 @@ if(GTK_FOUND) be_list(ptermapp pterm) target_link_libraries(ptermapp guiterminal eventloop settings utils ptermxpms - ${GTK_LIBRARIES} ${X11_LIBRARIES}) + ${GTK_LIBRARIES} ${X11_LIBRARIES} ${X11_Xrender_LIB}) endif() add_executable(putty @@ -181,7 +181,7 @@ if(GTK_FOUND) target_link_libraries(putty guiterminal eventloop sshclient otherbackends settings network crypto utils puttyxpms - ${GTK_LIBRARIES} ${X11_LIBRARIES}) + ${GTK_LIBRARIES} ${X11_LIBRARIES} ${X11_Xrender_LIB}) set_target_properties(putty PROPERTIES LINK_INTERFACE_MULTIPLICITY 2) installed_program(putty) @@ -196,7 +196,7 @@ if(GTK_FOUND) target_link_libraries(puttyapp guiterminal eventloop sshclient otherbackends settings network crypto utils puttyxpms - ${GTK_LIBRARIES} ${X11_LIBRARIES}) + ${GTK_LIBRARIES} ${X11_LIBRARIES} ${X11_Xrender_LIB}) endif() add_executable(puttytel @@ -213,7 +213,7 @@ if(GTK_FOUND) target_link_libraries(puttytel guiterminal eventloop otherbackends settings network utils puttyxpms - ${GTK_LIBRARIES} ${X11_LIBRARIES}) + ${GTK_LIBRARIES} ${X11_LIBRARIES} ${X11_Xrender_LIB}) add_executable(test_lineedit ${CMAKE_SOURCE_DIR}/test/test_lineedit.c diff --git a/unix/unifont.c b/unix/unifont.c index d8289ecd..6fb1e0ff 100644 --- a/unix/unifont.c +++ b/unix/unifont.c @@ -29,6 +29,8 @@ #ifndef NOT_X_WINDOWS #ifdef DRAW_TEXT_CAIRO #include +#include +#include #endif #include #include @@ -154,6 +156,7 @@ typedef struct x11font_individual { bool allocated; #ifdef DRAW_TEXT_CAIRO + XRenderPictFormat *pictformat; GC gc; #endif @@ -607,7 +610,26 @@ static void x11font_gdk_draw(unifont_drawctx *ctx, x11font_individual *xfi, static void x11font_cairo_setup( unifont_drawctx *ctx, x11font_individual *xfi, Display *disp) { - + /* + * To render X fonts under Cairo, we use the usual X font rendering + * requests to draw text into a pixmap, make a Cairo surface out + * of it, and then use that as a mask to paint the current colour + * into the terminal surface. This means that colours are + * entirely in the hands of Cairo and we don't have to think about + * X colourmaps. But we do need to be able to create that pixmap. + * + * All X servers are required to support 1bpp pixmaps, and Cairo + * can always use one of them as a mask. But on 2025's X servers, + * 1bpp font rendering is unaccelerated and hence much slower than + * on deeper drawables. So we find out if we can make an 8-bit + * alpha-only surface, and only fall back to a 1bpp pixmap if that + * fails. + * + * XRenderFindStandardFormat() will return NULL if the X Rendering + * Extension is missing or unusable, so we don't need to check + * that in advance. + */ + xfi->pictformat = XRenderFindStandardFormat(disp, PictStandardA8); } /* @@ -621,7 +643,7 @@ static void x11font_cairo_init_gc(x11font_individual *xfi, Display *disp, if (xfi->gc == None) { XGCValues gcvals = { .function = GXclear, - .foreground = 1, + .foreground = 0xffffffff, .background = 0, .font = xfi->xfs->fid, }; @@ -646,17 +668,27 @@ static void x11font_cairo_draw( &direction, &font_ascent, &font_descent, &bounds); pixwidth = bounds.rbearing - bounds.lbearing; pixheight = bounds.ascent + bounds.descent; - if (pixwidth > 0 && pixheight > 0) { - pixmap = XCreatePixmap(disp, GDK_DRAWABLE_XID(widgetwin), - pixwidth, pixheight, 1); + if (pixwidth > 0 && pixheight > 0) { + cairo_surface_t *surface; + if (xfi->pictformat != NULL) { + pixmap = XCreatePixmap(disp, GDK_DRAWABLE_XID(widgetwin), + pixwidth, pixheight, 8); + surface = cairo_xlib_surface_create_with_xrender_format( + disp, pixmap, ScreenOfDisplay(disp, widgetscr), + xfi->pictformat, pixwidth, pixheight); + } else { + pixmap = XCreatePixmap(disp, GDK_DRAWABLE_XID(widgetwin), + pixwidth, pixheight, 1); + surface = cairo_xlib_surface_create_for_bitmap( + disp, pixmap, ScreenOfDisplay(disp, widgetscr), + pixwidth, pixheight); + } x11font_cairo_init_gc(xfi, disp, pixmap); XFillRectangle(disp, pixmap, xfi->gc, 0, 0, pixwidth, pixheight); XDrawImageString16(disp, pixmap, xfi->gc, -bounds.lbearing, bounds.ascent, string+start, length); - cairo_surface_t *surface = cairo_xlib_surface_create_for_bitmap( - disp, pixmap, ScreenOfDisplay(disp, widgetscr), - pixwidth, pixheight); + cairo_surface_mark_dirty(surface); cairo_pattern_t *pattern = cairo_pattern_create_for_surface(surface); /* We really don't want bilinear interpolation of bitmap fonts. */ cairo_pattern_set_filter(pattern, CAIRO_FILTER_NEAREST);