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