From http://www.jwz.org/xscreensaver/xscreensaver-5.38.tar.gz
[xscreensaver] / jwxyz / jwxyz-common.c
1 /* xscreensaver, Copyright (c) 1991-2016 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 /* JWXYZ Is Not Xlib.
13
14    But it's a bunch of function definitions that bear some resemblance to
15    Xlib and that do Cocoa-ish or OpenGL-ish things that bear some resemblance
16    to the things that Xlib might have done.
17
18    This is the version of jwxyz for Android.  The version used by MacOS
19    and iOS is in jwxyz.m.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25
26 #ifdef HAVE_JWXYZ /* whole file */
27
28 #include <stdarg.h>
29 #include <stdio.h>
30
31 #include "jwxyzI.h"
32 #include "pow2.h"
33 #include "utf8wc.h"
34 #include "xft.h"
35
36 /* There's only one Window for a given jwxyz_Display. */
37 #define assert_window(dpy, w) \
38   Assert (w == RootWindow (dpy, 0), "not a window")
39
40 #define VTBL JWXYZ_VTBL(dpy)
41
42 struct jwxyz_Font {
43   Display *dpy;
44   void *native_font;
45   int refcount; // for deciding when to release the native font
46   int ascent, descent;
47   char *xa_font;
48
49   // In X11, "Font" is just an ID, and "XFontStruct" contains the metrics.
50   // But we need the metrics on both of them, so they go here.
51   XFontStruct metrics;
52 };
53
54 struct jwxyz_XFontSet {
55   XFontStruct *font;
56 };
57
58
59 void
60 Log (const char *fmt, ...)
61 {
62   va_list args;
63   va_start (args, fmt);
64   Logv (fmt, args);
65   va_end (args);
66 }
67
68
69 int
70 XDisplayWidth (Display *dpy, int screen)
71 {
72   return jwxyz_frame (XRootWindow (dpy, 0))->width;
73 }
74
75 int
76 XDisplayHeight (Display *dpy, int screen)
77 {
78   return jwxyz_frame (XRootWindow (dpy, 0))->height;
79 }
80
81
82 /* XLFDs use dots per inch, but Xlib uses millimeters. Go figure. */
83 static int
84 size_mm (Display *dpy, unsigned size)
85 {
86   /* ((mm / inch) / (points / inch)) * dots / (dots / points) */
87   return (25.4 / 72) * size / jwxyz_scale (XRootWindow (dpy,0)) + 0.5;
88 }
89
90 int
91 XDisplayWidthMM (Display *dpy, int screen)
92 {
93   return size_mm (dpy, XDisplayWidth (dpy, screen));
94 }
95
96 int
97 XDisplayHeightMM (Display *dpy, int screen)
98 {
99   return size_mm (dpy, XDisplayHeight (dpy, screen));
100 }
101
102 unsigned long
103 XBlackPixelOfScreen(Screen *screen)
104 {
105   return DefaultVisualOfScreen (screen)->rgba_masks[3];
106 }
107
108 unsigned long
109 XWhitePixelOfScreen(Screen *screen)
110 {
111   const unsigned long *masks = DefaultVisualOfScreen (screen)->rgba_masks;
112   return masks[0] | masks[1] | masks[2] | masks[3];
113 }
114
115 unsigned long
116 XCellsOfScreen(Screen *screen)
117 {
118   const unsigned long *masks = DefaultVisualOfScreen (screen)->rgba_masks;
119   return masks[0] | masks[1] | masks[2];
120 }
121
122 void
123 jwxyz_validate_pixel (Display *dpy, unsigned long pixel, unsigned int depth,
124                       Bool alpha_allowed_p)
125 {
126   Assert (depth == 1 || depth == visual_depth(NULL, NULL),
127           "invalid depth: %d", depth);
128
129   if (depth == 1)
130     Assert ((pixel == 0 || pixel == 1), "bogus mono  pixel: 0x%08X", pixel);
131   else if (!alpha_allowed_p)
132     Assert (((pixel & BlackPixel(dpy,0)) == BlackPixel(dpy,0)),
133             "bogus color pixel: 0x%08X", pixel);
134 }
135
136
137 int
138 XDrawPoint (Display *dpy, Drawable d, GC gc, int x, int y)
139 {
140   XPoint p;
141   p.x = x;
142   p.y = y;
143   return XDrawPoints (dpy, d, gc, &p, 1, CoordModeOrigin);
144 }
145
146
147 Bool
148 jwxyz_dumb_drawing_mode(Display *dpy, Drawable d, GC gc,
149                         int x, int y, unsigned width, unsigned height)
150 {
151   XGCValues *gcv = VTBL->gc_gcv (gc);
152
153   if (gcv->function == GXset || gcv->function == GXclear) {
154     // "set" and "clear" are dumb drawing modes that ignore the source
155     // bits and just draw solid rectangles.
156     unsigned depth = VTBL->gc_depth (gc);
157     jwxyz_fill_rect (dpy, d, 0, x, y, width, height,
158                      (gcv->function == GXset
159                       ? (depth == 1 ? 1 : WhitePixel(dpy,0))
160                       : (depth == 1 ? 0 : BlackPixel(dpy,0))));
161     return True;
162   }
163
164   return False;
165 }
166
167
168 int
169 XCopyArea (Display *dpy, Drawable src, Drawable dst, GC gc, 
170            int src_x, int src_y, 
171            unsigned int width, unsigned int height, 
172            int dst_x, int dst_y)
173 {
174   Assert (gc, "no GC");
175   Assert ((width  < 65535), "improbably large width");
176   Assert ((height < 65535), "improbably large height");
177   Assert ((src_x  < 65535 && src_x  > -65535), "improbably large src_x");
178   Assert ((src_y  < 65535 && src_y  > -65535), "improbably large src_y");
179   Assert ((dst_x  < 65535 && dst_x  > -65535), "improbably large dst_x");
180   Assert ((dst_y  < 65535 && dst_y  > -65535), "improbably large dst_y");
181
182   if (width == 0 || height == 0)
183     return 0;
184
185   if (jwxyz_dumb_drawing_mode (dpy, dst, gc, dst_x, dst_y, width, height))
186     return 0;
187
188   XRectangle src_frame, dst_frame; // Sizes and origins of the two drawables
189   Bool clipped = False;            // Whether we did any clipping of the rects.
190
191   src_frame = *jwxyz_frame (src);
192   dst_frame = *jwxyz_frame (dst);
193   
194   // Initialize src_rect...
195   //
196   src_x += src_frame.x;
197   src_y += src_frame.y;
198   if (src_y < -65535) Assert(0, "src.origin.y went nuts");
199
200   // Initialize dst_rect...
201   //
202   dst_x += dst_frame.x;
203   dst_y += dst_frame.y;
204   if (dst_y < -65535) Assert(0, "dst.origin.y went nuts");
205
206   // Use signed width and height for this...
207   int width0 = width, height0 = height;
208
209   // Clip rects to frames...
210   //
211
212 # define CLIP(THIS,THAT,VAL,SIZE) do { \
213   int off = THIS##_##VAL; \
214   if (off < 0) { \
215     clipped = True; \
216     SIZE##0      += off; \
217     THIS##_##VAL -= off; \
218     THAT##_##VAL -= off; \
219   } \
220   off = (( THIS##_##VAL +  SIZE##0) - \
221          (THIS##_frame.VAL + THIS##_frame.SIZE)); \
222   if (off > 0) { \
223     clipped = True; \
224     SIZE##0 -= off; \
225   }} while(0)
226
227   CLIP (dst, src, x, width);
228   CLIP (dst, src, y, height);
229
230   // Not actually the original dst_rect, just the one before it's clipped to
231   // the src_frame.
232   int orig_dst_x = dst_x;
233   int orig_dst_y = dst_y;
234   int orig_width  = width0;
235   int orig_height = height0;
236
237   if (width0 <= 0 || height0 <= 0)
238     return 0;
239
240   CLIP (src, dst, x, width);
241   CLIP (src, dst, y, height);
242 # undef CLIP
243
244   // Sort-of-special case where no pixels can be grabbed from the source,
245   // and the whole destination is filled with the background color.
246   if (width0 <= 0 || height0 <= 0) {
247     width0  = 0;
248     height0 = 0;
249   } else {
250     VTBL->copy_area (dpy, src, dst, gc,
251                      src_x, src_y, width0, height0, dst_x, dst_y);
252   }
253
254   // If either the src or dst rects did not lie within their drawables, then
255   // we have adjusted both the src and dst rects to account for the clipping;
256   // that means we need to clear to the background, so that clipped bits end
257   // up in the bg color instead of simply not being copied.
258   //
259   // This has to happen after the copy, because if it happens before, the
260   // cleared area will get grabbed if it overlaps with the source rectangle.
261   //
262   if (clipped && dst == XRootWindow (dpy,0)) {
263     int dst_x0 = dst_x;
264     int dst_y0 = dst_y;
265
266     Assert (orig_dst_x >= 0 &&
267             orig_dst_x + orig_width  <= dst_frame.width &&
268             orig_dst_y >= 0 &&
269             orig_dst_y + orig_height <= dst_frame.height,
270             "wrong dimensions");
271
272     XRectangle rects[4];
273     XRectangle *rects_end = rects;
274
275     if (orig_dst_y < dst_y0) {
276       rects_end->x = orig_dst_x;
277       rects_end->y = orig_dst_y;
278       rects_end->width = orig_width;
279       rects_end->height = dst_y0 - orig_dst_y;
280       ++rects_end;
281     }
282
283     if (orig_dst_y + orig_height > dst_y0 + height0) {
284       rects_end->x = orig_dst_x;
285       rects_end->y = dst_y0 + height0;
286       rects_end->width = orig_width;
287       rects_end->height = orig_dst_y + orig_height - dst_y0 - height0;
288       ++rects_end;
289     }
290
291     if (orig_dst_x < dst_x0) {
292       rects_end->x = orig_dst_x;
293       rects_end->y = dst_y0;
294       rects_end->width = dst_x0 - orig_dst_x;
295       rects_end->height = height0;
296       ++rects_end;
297     }
298
299     if (dst_x0 + width0 < orig_dst_x + orig_width) {
300       rects_end->x = dst_x0 + width0;
301       rects_end->y = dst_y0;
302       rects_end->width = orig_dst_x + orig_width - dst_x0 - width0;
303       rects_end->height = height0;
304       ++rects_end;
305     }
306
307     XGCValues *gcv = VTBL->gc_gcv (gc);
308     int old_function = gcv->function;
309     gcv->function = GXcopy;
310     VTBL->fill_rects (dpy, dst, gc, rects, rects_end - rects,
311                       *VTBL->window_background (dpy));
312     gcv->function = old_function;
313   }
314
315   return 0;
316 }
317
318
319 void
320 jwxyz_blit (const void *src_data, ptrdiff_t src_pitch,
321             unsigned src_x, unsigned src_y,
322             void *dst_data, ptrdiff_t dst_pitch,
323             unsigned dst_x, unsigned dst_y,
324             unsigned width, unsigned height)
325 {
326   Bool same = src_data == dst_data;
327   src_data = SEEK_XY (src_data, src_pitch, src_x, src_y);
328   dst_data = SEEK_XY (dst_data, dst_pitch, dst_x, dst_y);
329
330   size_t bytes = width * 4;
331
332   if (same && dst_y > src_y) {
333     // Copy upwards if the areas might overlap.
334     src_data += src_pitch * (height - 1);
335     dst_data += dst_pitch * (height - 1);
336     src_pitch = -src_pitch;
337     dst_pitch = -dst_pitch;
338   }
339
340   while (height) {
341     // memcpy is an alias for memmove on macOS.
342     memmove (dst_data, src_data, bytes);
343     src_data += src_pitch;
344     dst_data += dst_pitch;
345     --height;
346   }
347 }
348
349
350 int
351 XCopyPlane (Display *dpy, Drawable src, Drawable dest, GC gc,
352             int src_x, int src_y,
353             unsigned width, int height,
354             int dest_x, int dest_y, unsigned long plane)
355 {
356   Assert ((VTBL->gc_depth (gc) == 1 || plane == 1), "hairy plane mask!");
357   
358   // This isn't right: XCopyPlane() is supposed to map 1/0 to fg/bg,
359   // not to white/black.
360   return XCopyArea (dpy, src, dest, gc,
361                     src_x, src_y, width, height, dest_x, dest_y);
362 }
363
364
365 int
366 XDrawLine (Display *dpy, Drawable d, GC gc, int x1, int y1, int x2, int y2)
367 {
368   XSegment segment;
369   segment.x1 = x1;
370   segment.y1 = y1;
371   segment.x2 = x2;
372   segment.y2 = y2;
373   XDrawSegments (dpy, d, gc, &segment, 1);
374   return 0;
375 }
376
377
378 int
379 XSetWindowBackground (Display *dpy, Window w, unsigned long pixel)
380 {
381   Assert (w == XRootWindow (dpy,0), "not a window");
382   jwxyz_validate_pixel (dpy, pixel, visual_depth (NULL, NULL), False);
383   *VTBL->window_background (dpy) = pixel;
384   return 0;
385 }
386
387 void
388 jwxyz_fill_rect (Display *dpy, Drawable d, GC gc,
389                  int x, int y, unsigned int width, unsigned int height,
390                  unsigned long pixel)
391 {
392   XRectangle r = {x, y, width, height};
393   VTBL->fill_rects (dpy, d, gc, &r, 1, pixel);
394 }
395
396 int
397 XFillRectangle (Display *dpy, Drawable d, GC gc, int x, int y, 
398                 unsigned int width, unsigned int height)
399 {
400   jwxyz_fill_rect (dpy, d, gc, x, y, width, height,
401                    VTBL->gc_gcv (gc)->foreground);
402   return 0;
403 }
404
405 int
406 XDrawRectangle (Display *dpy, Drawable d, GC gc, int x, int y, 
407                 unsigned int width, unsigned int height)
408 {
409   XPoint points[5] = {
410     {x, y},
411     {x, y + height},
412     {x + width, y + height},
413     {x + width, y},
414     {x, y}
415   };
416
417   XDrawLines(dpy, d, gc, points, 5, CoordModeOrigin);
418   return 0;
419 }
420
421 int
422 XFillRectangles (Display *dpy, Drawable d, GC gc, XRectangle *rects, int n)
423 {
424   VTBL->fill_rects (dpy, d, gc, rects, n, VTBL->gc_gcv (gc)->foreground);
425   return 0;
426 }
427
428
429 int
430 XClearArea (Display *dpy, Window win, int x, int y, int w, int h, Bool exp)
431 {
432   Assert(win == XRootWindow(dpy,0), "XClearArea: not a window");
433   Assert(!exp, "XClearArea: exposures unsupported");
434   jwxyz_fill_rect (dpy, win, 0, x, y, w, h, *VTBL->window_background (dpy));
435   return 0;
436 }
437
438
439 int
440 XDrawArc (Display *dpy, Drawable d, GC gc, int x, int y,
441           unsigned int width, unsigned int height, int angle1, int angle2)
442 {
443   return VTBL->draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2,
444                          False);
445 }
446
447 int
448 XFillArc (Display *dpy, Drawable d, GC gc, int x, int y,
449           unsigned int width, unsigned int height, int angle1, int angle2)
450 {
451   return VTBL->draw_arc (dpy, d, gc, x, y, width, height, angle1, angle2,
452                          True);
453 }
454
455 int
456 XDrawArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
457 {
458   int i;
459   for (i = 0; i < narcs; i++)
460     VTBL->draw_arc (dpy, d, gc,
461                     arcs[i].x, arcs[i].y,
462                     arcs[i].width, arcs[i].height,
463                     arcs[i].angle1, arcs[i].angle2,
464                     False);
465   return 0;
466 }
467
468 int
469 XFillArcs (Display *dpy, Drawable d, GC gc, XArc *arcs, int narcs)
470 {
471   int i;
472   for (i = 0; i < narcs; i++)
473     VTBL->draw_arc (dpy, d, gc,
474                     arcs[i].x, arcs[i].y,
475                     arcs[i].width, arcs[i].height,
476                     arcs[i].angle1, arcs[i].angle2,
477                     True);
478   return 0;
479 }
480
481 void
482 jwxyz_gcv_defaults (Display *dpy, XGCValues *gcv, int depth)
483 {
484   memset (gcv, 0, sizeof(*gcv));
485   gcv->function   = GXcopy;
486   gcv->foreground = (depth == 1 ? 1 : WhitePixel(dpy,0));
487   gcv->background = (depth == 1 ? 0 : BlackPixel(dpy,0));
488   gcv->line_width = 1;
489   gcv->cap_style  = CapButt;
490   gcv->join_style = JoinMiter;
491   gcv->fill_rule  = EvenOddRule;
492
493   gcv->alpha_allowed_p = False;
494   gcv->antialias_p     = True;
495 }
496
497
498 int
499 XChangeGC (Display *dpy, GC gc, unsigned long mask, XGCValues *from)
500 {
501   if (! mask) return 0;
502   Assert (gc && from, "no gc");
503   if (!gc || !from) return 0;
504
505   XGCValues *to = VTBL->gc_gcv (gc);
506   unsigned depth = VTBL->gc_depth (gc);
507
508   if (mask & GCFunction)        to->function            = from->function;
509   if (mask & GCForeground)      to->foreground          = from->foreground;
510   if (mask & GCBackground)      to->background          = from->background;
511   if (mask & GCLineWidth)       to->line_width          = from->line_width;
512   if (mask & GCCapStyle)        to->cap_style           = from->cap_style;
513   if (mask & GCJoinStyle)       to->join_style          = from->join_style;
514   if (mask & GCFillRule)        to->fill_rule           = from->fill_rule;
515   if (mask & GCClipXOrigin)     to->clip_x_origin       = from->clip_x_origin;
516   if (mask & GCClipYOrigin)     to->clip_y_origin       = from->clip_y_origin;
517   if (mask & GCSubwindowMode)   to->subwindow_mode      = from->subwindow_mode;
518
519   if (mask & GCClipMask)        XSetClipMask (dpy, gc, from->clip_mask);
520   if (mask & GCFont)            XSetFont (dpy, gc, from->font);
521
522   if (mask & GCForeground)
523     jwxyz_validate_pixel (dpy, from->foreground, depth, to->alpha_allowed_p);
524   if (mask & GCBackground)
525     jwxyz_validate_pixel (dpy, from->background, depth, to->alpha_allowed_p);
526
527   Assert ((! (mask & (GCLineStyle |
528                       GCPlaneMask |
529                       GCFillStyle |
530                       GCTile |
531                       GCStipple |
532                       GCTileStipXOrigin |
533                       GCTileStipYOrigin |
534                       GCGraphicsExposures |
535                       GCDashOffset |
536                       GCDashList |
537                       GCArcMode))),
538           "unimplemented gcvalues mask");
539
540   return 0;
541 }
542
543
544 Status
545 XGetWindowAttributes (Display *dpy, Window w, XWindowAttributes *xgwa)
546 {
547   assert_window(dpy, w);
548   memset (xgwa, 0, sizeof(*xgwa));
549   const XRectangle *frame = jwxyz_frame (w);
550   xgwa->x      = frame->x;
551   xgwa->y      = frame->y;
552   xgwa->width  = frame->width;
553   xgwa->height = frame->height;
554   xgwa->depth  = visual_depth (NULL, NULL);
555   xgwa->screen = DefaultScreenOfDisplay (dpy);
556   xgwa->visual = XDefaultVisualOfScreen (xgwa->screen);
557   return 0;
558 }
559
560 Status
561 XGetGeometry (Display *dpy, Drawable d, Window *root_ret,
562               int *x_ret, int *y_ret,
563               unsigned int *w_ret, unsigned int *h_ret,
564               unsigned int *bw_ret, unsigned int *d_ret)
565 {
566   const XRectangle *frame = jwxyz_frame (d);
567   *x_ret    = frame->x;
568   *y_ret    = frame->y;
569   *w_ret    = frame->width;
570   *h_ret    = frame->height;
571   *d_ret    = jwxyz_drawable_depth (d);
572   *root_ret = RootWindow (dpy, 0);
573   *bw_ret   = 0;
574   return True;
575 }
576
577
578 Status
579 XAllocColor (Display *dpy, Colormap cmap, XColor *color)
580 {
581   const unsigned long *masks =
582     DefaultVisualOfScreen(DefaultScreenOfDisplay(dpy))->rgba_masks;
583   color->pixel =
584     (((color->red   << 16) >> (31 - i_log2(masks[0]))) & masks[0]) |
585     (((color->green << 16) >> (31 - i_log2(masks[1]))) & masks[1]) |
586     (((color->blue  << 16) >> (31 - i_log2(masks[2]))) & masks[2]) |
587     masks[3];
588   return 1;
589 }
590
591 Status
592 XAllocColorCells (Display *dpy, Colormap cmap, Bool contig,
593                   unsigned long *pmret, unsigned int npl,
594                   unsigned long *pxret, unsigned int npx)
595 {
596   return 0;
597 }
598
599 int
600 XStoreColors (Display *dpy, Colormap cmap, XColor *colors, int n)
601 {
602   Assert(0, "XStoreColors called");
603   return 0;
604 }
605
606 int
607 XStoreColor (Display *dpy, Colormap cmap, XColor *c)
608 {
609   Assert(0, "XStoreColor called");
610   return 0;
611 }
612
613 int
614 XFreeColors (Display *dpy, Colormap cmap, unsigned long *px, int npixels,
615              unsigned long planes)
616 {
617   return 0;
618 }
619
620 Status
621 XParseColor (Display *dpy, Colormap cmap, const char *spec, XColor *ret)
622 {
623   unsigned char r=0, g=0, b=0;
624   if (*spec == '#' && strlen(spec) == 7) {
625     static unsigned const char hex[] = {   // yeah yeah, shoot me.
626       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
627       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,
628       0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
629       0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
630       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
631       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
632       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
633       0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
634     const unsigned char *uspec = (const unsigned char *)spec;
635     r = (hex[uspec[1]] << 4) | hex[uspec[2]];
636     g = (hex[uspec[3]] << 4) | hex[uspec[4]];
637     b = (hex[uspec[5]] << 4) | hex[uspec[6]];
638   } else if (!strcasecmp(spec,"black")) {
639     //  r = g = b = 0;
640   } else if (!strcasecmp(spec,"white")) {
641     r = g = b = 255;
642   } else if (!strcasecmp(spec,"red")) {
643     r = 255;
644   } else if (!strcasecmp(spec,"green")) {
645     g = 255;
646   } else if (!strcasecmp(spec,"blue")) {
647     b = 255;
648   } else if (!strcasecmp(spec,"cyan")) {
649     g = b = 255;
650   } else if (!strcasecmp(spec,"magenta")) {
651     r = b = 255;
652   } else if (!strcasecmp(spec,"yellow")) {
653     r = g = 255;
654   } else {
655     return 0;
656   }
657
658   ret->red   = (r << 8) | r;
659   ret->green = (g << 8) | g;
660   ret->blue  = (b << 8) | b;
661   ret->flags = DoRed|DoGreen|DoBlue;
662   return 1;
663 }
664
665 Status
666 XAllocNamedColor (Display *dpy, Colormap cmap, char *name,
667                   XColor *screen_ret, XColor *exact_ret)
668 {
669   if (! XParseColor (dpy, cmap, name, screen_ret))
670     return False;
671   *exact_ret = *screen_ret;
672   return XAllocColor (dpy, cmap, screen_ret);
673 }
674
675 int
676 XQueryColor (Display *dpy, Colormap cmap, XColor *color)
677 {
678   jwxyz_validate_pixel (dpy, color->pixel, visual_depth (NULL, NULL), False);
679   uint16_t rgba[4];
680   JWXYZ_QUERY_COLOR (dpy, color->pixel, 0xffffull, rgba);
681   color->red   = rgba[0];
682   color->green = rgba[1];
683   color->blue  = rgba[2];
684   color->flags = DoRed|DoGreen|DoBlue;
685   return 0;
686 }
687
688 int
689 XQueryColors (Display *dpy, Colormap cmap, XColor *c, int n)
690 {
691   int i;
692   for (i = 0; i < n; i++)
693     XQueryColor (dpy, cmap, &c[i]);
694   return 0;
695 }
696
697
698 static unsigned long
699 ximage_getpixel_1 (XImage *ximage, int x, int y)
700 {
701   return ((ximage->data [y * ximage->bytes_per_line + (x>>3)] >> (x & 7)) & 1);
702 }
703
704 static int
705 ximage_putpixel_1 (XImage *ximage, int x, int y, unsigned long pixel)
706 {
707   if (pixel)
708     ximage->data [y * ximage->bytes_per_line + (x>>3)] |=  (1 << (x & 7));
709   else
710     ximage->data [y * ximage->bytes_per_line + (x>>3)] &= ~(1 << (x & 7));
711
712   return 0;
713 }
714
715 static unsigned long
716 ximage_getpixel_32 (XImage *ximage, int x, int y)
717 {
718   return ((unsigned long)
719           *((uint32_t *) ximage->data +
720             (y * (ximage->bytes_per_line >> 2)) +
721             x));
722 }
723
724 static int
725 ximage_putpixel_32 (XImage *ximage, int x, int y, unsigned long pixel)
726 {
727   *((uint32_t *) ximage->data +
728     (y * (ximage->bytes_per_line >> 2)) +
729     x) = (uint32_t) pixel;
730   return 0;
731 }
732
733
734 Status
735 XInitImage (XImage *ximage)
736 {
737   if (!ximage->bytes_per_line)
738     ximage->bytes_per_line = (ximage->depth == 1
739                               ? (ximage->width + 7) / 8
740                               : ximage->width * 4);
741
742   if (ximage->depth == 1) {
743     ximage->f.put_pixel = ximage_putpixel_1;
744     ximage->f.get_pixel = ximage_getpixel_1;
745   } else if (ximage->depth == 32 || ximage->depth == 24) {
746     ximage->f.put_pixel = ximage_putpixel_32;
747     ximage->f.get_pixel = ximage_getpixel_32;
748   } else {
749     Assert (0, "unknown depth");
750   }
751   return 1;
752 }
753
754
755 XImage *
756 XCreateImage (Display *dpy, Visual *visual, unsigned int depth,
757               int format, int offset, char *data,
758               unsigned int width, unsigned int height,
759               int bitmap_pad, int bytes_per_line)
760 {
761   XImage *ximage = (XImage *) calloc (1, sizeof(*ximage));
762   ximage->width = width;
763   ximage->height = height;
764   ximage->format = format;
765   ximage->data = data;
766   ximage->bitmap_unit = 8;
767   ximage->byte_order = LSBFirst;
768   ximage->bitmap_bit_order = ximage->byte_order;
769   ximage->bitmap_pad = bitmap_pad;
770   ximage->depth = depth;
771   Visual *v = DefaultVisualOfScreen (DefaultScreenOfDisplay (dpy));
772   ximage->red_mask   = (depth == 1 ? 0 : v->rgba_masks[0]);
773   ximage->green_mask = (depth == 1 ? 0 : v->rgba_masks[1]);
774   ximage->blue_mask  = (depth == 1 ? 0 : v->rgba_masks[2]);
775   ximage->bits_per_pixel = (depth == 1 ? 1 : visual_depth (NULL, NULL));
776   ximage->bytes_per_line = bytes_per_line;
777
778   XInitImage (ximage);
779   return ximage;
780 }
781
782 XImage *
783 XSubImage (XImage *from, int x, int y, unsigned int w, unsigned int h)
784 {
785   XImage *to = (XImage *) malloc (sizeof(*to));
786   memcpy (to, from, sizeof(*from));
787   to->width = w;
788   to->height = h;
789   to->bytes_per_line = 0;
790   XInitImage (to);
791
792   to->data = (char *) malloc (h * to->bytes_per_line);
793
794   if (x >= from->width)
795     w = 0;
796   else if (x+w > from->width)
797     w = from->width - x;
798
799   if (y >= from->height)
800     h = 0;
801   else if (y+h > from->height)
802     h = from->height - y;
803
804   int tx, ty;
805   for (ty = 0; ty < h; ty++)
806     for (tx = 0; tx < w; tx++)
807       XPutPixel (to, tx, ty, XGetPixel (from, x+tx, y+ty));
808   return to;
809 }
810
811
812 XPixmapFormatValues *
813 XListPixmapFormats (Display *dpy, int *n_ret)
814 {
815   XPixmapFormatValues *ret = calloc (2, sizeof(*ret));
816   ret[0].depth = visual_depth (NULL, NULL);
817   ret[0].bits_per_pixel = 32;
818   ret[0].scanline_pad = 8;
819   ret[1].depth = 1;
820   ret[1].bits_per_pixel = 1;
821   ret[1].scanline_pad = 8;
822   *n_ret = 2;
823   return ret;
824 }
825
826
827 unsigned long
828 XGetPixel (XImage *ximage, int x, int y)
829 {
830   return ximage->f.get_pixel (ximage, x, y);
831 }
832
833
834 int
835 XPutPixel (XImage *ximage, int x, int y, unsigned long pixel)
836 {
837   return ximage->f.put_pixel (ximage, x, y, pixel);
838 }
839
840 int
841 XDestroyImage (XImage *ximage)
842 {
843   if (ximage->data) free (ximage->data);
844   free (ximage);
845   return 0;
846 }
847
848
849 XImage *
850 XGetImage (Display *dpy, Drawable d, int x, int y,
851            unsigned int width, unsigned int height,
852            unsigned long plane_mask, int format)
853 {
854   unsigned depth = jwxyz_drawable_depth (d);
855   XImage *image = XCreateImage (dpy, 0, depth, format, 0, 0, width, height,
856                                 0, 0);
857   image->data = (char *) malloc (height * image->bytes_per_line);
858
859   return XGetSubImage (dpy, d, x, y, width, height, plane_mask, format,
860                        image, 0, 0);
861 }
862
863
864 Pixmap
865 XCreatePixmapFromBitmapData (Display *dpy, Drawable drawable,
866                              const char *data,
867                              unsigned int w, unsigned int h,
868                              unsigned long fg, unsigned long bg,
869                              unsigned int depth)
870 {
871   Pixmap p = XCreatePixmap (dpy, drawable, w, h, depth);
872   XImage *image = XCreateImage (dpy, 0, 1, XYPixmap, 0,
873                                 (char *) data, w, h, 0, 0);
874   XGCValues gcv;
875   gcv.foreground = fg;
876   gcv.background = bg;
877   GC gc = XCreateGC (dpy, p, GCForeground|GCBackground, &gcv);
878   XPutImage (dpy, p, gc, image, 0, 0, 0, 0, w, h);
879   XFreeGC (dpy, gc);
880   image->data = 0;
881   XDestroyImage (image);
882   return p;
883 }
884
885
886 char *
887 XGetAtomName (Display *dpy, Atom atom)
888 {
889   if (atom == XA_FONT)
890     return strdup ("FONT");
891
892   // Note that atoms (that aren't predefined) are just char *.
893   return strdup ((char *) atom);
894 }
895
896
897 // This is XQueryFont, but for the XFontStruct embedded in 'Font'
898 //
899 static void
900 query_font (Font fid)
901 {
902   Assert (fid && fid->native_font, "no native font in fid");
903
904   int first = 32;
905   int last = 255;
906
907   Display *dpy = fid->dpy;
908   void *native_font = fid->native_font;
909
910   XFontStruct *f = &fid->metrics;
911   XCharStruct *min = &f->min_bounds;
912   XCharStruct *max = &f->max_bounds;
913
914   f->fid               = fid;
915   f->min_char_or_byte2 = first;
916   f->max_char_or_byte2 = last;
917   f->default_char      = 'M';
918   f->ascent            = fid->ascent;
919   f->descent           = fid->descent;
920
921   min->width    = 32767;  // set to smaller values in the loop
922   min->ascent   = 32767;
923   min->descent  = 32767;
924   min->lbearing = 32767;
925   min->rbearing = 32767;
926
927   f->per_char = (XCharStruct *) calloc (last-first+2, sizeof (XCharStruct));
928
929   for (int i = first; i <= last; i++) {
930     XCharStruct *cs = &f->per_char[i-first];
931     char s = (char) i;
932     jwxyz_render_text (dpy, native_font, &s, 1, False, False, cs, 0);
933
934     max->width    = MAX (max->width,    cs->width);
935     max->ascent   = MAX (max->ascent,   cs->ascent);
936     max->descent  = MAX (max->descent,  cs->descent);
937     max->lbearing = MAX (max->lbearing, cs->lbearing);
938     max->rbearing = MAX (max->rbearing, cs->rbearing);
939
940     min->width    = MIN (min->width,    cs->width);
941     min->ascent   = MIN (min->ascent,   cs->ascent);
942     min->descent  = MIN (min->descent,  cs->descent);
943     min->lbearing = MIN (min->lbearing, cs->lbearing);
944     min->rbearing = MIN (min->rbearing, cs->rbearing);
945 /*
946     Log (" %3d %c: w=%3d lb=%3d rb=%3d as=%3d ds=%3d "
947          " bb=%5.1f x %5.1f @ %5.1f %5.1f  adv=%5.1f %5.1f\n"
948          i, i, cs->width, cs->lbearing, cs->rbearing,
949          cs->ascent, cs->descent,
950          bbox.size.width, bbox.size.height,
951          bbox.origin.x, bbox.origin.y,
952          advancement.width, advancement.height);
953  */
954   }
955 }
956
957
958 // Since 'Font' includes the metrics, this just makes a copy of that.
959 //
960 XFontStruct *
961 XQueryFont (Display *dpy, Font fid)
962 {
963   // copy XFontStruct
964   XFontStruct *f = (XFontStruct *) calloc (1, sizeof(*f));
965   *f = fid->metrics;
966   f->fid = fid;
967
968   // build XFontProps
969   f->n_properties = 1;
970   f->properties = malloc (sizeof(*f->properties) * f->n_properties);
971   f->properties[0].name = XA_FONT;
972   Assert (sizeof (f->properties[0].card32) >= sizeof (char *),
973           "atoms probably needs a real implementation");
974   // If XInternAtom is ever implemented, use it here.
975   f->properties[0].card32 = (unsigned long)fid->xa_font;
976
977   // copy XCharStruct array
978   int size = (f->max_char_or_byte2 - f->min_char_or_byte2) + 1;
979   f->per_char = (XCharStruct *) calloc (size + 2, sizeof (XCharStruct));
980
981   memcpy (f->per_char, fid->metrics.per_char,
982           size * sizeof (XCharStruct));
983
984   return f;
985 }
986
987
988 static Font
989 copy_font (Font fid)
990 {
991   fid->refcount++;
992   return fid;
993 }
994
995
996 /* On Cocoa and iOS, fonts may be specified as "Georgia Bold 24" instead
997    of XLFD strings; also they can be comma-separated strings with multiple
998    font names.  First one that exists wins.
999  */
1000 static void
1001 try_native_font (Display *dpy, const char *name, Font fid)
1002 {
1003   if (!name) return;
1004   const char *spc = strrchr (name, ' ');
1005   if (!spc) return;
1006
1007   char *token = strdup (name);
1008   char *otoken = token;
1009   char *name2;
1010   char *lasts;
1011
1012   while ((name2 = strtok_r (token, ",", &lasts))) {
1013     token = 0;
1014
1015     while (*name2 == ' ' || *name2 == '\t' || *name2 == '\n')
1016       name2++;
1017
1018     spc = strrchr (name2, ' ');
1019     if (!spc) continue;
1020
1021     int dsize = 0;
1022     if (1 != sscanf (spc, " %d ", &dsize))
1023       continue;
1024     float size = dsize;
1025
1026     if (size < 4) continue;
1027
1028     name2[strlen(name2) - strlen(spc)] = 0;
1029
1030     fid->native_font = jwxyz_load_native_font(XRootWindow(dpy,0), 0, 0, name2,
1031                                               strlen(name2) - strlen(spc),
1032                                               JWXYZ_FONT_FACE, size, NULL,
1033                                               &fid->ascent, &fid->descent);
1034     if (fid->native_font) {
1035       fid->xa_font = strdup (name); // Maybe this should be an XLFD?
1036       break;
1037     } else {
1038       Log("No native font: \"%s\" %.0f", name2, size);
1039     }
1040   }
1041
1042   free (otoken);
1043 }
1044
1045
1046 static const char *
1047 xlfd_field_end (const char *s)
1048 {
1049   const char *s2 = strchr(s, '-');
1050   if (!s2)
1051     s2 = s + strlen(s);
1052   return s2;
1053 }
1054
1055
1056 static size_t
1057 xlfd_next (const char **s, const char **s2)
1058 {
1059   if (!**s2) {
1060     *s = *s2;
1061   } else {
1062     Assert (**s2 == '-', "xlfd parse error");
1063     *s = *s2 + 1;
1064     *s2 = xlfd_field_end (*s);
1065   }
1066
1067   return *s2 - *s;
1068 }
1069
1070
1071 static void
1072 try_xlfd_font (Display *dpy, const char *name, Font fid)
1073 {
1074   const char *family_name = NULL; /* Not NULL-terminated. */
1075   size_t family_name_size = 0;
1076   int require = 0,
1077    // Default mask is for the built-in X11 font aliases.
1078    mask = JWXYZ_STYLE_MONOSPACE | JWXYZ_STYLE_BOLD | JWXYZ_STYLE_ITALIC;
1079   Bool rand  = False;
1080   float size = 12; /* In points (1/72 in.) */
1081
1082   const char *s = (name ? name : "");
1083
1084   size_t L = strlen (s);
1085 # define CMP(STR) (L == strlen(STR) && !strncasecmp (s, (STR), L))
1086 # define UNSPEC   (L == 0 || (L == 1 && *s == '*'))
1087   if      (CMP ("6x10"))     size = 8,  require |= JWXYZ_STYLE_MONOSPACE;
1088   else if (CMP ("6x10bold")) size = 8,  require |= JWXYZ_STYLE_MONOSPACE | JWXYZ_STYLE_BOLD;
1089   else if (CMP ("fixed"))    size = 12, require |= JWXYZ_STYLE_MONOSPACE;
1090   else if (CMP ("9x15"))     size = 12, require |= JWXYZ_STYLE_MONOSPACE;
1091   else if (CMP ("9x15bold")) size = 12, require |= JWXYZ_STYLE_MONOSPACE | JWXYZ_STYLE_BOLD;
1092   else if (CMP ("vga"))      size = 12, require |= JWXYZ_STYLE_MONOSPACE;
1093   else if (CMP ("console"))  size = 12, require |= JWXYZ_STYLE_MONOSPACE;
1094   else if (CMP ("gallant"))  size = 12, require |= JWXYZ_STYLE_MONOSPACE;
1095   else {
1096
1097     int forbid = 0;
1098
1099     // Incorrect fields are ignored.
1100
1101     if (*s == '-')
1102       ++s;
1103     const char *s2 = xlfd_field_end(s);
1104
1105     // Foundry (ignore)
1106
1107     L = xlfd_next (&s, &s2); // Family name
1108     // This used to substitute Georgia for Times. Now it doesn't.
1109     if (CMP ("random")) {
1110       rand = True;
1111     } else if (CMP ("fixed")) {
1112       require |= JWXYZ_STYLE_MONOSPACE;
1113       family_name = "Courier";
1114       family_name_size = strlen(family_name);
1115     } else if (!UNSPEC) {
1116       family_name = s;
1117       family_name_size = L;
1118     }
1119
1120     L = xlfd_next (&s, &s2); // Weight name
1121     if (CMP ("bold") || CMP ("demibold"))
1122       require |= JWXYZ_STYLE_BOLD;
1123     else if (CMP ("medium") || CMP ("regular"))
1124       forbid |= JWXYZ_STYLE_BOLD;
1125
1126     L = xlfd_next (&s, &s2); // Slant
1127     if (CMP ("i") || CMP ("o"))
1128       require |= JWXYZ_STYLE_ITALIC;
1129     else if (CMP ("r"))
1130       forbid |= JWXYZ_STYLE_ITALIC;
1131
1132     xlfd_next (&s, &s2); // Set width name (ignore)
1133     xlfd_next (&s, &s2); // Add style name (ignore)
1134
1135     L = xlfd_next (&s, &s2); // Pixel size
1136     char *s3;
1137     uintmax_t pxsize = strtoumax(s, &s3, 10);
1138     if (UNSPEC || s2 != s3)
1139       pxsize = UINTMAX_MAX; // i.e. it's invalid.
1140
1141     L = xlfd_next (&s, &s2); // Point size
1142     uintmax_t ptsize = strtoumax(s, &s3, 10);
1143     if (UNSPEC || s2 != s3)
1144       ptsize = UINTMAX_MAX;
1145
1146     xlfd_next (&s, &s2); // Resolution X (ignore)
1147     xlfd_next (&s, &s2); // Resolution Y (ignore)
1148
1149     L = xlfd_next (&s, &s2); // Spacing
1150     if (CMP ("p"))
1151       forbid |= JWXYZ_STYLE_MONOSPACE;
1152     else if (CMP ("m") || CMP ("c"))
1153       require |= JWXYZ_STYLE_MONOSPACE;
1154
1155     xlfd_next (&s, &s2); // Average width (ignore)
1156
1157     // -*-courier-bold-r-*-*-14-*-*-*-*-*-*-*         14 px
1158     // -*-courier-bold-r-*-*-*-140-*-*-m-*-*-*        14 pt
1159     // -*-courier-bold-r-*-*-140-*                    14 pt, via wildcard
1160     // -*-courier-bold-r-*-140-*                      14 pt, not handled
1161     // -*-courier-bold-r-*-*-14-180-*-*-*-*-*-*       error
1162
1163     L = xlfd_next (&s, &s2); // Charset registry
1164     if (ptsize != UINTMAX_MAX) {
1165       // It was in the ptsize field, so that's definitely what it is.
1166       size = ptsize / 10.0;
1167     } else if (pxsize != UINTMAX_MAX) {
1168       size = pxsize;
1169       // If it's a fully qualified XLFD, then this really is the pxsize.
1170       // Otherwise, this is probably point size with a multi-field wildcard.
1171       if (L == 0)
1172         size /= 10.0;
1173     }
1174
1175     mask = require | forbid;
1176   }
1177 # undef CMP
1178 # undef UNSPEC
1179
1180   if (!family_name && !rand) {
1181     family_name = jwxyz_default_font_family (require);
1182     family_name_size = strlen (family_name);
1183   }
1184
1185   if (size < 6 || size > 1000)
1186     size = 12;
1187
1188   char *family_name_ptr = NULL;
1189   fid->native_font = jwxyz_load_native_font (XRootWindow(dpy,0),
1190                                              require, mask,
1191                                              family_name, family_name_size,
1192                                              rand ? JWXYZ_FONT_RANDOM : JWXYZ_FONT_FAMILY,
1193                                              size, &family_name_ptr,
1194                                              &fid->ascent, &fid->descent);
1195
1196   if (fid->native_font) {
1197     unsigned dpi_d = XDisplayHeightMM (dpy,0) * 10 / 2;
1198     unsigned dpi = (254 * XDisplayHeight (dpy,0) + dpi_d) / (2 * dpi_d);
1199     asprintf(&fid->xa_font, "-*-%s-%s-%c-*-*-%u-%u-%u-%u-%c-0-iso10646-1",
1200              family_name_ptr,
1201              (require & JWXYZ_STYLE_BOLD) ? "bold" : "medium",
1202              (require & JWXYZ_STYLE_ITALIC) ? 'o' : 'r',
1203              (unsigned)(dpi * size / 72.27 + 0.5),
1204              (unsigned)(size * 10 + 0.5), dpi, dpi,
1205              (require & JWXYZ_STYLE_MONOSPACE) ? 'm' : 'p');
1206   }
1207
1208   free (family_name_ptr);
1209 }
1210
1211
1212 Font
1213 XLoadFont (Display *dpy, const char *name)
1214 {
1215   Font fid = (Font) calloc (1, sizeof(*fid));
1216
1217   fid->refcount = 1;
1218   fid->dpy = dpy;
1219   try_native_font (dpy, name, fid);
1220
1221   if (!fid->native_font && name &&
1222       strchr (name, ' ') &&
1223       !strchr (name, '*')) {
1224     // If name contains a space but no stars, it is a native font spec --
1225     // return NULL so that we know it really didn't exist.  Else, it is an
1226     //  XLFD font, so keep trying.
1227     free (fid);
1228     return 0;
1229   }
1230
1231   if (! fid->native_font)
1232     try_xlfd_font (dpy, name, fid);
1233
1234   if (!fid->native_font) {
1235     free (fid);
1236     return 0;
1237   }
1238
1239   query_font (fid);
1240
1241   return fid;
1242 }
1243
1244
1245 XFontStruct *
1246 XLoadQueryFont (Display *dpy, const char *name)
1247 {
1248   Font fid = XLoadFont (dpy, name);
1249   if (!fid) return 0;
1250   return XQueryFont (dpy, fid);
1251 }
1252
1253 int
1254 XUnloadFont (Display *dpy, Font fid)
1255 {
1256   if (--fid->refcount < 0) abort();
1257   if (fid->refcount > 0) return 0;
1258
1259   if (fid->native_font)
1260     jwxyz_release_native_font (fid->dpy, fid->native_font);
1261
1262   if (fid->metrics.per_char)
1263     free (fid->metrics.per_char);
1264
1265   free (fid);
1266   return 0;
1267 }
1268
1269 int
1270 XFreeFontInfo (char **names, XFontStruct *info, int n)
1271 {
1272   int i;
1273   if (names) {
1274     for (i = 0; i < n; i++)
1275       if (names[i]) free (names[i]);
1276     free (names);
1277   }
1278   if (info) {
1279     for (i = 0; i < n; i++)
1280       if (info[i].per_char) {
1281         free (info[i].per_char);
1282         free (info[i].properties);
1283       }
1284     free (info);
1285   }
1286   return 0;
1287 }
1288
1289 int
1290 XFreeFont (Display *dpy, XFontStruct *f)
1291 {
1292   Font fid = f->fid;
1293   XFreeFontInfo (0, f, 1);
1294   XUnloadFont (dpy, fid);
1295   return 0;
1296 }
1297
1298
1299 int
1300 XSetFont (Display *dpy, GC gc, Font fid)
1301 {
1302   XGCValues *gcv = VTBL->gc_gcv(gc);
1303   Font font2 = copy_font (fid);
1304   if (gcv->font)
1305     XUnloadFont (dpy, gcv->font);
1306   gcv->font = font2;
1307   return 0;
1308 }
1309
1310
1311 XFontSet
1312 XCreateFontSet (Display *dpy, char *name,
1313                 char ***missing_charset_list_return,
1314                 int *missing_charset_count_return,
1315                 char **def_string_return)
1316 {
1317   char *name2 = strdup (name);
1318   char *s = strchr (name, ',');
1319   if (s) *s = 0;
1320   XFontSet set = 0;
1321   XFontStruct *f = XLoadQueryFont (dpy, name2);
1322   if (f)
1323   {
1324     set = (XFontSet) calloc (1, sizeof(*set));
1325     set->font = f;
1326   }
1327   free (name2);
1328   if (missing_charset_list_return)  *missing_charset_list_return = 0;
1329   if (missing_charset_count_return) *missing_charset_count_return = 0;
1330   if (def_string_return) *def_string_return = 0;
1331   return set;
1332 }
1333
1334
1335 void
1336 XFreeFontSet (Display *dpy, XFontSet set)
1337 {
1338   XFreeFont (dpy, set->font);
1339   free (set);
1340 }
1341
1342
1343 void
1344 XFreeStringList (char **list)
1345 {
1346   int i;
1347   if (!list) return;
1348   for (i = 0; list[i]; i++)
1349     XFree (list[i]);
1350   XFree (list);
1351 }
1352
1353
1354 int
1355 XTextExtents (XFontStruct *f, const char *s, int length,
1356               int *dir_ret, int *ascent_ret, int *descent_ret,
1357               XCharStruct *cs)
1358 {
1359   // Unfortunately, adding XCharStructs together to get the extents for a
1360   // string doesn't work: Cocoa uses non-integral character advancements, but
1361   // XCharStruct.width is an integer. Plus that doesn't take into account
1362   // kerning pairs, alternate glyphs, and fun stuff like the word "Zapfino" in
1363   // Zapfino.
1364
1365   Font ff = f->fid;
1366   Display *dpy = ff->dpy;
1367   jwxyz_render_text (dpy, ff->native_font, s, length, False, False, cs, 0);
1368   *dir_ret = 0;
1369   *ascent_ret  = f->ascent;
1370   *descent_ret = f->descent;
1371   return 0;
1372 }
1373
1374 int
1375 XTextWidth (XFontStruct *f, const char *s, int length)
1376 {
1377   int ascent, descent, dir;
1378   XCharStruct cs;
1379   XTextExtents (f, s, length, &dir, &ascent, &descent, &cs);
1380   return cs.width;
1381 }
1382
1383
1384 int
1385 XTextExtents16 (XFontStruct *f, const XChar2b *s, int length,
1386                 int *dir_ret, int *ascent_ret, int *descent_ret,
1387                 XCharStruct *cs)
1388 {
1389   // Bool latin1_p = True;
1390   int i, utf8_len = 0;
1391   char *utf8 = XChar2b_to_utf8 (s, &utf8_len);   // already sanitized
1392
1393   for (i = 0; i < length; i++)
1394     if (s[i].byte1 > 0) {
1395       // latin1_p = False;
1396       break;
1397     }
1398
1399   {
1400     Font ff = f->fid;
1401     Display *dpy = ff->dpy;
1402     jwxyz_render_text (dpy, ff->native_font, utf8, strlen(utf8),
1403                        True, False, cs, 0);
1404   }
1405
1406   *dir_ret = 0;
1407   *ascent_ret  = f->ascent;
1408   *descent_ret = f->descent;
1409   free (utf8);
1410   return 0;
1411 }
1412
1413
1414 /* "Returns the distance in pixels in the primary draw direction from
1415  the drawing origin to the origin of the next character to be drawn."
1416
1417  "overall_ink_return is set to the bbox of the string's character ink."
1418
1419  "The overall_ink_return for a nondescending, horizontally drawn Latin
1420  character is conventionally entirely above the baseline; that is,
1421  overall_ink_return.height <= -overall_ink_return.y."
1422
1423  [So this means that y is the top of the ink, and height grows down:
1424  For above-the-baseline characters, y is negative.]
1425
1426  "The overall_ink_return for a nonkerned character is entirely at, and to
1427  the right of, the origin; that is, overall_ink_return.x >= 0."
1428
1429  [So this means that x is the left of the ink, and width grows right.
1430  For left-of-the-origin characters, x is negative.]
1431
1432  "A character consisting of a single pixel at the origin would set
1433  overall_ink_return fields y = 0, x = 0, width = 1, and height = 1."
1434  */
1435 int
1436 Xutf8TextExtents (XFontSet set, const char *str, int len,
1437                   XRectangle *overall_ink_return,
1438                   XRectangle *overall_logical_return)
1439 {
1440   XCharStruct cs;
1441   Font f = set->font->fid;
1442
1443   jwxyz_render_text (f->dpy, f->native_font, str, len, True, False, &cs,
1444                      NULL);
1445
1446   /* "The overall_logical_return is the bounding box that provides minimum
1447    spacing to other graphical features for the string. Other graphical
1448    features, for example, a border surrounding the text, should not
1449    intersect this rectangle."
1450
1451    So I think that means they're the same?  Or maybe "ink" is the bounding
1452    box, and "logical" is the advancement?  But then why is the return value
1453    the advancement?
1454    */
1455   if (overall_ink_return)
1456     XCharStruct_to_XmbRectangle (cs, *overall_ink_return);
1457   if (overall_logical_return)
1458     XCharStruct_to_XmbRectangle (cs, *overall_logical_return);
1459
1460   return cs.width;
1461 }
1462
1463
1464 int
1465 jwxyz_draw_string (Display *dpy, Drawable d, GC gc, int x, int y,
1466                    const char *str, size_t len, int utf8_p)
1467 {
1468   const XGCValues *gcv = VTBL->gc_gcv (gc);
1469   Font ff = gcv->font;
1470   XCharStruct cs;
1471
1472   char *data = 0;
1473   jwxyz_render_text (dpy, jwxyz_native_font (ff), str, len, utf8_p,
1474                      gcv->antialias_p, &cs, &data);
1475   int w = cs.rbearing - cs.lbearing;
1476   int h = cs.ascent + cs.descent;
1477
1478   if (w < 0 || h < 0) abort();
1479   if (w == 0 || h == 0) {
1480     if (data) free(data);
1481     return 0;
1482   }
1483
1484   XImage *img = XCreateImage (dpy, VTBL->visual (dpy), 32,
1485                               ZPixmap, 0, data, w, h, 0, 0);
1486
1487   /* The image of text is a 32-bit image, in white.
1488      Take the green channel for intensity and use that as alpha.
1489      replace RGB with the GC's foreground color.
1490      This expects that XPutImage respects alpha and only writes
1491      the bits that are not masked out.
1492    */
1493   {
1494 # define ROTL(x, rot) (((x) << ((rot) & 31)) | ((x) >> (32 - ((rot) & 31))))
1495
1496     const unsigned long *masks =
1497       DefaultVisualOfScreen (DefaultScreenOfDisplay(dpy))->rgba_masks;
1498     unsigned shift = (i_log2 (masks[3]) - i_log2 (masks[1])) & 31;
1499     uint32_t mask = ROTL(masks[1], shift) & masks[3],
1500              color = gcv->foreground & ~masks[3];
1501     uint32_t *s = (uint32_t *)data;
1502     uint32_t *end = s + (w * h);
1503     while (s < end) {
1504
1505       *s = (ROTL(*s, shift) & mask) | color;
1506       ++s;
1507     }
1508   }
1509
1510   {
1511     Bool old_alpha = gcv->alpha_allowed_p;
1512     jwxyz_XSetAlphaAllowed (dpy, gc, True);
1513     XPutImage (dpy, d, gc, img, 0, 0,
1514                x + cs.lbearing,
1515                y - cs.ascent,
1516                w, h);
1517     jwxyz_XSetAlphaAllowed (dpy, gc, old_alpha);
1518     XDestroyImage (img);
1519   }
1520
1521   return 0;
1522 }
1523
1524
1525 int
1526 XDrawString (Display *dpy, Drawable d, GC gc, int x, int y,
1527              const char  *str, int len)
1528 {
1529   return VTBL->draw_string (dpy, d, gc, x, y, str, len, False);
1530 }
1531
1532
1533 int
1534 XDrawString16 (Display *dpy, Drawable d, GC gc, int x, int y,
1535                const XChar2b *str, int len)
1536 {
1537   XChar2b *b2 = malloc ((len + 1) * sizeof(*b2));
1538   char *s2;
1539   int ret;
1540   memcpy (b2, str, len * sizeof(*b2));
1541   b2[len].byte1 = b2[len].byte2 = 0;
1542   s2 = XChar2b_to_utf8 (b2, 0);
1543   free (b2);
1544   ret = VTBL->draw_string (dpy, d, gc, x, y, s2, strlen(s2), True);
1545   free (s2);
1546   return ret;
1547 }
1548
1549
1550 void
1551 Xutf8DrawString (Display *dpy, Drawable d, XFontSet set, GC gc,
1552                  int x, int y, const char *str, int len)
1553 {
1554   VTBL->draw_string (dpy, d, gc, x, y, str, len, True);
1555 }
1556
1557
1558 int
1559 XDrawImageString (Display *dpy, Drawable d, GC gc, int x, int y,
1560                   const char *str, int len)
1561 {
1562   int ascent, descent, dir;
1563   XCharStruct cs;
1564   XTextExtents (&VTBL->gc_gcv (gc)->font->metrics, str, len,
1565                 &dir, &ascent, &descent, &cs);
1566   jwxyz_fill_rect (dpy, d, gc,
1567                    x + MIN (0, cs.lbearing),
1568                    y - MAX (0, ascent),
1569                    MAX (MAX (0, cs.rbearing) -
1570                         MIN (0, cs.lbearing),
1571                         cs.width),
1572                    MAX (0, ascent) + MAX (0, descent),
1573                    VTBL->gc_gcv(gc)->background);
1574   return XDrawString (dpy, d, gc, x, y, str, len);
1575 }
1576
1577
1578 void *
1579 jwxyz_native_font (Font f)
1580 {
1581   return f->native_font;
1582 }
1583
1584
1585 int
1586 XSetForeground (Display *dpy, GC gc, unsigned long fg)
1587 {
1588   XGCValues *gcv = VTBL->gc_gcv (gc);
1589   jwxyz_validate_pixel (dpy, fg, VTBL->gc_depth (gc), gcv->alpha_allowed_p);
1590   gcv->foreground = fg;
1591   return 0;
1592 }
1593
1594
1595 int
1596 XSetBackground (Display *dpy, GC gc, unsigned long bg)
1597 {
1598   XGCValues *gcv = VTBL->gc_gcv (gc);
1599   jwxyz_validate_pixel (dpy, bg, VTBL->gc_depth (gc), gcv->alpha_allowed_p);
1600   gcv->background = bg;
1601   return 0;
1602 }
1603
1604 int
1605 jwxyz_XSetAlphaAllowed (Display *dpy, GC gc, Bool allowed)
1606 {
1607   VTBL->gc_gcv (gc)->alpha_allowed_p = allowed;
1608   return 0;
1609 }
1610
1611 int
1612 jwxyz_XSetAntiAliasing (Display *dpy, GC gc, Bool antialias_p)
1613 {
1614   VTBL->gc_gcv (gc)->antialias_p = antialias_p;
1615   return 0;
1616 }
1617
1618
1619 int
1620 XSetLineAttributes (Display *dpy, GC gc, unsigned int line_width,
1621                     int line_style, int cap_style, int join_style)
1622 {
1623   XGCValues *gcv = VTBL->gc_gcv (gc);
1624   gcv->line_width = line_width;
1625   Assert (line_style == LineSolid, "only LineSolid implemented");
1626 //  gc->gcv.line_style = line_style;
1627   gcv->cap_style = cap_style;
1628   gcv->join_style = join_style;
1629   return 0;
1630 }
1631
1632 int
1633 XSetGraphicsExposures (Display *dpy, GC gc, Bool which)
1634 {
1635   return 0;
1636 }
1637
1638 int
1639 XSetFunction (Display *dpy, GC gc, int which)
1640 {
1641   VTBL->gc_gcv (gc)->function = which;
1642   return 0;
1643 }
1644
1645 int
1646 XSetSubwindowMode (Display *dpy, GC gc, int which)
1647 {
1648   VTBL->gc_gcv (gc)->subwindow_mode = which;
1649   return 0;
1650 }
1651
1652
1653 Bool
1654 XQueryPointer (Display *dpy, Window w, Window *root_ret, Window *child_ret,
1655                int *root_x_ret, int *root_y_ret,
1656                int *win_x_ret, int *win_y_ret, unsigned int *mask_ret)
1657 {
1658   assert_window (dpy, w);
1659
1660   XPoint vpos, p;
1661   jwxyz_get_pos (w, &vpos, &p);
1662
1663   if (root_x_ret) *root_x_ret = p.x;
1664   if (root_y_ret) *root_y_ret = p.y;
1665   if (win_x_ret)  *win_x_ret  = p.x - vpos.x;
1666   if (win_y_ret)  *win_y_ret  = p.y - vpos.y;
1667   if (mask_ret)   *mask_ret   = 0;  // #### poll the keyboard modifiers?
1668   if (root_ret)   *root_ret   = 0;
1669   if (child_ret)  *child_ret  = 0;
1670   return True;
1671 }
1672
1673 Bool
1674 XTranslateCoordinates (Display *dpy, Window w, Window dest_w,
1675                        int src_x, int src_y,
1676                        int *dest_x_ret, int *dest_y_ret,
1677                        Window *child_ret)
1678 {
1679   assert_window (dpy, w);
1680
1681   XPoint vpos, p;
1682   jwxyz_get_pos (w, &vpos, NULL);
1683
1684   // point starts out relative to top left of view
1685   p.x = src_x;
1686   p.y = src_y;
1687
1688   // get point relative to top left of screen
1689   p.x += vpos.x;
1690   p.y += vpos.y;
1691
1692   *dest_x_ret = p.x;
1693   *dest_y_ret = p.y;
1694   if (child_ret)
1695     *child_ret = w;
1696   return True;
1697 }
1698
1699
1700 KeySym
1701 XKeycodeToKeysym (Display *dpy, KeyCode code, int index)
1702 {
1703   return code;
1704 }
1705
1706 int
1707 XLookupString (XKeyEvent *e, char *buf, int size, KeySym *k_ret,
1708                XComposeStatus *xc)
1709 {
1710   KeySym ks = XKeycodeToKeysym (0, e->keycode, 0);
1711   char c = 0;
1712   // Do not put non-ASCII KeySyms like XK_Shift_L and XK_Page_Up in the string.
1713   if ((unsigned int) ks <= 255)
1714     c = (char) ks;
1715
1716   // Put control characters in the string.  Not meta.
1717   if (e->state & ControlMask) {
1718     if (c >= 'a' && c <= 'z')    // Upcase control.
1719       c -= 'a'-'A';
1720     if (c >= '@' && c <= '_')    // Shift to control page.
1721       c -= '@';
1722     if (c == ' ')                // C-SPC is NULL.
1723       c = 0;
1724   }
1725
1726   if (k_ret) *k_ret = ks;
1727   if (size > 0) buf[0] = c;
1728   if (size > 1) buf[1] = 0;
1729   return (size > 0 ? 1 : 0);
1730 }
1731
1732
1733 int
1734 XFlush (Display *dpy)
1735 {
1736   // Just let the event loop take care of this on its own schedule.
1737   return 0;
1738 }
1739
1740 int
1741 XSync (Display *dpy, Bool flush)
1742 {
1743   return XFlush (dpy);
1744 }
1745
1746
1747 // declared in utils/visual.h
1748 int
1749 has_writable_cells (Screen *s, Visual *v)
1750 {
1751   return 0;
1752 }
1753
1754 int
1755 visual_depth (Screen *s, Visual *v)
1756 {
1757   return 32;
1758 }
1759
1760 int
1761 visual_cells (Screen *s, Visual *v)
1762 {
1763   return (int)(v->rgba_masks[0] | v->rgba_masks[1] | v->rgba_masks[2]);
1764 }
1765
1766 int
1767 visual_class (Screen *s, Visual *v)
1768 {
1769   return TrueColor;
1770 }
1771
1772 void
1773 visual_rgb_masks (Screen *s, Visual *v, unsigned long *red_mask,
1774                   unsigned long *green_mask, unsigned long *blue_mask)
1775 {
1776   *red_mask = v->rgba_masks[0];
1777   *green_mask = v->rgba_masks[1];
1778   *blue_mask = v->rgba_masks[2];
1779 }
1780
1781 int
1782 visual_pixmap_depth (Screen *s, Visual *v)
1783 {
1784   return 32;
1785 }
1786
1787 int
1788 screen_number (Screen *screen)
1789 {
1790   return 0;
1791 }
1792
1793 // declared in utils/grabclient.h
1794 Bool
1795 use_subwindow_mode_p (Screen *screen, Window window)
1796 {
1797   return False;
1798 }
1799
1800 #endif /* HAVE_JWXYZ */