X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=jwxyz%2Fjwxyz-common.c;h=81ffe373e5049a133352344388f085d3863b8fd2;hb=78add6e627ee5f10e1fa6f3852602ea5066eee5a;hp=a168bda299a258883cd246752459ef9b9cdbdfcc;hpb=aa75c7476aeaa84cf3abc192b376a8b03c325213;p=xscreensaver diff --git a/jwxyz/jwxyz-common.c b/jwxyz/jwxyz-common.c index a168bda2..81ffe373 100644 --- a/jwxyz/jwxyz-common.c +++ b/jwxyz/jwxyz-common.c @@ -19,16 +19,54 @@ and iOS is in jwxyz.m. */ -#include "config.h" +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif #ifdef HAVE_JWXYZ /* whole file */ +#include +#include +#include + #include "jwxyzI.h" +#include "pow2.h" +#include "utf8wc.h" +#include "xft.h" /* There's only one Window for a given jwxyz_Display. */ #define assert_window(dpy, w) \ Assert (w == RootWindow (dpy, 0), "not a window") +#define VTBL JWXYZ_VTBL(dpy) + +struct jwxyz_Font { + Display *dpy; + void *native_font; + int refcount; // for deciding when to release the native font + int ascent, descent; + char *xa_font; + + // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics. + // But we need the metrics on both of them, so they go here. + XFontStruct metrics; +}; + +struct jwxyz_XFontSet { + XFontStruct *font; +}; + + +void +Log (const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + Logv (fmt, args); + va_end (args); +} + + int XDisplayWidth (Display *dpy, int screen) { @@ -43,22 +81,44 @@ XDisplayHeight (Display *dpy, int screen) /* XLFDs use dots per inch, but Xlib uses millimeters. Go figure. */ -static const unsigned dpi = 75; +static int +size_mm (Display *dpy, unsigned size) +{ + /* ((mm / inch) / (points / inch)) * dots / (dots / points) */ + return (25.4 / 72) * size / jwxyz_scale (XRootWindow (dpy,0)) + 0.5; +} int XDisplayWidthMM (Display *dpy, int screen) { - const unsigned denom = dpi * 10 / 2; - return (254 * XDisplayWidth (dpy, screen) + denom) / (2 * denom); + return size_mm (dpy, XDisplayWidth (dpy, screen)); } int XDisplayHeightMM (Display *dpy, int screen) { - const unsigned denom = dpi * 10 / 2; - return (254 * XDisplayHeight (dpy, screen) + denom) / (2 * denom); + return size_mm (dpy, XDisplayHeight (dpy, screen)); +} + +unsigned long +XBlackPixelOfScreen(Screen *screen) +{ + return DefaultVisualOfScreen (screen)->rgba_masks[3]; } +unsigned long +XWhitePixelOfScreen(Screen *screen) +{ + const unsigned long *masks = DefaultVisualOfScreen (screen)->rgba_masks; + return masks[0] | masks[1] | masks[2] | masks[3]; +} + +unsigned long +XCellsOfScreen(Screen *screen) +{ + const unsigned long *masks = DefaultVisualOfScreen (screen)->rgba_masks; + return masks[0] | masks[1] | masks[2]; +} void jwxyz_validate_pixel (Display *dpy, unsigned long pixel, unsigned int depth, @@ -89,12 +149,12 @@ Bool jwxyz_dumb_drawing_mode(Display *dpy, Drawable d, GC gc, int x, int y, unsigned width, unsigned height) { - XGCValues *gcv = jwxyz_gc_gcv (gc); + XGCValues *gcv = VTBL->gc_gcv (gc); if (gcv->function == GXset || gcv->function == GXclear) { // "set" and "clear" are dumb drawing modes that ignore the source // bits and just draw solid rectangles. - unsigned depth = jwxyz_gc_depth (gc); + unsigned depth = VTBL->gc_depth (gc); jwxyz_fill_rect (dpy, d, 0, x, y, width, height, (gcv->function == GXset ? (depth == 1 ? 1 : WhitePixel(dpy,0)) @@ -188,7 +248,7 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, width0 = 0; height0 = 0; } else { - jwxyz_copy_area (dpy, src, dst, gc, + VTBL->copy_area (dpy, src, dst, gc, src_x, src_y, width0, height0, dst_x, dst_y); } @@ -245,11 +305,11 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, ++rects_end; } - XGCValues *gcv = jwxyz_gc_gcv (gc); + XGCValues *gcv = VTBL->gc_gcv (gc); int old_function = gcv->function; gcv->function = GXcopy; - jwxyz_fill_rects (dpy, dst, gc, rects, rects_end - rects, - jwxyz_window_background (dpy)); + VTBL->fill_rects (dpy, dst, gc, rects, rects_end - rects, + *VTBL->window_background (dpy)); gcv->function = old_function; } @@ -257,13 +317,44 @@ XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, } +void +jwxyz_blit (const void *src_data, ptrdiff_t src_pitch, + unsigned src_x, unsigned src_y, + void *dst_data, ptrdiff_t dst_pitch, + unsigned dst_x, unsigned dst_y, + unsigned width, unsigned height) +{ + Bool same = src_data == dst_data; + src_data = SEEK_XY (src_data, src_pitch, src_x, src_y); + dst_data = SEEK_XY (dst_data, dst_pitch, dst_x, dst_y); + + size_t bytes = width * 4; + + if (same && dst_y > src_y) { + // Copy upwards if the areas might overlap. + src_data += src_pitch * (height - 1); + dst_data += dst_pitch * (height - 1); + src_pitch = -src_pitch; + dst_pitch = -dst_pitch; + } + + while (height) { + // memcpy is an alias for memmove on macOS. + memmove (dst_data, src_data, bytes); + src_data += src_pitch; + dst_data += dst_pitch; + --height; + } +} + + int XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc, int src_x, int src_y, unsigned width, int height, int dest_x, int dest_y, unsigned long plane) { - Assert ((jwxyz_gc_depth (gc) == 1 || plane == 1), "hairy plane mask!"); + Assert ((VTBL->gc_depth (gc) == 1 || plane == 1), "hairy plane mask!"); // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg, // not to white/black. @@ -272,13 +363,35 @@ XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc, } +int +XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2) +{ + XSegment segment; + segment.x1 = x1; + segment.y1 = y1; + segment.x2 = x2; + segment.y2 = y2; + XDrawSegments (dpy, d, gc, &segment, 1); + return 0; +} + + +int +XSetWindowBackground (Display *dpy, Window w, unsigned long pixel) +{ + Assert (w == XRootWindow (dpy,0), "not a window"); + jwxyz_validate_pixel (dpy, pixel, visual_depth (NULL, NULL), False); + *VTBL->window_background (dpy) = pixel; + return 0; +} + void jwxyz_fill_rect (Display *dpy, Drawable d, GC gc, int x, int y, unsigned int width, unsigned int height, unsigned long pixel) { XRectangle r = {x, y, width, height}; - jwxyz_fill_rects (dpy, d, gc, &r, 1, pixel); + VTBL->fill_rects (dpy, d, gc, &r, 1, pixel); } int @@ -286,7 +399,7 @@ XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y, unsigned int width, unsigned int height) { jwxyz_fill_rect (dpy, d, gc, x, y, width, height, - jwxyz_gc_gcv (gc)->foreground); + VTBL->gc_gcv (gc)->foreground); return 0; } @@ -309,7 +422,17 @@ XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y, int XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n) { - jwxyz_fill_rects (dpy, d, gc, rects, n, jwxyz_gc_gcv (gc)->foreground); + VTBL->fill_rects (dpy, d, gc, rects, n, VTBL->gc_gcv (gc)->foreground); + return 0; +} + + +int +XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp) +{ + Assert(win == XRootWindow(dpy,0), "XClearArea: not a window"); + Assert(!exp, "XClearArea: exposures unsupported"); + jwxyz_fill_rect (dpy, win, 0, x, y, w, h, *VTBL->window_background (dpy)); return 0; } @@ -318,7 +441,7 @@ int XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y, unsigned int width, unsigned int height, int angle1, int angle2) { - return jwxyz_draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, + return VTBL->draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, False); } @@ -326,7 +449,7 @@ int XFillArc (Display *dpy, Drawable d, GC gc, int x, int y, unsigned int width, unsigned int height, int angle1, int angle2) { - return jwxyz_draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, + return VTBL->draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2, True); } @@ -335,7 +458,7 @@ XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs) { int i; for (i = 0; i < narcs; i++) - jwxyz_draw_arc (dpy, d, gc, + VTBL->draw_arc (dpy, d, gc, arcs[i].x, arcs[i].y, arcs[i].width, arcs[i].height, arcs[i].angle1, arcs[i].angle2, @@ -348,7 +471,7 @@ XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs) { int i; for (i = 0; i < narcs; i++) - jwxyz_draw_arc (dpy, d, gc, + VTBL->draw_arc (dpy, d, gc, arcs[i].x, arcs[i].y, arcs[i].width, arcs[i].height, arcs[i].angle1, arcs[i].angle2, @@ -380,8 +503,8 @@ XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *from) Assert (gc && from, "no gc"); if (!gc || !from) return 0; - XGCValues *to = jwxyz_gc_gcv (gc); - unsigned depth = jwxyz_gc_depth (gc); + XGCValues *to = VTBL->gc_gcv (gc); + unsigned depth = VTBL->gc_depth (gc); if (mask & GCFunction) to->function = from->function; if (mask & GCForeground) to->foreground = from->foreground; @@ -394,8 +517,8 @@ XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *from) if (mask & GCClipYOrigin) to->clip_y_origin = from->clip_y_origin; if (mask & GCSubwindowMode) to->subwindow_mode = from->subwindow_mode; - if (mask & GCClipMask) XSetClipMask (0, gc, from->clip_mask); - if (mask & GCFont) XSetFont (0, gc, from->font); + if (mask & GCClipMask) XSetClipMask (dpy, gc, from->clip_mask); + if (mask & GCFont) XSetFont (dpy, gc, from->font); if (mask & GCForeground) jwxyz_validate_pixel (dpy, from->foreground, depth, to->alpha_allowed_p); @@ -456,11 +579,13 @@ XGetGeometry (Display *dpy, Drawable d, Window *root_ret, Status XAllocColor (Display *dpy, Colormap cmap, XColor *color) { - color->pixel = jwxyz_alloc_color (dpy, - color->red, - color->green, - color->blue, - 0xFFFF); + const unsigned long *masks = + DefaultVisualOfScreen(DefaultScreenOfDisplay(dpy))->rgba_masks; + color->pixel = + (((color->red << 16) >> (31 - i_log2(masks[0]))) & masks[0]) | + (((color->green << 16) >> (31 - i_log2(masks[1]))) & masks[1]) | + (((color->blue << 16) >> (31 - i_log2(masks[2]))) & masks[2]) | + masks[3]; return 1; } @@ -552,11 +677,11 @@ int XQueryColor (Display *dpy, Colormap cmap, XColor *color) { jwxyz_validate_pixel (dpy, color->pixel, visual_depth (NULL, NULL), False); - uint8_t rgba[4]; - jwxyz_query_color (dpy, color->pixel, rgba); - color->red = (rgba[0] << 8) | rgba[0]; - color->green = (rgba[1] << 8) | rgba[1]; - color->blue = (rgba[2] << 8) | rgba[2]; + uint16_t rgba[4]; + JWXYZ_QUERY_COLOR (dpy, color->pixel, 0xffffull, rgba); + color->red = rgba[0]; + color->green = rgba[1]; + color->blue = rgba[2]; color->flags = DoRed|DoGreen|DoBlue; return 0; } @@ -645,9 +770,9 @@ XCreateImage (Display *dpy, Visual *visual, unsigned int depth, ximage->bitmap_pad = bitmap_pad; ximage->depth = depth; Visual *v = DefaultVisualOfScreen (DefaultScreenOfDisplay (dpy)); - ximage->red_mask = (depth == 1 ? 0 : v->red_mask); - ximage->green_mask = (depth == 1 ? 0 : v->green_mask); - ximage->blue_mask = (depth == 1 ? 0 : v->blue_mask); + ximage->red_mask = (depth == 1 ? 0 : v->rgba_masks[0]); + ximage->green_mask = (depth == 1 ? 0 : v->rgba_masks[1]); + ximage->blue_mask = (depth == 1 ? 0 : v->rgba_masks[2]); ximage->bits_per_pixel = (depth == 1 ? 1 : visual_depth (NULL, NULL)); ximage->bytes_per_line = bytes_per_line; @@ -722,11 +847,26 @@ XDestroyImage (XImage *ximage) } +XImage * +XGetImage (Display *dpy, Drawable d, int x, int y, + unsigned int width, unsigned int height, + unsigned long plane_mask, int format) +{ + unsigned depth = jwxyz_drawable_depth (d); + XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height, + 0, 0); + image->data = (char *) malloc (height * image->bytes_per_line); + + return XGetSubImage (dpy, d, x, y, width, height, plane_mask, format, + image, 0, 0); +} + + Pixmap XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable, const char *data, unsigned int w, unsigned int h, - unsigned long fg, unsigned int bg, + unsigned long fg, unsigned long bg, unsigned int depth) { Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth); @@ -755,11 +895,706 @@ XGetAtomName (Display *dpy, Atom atom) } +// This is XQueryFont, but for the XFontStruct embedded in 'Font' +// +static void +query_font (Font fid) +{ + Assert (fid && fid->native_font, "no native font in fid"); + + int first = 32; + int last = 255; + + Display *dpy = fid->dpy; + void *native_font = fid->native_font; + + XFontStruct *f = &fid->metrics; + XCharStruct *min = &f->min_bounds; + XCharStruct *max = &f->max_bounds; + + f->fid = fid; + f->min_char_or_byte2 = first; + f->max_char_or_byte2 = last; + f->default_char = 'M'; + f->ascent = fid->ascent; + f->descent = fid->descent; + + min->width = 32767; // set to smaller values in the loop + min->ascent = 32767; + min->descent = 32767; + min->lbearing = 32767; + min->rbearing = 32767; + + f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct)); + + for (int i = first; i <= last; i++) { + XCharStruct *cs = &f->per_char[i-first]; + char s = (char) i; + jwxyz_render_text (dpy, native_font, &s, 1, False, False, cs, 0); + + max->width = MAX (max->width, cs->width); + max->ascent = MAX (max->ascent, cs->ascent); + max->descent = MAX (max->descent, cs->descent); + max->lbearing = MAX (max->lbearing, cs->lbearing); + max->rbearing = MAX (max->rbearing, cs->rbearing); + + min->width = MIN (min->width, cs->width); + min->ascent = MIN (min->ascent, cs->ascent); + min->descent = MIN (min->descent, cs->descent); + min->lbearing = MIN (min->lbearing, cs->lbearing); + min->rbearing = MIN (min->rbearing, cs->rbearing); +/* + Log (" %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d " + " bb=%5.1f x %5.1f @ %5.1f %5.1f adv=%5.1f %5.1f\n" + i, i, cs->width, cs->lbearing, cs->rbearing, + cs->ascent, cs->descent, + bbox.size.width, bbox.size.height, + bbox.origin.x, bbox.origin.y, + advancement.width, advancement.height); + */ + } +} + + +// Since 'Font' includes the metrics, this just makes a copy of that. +// +XFontStruct * +XQueryFont (Display *dpy, Font fid) +{ + // copy XFontStruct + XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f)); + *f = fid->metrics; + f->fid = fid; + + // build XFontProps + f->n_properties = 1; + f->properties = malloc (sizeof(*f->properties) * f->n_properties); + f->properties[0].name = XA_FONT; + Assert (sizeof (f->properties[0].card32) >= sizeof (char *), + "atoms probably needs a real implementation"); + // If XInternAtom is ever implemented, use it here. + f->properties[0].card32 = (unsigned long)fid->xa_font; + + // copy XCharStruct array + int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1; + f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct)); + + memcpy (f->per_char, fid->metrics.per_char, + size * sizeof (XCharStruct)); + + return f; +} + + +static Font +copy_font (Font fid) +{ + fid->refcount++; + return fid; +} + + +/* On Cocoa and iOS, fonts may be specified as "Georgia Bold 24" instead + of XLFD strings; also they can be comma-separated strings with multiple + font names. First one that exists wins. + */ +static void +try_native_font (Display *dpy, const char *name, Font fid) +{ + if (!name) return; + const char *spc = strrchr (name, ' '); + if (!spc) return; + + char *token = strdup (name); + char *otoken = token; + char *name2; + char *lasts; + + while ((name2 = strtok_r (token, ",", &lasts))) { + token = 0; + + while (*name2 == ' ' || *name2 == '\t' || *name2 == '\n') + name2++; + + spc = strrchr (name2, ' '); + if (!spc) continue; + + int dsize = 0; + if (1 != sscanf (spc, " %d ", &dsize)) + continue; + float size = dsize; + + if (size < 4) continue; + + name2[strlen(name2) - strlen(spc)] = 0; + + fid->native_font = jwxyz_load_native_font(XRootWindow(dpy,0), 0, 0, name2, + strlen(name2) - strlen(spc), + JWXYZ_FONT_FACE, size, NULL, + &fid->ascent, &fid->descent); + if (fid->native_font) { + fid->xa_font = strdup (name); // Maybe this should be an XLFD? + break; + } else { + // To list fonts: + // po [UIFont familyNames] + // po [UIFont fontNamesForFamilyName:@"Arial"] + Log("No native font: \"%s\" %.0f", name2, size); + } + } + + free (otoken); +} + + +static const char * +xlfd_field_end (const char *s) +{ + const char *s2 = strchr(s, '-'); + if (!s2) + s2 = s + strlen(s); + return s2; +} + + +static size_t +xlfd_next (const char **s, const char **s2) +{ + if (!**s2) { + *s = *s2; + } else { + Assert (**s2 == '-', "xlfd parse error"); + *s = *s2 + 1; + *s2 = xlfd_field_end (*s); + } + + return *s2 - *s; +} + + +static void +try_xlfd_font (Display *dpy, const char *name, Font fid) +{ + const char *family_name = NULL; /* Not NULL-terminated. */ + size_t family_name_size = 0; + int require = 0, + // Default mask is for the built-in X11 font aliases. + mask = JWXYZ_STYLE_MONOSPACE | JWXYZ_STYLE_BOLD | JWXYZ_STYLE_ITALIC; + Bool rand = False; + float size = 12; /* In points (1/72 in.) */ + + const char *s = (name ? name : ""); + + size_t L = strlen (s); +# define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L)) +# define UNSPEC (L == 0 || (L == 1 && *s == '*')) + if (CMP ("6x10")) size = 8, require |= JWXYZ_STYLE_MONOSPACE; + else if (CMP ("6x10bold")) size = 8, require |= JWXYZ_STYLE_MONOSPACE | JWXYZ_STYLE_BOLD; + else if (CMP ("fixed")) size = 12, require |= JWXYZ_STYLE_MONOSPACE; + else if (CMP ("9x15")) size = 12, require |= JWXYZ_STYLE_MONOSPACE; + else if (CMP ("9x15bold")) size = 12, require |= JWXYZ_STYLE_MONOSPACE | JWXYZ_STYLE_BOLD; + else if (CMP ("vga")) size = 12, require |= JWXYZ_STYLE_MONOSPACE; + else if (CMP ("console")) size = 12, require |= JWXYZ_STYLE_MONOSPACE; + else if (CMP ("gallant")) size = 12, require |= JWXYZ_STYLE_MONOSPACE; + else { + + int forbid = 0; + + // Incorrect fields are ignored. + + if (*s == '-') + ++s; + const char *s2 = xlfd_field_end(s); + + // Foundry (ignore) + + L = xlfd_next (&s, &s2); // Family name + // This used to substitute Georgia for Times. Now it doesn't. + if (CMP ("random")) { + rand = True; + } else if (CMP ("fixed")) { + require |= JWXYZ_STYLE_MONOSPACE; + family_name = "Courier"; + family_name_size = strlen(family_name); + } else if (!UNSPEC) { + family_name = s; + family_name_size = L; + } + + L = xlfd_next (&s, &s2); // Weight name + if (CMP ("bold") || CMP ("demibold")) + require |= JWXYZ_STYLE_BOLD; + else if (CMP ("medium") || CMP ("regular")) + forbid |= JWXYZ_STYLE_BOLD; + + L = xlfd_next (&s, &s2); // Slant + if (CMP ("i") || CMP ("o")) + require |= JWXYZ_STYLE_ITALIC; + else if (CMP ("r")) + forbid |= JWXYZ_STYLE_ITALIC; + + xlfd_next (&s, &s2); // Set width name (ignore) + xlfd_next (&s, &s2); // Add style name (ignore) + + L = xlfd_next (&s, &s2); // Pixel size + char *s3; + uintmax_t pxsize = strtoumax(s, &s3, 10); + if (UNSPEC || s2 != s3) + pxsize = UINTMAX_MAX; // i.e. it's invalid. + + L = xlfd_next (&s, &s2); // Point size + uintmax_t ptsize = strtoumax(s, &s3, 10); + if (UNSPEC || s2 != s3) + ptsize = UINTMAX_MAX; + + xlfd_next (&s, &s2); // Resolution X (ignore) + xlfd_next (&s, &s2); // Resolution Y (ignore) + + L = xlfd_next (&s, &s2); // Spacing + if (CMP ("p")) + forbid |= JWXYZ_STYLE_MONOSPACE; + else if (CMP ("m") || CMP ("c")) + require |= JWXYZ_STYLE_MONOSPACE; + + xlfd_next (&s, &s2); // Average width (ignore) + + // -*-courier-bold-r-*-*-14-*-*-*-*-*-*-* 14 px + // -*-courier-bold-r-*-*-*-140-*-*-m-*-*-* 14 pt + // -*-courier-bold-r-*-*-140-* 14 pt, via wildcard + // -*-courier-bold-r-*-140-* 14 pt, not handled + // -*-courier-bold-r-*-*-14-180-*-*-*-*-*-* error + + L = xlfd_next (&s, &s2); // Charset registry + if (ptsize != UINTMAX_MAX) { + // It was in the ptsize field, so that's definitely what it is. + size = ptsize / 10.0; + } else if (pxsize != UINTMAX_MAX) { + size = pxsize; + // If it's a fully qualified XLFD, then this really is the pxsize. + // Otherwise, this is probably point size with a multi-field wildcard. + if (L == 0) + size /= 10.0; + } + + mask = require | forbid; + } +# undef CMP +# undef UNSPEC + + if (!family_name && !rand) { + family_name = jwxyz_default_font_family (require); + family_name_size = strlen (family_name); + } + + if (size < 6 || size > 1000) + size = 12; + + char *family_name_ptr = NULL; + fid->native_font = jwxyz_load_native_font (XRootWindow(dpy,0), + require, mask, + family_name, family_name_size, + rand ? JWXYZ_FONT_RANDOM : JWXYZ_FONT_FAMILY, + size, &family_name_ptr, + &fid->ascent, &fid->descent); + + if (fid->native_font) { + unsigned dpi_d = XDisplayHeightMM (dpy,0) * 10 / 2; + unsigned dpi = (254 * XDisplayHeight (dpy,0) + dpi_d) / (2 * dpi_d); + asprintf(&fid->xa_font, "-*-%s-%s-%c-*-*-%u-%u-%u-%u-%c-0-iso10646-1", + family_name_ptr, + (require & JWXYZ_STYLE_BOLD) ? "bold" : "medium", + (require & JWXYZ_STYLE_ITALIC) ? 'o' : 'r', + (unsigned)(dpi * size / 72.27 + 0.5), + (unsigned)(size * 10 + 0.5), dpi, dpi, + (require & JWXYZ_STYLE_MONOSPACE) ? 'm' : 'p'); + } + + free (family_name_ptr); +} + + +Font +XLoadFont (Display *dpy, const char *name) +{ + Font fid = (Font) calloc (1, sizeof(*fid)); + + fid->refcount = 1; + fid->dpy = dpy; + try_native_font (dpy, name, fid); + + if (!fid->native_font && name && + strchr (name, ' ') && + !strchr (name, '*')) { + // If name contains a space but no stars, it is a native font spec -- + // return NULL so that we know it really didn't exist. Else, it is an + // XLFD font, so keep trying. + free (fid); + return 0; + } + + if (! fid->native_font) + try_xlfd_font (dpy, name, fid); + + if (!fid->native_font) { + free (fid); + return 0; + } + + query_font (fid); + + return fid; +} + + +XFontStruct * +XLoadQueryFont (Display *dpy, const char *name) +{ + Font fid = XLoadFont (dpy, name); + if (!fid) return 0; + return XQueryFont (dpy, fid); +} + +int +XUnloadFont (Display *dpy, Font fid) +{ + if (--fid->refcount < 0) abort(); + if (fid->refcount > 0) return 0; + + if (fid->native_font) + jwxyz_release_native_font (fid->dpy, fid->native_font); + + if (fid->metrics.per_char) + free (fid->metrics.per_char); + + free (fid); + return 0; +} + +int +XFreeFontInfo (char **names, XFontStruct *info, int n) +{ + int i; + if (names) { + for (i = 0; i < n; i++) + if (names[i]) free (names[i]); + free (names); + } + if (info) { + for (i = 0; i < n; i++) + if (info[i].per_char) { + free (info[i].per_char); + free (info[i].properties); + } + free (info); + } + return 0; +} + +int +XFreeFont (Display *dpy, XFontStruct *f) +{ + Font fid = f->fid; + XFreeFontInfo (0, f, 1); + XUnloadFont (dpy, fid); + return 0; +} + + +int +XSetFont (Display *dpy, GC gc, Font fid) +{ + XGCValues *gcv = VTBL->gc_gcv(gc); + Font font2 = copy_font (fid); + if (gcv->font) + XUnloadFont (dpy, gcv->font); + gcv->font = font2; + return 0; +} + + +XFontSet +XCreateFontSet (Display *dpy, char *name, + char ***missing_charset_list_return, + int *missing_charset_count_return, + char **def_string_return) +{ + char *name2 = strdup (name); + char *s = strchr (name, ','); + if (s) *s = 0; + XFontSet set = 0; + XFontStruct *f = XLoadQueryFont (dpy, name2); + if (f) + { + set = (XFontSet) calloc (1, sizeof(*set)); + set->font = f; + } + free (name2); + if (missing_charset_list_return) *missing_charset_list_return = 0; + if (missing_charset_count_return) *missing_charset_count_return = 0; + if (def_string_return) *def_string_return = 0; + return set; +} + + +void +XFreeFontSet (Display *dpy, XFontSet set) +{ + XFreeFont (dpy, set->font); + free (set); +} + + +void +XFreeStringList (char **list) +{ + int i; + if (!list) return; + for (i = 0; list[i]; i++) + XFree (list[i]); + XFree (list); +} + + +int +XTextExtents (XFontStruct *f, const char *s, int length, + int *dir_ret, int *ascent_ret, int *descent_ret, + XCharStruct *cs) +{ + // Unfortunately, adding XCharStructs together to get the extents for a + // string doesn't work: Cocoa uses non-integral character advancements, but + // XCharStruct.width is an integer. Plus that doesn't take into account + // kerning pairs, alternate glyphs, and fun stuff like the word "Zapfino" in + // Zapfino. + + Font ff = f->fid; + Display *dpy = ff->dpy; + jwxyz_render_text (dpy, ff->native_font, s, length, False, False, cs, 0); + *dir_ret = 0; + *ascent_ret = f->ascent; + *descent_ret = f->descent; + return 0; +} + +int +XTextWidth (XFontStruct *f, const char *s, int length) +{ + int ascent, descent, dir; + XCharStruct cs; + XTextExtents (f, s, length, &dir, &ascent, &descent, &cs); + return cs.width; +} + + +int +XTextExtents16 (XFontStruct *f, const XChar2b *s, int length, + int *dir_ret, int *ascent_ret, int *descent_ret, + XCharStruct *cs) +{ + // Bool latin1_p = True; + int i, utf8_len = 0; + char *utf8 = XChar2b_to_utf8 (s, &utf8_len); // already sanitized + + for (i = 0; i < length; i++) + if (s[i].byte1 > 0) { + // latin1_p = False; + break; + } + + { + Font ff = f->fid; + Display *dpy = ff->dpy; + jwxyz_render_text (dpy, ff->native_font, utf8, strlen(utf8), + True, False, cs, 0); + } + + *dir_ret = 0; + *ascent_ret = f->ascent; + *descent_ret = f->descent; + free (utf8); + return 0; +} + + +/* "Returns the distance in pixels in the primary draw direction from + the drawing origin to the origin of the next character to be drawn." + + "overall_ink_return is set to the bbox of the string's character ink." + + "The overall_ink_return for a nondescending, horizontally drawn Latin + character is conventionally entirely above the baseline; that is, + overall_ink_return.height <= -overall_ink_return.y." + + [So this means that y is the top of the ink, and height grows down: + For above-the-baseline characters, y is negative.] + + "The overall_ink_return for a nonkerned character is entirely at, and to + the right of, the origin; that is, overall_ink_return.x >= 0." + + [So this means that x is the left of the ink, and width grows right. + For left-of-the-origin characters, x is negative.] + + "A character consisting of a single pixel at the origin would set + overall_ink_return fields y = 0, x = 0, width = 1, and height = 1." + */ +int +Xutf8TextExtents (XFontSet set, const char *str, int len, + XRectangle *overall_ink_return, + XRectangle *overall_logical_return) +{ + XCharStruct cs; + Font f = set->font->fid; + + jwxyz_render_text (f->dpy, f->native_font, str, len, True, False, &cs, + NULL); + + /* "The overall_logical_return is the bounding box that provides minimum + spacing to other graphical features for the string. Other graphical + features, for example, a border surrounding the text, should not + intersect this rectangle." + + So I think that means they're the same? Or maybe "ink" is the bounding + box, and "logical" is the advancement? But then why is the return value + the advancement? + */ + if (overall_ink_return) + XCharStruct_to_XmbRectangle (cs, *overall_ink_return); + if (overall_logical_return) + XCharStruct_to_XmbRectangle (cs, *overall_logical_return); + + return cs.width; +} + + +int +jwxyz_draw_string (Display *dpy, Drawable d, GC gc, int x, int y, + const char *str, size_t len, int utf8_p) +{ + const XGCValues *gcv = VTBL->gc_gcv (gc); + Font ff = gcv->font; + XCharStruct cs; + + char *data = 0; + jwxyz_render_text (dpy, jwxyz_native_font (ff), str, len, utf8_p, + gcv->antialias_p, &cs, &data); + int w = cs.rbearing - cs.lbearing; + int h = cs.ascent + cs.descent; + + if (w < 0 || h < 0) abort(); + if (w == 0 || h == 0) { + if (data) free(data); + return 0; + } + + XImage *img = XCreateImage (dpy, VTBL->visual (dpy), 32, + ZPixmap, 0, data, w, h, 0, 0); + + /* The image of text is a 32-bit image, in white. + Take the green channel for intensity and use that as alpha. + replace RGB with the GC's foreground color. + This expects that XPutImage respects alpha and only writes + the bits that are not masked out. + */ + { +# define ROTL(x, rot) (((x) << ((rot) & 31)) | ((x) >> (32 - ((rot) & 31)))) + + const unsigned long *masks = + DefaultVisualOfScreen (DefaultScreenOfDisplay(dpy))->rgba_masks; + unsigned shift = (i_log2 (masks[3]) - i_log2 (masks[1])) & 31; + uint32_t mask = ROTL(masks[1], shift) & masks[3], + color = gcv->foreground & ~masks[3]; + uint32_t *s = (uint32_t *)data; + uint32_t *end = s + (w * h); + while (s < end) { + + *s = (ROTL(*s, shift) & mask) | color; + ++s; + } + } + + { + Bool old_alpha = gcv->alpha_allowed_p; + jwxyz_XSetAlphaAllowed (dpy, gc, True); + XPutImage (dpy, d, gc, img, 0, 0, + x + cs.lbearing, + y - cs.ascent, + w, h); + jwxyz_XSetAlphaAllowed (dpy, gc, old_alpha); + XDestroyImage (img); + } + + return 0; +} + + +int +XDrawString (Display *dpy, Drawable d, GC gc, int x, int y, + const char *str, int len) +{ + return VTBL->draw_string (dpy, d, gc, x, y, str, len, False); +} + + +int +XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y, + const XChar2b *str, int len) +{ + XChar2b *b2 = malloc ((len + 1) * sizeof(*b2)); + char *s2; + int ret; + memcpy (b2, str, len * sizeof(*b2)); + b2[len].byte1 = b2[len].byte2 = 0; + s2 = XChar2b_to_utf8 (b2, 0); + free (b2); + ret = VTBL->draw_string (dpy, d, gc, x, y, s2, strlen(s2), True); + free (s2); + return ret; +} + + +void +Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc, + int x, int y, const char *str, int len) +{ + VTBL->draw_string (dpy, d, gc, x, y, str, len, True); +} + + +int +XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y, + const char *str, int len) +{ + int ascent, descent, dir; + XCharStruct cs; + XTextExtents (&VTBL->gc_gcv (gc)->font->metrics, str, len, + &dir, &ascent, &descent, &cs); + jwxyz_fill_rect (dpy, d, gc, + x + MIN (0, cs.lbearing), + y - MAX (0, ascent), + + /* The +1 here is almost certainly wrong, but BSOD + requires it; and only BSOD, fluidballs, juggle + and grabclient call XDrawImageString... */ + MAX (MAX (0, cs.rbearing) - + MIN (0, cs.lbearing), + cs.width) + 1, + MAX (0, ascent) + MAX (0, descent), + VTBL->gc_gcv(gc)->background); + return XDrawString (dpy, d, gc, x, y, str, len); +} + + +void * +jwxyz_native_font (Font f) +{ + return f->native_font; +} + + int XSetForeground (Display *dpy, GC gc, unsigned long fg) { - XGCValues *gcv = jwxyz_gc_gcv (gc); - jwxyz_validate_pixel (dpy, fg, jwxyz_gc_depth (gc), gcv->alpha_allowed_p); + XGCValues *gcv = VTBL->gc_gcv (gc); + jwxyz_validate_pixel (dpy, fg, VTBL->gc_depth (gc), gcv->alpha_allowed_p); gcv->foreground = fg; return 0; } @@ -768,8 +1603,8 @@ XSetForeground (Display *dpy, GC gc, unsigned long fg) int XSetBackground (Display *dpy, GC gc, unsigned long bg) { - XGCValues *gcv = jwxyz_gc_gcv (gc); - jwxyz_validate_pixel (dpy, bg, jwxyz_gc_depth (gc), gcv->alpha_allowed_p); + XGCValues *gcv = VTBL->gc_gcv (gc); + jwxyz_validate_pixel (dpy, bg, VTBL->gc_depth (gc), gcv->alpha_allowed_p); gcv->background = bg; return 0; } @@ -777,14 +1612,14 @@ XSetBackground (Display *dpy, GC gc, unsigned long bg) int jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed) { - jwxyz_gc_gcv (gc)->alpha_allowed_p = allowed; + VTBL->gc_gcv (gc)->alpha_allowed_p = allowed; return 0; } int jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p) { - jwxyz_gc_gcv (gc)->antialias_p = antialias_p; + VTBL->gc_gcv (gc)->antialias_p = antialias_p; return 0; } @@ -793,7 +1628,7 @@ int XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width, int line_style, int cap_style, int join_style) { - XGCValues *gcv = jwxyz_gc_gcv (gc); + XGCValues *gcv = VTBL->gc_gcv (gc); gcv->line_width = line_width; Assert (line_style == LineSolid, "only LineSolid implemented"); // gc->gcv.line_style = line_style; @@ -811,14 +1646,14 @@ XSetGraphicsExposures (Display *dpy, GC gc, Bool which) int XSetFunction (Display *dpy, GC gc, int which) { - jwxyz_gc_gcv (gc)->function = which; + VTBL->gc_gcv (gc)->function = which; return 0; } int XSetSubwindowMode (Display *dpy, GC gc, int which) { - jwxyz_gc_gcv (gc)->subwindow_mode = which; + VTBL->gc_gcv (gc)->subwindow_mode = which; return 0; } @@ -933,7 +1768,7 @@ visual_depth (Screen *s, Visual *v) int visual_cells (Screen *s, Visual *v) { - return (int)(v->red_mask | v->green_mask | v->blue_mask); + return (int)(v->rgba_masks[0] | v->rgba_masks[1] | v->rgba_masks[2]); } int @@ -942,22 +1777,24 @@ visual_class (Screen *s, Visual *v) return TrueColor; } +void +visual_rgb_masks (Screen *s, Visual *v, unsigned long *red_mask, + unsigned long *green_mask, unsigned long *blue_mask) +{ + *red_mask = v->rgba_masks[0]; + *green_mask = v->rgba_masks[1]; + *blue_mask = v->rgba_masks[2]; +} + int -get_bits_per_pixel (Display *dpy, int depth) +visual_pixmap_depth (Screen *s, Visual *v) { - Assert (depth == 32 || depth == 1, "unexpected depth"); - return depth; + return 32; } int screen_number (Screen *screen) { - Display *dpy = DisplayOfScreen (screen); - int i; - for (i = 0; i < ScreenCount (dpy); i++) - if (ScreenOfDisplay (dpy, i) == screen) - return i; - abort (); return 0; }