wrlib: split RConvertImage into X11/Wayland backends

RConvertImage and RConvertImageMask now dispatch through function
pointers on RContext, set during context creation:
- RCreateContext sets x11_convert_image/x11_convert_image_mask
- RCreateContextWayland sets wl_convert_image/wl_convert_image_mask

The Wayland backend creates pixman_image_t from RImage RGBA data
instead of returning NULL pixmaps, fixing the SIGSEGV in the exit
dialog when compositing the application icon.

X11 code moved to convert_x11.c, Wayland code in convert_wl.c.
No dpy guards in the middle layer.
This commit is contained in:
Levi Neely 2026-05-14 16:41:15 +02:00
parent 3edf25e0b4
commit 685eb42ecc
8 changed files with 198 additions and 105 deletions

View File

@ -35,6 +35,9 @@ libwraster_la_SOURCES = \
xpixmap.c \ xpixmap.c \
convert.h \ convert.h \
convert.c \ convert.c \
convert_funcs.h \
convert_x11.c \
convert_wl.c \
context.c \ context.c \
misc.c \ misc.c \
scale.c \ scale.c \
@ -82,7 +85,7 @@ libwraster_la_SOURCES += load_magick.c
endif endif
AM_CFLAGS = @MAGICKFLAGS@ AM_CFLAGS = @MAGICKFLAGS@
AM_CPPFLAGS = $(DFLAGS) @HEADER_SEARCH_PATH@ AM_CPPFLAGS = $(DFLAGS) @HEADER_SEARCH_PATH@ $(PIXMAN_CFLAGS)
libwraster_la_LIBADD = @LIBRARY_SEARCH_PATH@ @GFXLIBS@ @MAGICKLIBS@ @XLIBS@ @LIBXMU@ -lm libwraster_la_LIBADD = @LIBRARY_SEARCH_PATH@ @GFXLIBS@ @MAGICKLIBS@ @XLIBS@ @LIBXMU@ -lm

View File

@ -40,6 +40,7 @@
#include "wraster.h" #include "wraster.h"
#include "scale.h" #include "scale.h"
#include "wr_i18n.h" #include "wr_i18n.h"
#include "convert_funcs.h"
#ifndef HAVE_FLOAT_MATHFUNC #ifndef HAVE_FLOAT_MATHFUNC
@ -656,6 +657,9 @@ RContext *RCreateContext(Display * dpy, int screen_number, const RContextAttribu
} }
#endif #endif
context->convert_image = x11_convert_image;
context->convert_image_mask = x11_convert_image_mask;
return context; return context;
} }
@ -761,5 +765,8 @@ RContext *RCreateContextWayland(int width, int height,
(void)width; (void)width;
(void)height; (void)height;
context->convert_image = wl_convert_image;
context->convert_image_mask = wl_convert_image_mask;
return context; return context;
} }

View File

