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