@ -292,7 +292,7 @@ convertTrueColor_generic(RXImage * ximg, RImage * image,
} }
} }
static RXImage *image2TrueColor(RContext * ctx, RImage * image) RXImage *image2TrueColor(RContext * ctx, RImage * image)
{ {
RXImage *ximg; RXImage *ximg;
unsigned short rmask, gmask, bmask; unsigned short rmask, gmask, bmask;
@ -481,7 +481,7 @@ convertPseudoColor_to_8(RXImage * ximg, RImage * image,
} }
} }
static RXImage *image2PseudoColor(RContext * ctx, RImage * image) RXImage *image2PseudoColor(RContext * ctx, RImage * image)
{ {
RXImage *ximg; RXImage *ximg;
register int x, y, r, g, b; register int x, y, r, g, b;
@ -565,7 +565,7 @@ static RXImage *image2PseudoColor(RContext * ctx, RImage * image)
/* /*
* For standard colormap * For standard colormap
*/ */
static RXImage *image2StandardPseudoColor(RContext * ctx, RImage * image) RXImage *image2StandardPseudoColor(RContext * ctx, RImage * image)
{ {
RXImage *ximg; RXImage *ximg;
register int x, y, r, g, b; register int x, y, r, g, b;
@ -715,7 +715,7 @@ static RXImage *image2StandardPseudoColor(RContext * ctx, RImage * image)
return ximg; return ximg;
} }
static RXImage *image2GrayScale(RContext * ctx, RImage * image) RXImage *image2GrayScale(RContext * ctx, RImage * image)
{ {
RXImage *ximg; RXImage *ximg;
register int x, y, g; register int x, y, g;
@ -838,7 +838,7 @@ static RXImage *image2GrayScale(RContext * ctx, RImage * image)
return ximg; return ximg;
} }
static RXImage *image2Bitmap(RContext * ctx, RImage * image, int threshold) RXImage *image2Bitmap(RContext * ctx, RImage * image, int threshold)
{ {
RXImage *ximg; RXImage *ximg;
unsigned char *alpha; unsigned char *alpha;
@ -862,74 +862,11 @@ static RXImage *image2Bitmap(RContext * ctx, RImage * image, int threshold)
int RConvertImage(RContext * context, RImage * image, Pixmap * pixmap) int RConvertImage(RContext * context, RImage * image, Pixmap * pixmap)
{ {
RXImage *ximg = NULL;
#ifdef USE_XSHM
Pixmap tmp;
#endif
assert(context != NULL); assert(context != NULL);
assert(image != NULL); assert(image != NULL);
assert(pixmap != NULL); assert(pixmap != NULL);
/* Wayland mode: no X11 display, pixmaps are not used */ return context->convert_image(context, image, pixmap);
if (!context->dpy) {
*pixmap = (Pixmap)0;
return 1;
}
switch (context->vclass) {
case TrueColor:
ximg = image2TrueColor(context, image);
break;
case PseudoColor:
case StaticColor:
if (context->attribs->standard_colormap_mode != RIgnoreStdColormap)
ximg = image2StandardPseudoColor(context, image);
else
ximg = image2PseudoColor(context, image);
break;
case GrayScale:
case StaticGray:
ximg = image2GrayScale(context, image);
break;
}
if (!ximg) {
return False;
}
*pixmap = XCreatePixmap(context->dpy, context->drawable, image->width, image->height, context->depth);
#ifdef USE_XSHM
if (context->flags.use_shared_pixmap && ximg->is_shared)
tmp = R_CreateXImageMappedPixmap(context, ximg);
else
tmp = None;
if (tmp) {
/*
* We have to copy the shm Pixmap into a normal Pixmap because
* otherwise, we would have to control when Pixmaps are freed so
* that we can detach their shm segments. This is a problem if the
* program crash, leaving stale shared memory segments in the
* system (lots of them). But with some work, we can optimize
* things and remove this XCopyArea. This will require
* explicitly freeing all pixmaps when exiting or restarting
* wmaker.
*/
XCopyArea(context->dpy, tmp, *pixmap, context->copy_gc, 0, 0, image->width, image->height, 0, 0);
XFreePixmap(context->dpy, tmp);
} else {
RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0, image->width, image->height);
}
#else /* !USE_XSHM */
RPutXImage(context, *pixmap, context->copy_gc, ximg, 0, 0, 0, 0, image->width, image->height);
#endif /* !USE_XSHM */
RDestroyXImage(context, ximg);
return True;
} }
/* make the gc permanent (create with context creation). /* make the gc permanent (create with context creation).
@ -937,44 +874,12 @@ int RConvertImage(RContext * context, RImage * image, Pixmap * pixmap)
*/ */
int RConvertImageMask(RContext * context, RImage * image, Pixmap * pixmap, Pixmap * mask, int threshold) int RConvertImageMask(RContext * context, RImage * image, Pixmap * pixmap, Pixmap * mask, int threshold)
{ {
GC gc;
XGCValues gcv;
RXImage *ximg = NULL;
assert(context != NULL); assert(context != NULL);
assert(image != NULL); assert(image != NULL);
assert(pixmap != NULL); assert(pixmap != NULL);
assert(mask != NULL); assert(mask != NULL);
if (!context->dpy) { return context->convert_image_mask(context, image, pixmap, mask, threshold);
*pixmap = (Pixmap)0;
*mask = (Pixmap)0;
return True;
}
if (!RConvertImage(context, image, pixmap))
return False;
if (image->format == RRGBFormat) {
*mask = None;
return True;
}
ximg = image2Bitmap(context, image, threshold);
if (!ximg) {
return False;
}
*mask = XCreatePixmap(context->dpy, context->drawable, image->width, image->height, 1);
gcv.foreground = context->black;
gcv.background = context->white;
gcv.graphics_exposures = False;
gc = XCreateGC(context->dpy, *mask, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
RPutXImage(context, *mask, gc, ximg, 0, 0, 0, 0, image->width, image->height);
RDestroyXImage(context, ximg);
XFreeGC(context->dpy, gc);
return True;
} }
Bool RGetClosestXColor(RContext * context, const RColor * color, XColor * retColor) Bool RGetClosestXColor(RContext * context, const RColor * color, XColor * retColor)

View File

@ -35,5 +35,11 @@
*/ */
void r_destroy_conversion_tables(void); void r_destroy_conversion_tables(void);
/* Internal image-to-XImage converters (used by convert_x11.c) */
RXImage *image2TrueColor(RContext *ctx, RImage *image);
RXImage *image2PseudoColor(RContext *ctx, RImage *image);
RXImage *image2StandardPseudoColor(RContext *ctx, RImage *image);
RXImage *image2GrayScale(RContext *ctx, RImage *image);
RXImage *image2Bitmap(RContext *ctx, RImage *image, int threshold);
#endif #endif

12
wrlib/convert_funcs.h Normal file
View File

@ -0,0 +1,12 @@
/* convert_funcs.h - backend convert function declarations */
#ifndef WRLIB_CONVERT_FUNCS_H
#define WRLIB_CONVERT_FUNCS_H
#include "wraster.h"
int x11_convert_image(RContext *ctx, RImage *image, Pixmap *pixmap);
int x11_convert_image_mask(RContext *ctx, RImage *image, Pixmap *pixmap, Pixmap *mask, int threshold);
int wl_convert_image(RContext *ctx, RImage *image, Pixmap *pixmap);
int wl_convert_image_mask(RContext *ctx, RImage *image, Pixmap *pixmap, Pixmap *mask, int threshold);
#endif

58
wrlib/convert_wl.c Normal file
View File

@ -0,0 +1,58 @@
/* convert_wl.c - Wayland backend for RImage → pixman_image_t conversion */
#include <config.h>
#include <pixman-1/pixman.h>
#include <string.h>
#include "wraster.h"
int wl_convert_image(RContext *context, RImage *image, Pixmap *pixmap)
{
(void)context;
unsigned int w = image->width;
unsigned int h = image->height;
pixman_image_t *img = pixman_image_create_bits(PIXMAN_a8r8g8b8, w, h, NULL, 0);
uint32_t *dst = pixman_image_get_data(img);
unsigned char *src = image->data;
int has_alpha = (image->format == RRGBAFormat);
for (unsigned int i = 0; i < w * h; i++) {
unsigned char r = src[0], g = src[1], b = src[2];
unsigned char a = has_alpha ? src[3] : 0xFF;
dst[i] = ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
src += has_alpha ? 4 : 3;
}
*pixmap = (Pixmap)(uintptr_t)img;
return True;
}
int wl_convert_image_mask(RContext *context, RImage *image,
Pixmap *pixmap, Pixmap *mask, int threshold)
{
if (!wl_convert_image(context, image, pixmap))
return False;
if (image->format == RRGBFormat) {
*mask = (Pixmap)0;
return True;
}
/* Create 8-bit alpha mask from image alpha channel */
unsigned int w = image->width;
unsigned int h = image->height;
pixman_image_t *m = pixman_image_create_bits(PIXMAN_a8, w, h, NULL, 0);
uint8_t *mdst = (uint8_t *)pixman_image_get_data(m);
int stride = pixman_image_get_stride(m);
unsigned char *src = image->data;
for (unsigned int y = 0; y < h; y++) {
for (unsigned int x = 0; x < w; x++) {
unsigned char a = src[(y * w + x) * 4 + 3];
mdst[y * stride + x] = (a >= threshold) ? 0xFF : 0x00;
}
}
*mask = (Pixmap)(uintptr_t)m;
return True;
}

96
wrlib/convert_x11.c Normal file
View File

@ -0,0 +1,96 @@
/* convert_x11.c - X11 backend for RImage → Pixmap conversion */
#include <config.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdlib.h>
#include <string.h>
#include "wraster.h"
#include "convert.h"
#include "xutil.h"
int x11_convert_image(RContext *context, RImage *image, Pixmap *pixmap)
{
RXImage *ximg = NULL;
#ifdef USE_XSHM
Pixmap tmp;
#endif
switch (context->vclass) {
case TrueColor:
ximg = image2TrueColor(context, image);
break;
case PseudoColor:
case StaticColor:
if (context->attribs->standard_colormap_mode != RIgnoreStdColormap)
ximg = image2StandardPseudoColor(context, image);
else
ximg = image2PseudoColor(context, image);
break;
case GrayScale:
case StaticGray:
ximg = image2GrayScale(context, image);
break;
}
if (!ximg)
return False;
*pixmap = XCreatePixmap(context->dpy, context->drawable,
image->width, image->height, context->depth);
#ifdef USE_XSHM
if (context->flags.use_shared_pixmap && ximg->is_shared)
tmp = R_CreateXImageMappedPixmap(context, ximg);
else
tmp = None;
if (tmp) {
XCopyArea(context->dpy, tmp, *pixmap, context->copy_gc,
0, 0, image->width, image->height, 0, 0);
XFreePixmap(context->dpy, tmp);
} else {
RPutXImage(context, *pixmap, context->copy_gc, ximg,
0, 0, 0, 0, image->width, image->height);
}
#else
RPutXImage(context, *pixmap, context->copy_gc, ximg,
0, 0, 0, 0, image->width, image->height);
#endif
RDestroyXImage(context, ximg);
return True;
}
int x11_convert_image_mask(RContext *context, RImage *image,
Pixmap *pixmap, Pixmap *mask, int threshold)
{
GC gc;
XGCValues gcv;
RXImage *ximg;
if (!RConvertImage(context, image, pixmap))
return False;
if (image->format == RRGBFormat) {
*mask = None;
return True;
}
ximg = image2Bitmap(context, image, threshold);
if (!ximg)
return False;
*mask = XCreatePixmap(context->dpy, context->drawable,
image->width, image->height, 1);
gcv.foreground = context->black;
gcv.background = context->white;
gcv.graphics_exposures = False;
gc = XCreateGC(context->dpy, *mask,
GCForeground | GCBackground | GCGraphicsExposures, &gcv);
RPutXImage(context, *mask, gc, ximg, 0, 0, 0, 0,
image->width, image->height);
RDestroyXImage(context, ximg);
XFreeGC(context->dpy, gc);
return True;
}

View File

@ -166,6 +166,8 @@ typedef struct RContextAttributes {
* we can use, if we should do dithering, and what colors to use for * we can use, if we should do dithering, and what colors to use for
* dithering. * dithering.
*/ */
typedef struct RImage RImage;
typedef struct RContext { typedef struct RContext {
Display *dpy; Display *dpy;
int screen_number; int screen_number;
@ -203,6 +205,10 @@ typedef struct RContext {
__wrlib_deprecated("Flag optimize_for_speed in RContext is not used anymore " __wrlib_deprecated("Flag optimize_for_speed in RContext is not used anymore "
"and will be removed in future version, please do not use"); "and will be removed in future version, please do not use");
} flags; } flags;
/* Backend conversion vtable (set during context creation) */
int (*convert_image)(struct RContext *ctx, RImage *image, Pixmap *pixmap);
int (*convert_image_mask)(struct RContext *ctx, RImage *image, Pixmap *pixmap, Pixmap *mask, int threshold);
} RContext; } RContext;
@ -243,13 +249,13 @@ enum RImageFormat {
/* /*
* internal 24bit+alpha image representation * internal 24bit+alpha image representation
*/ */
typedef struct RImage { struct RImage {
unsigned char *data; /* image data RGBA or RGB */ unsigned char *data; /* image data RGBA or RGB */
int width, height; /* size of the image */ int width, height; /* size of the image */
enum RImageFormat format; enum RImageFormat format;
RColor background; /* background color */ RColor background; /* background color */
int refCount; int refCount;
} RImage; };
/* /*